summaryrefslogtreecommitdiff
path: root/lib/x509
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2014-02-13 14:47:14 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2014-02-17 13:45:28 +0100
commit40a9a1a450c2b3c045e7b9bfb8f3c3f13715f35d (patch)
tree9533d5642dc4eb3e005187ee841b2b4854a3a6f6 /lib/x509
parent4b4b7bf4614c9ad3dc553d385004f07da8becf9d (diff)
downloadgnutls-40a9a1a450c2b3c045e7b9bfb8f3c3f13715f35d.tar.gz
Added support for name constraints X.509 extension.
This allows to generate and read the name constraints extension, as well as check against the DNSNAME value.
Diffstat (limited to 'lib/x509')
-rw-r--r--lib/x509/Makefile.am1
-rw-r--r--lib/x509/extensions.c53
-rw-r--r--lib/x509/name_constraints.c641
-rw-r--r--lib/x509/output.c78
-rw-r--r--lib/x509/x509.c21
-rw-r--r--lib/x509/x509_int.h5
6 files changed, 775 insertions, 24 deletions
diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am
index 4fc657969b..b2e5a17a48 100644
--- a/lib/x509/Makefile.am
+++ b/lib/x509/Makefile.am
@@ -56,6 +56,7 @@ libgnutls_x509_la_SOURCES = \
x509.c x509_dn.c \
x509_int.h \
x509_write.c \
+ name_constraints.c \
verify-high.c \
verify-high2.c \
verify-high.h
diff --git a/lib/x509/extensions.c b/lib/x509/extensions.c
index 4748421b68..5b2da0e38c 100644
--- a/lib/x509/extensions.c
+++ b/lib/x509/extensions.c
@@ -820,8 +820,8 @@ int _gnutls_x509_ext_gen_keyUsage(uint16_t usage, gnutls_datum_t * der_ext)
return 0;
}
-static int
-write_new_general_name(ASN1_TYPE ext, const char *ext_name,
+int
+_gnutls_write_general_name(ASN1_TYPE ext, const char *ext_name,
gnutls_x509_subject_alt_name_t type,
const void *data, unsigned int data_size)
{
@@ -829,12 +829,6 @@ write_new_general_name(ASN1_TYPE ext, const char *ext_name,
int result;
char name[128];
- result = asn1_write_value(ext, ext_name, "NEW", 1);
- if (result != ASN1_SUCCESS) {
- gnutls_assert();
- return _gnutls_asn2err(result);
- }
-
switch (type) {
case GNUTLS_SAN_DNSNAME:
str = "dNSName";
@@ -853,21 +847,13 @@ write_new_general_name(ASN1_TYPE ext, const char *ext_name,
return GNUTLS_E_INTERNAL_ERROR;
}
- if (ext_name[0] == 0) { /* no dot */
- _gnutls_str_cpy(name, sizeof(name), "?LAST");
- } else {
- _gnutls_str_cpy(name, sizeof(name), ext_name);
- _gnutls_str_cat(name, sizeof(name), ".?LAST");
- }
-
- result = asn1_write_value(ext, name, str, 1);
+ result = asn1_write_value(ext, ext_name, str, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
- _gnutls_str_cat(name, sizeof(name), ".");
- _gnutls_str_cat(name, sizeof(name), str);
+ snprintf(name, sizeof(name), "%s.%s", ext_name, str);
result = asn1_write_value(ext, name, data, data_size);
if (result != ASN1_SUCCESS) {
@@ -879,6 +865,37 @@ write_new_general_name(ASN1_TYPE ext, const char *ext_name,
return 0;
}
+static int
+write_new_general_name(ASN1_TYPE ext, const char *ext_name,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data, unsigned int data_size)
+{
+ int result;
+ char name[128];
+
+ result = asn1_write_value(ext, ext_name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (ext_name[0] == 0) { /* no dot */
+ _gnutls_str_cpy(name, sizeof(name), "?LAST");
+ } else {
+ _gnutls_str_cpy(name, sizeof(name), ext_name);
+ _gnutls_str_cat(name, sizeof(name), ".?LAST");
+ }
+
+ result = _gnutls_write_general_name(ext, name, type,
+ data, data_size);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
/* Convert the given name to GeneralNames in a DER encoded extension.
* This is the same as subject alternative name.
*/
diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c
new file mode 100644
index 0000000000..bdcfdefaf9
--- /dev/null
+++ b/lib/x509/name_constraints.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2014 Free Software Foundation, Inc.
+ *
+ * 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/>
+ *
+ */
+
+/* Functions on X.509 Certificate parsing
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_datum.h>
+#include <gnutls_global.h>
+#include <gnutls_errors.h>
+#include <common.h>
+#include <gnutls_x509.h>
+#include <x509_b64.h>
+#include <x509_int.h>
+#include <libtasn1.h>
+
+/* Name constraints is limited to DNS names.
+ */
+typedef struct gnutls_name_constraints_st {
+ struct name_constraints_node_st * permitted;
+ struct name_constraints_node_st * excluded;
+} gnutls_name_constraints_st;
+
+typedef struct name_constraints_node_st {
+ unsigned type;
+ gnutls_datum_t name;
+ struct name_constraints_node_st *next;
+} name_constraints_node_st;
+
+static int extract_name_constraints(ASN1_TYPE c2, const char *vstr,
+ name_constraints_node_st ** _nc)
+{
+ int ret;
+ char tmpstr[128];
+ unsigned indx = 0;
+ gnutls_datum_t tmp = { NULL, 0 };
+ unsigned int type;
+ struct name_constraints_node_st *nc, *prev;
+
+ nc = prev = *_nc;
+
+ do {
+ indx++;
+ snprintf(tmpstr, sizeof(tmpstr), "%s.?%u.base", vstr, indx);
+
+ ret =
+ _gnutls_parse_general_name2(c2, tmpstr, -1, &tmp, &type, 0);
+
+ if (ret < 0)
+ break;
+
+ if (type != GNUTLS_SAN_DNSNAME && type != GNUTLS_SAN_RFC822NAME
+ && type != GNUTLS_SAN_DN && type != GNUTLS_SAN_URI) {
+ gnutls_assert();
+ ret = GNUTLS_E_ILLEGAL_PARAMETER;
+ goto cleanup;
+ }
+
+ nc = gnutls_malloc(sizeof(struct name_constraints_node_st));
+ if (nc == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ memcpy(&nc->name, &tmp, sizeof(gnutls_datum_t));
+ nc->type = type;
+ nc->next = NULL;
+
+ if (prev == NULL) {
+ *_nc = prev = nc;
+ } else {
+ prev->next = nc;
+ prev = nc;
+ }
+
+ tmp.data = NULL;
+ } while (ret >= 0);
+
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ if (ret < 0) {
+ nc = *_nc;
+ while (nc != NULL) {
+ prev = nc->next;
+ free(nc->name.data);
+ free(nc);
+ nc = prev;
+ }
+ *_nc = NULL;
+ }
+ gnutls_free(tmp.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_name_constraints:
+ * @crt: should contain a #gnutls_x509_crt_t structure
+ * @nc: The nameconstraints intermediate structure
+ * @critical: the extension status
+ *
+ * This function will return an intermediate structure containing
+ * the name constraints of the provided CA certificate. That
+ * structure can be used in combination with gnutls_x509_name_constraints_check()
+ * to verify whether a server's name is in accordance with the constraints.
+ *
+ * Note that @nc must be initialized prior to calling this function.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_crt_get_name_constraints(gnutls_x509_crt_t crt,
+ gnutls_x509_name_constraints_t nc,
+ unsigned int *critical)
+{
+ int result, ret;
+ gnutls_datum_t der = { NULL, 0 };
+ ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _gnutls_x509_crt_get_extension(crt, "2.5.29.30", 0, &der,
+ critical);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (der.size == 0 || der.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.NameConstraints", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_der_decoding(&c2, der.data, der.size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = extract_name_constraints(c2, "permittedSubtrees", &nc->permitted);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = extract_name_constraints(c2, "excludedSubtrees", &nc->excluded);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ _gnutls_free_datum(&der);
+ asn1_delete_structure(&c2);
+
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_name_constraints_deinit:
+ * @nc: The nameconstraints structure
+ *
+ * This function will deinitialize a name constraints structure.
+ *
+ * Since: 3.3.0
+ **/
+void gnutls_x509_name_constraints_deinit(gnutls_x509_name_constraints_t nc)
+{
+ name_constraints_node_st * next, *t;
+
+ t = nc->permitted;
+ while (t != NULL) {
+ next = t->next;
+ free(t->name.data);
+ free(t);
+ t = next;
+ }
+
+ t = nc->excluded;
+ while (t != NULL) {
+ next = t->next;
+ free(t->name.data);
+ free(t);
+ t = next;
+ }
+}
+
+/**
+ * gnutls_x509_name_constraints_init:
+ * @nc: The nameconstraints structure
+ *
+ * This function will initialize a name constraints structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_init(gnutls_x509_name_constraints_t *nc)
+{
+ *nc = gnutls_calloc(1, sizeof(struct gnutls_name_constraints_st));
+ if (*nc == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ return 0;
+}
+
+static
+int name_constraints_add(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * name,
+ unsigned permitted)
+{
+ struct name_constraints_node_st * tmp, *prev = NULL;
+ int ret;
+
+ if (type != GNUTLS_SAN_DNSNAME && type != GNUTLS_SAN_RFC822NAME &&
+ type != GNUTLS_SAN_DN && type != GNUTLS_SAN_URI)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (type == GNUTLS_SAN_DNSNAME && name->size > 0 && name->data[0] == '.') {
+ _gnutls_debug_log("DNSNAME constraints cannot start with '.'. They must contain a domain name\n");
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ if (permitted != 0)
+ prev = tmp = nc->permitted;
+ else
+ prev = tmp = nc->excluded;
+
+ while(tmp != NULL) {
+ tmp = tmp->next;
+ if (tmp != NULL)
+ prev = tmp;
+ }
+
+ tmp = gnutls_malloc(sizeof(struct name_constraints_node_st));
+ if (tmp == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ tmp->next = NULL;
+ tmp->type = type;
+ ret = _gnutls_set_datum(&tmp->name, name->data, name->size);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(tmp);
+ return ret;
+ }
+
+ if (prev == NULL) {
+ if (permitted != 0)
+ nc->permitted = tmp;
+ else
+ nc->excluded = tmp;
+ } else
+ prev->next = tmp;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_name_constraints_add_permitted:
+ * @nc: The nameconstraints structure
+ * @type: The type of the constraints
+ * @name: The data of the constraints
+ *
+ * This function will add a name constraint to the list of permitted
+ * constraints.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_add_permitted(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * name)
+{
+ return name_constraints_add(nc, type, name, 1);
+}
+
+/**
+ * gnutls_x509_name_constraints_add_excluded:
+ * @nc: The nameconstraints structure
+ * @type: The type of the constraints
+ * @name: The data of the constraints
+ *
+ * This function will add a name constraint to the list of excluded
+ * constraints.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_add_excluded(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * name)
+{
+ return name_constraints_add(nc, type, name, 0);
+}
+
+/**
+ * gnutls_x509_crt_set_name_constraints:
+ * @crt: The certificate structure
+ * @nc: The nameconstraints structure
+ *
+ * This function will set the provided name constraints to
+ * the certificate extension list. This extension is always
+ * marked as critical.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_crt_set_name_constraints(gnutls_x509_crt_t crt,
+ gnutls_x509_name_constraints_t nc)
+{
+int ret, result;
+gnutls_datum_t der_data;
+uint8_t null = 0;
+ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
+struct name_constraints_node_st * tmp;
+
+ if (nc->permitted == NULL && nc->excluded == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.NameConstraints", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (nc->permitted == NULL) {
+ asn1_write_value(c2, "permittedSubtrees", NULL, 0);
+ } else {
+ tmp = nc->permitted;
+ do {
+ result = asn1_write_value(c2, "permittedSubtrees", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(c2, "permittedSubtrees.?LAST.maximum", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(c2, "permittedSubtrees.?LAST.minimum", &null, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = _gnutls_write_general_name(c2, "permittedSubtrees.?LAST.base",
+ tmp->type, tmp->name.data, tmp->name.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ tmp = tmp->next;
+ } while(tmp != NULL);
+ }
+
+ if (nc->excluded == NULL) {
+ asn1_write_value(c2, "excludedSubtrees", NULL, 0);
+ } else {
+ tmp = nc->excluded;
+ do {
+ result = asn1_write_value(c2, "excludedSubtrees", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(c2, "excludedSubtrees.?LAST.maximum", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(c2, "excludedSubtrees.?LAST.minimum", &null, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = _gnutls_write_general_name(c2, "excludedSubtrees.?LAST.base",
+ tmp->type, tmp->name.data, tmp->name.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ tmp = tmp->next;
+ } while(tmp != NULL);
+
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", &der_data, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.30", &der_data, 1);
+
+ _gnutls_free_datum(&der_data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ crt->use_extensions = 1;
+
+cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+
+}
+
+static
+unsigned ends_with(const gnutls_datum_t * str, const gnutls_datum_t * suffix)
+{
+ if (suffix->size >= str->size)
+ return 0;
+
+ if (memcmp(str->data + str->size - suffix->size, suffix->data, suffix->size) == 0 &&
+ str->data[str->size - suffix->size -1] == '.')
+ return 1;
+
+ return 0;
+}
+
+static unsigned dnsname_matches(const gnutls_datum_t *name, const gnutls_datum_t *suffix)
+{
+ _gnutls_hard_log("matching %.*s with constraint %.*s\n", name->size, name->data,
+ suffix->size, suffix->data);
+
+ if (suffix->size == name->size && memcmp(suffix->data, name->data, suffix->size) == 0)
+ return 1; /* match */
+
+ return ends_with(name, suffix);
+}
+
+/**
+ * gnutls_x509_name_constraints_check:
+ * @nc: the extracted name constraints structure
+ * @type: the type of the constraint to check (of type gnutls_x509_subject_alt_name_t)
+ * @name: the name to be checked
+ *
+ * This function will check the provided name against the constraints in
+ * @nc using the RFC5280 rules. Currently this function is limited to DNS
+ * names (of type %GNUTLS_SAN_DNSNAME).
+ *
+ * Returns: zero if the provided name is not acceptable, and non-zero otherwise.
+ *
+ * Since: 3.3.0
+ **/
+unsigned gnutls_x509_name_constraints_check(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * name)
+{
+unsigned i;
+int ret;
+unsigned rtype;
+unsigned allowed_found = 0;
+gnutls_datum_t rname;
+
+ if (type != GNUTLS_SAN_DNSNAME)
+ return gnutls_assert_val(0);
+
+ /* check restrictions */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname);
+ if (ret >= 0 && rtype != type)
+ continue;
+
+ if (rname.size == 0)
+ continue;
+
+ if (dnsname_matches(name, &rname) != 0)
+ return gnutls_assert_val(0); /* rejected */
+ } while(ret == 0);
+
+ /* check allowed */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_permitted(nc, i++, &rtype, &rname);
+ if (ret >= 0 && rtype != type)
+ continue;
+
+ if (rname.size == 0)
+ continue;
+
+ allowed_found = 1;
+
+ if (dnsname_matches(name, &rname) != 0)
+ return 1; /* accepted */
+ } while(ret == 0);
+
+ if (allowed_found != 0) /* there are allowed directives but this host wasn't found */
+ return gnutls_assert_val(0);
+
+ return 1;
+}
+
+/**
+ * gnutls_x509_name_constraints_get_permitted:
+ * @nc: the extracted name constraints structure
+ * @idx: the index of the constraint
+ * @type: the type of the constraint (of type gnutls_x509_subject_alt_name_t)
+ * @name: the name in the constraint (of the specific type)
+ *
+ * This function will return an intermediate structure containing
+ * the name constraints of the provided CA certificate. That
+ * structure can be used in combination with gnutls_x509_name_constraints_check()
+ * to verify whether a server's name is in accordance with the constraints.
+ *
+ * The name should be treated as constant and valid for the lifetime of @nc.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_get_permitted(gnutls_x509_name_constraints_t nc,
+ unsigned idx,
+ unsigned *type, gnutls_datum_t * name)
+{
+ unsigned int i;
+ struct name_constraints_node_st * tmp = nc->permitted;
+
+ for (i = 0; i < idx; i++) {
+ if (tmp == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ tmp = tmp->next;
+ }
+
+ if (tmp == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ *type = tmp->type;
+ *name = tmp->name;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_name_constraints_get_excluded:
+ * @nc: the extracted name constraints structure
+ * @idx: the index of the constraint
+ * @type: the type of the constraint (of type gnutls_x509_subject_alt_name_t)
+ * @name: the name in the constraint (of the specific type)
+ *
+ * This function will return an intermediate structure containing
+ * the name constraints of the provided CA certificate. That
+ * structure can be used in combination with gnutls_x509_name_constraints_check()
+ * to verify whether a server's name is in accordance with the constraints.
+ *
+ * The name should be treated as constant and valid for the lifetime of @nc.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_get_excluded(gnutls_x509_name_constraints_t nc,
+ unsigned idx,
+ unsigned *type, gnutls_datum_t * name)
+{
+ unsigned int i;
+ struct name_constraints_node_st * tmp = nc->excluded;
+
+ for (i = 0; i < idx; i++) {
+ if (tmp == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ tmp = tmp->next;
+ }
+
+ if (tmp == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ *type = tmp->type;
+ *name = tmp->name;
+
+ return 0;
+}
diff --git a/lib/x509/output.c b/lib/x509/output.c
index f88b1a802d..fd3fd1a72e 100644
--- a/lib/x509/output.c
+++ b/lib/x509/output.c
@@ -158,6 +158,69 @@ static void print_proxy(gnutls_buffer_st * str, gnutls_x509_crt_t cert)
}
}
+static void print_nc(gnutls_buffer_st * str, const char* prefix, gnutls_x509_crt_t cert)
+{
+ gnutls_x509_name_constraints_t nc;
+ int ret;
+ unsigned critical, idx = 0;
+ gnutls_datum_t name;
+ unsigned type;
+
+ ret = gnutls_x509_name_constraints_init(&nc);
+ if (ret < 0)
+ return;
+
+ ret = gnutls_x509_crt_get_name_constraints(cert, nc, &critical);
+ if (ret < 0)
+ goto cleanup;
+
+ do {
+ ret = gnutls_x509_name_constraints_get_permitted(nc, idx++, &type, &name);
+
+ if (ret >= 0) {
+ if (idx == 1)
+ addf(str, _("%s\t\t\tPermitted:\n"), prefix);
+
+ if (type == GNUTLS_SAN_DNSNAME) {
+ addf(str, _("%s\t\t\tDNSname:%s\n"), prefix, name.data);
+ } else if (type == GNUTLS_SAN_RFC822NAME) {
+ addf(str, _("%s\t\t\tRFC822Name:%s\n"), prefix, name.data);
+ } else if (type == GNUTLS_SAN_URI) {
+ addf(str, _("%s\t\t\tURI:%s\n"), prefix, name.data);
+ } else if (type == GNUTLS_SAN_DN) {
+ addf(str, _("%s\t\t\tdirectoryName:"), prefix);
+ _gnutls_buffer_hexprint(str, name.data, name.size);
+ adds(str, _(" \n"));
+ }
+ }
+ } while (ret == 0);
+
+ idx = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_excluded(nc, idx++, &type, &name);
+
+ if (ret >= 0) {
+ if (idx == 1)
+ addf(str, _("%s\t\t\tExcluded:\n"), prefix);
+
+ if (type == GNUTLS_SAN_DNSNAME) {
+ addf(str, _("%s\t\t\tDNSname:%s\n"), prefix, name.data);
+ } else if (type == GNUTLS_SAN_RFC822NAME) {
+ addf(str, _("%s\t\t\tRFC822Name:%s\n"), prefix, name.data);
+ } else if (type == GNUTLS_SAN_URI) {
+ addf(str, _("%s\t\t\tURI:%s\n"), prefix, name.data);
+ } else if (type == GNUTLS_SAN_DN) {
+ addf(str, _("%s\t\t\tdirectoryName:"), prefix);
+ _gnutls_buffer_hexprint(str, name.data, name.size);
+ adds(str, _(" \n"));
+ }
+ }
+ } while (ret == 0);
+
+cleanup:
+ gnutls_x509_name_constraints_deinit(nc);
+}
+
static void print_aia(gnutls_buffer_st * str, gnutls_x509_crt_t cert)
{
int err;
@@ -982,7 +1045,7 @@ print_extensions(gnutls_buffer_st * str, const char *prefix, int type,
int keyusage_idx = 0;
int keypurpose_idx = 0;
int ski_idx = 0;
- int aki_idx = 0;
+ int aki_idx = 0, nc_idx = 0;
int crldist_idx = 0, pkey_usage_period_idx = 0;
char pfx[16];
@@ -1225,6 +1288,19 @@ print_extensions(gnutls_buffer_st * str, const char *prefix, int type,
if (type == TYPE_CRT)
print_aia(str, cert.crt);
+ } else if (strcmp(oid, "2.5.29.30") == 0) {
+ if (nc_idx) {
+ addf(str,
+ "error: more than one name constraints extension\n");
+ continue;
+ }
+ nc_idx++;
+
+ addf(str, _("%s\t\tName Constraints (%s):\n"), prefix,
+ critical ? _("critical") : _("not critical"));
+
+ if (type == TYPE_CRT)
+ print_nc(str, prefix, cert.crt);
} else {
char *buffer;
size_t extlen = 0;
diff --git a/lib/x509/x509.c b/lib/x509/x509.c
index cf9afa5554..24722cff2b 100644
--- a/lib/x509/x509.c
+++ b/lib/x509/x509.c
@@ -1108,6 +1108,13 @@ inline static int is_type_printable(int type)
/* returns the type and the name on success.
* Type is also returned as a parameter in case of an error.
+ *
+ * @seq: in case of GeneralNames it will return the corresponding name.
+ * in case of GeneralName, it must be -1
+ * @dname: the name returned
+ * @ret_type: The type of the name
+ * @othername_oid: if the name is AnotherName return the OID
+ *
*/
int
_gnutls_parse_general_name2(ASN1_TYPE src, const char *src_name,
@@ -1121,12 +1128,16 @@ _gnutls_parse_general_name2(ASN1_TYPE src, const char *src_name,
char choice_type[128];
gnutls_x509_subject_alt_name_t type;
- seq++; /* 0->1, 1->2 etc */
+ if (seq != -1) {
+ seq++; /* 0->1, 1->2 etc */
- if (src_name[0] != 0)
- snprintf(nptr, sizeof(nptr), "%s.?%u", src_name, seq);
- else
- snprintf(nptr, sizeof(nptr), "?%u", seq);
+ if (src_name[0] != 0)
+ snprintf(nptr, sizeof(nptr), "%s.?%u", src_name, seq);
+ else
+ snprintf(nptr, sizeof(nptr), "?%u", seq);
+ } else {
+ snprintf(nptr, sizeof(nptr), "%s", src_name);
+ }
len = sizeof(choice_type);
result = asn1_read_value(src, nptr, choice_type, &len);
diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h
index ae3871c779..0389de8133 100644
--- a/lib/x509/x509_int.h
+++ b/lib/x509/x509_int.h
@@ -215,6 +215,11 @@ _gnutls_x509_ext_gen_number(const uint8_t * nuber, size_t nr_size,
gnutls_datum_t * der_ext);
+int
+_gnutls_write_general_name(ASN1_TYPE ext, const char *ext_name,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data, unsigned int data_size);
+
int _gnutls_x509_ext_gen_basicConstraints(int CA, int pathLenConstraint,
gnutls_datum_t * der_ext);
int _gnutls_x509_ext_gen_keyUsage(uint16_t usage,