/** * gnutls_openpgp_key_check_hostname - This function compares the given hostname with the hostname in the key * @key: should contain an gnutls_openpgp_key_t structure * @hostname: A null terminated string that contains a DNS name * * This function will check if the given key's owner matches * the given hostname. This is a basic implementation of the matching * described in RFC2818 (HTTPS), which takes into account wildcards. * * Returns non zero on success, and zero on failure. * **/ int gnutls_openpgp_key_check_hostname (gnutls_openpgp_key_t key, const char *hostname) { char dnsname[MAX_CN]; size_t dnsnamesize; int ret = 0; int i = 0; /* Check through all included names. */ for (i = 0; !(ret < 0); i++) { dnsnamesize = sizeof (dnsname); ret = gnutls_openpgp_key_get_name (key, i, dnsname, &dnsnamesize); if (_gnutls_hostname_compare (dnsname, hostname)) { return 1; } } /* not found a matching name */ return 0; }
/* compare hostname against certificate, taking account of wildcards * return 1 on success or 0 on error * * note: certnamesize is required as X509 certs can contain embedded NULs in * the strings such as CN or subjectAltName. * * @level: is used for recursion. Use 0 when you call this function. */ int _gnutls_hostname_compare (const char *certname, size_t certnamesize, const char *hostname, int level) { if (level > 5) return 0; /* find the first different character */ for (; *certname && *hostname && c_toupper (*certname) == c_toupper (*hostname); certname++, hostname++, certnamesize--) ; /* the strings are the same */ if (certnamesize == 0 && *hostname == '\0') return 1; if (*certname == '*') { /* a wildcard certificate */ certname++; certnamesize--; while (1) { /* Use a recursive call to allow multiple wildcards */ if (_gnutls_hostname_compare (certname, certnamesize, hostname, level+1)) return 1; /* wildcards are only allowed to match a single domain component or component fragment */ if (*hostname == '\0' || *hostname == '.') break; hostname++; } return 0; } return 0; }
/** * gnutls_x509_crt_check_hostname: * @cert: should contain an gnutls_x509_crt_t structure * @hostname: A null terminated string that contains a DNS name * * This function will check if the given certificate's subject matches * the given hostname. This is a basic implementation of the matching * described in RFC2818 (HTTPS), which takes into account wildcards, * and the DNSName/IPAddress subject alternative name PKIX extension. * * Returns: non-zero for a successful match, and zero on failure. **/ int gnutls_x509_crt_check_hostname (gnutls_x509_crt_t cert, const char *hostname) { char dnsname[MAX_CN]; size_t dnsnamesize; int found_dnsname = 0; int ret = 0; int i = 0; /* try matching against: * 1) a DNS name as an alternative name (subjectAltName) extension * in the certificate * 2) the common name (CN) in the certificate * * either of these may be of the form: *.domain.tld * * only try (2) if there is no subjectAltName extension of * type dNSName */ /* Check through all included subjectAltName extensions, comparing * against all those of type dNSName. */ for (i = 0; !(ret < 0); i++) { dnsnamesize = sizeof (dnsname); ret = gnutls_x509_crt_get_subject_alt_name (cert, i, dnsname, &dnsnamesize, NULL); if (ret == GNUTLS_SAN_DNSNAME) { found_dnsname = 1; if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname, 0)) { return 1; } } } if (!found_dnsname) { /* not got the necessary extension, use CN instead */ dnsnamesize = sizeof (dnsname); if (gnutls_x509_crt_get_dn_by_oid (cert, OID_X520_COMMON_NAME, 0, 0, dnsname, &dnsnamesize) < 0) { /* got an error, can't find a name */ return 0; } if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname, 0)) { return 1; } } /* not found a matching name */ return 0; }
/** * From gnutls and spice red_peer.c * TODO: switch to gnutls and get rid of this * * This function will check if the given certificate's subject matches * the given hostname. This is a basic implementation of the matching * described in RFC2818 (HTTPS), which takes into account wildcards, * and the DNSName/IPAddress subject alternative name PKIX extension. * * Returns: 1 for a successful match, and 0 on failure. **/ static int verify_hostname(X509* cert, const char *hostname) { GENERAL_NAMES* subject_alt_names; int found_dns_name = 0; struct in_addr addr; int addr_len = 0; int cn_match = 0; if (!cert) { SPICE_DEBUG("warning: no cert!"); return 0; } // only IpV4 supported if (inet_aton(hostname, &addr)) { addr_len = sizeof(struct in_addr); } /* try matching against: * 1) a DNS name as an alternative name (subjectAltName) extension * in the certificate * 2) the common name (CN) in the certificate * * either of these may be of the form: *.domain.tld * * only try (2) if there is no subjectAltName extension of * type dNSName */ /* Check through all included subjectAltName extensions, comparing * against all those of type dNSName. */ subject_alt_names = (GENERAL_NAMES*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (subject_alt_names) { int num_alts = sk_GENERAL_NAME_num(subject_alt_names); int i; for (i = 0; i < num_alts; i++) { const GENERAL_NAME* name = sk_GENERAL_NAME_value(subject_alt_names, i); if (name->type == GEN_DNS) { found_dns_name = 1; if (_gnutls_hostname_compare((char *)ASN1_STRING_data(name->d.dNSName), ASN1_STRING_length(name->d.dNSName), hostname)) { SPICE_DEBUG("alt name match=%s", ASN1_STRING_data(name->d.dNSName)); GENERAL_NAMES_free(subject_alt_names); return 1; } } else if (name->type == GEN_IPADD) { int alt_ip_len = ASN1_STRING_length(name->d.iPAddress); found_dns_name = 1; if ((addr_len == alt_ip_len)&& !memcmp(ASN1_STRING_data(name->d.iPAddress), &addr, addr_len)) { SPICE_DEBUG("alt name IP match=%s", inet_ntoa(*((struct in_addr*)ASN1_STRING_data(name->d.dNSName)))); GENERAL_NAMES_free(subject_alt_names); return 1; } } } GENERAL_NAMES_free(subject_alt_names); } if (found_dns_name) { SPICE_DEBUG("warning: SubjectAltName mismatch"); return 0; } /* extracting commonNames */ X509_NAME* subject = X509_get_subject_name(cert); if (subject) { int pos = -1; X509_NAME_ENTRY* cn_entry; ASN1_STRING* cn_asn1; while ((pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos)) != -1) { cn_entry = X509_NAME_get_entry(subject, pos); if (!cn_entry) { continue; } cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry); if (!cn_asn1) { continue; } if (_gnutls_hostname_compare((char*)ASN1_STRING_data(cn_asn1), ASN1_STRING_length(cn_asn1), hostname)) { SPICE_DEBUG("common name match=%s", (char*)ASN1_STRING_data(cn_asn1)); cn_match = 1; break; } } } if (!cn_match) SPICE_DEBUG("warning: common name mismatch"); return cn_match; }