/** * 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; }
void doit(void) { gnutls_x509_crt_t x509; #ifdef ENABLE_OPENPGP gnutls_openpgp_crt_t pgp; #endif gnutls_datum_t data; int ret; ret = global_init(); if (ret < 0) fail("global_init: %d\n", ret); ret = gnutls_x509_crt_init(&x509); if (ret < 0) fail("gnutls_x509_crt_init: %d\n", ret); #ifdef ENABLE_OPENPGP ret = gnutls_openpgp_crt_init(&pgp); if (ret < 0) fail("gnutls_openpgp_crt_init: %d\n", ret); #endif if (debug) success("Testing wildcards...\n"); data.data = (unsigned char *) wildcards; data.size = strlen(wildcards); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "example.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "example.org"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example.net"); if (ret==0) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); if (debug) success("Testing pem1...\n"); data.data = (unsigned char *) pem1; data.size = strlen(pem1); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); if (debug) success("Testing pem2...\n"); data.data = (unsigned char *) pem2; data.size = strlen(pem2); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "*.example.org"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); if (debug) success("Testing pem3...\n"); data.data = (unsigned char *) pem3; data.size = strlen(pem3); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "*.example.org"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); if (debug) success("Testing pem4...\n"); data.data = (unsigned char *) pem4; data.size = strlen(pem4); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname2(x509, "www.example.org", GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo.example.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo.example.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); #ifdef SUPPORT_COMPLEX_WILDCARDS if (debug) success("Testing pem6...\n"); data.data = (unsigned char *) pem6; data.size = strlen(pem6); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo.example.org"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "bar.foo.example.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); if (debug) success("Testing pem7...\n"); data.data = (unsigned char *) pem7; data.size = strlen(pem7); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo.bar.example.org"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foobar.bar.example.org"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foobar.example.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foobazbar.example.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); #endif if (debug) success("Testing pem8...\n"); data.data = (unsigned char *) pem8; data.size = strlen(pem8); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); /* this was passing in old gnutls versions, but that was not a * good idea. See http://permalink.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/7380 * for a discussion. */ ret = gnutls_x509_crt_check_hostname(x509, "www.example.org"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example."); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); /* this was passing in old gnutls versions, but that was not a * good idea. See http://permalink.gmane.org/gmane.comp.encryption.gpg.gnutls.devel/7380 * for a discussion. */ ret = gnutls_x509_crt_check_hostname(x509, "www.example.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example.foo.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); if (debug) success("Testing pem9...\n"); data.data = (unsigned char *) pem9; data.size = strlen(pem9); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "foo.example.org"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "bar.example.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); if (debug) success("Testing pem10...\n"); data.data = (unsigned char *) pem10; data.size = strlen(pem10); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "localhost"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); if (debug) success("Testing pem_too_many...\n"); data.data = (unsigned char *) pem_too_many; data.size = strlen(pem_too_many); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "localhost.gnutls.gnutls.org"); if (ret) fail("%d: Hostname verification should have failed (too many wildcards)\n", __LINE__); if (debug) success("Testing pem-ips...\n"); data.data = (unsigned char *) pem_ips; data.size = strlen(pem_ips); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "127.0.0.2"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "example.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "127.0.0.1"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "192.168.5.1"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "::1"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "fe80::3e97:eff:fe18:359a"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); if (debug) success("Testing multi-cns...\n"); data.data = (unsigned char *) multi_cns; data.size = strlen(multi_cns); ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); if (ret < 0) fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "example.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example2.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); ret = gnutls_x509_crt_check_hostname(x509, "www.example3.com"); if (ret) fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); #ifdef ENABLE_OPENPGP if (debug) success("Testing pem11...\n"); data.data = (unsigned char *) pem11; data.size = strlen(pem11); ret = gnutls_openpgp_crt_import(pgp, &data, GNUTLS_OPENPGP_FMT_BASE64); if (ret < 0) fail("%d: gnutls_openpgp_crt_import: %d\n", __LINE__, ret); ret = gnutls_openpgp_crt_check_hostname(pgp, "test.gnutls.org"); if (!ret) fail("%d: Hostname incorrectly does not match (%d)\n", __LINE__, ret); gnutls_openpgp_crt_deinit(pgp); #endif gnutls_x509_crt_deinit(x509); gnutls_global_deinit(); }
static int tls_connection_verify_peer(gnutls_session_t session) { struct tls_connection *conn; unsigned int status, num_certs, i; struct os_time now; const gnutls_datum_t *certs; gnutls_x509_crt_t cert; gnutls_alert_description_t err; int res; conn = gnutls_session_get_ptr(session); if (!conn->verify_peer) { wpa_printf(MSG_DEBUG, "GnuTLS: No peer certificate verification enabled"); return 0; } wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate"); #if GNUTLS_VERSION_NUMBER >= 0x030300 { gnutls_typed_vdata_st data[1]; unsigned int elements = 0; os_memset(data, 0, sizeof(data)); if (!conn->global->server) { data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID; data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER; elements++; } res = gnutls_certificate_verify_peers(session, data, 1, &status); } #else /* < 3.3.0 */ res = gnutls_certificate_verify_peers2(session, &status); #endif if (res < 0) { wpa_printf(MSG_INFO, "TLS: Failed to verify peer " "certificate chain"); err = GNUTLS_A_INTERNAL_ERROR; goto out; } #if GNUTLS_VERSION_NUMBER >= 0x030104 { gnutls_datum_t info; int ret, type; type = gnutls_certificate_type_get(session); ret = gnutls_certificate_verification_status_print(status, type, &info, 0); if (ret < 0) { wpa_printf(MSG_DEBUG, "GnuTLS: Failed to print verification status"); err = GNUTLS_A_INTERNAL_ERROR; goto out; } wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data); gnutls_free(info.data); } #endif /* GnuTLS 3.1.4 or newer */ certs = gnutls_certificate_get_peers(session, &num_certs); if (certs == NULL || num_certs == 0) { wpa_printf(MSG_INFO, "TLS: No peer certificate chain received"); err = GNUTLS_A_UNKNOWN_CA; goto out; } if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate uses insecure algorithm", TLS_FAIL_BAD_CERTIFICATE); err = GNUTLS_A_INSUFFICIENT_SECURITY; goto out; } if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate not yet valid", TLS_FAIL_NOT_YET_VALID); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } if (status & GNUTLS_CERT_EXPIRED) { wpa_printf(MSG_INFO, "TLS: Certificate expired"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate has expired", TLS_FAIL_EXPIRED); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } gnutls_tls_fail_event(conn, NULL, 0, NULL, "untrusted certificate", TLS_FAIL_UNTRUSTED); err = GNUTLS_A_INTERNAL_ERROR; goto out; } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " "known issuer"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found", TLS_FAIL_UNTRUSTED); err = GNUTLS_A_UNKNOWN_CA; goto out; } if (status & GNUTLS_CERT_REVOKED) { wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate revoked", TLS_FAIL_REVOKED); err = GNUTLS_A_CERTIFICATE_REVOKED; goto out; } if (status != 0) { wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d", status); err = GNUTLS_A_INTERNAL_ERROR; goto out; } if (check_ocsp(conn, session, &err)) goto out; os_get_time(&now); for (i = 0; i < num_certs; i++) { char *buf; size_t len; if (gnutls_x509_crt_init(&cert) < 0) { wpa_printf(MSG_INFO, "TLS: Certificate initialization " "failed"); err = GNUTLS_A_BAD_CERTIFICATE; goto out; } if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { wpa_printf(MSG_INFO, "TLS: Could not parse peer " "certificate %d/%d", i + 1, num_certs); gnutls_x509_crt_deinit(cert); err = GNUTLS_A_BAD_CERTIFICATE; goto out; } gnutls_x509_crt_get_dn(cert, NULL, &len); len++; buf = os_malloc(len + 1); if (buf) { buf[0] = buf[len] = '\0'; gnutls_x509_crt_get_dn(cert, buf, &len); } wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", i + 1, num_certs, buf); if (conn->global->event_cb) { struct wpabuf *cert_buf = NULL; union tls_event_data ev; #ifdef CONFIG_SHA256 u8 hash[32]; const u8 *_addr[1]; size_t _len[1]; #endif /* CONFIG_SHA256 */ os_memset(&ev, 0, sizeof(ev)); if (conn->global->cert_in_cb) { cert_buf = wpabuf_alloc_copy(certs[i].data, certs[i].size); ev.peer_cert.cert = cert_buf; } #ifdef CONFIG_SHA256 _addr[0] = certs[i].data; _len[0] = certs[i].size; if (sha256_vector(1, _addr, _len, hash) == 0) { ev.peer_cert.hash = hash; ev.peer_cert.hash_len = sizeof(hash); } #endif /* CONFIG_SHA256 */ ev.peer_cert.depth = i; ev.peer_cert.subject = buf; conn->global->event_cb(conn->global->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert_buf); } if (i == 0) { if (conn->suffix_match && !gnutls_x509_crt_check_hostname( cert, conn->suffix_match)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", conn->suffix_match); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Domain suffix mismatch", TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #if GNUTLS_VERSION_NUMBER >= 0x030300 if (conn->domain_match && !gnutls_x509_crt_check_hostname2( cert, conn->domain_match, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", conn->domain_match); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Domain mismatch", TLS_FAIL_DOMAIN_MISMATCH); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #endif /* >= 3.3.0 */ /* TODO: validate altsubject_match. * For now, any such configuration is rejected in * tls_connection_set_params() */ #if GNUTLS_VERSION_NUMBER < 0x030300 /* * gnutls_certificate_verify_peers() not available, so * need to check EKU separately. */ if (!conn->global->server && !server_eku_purpose(cert)) { wpa_printf(MSG_WARNING, "GnuTLS: No server EKU"); gnutls_tls_fail_event( conn, &certs[i], i, buf, "No server EKU", TLS_FAIL_BAD_CERTIFICATE); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #endif /* < 3.3.0 */ } if (!conn->disable_time_checks && (gnutls_x509_crt_get_expiration_time(cert) < now.sec || gnutls_x509_crt_get_activation_time(cert) > now.sec)) { wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " "not valid at this time", i + 1, num_certs); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Certificate is not valid at this time", TLS_FAIL_EXPIRED); gnutls_x509_crt_deinit(cert); os_free(buf); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } os_free(buf); gnutls_x509_crt_deinit(cert); } if (conn->global->event_cb != NULL) conn->global->event_cb(conn->global->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); return 0; out: conn->failed++; gnutls_alert_send(session, GNUTLS_AL_FATAL, err); return GNUTLS_E_CERTIFICATE_ERROR; }