/** * gnutls_x509_name_constraints_check_crt: * @nc: the extracted name constraints * @type: the type of the constraint to check (of type gnutls_x509_subject_alt_name_t) * @cert: the certificate to be checked * * This function will check the provided certificate names against the constraints in * @nc using the RFC5280 rules. It will traverse all the certificate's names and * alternative names. * * Currently this function is limited to DNS * names and emails (of type %GNUTLS_SAN_DNSNAME and %GNUTLS_SAN_RFC822NAME). * * Returns: zero if the provided name is not acceptable, and non-zero otherwise. * * Since: 3.3.0 **/ unsigned gnutls_x509_name_constraints_check_crt(gnutls_x509_name_constraints_t nc, gnutls_x509_subject_alt_name_t type, gnutls_x509_crt_t cert) { char name[MAX_CN]; size_t name_size; int ret; unsigned idx, t, san_type; gnutls_datum_t n; unsigned found_one; if (is_nc_empty(nc, type) != 0) return 1; /* shortcut; no constraints to check */ if (type == GNUTLS_SAN_RFC822NAME) { idx = found_one = 0; do { name_size = sizeof(name); ret = gnutls_x509_crt_get_subject_alt_name2(cert, idx++, name, &name_size, &san_type, NULL); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; else if (ret < 0) return gnutls_assert_val(0); if (san_type != GNUTLS_SAN_RFC822NAME) continue; found_one = 1; n.data = (void*)name; n.size = name_size; t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, &n); if (t == 0) return gnutls_assert_val(t); } while(ret >= 0); /* there is at least a single e-mail. That means that the EMAIL field will * not be used for verifying the identity of the holder. */ if (found_one != 0) return 1; do { /* ensure there is only a single EMAIL, similarly to CN handling (rfc6125) */ name_size = sizeof(name); ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 1, 0, name, &name_size); if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) return gnutls_assert_val(0); name_size = sizeof(name); ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, name, &name_size); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; else if (ret < 0) return gnutls_assert_val(0); found_one = 1; n.data = (void*)name; n.size = name_size; t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, &n); if (t == 0) return gnutls_assert_val(t); } while(0); /* passed */ if (found_one != 0) return 1; else { /* no name was found. According to RFC5280: * If no name of the type is in the certificate, the certificate is acceptable. */ return gnutls_assert_val(1); } } else if (type == GNUTLS_SAN_DNSNAME) { idx = found_one = 0; do { name_size = sizeof(name); ret = gnutls_x509_crt_get_subject_alt_name2(cert, idx++, name, &name_size, &san_type, NULL); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; else if (ret < 0) return gnutls_assert_val(0); if (san_type != GNUTLS_SAN_DNSNAME) continue; found_one = 1; n.data = (void*)name; n.size = name_size; t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME, &n); if (t == 0) return gnutls_assert_val(t); } while(ret >= 0); /* there is at least a single DNS name. That means that the CN will * not be used for verifying the identity of the holder. */ if (found_one != 0) return 1; /* verify the name constraints against the CN, if the certificate is * not a CA. We do this check only on certificates marked as WWW server, * because that's where the CN check is only performed. */ if (_gnutls_check_key_purpose(cert, GNUTLS_KP_TLS_WWW_SERVER, 0) != 0) do { /* ensure there is only a single CN, according to rfc6125 */ name_size = sizeof(name); ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 1, 0, name, &name_size); if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) return gnutls_assert_val(0); name_size = sizeof(name); ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, name, &name_size); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; else if (ret < 0) return gnutls_assert_val(0); found_one = 1; n.data = (void*)name; n.size = name_size; t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME, &n); if (t == 0) return gnutls_assert_val(t); } while(0); /* passed */ if (found_one != 0) return 1; else { /* no name was found. According to RFC5280: * If no name of the type is in the certificate, the certificate is acceptable. */ return gnutls_assert_val(1); } } else if (type == GNUTLS_SAN_IPADDRESS || type == GNUTLS_SAN_URI) { return check_unsupported_constraint2(cert, nc, type); } else return check_unsupported_constraint(nc, type); }
/* 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; }
/* Verify X.509 certificate chain. * * Note that the return value is an OR of GNUTLS_CERT_* elements. * * 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_verify_crt_status(const gnutls_x509_crt_t * certificate_list, int clist_size, const gnutls_x509_crt_t * trusted_cas, int tcas_size, unsigned int flags, const char *purpose, gnutls_verify_output_function func) { int i = 0, ret; unsigned int status = 0, output; time_t now = gnutls_time(0); verify_state_st vparams; 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++) { int j; for (j = 0; j < tcas_size; j++) { /* we check for a certificate that may not be identical with the one * sent by the client, but will have the same name and key. That is * because it can happen that a CA certificate is upgraded from intermediate * CA to self-signed CA at some point. */ if (_gnutls_check_if_same_key (certificate_list[i], trusted_cas[j], i) != 0) { /* explicit time check for trusted CA that we remove from * list. GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS */ if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) && !(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { status |= check_time_status(trusted_cas[j], now); if (status != 0) { if (func) func(certificate_list[i], trusted_cas[j], NULL, status); return status; } } if (func) func(certificate_list[i], trusted_cas[j], 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; } memset(&vparams, 0, sizeof(vparams)); vparams.now = now; vparams.max_path = MAX_VERIFY_DEPTH; vparams.func = func; ret = gnutls_x509_name_constraints_init(&vparams.nc); if (ret < 0) { gnutls_assert(); status |= GNUTLS_CERT_INVALID; return status; } ret = gnutls_x509_tlsfeatures_init(&vparams.tls_feat); if (ret < 0) { gnutls_assert(); status |= GNUTLS_CERT_INVALID; goto cleanup; } /* Verify the last certificate in the certificate path * against the trusted CA certificate list. * * If no CAs are present returns CERT_INVALID. Thus works * in self signed etc certificates. */ output = 0; ret = verify_crt(certificate_list[clist_size - 1], trusted_cas, tcas_size, flags, &output, &vparams, clist_size==1?1:0); if (ret != 1) { /* if the last certificate in the certificate * list is invalid, then the certificate is not * trusted. */ gnutls_assert(); status |= output; status |= GNUTLS_CERT_INVALID; goto cleanup; } /* Verify the certificate path (chain) */ for (i = clist_size - 1; i > 0; i--) { output = 0; if (i - 1 < 0) break; if (purpose != NULL) { ret = _gnutls_check_key_purpose(certificate_list[i], purpose, 1); if (ret != 1) { gnutls_assert(); status |= GNUTLS_CERT_INVALID; status |= GNUTLS_CERT_PURPOSE_MISMATCH; if (func) func(certificate_list[i-1], certificate_list[i], NULL, status); goto cleanup; } } /* note that here we disable this V1 CA flag. So that no version 1 * certificates can exist in a supplied chain. */ if (!(flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT)) { flags |= GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT; } if ((ret = verify_crt(certificate_list[i - 1], &certificate_list[i], 1, flags, &output, &vparams, i==1?1:0)) != 1) { gnutls_assert(); status |= output; status |= GNUTLS_CERT_INVALID; goto cleanup; } } cleanup: gnutls_x509_name_constraints_deinit(vparams.nc); gnutls_x509_tlsfeatures_deinit(vparams.tls_feat); return status; }
/** * gnutls_x509_trust_list_verify_crt2: * @list: The structure of the list * @cert_list: is the certificate list to be verified * @cert_list_size: is the certificate list size * @data: an array of typed data * @elements: the number of data elements * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. * @voutput: will hold the certificate verification output. * @func: If non-null will be called on each chain element verification with the output. * * This function will attempt to verify the given certificate and return * its status. The @voutput parameter will hold an OR'ed sequence of * %gnutls_certificate_status_t flags. When a chain of @cert_list_size with * more than one certificates is provided, the verification status will apply * to the first certificate in the chain that failed verification. The * verification process starts from the end of the chain (from CA to end * certificate). * * Additionally a certificate verification profile can be specified * from the ones in %gnutls_certificate_verification_profiles_t by * ORing the result of GNUTLS_PROFILE_TO_VFLAGS() to the verification * flags. * * The acceptable @data types are %GNUTLS_DT_DNS_HOSTNAME and %GNUTLS_DT_KEY_PURPOSE_OID. * The former accepts as data a null-terminated hostname, and the latter a null-terminated * object identifier (e.g., %GNUTLS_KP_TLS_WWW_SERVER). * If a DNS hostname is provided then this function will compare * the hostname in the certificate against the given. If names do not match the * %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set. In addition it * will consider certificates provided with gnutls_x509_trust_list_add_named_crt(). * * If a key purpose OID is provided and the end-certificate contains the extended key * usage PKIX extension, it will be required to match the provided OID * or be marked for any purpose, otherwise verification will fail with * %GNUTLS_CERT_PURPOSE_MISMATCH status. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. Note that verification failure will not result to an * error code, only @voutput will be updated. * * Since: 3.3.8 **/ int gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, gnutls_x509_crt_t * cert_list, unsigned int cert_list_size, gnutls_typed_vdata_st *data, unsigned int elements, unsigned int flags, unsigned int *voutput, gnutls_verify_output_function func) { int ret; unsigned int i; uint32_t hash; gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; const char *hostname = NULL, *purpose = NULL; unsigned hostname_size = 0; if (cert_list == NULL || cert_list_size < 1) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); for (i=0;i<elements;i++) { if (data[i].type == GNUTLS_DT_DNS_HOSTNAME) { hostname = (void*)data[i].data; if (data[i].size > 0) { hostname_size = data[i].size; } } else if (data[i].type == GNUTLS_DT_KEY_PURPOSE_OID) { purpose = (void*)data[i].data; } } if (hostname) { /* shortcut using the named certs - if any */ unsigned vtmp = 0; if (hostname_size == 0) hostname_size = strlen(hostname); ret = gnutls_x509_trust_list_verify_named_crt(list, cert_list[0], hostname, hostname_size, flags, &vtmp, func); if (ret == 0 && vtmp == 0) { *voutput = vtmp; return 0; } } if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN)) cert_list = _gnutls_sort_clist(sorted, cert_list, &cert_list_size, NULL); cert_list_size = shorten_clist(list, cert_list, cert_list_size); if (cert_list_size <= 0) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); hash = hash_pjw_bare(cert_list[cert_list_size - 1]->raw_issuer_dn. data, cert_list[cert_list_size - 1]->raw_issuer_dn.size); hash %= list->size; ret = check_if_in_blacklist(cert_list, cert_list_size, list->blacklisted, list->blacklisted_size); if (ret != 0) { *voutput = 0; *voutput |= GNUTLS_CERT_REVOKED; *voutput |= GNUTLS_CERT_INVALID; return 0; } *voutput = _gnutls_verify_crt_status(cert_list, cert_list_size, list->node[hash].trusted_cas, list-> node[hash].trusted_ca_size, flags, purpose, func); #define LAST_DN cert_list[cert_list_size-1]->raw_dn #define LAST_IDN cert_list[cert_list_size-1]->raw_issuer_dn if ((*voutput) & GNUTLS_CERT_SIGNER_NOT_FOUND && (LAST_DN.size != LAST_IDN.size || memcmp(LAST_DN.data, LAST_IDN.data, LAST_IDN.size) != 0)) { /* if we couldn't find the issuer, try to see if the last * certificate is in the trusted list and try to verify against * (if it is not self signed) */ hash = hash_pjw_bare(cert_list[cert_list_size - 1]->raw_dn. data, cert_list[cert_list_size - 1]->raw_dn.size); hash %= list->size; *voutput = _gnutls_verify_crt_status(cert_list, cert_list_size, list->node[hash].trusted_cas, list-> node[hash].trusted_ca_size, flags, purpose, func); } #ifdef ENABLE_PKCS11 if ((*voutput & GNUTLS_CERT_SIGNER_NOT_FOUND) && list->pkcs11_token) { /* use the token for verification */ *voutput = _gnutls_pkcs11_verify_crt_status(list->pkcs11_token, cert_list, cert_list_size, purpose, flags, func); if (*voutput != 0) { gnutls_assert(); } } #endif /* End-certificate, key purpose and hostname checks. */ if (purpose) { ret = _gnutls_check_key_purpose(cert_list[0], purpose, 0); if (ret != 1) { gnutls_assert(); *voutput |= GNUTLS_CERT_PURPOSE_MISMATCH|GNUTLS_CERT_INVALID; } } if (hostname) { ret = gnutls_x509_crt_check_hostname2(cert_list[0], hostname, flags); if (ret == 0) *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; } /* CRL checks follow */ if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS)) return 0; /* Check revocation of individual certificates. * start with the last one that we already have its hash */ ret = _gnutls_x509_crt_check_revocation(cert_list [cert_list_size - 1], list->node[hash].crls, list->node[hash].crl_size, func); if (ret == 1) { /* revoked */ *voutput |= GNUTLS_CERT_REVOKED; *voutput |= GNUTLS_CERT_INVALID; return 0; } for (i = 0; i < cert_list_size - 1; i++) { hash = hash_pjw_bare(cert_list[i]->raw_issuer_dn.data, cert_list[i]->raw_issuer_dn.size); hash %= list->size; ret = _gnutls_x509_crt_check_revocation(cert_list[i], list->node[hash]. crls, list->node[hash]. crl_size, func); if (ret < 0) { gnutls_assert(); } else if (ret == 1) { /* revoked */ *voutput |= GNUTLS_CERT_REVOKED; *voutput |= GNUTLS_CERT_INVALID; return 0; } } return 0; }