/** * gnutls_x509_trust_list_get_issuer: * @list: The structure of the list * @cert: is the certificate to find issuer for * @issuer: Will hold the issuer if any. Should be treated as constant. * @flags: Use zero or %GNUTLS_TL_GET_COPY * * This function will find the issuer of the given certificate. * If the flag %GNUTLS_TL_GET_COPY is specified a copy of the issuer * will be returned which must be freed using gnutls_x509_crt_deinit(). * Note that the flag %GNUTLS_TL_GET_COPY is required for this function * to work with PKCS #11 trust lists in a thread-safe way. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 3.0 **/ int gnutls_x509_trust_list_get_issuer(gnutls_x509_trust_list_t list, gnutls_x509_crt_t cert, gnutls_x509_crt_t * issuer, unsigned int flags) { int ret; ret = trust_list_get_issuer(list, cert, issuer, flags); if (ret == 0) { return 0; } #ifdef ENABLE_PKCS11 if (ret < 0 && list->pkcs11_token) { gnutls_x509_crt_t crt; gnutls_datum_t der = {NULL, 0}; /* use the token for verification */ ret = gnutls_pkcs11_get_raw_issuer(list->pkcs11_token, cert, &der, GNUTLS_X509_FMT_DER, 0); if (ret < 0) { gnutls_assert(); return ret; } ret = gnutls_x509_crt_init(&crt); if (ret < 0) { gnutls_free(der.data); return gnutls_assert_val(ret); } ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER); gnutls_free(der.data); if (ret < 0) { gnutls_x509_crt_deinit(crt); return gnutls_assert_val(ret); } if (flags & GNUTLS_TL_GET_COPY) { *issuer = crt; return 0; } else { /* we add this CA to the keep_cert list in order to make it * persistent. It will be deallocated when the trust list is. */ ret = trust_list_add_compat(list, crt); if (ret < 0) { gnutls_x509_crt_deinit(crt); return gnutls_assert_val(ret); } *issuer = crt; return ret; } } #endif return ret; }
/* 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 pkcs11_export_chain(FILE * outfile, const char *url, unsigned int flags, common_info_st * info) { gnutls_pkcs11_obj_t obj; gnutls_x509_crt_t xcrt; gnutls_datum_t t; int ret; unsigned int obj_flags = flags; pkcs11_common(info); FIX(url, outfile, 0, info); ret = gnutls_pkcs11_obj_init(&obj); if (ret < 0) { fprintf(stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror(ret)); exit(1); } ret = gnutls_pkcs11_obj_import_url(obj, url, obj_flags); if (ret < 0) { fprintf(stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror(ret)); exit(1); } /* make a crt */ ret = gnutls_x509_crt_init(&xcrt); if (ret < 0) { fprintf(stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror(ret)); exit(1); } ret = gnutls_x509_crt_import_pkcs11(xcrt, obj); if (ret < 0) { fprintf(stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror(ret)); exit(1); } ret = gnutls_pkcs11_obj_export3(obj, GNUTLS_X509_FMT_PEM, &t); if (ret < 0) { fprintf(stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror(ret)); exit(1); } fwrite(t.data, 1, t.size, outfile); fputs("\n\n", outfile); gnutls_free(t.data); gnutls_pkcs11_obj_deinit(obj); do { ret = gnutls_pkcs11_get_raw_issuer(url, xcrt, &t, GNUTLS_X509_FMT_PEM, 0); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; if (ret < 0) { fprintf(stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror(ret)); exit(1); } fwrite(t.data, 1, t.size, outfile); fputs("\n\n", outfile); gnutls_x509_crt_deinit(xcrt); ret = gnutls_x509_crt_init(&xcrt); if (ret < 0) { fprintf(stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror(ret)); exit(1); } ret = gnutls_x509_crt_import(xcrt, &t, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "Error in %s:%d: %s\n", __func__, __LINE__, gnutls_strerror(ret)); exit(1); } gnutls_free(t.data); ret = gnutls_x509_crt_check_issuer(xcrt, xcrt); if (ret != 0) { /* self signed */ break; } } while(1); UNFIX; return; }