summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2015-03-25 09:42:16 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2015-03-25 10:04:18 +0100
commitd3fedf9da0df6a6097c368679356b2fc1058c6f3 (patch)
tree182619eb3f5fb86859185a296d1a278e86222c3e /lib
parent9d0ba0df29a2c427454ababbe8f1e7d8791b5f3a (diff)
downloadgnutls-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.c20
-rw-r--r--lib/includes/gnutls/gnutls.h.in4
-rw-r--r--lib/includes/gnutls/openpgp.h2
-rw-r--r--lib/includes/gnutls/x509.h3
-rw-r--r--lib/libgnutls.map2
-rw-r--r--lib/openpgp/compat.c3
-rw-r--r--lib/openpgp/gnutls_openpgp.h14
-rw-r--r--lib/openpgp/pgp.c42
-rw-r--r--lib/x509/Makefile.am3
-rw-r--r--lib/x509/email-verify.c166
-rw-r--r--lib/x509/verify-high.c13
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))