/*
* 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
*
*/
#include "gnutls_int.h"
#include
#include
#include
#include "errors.h"
#include
#include
#include
/* 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 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;
}