diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2015-03-25 09:42:16 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2015-03-25 10:04:18 +0100 |
commit | d3fedf9da0df6a6097c368679356b2fc1058c6f3 (patch) | |
tree | 182619eb3f5fb86859185a296d1a278e86222c3e /lib | |
parent | 9d0ba0df29a2c427454ababbe8f1e7d8791b5f3a (diff) | |
download | gnutls-d3fedf9da0df6a6097c368679356b2fc1058c6f3.tar.gz |
Added gnutls_x509_crt_check_email(), gnutls_openpgp_crt_check_email() and GNUTLS_DT_RFC822NAME
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gnutls_cert.c | 20 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 4 | ||||
-rw-r--r-- | lib/includes/gnutls/openpgp.h | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/x509.h | 3 | ||||
-rw-r--r-- | lib/libgnutls.map | 2 | ||||
-rw-r--r-- | lib/openpgp/compat.c | 3 | ||||
-rw-r--r-- | lib/openpgp/gnutls_openpgp.h | 14 | ||||
-rw-r--r-- | lib/openpgp/pgp.c | 42 | ||||
-rw-r--r-- | lib/x509/Makefile.am | 3 | ||||
-rw-r--r-- | lib/x509/email-verify.c | 166 | ||||
-rw-r--r-- | lib/x509/verify-high.c | 13 |
11 files changed, 256 insertions, 16 deletions
diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index ff007b4044..9d32f1d5be 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -506,6 +506,7 @@ _gnutls_x509_get_raw_crt_expiration_time(const gnutls_datum_t * cert) -*/ static int _gnutls_openpgp_crt_verify_peers(gnutls_session_t session, + gnutls_x509_subject_alt_name_t type, const char *hostname, unsigned int *status) { @@ -547,7 +548,7 @@ _gnutls_openpgp_crt_verify_peers(gnutls_session_t session, /* Verify certificate */ ret = - _gnutls_openpgp_verify_key(cred, hostname, + _gnutls_openpgp_verify_key(cred, type, hostname, &info->raw_certificate_list[0], peer_certificate_list_size, verify_flags, @@ -653,8 +654,8 @@ gnutls_typed_vdata_st data; * using gnutls_certificate_set_verify_flags(). See the documentation * of gnutls_certificate_verify_peers2() for details in the verification process. * - * 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 + * The acceptable @data types are %GNUTLS_DT_DNS_HOSTNAME, %GNUTLS_DT_RFC822NAME and %GNUTLS_DT_KEY_PURPOSE_OID. + * The former two accept as data a null-terminated hostname or email address, 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 @@ -675,7 +676,7 @@ gnutls_certificate_verify_peers(gnutls_session_t session, { cert_auth_info_t info; const char *hostname = NULL; - unsigned i; + unsigned i, type = 0; CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); @@ -696,11 +697,18 @@ gnutls_certificate_verify_peers(gnutls_session_t session, case GNUTLS_CRT_OPENPGP: for (i=0;i<elements;i++) { if (data[i].type == GNUTLS_DT_DNS_HOSTNAME) { - hostname = (void*)data[i].data; + hostname = (char*)data[i].data; + type = GNUTLS_SAN_DNSNAME; + break; + } else if (data[i].type == GNUTLS_DT_RFC822NAME) { + hostname = (char*)data[i].data; + type = GNUTLS_SAN_RFC822NAME; break; } } - return _gnutls_openpgp_crt_verify_peers(session, hostname, + return _gnutls_openpgp_crt_verify_peers(session, + type, + hostname, status); #endif default: diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index faefedb088..21009d8b9e 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -2007,6 +2007,7 @@ int gnutls_certificate_verify_peers3(gnutls_session_t session, * gnutls_vdata_types_t: * @GNUTLS_DT_UNKNOWN: Unknown data type. * @GNUTLS_DT_DNS_HOSTNAME: The data contain a null-terminated DNS hostname. + * @GNUTLS_DT_RFC822NAME: The data contain a null-terminated email address. * @GNUTLS_DT_KEY_PURPOSE_OID: The data contain a null-terminated key purpose OID. * * Enumeration of different key exchange algorithms. @@ -2014,7 +2015,8 @@ int gnutls_certificate_verify_peers3(gnutls_session_t session, typedef enum { GNUTLS_DT_UNKNOWN = 0, GNUTLS_DT_DNS_HOSTNAME = 1, - GNUTLS_DT_KEY_PURPOSE_OID = 2 + GNUTLS_DT_KEY_PURPOSE_OID = 2, + GNUTLS_DT_RFC822NAME = 3 } gnutls_vdata_types_t; typedef struct { diff --git a/lib/includes/gnutls/openpgp.h b/lib/includes/gnutls/openpgp.h index cdd3f80999..7236274785 100644 --- a/lib/includes/gnutls/openpgp.h +++ b/lib/includes/gnutls/openpgp.h @@ -112,6 +112,8 @@ 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_check_email(gnutls_openpgp_crt_t key, const char *email, unsigned 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 c549349fe2..8c717c5d86 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -181,6 +181,9 @@ 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_check_email(gnutls_x509_crt_t cert, + const char *email, 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, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 17afce0131..fe910a3b6c 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1026,6 +1026,8 @@ GNUTLS_3_4 gnutls_do_send_supplemental; gnutls_ext_get_data; gnutls_ext_set_data; + gnutls_openpgp_crt_check_email; + gnutls_x509_crt_check_email; local: *; }; diff --git a/lib/openpgp/compat.c b/lib/openpgp/compat.c index 87cd5e96fb..4fae6714fd 100644 --- a/lib/openpgp/compat.c +++ b/lib/openpgp/compat.c @@ -38,13 +38,14 @@ * Verify all signatures in the certificate list. When the key * is not available, the signature is skipped. * - * The return value is one of the CertificateStatus entries. + * Returns: a negative error code on error and %GNUTLS_E_SUCCESS (0) on success. * * NOTE: this function does not verify using any "web of trust". You * may use GnuPG for that purpose, or any other external PGP application. -*/ int _gnutls_openpgp_verify_key(const gnutls_certificate_credentials_t cred, + gnutls_x509_subject_alt_name_t type, const char *hostname, const gnutls_datum_t * cert_list, int cert_list_length, diff --git a/lib/openpgp/gnutls_openpgp.h b/lib/openpgp/gnutls_openpgp.h index 3227f98729..3d37bdc314 100644 --- a/lib/openpgp/gnutls_openpgp.h +++ b/lib/openpgp/gnutls_openpgp.h @@ -56,12 +56,14 @@ _gnutls_openpgp_request_key(gnutls_session_t, const gnutls_certificate_credentials_t cred, uint8_t * key_fpr, int key_fpr_size); -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 verify_flags, - unsigned int *status); +int +_gnutls_openpgp_verify_key(const gnutls_certificate_credentials_t cred, + gnutls_x509_subject_alt_name_t type, + const char *hostname, + const gnutls_datum_t * cert_list, + 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 69f68b2130..1e37c81c23 100644 --- a/lib/openpgp/pgp.c +++ b/lib/openpgp/pgp.c @@ -645,6 +645,48 @@ gnutls_openpgp_crt_check_hostname2(gnutls_openpgp_crt_t key, return 0; } +/** + * gnutls_openpgp_crt_check_email: + * @key: should contain a #gnutls_openpgp_crt_t type + * @email: A null terminated string that contains an RFC822 address (email) + * @flags: gnutls_certificate_verify_flags + * + * This function will check if the given key's owner matches the + * given email address. + * + * Returns: non-zero for a successful match, and zero on failure. + **/ +int +gnutls_openpgp_crt_check_email(gnutls_openpgp_crt_t key, + const char *email, unsigned flags) +{ + char rfc822name[MAX_CN]; + size_t rfc822name_size; + int ret = 0; + int i; + + /* Check through all included names. */ + for (i = 0; !(ret < 0); i++) { + rfc822name_size = sizeof(rfc822name); + ret = + gnutls_openpgp_crt_get_name(key, i, rfc822name, + &rfc822name_size); + + if (ret == 0) { + /* Length returned by gnutls_openpgp_crt_get_name includes + the terminating (0). */ + rfc822name_size--; + + if (_gnutls_hostname_compare + (rfc822name, rfc822name_size, email, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) + return 1; + } + } + + /* not found a matching name */ + return 0; +} + unsigned int _gnutls_get_pgp_key_usage(unsigned int cdk_usage) { unsigned int usage = 0; diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index 95c8fdbbd3..4da0beb180 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -59,7 +59,8 @@ libgnutls_x509_la_SOURCES = \ verify-high.c \ verify-high2.c \ verify-high.h \ - x509_ext.c + x509_ext.c \ + email-verify.c if ENABLE_OCSP libgnutls_x509_la_SOURCES += ocsp.c ocsp_output.c diff --git a/lib/x509/email-verify.c b/lib/x509/email-verify.c new file mode 100644 index 0000000000..4e9b0a1672 --- /dev/null +++ b/lib/x509/email-verify.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2003-2012 Free Software Foundation, Inc. + * Copyright (C) 2002 Andrew McDonald + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include <gnutls_int.h> +#include <gnutls_str.h> +#include <x509_int.h> +#include <common.h> +#include <gnutls_errors.h> +#include <system.h> +#include <gnutls-idna.h> + +static int has_embedded_null(const char *str, unsigned size) +{ + if (strlen(str) != size) + return 1; + return 0; +} + +/** + * gnutls_x509_crt_check_email: + * @cert: should contain an gnutls_x509_crt_t type + * @email: A null terminated string that contains an email address (RFC822) + * @flags: should be zero + * + * This function will check if the given certificate's subject matches + * the given email address. + * + * Returns: non-zero for a successful match, and zero on failure. + **/ +int +gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, + const char *email, unsigned int flags) +{ + char rfc822name[MAX_CN]; + size_t rfc822namesize; + int found_rfc822name = 0; + int ret = 0, rc; + int i = 0; + char *a_email; + char *a_rfc822name; + + /* convert the provided email to ACE-Labels domain. */ + rc = idna_to_ascii_8z (email, &a_email, 0); + if (rc != IDNA_SUCCESS) { + _gnutls_debug_log("unable to convert email %s to IDNA format: %s\n", email, idna_strerror (rc)); + a_email = (char*)email; + } + + /* try matching against: + * 1) an address as an alternative name (subjectAltName) extension + * in the certificate + * 2) the EMAIL field in the certificate + * + * only try (2) if there is no subjectAltName extension of + * type RFC822Name, and there is a single EMAIL. + */ + + /* Check through all included subjectAltName extensions, comparing + * against all those of type RFC822Name. + */ + for (i = 0; !(ret < 0); i++) { + + rfc822namesize = sizeof(rfc822name); + ret = gnutls_x509_crt_get_subject_alt_name(cert, i, + rfc822name, + &rfc822namesize, + NULL); + + if (ret == GNUTLS_SAN_RFC822NAME) { + found_rfc822name = 1; + + if (has_embedded_null(rfc822name, rfc822namesize)) { + _gnutls_debug_log("certificate has %s with embedded null in rfc822name\n", rfc822name); + continue; + } + + rc = idna_to_ascii_8z (rfc822name, &a_rfc822name, 0); + if (rc != IDNA_SUCCESS) { + _gnutls_debug_log("unable to convert rfc822name %s to IDNA format: %s\n", rfc822name, idna_strerror (rc)); + continue; + } + + ret = _gnutls_hostname_compare(a_rfc822name, strlen(a_rfc822name), a_email, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); + idn_free(a_rfc822name); + + if (ret != 0) { + ret = 1; + goto cleanup; + } + } + } + + if (!found_rfc822name) { + /* did not get the necessary extension, use CN instead + */ + + /* enforce the RFC6125 (ยง1.8) requirement that only + * a single CN must be present */ + rfc822namesize = sizeof(rfc822name); + ret = gnutls_x509_crt_get_dn_by_oid + (cert, GNUTLS_OID_PKCS9_EMAIL, 1, 0, rfc822name, + &rfc822namesize); + if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + ret = 0; + goto cleanup; + } + + rfc822namesize = sizeof(rfc822name); + ret = gnutls_x509_crt_get_dn_by_oid + (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, rfc822name, + &rfc822namesize); + if (ret < 0) { + ret = 0; + goto cleanup; + } + + if (has_embedded_null(rfc822name, rfc822namesize)) { + _gnutls_debug_log("certificate has EMAIL %s with embedded null in name\n", rfc822name); + ret = 0; + goto cleanup; + } + + rc = idna_to_ascii_8z (rfc822name, &a_rfc822name, 0); + if (rc != IDNA_SUCCESS) { + _gnutls_debug_log("unable to convert EMAIL %s to IDNA format: %s\n", rfc822name, idna_strerror (rc)); + ret = 0; + goto cleanup; + } + + ret = _gnutls_hostname_compare(a_rfc822name, strlen(a_rfc822name), a_email, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); + + idn_free(a_rfc822name); + + if (ret != 0) { + ret = 1; + goto cleanup; + } + } + + /* not found a matching name + */ + ret = 0; + cleanup: + if (a_email != email) { + idn_free(a_email); + } + return ret; +} diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c index 6e3a4be20e..2729976bbe 100644 --- a/lib/x509/verify-high.c +++ b/lib/x509/verify-high.c @@ -1106,7 +1106,7 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, unsigned int i; uint32_t hash; gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; - const char *hostname = NULL, *purpose = NULL; + const char *hostname = NULL, *purpose = NULL, *email = NULL; unsigned hostname_size = 0; if (cert_list == NULL || cert_list_size < 1) @@ -1118,6 +1118,10 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, if (data[i].size > 0) { hostname_size = data[i].size; } + if (email != NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } else if (data[i].type == GNUTLS_DT_RFC822NAME) { + email = (void*)data[i].data; + if (hostname != NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); } else if (data[i].type == GNUTLS_DT_KEY_PURPOSE_OID) { purpose = (void*)data[i].data; } @@ -1220,6 +1224,13 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; } + if (email) { + ret = + gnutls_x509_crt_check_email(cert_list[0], email, 0); + if (ret == 0) + *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; + } + /* CRL checks follow */ if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS)) |