/* Verify X.509 certificate chain using a PKCS #11 token. * * Note that the return value is an OR of GNUTLS_CERT_* elements. * * Unlike the non-PKCS#11 version, this function accepts a key purpose * (from GNUTLS_KP_...). That is because in the p11-kit trust modules * anchors are mixed and get assigned a purpose. * * This function verifies a X.509 certificate list. The certificate * list should lead to a trusted certificate in order to be trusted. */ unsigned int _gnutls_pkcs11_verify_crt_status(const char* url, const gnutls_x509_crt_t * certificate_list, unsigned clist_size, const char *purpose, unsigned int flags, gnutls_verify_output_function func) { int ret; unsigned int status = 0, i; gnutls_x509_crt_t issuer = NULL; gnutls_datum_t raw_issuer = {NULL, 0}; time_t now = gnutls_time(0); if (clist_size > 1) { /* Check if the last certificate in the path is self signed. * In that case ignore it (a certificate is trusted only if it * leads to a trusted party by us, not the server's). * * This prevents from verifying self signed certificates against * themselves. This (although not bad) caused verification * failures on some root self signed certificates that use the * MD2 algorithm. */ if (gnutls_x509_crt_check_issuer (certificate_list[clist_size - 1], certificate_list[clist_size - 1]) != 0) { clist_size--; } } /* We want to shorten the chain by removing the cert that matches * one of the certs we trust and all the certs after that i.e. if * cert chain is A signed-by B signed-by C signed-by D (signed-by * self-signed E but already removed above), and we trust B, remove * B, C and D. */ if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) i = 0; /* also replace the first one */ else i = 1; /* do not replace the first one */ for (; i < clist_size; i++) { unsigned vflags; if (i == 0) /* in the end certificate do full comparison */ vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| GNUTLS_PKCS11_OBJ_FLAG_COMPARE|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; else vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; if (gnutls_pkcs11_crt_is_known (url, certificate_list[i], vflags) != 0) { if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) && !(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { status |= check_time_status(certificate_list[i], now); if (status != 0) { if (func) func(certificate_list[i], certificate_list[i], NULL, status); return status; } } if (func) func(certificate_list[i], certificate_list[i], NULL, status); clist_size = i; break; } /* clist_size may have been changed which gets out of loop */ } if (clist_size == 0) { /* The certificate is already present in the trusted certificate list. * Nothing to verify. */ return status; } /* check for blacklists */ for (i = 0; i < clist_size; i++) { if (gnutls_pkcs11_crt_is_known (url, certificate_list[i], GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) { status |= GNUTLS_CERT_INVALID; status |= GNUTLS_CERT_REVOKED; if (func) func(certificate_list[i], certificate_list[i], NULL, status); goto cleanup; } } /* check against issuer */ ret = gnutls_pkcs11_get_raw_issuer(url, certificate_list[clist_size - 1], &raw_issuer, GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT|GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE); if (ret < 0) { gnutls_assert(); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && clist_size > 2) { /* check if the last certificate in the chain is present * in our trusted list, and if yes, verify against it. */ ret = gnutls_pkcs11_crt_is_known(url, certificate_list[clist_size - 1], GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED|GNUTLS_PKCS11_OBJ_FLAG_COMPARE); if (ret != 0) { return _gnutls_verify_crt_status(certificate_list, clist_size, &certificate_list[clist_size - 1], 1, flags, purpose, func); } } status |= GNUTLS_CERT_INVALID; status |= GNUTLS_CERT_SIGNER_NOT_FOUND; /* verify the certificate list against 0 trusted CAs in order * to get, any additional flags from the certificate list (e.g., * insecure algorithms or expired */ status |= _gnutls_verify_crt_status(certificate_list, clist_size, NULL, 0, flags, purpose, func); goto cleanup; } ret = gnutls_x509_crt_init(&issuer); if (ret < 0) { gnutls_assert(); status |= GNUTLS_CERT_INVALID; status |= GNUTLS_CERT_SIGNER_NOT_FOUND; goto cleanup; } ret = gnutls_x509_crt_import(issuer, &raw_issuer, GNUTLS_X509_FMT_DER); if (ret < 0) { gnutls_assert(); status |= GNUTLS_CERT_INVALID; status |= GNUTLS_CERT_SIGNER_NOT_FOUND; goto cleanup; } /* check if the raw issuer is blacklisted (it can happen if * the issuer is both in the trusted list and the blacklisted) */ if (gnutls_pkcs11_crt_is_known (url, issuer, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE| GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) { status |= GNUTLS_CERT_INVALID; status |= GNUTLS_CERT_SIGNER_NOT_FOUND; /* if the signer is revoked - it is as if it doesn't exist */ goto cleanup; } /* security modules that provide trust, bundle all certificates (of all purposes) * together. In software that doesn't specify any purpose assume the default to * be www-server. */ ret = _gnutls_check_key_purpose(issuer, purpose==NULL?GNUTLS_KP_TLS_WWW_SERVER:purpose, 0); if (ret != 1) { gnutls_assert(); status |= GNUTLS_CERT_INVALID; status |= GNUTLS_CERT_SIGNER_NOT_FOUND; goto cleanup; } status = _gnutls_verify_crt_status(certificate_list, clist_size, &issuer, 1, flags, purpose, func); cleanup: gnutls_free(raw_issuer.data); if (issuer != NULL) gnutls_x509_crt_deinit(issuer); return status; }
void doit(void) { char buf[128]; int exit_val = 0; int ret; unsigned j; const char *lib, *bin; gnutls_x509_crt_t issuer = NULL; gnutls_x509_trust_list_t tl; gnutls_x509_crt_t certs[MAX_CHAIN]; gnutls_x509_crt_t intermediate; gnutls_datum_t tmp; /* The overloading of time() seems to work in linux (ELF?) * systems only. Disable it on windows. */ #ifdef _WIN32 exit(77); #endif bin = softhsm_bin(); lib = softhsm_lib(); ret = global_init(); if (ret != 0) { fail("%d: %s\n", ret, gnutls_strerror(ret)); exit(1); } gnutls_pkcs11_set_pin_function(pin_func, NULL); gnutls_global_set_time_function(mytime); gnutls_global_set_log_function(tls_log_func); if (debug) gnutls_global_set_log_level(4711); set_softhsm_conf(CONFIG); snprintf(buf, sizeof(buf), "%s --init-token --slot 0 --label test --so-pin "PIN" --pin "PIN, bin); system(buf); ret = gnutls_pkcs11_add_provider(lib, "trusted"); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_init: %s\n", gnutls_strerror(ret)); exit(1); } for (j = 0; ca_list[j]; j++) { if (debug > 2) printf("\tAdding certificate %d...", (int) j); ret = gnutls_x509_crt_init(&certs[j]); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_init[%d,%d]: %s\n", (int) 3, (int) j, gnutls_strerror(ret)); exit(1); } tmp.data = (unsigned char *) ca_list[j]; tmp.size = strlen(ca_list[j]); ret = gnutls_x509_crt_import(certs[j], &tmp, GNUTLS_X509_FMT_PEM); if (debug > 2) printf("done\n"); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_import[%d]: %s\n", (int) j, gnutls_strerror(ret)); exit(1); } gnutls_x509_crt_print(certs[j], GNUTLS_CRT_PRINT_ONELINE, &tmp); if (debug) printf("\tCertificate %d: %.*s\n", (int) j, tmp.size, tmp.data); gnutls_free(tmp.data); } if (debug > 2) printf("\tAdding intermediate certificate..."); ret = gnutls_x509_crt_init(&intermediate); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_init: %s\n", gnutls_strerror(ret)); exit(1); } tmp.data = (unsigned char *) intermediate_str; tmp.size = strlen(intermediate_str); ret = gnutls_x509_crt_import(intermediate, &tmp, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_import: %s\n", gnutls_strerror(ret)); exit(1); } if (debug > 2) printf("done\n"); gnutls_x509_crt_print(intermediate, GNUTLS_CRT_PRINT_ONELINE, &tmp); if (debug) printf("\tIntermediate Certificate: %.*s\n", tmp.size, tmp.data); gnutls_free(tmp.data); if (debug) printf("\tVerifying..."); /* initialize softhsm token */ ret = gnutls_pkcs11_token_init(SOFTHSM_URL, PIN, "test"); if (ret < 0) { fail("gnutls_pkcs11_token_init\n"); exit(1); } /* write CA certificate to softhsm */ for (j = 0; ca_list[j]; j++) { char name[64]; snprintf(name, sizeof(name), "test-ca%d", j); ret = gnutls_pkcs11_copy_x509_crt(SOFTHSM_URL, certs[j], name, GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED|GNUTLS_PKCS11_OBJ_FLAG_MARK_CA|GNUTLS_PKCS11_OBJ_FLAG_LOGIN_SO); if (ret < 0) { fail("gnutls_pkcs11_copy_x509_crt: %s\n", gnutls_strerror(ret)); exit(1); } } /* try to extract an issuer when using an object URL */ gnutls_x509_trust_list_init(&tl, 0); ret = gnutls_x509_trust_list_add_trust_file(tl, OBJ_URL, NULL, 0, 0, 0); if (ret != 1) { fail("gnutls_x509_trust_list_add_trust_file (with expl. object 0): %d\n", ret); exit(1); } /* extract the issuer of the certificate */ ret = gnutls_x509_trust_list_get_issuer(tl, intermediate, &issuer, GNUTLS_TL_GET_COPY); if (ret < 0) { fail("gnutls_x509_trust_list_get_issuer (with expl. object) should have succeeded\n"); exit(1); } gnutls_x509_crt_deinit(issuer); gnutls_x509_trust_list_deinit(tl, 0); /* Try to extract issuers using PKCS #11 token URL */ gnutls_x509_trust_list_init(&tl, 0); ret = gnutls_x509_trust_list_add_trust_file(tl, SOFTHSM_URL, NULL, 0, 0, 0); if (ret < 0) { fail("gnutls_x509_trust_list_add_trust_file\n"); exit(1); } /* extract the issuer of the certificate */ ret = gnutls_x509_trust_list_get_issuer(tl, intermediate, &issuer, GNUTLS_TL_GET_COPY); if (ret < 0) { fail("gnutls_x509_trust_list_get_issuer should have succeeded\n"); exit(1); } gnutls_x509_crt_deinit(issuer); ret = gnutls_pkcs11_crt_is_known(SOFTHSM_URL, certs[2], GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED); if (ret == 0) { fail("error in gnutls_pkcs11_crt_is_known - 0\n"); exit(1); } ret = gnutls_pkcs11_crt_is_known(SOFTHSM_URL, certs[0], GNUTLS_PKCS11_OBJ_FLAG_COMPARE|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED); if (ret == 0) { fail("error in gnutls_pkcs11_crt_is_known - 0\n"); exit(1); } ret = gnutls_pkcs11_crt_is_known(SOFTHSM_URL, certs[1], GNUTLS_PKCS11_OBJ_FLAG_COMPARE|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED); if (ret == 0) { fail("error in gnutls_pkcs11_crt_is_known - 0\n"); exit(1); } gnutls_x509_trust_list_deinit(tl, 0); /* deinit */ if (debug) printf("\tCleanup..."); gnutls_x509_crt_deinit(intermediate); for (j = 0; ca_list[j]; j++) { gnutls_x509_crt_deinit(certs[j]); } if (debug) printf("done\n\n\n"); gnutls_global_deinit(); if (debug) printf("Exit status...%d\n", exit_val); remove(CONFIG); exit(exit_val); }
void doit(void) { char buf[128]; int exit_val = 0; int ret; unsigned j; const char *lib, *bin; gnutls_x509_crt_t issuer = NULL; gnutls_x509_trust_list_t tl; gnutls_x509_crt_t certs[MAX_CHAIN]; gnutls_x509_crt_t end, ca; unsigned verify_status = 0; gnutls_datum_t tmp; /* The overloading of time() seems to work in linux (ELF?) * systems only. Disable it on windows. */ #ifdef _WIN32 exit(77); #endif bin = softhsm_bin(); lib = softhsm_lib(); ret = global_init(); if (ret != 0) { fail("%d: %s\n", ret, gnutls_strerror(ret)); exit(1); } gnutls_pkcs11_set_pin_function(pin_func, NULL); gnutls_global_set_time_function(mytime); gnutls_global_set_log_function(tls_log_func); if (debug) gnutls_global_set_log_level(4711); set_softhsm_conf(CONFIG); snprintf(buf, sizeof(buf), "%s --init-token --slot 0 --label test --so-pin "PIN" --pin "PIN, bin); system(buf); ret = gnutls_pkcs11_add_provider(lib, "trusted"); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_init: %s\n", gnutls_strerror(ret)); exit(1); } for (j = 0; ca_list[j]; j++) { if (debug > 2) printf("\tAdding certificate %d...", (int) j); ret = gnutls_x509_crt_init(&certs[j]); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_init[%d,%d]: %s\n", (int) 3, (int) j, gnutls_strerror(ret)); exit(1); } tmp.data = (unsigned char *) ca_list[j]; tmp.size = strlen(ca_list[j]); ret = gnutls_x509_crt_import(certs[j], &tmp, GNUTLS_X509_FMT_PEM); if (debug > 2) printf("done\n"); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_import[%d]: %s\n", (int) j, gnutls_strerror(ret)); exit(1); } gnutls_x509_crt_print(certs[j], GNUTLS_CRT_PRINT_ONELINE, &tmp); if (debug) printf("\tCertificate %d: %.*s\n", (int) j, tmp.size, tmp.data); gnutls_free(tmp.data); } if (debug > 2) printf("\tAdding end certificate..."); ret = gnutls_x509_crt_init(&end); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_init: %s\n", gnutls_strerror(ret)); exit(1); } tmp.data = (unsigned char *) v1_root_check[0]; tmp.size = strlen(v1_root_check[0]); ret = gnutls_x509_crt_import(end, &tmp, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_import: %s\n", gnutls_strerror(ret)); exit(1); } gnutls_x509_crt_print(end, GNUTLS_CRT_PRINT_ONELINE, &tmp); if (debug) printf("\tEnd Certificate: %.*s\n", tmp.size, tmp.data); gnutls_free(tmp.data); ret = gnutls_x509_crt_init(&ca); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_init: %s\n", gnutls_strerror(ret)); exit(1); } tmp.data = (unsigned char *) v1_root_check[1]; tmp.size = strlen(v1_root_check[1]); ret = gnutls_x509_crt_import(ca, &tmp, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "gnutls_x509_crt_import: %s\n", gnutls_strerror(ret)); exit(1); } gnutls_x509_crt_print(end, GNUTLS_CRT_PRINT_ONELINE, &tmp); if (debug) printf("\tCA Certificate: %.*s\n", tmp.size, tmp.data); gnutls_free(tmp.data); if (debug > 2) printf("done\n"); if (debug) printf("\tChecking presence and verification..."); /* initialize softhsm token */ ret = gnutls_pkcs11_token_init(SOFTHSM_URL, PIN, "test"); if (ret < 0) { fail("gnutls_pkcs11_token_init\n"); exit(1); } /* write CA certificate to softhsm */ for (j = 0; ca_list[j]; j++) { char name[64]; snprintf(name, sizeof(name), "test-ca%d", j); ret = gnutls_pkcs11_copy_x509_crt(SOFTHSM_URL, certs[j], name, GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED|GNUTLS_PKCS11_OBJ_FLAG_LOGIN_SO); if (ret < 0) { fail("gnutls_pkcs11_copy_x509_crt: %s\n", gnutls_strerror(ret)); exit(1); } } gnutls_x509_trust_list_init(&tl, 0); ret = gnutls_x509_trust_list_add_trust_file(tl, SOFTHSM_URL, NULL, 0, 0, 0); if (ret < 0) { fail("gnutls_x509_trust_list_add_trust_file\n"); exit(1); } ret = gnutls_x509_trust_list_add_cas(tl, &ca, 1, 0); if (ret < 0) { fail("gnutls_x509_trust_list_add_cas\n"); exit(1); } /* extract the issuer of the certificate */ ret = gnutls_x509_trust_list_get_issuer(tl, end, &issuer, GNUTLS_TL_GET_COPY); if (ret < 0) { fail("gnutls_x509_trust_list_get_issuer should have succeeded\n"); exit(1); } gnutls_x509_crt_deinit(issuer); ret = gnutls_pkcs11_crt_is_known(SOFTHSM_URL, ca, GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED); if (ret != 0) { fail("gnutls_pkcs11_crt_is_known should have failed!\n"); exit(1); } ret = gnutls_x509_trust_list_verify_crt2(tl, &end, 1, NULL, 0, GNUTLS_VERIFY_DISABLE_TIME_CHECKS, &verify_status, NULL); if (ret < 0) { fail("gnutls_x509_trust_list_verify_crt2 should have succeeded\n"); exit(1); } if (verify_status != 0) { fail("verification should have succeeded: %.2x\n", verify_status); exit(1); } if (debug) printf("\tCleanup..."); gnutls_x509_trust_list_deinit(tl, 0); gnutls_x509_crt_deinit(ca); gnutls_x509_crt_deinit(end); for (j = 0; ca_list[j]; j++) { gnutls_x509_crt_deinit(certs[j]); } if (debug) printf("done\n\n\n"); gnutls_global_deinit(); if (debug) printf("Exit status...%d\n", exit_val); remove(CONFIG); exit(exit_val); }