From: Nikos Mavrogiannopoulos Date: Wed, 2 Apr 2014 08:51:46 +0000 (+0200) Subject: Added verification flag to disable wildcard checking X-Git-Tag: gnutls_3_3_0~72 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=74c840c8725d53e49fda22166df0115b8ab2bd07;p=thirdparty%2Fgnutls.git Added verification flag to disable wildcard checking This adds the verification flag GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS, and gnutls_x509_crt_check_hostname2(), gnutls_openpgp_crt_check_hostname2().  --- diff --git a/NEWS b/NEWS index 2f558c13b6..0b0ba4850b 100644 --- a/NEWS +++ b/NEWS @@ -93,6 +93,7 @@ That option enables (when running on FIPS140-enabled system): than ECRYPT). ** API and ABI modifications: +GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS: Added gnutls_privkey_generate: Added gnutls_pkcs11_crt_is_known: Added gnutls_fips140_mode_enabled: Added @@ -109,6 +110,8 @@ gnutls_privkey_import_ecc_raw: Added gnutls_privkey_import_dsa_raw: Added gnutls_privkey_import_rsa_raw: Added gnutls_privkey_verify_params: Added +gnutls_x509_crt_check_hostname2: Added +gnutls_openpgp_crt_check_hostname2: Added gnutls_x509_name_constraints_init: Added gnutls_x509_name_constraints_deinit: Added gnutls_x509_crt_get_name_constraints: Added diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index 9ad2aef034..1b5f2034e8 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -575,6 +575,7 @@ _gnutls_openpgp_crt_verify_peers(gnutls_session_t session, cert_auth_info_t info; gnutls_certificate_credentials_t cred; int peer_certificate_list_size, ret; + unsigned int verify_flags; CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); @@ -594,6 +595,8 @@ _gnutls_openpgp_crt_verify_peers(gnutls_session_t session, return GNUTLS_E_NO_CERTIFICATE_FOUND; } + verify_flags = cred->verify_flags | session->internals.priorities.additional_verify_flags; + /* generate a list of gnutls_certs based on the auth info * raw certs. */ @@ -609,7 +612,9 @@ _gnutls_openpgp_crt_verify_peers(gnutls_session_t session, ret = _gnutls_openpgp_verify_key(cred, hostname, &info->raw_certificate_list[0], - peer_certificate_list_size, status); + peer_certificate_list_size, + verify_flags, + status); if (ret < 0) { gnutls_assert(); @@ -630,6 +635,8 @@ _gnutls_openpgp_crt_verify_peers(gnutls_session_t session, * values or zero if the certificate is trusted. Note that value in @status * is set only when the return value of this function is success (i.e, failure * to trust a certificate does not imply a negative return value). + * The default verification flags used by this function can be overriden + * using gnutls_certificate_set_verify_flags(). * * If available the OCSP Certificate Status extension will be * utilized by this function. @@ -685,6 +692,8 @@ gnutls_certificate_verify_peers2(gnutls_session_t session, * values or zero if the certificate is trusted. Note that value in @status * is set only when the return value of this function is success (i.e, failure * to trust a certificate does not imply a negative return value). + * The default verification flags used by this function can be overriden + * using gnutls_certificate_set_verify_flags(). * * If the @hostname provided is non-NULL then this function will compare * the hostname in the certificate against the given. The comparison will diff --git a/lib/gnutls_str.c b/lib/gnutls_str.c index d94a54ac23..54b6433fbe 100644 --- a/lib/gnutls_str.c +++ b/lib/gnutls_str.c @@ -604,7 +604,7 @@ hostname_compare_ascii(const char *certname, */ int _gnutls_hostname_compare(const char *certname, - size_t certnamesize, const char *hostname) + size_t certnamesize, const char *hostname, unsigned vflags) { char *p; unsigned i; @@ -614,7 +614,7 @@ _gnutls_hostname_compare(const char *certname, return hostname_compare_raw(certname, certnamesize, hostname); } - if (*certname == '*') { + if (*certname == '*' && !(vflags & GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { /* a wildcard certificate */ /* ensure that we have at least two domain components after @@ -628,7 +628,6 @@ _gnutls_hostname_compare(const char *certname, certnamesize--; while (1) { - /* Use a recursive call to allow multiple wildcards */ if (hostname_compare_ascii(certname, certnamesize, hostname)) return 1; diff --git a/lib/gnutls_str.h b/lib/gnutls_str.h index d497e8d1a3..bc9d8bd66b 100644 --- a/lib/gnutls_str.h +++ b/lib/gnutls_str.h @@ -114,7 +114,7 @@ int _gnutls_hex2bin(const char *hex_data, size_t hex_size, uint8_t * bin_data, size_t * bin_size); int _gnutls_hostname_compare(const char *certname, size_t certnamesize, - const char *hostname); + const char *hostname, unsigned vflags); #define MAX_CN 256 #define MAX_DN 1024 diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index 70d58a5d0c..2afcabf966 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -300,8 +300,8 @@ _gnutls_x509_cert_verify_peers(gnutls_session_t session, if (hostname) { ret = - gnutls_x509_crt_check_hostname(peer_certificate_list - [0], hostname); + gnutls_x509_crt_check_hostname2(peer_certificate_list + [0], hostname, verify_flags); if (ret == 0) *status |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; } diff --git a/lib/includes/gnutls/openpgp.h b/lib/includes/gnutls/openpgp.h index 1fee58fb49..11958b3764 100644 --- a/lib/includes/gnutls/openpgp.h +++ b/lib/includes/gnutls/openpgp.h @@ -110,6 +110,8 @@ int gnutls_openpgp_crt_get_key_id(gnutls_openpgp_crt_t key, int gnutls_openpgp_crt_check_hostname(gnutls_openpgp_crt_t key, const char *hostname); +int gnutls_openpgp_crt_check_hostname2(gnutls_openpgp_crt_t key, + const char *hostname, unsigned int flags); int gnutls_openpgp_crt_get_revoked_status(gnutls_openpgp_crt_t key); diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index f0d6c4e40e..25d662a161 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -171,6 +171,8 @@ int gnutls_x509_crt_get_dn_by_oid(gnutls_x509_crt_t cert, size_t * buf_size); int gnutls_x509_crt_check_hostname(gnutls_x509_crt_t cert, const char *hostname); +int gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert, + const char *hostname, unsigned int flags); int gnutls_x509_crt_get_signature_algorithm(gnutls_x509_crt_t cert); int gnutls_x509_crt_get_signature(gnutls_x509_crt_t cert, @@ -790,6 +792,8 @@ int gnutls_pkcs7_delete_crl(gnutls_pkcs7_t pkcs7, int indx); * this unless you understand the security implications. * @GNUTLS_VERIFY_DISABLE_CRL_CHECKS: Disable checking for validity * using certificate revocation lists or the available OCSP data. + * @GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS: When including a hostname + * check in the verification, do not consider any wildcards. * * Enumeration of different certificate verify flags. Additional * verification profiles can be set using GNUTLS_PROFILE_TO_VFLAGS() @@ -807,6 +811,7 @@ typedef enum gnutls_certificate_verify_flags { GNUTLS_VERIFY_DISABLE_CRL_CHECKS = 1 << 9, GNUTLS_VERIFY_ALLOW_UNSORTED_CHAIN = 1 << 10, GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN = 1 << 11, + GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS = 1 << 12, } gnutls_certificate_verify_flags; /** diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 2b99150e01..8834c093a7 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1008,6 +1008,8 @@ GNUTLS_3_1_0 { gnutls_x509_key_purpose_get; gnutls_x509_ext_import_key_purposes; gnutls_x509_ext_export_key_purposes; + gnutls_x509_crt_check_hostname2; + gnutls_openpgp_crt_check_hostname2; } GNUTLS_3_0_0; GNUTLS_PRIVATE { diff --git a/lib/openpgp/compat.c b/lib/openpgp/compat.c index 7c7bd698e4..87cd5e96fb 100644 --- a/lib/openpgp/compat.c +++ b/lib/openpgp/compat.c @@ -47,7 +47,9 @@ int _gnutls_openpgp_verify_key(const gnutls_certificate_credentials_t cred, const char *hostname, const gnutls_datum_t * cert_list, - int cert_list_length, unsigned int *status) + int cert_list_length, + unsigned int verify_flags, + unsigned int *status) { int ret = 0; gnutls_openpgp_crt_t key = NULL; @@ -96,7 +98,7 @@ _gnutls_openpgp_verify_key(const gnutls_certificate_credentials_t cred, *status |= GNUTLS_CERT_SIGNER_NOT_FOUND; if (hostname) { - ret = gnutls_openpgp_crt_check_hostname(key, hostname); + ret = gnutls_openpgp_crt_check_hostname2(key, hostname, verify_flags); if (ret == 0) *status |= GNUTLS_CERT_UNEXPECTED_OWNER; } diff --git a/lib/openpgp/gnutls_openpgp.h b/lib/openpgp/gnutls_openpgp.h index 8bd04dd735..3227f98729 100644 --- a/lib/openpgp/gnutls_openpgp.h +++ b/lib/openpgp/gnutls_openpgp.h @@ -59,7 +59,9 @@ _gnutls_openpgp_request_key(gnutls_session_t, int _gnutls_openpgp_verify_key(const gnutls_certificate_credentials_t, const char *hostname, const gnutls_datum_t * cert_list, - int cert_list_length, unsigned int *status); + int cert_list_length, + unsigned int verify_flags, + unsigned int *status); int _gnutls_openpgp_fingerprint(const gnutls_datum_t * cert, unsigned char *fpr, size_t * fprlen); time_t _gnutls_openpgp_get_raw_key_creation_time(const gnutls_datum_t * diff --git a/lib/openpgp/pgp.c b/lib/openpgp/pgp.c index c3bb988822..c3a53b4b96 100644 --- a/lib/openpgp/pgp.c +++ b/lib/openpgp/pgp.c @@ -595,6 +595,28 @@ int gnutls_openpgp_crt_get_revoked_status(gnutls_openpgp_crt_t key) int gnutls_openpgp_crt_check_hostname(gnutls_openpgp_crt_t key, const char *hostname) +{ + return gnutls_openpgp_crt_check_hostname2(key, hostname, 0); +} + +/** + * gnutls_openpgp_crt_check_hostname2: + * @key: should contain a #gnutls_openpgp_crt_t structure + * @hostname: A null terminated string that contains a DNS name + * @flags: gnutls_certificate_verify_flags + * + * This function will check if the given key's owner matches the + * given hostname. + * + * Unless, the flag %GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS is specified, + * wildcards are only considered if the domain name consists of three + * components or more, and the wildcard starts at the leftmost position. + * + * Returns: non-zero for a successful match, and zero on failure. + **/ +int +gnutls_openpgp_crt_check_hostname2(gnutls_openpgp_crt_t key, + const char *hostname, unsigned flags) { char dnsname[MAX_CN]; size_t dnsnamesize; @@ -614,7 +636,7 @@ gnutls_openpgp_crt_check_hostname(gnutls_openpgp_crt_t key, dnsnamesize--; if (_gnutls_hostname_compare - (dnsname, dnsnamesize, hostname)) + (dnsname, dnsnamesize, hostname, flags)) return 1; } } diff --git a/lib/x509/rfc2818_hostname.c b/lib/x509/rfc2818_hostname.c index 5412ccf8ab..2236052a9d 100644 --- a/lib/x509/rfc2818_hostname.c +++ b/lib/x509/rfc2818_hostname.c @@ -47,6 +47,33 @@ int gnutls_x509_crt_check_hostname(gnutls_x509_crt_t cert, const char *hostname) { + return gnutls_x509_crt_check_hostname2(cert, hostname, 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 + * @flags: gnutls_certificate_verify_flags + * + * 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. + * + * The comparison may have false-negatives as it is done byte by byte in + * non-ascii names. + * + * Unless, the flag %GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS is specified, + * wildcards are only considered if the domain name consists of three + * components or more, and the wildcard starts at the leftmost position. + * + * Returns: non-zero for a successful match, and zero on failure. + **/ +int +gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert, + const char *hostname, unsigned int flags) +{ char dnsname[MAX_CN]; size_t dnsnamesize; @@ -79,7 +106,7 @@ gnutls_x509_crt_check_hostname(gnutls_x509_crt_t cert, if (ret == GNUTLS_SAN_DNSNAME) { found_dnsname = 1; if (_gnutls_hostname_compare - (dnsname, dnsnamesize, hostname)) { + (dnsname, dnsnamesize, hostname, flags)) { return 1; } } @@ -98,7 +125,7 @@ gnutls_x509_crt_check_hostname(gnutls_x509_crt_t cert, } if (_gnutls_hostname_compare - (dnsname, dnsnamesize, hostname)) { + (dnsname, dnsnamesize, hostname, flags)) { return 1; } } diff --git a/tests/hostname-check.c b/tests/hostname-check.c index a6b50879ce..6a2f8d34fb 100644 --- a/tests/hostname-check.c +++ b/tests/hostname-check.c @@ -772,6 +772,10 @@ void doit(void) 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);