summaryrefslogtreecommitdiffstats
path: root/lib/x509/dn.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/x509/dn.c1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/lib/x509/dn.c b/lib/x509/dn.c
new file mode 100644
index 0000000..8d89f56
--- /dev/null
+++ b/lib/x509/dn.c
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <str.h>
+#include <common.h>
+#include <num.h>
+
+/* This file includes all the required to parse an X.509 Distriguished
+ * Name (you need a parser just to read a name in the X.509 protocols!!!)
+ */
+
+static int append_elements(asn1_node asn1_struct, const char *asn1_rdn_name, gnutls_buffer_st *str, int k1, unsigned last)
+{
+ int k2, result, max_k2;
+ int len;
+ uint8_t value[MAX_STRING_LEN];
+ char tmpbuffer1[MAX_NAME_SIZE];
+ char tmpbuffer2[MAX_NAME_SIZE];
+ char tmpbuffer3[MAX_NAME_SIZE];
+ const char *ldap_desc;
+ char oid[MAX_OID_SIZE];
+ gnutls_datum_t td = { NULL, 0 };
+ gnutls_datum_t tvd = { NULL, 0 };
+
+ /* create a string like "tbsCertList.issuer.rdnSequence.?1"
+ */
+ if (asn1_rdn_name[0] != 0)
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d",
+ asn1_rdn_name, k1);
+ else
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d",
+ k1);
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer1, value, &len);
+
+ if (result != ASN1_VALUE_NOT_FOUND && result != ASN1_SUCCESS) { /* expected */
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ k2 = 0;
+
+ result = asn1_number_of_elements(asn1_struct, tmpbuffer1, &max_k2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ do { /* Move to the attribute type and values
+ */
+ k2++;
+
+ if (tmpbuffer1[0] != 0)
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "%s.?%d", tmpbuffer1, k2);
+ else
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "?%d", k2);
+
+ /* Try to read the RelativeDistinguishedName attributes.
+ */
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer2, value,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ if (result != ASN1_VALUE_NOT_FOUND && result != ASN1_SUCCESS) { /* expected */
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3),
+ ".type");
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer3, oid,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the Value
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3),
+ ".value");
+
+ len = 0;
+
+ result =
+ _gnutls_x509_read_value(asn1_struct,
+ tmpbuffer3, &tvd);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+#define STR_APPEND(y) if ((result=_gnutls_buffer_append_str( str, y)) < 0) { \
+ gnutls_assert(); \
+ goto cleanup; \
+}
+#define DATA_APPEND(x,y) if ((result=_gnutls_buffer_append_data( str, x,y)) < 0) { \
+ gnutls_assert(); \
+ goto cleanup; \
+}
+ /* The encodings of adjoining RelativeDistinguishedNames are separated
+ * by a comma character (',' ASCII 44).
+ */
+
+ ldap_desc =
+ gnutls_x509_dn_oid_name(oid,
+ GNUTLS_X509_DN_OID_RETURN_OID);
+
+ STR_APPEND(ldap_desc);
+ STR_APPEND("=");
+
+ /* DirectoryString by definition in RFC 5280 cannot be empty.
+ * If asn_node.value_len = 0 the parser correctly rejects such DirectoryString.
+ * However, if asn_node.value contains ASN.1 TLV triplet with length = 0,
+ * such DirectoryString is not rejected by the parser as the node itself is not empty.
+ * Explicitly reject DirectoryString in such case.
+ */
+ const char *asn_desc = _gnutls_oid_get_asn_desc(oid);
+ if (asn_desc && !strcmp(asn_desc, "PKIX1.DirectoryString") && tvd.data[1] == 0) {
+ gnutls_assert();
+ result = GNUTLS_E_ASN1_VALUE_NOT_VALID;
+ _gnutls_debug_log("Empty DirectoryString\n");
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_dn_to_string(oid, tvd.data,
+ tvd.size, &td);
+ if (result < 0) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Cannot parse OID: '%s' with value '%s'\n",
+ oid, _gnutls_bin2hex(tvd.data,
+ tvd.size,
+ tmpbuffer3,
+ sizeof
+ (tmpbuffer3),
+ NULL));
+ goto cleanup;
+ }
+
+ DATA_APPEND(td.data, td.size);
+ _gnutls_free_datum(&td);
+ _gnutls_free_datum(&tvd);
+
+ /* Where there is a multi-valued RDN, the outputs from adjoining
+ * AttributeTypeAndValues are separated by a plus ('+' ASCII 43)
+ * character.
+ */
+ if (k2 < max_k2) {
+ STR_APPEND("+");
+ } else if (!last) {
+ STR_APPEND(",");
+ }
+ }
+ while (1);
+
+ result = 0;
+
+ cleanup:
+ _gnutls_free_datum(&td);
+ _gnutls_free_datum(&tvd);
+ return result;
+}
+
+int
+_gnutls_x509_get_dn(asn1_node asn1_struct,
+ const char *asn1_rdn_name, gnutls_datum_t * dn,
+ unsigned flags)
+{
+ gnutls_buffer_st out_str;
+ int i, k1, result;
+
+ _gnutls_buffer_init(&out_str);
+
+ result = asn1_number_of_elements(asn1_struct, asn1_rdn_name, &k1);
+ if (result != ASN1_SUCCESS) {
+ if (result == ASN1_ELEMENT_NOT_FOUND || result == ASN1_VALUE_NOT_FOUND) {
+ result = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ } else {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ }
+ goto cleanup;
+ }
+
+ if (k1 == 0) {
+ gnutls_assert();
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ }
+
+ if (flags & GNUTLS_X509_DN_FLAG_COMPAT) {
+ for (i=0;i<k1;i++) {
+ result = append_elements(asn1_struct, asn1_rdn_name, &out_str, i+1, (i==(k1-1))?1:0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ } else {
+ while (k1 > 0) {
+ result = append_elements(asn1_struct, asn1_rdn_name, &out_str, k1, k1==1?1:0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ k1--;
+ }
+ }
+
+ return _gnutls_buffer_to_datum(&out_str, dn, 1);
+
+ cleanup:
+ _gnutls_buffer_clear(&out_str);
+ return result;
+
+}
+
+
+/* Parses an X509 DN in the asn1_struct, and puts the output into
+ * the string buf. The output is an LDAP encoded DN.
+ *
+ * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence".
+ * That is to point in the rndSequence.
+ */
+int
+_gnutls_x509_parse_dn(asn1_node asn1_struct,
+ const char *asn1_rdn_name, char *buf,
+ size_t * buf_size, unsigned flags)
+{
+ int ret;
+ gnutls_datum_t dn = {NULL, 0};
+
+ if (buf_size == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (*buf_size > 0 && buf)
+ buf[0] = 0;
+ else
+ *buf_size = 0;
+
+ ret = _gnutls_x509_get_dn(asn1_struct, asn1_rdn_name, &dn, flags);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (dn.size >= (unsigned int) *buf_size) {
+ gnutls_assert();
+ *buf_size = dn.size + 1;
+ ret = GNUTLS_E_SHORT_MEMORY_BUFFER;
+ goto cleanup;
+ }
+
+ assert(dn.data != NULL);
+
+ if (buf) {
+ memcpy(buf, dn.data, dn.size);
+ buf[dn.size] = 0;
+ *buf_size = dn.size;
+ } else
+ *buf_size = dn.size + 1;
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&dn);
+ return ret;
+}
+
+/* Parses an X509 DN in the asn1_struct, and searches for the
+ * given OID in the DN.
+ *
+ * If raw_flag == 0, the output will be encoded in the LDAP way. (#hex for non printable)
+ * Otherwise the raw DER data are returned.
+ *
+ * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence".
+ * That is to point in the rndSequence.
+ *
+ * indx specifies which OID to return. Ie 0 means return the first specified
+ * OID found, 1 the second etc.
+ */
+int
+_gnutls_x509_parse_dn_oid(asn1_node asn1_struct,
+ const char *asn1_rdn_name,
+ const char *given_oid, int indx,
+ unsigned int raw_flag, gnutls_datum_t * out)
+{
+ int k2, k1, result;
+ char tmpbuffer1[MAX_NAME_SIZE];
+ char tmpbuffer2[MAX_NAME_SIZE];
+ char tmpbuffer3[MAX_NAME_SIZE];
+ gnutls_datum_t td;
+ uint8_t value[256];
+ char oid[MAX_OID_SIZE];
+ int len;
+ int i = 0;
+
+ k1 = 0;
+ do {
+
+ k1++;
+ /* create a string like "tbsCertList.issuer.rdnSequence.?1"
+ */
+ if (asn1_rdn_name[0] != 0)
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d",
+ asn1_rdn_name, k1);
+ else
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d",
+ k1);
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer1, value, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ }
+
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ k2 = 0;
+
+ do { /* Move to the attribute type and values
+ */
+ k2++;
+
+ if (tmpbuffer1[0] != 0)
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "%s.?%d", tmpbuffer1, k2);
+ else
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "?%d", k2);
+
+ /* Try to read the RelativeDistinguishedName attributes.
+ */
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer2, value,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ break;
+ }
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3),
+ ".type");
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer3, oid,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (strcmp(oid, given_oid) == 0 && indx == i++) { /* Found the OID */
+
+ /* Read the Value
+ */
+ _gnutls_str_cpy(tmpbuffer3,
+ sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3,
+ sizeof(tmpbuffer3),
+ ".value");
+
+ result =
+ _gnutls_x509_read_value(asn1_struct,
+ tmpbuffer3,
+ &td);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (raw_flag != 0) {
+ out->data = td.data;
+ out->size = td.size;
+ return 0;
+
+ } else { /* parse data. raw_flag == 0 */
+ result =
+ _gnutls_x509_dn_to_string(oid,
+ td.
+ data,
+ td.
+ size,
+ out);
+
+ _gnutls_free_datum(&td);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+
+ } /* raw_flag == 0 */
+ }
+ }
+ while (1);
+
+ }
+ while (1);
+
+ gnutls_assert();
+
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ cleanup:
+ return result;
+}
+
+
+/* Parses an X509 DN in the asn1_struct, and returns the requested
+ * DN OID.
+ *
+ * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence".
+ * That is to point in the rndSequence.
+ *
+ * indx specifies which OID to return. Ie 0 means return the first specified
+ * OID found, 1 the second etc.
+ */
+int
+_gnutls_x509_get_dn_oid(asn1_node asn1_struct,
+ const char *asn1_rdn_name,
+ int indx, void *_oid, size_t * sizeof_oid)
+{
+ int k2, k1, result;
+ char tmpbuffer1[MAX_NAME_SIZE];
+ char tmpbuffer2[MAX_NAME_SIZE];
+ char tmpbuffer3[MAX_NAME_SIZE];
+ char value[256];
+ char oid[MAX_OID_SIZE];
+ int len;
+ int i = 0;
+
+ k1 = 0;
+ do {
+
+ k1++;
+ /* create a string like "tbsCertList.issuer.rdnSequence.?1"
+ */
+ if (asn1_rdn_name[0] != 0)
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d",
+ asn1_rdn_name, k1);
+ else
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d",
+ k1);
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer1, value, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ }
+
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ k2 = 0;
+
+ do { /* Move to the attribute type and values
+ */
+ k2++;
+
+ if (tmpbuffer1[0] != 0)
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "%s.?%d", tmpbuffer1, k2);
+ else
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "?%d", k2);
+
+ /* Try to read the RelativeDistinguishedName attributes.
+ */
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer2, value,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ break;
+ }
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3),
+ ".type");
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer3, oid,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (indx == i++) { /* Found the OID */
+
+ len = strlen(oid) + 1;
+
+ if (*sizeof_oid < (unsigned) len) {
+ *sizeof_oid = len;
+ gnutls_assert();
+ return
+ GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ memcpy(_oid, oid, len);
+ *sizeof_oid = len - 1;
+
+ return 0;
+ }
+ }
+ while (1);
+
+ }
+ while (1);
+
+ gnutls_assert();
+
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ cleanup:
+ return result;
+}
+
+/* This will write the AttributeTypeAndValue field. The data must be already DER encoded.
+ * 'multi' must be (0) if writing an AttributeTypeAndValue, and 1 if Attribute.
+ * In all cases only one value is written.
+ */
+static int
+_gnutls_x509_write_attribute(const char *given_oid,
+ asn1_node asn1_struct, const char *where,
+ const void *_data, int sizeof_data)
+{
+ char tmp[128];
+ int result;
+
+ /* write the data (value)
+ */
+
+ _gnutls_str_cpy(tmp, sizeof(tmp), where);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".value");
+
+ result = asn1_write_value(asn1_struct, tmp, _data, sizeof_data);
+ if (result < 0) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* write the type
+ */
+ _gnutls_str_cpy(tmp, sizeof(tmp), where);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".type");
+
+ result = asn1_write_value(asn1_struct, tmp, given_oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+
+/* Decodes an X.509 Attribute (if multi==1) or an AttributeTypeAndValue
+ * otherwise.
+ *
+ * octet_string should be non-zero if we are to decode octet strings after
+ * decoding.
+ *
+ * The output is allocated and stored in value.
+ */
+int
+_gnutls_x509_decode_and_read_attribute(asn1_node asn1_struct,
+ const char *where, char *oid,
+ int oid_size,
+ gnutls_datum_t * value, int multi,
+ int octet_string)
+{
+ char tmpbuffer[128];
+ int len, result;
+
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer, sizeof(tmpbuffer), where);
+ _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), ".type");
+
+ len = oid_size - 1;
+ result = asn1_read_value(asn1_struct, tmpbuffer, oid, &len);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ return result;
+ }
+
+ /* Read the Value
+ */
+
+ _gnutls_str_cpy(tmpbuffer, sizeof(tmpbuffer), where);
+ _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), ".value");
+
+ if (multi)
+ _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), "s.?1"); /* .values.?1 */
+
+ if (octet_string)
+ result =
+ _gnutls_x509_read_string(asn1_struct, tmpbuffer, value,
+ ASN1_ETYPE_OCTET_STRING, 0);
+ else
+ result =
+ _gnutls_x509_read_value(asn1_struct, tmpbuffer, value);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+
+}
+
+/* Sets an X509 DN in the asn1_struct, and puts the given OID in the DN.
+ * The input is assumed to be raw data.
+ *
+ * asn1_rdn_name must be a string in the form "tbsCertificate.issuer".
+ * That is to point before the rndSequence.
+ *
+ */
+int
+_gnutls_x509_set_dn_oid(asn1_node asn1_struct,
+ const char *asn1_name, const char *given_oid,
+ int raw_flag, const char *name, int sizeof_name)
+{
+ int result;
+ char tmp[MAX_NAME_SIZE], asn1_rdn_name[MAX_NAME_SIZE];
+
+ if (sizeof_name == 0 || name == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* create the rdnSequence
+ */
+ result =
+ asn1_write_value(asn1_struct, asn1_name, "rdnSequence", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (asn1_name[0] != 0) {
+ _gnutls_str_cpy(asn1_rdn_name, sizeof(asn1_rdn_name), asn1_name);
+ _gnutls_str_cat(asn1_rdn_name, sizeof(asn1_rdn_name),
+ ".rdnSequence");
+ } else {
+ _gnutls_str_cpy(asn1_rdn_name, sizeof(asn1_rdn_name), "rdnSequence");
+ }
+
+ /* create a new element
+ */
+ result = asn1_write_value(asn1_struct, asn1_rdn_name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ _gnutls_str_cpy(tmp, sizeof(tmp), asn1_rdn_name);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".?LAST");
+
+ /* create the set with only one element
+ */
+ result = asn1_write_value(asn1_struct, tmp, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+
+ /* Encode and write the data
+ */
+ _gnutls_str_cpy(tmp, sizeof(tmp), asn1_rdn_name);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".?LAST.?LAST");
+
+ if (!raw_flag) {
+ result =
+ _gnutls_x509_encode_and_write_attribute(given_oid,
+ asn1_struct,
+ tmp, name,
+ sizeof_name,
+ 0);
+ } else {
+ result =
+ _gnutls_x509_write_attribute(given_oid, asn1_struct,
+ tmp, name, sizeof_name);
+ }
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+
+/**
+ * gnutls_x509_rdn_get:
+ * @idn: should contain a DER encoded RDN sequence
+ * @buf: a pointer to a structure to hold the peer's name
+ * @buf_size: holds the size of @buf
+ *
+ * This function will return the name of the given RDN sequence. The
+ * name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in
+ * RFC4514.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_rdn_get2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is
+ * updated if the provided buffer is not long enough, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_rdn_get(const gnutls_datum_t * idn,
+ char *buf, size_t * buf_size)
+{
+ int ret;
+ gnutls_datum_t out;
+
+ ret = gnutls_x509_rdn_get2(idn, &out, GNUTLS_X509_DN_FLAG_COMPAT);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_copy_string(&out, (void*)buf, buf_size);
+ gnutls_free(out.data);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_rdn_get2:
+ * @idn: should contain a DER encoded RDN sequence
+ * @str: a datum that will hold the name
+ * @flags: zero of %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will return the name of the given RDN sequence. The
+ * name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in
+ * RFC4514.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is
+ * updated if the provided buffer is not long enough, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_rdn_get2(const gnutls_datum_t * idn,
+ gnutls_datum_t *str, unsigned flags)
+{
+ int ret;
+ gnutls_x509_dn_t dn;
+
+ ret = gnutls_x509_dn_init(&dn);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_x509_dn_import(dn, idn);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_dn_get_str2(dn, str, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_x509_dn_deinit(dn);
+ return ret;
+}
+
+/**
+ * gnutls_x509_rdn_get_by_oid:
+ * @idn: should contain a DER encoded RDN sequence
+ * @oid: an Object Identifier
+ * @indx: In case multiple same OIDs exist in the RDN indicates which
+ * to send. Use 0 for the first one.
+ * @raw_flag: If non-zero then the raw DER data are returned.
+ * @buf: a pointer to a structure to hold the peer's name
+ * @buf_size: holds the size of @buf
+ *
+ * This function will return the name of the given Object identifier,
+ * of the RDN sequence. The name will be encoded using the rules
+ * from RFC4514.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is
+ * updated if the provided buffer is not long enough, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_rdn_get_by_oid(const gnutls_datum_t * idn, const char *oid,
+ unsigned indx, unsigned int raw_flag,
+ void *buf, size_t * buf_size)
+{
+ int result;
+ asn1_node dn = NULL;
+ gnutls_datum_t td;
+
+ if (buf_size == 0) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Name", &dn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&dn, idn->data, idn->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ /* couldn't decode DER */
+ gnutls_assert();
+ asn1_delete_structure(&dn);
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _gnutls_x509_parse_dn_oid(dn, "rdnSequence", oid, indx,
+ raw_flag, &td);
+
+ asn1_delete_structure(&dn);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ return _gnutls_strdatum_to_buf(&td, buf, buf_size);
+}
+
+/**
+ * gnutls_x509_rdn_get_oid:
+ * @idn: should contain a DER encoded RDN sequence
+ * @indx: Indicates which OID to return. Use 0 for the first one.
+ * @buf: a pointer to a structure to hold the peer's name OID
+ * @buf_size: holds the size of @buf
+ *
+ * This function will return the specified Object identifier, of the
+ * RDN sequence.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is
+ * updated if the provided buffer is not long enough, otherwise a
+ * negative error value.
+ *
+ * Since: 2.4.0
+ **/
+int
+gnutls_x509_rdn_get_oid(const gnutls_datum_t * idn,
+ unsigned indx, void *buf, size_t * buf_size)
+{
+ int result;
+ asn1_node dn = NULL;
+
+ if (buf_size == 0) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Name", &dn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&dn, idn->data, idn->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ /* couldn't decode DER */
+ gnutls_assert();
+ asn1_delete_structure(&dn);
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _gnutls_x509_get_dn_oid(dn, "rdnSequence", indx, buf,
+ buf_size);
+
+ asn1_delete_structure(&dn);
+ return result;
+}
+
+/*
+ * Compares the DER encoded part of a DN.
+ *
+ * Returns 1 if the DN's match and (0) if they don't match. Otherwise
+ * a negative error code is returned to indicate error.
+ */
+int
+_gnutls_x509_compare_raw_dn(const gnutls_datum_t * dn1,
+ const gnutls_datum_t * dn2)
+{
+ int ret;
+ gnutls_datum_t str1, str2;
+
+ /* Simple case of completely identical? */
+
+ if (dn1->size == dn2->size) {
+ if (memcmp(dn1->data, dn2->data, dn2->size) == 0) {
+ return 1;
+ }
+ }
+
+ /* RFC5280 (https://tools.ietf.org/html/rfc5280#section-7.1)
+ * requires that the LDAP StringPrep profile and caseIgnoreMatch
+ * must be used for this comparison. We do not use that but
+ * instead we do a simpler comparison that ignores the tags used
+ * such as `UTF8String` and `PrintableString`. */
+
+ if ((dn1->size == 0) || (dn2->size == 0)) {
+ gnutls_assert();
+ return 0;
+ }
+
+ ret = gnutls_x509_rdn_get2(dn1, &str1, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return 0;
+ }
+
+ ret = gnutls_x509_rdn_get2(dn2, &str2, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_free_datum(&str1);
+ return 0;
+ }
+
+ if (str1.size != str2.size) {
+ ret = 0;
+ goto cleanup;
+ }
+ if (memcmp(str1.data, str2.data, str2.size) != 0) {
+ gnutls_assert();
+ ret = 0;
+ goto cleanup;
+ }
+
+ ret = 1; /* they match */
+
+cleanup:
+ _gnutls_free_datum(&str1);
+ _gnutls_free_datum(&str2);
+
+ return ret;
+}