/*
* 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
*
*/
/* Functions that relate to the X.509 extension parsing.
*/
#include "gnutls_int.h"
#include "errors.h"
#include
#include
#include
#include
#include
#include
#include
int
_gnutls_get_extension(ASN1_TYPE asn, const char *root,
const char *extension_id, int indx,
gnutls_datum_t * ret, unsigned int *_critical)
{
int k, result, len;
char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
char str_critical[10];
int critical = 0;
char extnID[MAX_OID_SIZE];
gnutls_datum_t value;
int indx_counter = 0;
ret->data = NULL;
ret->size = 0;
k = 0;
do {
k++;
snprintf(name, sizeof(name), "%s.?%u", root, k);
_gnutls_str_cpy(name2, sizeof(name2), name);
_gnutls_str_cat(name2, sizeof(name2), ".extnID");
len = sizeof(extnID) - 1;
result = asn1_read_value(asn, name2, extnID, &len);
if (result == ASN1_ELEMENT_NOT_FOUND) {
gnutls_assert();
break;
} else if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
/* Handle Extension
*/
if (strcmp(extnID, extension_id) == 0
&& indx == indx_counter++) {
/* extension was found
*/
/* read the critical status.
*/
_gnutls_str_cpy(name2, sizeof(name2), name);
_gnutls_str_cat(name2, sizeof(name2), ".critical");
len = sizeof(str_critical);
result =
asn1_read_value(asn, name2,
str_critical, &len);
if (result == ASN1_ELEMENT_NOT_FOUND) {
gnutls_assert();
break;
} else if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (str_critical[0] == 'T')
critical = 1;
else
critical = 0;
/* read the value.
*/
_gnutls_str_cpy(name2, sizeof(name2), name);
_gnutls_str_cat(name2, sizeof(name2),
".extnValue");
result =
_gnutls_x509_read_value(asn, name2, &value);
if (result < 0) {
gnutls_assert();
return result;
}
ret->data = value.data;
ret->size = value.size;
if (_critical)
*_critical = critical;
return 0;
}
}
while (1);
if (result == ASN1_ELEMENT_NOT_FOUND) {
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
} else {
gnutls_assert();
return _gnutls_asn2err(result);
}
}
static int
get_indx_extension(ASN1_TYPE asn, const char *root,
int indx, gnutls_datum_t * out)
{
char name[MAX_NAME_SIZE];
int ret;
out->data = NULL;
out->size = 0;
snprintf(name, sizeof(name), "%s.?%u.extnValue", root, indx+1);
ret = _gnutls_x509_read_value(asn, name, out);
if (ret < 0)
return gnutls_assert_val(ret);
return 0;
}
int
_gnutls_x509_crt_get_extension(gnutls_x509_crt_t cert,
const char *extension_id, int indx,
gnutls_datum_t * data, unsigned int *critical)
{
return _gnutls_get_extension(cert->cert, "tbsCertificate.extensions",
extension_id, indx, data, critical);
}
/**
* gnutls_x509_crt_get_extension_data2:
* @cert: should contain a #gnutls_x509_crt_t type
* @indx: Specifies which extension OID to read. Use (0) to get the first one.
* @data: will contain the extension DER-encoded data
*
* This function will return the requested by the index extension data in the
* certificate. The extension data will be allocated using
* gnutls_malloc().
*
* Use gnutls_x509_crt_get_extension_info() to extract the OID.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
* otherwise a negative error code is returned. If you have reached the
* last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
* will be returned.
**/
int
gnutls_x509_crt_get_extension_data2(gnutls_x509_crt_t cert,
unsigned indx,
gnutls_datum_t * data)
{
return get_indx_extension(cert->cert, "tbsCertificate.extensions",
indx, data);
}
int
_gnutls_x509_crl_get_extension(gnutls_x509_crl_t crl,
const char *extension_id, int indx,
gnutls_datum_t * data,
unsigned int *critical)
{
return _gnutls_get_extension(crl->crl, "tbsCertList.crlExtensions",
extension_id, indx, data, critical);
}
/**
* gnutls_x509_crl_get_extension_data2:
* @crl: should contain a #gnutls_x509_crl_t type
* @indx: Specifies which extension OID to read. Use (0) to get the first one.
* @data: will contain the extension DER-encoded data
*
* This function will return the requested by the index extension data in the
* certificate revocation list. The extension data will be allocated using
* gnutls_malloc().
*
* Use gnutls_x509_crt_get_extension_info() to extract the OID.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
* otherwise a negative error code is returned. If you have reached the
* last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
* will be returned.
**/
int
gnutls_x509_crl_get_extension_data2(gnutls_x509_crl_t crl,
unsigned indx,
gnutls_datum_t * data)
{
return get_indx_extension(crl->crl, "tbsCertList.crlExtensions",
indx, data);
}
/* This function will attempt to return the requested extension OID found in
* the given X509v3 certificate.
*
* If you have passed the last extension, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
* be returned.
*/
static int get_extension_oid(ASN1_TYPE asn, const char *root,
unsigned indx, void *oid, size_t * sizeof_oid)
{
int k, result, len;
char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
char extnID[MAX_OID_SIZE];
unsigned indx_counter = 0;
k = 0;
do {
k++;
snprintf(name, sizeof(name), "%s.?%u", root, k);
_gnutls_str_cpy(name2, sizeof(name2), name);
_gnutls_str_cat(name2, sizeof(name2), ".extnID");
len = sizeof(extnID) - 1;
result = asn1_read_value(asn, name2, extnID, &len);
if (result == ASN1_ELEMENT_NOT_FOUND) {
gnutls_assert();
break;
} else if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
/* Handle Extension
*/
if (indx == indx_counter++) {
len = strlen(extnID) + 1;
if (*sizeof_oid < (unsigned) len) {
*sizeof_oid = len;
gnutls_assert();
return GNUTLS_E_SHORT_MEMORY_BUFFER;
}
memcpy(oid, extnID, len);
*sizeof_oid = len - 1;
return 0;
}
}
while (1);
if (result == ASN1_ELEMENT_NOT_FOUND) {
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
} else {
gnutls_assert();
return _gnutls_asn2err(result);
}
}
/* This function will attempt to return the requested extension OID found in
* the given X509v3 certificate.
*
* If you have passed the last extension, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
* be returned.
*/
int
_gnutls_x509_crt_get_extension_oid(gnutls_x509_crt_t cert,
int indx, void *oid,
size_t * sizeof_oid)
{
return get_extension_oid(cert->cert, "tbsCertificate.extensions",
indx, oid, sizeof_oid);
}
int
_gnutls_x509_crl_get_extension_oid(gnutls_x509_crl_t crl,
int indx, void *oid,
size_t * sizeof_oid)
{
return get_extension_oid(crl->crl, "tbsCertList.crlExtensions",
indx, oid, sizeof_oid);
}
/* This function will attempt to set the requested extension in
* the given X509v3 certificate.
*
* Critical will be either 0 or 1.
*/
static int
add_extension(ASN1_TYPE asn, const char *root, const char *extension_id,
const gnutls_datum_t * ext_data, unsigned int critical)
{
int result;
const char *str;
char name[MAX_NAME_SIZE];
snprintf(name, sizeof(name), "%s", root);
/* Add a new extension in the list.
*/
result = asn1_write_value(asn, name, "NEW", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (root[0] != 0)
snprintf(name, sizeof(name), "%s.?LAST.extnID", root);
else
snprintf(name, sizeof(name), "?LAST.extnID");
result = asn1_write_value(asn, name, extension_id, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (critical == 0)
str = "FALSE";
else
str = "TRUE";
if (root[0] != 0)
snprintf(name, sizeof(name), "%s.?LAST.critical", root);
else
snprintf(name, sizeof(name), "?LAST.critical");
result = asn1_write_value(asn, name, str, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (root[0] != 0)
snprintf(name, sizeof(name), "%s.?LAST.extnValue", root);
else
snprintf(name, sizeof(name), "?LAST.extnValue");
result = _gnutls_x509_write_value(asn, name, ext_data);
if (result < 0) {
gnutls_assert();
return result;
}
return 0;
}
/* Overwrite the given extension (using the index)
* index here starts from one.
*/
static int
overwrite_extension(ASN1_TYPE asn, const char *root, unsigned int indx,
const gnutls_datum_t * ext_data, unsigned int critical)
{
char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
const char *str;
int result;
if (root[0] != 0)
snprintf(name, sizeof(name), "%s.?%u", root, indx);
else
snprintf(name, sizeof(name), "?%u", indx);
if (critical == 0)
str = "FALSE";
else
str = "TRUE";
_gnutls_str_cpy(name2, sizeof(name2), name);
_gnutls_str_cat(name2, sizeof(name2), ".critical");
result = asn1_write_value(asn, name2, str, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
_gnutls_str_cpy(name2, sizeof(name2), name);
_gnutls_str_cat(name2, sizeof(name2), ".extnValue");
result = _gnutls_x509_write_value(asn, name2, ext_data);
if (result < 0) {
gnutls_assert();
return result;
}
return 0;
}
int
_gnutls_set_extension(ASN1_TYPE asn, const char *root,
const char *ext_id,
const gnutls_datum_t * ext_data, unsigned int critical)
{
int result = 0;
int k, len;
char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
char extnID[MAX_OID_SIZE];
/* Find the index of the given extension.
*/
k = 0;
do {
k++;
if (root[0] != 0)
snprintf(name, sizeof(name), "%s.?%u", root, k);
else
snprintf(name, sizeof(name), "?%u", k);
len = sizeof(extnID) - 1;
result = asn1_read_value(asn, name, extnID, &len);
/* move to next
*/
if (result == ASN1_ELEMENT_NOT_FOUND) {
break;
}
do {
_gnutls_str_cpy(name2, sizeof(name2), name);
_gnutls_str_cat(name2, sizeof(name2), ".extnID");
len = sizeof(extnID) - 1;
result = asn1_read_value(asn, name2, extnID, &len);
if (result == ASN1_ELEMENT_NOT_FOUND) {
gnutls_assert();
break;
} else if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
/* Handle Extension
*/
if (strcmp(extnID, ext_id) == 0) {
/* extension was found
*/
return overwrite_extension(asn, root, k,
ext_data,
critical);
}
}
while (0);
}
while (1);
if (result == ASN1_ELEMENT_NOT_FOUND) {
return add_extension(asn, root, ext_id, ext_data,
critical);
} else {
gnutls_assert();
return _gnutls_asn2err(result);
}
return 0;
}
/* This function will attempt to overwrite the requested extension with
* the given one.
*
* Critical will be either 0 or 1.
*/
int
_gnutls_x509_crt_set_extension(gnutls_x509_crt_t cert,
const char *ext_id,
const gnutls_datum_t * ext_data,
unsigned int critical)
{
MODIFIED(cert);
cert->use_extensions = 1;
return _gnutls_set_extension(cert->cert, "tbsCertificate.extensions",
ext_id, ext_data, critical);
}
int
_gnutls_x509_crl_set_extension(gnutls_x509_crl_t crl,
const char *ext_id,
const gnutls_datum_t * ext_data,
unsigned int critical)
{
return _gnutls_set_extension(crl->crl, "tbsCertList.crlExtensions", ext_id,
ext_data, critical);
}
int
_gnutls_x509_crq_set_extension(gnutls_x509_crq_t crq,
const char *ext_id,
const gnutls_datum_t * ext_data,
unsigned int critical)
{
unsigned char *extensions = NULL;
size_t extensions_size = 0;
gnutls_datum_t der;
ASN1_TYPE c2;
int result;
result =
gnutls_x509_crq_get_attribute_by_oid(crq,
"1.2.840.113549.1.9.14",
0, NULL,
&extensions_size);
if (result == GNUTLS_E_SHORT_MEMORY_BUFFER) {
extensions = gnutls_malloc(extensions_size);
if (extensions == NULL) {
gnutls_assert();
return GNUTLS_E_MEMORY_ERROR;
}
result = gnutls_x509_crq_get_attribute_by_oid(crq,
"1.2.840.113549.1.9.14",
0,
extensions,
&extensions_size);
}
if (result < 0) {
if (result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
extensions_size = 0;
} else {
gnutls_assert();
gnutls_free(extensions);
return result;
}
}
result =
asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions",
&c2);
if (result != ASN1_SUCCESS) {
gnutls_assert();
gnutls_free(extensions);
return _gnutls_asn2err(result);
}
if (extensions_size > 0) {
result =
_asn1_strict_der_decode(&c2, extensions, extensions_size,
NULL);
gnutls_free(extensions);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&c2);
return _gnutls_asn2err(result);
}
}
result = _gnutls_set_extension(c2, "", ext_id, ext_data, critical);
if (result < 0) {
gnutls_assert();
asn1_delete_structure(&c2);
return result;
}
result = _gnutls_x509_der_encode(c2, "", &der, 0);
asn1_delete_structure(&c2);
if (result < 0) {
gnutls_assert();
return result;
}
result =
gnutls_x509_crq_set_attribute_by_oid(crq,
"1.2.840.113549.1.9.14",
der.data, der.size);
gnutls_free(der.data);
if (result < 0) {
gnutls_assert();
return result;
}
return 0;
}
/* extract an INTEGER from the DER encoded extension
*/
int
_gnutls_x509_ext_extract_number(uint8_t * number,
size_t * _nr_size,
uint8_t * extnValue, int extnValueLen)
{
ASN1_TYPE ext = ASN1_TYPE_EMPTY;
int result;
int nr_size = *_nr_size;
/* here it doesn't matter so much that we use CertificateSerialNumber. It is equal
* to using INTEGER.
*/
if ((result = asn1_create_element
(_gnutls_get_pkix(), "PKIX1.CertificateSerialNumber",
&ext)) != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = _asn1_strict_der_decode(&ext, extnValue, extnValueLen, NULL);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&ext);
return _gnutls_asn2err(result);
}
/* the default value of cA is false.
*/
result = asn1_read_value(ext, "", number, &nr_size);
if (result != ASN1_SUCCESS)
result = _gnutls_asn2err(result);
else
result = 0;
*_nr_size = nr_size;
asn1_delete_structure(&ext);
return result;
}
/* generate an INTEGER in a DER encoded extension
*/
int
_gnutls_x509_ext_gen_number(const uint8_t * number, size_t nr_size,
gnutls_datum_t * der_ext)
{
ASN1_TYPE ext = ASN1_TYPE_EMPTY;
int result;
result =
asn1_create_element(_gnutls_get_pkix(),
"PKIX1.CertificateSerialNumber", &ext);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
result = asn1_write_value(ext, "", number, nr_size);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&ext);
return _gnutls_asn2err(result);
}
result = _gnutls_x509_der_encode(ext, "", der_ext, 0);
asn1_delete_structure(&ext);
if (result < 0) {
gnutls_assert();
return result;
}
return 0;
}
int
_gnutls_write_general_name(ASN1_TYPE ext, const char *ext_name,
gnutls_x509_subject_alt_name_t type,
const void *data, unsigned int data_size)
{
const char *str;
int result;
char name[128];
if (data == NULL) {
if (data_size == 0)
data = (void*)"";
else
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
}
switch (type) {
case GNUTLS_SAN_DNSNAME:
str = "dNSName";
break;
case GNUTLS_SAN_RFC822NAME:
str = "rfc822Name";
break;
case GNUTLS_SAN_URI:
str = "uniformResourceIdentifier";
break;
case GNUTLS_SAN_IPADDRESS:
str = "iPAddress";
break;
default:
gnutls_assert();
return GNUTLS_E_INTERNAL_ERROR;
}
result = asn1_write_value(ext, ext_name, str, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
snprintf(name, sizeof(name), "%s.%s", ext_name, str);
result = asn1_write_value(ext, name, data, data_size);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&ext);
return _gnutls_asn2err(result);
}
return 0;
}
int
_gnutls_write_new_general_name(ASN1_TYPE ext, const char *ext_name,
gnutls_x509_subject_alt_name_t type,
const void *data, unsigned int data_size)
{
int result;
char name[128];
result = asn1_write_value(ext, ext_name, "NEW", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (ext_name[0] == 0) { /* no dot */
_gnutls_str_cpy(name, sizeof(name), "?LAST");
} else {
_gnutls_str_cpy(name, sizeof(name), ext_name);
_gnutls_str_cat(name, sizeof(name), ".?LAST");
}
result = _gnutls_write_general_name(ext, name, type,
data, data_size);
if (result < 0) {
gnutls_assert();
return result;
}
return 0;
}
int
_gnutls_write_new_othername(ASN1_TYPE ext, const char *ext_name,
const char *oid,
const void *data, unsigned int data_size)
{
int result;
char name[128];
char name2[128];
result = asn1_write_value(ext, ext_name, "NEW", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
if (ext_name[0] == 0) { /* no dot */
_gnutls_str_cpy(name, sizeof(name), "?LAST");
} else {
_gnutls_str_cpy(name, sizeof(name), ext_name);
_gnutls_str_cat(name, sizeof(name), ".?LAST");
}
result = asn1_write_value(ext, name, "otherName", 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
return _gnutls_asn2err(result);
}
snprintf(name2, sizeof(name2), "%s.otherName.type-id", name);
result = asn1_write_value(ext, name2, oid, 1);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&ext);
return _gnutls_asn2err(result);
}
snprintf(name2, sizeof(name2), "%s.otherName.value", name);
result = asn1_write_value(ext, name2, data, data_size);
if (result != ASN1_SUCCESS) {
gnutls_assert();
asn1_delete_structure(&ext);
return _gnutls_asn2err(result);
}
return 0;
}
/* Convert the given name to GeneralNames in a DER encoded extension.
* This is the same as subject alternative name.
*/
int
_gnutls_x509_ext_gen_subject_alt_name(gnutls_x509_subject_alt_name_t
type,
const char *othername_oid,
const void *data,
unsigned int data_size,
const gnutls_datum_t * prev_der_ext,
gnutls_datum_t * der_ext)
{
int ret;
gnutls_subject_alt_names_t sans = NULL;
gnutls_datum_t name;
ret = gnutls_subject_alt_names_init(&sans);
if (ret < 0) {
gnutls_assert();
return ret;
}
if (prev_der_ext && prev_der_ext->data != NULL &&
prev_der_ext->size != 0) {
ret = gnutls_x509_ext_import_subject_alt_names(prev_der_ext, sans, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
}
name.data = (void*)data;
name.size = data_size;
ret = gnutls_subject_alt_names_set(sans, type, &name, othername_oid);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = gnutls_x509_ext_export_subject_alt_names(sans, der_ext);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
if (sans != NULL)
gnutls_subject_alt_names_deinit(sans);
return ret;
}
/* generate the AuthorityKeyID in a DER encoded extension
*/
int
_gnutls_x509_ext_gen_auth_key_id(const void *id, size_t id_size,
gnutls_datum_t * der_ext)
{
gnutls_x509_aki_t aki;
int ret;
gnutls_datum_t l_id;
ret = gnutls_x509_aki_init(&aki);
if (ret < 0)
return gnutls_assert_val(ret);
l_id.data = (void*)id;
l_id.size = id_size;
ret = gnutls_x509_aki_set_id(aki, &l_id);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = gnutls_x509_ext_export_authority_key_id(aki, der_ext);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
ret = 0;
cleanup:
gnutls_x509_aki_deinit(aki);
return ret;
}