/*
* Copyright (C) 2014-2016 Free Software Foundation, Inc.
* Copyright (C) 2016 Red Hat, 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
*
*/
/* This file contains functions to handle X.509 certificate extensions (the x509-ext API)
*/
#include "gnutls_int.h"
#include
#include "errors.h"
#include
#include
#include
#include "x509_ext_int.h"
#include "virt-san.h"
#include
#define MAX_ENTRIES 64
struct gnutls_subject_alt_names_st {
struct name_st *names;
unsigned int size;
};
/**
* gnutls_subject_alt_names_init:
* @sans: The alternative names
*
* This function will initialize an alternative names structure.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_subject_alt_names_init(gnutls_subject_alt_names_t * sans)
{
*sans = gnutls_calloc(1, sizeof(struct gnutls_subject_alt_names_st));
if (*sans == NULL) {
gnutls_assert();
return GNUTLS_E_MEMORY_ERROR;
}
return 0;
}
static void subject_alt_names_deinit(gnutls_subject_alt_names_t sans)
{
unsigned int i;
for (i = 0; i < sans->size; i++) {
gnutls_free(sans->names[i].san.data);
gnutls_free(sans->names[i].othername_oid.data);
}
gnutls_free(sans->names);
}
/**
* gnutls_subject_alt_names_deinit:
* @sans: The alternative names
*
* This function will deinitialize an alternative names structure.
*
* Since: 3.3.0
**/
void gnutls_subject_alt_names_deinit(gnutls_subject_alt_names_t sans)
{
subject_alt_names_deinit(sans);
gnutls_free(sans);
}
/**
* gnutls_subject_alt_names_get:
* @sans: The alternative names
* @seq: The index of the name to get
* @san_type: Will hold the type of the name (of %gnutls_subject_alt_names_t)
* @san: The alternative name data (should be treated as constant)
* @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME (should be treated as constant)
*
* This function will return a specific alternative name as stored in
* the @sans type. The returned values should be treated as constant
* and valid for the lifetime of @sans.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
* if the index is out of bounds, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_subject_alt_names_get(gnutls_subject_alt_names_t sans,
unsigned int seq, unsigned int *san_type,
gnutls_datum_t * san,
gnutls_datum_t * othername_oid)
{
if (seq >= sans->size)
return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
if (san) {
memcpy(san, &sans->names[seq].san, sizeof(gnutls_datum_t));
}
if (san_type)
*san_type = sans->names[seq].type;
if (othername_oid != NULL && sans->names[seq].type == GNUTLS_SAN_OTHERNAME) {
othername_oid->data = sans->names[seq].othername_oid.data;
othername_oid->size = sans->names[seq].othername_oid.size;
}
return 0;
}
/* This is the same as gnutls_subject_alt_names_set() but will not
* copy the strings. It expects all the provided input to be already
* allocated by gnutls. */
static
int subject_alt_names_set(struct name_st **names,
unsigned int *size,
unsigned int san_type,
gnutls_datum_t * san, char *othername_oid,
unsigned raw)
{
void *tmp;
int ret;
tmp = gnutls_realloc(*names, (*size + 1) * sizeof((*names)[0]));
if (tmp == NULL) {
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
}
*names = tmp;
ret = _gnutls_alt_name_assign_virt_type(&(*names)[*size], san_type, san, othername_oid, raw);
if (ret < 0)
return gnutls_assert_val(ret);
(*size)++;
return 0;
}
/**
* gnutls_subject_alt_names_set:
* @sans: The alternative names
* @san_type: The type of the name (of %gnutls_subject_alt_names_t)
* @san: The alternative name data
* @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
*
* This function will store the specified alternative name in
* the @sans.
*
* Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
* %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_subject_alt_names_set(gnutls_subject_alt_names_t sans,
unsigned int san_type,
const gnutls_datum_t * san,
const char *othername_oid)
{
int ret;
gnutls_datum_t copy;
char *ooc;
ret = _gnutls_set_strdatum(©, san->data, san->size);
if (ret < 0)
return gnutls_assert_val(ret);
if (othername_oid != NULL)
ooc = gnutls_strdup(othername_oid);
else
ooc = NULL;
ret = subject_alt_names_set(&sans->names, &sans->size,
san_type, ©, ooc, 0);
if (ret < 0) {
gnutls_free(copy.data);
return gnutls_assert_val(ret);
}
return 0;
}
/**
* gnutls_x509_ext_import_subject_alt_names:
* @ext: The DER-encoded extension data
* @sans: The alternative names
* @flags: should be zero
*
* This function will export the alternative names in the provided DER-encoded
* SubjectAltName PKIX extension, to a %gnutls_subject_alt_names_t type. @sans
* must be initialized.
*
* This function will succeed even if there no subject alternative names
* in the structure.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_subject_alt_names(const gnutls_datum_t * ext,
gnutls_subject_alt_names_t sans,
unsigned int flags)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int result, ret;
unsigned int i;
gnutls_datum_t san, othername_oid;
unsigned type;
result =
asn1_create_element(_gnutls_get_pkix(), "PKIX1.GeneralNames", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
for (i=0;;i++) {
san.data = NULL;
san.size = 0;
othername_oid.data = NULL;
ret = _gnutls_parse_general_name2(c2, "", i, &san, &type, 0);
if (ret < 0)
break;
if (type == GNUTLS_SAN_OTHERNAME) {
ret =
_gnutls_parse_general_name2(c2, "", i,
&othername_oid,
NULL, 1);
if (ret < 0)
break;
} else if (san.size == 0 || san.data == NULL) {
ret = gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN);
break;
}
ret = subject_alt_names_set(&sans->names, &sans->size,
type, &san,
(char *)othername_oid.data, 1);
if (ret < 0)
break;
}
sans->size = i;
if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
gnutls_free(san.data);
gnutls_free(othername_oid.data);
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_export_subject_alt_names:
* @sans: The alternative names
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the provided alternative names structure to a
* DER-encoded SubjectAltName PKIX extension. The output data in @ext will be allocated using
* gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_subject_alt_names(gnutls_subject_alt_names_t sans,
gnutls_datum_t * ext)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int result, ret;
unsigned i;
result =
asn1_create_element(_gnutls_get_pkix(), "PKIX1.GeneralNames", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
for (i = 0; i < sans->size; i++) {
if (sans->names[i].type == GNUTLS_SAN_OTHERNAME) {
ret = _gnutls_write_new_othername(c2, "", (char*)sans->names[i].othername_oid.data,
sans->names[i].san.data, sans->names[i].san.size);
} else {
ret =
_gnutls_write_new_general_name(c2, "", sans->names[i].type,
sans->names[i].san.data,
sans->names[i].san.size);
}
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
}
ret = _gnutls_x509_der_encode(c2, "", ext, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_import_name_constraints:
* @ext: a DER encoded extension
* @nc: The nameconstraints
* @flags: zero or %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND
*
* This function will return an intermediate type containing
* the name constraints of the provided NameConstraints extension. That
* can be used in combination with gnutls_x509_name_constraints_check()
* to verify whether a server's name is in accordance with the constraints.
*
* When the @flags is set to %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, then if
* the @nc type is empty this function will behave identically as if the flag was not set.
* Otherwise if there are elements in the @nc structure then the
* constraints will be merged with the existing constraints following
* RFC5280 p6.1.4 (excluded constraints will be appended, permitted
* will be intersected).
*
* 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_ext_import_name_constraints(const gnutls_datum_t * ext,
gnutls_x509_name_constraints_t nc,
unsigned int flags)
{
int result, ret;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
gnutls_x509_name_constraints_t nc2 = NULL;
result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.NameConstraints", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
if (flags & GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND &&
(nc->permitted != NULL || nc->excluded != NULL)) {
ret = gnutls_x509_name_constraints_init (&nc2);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret =
_gnutls_extract_name_constraints(c2, "permittedSubtrees",
&nc2->permitted);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret =
_gnutls_extract_name_constraints(c2, "excludedSubtrees",
&nc2->excluded);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = _gnutls_x509_name_constraints_merge(nc, nc2);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
} else {
_gnutls_name_constraints_node_free(nc->permitted);
_gnutls_name_constraints_node_free(nc->excluded);
ret =
_gnutls_extract_name_constraints(c2, "permittedSubtrees",
&nc->permitted);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret =
_gnutls_extract_name_constraints(c2, "excludedSubtrees",
&nc->excluded);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
if (nc2)
gnutls_x509_name_constraints_deinit (nc2);
return ret;
}
/**
* gnutls_x509_ext_export_name_constraints:
* @nc: The nameconstraints
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the provided name constraints type to a
* DER-encoded PKIX NameConstraints (2.5.29.30) extension. The output data in
* @ext will be allocated using gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
gnutls_datum_t * ext)
{
int ret, result;
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) {
(void)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) {
(void)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, "", ext, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_import_subject_key_id:
* @ext: a DER encoded extension
* @id: will contain the subject key ID
*
* This function will return the subject key ID stored in the provided
* SubjectKeyIdentifier extension. The ID will be allocated using
* gnutls_malloc().
*
* 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_ext_import_subject_key_id(const gnutls_datum_t * ext,
gnutls_datum_t * id)
{
int result, ret;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
if (ext->size == 0 || ext->data == NULL) {
gnutls_assert();
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
}
result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.SubjectKeyIdentifier", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
ret = _gnutls_x509_read_value(c2, "", id);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_export_subject_key_id:
* @id: The key identifier
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the provided key identifier to a
* DER-encoded PKIX SubjectKeyIdentifier extension.
* The output data in @ext will be allocated using
* gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_subject_key_id(const gnutls_datum_t * id,
gnutls_datum_t * ext)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int ret, result;
result =
asn1_create_element(_gnutls_get_pkix(),
"PKIX1.SubjectKeyIdentifier", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = asn1_write_value(c2, "", id->data, id->size);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
ret = _gnutls_x509_der_encode(c2, "", ext, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
struct gnutls_x509_aki_st {
gnutls_datum_t id;
struct gnutls_subject_alt_names_st cert_issuer;
gnutls_datum_t serial;
};
/**
* gnutls_x509_aki_init:
* @aki: The authority key ID type
*
* This function will initialize an authority key ID.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_aki_init(gnutls_x509_aki_t * aki)
{
*aki = gnutls_calloc(1, sizeof(struct gnutls_x509_aki_st));
if (*aki == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
return 0;
}
/**
* gnutls_x509_aki_deinit:
* @aki: The authority key identifier type
*
* This function will deinitialize an authority key identifier.
*
* Since: 3.3.0
**/
void gnutls_x509_aki_deinit(gnutls_x509_aki_t aki)
{
gnutls_free(aki->serial.data);
gnutls_free(aki->id.data);
subject_alt_names_deinit(&aki->cert_issuer);
gnutls_free(aki);
}
/**
* gnutls_x509_aki_get_id:
* @aki: The authority key ID
* @id: Will hold the identifier
*
* This function will return the key identifier as stored in
* the @aki type. The identifier should be treated as constant.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
* if the index is out of bounds, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_aki_get_id(gnutls_x509_aki_t aki, gnutls_datum_t * id)
{
if (aki->id.size == 0)
return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
memcpy(id, &aki->id, sizeof(gnutls_datum_t));
return 0;
}
/**
* gnutls_x509_aki_set_id:
* @aki: The authority key ID
* @id: the key identifier
*
* This function will set the keyIdentifier to be stored in the @aki
* type.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_aki_set_id(gnutls_x509_aki_t aki, const gnutls_datum_t * id)
{
return _gnutls_set_datum(&aki->id, id->data, id->size);
}
/**
* gnutls_x509_aki_set_cert_issuer:
* @aki: The authority key ID
* @san_type: the type of the name (of %gnutls_subject_alt_names_t), may be null
* @san: The alternative name data
* @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
* @serial: The authorityCertSerialNumber number (may be null)
*
* This function will set the authorityCertIssuer name and the authorityCertSerialNumber
* to be stored in the @aki type. When storing multiple names, the serial
* should be set on the first call, and subsequent calls should use a %NULL serial.
*
* Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
* %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_aki_set_cert_issuer(gnutls_x509_aki_t aki,
unsigned int san_type,
const gnutls_datum_t *san,
const char *othername_oid,
const gnutls_datum_t *serial)
{
int ret;
gnutls_datum_t t_san, t_othername_oid = { NULL, 0 };
ret = _gnutls_set_datum(&aki->serial, serial->data, serial->size);
if (ret < 0)
return gnutls_assert_val(ret);
aki->cert_issuer.names[aki->cert_issuer.size].type = san_type;
ret = _gnutls_set_strdatum(&t_san, san->data, san->size);
if (ret < 0)
return gnutls_assert_val(ret);
if (othername_oid) {
t_othername_oid.data = (uint8_t *) gnutls_strdup(othername_oid);
if (t_othername_oid.data == NULL) {
gnutls_free(t_san.data);
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
}
t_othername_oid.size = strlen(othername_oid);
}
ret =
subject_alt_names_set(&aki->cert_issuer.names,
&aki->cert_issuer.size, san_type, &t_san,
(char *)t_othername_oid.data, 0);
if (ret < 0) {
gnutls_assert();
return ret;
}
return 0;
}
/**
* gnutls_x509_aki_get_cert_issuer:
* @aki: The authority key ID
* @seq: The index of the name to get
* @san_type: Will hold the type of the name (of %gnutls_subject_alt_names_t)
* @san: The alternative name data
* @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
* @serial: The authorityCertSerialNumber number
*
* This function will return a specific authorityCertIssuer name as stored in
* the @aki type, as well as the authorityCertSerialNumber. All the returned
* values should be treated as constant, and may be set to %NULL when are not required.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
* if the index is out of bounds, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_aki_get_cert_issuer(gnutls_x509_aki_t aki, unsigned int seq,
unsigned int *san_type,
gnutls_datum_t *san,
gnutls_datum_t *othername_oid,
gnutls_datum_t *serial)
{
if (seq >= aki->cert_issuer.size)
return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
if (aki->serial.size == 0)
return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
if (serial)
memcpy(serial, &aki->serial, sizeof(gnutls_datum_t));
if (san) {
memcpy(san, &aki->cert_issuer.names[seq].san,
sizeof(gnutls_datum_t));
}
if (othername_oid != NULL
&& aki->cert_issuer.names[seq].type == GNUTLS_SAN_OTHERNAME) {
othername_oid->data =
aki->cert_issuer.names[seq].othername_oid.data;
othername_oid->size =
aki->cert_issuer.names[seq].othername_oid.size;
}
if (san_type)
*san_type = aki->cert_issuer.names[seq].type;
return 0;
}
/**
* gnutls_x509_ext_import_authority_key_id:
* @ext: a DER encoded extension
* @aki: An initialized authority key identifier type
* @flags: should be zero
*
* This function will return the subject key ID stored in the provided
* AuthorityKeyIdentifier extension.
*
* 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_ext_import_authority_key_id(const gnutls_datum_t * ext,
gnutls_x509_aki_t aki,
unsigned int flags)
{
int ret;
unsigned i;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
gnutls_datum_t san, othername_oid;
unsigned type;
ret = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.AuthorityKeyIdentifier", &c2);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(ret);
}
ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(ret);
goto cleanup;
}
/* Read authorityCertIssuer */
for (i=0;;i++) {
san.data = NULL;
san.size = 0;
othername_oid.data = NULL;
ret = _gnutls_parse_general_name2(c2, "authorityCertIssuer", i,
&san, &type, 0);
if (ret < 0)
break;
if (type == GNUTLS_SAN_OTHERNAME) {
ret =
_gnutls_parse_general_name2(c2,
"authorityCertIssuer",
i,
&othername_oid,
NULL, 1);
if (ret < 0)
break;
}
ret = subject_alt_names_set(&aki->cert_issuer.names,
&aki->cert_issuer.size,
type, &san,
(char *)othername_oid.data, 1);
if (ret < 0)
break;
}
aki->cert_issuer.size = i;
if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
&& ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
gnutls_assert();
gnutls_free(san.data);
gnutls_free(othername_oid.data);
goto cleanup;
}
/* Read the serial number */
ret =
_gnutls_x509_read_value(c2, "authorityCertSerialNumber",
&aki->serial);
if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
&& ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
gnutls_assert();
goto cleanup;
}
/* Read the key identifier */
ret = _gnutls_x509_read_value(c2, "keyIdentifier", &aki->id);
if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
&& ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_export_authority_key_id:
* @aki: An initialized authority key identifier
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the provided key identifier to a
* DER-encoded PKIX AuthorityKeyIdentifier extension.
* The output data in @ext will be allocated using
* gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_authority_key_id(gnutls_x509_aki_t aki,
gnutls_datum_t * ext)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
unsigned i;
int result, ret;
result =
asn1_create_element(_gnutls_get_pkix(),
"PKIX1.AuthorityKeyIdentifier", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (aki->id.data != NULL) {
result =
asn1_write_value(c2, "keyIdentifier", aki->id.data,
aki->id.size);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
} else {
(void)asn1_write_value(c2, "keyIdentifier", NULL, 0);
}
if (aki->serial.data != NULL) {
result =
asn1_write_value(c2, "authorityCertSerialNumber",
aki->serial.data, aki->serial.size);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
} else {
(void)asn1_write_value(c2, "authorityCertSerialNumber", NULL, 0);
}
if (aki->cert_issuer.size == 0) {
(void)asn1_write_value(c2, "authorityCertIssuer", NULL, 0);
} else {
for (i = 0; i < aki->cert_issuer.size; i++) {
ret =
_gnutls_write_new_general_name(c2,
"authorityCertIssuer",
aki->cert_issuer.
names[i].type,
aki->
cert_issuer.names[i].
san.data,
aki->cert_issuer.
names[i].san.size);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
}
}
ret = _gnutls_x509_der_encode(c2, "", ext, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_import_key_usage:
* @ext: the DER encoded extension data
* @key_usage: where the key usage bits will be stored
*
* This function will return certificate's key usage, by reading the DER
* data of the keyUsage X.509 extension (2.5.29.15). The key usage value will ORed
* values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE,
* %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT,
* %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT,
* %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN,
* %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY.
*
* Returns: the certificate key usage, or a negative error code in case of
* parsing error. If the certificate does not contain the keyUsage
* extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
* returned.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_key_usage(const gnutls_datum_t * ext,
unsigned int *key_usage)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int len, result;
uint8_t str[2];
str[0] = str[1] = 0;
*key_usage = 0;
if ((result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.KeyUsage", &c2)) != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&c2);
return _gnutls_asn2err(result);
}
len = sizeof(str);
result = asn1_read_value(c2, "", str, &len);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&c2);
return 0;
}
*key_usage = str[0] | (str[1] << 8);
asn1_delete_structure(&c2);
return 0;
}
/**
* gnutls_x509_ext_export_key_usage:
* @usage: an ORed sequence of the GNUTLS_KEY_* elements.
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the keyUsage bit string to a DER
* encoded PKIX extension. The @ext data will be allocated using
* gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_key_usage(unsigned int usage, gnutls_datum_t * ext)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int result;
uint8_t str[2];
result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.KeyUsage", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
str[0] = usage & 0xff;
str[1] = usage >> 8;
/* Since KeyUsage is a BIT STRING, the input to asn1_write_value
* is the number of bits to be read. */
result = asn1_write_value(c2, "", str, 9);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&c2);
return _gnutls_asn2err(result);
}
result = _gnutls_x509_der_encode(c2, "", ext, 0);
asn1_delete_structure(&c2);
if (result < 0) {
gnutls_assert();
return result;
}
return 0;
}
/**
* gnutls_x509_ext_import_inhibit_anypolicy:
* @ext: the DER encoded extension data
* @skipcerts: will hold the number of certificates after which anypolicy is no longer acceptable.
*
* This function will return certificate's value of SkipCerts,
* by reading the DER data of the Inhibit anyPolicy X.509 extension (2.5.29.54).
*
* The @skipcerts value is the number of additional certificates that
* may appear in the path before the anyPolicy (%GNUTLS_X509_OID_POLICY_ANY)
* is no longer acceptable.
*
* Returns: zero, or a negative error code in case of
* parsing error. If the certificate does not contain the Inhibit anyPolicy
* extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
* returned.
*
* Since: 3.6.0
**/
int gnutls_x509_ext_import_inhibit_anypolicy(const gnutls_datum_t * ext,
unsigned int *skipcerts)
{
int ret;
ret = _gnutls_x509_read_der_uint(ext->data, ext->size, skipcerts);
if (ret < 0) {
gnutls_assert();
}
return ret;
}
/**
* gnutls_x509_ext_export_inhibit_anypolicy:
* @skipcerts: number of certificates after which anypolicy is no longer acceptable.
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the @skipcerts value to a DER
* encoded Inhibit AnyPolicy PKIX extension. The @ext data will be allocated using
* gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.6.0
**/
int gnutls_x509_ext_export_inhibit_anypolicy(unsigned int skipcerts, gnutls_datum_t * ext)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int result, ret;
result = asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
ret = _gnutls_x509_write_uint32(c2, "", skipcerts);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = _gnutls_x509_der_encode(c2, "", ext, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_import_private_key_usage_period:
* @ext: the DER encoded extension data
* @activation: Will hold the activation time
* @expiration: Will hold the expiration time
*
* This function will return the expiration and activation
* times of the private key as written in the
* PKIX extension 2.5.29.16.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_private_key_usage_period(const gnutls_datum_t * ext,
time_t * activation,
time_t * expiration)
{
int result, ret;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.PrivateKeyUsagePeriod", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
if (activation)
*activation = _gnutls_x509_get_time(c2, "notBefore", 1);
if (expiration)
*expiration = _gnutls_x509_get_time(c2, "notAfter", 1);
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_export_private_key_usage_period:
* @activation: The activation time
* @expiration: The expiration time
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the periods provided to a private key
* usage DER encoded extension (2.5.29.16).
(
* The @ext data will be allocated using
* gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_private_key_usage_period(time_t activation,
time_t expiration,
gnutls_datum_t * ext)
{
int result;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
result =
asn1_create_element(_gnutls_get_pkix(),
"PKIX1.PrivateKeyUsagePeriod", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _gnutls_x509_set_time(c2, "notBefore", activation, 1);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
result = _gnutls_x509_set_time(c2, "notAfter", expiration, 1);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
result = _gnutls_x509_der_encode(c2, "", ext, 0);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
cleanup:
asn1_delete_structure(&c2);
return result;
}
/**
* gnutls_x509_ext_import_basic_constraints:
* @ext: the DER encoded extension data
* @ca: will be non zero if the CA status is true
* @pathlen: the path length constraint; will be set to -1 for no limit
*
* This function will return the CA status and path length constraint
* as written in the PKIX extension 2.5.29.19.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_basic_constraints(const gnutls_datum_t * ext,
unsigned int *ca, int *pathlen)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
char str[128]="";
int len, result;
if ((result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.BasicConstraints",
&c2)) != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
if (pathlen) {
result = _gnutls_x509_read_uint(c2, "pathLenConstraint",
(unsigned int *)
pathlen);
if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
*pathlen = -1;
else if (result != GNUTLS_E_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
}
/* the default value of cA is false.
*/
len = sizeof(str) - 1;
result = asn1_read_value(c2, "cA", str, &len);
if (result == ASN1_SUCCESS && strcmp(str, "TRUE") == 0)
*ca = 1;
else
*ca = 0;
result = 0;
cleanup:
asn1_delete_structure(&c2);
return result;
}
/**
* gnutls_x509_ext_export_basic_constraints:
* @ca: non-zero for a CA
* @pathlen: The path length constraint (set to -1 for no constraint)
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the parameters provided to a basic constraints
* DER encoded extension (2.5.29.19).
(
* The @ext data will be allocated using
* gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_basic_constraints(unsigned int ca, int pathlen,
gnutls_datum_t * ext)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
const char *str;
int result;
if (ca == 0)
str = "FALSE";
else
str = "TRUE";
result =
asn1_create_element(_gnutls_get_pkix(),
"PKIX1.BasicConstraints", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
result = asn1_write_value(c2, "cA", str, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
if (pathlen < 0) {
result = asn1_write_value(c2, "pathLenConstraint", NULL, 0);
if (result < 0)
result = _gnutls_asn2err(result);
} else
result =
_gnutls_x509_write_uint32(c2, "pathLenConstraint", pathlen);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
result = _gnutls_x509_der_encode(c2, "", ext, 0);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
result = 0;
cleanup:
asn1_delete_structure(&c2);
return result;
}
/**
* gnutls_x509_ext_import_proxy:
* @ext: the DER encoded extension data
* @pathlen: pointer to output integer indicating path length (may be
* NULL), non-negative error codes indicate a present pCPathLenConstraint
* field and the actual value, -1 indicate that the field is absent.
* @policyLanguage: output variable with OID of policy language
* @policy: output variable with policy data
* @sizeof_policy: output variable with size of policy data
*
* This function will return the information from a proxy certificate
* extension. It reads the ProxyCertInfo X.509 extension (1.3.6.1.5.5.7.1.14).
* The @policyLanguage and @policy values must be deinitialized using gnutls_free() after use.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_proxy(const gnutls_datum_t *ext, int *pathlen,
char **policyLanguage, char **policy,
size_t *sizeof_policy)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int result;
gnutls_datum_t value1 = { NULL, 0 };
gnutls_datum_t value2 = { NULL, 0 };
if ((result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.ProxyCertInfo",
&c2)) != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
if (pathlen) {
result = _gnutls_x509_read_uint(c2, "pCPathLenConstraint",
(unsigned int *)
pathlen);
if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
*pathlen = -1;
else if (result != GNUTLS_E_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
}
result = _gnutls_x509_read_value(c2, "proxyPolicy.policyLanguage",
&value1);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
result = _gnutls_x509_read_value(c2, "proxyPolicy.policy", &value2);
if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
if (policy)
*policy = NULL;
if (sizeof_policy)
*sizeof_policy = 0;
} else if (result < 0) {
gnutls_assert();
goto cleanup;
} else {
if (policy) {
*policy = (char *)value2.data;
value2.data = NULL;
}
if (sizeof_policy)
*sizeof_policy = value2.size;
}
if (policyLanguage) {
*policyLanguage = (char *)value1.data;
value1.data = NULL;
}
result = 0;
cleanup:
gnutls_free(value1.data);
gnutls_free(value2.data);
asn1_delete_structure(&c2);
return result;
}
/**
* gnutls_x509_ext_export_proxy:
* @pathLenConstraint: A negative value will remove the path length constraint,
* while non-negative values will be set as the length of the pathLenConstraints field.
* @policyLanguage: OID describing the language of @policy.
* @policy: uint8_t byte array with policy language, can be %NULL
* @sizeof_policy: size of @policy.
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the parameters provided to a proxyCertInfo extension.
*
* The @ext data will be allocated using gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_proxy(int pathLenConstraint, const char *policyLanguage,
const char *policy, size_t sizeof_policy,
gnutls_datum_t * ext)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int result;
result = asn1_create_element(_gnutls_get_pkix(),
"PKIX1.ProxyCertInfo", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (pathLenConstraint < 0) {
result = asn1_write_value(c2, "pCPathLenConstraint", NULL, 0);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
} else {
result =
_gnutls_x509_write_uint32(c2, "pCPathLenConstraint",
pathLenConstraint);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
}
result = asn1_write_value(c2, "proxyPolicy.policyLanguage",
policyLanguage, 1);
if (result < 0) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
result = asn1_write_value(c2, "proxyPolicy.policy",
policy, sizeof_policy);
if (result < 0) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
result = _gnutls_x509_der_encode(c2, "", ext, 0);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
result = 0;
cleanup:
asn1_delete_structure(&c2);
return result;
}
static int decode_user_notice(const void *data, size_t size,
gnutls_datum_t * txt)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int ret, len;
char choice_type[64];
char name[128];
gnutls_datum_t td = {NULL,0}, utd;
ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.UserNotice", &c2);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = GNUTLS_E_PARSING_ERROR;
goto cleanup;
}
ret = _asn1_strict_der_decode(&c2, data, size, NULL);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = GNUTLS_E_PARSING_ERROR;
goto cleanup;
}
len = sizeof(choice_type);
ret = asn1_read_value(c2, "explicitText", choice_type, &len);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = GNUTLS_E_PARSING_ERROR;
goto cleanup;
}
if (strcmp(choice_type, "utf8String") != 0
&& strcmp(choice_type, "ia5String") != 0
&& strcmp(choice_type, "bmpString") != 0
&& strcmp(choice_type, "visibleString") != 0) {
gnutls_assert();
ret = GNUTLS_E_PARSING_ERROR;
goto cleanup;
}
snprintf(name, sizeof(name), "explicitText.%s", choice_type);
ret = _gnutls_x509_read_value(c2, name, &td);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
if (strcmp(choice_type, "bmpString") == 0) { /* convert to UTF-8 */
ret = _gnutls_ucs2_to_utf8(td.data, td.size, &utd, 1);
_gnutls_free_datum(&td);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
td.data = utd.data;
td.size = utd.size;
} else {
/* _gnutls_x509_read_value allows that */
td.data[td.size] = 0;
}
txt->data = (void *)td.data;
txt->size = td.size;
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
struct gnutls_x509_policies_st {
struct gnutls_x509_policy_st policy[MAX_ENTRIES];
unsigned int size;
};
/**
* gnutls_x509_policies_init:
* @policies: The authority key ID
*
* This function will initialize an authority key ID type.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_policies_init(gnutls_x509_policies_t * policies)
{
*policies = gnutls_calloc(1, sizeof(struct gnutls_x509_policies_st));
if (*policies == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
return 0;
}
/**
* gnutls_x509_policies_deinit:
* @policies: The authority key identifier
*
* This function will deinitialize an authority key identifier type.
*
* Since: 3.3.0
**/
void gnutls_x509_policies_deinit(gnutls_x509_policies_t policies)
{
unsigned i;
for (i = 0; i < policies->size; i++) {
gnutls_x509_policy_release(&policies->policy[i]);
}
gnutls_free(policies);
}
/**
* gnutls_x509_policies_get:
* @policies: The policies
* @seq: The index of the name to get
* @policy: Will hold the policy
*
* This function will return a specific policy as stored in
* the @policies type. The returned values should be treated as constant
* and valid for the lifetime of @policies.
*
* The any policy OID is available as the %GNUTLS_X509_OID_POLICY_ANY macro.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
* if the index is out of bounds, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_policies_get(gnutls_x509_policies_t policies,
unsigned int seq,
struct gnutls_x509_policy_st *policy)
{
if (seq >= policies->size)
return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
if (policy) {
memcpy(policy, &policies->policy[seq],
sizeof(struct gnutls_x509_policy_st));
}
return 0;
}
void _gnutls_x509_policies_erase(gnutls_x509_policies_t policies,
unsigned int seq)
{
if (seq >= policies->size)
return;
memset(&policies->policy[seq], 0, sizeof(struct gnutls_x509_policy_st));
}
/**
* gnutls_x509_policies_set:
* @policies: An initialized policies
* @seq: The index of the name to get
* @policy: Contains the policy to set
*
* This function will store the specified policy in
* the provided @policies.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_policies_set(gnutls_x509_policies_t policies,
const struct gnutls_x509_policy_st *policy)
{
unsigned i;
if (policies->size + 1 > MAX_ENTRIES)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
policies->policy[policies->size].oid = gnutls_strdup(policy->oid);
if (policies->policy[policies->size].oid == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
for (i = 0; i < policy->qualifiers; i++) {
policies->policy[policies->size].qualifier[i].type =
policy->qualifier[i].type;
policies->policy[policies->size].qualifier[i].size =
policy->qualifier[i].size;
policies->policy[policies->size].qualifier[i].data =
gnutls_malloc(policy->qualifier[i].size + 1);
if (policies->policy[policies->size].qualifier[i].data == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
memcpy(policies->policy[policies->size].qualifier[i].data,
policy->qualifier[i].data, policy->qualifier[i].size);
policies->policy[policies->size].qualifier[i].data[policy->
qualifier[i].
size] = 0;
}
policies->policy[policies->size].qualifiers = policy->qualifiers;
policies->size++;
return 0;
}
/**
* gnutls_x509_ext_import_policies:
* @ext: the DER encoded extension data
* @policies: A pointer to an initialized policies.
* @flags: should be zero
*
* This function will extract the certificate policy extension (2.5.29.32)
* and store it the provided policies.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_policies(const gnutls_datum_t * ext,
gnutls_x509_policies_t policies,
unsigned int flags)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
char tmpstr[128];
char tmpoid[MAX_OID_SIZE];
gnutls_datum_t tmpd = { NULL, 0 };
int ret, len;
unsigned i, j, current = 0;
ret = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.certificatePolicies", &c2);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(ret);
goto cleanup;
}
ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(ret);
goto cleanup;
}
for (j = 0;; j++) {
if (j >= MAX_ENTRIES)
break;
memset(&policies->policy[j], 0,
sizeof(struct gnutls_x509_policy_st));
/* create a string like "?1"
*/
snprintf(tmpstr, sizeof(tmpstr), "?%u.policyIdentifier", j + 1);
current = j+1;
ret = _gnutls_x509_read_value(c2, tmpstr, &tmpd);
if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
break;
if (ret < 0) {
gnutls_assert();
goto full_cleanup;
}
policies->policy[j].oid = (void *)tmpd.data;
tmpd.data = NULL;
for (i = 0; i < GNUTLS_MAX_QUALIFIERS; i++) {
gnutls_datum_t td;
snprintf(tmpstr, sizeof(tmpstr),
"?%u.policyQualifiers.?%u.policyQualifierId",
j + 1, i + 1);
len = sizeof(tmpoid);
ret = asn1_read_value(c2, tmpstr, tmpoid, &len);
if (ret == ASN1_ELEMENT_NOT_FOUND)
break; /* finished */
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(ret);
goto full_cleanup;
}
if (strcmp(tmpoid, "1.3.6.1.5.5.7.2.1") == 0) {
snprintf(tmpstr, sizeof(tmpstr),
"?%u.policyQualifiers.?%u.qualifier",
j + 1, i + 1);
ret =
_gnutls_x509_read_string(c2, tmpstr, &td,
ASN1_ETYPE_IA5_STRING, 0);
if (ret < 0) {
gnutls_assert();
goto full_cleanup;
}
policies->policy[j].qualifier[i].data =
(void *)td.data;
policies->policy[j].qualifier[i].size = td.size;
td.data = NULL;
policies->policy[j].qualifier[i].type =
GNUTLS_X509_QUALIFIER_URI;
} else if (strcmp(tmpoid, "1.3.6.1.5.5.7.2.2") == 0) {
gnutls_datum_t txt = {NULL, 0};
snprintf(tmpstr, sizeof(tmpstr),
"?%u.policyQualifiers.?%u.qualifier",
j + 1, i + 1);
ret = _gnutls_x509_read_value(c2, tmpstr, &td);
if (ret < 0) {
gnutls_assert();
goto full_cleanup;
}
ret =
decode_user_notice(td.data, td.size, &txt);
gnutls_free(td.data);
if (ret < 0) {
gnutls_assert();
goto full_cleanup;
}
policies->policy[j].qualifier[i].data =
(void *)txt.data;
policies->policy[j].qualifier[i].size =
txt.size;
policies->policy[j].qualifier[i].type =
GNUTLS_X509_QUALIFIER_NOTICE;
} else
policies->policy[j].qualifier[i].type =
GNUTLS_X509_QUALIFIER_UNKNOWN;
policies->policy[j].qualifiers++;
}
}
policies->size = j;
ret = 0;
goto cleanup;
full_cleanup:
for (j = 0; j < current; j++)
gnutls_x509_policy_release(&policies->policy[j]);
cleanup:
_gnutls_free_datum(&tmpd);
asn1_delete_structure(&c2);
return ret;
}
static int encode_user_notice(const gnutls_datum_t * txt,
gnutls_datum_t * der_data)
{
int result;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
if ((result =
asn1_create_element(_gnutls_get_pkix(),
"PKIX1.UserNotice", &c2)) != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto error;
}
/* delete noticeRef */
result = asn1_write_value(c2, "noticeRef", NULL, 0);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto error;
}
result = asn1_write_value(c2, "explicitText", "utf8String", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto error;
}
result =
asn1_write_value(c2, "explicitText.utf8String", txt->data,
txt->size);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto error;
}
result = _gnutls_x509_der_encode(c2, "", der_data, 0);
if (result < 0) {
gnutls_assert();
goto error;
}
result = 0;
error:
asn1_delete_structure(&c2);
return result;
}
/**
* gnutls_x509_ext_export_policies:
* @policies: A pointer to an initialized policies.
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the provided policies, to a certificate policy
* DER encoded extension (2.5.29.32).
*
* The @ext data will be allocated using gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_policies(gnutls_x509_policies_t policies,
gnutls_datum_t * ext)
{
int result;
unsigned i, j;
gnutls_datum_t der_data = {NULL, 0}, tmpd;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
const char *oid;
result =
asn1_create_element(_gnutls_get_pkix(),
"PKIX1.certificatePolicies", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
for (j = 0; j < policies->size; j++) {
/* 1. write a new policy */
result = asn1_write_value(c2, "", "NEW", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
/* 2. Add the OID.
*/
result =
asn1_write_value(c2, "?LAST.policyIdentifier",
policies->policy[j].oid, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
for (i = 0;
i < MIN(policies->policy[j].qualifiers,
GNUTLS_MAX_QUALIFIERS); i++) {
result =
asn1_write_value(c2, "?LAST.policyQualifiers",
"NEW", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
if (policies->policy[j].qualifier[i].type ==
GNUTLS_X509_QUALIFIER_URI)
oid = "1.3.6.1.5.5.7.2.1";
else if (policies->policy[j].qualifier[i].type ==
GNUTLS_X509_QUALIFIER_NOTICE)
oid = "1.3.6.1.5.5.7.2.2";
else {
result =
gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
goto cleanup;
}
result =
asn1_write_value(c2,
"?LAST.policyQualifiers.?LAST.policyQualifierId",
oid, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
if (policies->policy[j].qualifier[i].type ==
GNUTLS_X509_QUALIFIER_URI) {
tmpd.data =
(void *)policies->policy[j].qualifier[i].
data;
tmpd.size =
policies->policy[j].qualifier[i].size;
result =
_gnutls_x509_write_string(c2,
"?LAST.policyQualifiers.?LAST.qualifier",
&tmpd,
ASN1_ETYPE_IA5_STRING);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
} else if (policies->policy[j].qualifier[i].type ==
GNUTLS_X509_QUALIFIER_NOTICE) {
tmpd.data =
(void *)policies->policy[j].qualifier[i].
data;
tmpd.size =
policies->policy[j].qualifier[i].size;
if (tmpd.size > 200) {
gnutls_assert();
result = GNUTLS_E_INVALID_REQUEST;
goto cleanup;
}
result = encode_user_notice(&tmpd, &der_data);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
result =
_gnutls_x509_write_value(c2,
"?LAST.policyQualifiers.?LAST.qualifier",
&der_data);
_gnutls_free_datum(&der_data);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
}
}
}
result = _gnutls_x509_der_encode(c2, "", ext, 0);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
cleanup:
asn1_delete_structure(&c2);
return result;
}
struct crl_dist_point_st {
unsigned int type;
gnutls_datum_t san;
unsigned int reasons;
};
struct gnutls_x509_crl_dist_points_st {
struct crl_dist_point_st *points;
unsigned int size;
};
/**
* gnutls_x509_crl_dist_points_init:
* @cdp: The CRL distribution points
*
* This function will initialize a CRL distribution points type.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_crl_dist_points_init(gnutls_x509_crl_dist_points_t * cdp)
{
*cdp = gnutls_calloc(1, sizeof(struct gnutls_x509_crl_dist_points_st));
if (*cdp == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
return 0;
}
/**
* gnutls_x509_crl_dist_points_deinit:
* @cdp: The CRL distribution points
*
* This function will deinitialize a CRL distribution points type.
*
* Since: 3.3.0
**/
void gnutls_x509_crl_dist_points_deinit(gnutls_x509_crl_dist_points_t cdp)
{
unsigned i;
for (i = 0; i < cdp->size; i++) {
gnutls_free(cdp->points[i].san.data);
}
gnutls_free(cdp->points);
gnutls_free(cdp);
}
/**
* gnutls_x509_crl_dist_points_get:
* @cdp: The CRL distribution points
* @seq: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.)
* @type: The name type of the corresponding name (gnutls_x509_subject_alt_name_t)
* @san: The distribution point names (to be treated as constant)
* @reasons: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t.
*
* This function retrieves the individual CRL distribution points (2.5.29.31),
* contained in provided type.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
* if the index is out of bounds, otherwise a negative error value.
**/
int gnutls_x509_crl_dist_points_get(gnutls_x509_crl_dist_points_t cdp,
unsigned int seq, unsigned int *type,
gnutls_datum_t * san, unsigned int *reasons)
{
if (seq >= cdp->size)
return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
if (reasons)
*reasons = cdp->points[seq].reasons;
if (type)
*type = cdp->points[seq].type;
if (san) {
san->data = cdp->points[seq].san.data;
san->size = cdp->points[seq].san.size;
}
return 0;
}
static
int crl_dist_points_set(gnutls_x509_crl_dist_points_t cdp,
gnutls_x509_subject_alt_name_t type,
const gnutls_datum_t * san, unsigned int reasons)
{
void *tmp;
/* new dist point */
tmp =
gnutls_realloc(cdp->points,
(cdp->size + 1) * sizeof(cdp->points[0]));
if (tmp == NULL) {
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
}
cdp->points = tmp;
cdp->points[cdp->size].type = type;
cdp->points[cdp->size].san.data = san->data;
cdp->points[cdp->size].san.size = san->size;
cdp->points[cdp->size].reasons = reasons;
cdp->size++;
return 0;
}
/**
* gnutls_x509_crl_dist_points_set:
* @cdp: The CRL distribution points
* @type: The type of the name (of %gnutls_subject_alt_names_t)
* @san: The point name data
* @reasons: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t.
*
* This function will store the specified CRL distribution point value
* the @cdp type.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_crl_dist_points_set(gnutls_x509_crl_dist_points_t cdp,
gnutls_x509_subject_alt_name_t type,
const gnutls_datum_t * san,
unsigned int reasons)
{
int ret;
gnutls_datum_t t_san;
ret = _gnutls_set_datum(&t_san, san->data, san->size);
if (ret < 0)
return gnutls_assert_val(ret);
ret = crl_dist_points_set(cdp, type, &t_san, reasons);
if (ret < 0) {
gnutls_free(t_san.data);
return gnutls_assert_val(ret);
}
return 0;
}
/**
* gnutls_x509_ext_import_crl_dist_points:
* @ext: the DER encoded extension data
* @cdp: A pointer to an initialized CRL distribution points.
* @flags: should be zero
*
* This function will extract the CRL distribution points extension (2.5.29.31)
* and store it into the provided type.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_crl_dist_points(const gnutls_datum_t * ext,
gnutls_x509_crl_dist_points_t cdp,
unsigned int flags)
{
int result;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
char name[MAX_NAME_SIZE];
int len, ret;
uint8_t reasons[2];
unsigned i, type, rflags, j;
gnutls_datum_t san = {NULL, 0};
result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.CRLDistributionPoints", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
/* Return the different names from the first CRLDistr. point.
* The whole thing is a mess.
*/
i = 0;
do {
snprintf(name, sizeof(name), "?%u.reasons", (unsigned)i + 1);
len = sizeof(reasons);
result = asn1_read_value(c2, name, reasons, &len);
if (result != ASN1_VALUE_NOT_FOUND &&
result != ASN1_ELEMENT_NOT_FOUND &&
result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
break;
}
if (result == ASN1_VALUE_NOT_FOUND
|| result == ASN1_ELEMENT_NOT_FOUND)
rflags = 0;
else
rflags = reasons[0] | (reasons[1] << 8);
snprintf(name, sizeof(name),
"?%u.distributionPoint.fullName", (unsigned)i + 1);
for (j=0;;j++) {
san.data = NULL;
san.size = 0;
ret =
_gnutls_parse_general_name2(c2, name, j, &san,
&type, 0);
if (j > 0
&& ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
ret = 0;
break;
}
if (ret < 0)
break;
ret = crl_dist_points_set(cdp, type, &san, rflags);
if (ret < 0)
break;
san.data = NULL; /* it is now in cdp */
}
i++;
} while (ret >= 0);
if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
gnutls_assert();
gnutls_free(san.data);
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_export_crl_dist_points:
* @cdp: A pointer to an initialized CRL distribution points.
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the provided policies, to a certificate policy
* DER encoded extension (2.5.29.31).
*
* The @ext data will be allocated using gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_crl_dist_points(gnutls_x509_crl_dist_points_t cdp,
gnutls_datum_t * ext)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int result;
uint8_t reasons[2];
unsigned i;
result =
asn1_create_element(_gnutls_get_pkix(),
"PKIX1.CRLDistributionPoints", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
for (i = 0; i < cdp->size; i++) {
if (i == 0
|| cdp->points[i].reasons != cdp->points[i - 1].reasons) {
result = asn1_write_value(c2, "", "NEW", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
if (cdp->points[i].reasons) {
reasons[0] = cdp->points[i].reasons & 0xff;
reasons[1] = cdp->points[i].reasons >> 8;
result =
asn1_write_value(c2, "?LAST.reasons",
reasons, 2);
} else {
result =
asn1_write_value(c2, "?LAST.reasons", NULL,
0);
}
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
result =
asn1_write_value(c2, "?LAST.cRLIssuer", NULL, 0);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
/* When used as type CHOICE.
*/
result =
asn1_write_value(c2, "?LAST.distributionPoint",
"fullName", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
result = _gnutls_asn2err(result);
goto cleanup;
}
}
result =
_gnutls_write_new_general_name(c2,
"?LAST.distributionPoint.fullName",
cdp->points[i].type,
cdp->points[i].san.data,
cdp->points[i].san.size);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
}
result = _gnutls_x509_der_encode(c2, "", ext, 0);
if (result < 0) {
gnutls_assert();
goto cleanup;
}
result = 0;
cleanup:
asn1_delete_structure(&c2);
return result;
}
struct gnutls_x509_aia_st {
struct {
gnutls_datum_t oid;
unsigned int san_type;
gnutls_datum_t san;
} *aia;
unsigned int size;
};
/**
* gnutls_x509_aia_init:
* @aia: The authority info access
*
* This function will initialize an authority info access type.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_aia_init(gnutls_x509_aia_t * aia)
{
*aia = gnutls_calloc(1, sizeof(struct gnutls_x509_aia_st));
if (*aia == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
return 0;
}
/**
* gnutls_x509_aia_deinit:
* @aia: The authority info access
*
* This function will deinitialize an authority info access type.
*
* Since: 3.3.0
**/
void gnutls_x509_aia_deinit(gnutls_x509_aia_t aia)
{
unsigned i;
for (i = 0; i < aia->size; i++) {
gnutls_free(aia->aia[i].san.data);
gnutls_free(aia->aia[i].oid.data);
}
gnutls_free(aia->aia);
gnutls_free(aia);
}
/**
* gnutls_x509_aia_get:
* @aia: The authority info access
* @seq: specifies the sequence number of the access descriptor (0 for the first one, 1 for the second etc.)
* @oid: the type of available data; to be treated as constant.
* @san_type: Will hold the type of the name of %gnutls_subject_alt_names_t (may be null).
* @san: the access location name; to be treated as constant (may be null).
*
* This function reads from the Authority Information Access type.
*
* The @seq input parameter is used to indicate which member of the
* sequence the caller is interested in. The first member is 0, the
* second member 1 and so on. When the @seq value is out of bounds,
* %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
*
* Typically @oid is %GNUTLS_OID_AD_CAISSUERS or %GNUTLS_OID_AD_OCSP.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_aia_get(gnutls_x509_aia_t aia, unsigned int seq,
gnutls_datum_t *oid,
unsigned *san_type,
gnutls_datum_t *san)
{
if (seq >= aia->size)
return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
if (san_type)
*san_type = aia->aia[seq].san_type;
if (san) {
san->data = aia->aia[seq].san.data;
san->size = aia->aia[seq].san.size;
}
if (oid) {
oid->data = aia->aia[seq].oid.data;
oid->size = aia->aia[seq].oid.size;
}
return 0;
}
int _gnutls_alt_name_process(gnutls_datum_t *out, unsigned type, const gnutls_datum_t *san, unsigned raw)
{
int ret;
if (type == GNUTLS_SAN_DNSNAME && !raw) {
ret = gnutls_idna_map((char*)san->data, san->size, out, 0);
if (ret < 0) {
return gnutls_assert_val(ret);
}
} else if (type == GNUTLS_SAN_RFC822NAME && !raw) {
ret = _gnutls_idna_email_map((char*)san->data, san->size, out);
if (ret < 0) {
return gnutls_assert_val(ret);
}
} else if (type == GNUTLS_SAN_URI && !raw) {
if (!_gnutls_str_is_print((char*)san->data, san->size)) {
_gnutls_debug_log("non-ASCII URIs are not supported\n");
return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
} else {
ret = _gnutls_set_strdatum(out, san->data, san->size);
if (ret < 0)
return gnutls_assert_val(ret);
}
} else {
ret = _gnutls_set_strdatum(out, san->data, san->size);
if (ret < 0)
return gnutls_assert_val(ret);
}
return 0;
}
/**
* gnutls_x509_aia_set:
* @aia: The authority info access
* @oid: the type of data.
* @san_type: The type of the name (of %gnutls_subject_alt_names_t)
* @san: The alternative name data
* @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
*
* This function will store the specified alternative name in
* the @aia type.
*
* Typically the value for @oid should be %GNUTLS_OID_AD_OCSP, or
* %GNUTLS_OID_AD_CAISSUERS.
*
* Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, and %GNUTLS_SAN_DNSNAME,
* are converted to ACE format when necessary.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_aia_set(gnutls_x509_aia_t aia,
const char *oid,
unsigned san_type,
const gnutls_datum_t * san)
{
int ret;
void *tmp;
unsigned indx;
tmp = gnutls_realloc(aia->aia, (aia->size + 1) * sizeof(aia->aia[0]));
if (tmp == NULL) {
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
}
aia->aia = tmp;
indx = aia->size;
aia->aia[indx].san_type = san_type;
if (oid) {
aia->aia[indx].oid.data = (void*)gnutls_strdup(oid);
aia->aia[indx].oid.size = strlen(oid);
} else {
aia->aia[indx].oid.data = NULL;
aia->aia[indx].oid.size = 0;
}
ret = _gnutls_alt_name_process(&aia->aia[indx].san, san_type, san, 0);
if (ret < 0)
return gnutls_assert_val(ret);
aia->size++;
return 0;
}
static int parse_aia(ASN1_TYPE c2, gnutls_x509_aia_t aia)
{
int len;
char nptr[MAX_NAME_SIZE];
int ret, result;
char tmpoid[MAX_OID_SIZE];
void * tmp;
unsigned i, indx;
for (i = 1;; i++) {
snprintf(nptr, sizeof(nptr), "?%u.accessMethod", i);
len = sizeof(tmpoid);
result = asn1_read_value(c2, nptr, tmpoid, &len);
if (result == ASN1_VALUE_NOT_FOUND
|| result == ASN1_ELEMENT_NOT_FOUND) {
ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
break;
}
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
indx = aia->size;
tmp = gnutls_realloc(aia->aia, (aia->size + 1) * sizeof(aia->aia[0]));
if (tmp == NULL) {
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
}
aia->aia = tmp;
snprintf(nptr, sizeof(nptr), "?%u.accessLocation", i);
ret = _gnutls_parse_general_name2(c2, nptr, -1, &aia->aia[indx].san,
&aia->aia[indx].san_type, 0);
if (ret < 0)
break;
/* we do the strdup after parsing to avoid a memory leak */
aia->aia[indx].oid.data = (void*)gnutls_strdup(tmpoid);
aia->aia[indx].oid.size = strlen(tmpoid);
aia->size++;
if (aia->aia[indx].oid.data == NULL) {
gnutls_assert();
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
}
}
if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
return ret;
}
return 0;
}
/**
* gnutls_x509_ext_import_aia:
* @ext: The DER-encoded extension data
* @aia: The authority info access
* @flags: should be zero
*
* This function extracts the Authority Information Access (AIA)
* extension from the provided DER-encoded data; see RFC 5280 section 4.2.2.1
* for more information on the extension. The
* AIA extension holds a sequence of AccessDescription (AD) data.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_aia(const gnutls_datum_t * ext,
gnutls_x509_aia_t aia,
unsigned int flags)
{
int ret;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
if (ext->size == 0 || ext->data == NULL) {
gnutls_assert();
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
}
ret = asn1_create_element(_gnutls_get_pkix(),
"PKIX1.AuthorityInfoAccessSyntax", &c2);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(ret);
}
ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(ret);
goto cleanup;
}
ret = parse_aia(c2, aia);
if (ret < 0) {
gnutls_assert();
}
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_export_aia:
* @aia: The authority info access
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will DER encode the Authority Information Access (AIA)
* extension; see RFC 5280 section 4.2.2.1 for more information on the
* extension.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
* negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_aia(gnutls_x509_aia_t aia,
gnutls_datum_t * ext)
{
int ret, result;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
unsigned int i;
ret = asn1_create_element(_gnutls_get_pkix(),
"PKIX1.AuthorityInfoAccessSyntax", &c2);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(ret);
}
/* 1. create a new element.
*/
for (i=0;isize;i++) {
result = asn1_write_value(c2, "", "NEW", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
/* 2. Add the OID.
*/
result = asn1_write_value(c2, "?LAST.accessMethod", aia->aia[i].oid.data, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
ret =
_gnutls_write_general_name(c2,
"?LAST.accessLocation",
aia->aia[i].san_type,
aia->aia[i].san.data,
aia->aia[i].san.size);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
}
ret = _gnutls_x509_der_encode(c2, "", ext, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
cleanup:
asn1_delete_structure(&c2);
return ret;
}
struct gnutls_x509_key_purposes_st {
gnutls_datum_t oid[MAX_ENTRIES];
unsigned int size;
};
/**
* gnutls_subject_alt_names_init:
* @p: The key purposes
*
* This function will initialize an alternative names type.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_key_purpose_init(gnutls_x509_key_purposes_t * p)
{
*p = gnutls_calloc(1, sizeof(struct gnutls_x509_key_purposes_st));
if (*p == NULL) {
gnutls_assert();
return GNUTLS_E_MEMORY_ERROR;
}
return 0;
}
static void key_purposes_deinit(gnutls_x509_key_purposes_t p)
{
unsigned int i;
for (i = 0; i < p->size; i++) {
gnutls_free(p->oid[i].data);
}
}
/**
* gnutls_x509_key_purpose_deinit:
* @p: The key purposes
*
* This function will deinitialize a key purposes type.
*
* Since: 3.3.0
**/
void gnutls_x509_key_purpose_deinit(gnutls_x509_key_purposes_t p)
{
key_purposes_deinit(p);
gnutls_free(p);
}
/**
* gnutls_x509_key_purpose_set:
* @p: The key purposes
* @oid: The object identifier of the key purpose
*
* This function will store the specified key purpose in the
* purposes.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_key_purpose_set(gnutls_x509_key_purposes_t p, const char *oid)
{
if (p->size + 1 > MAX_ENTRIES)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
p->oid[p->size].data = (void*)gnutls_strdup(oid);
if (p->oid[p->size].data == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
p->oid[p->size].size = strlen(oid);
p->size++;
return 0;
}
/**
* gnutls_x509_key_purpose_get:
* @p: The key purposes
* @idx: The index of the key purpose to retrieve
* @oid: Will hold the object identifier of the key purpose (to be treated as constant)
*
* This function will retrieve the specified by the index key purpose in the
* purposes type. The object identifier will be a null terminated string.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
* if the index is out of bounds, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_key_purpose_get(gnutls_x509_key_purposes_t p, unsigned idx, gnutls_datum_t *oid)
{
if (idx >= p->size)
return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
oid->data = p->oid[idx].data;
oid->size = p->oid[idx].size;
return 0;
}
/**
* gnutls_x509_ext_import_key_purposes:
* @ext: The DER-encoded extension data
* @p: The key purposes
* @flags: should be zero
*
* This function will extract the key purposes in the provided DER-encoded
* ExtKeyUsageSyntax PKIX extension, to a %gnutls_x509_key_purposes_t type.
* The data must be initialized.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_import_key_purposes(const gnutls_datum_t * ext,
gnutls_x509_key_purposes_t p,
unsigned int flags)
{
char tmpstr[MAX_NAME_SIZE];
int result, ret;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
gnutls_datum_t oid = {NULL, 0};
unsigned i;
result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
key_purposes_deinit(p);
i = 0;
p->size = 0;
for (;ioid[i].data = oid.data;
p->oid[i].size = oid.size;
oid.data = NULL;
oid.size = 0;
p->size++;
}
ret = 0;
cleanup:
gnutls_free(oid.data);
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_export_key_purposes:
* @p: The key purposes
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the key purposes type to a
* DER-encoded PKIX ExtKeyUsageSyntax (2.5.29.37) extension. The output data in
* @ext will be allocated using gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.3.0
**/
int gnutls_x509_ext_export_key_purposes(gnutls_x509_key_purposes_t p,
gnutls_datum_t * ext)
{
int result, ret;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
unsigned i;
result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
/* generate the extension.
*/
for (i=0;isize;i++) {
/* 1. create a new element.
*/
result = asn1_write_value(c2, "", "NEW", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
/* 2. Add the OID.
*/
result = asn1_write_value(c2, "?LAST", p->oid[i].data, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
}
ret = _gnutls_x509_der_encode(c2, "", ext, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_ext_deinit:
* @ext: The extensions structure
*
* This function will deinitialize an extensions structure.
*
* Since: 3.3.8
**/
void gnutls_x509_ext_deinit(gnutls_x509_ext_st *ext)
{
gnutls_free(ext->oid);
gnutls_free(ext->data.data);
}
int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out)
{
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
char str_critical[10];
char oid[MAX_OID_SIZE];
int result, len, ret;
memset(out, 0, sizeof(*out));
/* decode der */
result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extension", &c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&c2, der->data, der->size, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
len = sizeof(oid)-1;
result = asn1_read_value(c2, "extnID", oid, &len);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
len = sizeof(str_critical)-1;
result = asn1_read_value(c2, "critical", str_critical, &len);
if (result != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(result);
goto cleanup;
}
if (str_critical[0] == 'T')
out->critical = 1;
else
out->critical = 0;
ret = _gnutls_x509_read_value(c2, "extnValue", &out->data);
if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE || ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
out->data.data = NULL;
out->data.size = 0;
} else if (ret < 0) {
gnutls_assert();
goto fail;
}
out->oid = gnutls_strdup(oid);
if (out->oid == NULL) {
ret = GNUTLS_E_MEMORY_ERROR;
goto fail;
}
ret = 0;
goto cleanup;
fail:
memset(out, 0, sizeof(*out));
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/* flags can be zero or GNUTLS_EXT_FLAG_APPEND
*/
static int parse_tlsfeatures(ASN1_TYPE c2, gnutls_x509_tlsfeatures_t f, unsigned flags)
{
char nptr[MAX_NAME_SIZE];
int result;
unsigned i, indx, j;
unsigned int feature;
if (!(flags & GNUTLS_EXT_FLAG_APPEND))
f->size = 0;
for (i = 1;; i++) {
unsigned skip = 0;
snprintf(nptr, sizeof(nptr), "?%u", i);
result = _gnutls_x509_read_uint(c2, nptr, &feature);
if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || result == GNUTLS_E_ASN1_VALUE_NOT_FOUND) {
break;
}
else if (result != GNUTLS_E_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (feature > UINT16_MAX) {
gnutls_assert();
return GNUTLS_E_CERTIFICATE_ERROR;
}
/* skip duplicates */
for (j=0;jsize;j++) {
if (f->feature[j] == feature) {
skip = 1;
break;
}
}
if (!skip) {
if (f->size >= sizeof(f->feature)/sizeof(f->feature[0])) {
gnutls_assert();
return GNUTLS_E_INTERNAL_ERROR;
}
indx = f->size;
f->feature[indx] = feature;
f->size++;
}
}
return 0;
}
/**
* gnutls_x509_ext_import_tlsfeatures:
* @ext: The DER-encoded extension data
* @f: The features structure
* @flags: zero or %GNUTLS_EXT_FLAG_APPEND
*
* This function will export the features in the provided DER-encoded
* TLS Features PKIX extension, to a %gnutls_x509_tlsfeatures_t type. @f
* must be initialized.
*
* When the @flags is set to %GNUTLS_EXT_FLAG_APPEND,
* then if the @features structure is empty this function will behave
* identically as if the flag was not set. Otherwise if there are elements
* in the @features structure then they will be merged with.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.5.1
**/
int gnutls_x509_ext_import_tlsfeatures(const gnutls_datum_t * ext,
gnutls_x509_tlsfeatures_t f,
unsigned int flags)
{
int ret;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
if (ext->size == 0 || ext->data == NULL) {
gnutls_assert();
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
}
ret = asn1_create_element(_gnutls_get_pkix(),
"PKIX1.TlsFeatures", &c2);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(ret);
}
ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(ret);
goto cleanup;
}
ret = parse_tlsfeatures(c2, f, flags);
if (ret < 0) {
gnutls_assert();
}
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_ext_export_tlsfeatures:
* @f: The features structure
* @ext: The DER-encoded extension data; must be freed using gnutls_free().
*
* This function will convert the provided TLS features structure structure to a
* DER-encoded TLS features PKIX extension. The output data in @ext will be allocated using
* gnutls_malloc().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
*
* Since: 3.5.1
**/
int gnutls_x509_ext_export_tlsfeatures(gnutls_x509_tlsfeatures_t f,
gnutls_datum_t * ext)
{
if (f == NULL) {
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
int ret;
unsigned i;
ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.TlsFeatures", &c2);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(ret);
}
for (i = 0; i < f->size; ++i) {
ret = asn1_write_value(c2, "", "NEW", 1);
if (ret != ASN1_SUCCESS) {
gnutls_assert();
ret = _gnutls_asn2err(ret);
goto cleanup;
}
ret = _gnutls_x509_write_uint32(c2, "?LAST", f->feature[i]);
if (ret != GNUTLS_E_SUCCESS) {
gnutls_assert();
goto cleanup;
}
}
ret = _gnutls_x509_der_encode(c2, "", ext, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
return ret;
}
/**
* gnutls_x509_tlsfeatures_add:
* @f: The TLS features
* @feature: The feature to add
*
* This function will append a feature to the X.509 TLS features
* extension structure.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
* otherwise a negative error value.
*
* Since: 3.5.1
**/
int gnutls_x509_tlsfeatures_add(gnutls_x509_tlsfeatures_t f, unsigned int feature)
{
if (f == NULL) {
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
if (feature > UINT16_MAX)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
if (f->size >= sizeof(f->feature)/sizeof(f->feature[0]))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
f->feature[f->size++] = feature;
return 0;
}