]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added gnutls_x509_crt_check_email(), gnutls_openpgp_crt_check_email() and GNUTLS_DT_R...
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Wed, 25 Mar 2015 08:42:16 +0000 (09:42 +0100)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Wed, 25 Mar 2015 09:04:18 +0000 (10:04 +0100)
lib/gnutls_cert.c
lib/includes/gnutls/gnutls.h.in
lib/includes/gnutls/openpgp.h
lib/includes/gnutls/x509.h
lib/libgnutls.map
lib/openpgp/compat.c
lib/openpgp/gnutls_openpgp.h
lib/openpgp/pgp.c
lib/x509/Makefile.am
lib/x509/email-verify.c [new file with mode: 0644]
lib/x509/verify-high.c

index ff007b4044d5b965da6afcb6e8bd43fd949067fb..9d32f1d5befaf6766af7eda571f589b791d271f4 100644 (file)
@@ -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:
index faefedb08875eee15c2d9b91406fd0832d917b72..21009d8b9ed4f65204a0b119a97eeb444fa24bd3 100644 (file)
@@ -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 {
index cdd3f809997dd84f4bf8722c1b0e422ff4bf1a03..7236274785d0b176d12cbd5d592d70e56b9b826a 100644 (file)
@@ -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);
 
index c549349fe2b2852cad5a811d3d63b9a7d8d5767f..8c717c5d86489ff0ce44f84b9e50f45c103708fa 100644 (file)
@@ -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,
index 17afce01313048b43119219778b237a58278b082..fe910a3b6c811f941b37af0226d1b3d2f3d4da75 100644 (file)
@@ -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:
        *;
 };
index 87cd5e96fb9aec855d8842ca2e30ba8673490484..4fae6714fdcc63bb2a856e5f188bcaa2a6ccc9cd 100644 (file)
  * 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,
index 3227f98729e08e2ebdf5a73da22f0f9c14472df3..3d37bdc314a3ca745d0609b7988af9d74b858183 100644 (file)
@@ -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 *
index 69f68b2130ec804e170508cc9ce9c8ee42d403d5..1e37c81c2330e283fe471b073af88d1b15b12f45 100644 (file)
@@ -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;
index 95c8fdbbd3982db85c94ea85e93a412e78663652..4da0beb1807846a2ee1a82750bcaae7aeec6b411 100644 (file)
@@ -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 (file)
index 0000000..4e9b0a1
--- /dev/null
@@ -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;
+}
index 6e3a4be20edccb4877a933ea9edcf58163518208..2729976bbebde9f5aeaee75e01d9c3b3c01d4382 100644 (file)
@@ -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))