diff options
Diffstat (limited to 'lib/x509/x509.c')
-rw-r--r-- | lib/x509/x509.c | 4689 |
1 files changed, 4689 insertions, 0 deletions
diff --git a/lib/x509/x509.c b/lib/x509/x509.c new file mode 100644 index 0000000..50dcc8e --- /dev/null +++ b/lib/x509/x509.c @@ -0,0 +1,4689 @@ +/* + * Copyright (C) 2003-2018 Free Software Foundation, Inc. + * Copyright (C) 2018 Red Hat, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Simon Josefsson, Howard Chu + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* Functions on X.509 Certificate parsing + */ + +#include "gnutls_int.h" +#include <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <gnutls/x509-ext.h> +#include <x509.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <libtasn1.h> +#include <pk.h> +#include <pkcs11_int.h> +#include "urls.h" +#include "system-keys.h" +#include "hash.h" +#include "hash-pjw-bare.h" + +static int crt_reinit(gnutls_x509_crt_t crt) +{ + int result; + + _gnutls_free_datum(&crt->der); + crt->raw_dn.size = 0; + crt->raw_issuer_dn.size = 0; + crt->raw_spki.size = 0; + + asn1_delete_structure(&crt->cert); + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Certificate", + &crt->cert); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + return result; + } + + gnutls_subject_alt_names_deinit(crt->san); + result = gnutls_subject_alt_names_init(&crt->san); + if (result < 0) { + gnutls_assert(); + return result; + } + + gnutls_subject_alt_names_deinit(crt->ian); + result = gnutls_subject_alt_names_init(&crt->ian); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_equals - This function compares two gnutls_x509_crt_t certificates + * @cert1: The first certificate + * @cert2: The second certificate + * + * This function will compare two X.509 certificate structures. + * + * Returns: On equality non-zero is returned, otherwise zero. + * + * Since: 3.5.0 + **/ +unsigned gnutls_x509_crt_equals(gnutls_x509_crt_t cert1, + gnutls_x509_crt_t cert2) +{ + int ret; + bool result; + + if (cert1->modified == 0 && cert2->modified == 0 && + cert1->raw_dn.size > 0 && cert2->raw_dn.size > 0) { + ret = _gnutls_is_same_dn(cert1, cert2); + if (ret == 0) + return 0; + } + + if (cert1->der.size == 0 || cert2->der.size == 0 || + cert1->modified != 0 || cert2->modified != 0) { + gnutls_datum_t tmp1, tmp2; + + /* on uninitialized or modified certificates, we have to re-encode */ + ret = + gnutls_x509_crt_export2(cert1, GNUTLS_X509_FMT_DER, &tmp1); + if (ret < 0) + return gnutls_assert_val(0); + + ret = + gnutls_x509_crt_export2(cert2, GNUTLS_X509_FMT_DER, &tmp2); + if (ret < 0) { + gnutls_free(tmp1.data); + return gnutls_assert_val(0); + } + + if ((tmp1.size == tmp2.size) && + (memcmp(tmp1.data, tmp2.data, tmp1.size) == 0)) + result = 1; + else + result = 0; + + gnutls_free(tmp1.data); + gnutls_free(tmp2.data); + } else { + if ((cert1->der.size == cert2->der.size) && + (memcmp(cert1->der.data, cert2->der.data, cert1->der.size) == 0)) + result = 1; + else + result = 0; + } + + return result; +} + +/** + * gnutls_x509_crt_equals2 - This function compares a gnutls_x509_crt_t cert with DER data + * @cert1: The first certificate + * @der: A DER encoded certificate + * + * This function will compare an X.509 certificate structures, with DER + * encoded certificate data. + * + * Returns: On equality non-zero is returned, otherwise zero. + * + * Since: 3.5.0 + **/ +unsigned +gnutls_x509_crt_equals2(gnutls_x509_crt_t cert1, + const gnutls_datum_t * der) +{ + bool result; + + if (cert1 == NULL || der == NULL) + return 0; + + if (cert1->der.size == 0 || cert1->modified) { + gnutls_datum_t tmp1; + int ret; + + /* on uninitialized or modified certificates, we have to re-encode */ + ret = + gnutls_x509_crt_export2(cert1, GNUTLS_X509_FMT_DER, &tmp1); + if (ret < 0) + return gnutls_assert_val(0); + + if ((tmp1.size == der->size) && + (memcmp(tmp1.data, der->data, tmp1.size) == 0)) + result = 1; + else + result = 0; + + gnutls_free(tmp1.data); + } else { + if ((cert1->der.size == der->size) && + (memcmp(cert1->der.data, der->data, cert1->der.size) == 0)) + result = 1; + else + result = 0; + } + + return result; +} + +/** + * gnutls_x509_crt_init: + * @cert: A pointer to the type to be initialized + * + * This function will initialize an X.509 certificate structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int gnutls_x509_crt_init(gnutls_x509_crt_t * cert) +{ + gnutls_x509_crt_t tmp; + int result; + + FAIL_IF_LIB_ERROR; + + tmp = + gnutls_calloc(1, sizeof(gnutls_x509_crt_int)); + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.Certificate", &tmp->cert); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(tmp); + return _gnutls_asn2err(result); + } + + result = gnutls_subject_alt_names_init(&tmp->san); + if (result < 0) { + gnutls_assert(); + asn1_delete_structure(&tmp->cert); + gnutls_free(tmp); + return result; + } + + result = gnutls_subject_alt_names_init(&tmp->ian); + if (result < 0) { + gnutls_assert(); + asn1_delete_structure(&tmp->cert); + gnutls_subject_alt_names_deinit(tmp->san); + gnutls_free(tmp); + return result; + } + + /* If you add anything here, be sure to check if it has to be added + to gnutls_x509_crt_import as well. */ + + *cert = tmp; + + return 0; /* success */ +} + +/*- + * _gnutls_x509_crt_cpy - This function copies a gnutls_x509_crt_t type + * @dest: The data where to copy + * @src: The data to be copied + * @flags: zero or CRT_CPY_FAST + * + * This function will copy an X.509 certificate structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + -*/ +int _gnutls_x509_crt_cpy(gnutls_x509_crt_t dest, gnutls_x509_crt_t src) +{ + int ret; + gnutls_datum_t tmp; + unsigned dealloc = 0; + + if (src->der.size == 0 || src->modified) { + ret = + gnutls_x509_crt_export2(src, GNUTLS_X509_FMT_DER, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); + dealloc = 1; + } else { + tmp.data = src->der.data; + tmp.size = src->der.size; + } + + ret = gnutls_x509_crt_import(dest, &tmp, GNUTLS_X509_FMT_DER); + + if (dealloc) { + gnutls_free(tmp.data); + } + + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + +/** + * gnutls_x509_crt_deinit: + * @cert: The data to be deinitialized + * + * This function will deinitialize a certificate structure. + **/ +void gnutls_x509_crt_deinit(gnutls_x509_crt_t cert) +{ + if (!cert) + return; + + if (cert->cert) + asn1_delete_structure(&cert->cert); + gnutls_free(cert->der.data); + gnutls_subject_alt_names_deinit(cert->san); + gnutls_subject_alt_names_deinit(cert->ian); + gnutls_free(cert); +} + +static int compare_sig_algorithm(gnutls_x509_crt_t cert) +{ + int ret, len1, len2, result; + char oid1[MAX_OID_SIZE]; + char oid2[MAX_OID_SIZE]; + gnutls_datum_t sp1 = {NULL, 0}; + gnutls_datum_t sp2 = {NULL, 0}; + unsigned empty1 = 0, empty2 = 0; + + len1 = sizeof(oid1); + result = asn1_read_value(cert->cert, "signatureAlgorithm.algorithm", oid1, &len1); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + len2 = sizeof(oid2); + result = asn1_read_value(cert->cert, "tbsCertificate.signature.algorithm", oid2, &len2); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (len1 != len2 || memcmp(oid1, oid2, len1) != 0) { + _gnutls_debug_log("signatureAlgorithm.algorithm differs from tbsCertificate.signature.algorithm: %s, %s\n", + oid1, oid2); + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* compare the parameters */ + ret = _gnutls_x509_read_value(cert->cert, "signatureAlgorithm.parameters", &sp1); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + empty1 = 1; + } else if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_read_value(cert->cert, "tbsCertificate.signature.parameters", &sp2); + if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { + empty2 = 1; + } else if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* handle equally empty parameters with missing parameters */ + if (sp1.size == 2 && memcmp(sp1.data, "\x05\x00", 2) == 0) { + empty1 = 1; + _gnutls_free_datum(&sp1); + } + + if (sp2.size == 2 && memcmp(sp2.data, "\x05\x00", 2) == 0) { + empty2 = 1; + _gnutls_free_datum(&sp2); + } + + if (empty1 != empty2 || + sp1.size != sp2.size || + (sp1.size > 0 && memcmp(sp1.data, sp2.data, sp1.size) != 0)) { + gnutls_assert(); + ret = GNUTLS_E_CERTIFICATE_ERROR; + goto cleanup; + } + + ret = 0; + cleanup: + _gnutls_free_datum(&sp1); + _gnutls_free_datum(&sp2); + return ret; +} + +static int cache_alt_names(gnutls_x509_crt_t cert) +{ + gnutls_datum_t tmpder = {NULL, 0}; + int ret; + + /* pre-parse subject alt name */ + ret = _gnutls_x509_crt_get_extension(cert, "2.5.29.17", 0, &tmpder, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_free(tmpder.data); + return gnutls_assert_val(ret); + } + + if (ret >= 0) { + ret = gnutls_x509_ext_import_subject_alt_names(&tmpder, cert->san, 0); + gnutls_free(tmpder.data); + if (ret < 0) + return gnutls_assert_val(ret); + } + + ret = _gnutls_x509_crt_get_extension(cert, "2.5.29.18", 0, &tmpder, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return gnutls_assert_val(ret); + + if (ret >= 0) { + ret = gnutls_x509_ext_import_subject_alt_names(&tmpder, cert->ian, 0); + gnutls_free(tmpder.data); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return 0; +} + +static bool hcomparator(const void *v1, const void *v2) +{ + return (strcmp(v1, v2)==0); +} + +static size_t hhasher(const void *entry, size_t n) +{ + const char *e = entry; + if (e == NULL || e[0] == 0) + return 0; + + return hash_pjw_bare(e, strlen(e)) % n; +} + +#ifdef STRICT_X509 + +/* Check whether certificates serial number is RFC5280 compliant */ +static bool has_valid_serial(gnutls_x509_crt_t cert) +{ + int err, is_zero; + unsigned i; + unsigned char serial[128]; + size_t serial_size = sizeof(serial); + + err = gnutls_x509_crt_get_serial(cert, serial, &serial_size); + if (err < 0) { + _gnutls_debug_log("error: could not read serial number\n"); + return false; + } + + if (serial_size > 20) { + _gnutls_debug_log("error: serial number value is longer than 20 octets\n"); + return false; + } + + if (serial[0] & 0x80) { + _gnutls_debug_log("error: serial number is negative\n"); + return false; + } + + is_zero = 1; + for (i = 0; i < serial_size; ++i) { + if (serial[i]) { + is_zero = 0; + break; + } + } + + if (is_zero) { + _gnutls_debug_log("error: serial number is zero\n"); + return false; + } + + return true; +} + +/* Check if extension can be successfully parsed */ +static bool is_valid_extension(const char *oid, gnutls_datum_t *der) +{ + int err = 0, i; + unsigned u; + size_t sz; + time_t t1, t2; + char *s1 = NULL, *s2 = NULL; + gnutls_datum_t datum = {NULL, 0}; + + if (!strcmp(oid, GNUTLS_X509EXT_OID_BASIC_CONSTRAINTS)) { + err = gnutls_x509_ext_import_basic_constraints(der, &u, &i); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_SUBJECT_KEY_ID)) { + err = gnutls_x509_ext_import_subject_key_id(der, &datum); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CRT_POLICY)) { + gnutls_x509_policies_t policies; + if (gnutls_x509_policies_init(&policies) < 0) + return false; + err = gnutls_x509_ext_import_policies(der, policies, 0); + gnutls_x509_policies_deinit(policies); + } else if (!strcmp(oid, GNUTLS_X509_OID_POLICY_ANY)) { + err = gnutls_x509_ext_import_inhibit_anypolicy(der, &u); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_AUTHORITY_KEY_ID)) { + gnutls_x509_aki_t aki; + if (gnutls_x509_aki_init(&aki) < 0) + return false; + err = gnutls_x509_ext_import_authority_key_id(der, aki, 0); + gnutls_x509_aki_deinit(aki); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_KEY_USAGE)) { + err = gnutls_x509_ext_import_key_usage(der, &u); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_PRIVATE_KEY_USAGE_PERIOD)) { + err = gnutls_x509_ext_import_private_key_usage_period(der, &t1, &t2); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_EXTENDED_KEY_USAGE)) { + gnutls_x509_key_purposes_t purposes; + if (gnutls_x509_key_purpose_init(&purposes) < 0) + return false; + err = gnutls_x509_ext_import_key_purposes(der, purposes, 0); + gnutls_x509_key_purpose_deinit(purposes); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_SAN) || + !strcmp(oid, GNUTLS_X509EXT_OID_IAN)) { + gnutls_subject_alt_names_t names; + if (gnutls_subject_alt_names_init(&names) < 0) + return false; + err = gnutls_x509_ext_import_subject_alt_names(der, names, 0); + gnutls_subject_alt_names_deinit(names); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CRL_DIST_POINTS)) { + gnutls_x509_crl_dist_points_t dp; + if (gnutls_x509_crl_dist_points_init(&dp) < 0) + return false; + err = gnutls_x509_ext_import_crl_dist_points(der, dp, 0); + gnutls_x509_crl_dist_points_deinit(dp); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_PROXY_CRT_INFO)) { + err = gnutls_x509_ext_import_proxy(der, &i, &s1, &s2, &sz); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_AUTHORITY_INFO_ACCESS)) { + gnutls_x509_aia_t aia; + if (gnutls_x509_aia_init(&aia) < 0) + return false; + err = gnutls_x509_ext_import_aia(der, aia, 0); + gnutls_x509_aia_deinit(aia); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CT_SCT_V1)) { + gnutls_x509_ct_scts_t scts; + if (gnutls_x509_ext_ct_scts_init(&scts) < 0) + return false; + err = gnutls_x509_ext_ct_import_scts(der, scts, 0); + gnutls_x509_ext_ct_scts_deinit(scts); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_NAME_CONSTRAINTS)) { + gnutls_x509_name_constraints_t nc; + if (gnutls_x509_name_constraints_init(&nc) < 0) + return false; + err = gnutls_x509_ext_import_name_constraints(der, nc, 0); + gnutls_x509_name_constraints_deinit(nc); + } else if (!strcmp(oid, GNUTLS_X509EXT_OID_TLSFEATURES)) { + gnutls_x509_tlsfeatures_t features; + if (gnutls_x509_tlsfeatures_init(&features) < 0) + return false; + err = gnutls_x509_ext_import_tlsfeatures(der, features, 0); + gnutls_x509_tlsfeatures_deinit(features); + } else { + return true; + } + + gnutls_free(s1); + gnutls_free(s2); + _gnutls_free_datum(&datum); + + return err == 0; +} + +#endif /* STRICT_X509 */ + +int _gnutls_check_cert_sanity(gnutls_x509_crt_t cert) +{ + int ret = 0, version; + gnutls_datum_t exts; + Hash_table *htable = NULL; + + if (cert->flags & GNUTLS_X509_CRT_FLAG_IGNORE_SANITY) + return 0; + + /* enforce the rule that only version 3 certificates carry extensions */ + ret = gnutls_x509_crt_get_version(cert); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + version = ret; + +#ifdef STRICT_X509 + /* enforce upper bound on certificate version (RFC5280 compliant) */ + if (version > 3) { + _gnutls_debug_log("error: invalid certificate version %d\n", version); + return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + } +#endif + + if (version < 3) { + if (!cert->modified) { + ret = _gnutls_x509_get_raw_field2(cert->cert, &cert->der, + "tbsCertificate.extensions", &exts); + if (ret >= 0 && exts.size > 0) { + _gnutls_debug_log("error: extensions present in certificate with version %d\n", version); + return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + } + } else { + if (cert->use_extensions) { + _gnutls_debug_log("error: extensions set in certificate with version %d\n", version); + return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + } + } + } else { + /* Version is 3; ensure no duplicate extensions are present. */ + unsigned i, critical; + char oid[MAX_OID_SIZE]; + size_t oid_size; + char *o; + + htable = hash_initialize(16, NULL, hhasher, hcomparator, gnutls_free); + if (htable == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + for (i=0;;i++) { + oid_size = sizeof(oid); + ret = gnutls_x509_crt_get_extension_info(cert, i, oid, &oid_size, &critical); + if (ret < 0) { + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + gnutls_assert(); + goto cleanup; + } + o = gnutls_strdup(oid); + if (o == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + ret = hash_insert_if_absent(htable, o, NULL); + if (ret == -1) { + gnutls_free(o); + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } else if (ret == 0) { + /* duplicate */ + gnutls_free(o); + _gnutls_debug_log("error: duplicate extension (%s) detected\n", oid); + ret = gnutls_assert_val(GNUTLS_E_X509_DUPLICATE_EXTENSION); + goto cleanup; + } + +#ifdef STRICT_X509 + gnutls_datum_t der = { NULL, 0 }; + ret = gnutls_x509_crt_get_extension_data2(cert, i, &der); + if (ret < 0) + continue; + if (critical && !is_valid_extension(oid, &der)) { + _gnutls_free_datum(&der); + _gnutls_debug_log("error: could not parse extension (%s)\n"); + return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + } + _gnutls_free_datum(&der); +#endif + } + + hash_free(htable); + htable = NULL; + } + + if (version < 2) { + char id[128]; + size_t id_size; + + id_size = sizeof(id); + ret = gnutls_x509_crt_get_subject_unique_id(cert, id, &id_size); + if (ret >= 0 || ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + _gnutls_debug_log("error: subjectUniqueID present in certificate with version %d\n", version); + ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + goto cleanup; + } + + id_size = sizeof(id); + ret = gnutls_x509_crt_get_issuer_unique_id(cert, id, &id_size); + if (ret >= 0 || ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + _gnutls_debug_log("error: subjectUniqueID present in certificate with version %d\n", version); + ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + goto cleanup; + } + } + +#ifdef STRICT_X509 + if (!has_valid_serial(cert)) { + ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR); + goto cleanup; + } +#endif + + if (gnutls_x509_crt_get_expiration_time(cert) == -1 || + gnutls_x509_crt_get_activation_time(cert) == -1) { + _gnutls_debug_log("error: invalid expiration or activation time in certificate\n"); + ret = gnutls_assert_val(GNUTLS_E_CERTIFICATE_TIME_ERROR); + goto cleanup; + } + + ret = 0; + + cleanup: + if (htable) + hash_free(htable); + return ret; +} + +/** + * gnutls_x509_crt_import: + * @cert: The data to store the parsed certificate. + * @data: The DER or PEM encoded certificate. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded Certificate + * to the native gnutls_x509_crt_t format. The output will be stored + * in @cert. + * + * If the Certificate is PEM encoded it should have a header of "X509 + * CERTIFICATE", or "CERTIFICATE". + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_import(gnutls_x509_crt_t cert, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (cert->expanded) { + /* Any earlier _asn1_strict_der_decode will modify the ASN.1 + structure, so we need to replace it with a fresh + structure. */ + result = crt_reinit(cert); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + } + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + /* Try the first header */ + result = + _gnutls_fbase64_decode(PEM_X509_CERT2, data->data, + data->size, &cert->der); + + if (result < 0) { + /* try for the second header */ + result = + _gnutls_fbase64_decode(PEM_X509_CERT, + data->data, data->size, + &cert->der); + + if (result < 0) { + gnutls_assert(); + return result; + } + } + } else { + result = _gnutls_set_datum(&cert->der, data->data, data->size); + if (result < 0) { + gnutls_assert(); + return result; + } + } + + cert->expanded = 1; + cert->modified = 0; + + result = + _asn1_strict_der_decode(&cert->cert, cert->der.data, cert->der.size, NULL); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + result = compare_sig_algorithm(cert); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* The following do not allocate but rather point to DER data */ + result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der, + "tbsCertificate.issuer.rdnSequence", + &cert->raw_issuer_dn); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der, + "tbsCertificate.subject.rdnSequence", + &cert->raw_dn); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der, + "tbsCertificate.subjectPublicKeyInfo", + &cert->raw_spki); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = cache_alt_names(cert); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_check_cert_sanity(cert); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + /* Since we do not want to disable any extension + */ + cert->use_extensions = 1; + + return 0; + + cleanup: + _gnutls_free_datum(&cert->der); + return result; +} + + +/** + * gnutls_x509_crt_get_issuer_dn: + * @cert: should contain a #gnutls_x509_crt_t type + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * + * This function will copy the name of the Certificate issuer in the + * provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC4514. The output string + * will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * If @buf is null then only the size will be filled. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crt_get_issuer_dn3(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated + * with the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if + * the DN does not exist, or another error value on error. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_issuer_dn(gnutls_x509_crt_t cert, char *buf, + size_t * buf_size) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn(cert->cert, + "tbsCertificate.issuer.rdnSequence", + buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crt_get_issuer_dn2: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * + * This function will allocate buffer and copy the name of issuer of the Certificate. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crt_get_issuer_dn3(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.10 + **/ +int +gnutls_x509_crt_get_issuer_dn2(gnutls_x509_crt_t cert, gnutls_datum_t * dn) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(cert->cert, + "tbsCertificate.issuer.rdnSequence", + dn, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crt_get_issuer_dn3: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will allocate buffer and copy the name of issuer of the Certificate. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.7 + **/ +int +gnutls_x509_crt_get_issuer_dn3(gnutls_x509_crt_t cert, gnutls_datum_t *dn, unsigned flags) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(cert->cert, + "tbsCertificate.issuer.rdnSequence", + dn, flags); +} + +/** + * gnutls_x509_crt_get_issuer_dn_by_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use (0) to get the first one. + * @raw_flag: If non-zero returns the raw DER data of the DN part. + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * issuer specified by the given OID. The output, if the raw flag is not + * used, will be encoded as described in RFC4514. Thus a string that is + * ASCII or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is (0), this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC4514 -- + * in hex format with a '#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * If @buf is null then only the size will be filled. If the @raw_flag + * is not specified the output is always null terminated, although the + * @buf_size will not include the null character. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated with + * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there + * are no data in the current index. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_issuer_dn_by_oid(gnutls_x509_crt_t cert, + const char *oid, unsigned indx, + unsigned int raw_flag, void *buf, + size_t * buf_size) +{ + gnutls_datum_t td; + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_parse_dn_oid(cert->cert, + "tbsCertificate.issuer.rdnSequence", + oid, indx, raw_flag, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_crt_get_issuer_dn_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: This specifies which OID to return. Use (0) to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will extract the OIDs of the name of the Certificate + * issuer specified by the given index. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated with + * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there + * are no data in the current index. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_issuer_dn_oid(gnutls_x509_crt_t cert, + unsigned indx, void *oid, size_t * oid_size) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid(cert->cert, + "tbsCertificate.issuer.rdnSequence", + indx, oid, oid_size); +} + +/** + * gnutls_x509_crt_get_dn: + * @cert: should contain a #gnutls_x509_crt_t type + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * + * This function will copy the name of the Certificate in the provided + * buffer. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * The @buf returned will be null terminated and the @buf_size will account + * for the trailing null. If @buf is null then only the size will be filled. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crt_get_dn3(). + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated + * with the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if + * the DN does not exist, or another error value on error. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_dn(gnutls_x509_crt_t cert, char *buf, + size_t * buf_size) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn(cert->cert, + "tbsCertificate.subject.rdnSequence", + buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crt_get_dn2: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * + * This function will allocate buffer and copy the name of the Certificate. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * This function does not output a fully RFC4514 compliant string, if + * that is required see gnutls_x509_crt_get_dn3(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.10 + **/ +int gnutls_x509_crt_get_dn2(gnutls_x509_crt_t cert, gnutls_datum_t * dn) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(cert->cert, + "tbsCertificate.subject.rdnSequence", + dn, GNUTLS_X509_DN_FLAG_COMPAT); +} + +/** + * gnutls_x509_crt_get_dn3: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free() + * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT + * + * This function will allocate buffer and copy the name of the Certificate. + * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as + * described in RFC4514. The output string will be ASCII or UTF-8 + * encoded, depending on the certificate data. + * + * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output + * format will match the format output by previous to 3.5.6 versions of GnuTLS + * which was not not fully RFC4514-compliant. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.5.7 + **/ +int gnutls_x509_crt_get_dn3(gnutls_x509_crt_t cert, gnutls_datum_t *dn, unsigned flags) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn(cert->cert, + "tbsCertificate.subject.rdnSequence", + dn, flags); +} + +/** + * gnutls_x509_crt_get_dn_by_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use (0) to get the first one. + * @raw_flag: If non-zero returns the raw DER data of the DN part. + * @buf: a pointer where the DN part will be copied (may be null). + * @buf_size: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * subject specified by the given OID. The output, if the raw flag is + * not used, will be encoded as described in RFC4514. Thus a string + * that is ASCII or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is (0), this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC4514 -- + * in hex format with a '#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * If @buf is null then only the size will be filled. If the @raw_flag + * is not specified the output is always null terminated, although the + * @buf_size will not include the null character. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated with + * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there + * are no data in the current index. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_dn_by_oid(gnutls_x509_crt_t cert, const char *oid, + unsigned indx, unsigned int raw_flag, + void *buf, size_t * buf_size) +{ + gnutls_datum_t td; + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_parse_dn_oid(cert->cert, + "tbsCertificate.subject.rdnSequence", + oid, indx, raw_flag, &td); + if (ret < 0) + return gnutls_assert_val(ret); + + return _gnutls_strdatum_to_buf(&td, buf, buf_size); +} + +/** + * gnutls_x509_crt_get_dn_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: This specifies which OID to return. Use (0) to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will extract the OIDs of the name of the Certificate + * subject specified by the given index. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the @buf_size will be updated with + * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there + * are no data in the current index. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_dn_oid(gnutls_x509_crt_t cert, + unsigned indx, void *oid, size_t * oid_size) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid(cert->cert, + "tbsCertificate.subject.rdnSequence", + indx, oid, oid_size); +} + +/** + * gnutls_x509_crt_get_signature_algorithm: + * @cert: should contain a #gnutls_x509_crt_t type + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm that has been used to + * sign this certificate. + * + * Since 3.6.0 this function never returns a negative error code. + * Error cases and unknown/unsupported signature algorithms are + * mapped to %GNUTLS_SIGN_UNKNOWN. + * + * Returns: a #gnutls_sign_algorithm_t value + **/ +int gnutls_x509_crt_get_signature_algorithm(gnutls_x509_crt_t cert) +{ + return map_errs_to_zero(_gnutls_x509_get_signature_algorithm(cert->cert, + "signatureAlgorithm")); +} + +/** + * gnutls_x509_crt_get_signature_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the signature algorithm + * that has been used to sign this certificate. This is function + * is useful in the case gnutls_x509_crt_get_signature_algorithm() + * returned %GNUTLS_SIGN_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crt_get_signature_oid(gnutls_x509_crt_t cert, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(cert->cert, "signatureAlgorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_pk_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the OID of the public key algorithm + * on that certificate. This is function + * is useful in the case gnutls_x509_crt_get_pk_algorithm() + * returned %GNUTLS_PK_UNKNOWN. + * + * Returns: zero or a negative error code on error. + * + * Since: 3.5.0 + **/ +int gnutls_x509_crt_get_pk_oid(gnutls_x509_crt_t cert, char *oid, size_t *oid_size) +{ + char str[MAX_OID_SIZE]; + int len, result, ret; + gnutls_datum_t out; + + len = sizeof(str); + result = asn1_read_value(cert->cert, "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + out.data = (void*)str; + out.size = len; + + ret = _gnutls_copy_string(&out, (void*)oid, oid_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_signature: + * @cert: should contain a #gnutls_x509_crt_t type + * @sig: a pointer where the signature part will be copied (may be null). + * @sig_size: initially holds the size of @sig + * + * This function will extract the signature field of a certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_get_signature(gnutls_x509_crt_t cert, + char *sig, size_t * sig_size) +{ + gnutls_datum_t dsig = {NULL, 0}; + int ret; + + if (cert == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = _gnutls_x509_get_signature(cert->cert, "signature", &dsig); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_copy_data(&dsig, (uint8_t*)sig, sig_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(dsig.data); + return ret; +} + +/** + * gnutls_x509_crt_get_version: + * @cert: should contain a #gnutls_x509_crt_t type + * + * This function will return the version of the specified Certificate. + * + * Returns: version of certificate, or a negative error code on error. + **/ +int gnutls_x509_crt_get_version(gnutls_x509_crt_t cert) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_version(cert->cert, "tbsCertificate.version"); +} + +/** + * gnutls_x509_crt_get_activation_time: + * @cert: should contain a #gnutls_x509_crt_t type + * + * This function will return the time this Certificate was or will be + * activated. + * + * Returns: activation time, or (time_t)-1 on error. + **/ +time_t gnutls_x509_crt_get_activation_time(gnutls_x509_crt_t cert) +{ + if (cert == NULL) { + gnutls_assert(); + return (time_t) - 1; + } + + return _gnutls_x509_get_time(cert->cert, + "tbsCertificate.validity.notBefore", + 0); +} + +/** + * gnutls_x509_crt_get_expiration_time: + * @cert: should contain a #gnutls_x509_crt_t type + * + * This function will return the time this certificate was or will be + * expired. + * + * Returns: expiration time, or (time_t)-1 on error. + **/ +time_t gnutls_x509_crt_get_expiration_time(gnutls_x509_crt_t cert) +{ + if (cert == NULL) { + gnutls_assert(); + return (time_t) - 1; + } + + return _gnutls_x509_get_time(cert->cert, + "tbsCertificate.validity.notAfter", + 0); +} + +/** + * gnutls_x509_crt_get_private_key_usage_period: + * @cert: should contain a #gnutls_x509_crt_t type + * @activation: The activation time + * @expiration: The expiration time + * @critical: the extension status + * + * This function will return the expiration and activation + * times of the private key of the certificate. It relies on + * the PKIX extension 2.5.29.16 being present. + * + * 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. + **/ +int +gnutls_x509_crt_get_private_key_usage_period(gnutls_x509_crt_t cert, + time_t * activation, + time_t * expiration, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der = { NULL, 0 }; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.16", 0, &der, + critical); + if (ret < 0) + return gnutls_assert_val(ret); + + if (der.size == 0 || der.data == NULL) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + ret = gnutls_x509_ext_import_private_key_usage_period(&der, activation, expiration); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + _gnutls_free_datum(&der); + + return ret; +} + + +/** + * gnutls_x509_crt_get_serial: + * @cert: should contain a #gnutls_x509_crt_t type + * @result: The place where the serial number will be copied + * @result_size: Holds the size of the result field. + * + * This function will return the X.509 certificate's serial number. + * This is obtained by the X509 Certificate serialNumber field. Serial + * is not always a 32 or 64bit number. Some CAs use large serial + * numbers, thus it may be wise to handle it as something uint8_t. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crt_get_serial(gnutls_x509_crt_t cert, void *result, + size_t * result_size) +{ + int ret, len; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + len = *result_size; + ret = + asn1_read_value(cert->cert, "tbsCertificate.serialNumber", + result, &len); + *result_size = len; + + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + return 0; +} + +/** + * gnutls_x509_crt_get_subject_key_id: + * @cert: should contain a #gnutls_x509_crt_t type + * @ret: The place where the identifier will be copied + * @ret_size: Holds the size of the result field. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the X.509v3 certificate's subject key + * identifier. This is obtained by the X.509 Subject Key identifier + * extension field (2.5.29.14). + * + * 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. + **/ +int +gnutls_x509_crt_get_subject_key_id(gnutls_x509_crt_t cert, void *ret, + size_t * ret_size, + unsigned int *critical) +{ + int result; + gnutls_datum_t id = {NULL,0}; + gnutls_datum_t der = {NULL, 0}; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (ret == NULL) + *ret_size = 0; + + if ((result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.14", 0, &der, + critical)) < 0) { + return result; + } + + result = gnutls_x509_ext_import_subject_key_id(&der, &id); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_copy_data(&id, ret, ret_size); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + gnutls_free(der.data); + gnutls_free(id.data); + return result; +} + +inline static int is_type_printable(int type) +{ + if (type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_RFC822NAME || + type == GNUTLS_SAN_URI || type == GNUTLS_SAN_OTHERNAME_XMPP || + type == GNUTLS_SAN_OTHERNAME || type == GNUTLS_SAN_REGISTERED_ID) + return 1; + else + return 0; +} + +/** + * gnutls_x509_crt_get_authority_key_gn_serial: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @alt: is the place where the alternative name will be copied to + * @alt_size: holds the size of alt. + * @alt_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @serial: buffer to store the serial number (may be null) + * @serial_size: Holds the size of the serial field (may be null) + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the X.509 authority key + * identifier when stored as a general name (authorityCertIssuer) + * and serial number. + * + * Because more than one general names might be stored + * @seq can be used as a counter to request them all until + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * 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.0 + **/ +int +gnutls_x509_crt_get_authority_key_gn_serial(gnutls_x509_crt_t cert, + unsigned int seq, void *alt, + size_t * alt_size, + unsigned int *alt_type, + void *serial, + size_t * serial_size, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der, san, iserial; + gnutls_x509_aki_t aki = NULL; + unsigned san_type; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &der, + critical)) < 0) { + return gnutls_assert_val(ret); + } + + if (der.size == 0 || der.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_aki_init(&aki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_import_authority_key_id(&der, aki, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_aki_get_cert_issuer(aki, seq, &san_type, &san, NULL, &iserial); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (is_type_printable(san_type)) + ret = _gnutls_copy_string(&san, alt, alt_size); + else + ret = _gnutls_copy_data(&san, alt, alt_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (alt_type) + *alt_type = san_type; + + ret = _gnutls_copy_data(&iserial, serial, serial_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + if (aki != NULL) + gnutls_x509_aki_deinit(aki); + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_x509_crt_get_authority_key_id: + * @cert: should contain a #gnutls_x509_crt_t type + * @id: The place where the identifier will be copied + * @id_size: Holds the size of the id field. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the X.509v3 certificate authority's key + * identifier. This is obtained by the X.509 Authority Key + * identifier extension field (2.5.29.35). Note that this function + * only returns the keyIdentifier field of the extension and + * %GNUTLS_E_X509_UNSUPPORTED_EXTENSION, if the extension contains + * the name and serial number of the certificate. In that case + * gnutls_x509_crt_get_authority_key_gn_serial() may be used. + * + * 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. + **/ +int +gnutls_x509_crt_get_authority_key_id(gnutls_x509_crt_t cert, void *id, + size_t * id_size, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der, l_id; + gnutls_x509_aki_t aki = NULL; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &der, + critical)) < 0) { + return gnutls_assert_val(ret); + } + + if (der.size == 0 || der.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_aki_init(&aki); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_import_authority_key_id(&der, aki, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_aki_get_id(aki, &l_id); + + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + gnutls_datum_t serial; + ret = gnutls_x509_aki_get_cert_issuer(aki, 0, NULL, NULL, NULL, &serial); + if (ret >= 0) { + ret = gnutls_assert_val(GNUTLS_E_X509_UNSUPPORTED_EXTENSION); + } else { + ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_copy_data(&l_id, id, id_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + if (aki != NULL) + gnutls_x509_aki_deinit(aki); + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_algorithm: + * @cert: should contain a #gnutls_x509_crt_t type + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of an X.509 + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public + * exponent. + * + * Unknown/unsupported algorithms are mapped to %GNUTLS_PK_UNKNOWN. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + **/ +int +gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert, + unsigned int *bits) +{ + int result; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bits) + *bits = 0; + + result = + _gnutls_x509_get_pk_algorithm(cert->cert, + "tbsCertificate.subjectPublicKeyInfo", + NULL, + bits); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return result; +} + +/** + * gnutls_x509_crt_get_spki: + * @cert: a certificate of type #gnutls_x509_crt_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will return the public key information of an X.509 + * certificate. The provided @spki must be initialized. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crt_get_spki(gnutls_x509_crt_t cert, gnutls_x509_spki_t spki, unsigned int flags) +{ + int result; + gnutls_x509_spki_st params; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + + spki->pk = gnutls_x509_crt_get_pk_algorithm(cert, NULL); + + memset(¶ms, 0, sizeof(params)); + + result = _gnutls_x509_crt_read_spki_params(cert, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (params.pk == GNUTLS_PK_UNKNOWN) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + spki->rsa_pss_dig = params.rsa_pss_dig; + spki->salt_size = params.salt_size; + + return 0; +} + +/* returns the type and the name on success. + * Type is also returned as a parameter in case of an error. + * + * @seq: in case of GeneralNames it will return the corresponding name. + * in case of GeneralName, it must be -1 + * @dname: the name returned + * @ret_type: The type of the name + * @othername_oid: if the name is otherName return the OID + * + */ +int +_gnutls_parse_general_name2(asn1_node src, const char *src_name, + int seq, gnutls_datum_t *dname, + unsigned int *ret_type, int othername_oid) +{ + int len, ret; + char nptr[MAX_NAME_SIZE]; + int result; + gnutls_datum_t tmp = {NULL, 0}; + char choice_type[128]; + gnutls_x509_subject_alt_name_t type; + + if (seq != -1) { + seq++; /* 0->1, 1->2 etc */ + + if (src_name[0] != 0) + snprintf(nptr, sizeof(nptr), "%s.?%d", src_name, seq); + else + snprintf(nptr, sizeof(nptr), "?%d", seq); + } else { + snprintf(nptr, sizeof(nptr), "%s", src_name); + } + + len = sizeof(choice_type); + result = asn1_read_value(src, nptr, choice_type, &len); + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + type = _gnutls_x509_san_find_type(choice_type); + if (type == (gnutls_x509_subject_alt_name_t) - 1) { + gnutls_assert(); + return GNUTLS_E_X509_UNKNOWN_SAN; + } + + if (ret_type) + *ret_type = type; + + if (type == GNUTLS_SAN_OTHERNAME) { + if (othername_oid) + _gnutls_str_cat(nptr, sizeof(nptr), + ".otherName.type-id"); + else + _gnutls_str_cat(nptr, sizeof(nptr), + ".otherName.value"); + + ret = _gnutls_x509_read_value(src, nptr, &tmp); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (othername_oid) { + dname->size = tmp.size; + dname->data = tmp.data; + } else { + char oid[MAX_OID_SIZE]; + + if (src_name[0] != 0 && seq != -1) + snprintf(nptr, sizeof(nptr), + "%s.?%d.otherName.type-id", + src_name, seq); + else if (src_name[0] != 0) + snprintf(nptr, sizeof(nptr), + "%s.otherName.type-id", + src_name); + else + snprintf(nptr, sizeof(nptr), + "?%d.otherName.type-id", seq); + + len = sizeof(oid); + + result = asn1_read_value(src, nptr, oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(result); + goto cleanup; + } + if (len > 0) len--; + + dname->size = tmp.size; + dname->data = tmp.data; + } + } else if (type == GNUTLS_SAN_DN) { + _gnutls_str_cat(nptr, sizeof(nptr), ".directoryName"); + ret = _gnutls_x509_get_dn(src, nptr, dname, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else if (othername_oid) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } else { + _gnutls_str_cat(nptr, sizeof(nptr), "."); + _gnutls_str_cat(nptr, sizeof(nptr), choice_type); + + ret = _gnutls_x509_read_null_value(src, nptr, &tmp); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* _gnutls_x509_read_value() null terminates */ + dname->size = tmp.size; + dname->data = tmp.data; + } + + return type; + + cleanup: + gnutls_free(tmp.data); + return ret; +} + +/* returns the type and the name on success. + * Type is also returned as a parameter in case of an error. + */ +int +_gnutls_parse_general_name(asn1_node src, const char *src_name, + int seq, void *name, size_t * name_size, + unsigned int *ret_type, int othername_oid) +{ + int ret; + gnutls_datum_t res = {NULL,0}; + unsigned type; + + ret = _gnutls_parse_general_name2(src, src_name, seq, &res, ret_type, othername_oid); + if (ret < 0) + return gnutls_assert_val(ret); + + type = ret; + + if (is_type_printable(type)) { + ret = _gnutls_copy_string(&res, name, name_size); + } else { + ret = _gnutls_copy_data(&res, name, name_size); + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = type; +cleanup: + gnutls_free(res.data); + return ret; +} + +static int +get_alt_name(gnutls_subject_alt_names_t san, + unsigned int seq, uint8_t *alt, + size_t * alt_size, unsigned int *alt_type, + unsigned int *critical, int othername_oid) +{ + int ret; + gnutls_datum_t ooid = {NULL, 0}; + gnutls_datum_t oname; + gnutls_datum_t virt = {NULL, 0}; + unsigned int type; + + if (san == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (alt == NULL) + *alt_size = 0; + + ret = gnutls_subject_alt_names_get(san, seq, &type, &oname, &ooid); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (type == GNUTLS_SAN_OTHERNAME && ooid.data) { + unsigned vtype; + ret = gnutls_x509_othername_to_virtual((char*)ooid.data, &oname, &vtype, &virt); + if (ret >= 0) { + type = vtype; + oname.data = virt.data; + oname.size = virt.size; + } + } + + if (alt_type) + *alt_type = type; + + if (othername_oid) { + ret = _gnutls_copy_string(&ooid, alt, alt_size); + } else { + if (is_type_printable(type)) { + ret = _gnutls_copy_string(&oname, alt, alt_size); + } else { + ret = _gnutls_copy_data(&oname, alt, alt_size); + } + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = type; +cleanup: + gnutls_free(virt.data); + + return ret; +} + +/** + * gnutls_x509_crt_get_subject_alt_name: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @san: is the place where the alternative name will be copied to + * @san_size: holds the size of san. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function retrieves the Alternative Name (2.5.29.17), contained + * in the given certificate in the X509v3 Certificate Extensions. + * + * When the SAN type is otherName, it will extract the data in the + * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned. + * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get + * the corresponding OID and the "virtual" SAN types (e.g., + * %GNUTLS_SAN_OTHERNAME_XMPP). + * + * If an otherName OID is known, the data will be decoded. Otherwise + * the returned data will be DER encoded, and you will have to decode + * it yourself. Currently, only the RFC 3920 id-on-xmppAddr SAN is + * recognized. + * + * Returns: the alternative subject name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @san_size is not large enough to + * hold the value. In that case @san_size will be updated with the + * required size. If the certificate does not have an Alternative + * name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_subject_alt_name(gnutls_x509_crt_t cert, + unsigned int seq, void *san, + size_t * san_size, + unsigned int *critical) +{ + return get_alt_name(cert->san, seq, san, san_size, NULL, + critical, 0); +} + +/** + * gnutls_x509_crt_get_issuer_alt_name: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ian: is the place where the alternative name will be copied to + * @ian_size: holds the size of ian. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function retrieves the Issuer Alternative Name (2.5.29.18), + * contained in the given certificate in the X509v3 Certificate + * Extensions. + * + * When the SAN type is otherName, it will extract the data in the + * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned. + * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get + * the corresponding OID and the "virtual" SAN types (e.g., + * %GNUTLS_SAN_OTHERNAME_XMPP). + * + * If an otherName OID is known, the data will be decoded. Otherwise + * the returned data will be DER encoded, and you will have to decode + * it yourself. Currently, only the RFC 3920 id-on-xmppAddr Issuer + * AltName is recognized. + * + * Returns: the alternative issuer name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ian_size is not large enough + * to hold the value. In that case @ian_size will be updated with + * the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.10.0 + **/ +int +gnutls_x509_crt_get_issuer_alt_name(gnutls_x509_crt_t cert, + unsigned int seq, void *ian, + size_t * ian_size, + unsigned int *critical) +{ + return get_alt_name(cert->ian, seq, ian, ian_size, NULL, + critical, 0); +} + +/** + * gnutls_x509_crt_get_subject_alt_name2: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @san: is the place where the alternative name will be copied to + * @san_size: holds the size of ret. + * @san_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. It is the same as + * gnutls_x509_crt_get_subject_alt_name() except for the fact that it + * will return the type of the alternative name in @san_type even if + * the function fails for some reason (i.e. the buffer provided is + * not enough). + * + * Returns: the alternative subject name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @san_size is not large enough + * to hold the value. In that case @san_size will be updated with + * the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_subject_alt_name2(gnutls_x509_crt_t cert, + unsigned int seq, void *san, + size_t * san_size, + unsigned int *san_type, + unsigned int *critical) +{ + return get_alt_name(cert->san, seq, san, san_size, + san_type, critical, 0); +} + +/** + * gnutls_x509_crt_get_issuer_alt_name2: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ian: is the place where the alternative name will be copied to + * @ian_size: holds the size of ret. + * @ian_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. It is the same as + * gnutls_x509_crt_get_issuer_alt_name() except for the fact that it + * will return the type of the alternative name in @ian_type even if + * the function fails for some reason (i.e. the buffer provided is + * not enough). + * + * Returns: the alternative issuer name type on success, one of the + * enumerated #gnutls_x509_subject_alt_name_t. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ian_size is not large enough + * to hold the value. In that case @ian_size will be updated with + * the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.10.0 + * + **/ +int +gnutls_x509_crt_get_issuer_alt_name2(gnutls_x509_crt_t cert, + unsigned int seq, void *ian, + size_t * ian_size, + unsigned int *ian_type, + unsigned int *critical) +{ + return get_alt_name(cert->ian, seq, ian, ian_size, + ian_type, critical, 0); +} + +/** + * gnutls_x509_crt_get_subject_alt_othername_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @oid: is the place where the otherName OID will be copied to + * @oid_size: holds the size of ret. + * + * This function will extract the type OID of an otherName Subject + * Alternative Name, contained in the given certificate, and return + * the type as an enumerated element. + * + * This function is only useful if + * gnutls_x509_crt_get_subject_alt_name() returned + * %GNUTLS_SAN_OTHERNAME. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: the alternative subject name type on success, one of the + * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, it + * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types, + * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for + * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if + * @ian_size is not large enough to hold the value. In that case + * @ian_size will be updated with the required size. If the + * certificate does not have an Alternative name with the specified + * sequence number and with the otherName type then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_subject_alt_othername_oid(gnutls_x509_crt_t cert, + unsigned int seq, + void *oid, size_t * oid_size) +{ + return get_alt_name(cert->san, seq, oid, oid_size, NULL, + NULL, 1); +} + +/** + * gnutls_x509_crt_get_issuer_alt_othername_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the otherName OID will be copied to + * @ret_size: holds the size of ret. + * + * This function will extract the type OID of an otherName Subject + * Alternative Name, contained in the given certificate, and return + * the type as an enumerated element. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * This function is only useful if + * gnutls_x509_crt_get_issuer_alt_name() returned + * %GNUTLS_SAN_OTHERNAME. + * + * Returns: the alternative issuer name type on success, one of the + * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, it + * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types, + * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for + * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if + * @ret_size is not large enough to hold the value. In that case + * @ret_size will be updated with the required size. If the + * certificate does not have an Alternative name with the specified + * sequence number and with the otherName type then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + * Since: 2.10.0 + **/ +int +gnutls_x509_crt_get_issuer_alt_othername_oid(gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, size_t * ret_size) +{ + return get_alt_name(cert->ian, seq, ret, ret_size, NULL, + NULL, 1); +} + +/** + * gnutls_x509_crt_get_basic_constraints: + * @cert: should contain a #gnutls_x509_crt_t type + * @critical: will be non-zero if the extension is marked as critical + * @ca: pointer to output integer indicating CA status, may be NULL, + * value is 1 if the certificate CA flag is set, 0 otherwise. + * @pathlen: pointer to output integer indicating path length (may be + * NULL), non-negative error codes indicate a present pathLenConstraint + * field and the actual value, -1 indicate that the field is absent. + * + * This function will read the certificate's basic constraints, and + * return the certificates CA status. It reads the basicConstraints + * X.509 extension (2.5.29.19). + * + * Returns: If the certificate is a CA a positive value will be + * returned, or (0) if the certificate does not have CA flag set. A + * negative error code may be returned in case of errors. If the + * certificate does not contain the basicConstraints extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_basic_constraints(gnutls_x509_crt_t cert, + unsigned int *critical, + unsigned int *ca, int *pathlen) +{ + int result; + gnutls_datum_t basicConstraints; + unsigned int tmp_ca; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.19", 0, + &basicConstraints, + critical)) < 0) { + return result; + } + + if (basicConstraints.size == 0 || basicConstraints.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = gnutls_x509_ext_import_basic_constraints(&basicConstraints, &tmp_ca, pathlen); + if (ca) + *ca = tmp_ca; + + _gnutls_free_datum(&basicConstraints); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return tmp_ca; +} + +/** + * gnutls_x509_crt_get_ca_status: + * @cert: should contain a #gnutls_x509_crt_t type + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return certificates CA status, by reading the + * basicConstraints X.509 extension (2.5.29.19). If the certificate is + * a CA a positive value will be returned, or (0) if the certificate + * does not have CA flag set. + * + * Use gnutls_x509_crt_get_basic_constraints() if you want to read the + * pathLenConstraint field too. + * + * Returns: If the certificate is a CA a positive value will be + * returned, or (0) if the certificate does not have CA flag set. A + * negative error code may be returned in case of errors. If the + * certificate does not contain the basicConstraints extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_ca_status(gnutls_x509_crt_t cert, + unsigned int *critical) +{ + int pathlen; + unsigned int ca; + return gnutls_x509_crt_get_basic_constraints(cert, critical, &ca, + &pathlen); +} + +/** + * gnutls_x509_crt_get_key_usage: + * @cert: should contain a #gnutls_x509_crt_t type + * @key_usage: where the key usage bits will be stored + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return certificate's key usage, by reading 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: zero on success, 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. + **/ +int +gnutls_x509_crt_get_key_usage(gnutls_x509_crt_t cert, + unsigned int *key_usage, + unsigned int *critical) +{ + int result; + gnutls_datum_t keyUsage; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension(cert, "2.5.29.15", 0, + &keyUsage, critical)) < 0) { + return result; + } + + if (keyUsage.size == 0 || keyUsage.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = gnutls_x509_ext_import_key_usage(&keyUsage, key_usage); + _gnutls_free_datum(&keyUsage); + + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_inhibit_anypolicy: + * @cert: should contain a #gnutls_x509_crt_t type + * @skipcerts: will hold the number of certificates after which anypolicy is no longer acceptable. + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return certificate's value of the SkipCerts, i.e., + * the Inhibit anyPolicy X.509 extension (2.5.29.54). + * + * The returned value is the number of additional certificates that + * may appear in the path before the anyPolicy is no longer acceptable. + + * Returns: zero on success, 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_crt_get_inhibit_anypolicy(gnutls_x509_crt_t cert, + unsigned int *skipcerts, + unsigned int *critical) +{ + int ret; + gnutls_datum_t ext; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.54", 0, + &ext, critical)) < 0) { + return ret; + } + + if (ext.size == 0 || ext.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_ext_import_key_usage(&ext, skipcerts); + _gnutls_free_datum(&ext); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_proxy: + * @cert: should contain a #gnutls_x509_crt_t type + * @critical: will be non-zero if the extension is marked as critical + * @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 size of policy data + * + * This function will get information from a proxy certificate. It + * reads the ProxyCertInfo X.509 extension (1.3.6.1.5.5.7.1.14). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + **/ +int +gnutls_x509_crt_get_proxy(gnutls_x509_crt_t cert, + unsigned int *critical, + int *pathlen, + char **policyLanguage, + char **policy, size_t * sizeof_policy) +{ + int result; + gnutls_datum_t proxyCertInfo; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension(cert, "1.3.6.1.5.5.7.1.14", 0, + &proxyCertInfo, critical)) < 0) + { + return result; + } + + if (proxyCertInfo.size == 0 || proxyCertInfo.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = gnutls_x509_ext_import_proxy(&proxyCertInfo, pathlen, + policyLanguage, + policy, + sizeof_policy); + _gnutls_free_datum(&proxyCertInfo); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} + + +/** + * gnutls_x509_policy_release: + * @policy: a certificate policy + * + * This function will deinitialize all memory associated with the provided + * @policy. The policy is allocated using gnutls_x509_crt_get_policy(). + * + * Since: 3.1.5 + **/ +void gnutls_x509_policy_release(struct gnutls_x509_policy_st *policy) +{ + unsigned i; + + gnutls_free(policy->oid); + for (i = 0; i < policy->qualifiers; i++) + gnutls_free(policy->qualifier[i].data); +} + + +/** + * gnutls_x509_crt_get_policy: + * @crt: should contain a #gnutls_x509_crt_t type + * @indx: This specifies which policy to return. Use (0) to get the first one. + * @policy: A pointer to a policy structure. + * @critical: will be non-zero if the extension is marked as critical + * + * This function will extract the certificate policy (extension 2.5.29.32) + * specified by the given index. + * + * The policy returned by this function must be deinitialized by using + * gnutls_x509_policy_release(). + * + * 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.1.5 + **/ +int +gnutls_x509_crt_get_policy(gnutls_x509_crt_t crt, unsigned indx, + struct gnutls_x509_policy_st *policy, + unsigned int *critical) +{ + gnutls_datum_t tmpd = { NULL, 0 }; + int ret; + gnutls_x509_policies_t policies = NULL; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(policy, 0, sizeof(*policy)); + + ret = gnutls_x509_policies_init(&policies); + if (ret < 0) + return gnutls_assert_val(ret); + + if ((ret = + _gnutls_x509_crt_get_extension(crt, "2.5.29.32", 0, &tmpd, + critical)) < 0) { + goto cleanup; + } + + if (tmpd.size == 0 || tmpd.data == NULL) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + ret = gnutls_x509_ext_import_policies(&tmpd, policies, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_policies_get(policies, indx, policy); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + _gnutls_x509_policies_erase(policies, indx); + + ret = 0; + + cleanup: + if (policies != NULL) + gnutls_x509_policies_deinit(policies); + _gnutls_free_datum(&tmpd); + + return ret; +} + + +/** + * gnutls_x509_crt_get_extension_by_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this specifies which to send. Use (0) to get the first one. + * @buf: a pointer to a structure to hold the name (may be null) + * @buf_size: initially holds the size of @buf + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in the + * certificate. The extensions will be returned as binary data DER + * encoded, in the provided buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If the certificate does not + * contain the specified extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_extension_by_oid(gnutls_x509_crt_t cert, + const char *oid, unsigned indx, + void *buf, size_t * buf_size, + unsigned int *critical) +{ + int result; + gnutls_datum_t output; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension(cert, oid, indx, &output, + critical)) < 0) { + gnutls_assert(); + return result; + } + + if (output.size == 0 || output.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (output.size > (unsigned int) *buf_size) { + *buf_size = output.size; + _gnutls_free_datum(&output); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + *buf_size = output.size; + + if (buf) + memcpy(buf, output.data, output.size); + + _gnutls_free_datum(&output); + + return 0; +} + +/** + * gnutls_x509_crt_get_extension_by_oid2: + * @cert: should contain a #gnutls_x509_crt_t type + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this specifies which to send. Use (0) to get the first one. + * @output: will hold the allocated extension data + * @critical: will be non-zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in the + * certificate. The extensions will be returned as binary data DER + * encoded, in the provided buffer. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. If the certificate does not + * contain the specified extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + * Since: 3.3.8 + **/ +int +gnutls_x509_crt_get_extension_by_oid2(gnutls_x509_crt_t cert, + const char *oid, unsigned indx, + gnutls_datum_t *output, + unsigned int *critical) +{ + int ret; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(cert, oid, indx, output, + critical)) < 0) { + gnutls_assert(); + return ret; + } + + if (output->size == 0 || output->data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_extension_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: Specifies which extension OID to send. Use (0) to get the first one. + * @oid: a pointer to a structure to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * + * This function will return the requested extension OID in the certificate. + * The extension OID will be stored as a string in the provided buffer. + * + * The @oid returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * 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_oid(gnutls_x509_crt_t cert, unsigned indx, + void *oid, size_t * oid_size) +{ + int result; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_x509_crt_get_extension_oid(cert, indx, oid, oid_size); + if (result < 0) { + return result; + } + + return 0; + +} + +/** + * gnutls_x509_crt_get_extension_info: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: Specifies which extension OID to send. Use (0) to get the first one. + * @oid: a pointer to a structure to hold the OID + * @oid_size: initially holds the maximum size of @oid, on return + * holds actual size of @oid. + * @critical: output variable with critical flag, may be NULL. + * + * This function will return the requested extension OID in the + * certificate, and the critical flag for it. The extension OID will + * be stored as a string in the provided buffer. Use + * gnutls_x509_crt_get_extension() to extract the data. + * + * If the buffer provided is not long enough to hold the output, then + * @oid_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be + * returned. The @oid returned will be null terminated, although + * @oid_size will not account for the trailing null (the latter is not + * true for GnuTLS prior to 3.6.0). + * + * 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_info(gnutls_x509_crt_t cert, unsigned indx, + void *oid, size_t * oid_size, + unsigned int *critical) +{ + int result; + char str_critical[10]; + char name[MAX_NAME_SIZE]; + int len; + + if (!cert) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsCertificate.extensions.?%u.extnID", indx + 1); + + len = *oid_size; + result = asn1_read_value(cert->cert, name, oid, &len); + *oid_size = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + /* remove any trailing null */ + if (oid && len > 0 && ((uint8_t*)oid)[len-1] == 0) + (*oid_size)--; + + if (critical) { + snprintf(name, sizeof(name), + "tbsCertificate.extensions.?%u.critical", indx + 1); + len = sizeof(str_critical); + result = asn1_read_value(cert->cert, name, str_critical, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (str_critical[0] == 'T') + *critical = 1; + else + *critical = 0; + } + + return 0; + +} + +/** + * gnutls_x509_crt_get_extension_data: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: Specifies which extension OID to send. Use (0) to get the first one. + * @data: a pointer to a structure to hold the data (may be null) + * @sizeof_data: initially holds the size of @data + * + * This function will return the requested extension data in the + * certificate. The extension data will be stored in the + * provided buffer. + * + * Use gnutls_x509_crt_get_extension_info() to extract the OID and + * critical flag. Use gnutls_x509_crt_get_extension_by_oid() instead, + * if you want to get data indexed by the extension OID rather than + * sequence. + * + * 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_data(gnutls_x509_crt_t cert, unsigned indx, + void *data, size_t * sizeof_data) +{ + int result, len; + char name[MAX_NAME_SIZE]; + + if (!cert) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf(name, sizeof(name), + "tbsCertificate.extensions.?%u.extnValue", indx + 1); + + len = *sizeof_data; + result = asn1_read_value(cert->cert, name, data, &len); + *sizeof_data = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } else if (result == ASN1_MEM_ERROR && data == NULL) { + /* normally we should return GNUTLS_E_SHORT_MEMORY_BUFFER, + * but we haven't done that for long time, so use + * backwards compatible behavior */ + return 0; + } else if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + return 0; +} + +/** + * gnutls_x509_crt_get_raw_issuer_dn: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: will hold the starting point of the DN + * + * This function will return a pointer to the DER encoded DN structure + * and the length. This points to allocated data that must be free'd using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value.or a negative error code on error. + * + **/ +int +gnutls_x509_crt_get_raw_issuer_dn(gnutls_x509_crt_t cert, + gnutls_datum_t * dn) +{ + if (cert->raw_issuer_dn.size > 0 && cert->modified == 0) { + return _gnutls_set_datum(dn, cert->raw_issuer_dn.data, + cert->raw_issuer_dn.size); + } else { + return _gnutls_x509_get_raw_field(cert->cert, "tbsCertificate.issuer.rdnSequence", dn); + } +} + +/** + * gnutls_x509_crt_get_raw_dn: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: will hold the starting point of the DN + * + * This function will return a pointer to the DER encoded DN structure and + * the length. This points to allocated data that must be free'd using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. or a negative error code on error. + * + **/ +int gnutls_x509_crt_get_raw_dn(gnutls_x509_crt_t cert, gnutls_datum_t * dn) +{ + if (cert->raw_dn.size > 0 && cert->modified == 0) { + return _gnutls_set_datum(dn, cert->raw_dn.data, cert->raw_dn.size); + } else { + return _gnutls_x509_get_raw_field(cert->cert, "tbsCertificate.subject.rdnSequence", dn); + } +} + +static int +get_dn(gnutls_x509_crt_t cert, const char *whom, gnutls_x509_dn_t * dn, unsigned subject) +{ + gnutls_x509_dn_st *store; + + if (subject) + store = &cert->dn; + else + store = &cert->idn; + + store->asn = asn1_find_node(cert->cert, whom); + if (!store->asn) + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + + *dn = store; + + return 0; +} + +/** + * gnutls_x509_crt_get_subject: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: output variable with pointer to uint8_t DN. + * + * Return the Certificate's Subject DN as a %gnutls_x509_dn_t data type, + * that can be decoded using gnutls_x509_dn_get_rdn_ava(). + * + * Note that @dn should be treated as constant. Because it points + * into the @cert object, you should not use @dn after @cert is + * deallocated. + * + * Returns: Returns 0 on success, or an error code. + **/ +int +gnutls_x509_crt_get_subject(gnutls_x509_crt_t cert, gnutls_x509_dn_t * dn) +{ + return get_dn(cert, "tbsCertificate.subject.rdnSequence", dn, 1); +} + +/** + * gnutls_x509_crt_get_issuer: + * @cert: should contain a #gnutls_x509_crt_t type + * @dn: output variable with pointer to uint8_t DN + * + * Return the Certificate's Issuer DN as a %gnutls_x509_dn_t data type, + * that can be decoded using gnutls_x509_dn_get_rdn_ava(). + * + * Note that @dn should be treated as constant. Because it points + * into the @cert object, you should not use @dn after @cert is + * deallocated. + * + * Returns: Returns 0 on success, or an error code. + **/ +int +gnutls_x509_crt_get_issuer(gnutls_x509_crt_t cert, gnutls_x509_dn_t * dn) +{ + return get_dn(cert, "tbsCertificate.issuer.rdnSequence", dn, 0); +} + +/** + * gnutls_x509_crt_get_fingerprint: + * @cert: should contain a #gnutls_x509_crt_t type + * @algo: is a digest algorithm + * @buf: a pointer to a structure to hold the fingerprint (may be null) + * @buf_size: initially holds the size of @buf + * + * This function will calculate and copy the certificate's fingerprint + * in the provided buffer. The fingerprint is a hash of the DER-encoded + * data of the certificate. + * + * If the buffer is null then only the size will be filled. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *buf_size will be updated + * with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_fingerprint(gnutls_x509_crt_t cert, + gnutls_digest_algorithm_t algo, + void *buf, size_t * buf_size) +{ + uint8_t *cert_buf; + int cert_buf_size; + int result; + gnutls_datum_t tmp; + + if (buf_size == 0 || cert == NULL) { + return GNUTLS_E_INVALID_REQUEST; + } + + cert_buf_size = 0; + result = asn1_der_coding(cert->cert, "", NULL, &cert_buf_size, NULL); + if (result != ASN1_MEM_ERROR) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + cert_buf = gnutls_malloc(cert_buf_size); + if (cert_buf == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + asn1_der_coding(cert->cert, "", cert_buf, &cert_buf_size, + NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(cert_buf); + return _gnutls_asn2err(result); + } + + tmp.data = cert_buf; + tmp.size = cert_buf_size; + + result = gnutls_fingerprint(algo, &tmp, buf, buf_size); + gnutls_free(cert_buf); + + return result; +} + +/** + * gnutls_x509_crt_export: + * @cert: Holds the certificate + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a certificate PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the certificate to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN CERTIFICATE". + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + **/ +int +gnutls_x509_crt_export(gnutls_x509_crt_t cert, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + gnutls_datum_t out; + int ret; + + ret = gnutls_x509_crt_export2(cert, format, &out); + if (ret < 0) + return gnutls_assert_val(ret); + + if (format == GNUTLS_X509_FMT_PEM) + ret = _gnutls_copy_string(&out, (uint8_t*)output_data, output_data_size); + else + ret = _gnutls_copy_data(&out, (uint8_t*)output_data, output_data_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(out.data); + return ret; +} + +/** + * gnutls_x509_crt_export2: + * @cert: Holds the certificate + * @format: the format of output params. One of PEM or DER. + * @out: will contain a certificate PEM or DER encoded + * + * This function will export the certificate to DER or PEM format. + * The output buffer is allocated using gnutls_malloc(). + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN CERTIFICATE". + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + * + * Since: 3.1.3 + **/ +int +gnutls_x509_crt_export2(gnutls_x509_crt_t cert, + gnutls_x509_crt_fmt_t format, gnutls_datum_t * out) +{ + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (!cert->modified && cert->der.size) { + if (format == GNUTLS_X509_FMT_DER) + return _gnutls_set_datum(out, cert->der.data, cert->der.size); + else { + int ret = _gnutls_fbase64_encode(PEM_X509_CERT2, + cert->der.data, + cert->der.size, + out); + if (ret < 0) + return ret; + return 0; + } + } + + return _gnutls_x509_export_int2(cert->cert, format, PEM_X509_CERT2, + out); +} + +int +_gnutls_get_key_id(gnutls_pk_params_st * params, + unsigned char *output_data, size_t * output_data_size, + unsigned flags) +{ + int ret = 0; + gnutls_datum_t der = { NULL, 0 }; + gnutls_digest_algorithm_t hash = GNUTLS_DIG_SHA1; + unsigned int digest_len; + + if ((flags & GNUTLS_KEYID_USE_SHA512) || (flags & GNUTLS_KEYID_USE_BEST_KNOWN)) + hash = GNUTLS_DIG_SHA512; + else if (flags & GNUTLS_KEYID_USE_SHA256) + hash = GNUTLS_DIG_SHA256; + + digest_len = + _gnutls_hash_get_algo_len(hash_to_entry(hash)); + + if (output_data == NULL || *output_data_size < digest_len) { + gnutls_assert(); + *output_data_size = digest_len; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + ret = _gnutls_x509_encode_PKI_params(&der, params); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_hash_fast(hash, der.data, der.size, output_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + *output_data_size = digest_len; + + ret = 0; + + cleanup: + + _gnutls_free_datum(&der); + return ret; +} + +/** + * gnutls_x509_crt_get_key_id: + * @crt: Holds the certificate + * @flags: should be one of the flags from %gnutls_keyid_flags_t + * @output_data: will contain the key ID + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will return a unique ID that depends on the public + * key parameters. This ID can be used in checking whether a + * certificate corresponds to the given private key. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. The output will normally be a SHA-1 hash output, + * which is 20 bytes. + * + * Returns: In case of failure a negative error code will be + * returned, and 0 on success. + **/ +int +gnutls_x509_crt_get_key_id(gnutls_x509_crt_t crt, unsigned int flags, + unsigned char *output_data, + size_t * output_data_size) +{ + int ret = 0; + gnutls_pk_params_st params; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* initializes params */ + ret = _gnutls_x509_crt_get_mpis(crt, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_get_key_id(¶ms, output_data, output_data_size, flags); + + gnutls_pk_params_release(¶ms); + + return ret; +} + +static int +crl_issuer_matches(gnutls_x509_crl_t crl, gnutls_x509_crt_t cert) +{ + if (_gnutls_x509_compare_raw_dn + (&crl->raw_issuer_dn, &cert->raw_issuer_dn) != 0) + return 1; + else + return 0; +} + +/* This is exactly as gnutls_x509_crt_check_revocation() except that + * it calls func. + */ +int +_gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert, + const gnutls_x509_crl_t * crl_list, + int crl_list_length, + gnutls_verify_output_function func) +{ + uint8_t serial[128]; + uint8_t cert_serial[128]; + size_t serial_size, cert_serial_size; + int ret, j; + gnutls_x509_crl_iter_t iter = NULL; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + for (j = 0; j < crl_list_length; j++) { /* do for all the crls */ + + /* Step 1. check if issuer's DN match + */ + ret = crl_issuer_matches(crl_list[j], cert); + if (ret == 0) { + /* issuers do not match so don't even + * bother checking. + */ + gnutls_assert(); + continue; + } + + /* Step 2. Read the certificate's serial number + */ + cert_serial_size = sizeof(cert_serial); + ret = + gnutls_x509_crt_get_serial(cert, cert_serial, + &cert_serial_size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* Step 3. cycle through the CRL serials and compare with + * certificate serial we have. + */ + + iter = NULL; + do { + serial_size = sizeof(serial); + ret = + gnutls_x509_crl_iter_crt_serial(crl_list[j], + &iter, + serial, + &serial_size, + NULL); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + break; + } else if (ret < 0) { + gnutls_assert(); + goto fail; + } + + if (serial_size == cert_serial_size) { + if (memcmp + (serial, cert_serial, + serial_size) == 0) { + /* serials match */ + if (func) + func(cert, NULL, + crl_list[j], + GNUTLS_CERT_REVOKED | + GNUTLS_CERT_INVALID); + ret = 1; /* revoked! */ + goto fail; + } + } + } while(1); + + gnutls_x509_crl_iter_deinit(iter); + iter = NULL; + + if (func) + func(cert, NULL, crl_list[j], 0); + + } + return 0; /* not revoked. */ + + fail: + gnutls_x509_crl_iter_deinit(iter); + return ret; +} + + +/** + * gnutls_x509_crt_check_revocation: + * @cert: should contain a #gnutls_x509_crt_t type + * @crl_list: should contain a list of gnutls_x509_crl_t types + * @crl_list_length: the length of the crl_list + * + * This function will check if the given certificate is + * revoked. It is assumed that the CRLs have been verified before. + * + * Returns: 0 if the certificate is NOT revoked, and 1 if it is. A + * negative error code is returned on error. + **/ +int +gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert, + const gnutls_x509_crl_t * crl_list, + unsigned crl_list_length) +{ + return _gnutls_x509_crt_check_revocation(cert, crl_list, + crl_list_length, NULL); +} + +/** + * gnutls_x509_crt_check_key_purpose: + * @cert: should contain a #gnutls_x509_crt_t type + * @purpose: a key purpose OID (e.g., %GNUTLS_KP_CODE_SIGNING) + * @flags: zero or %GNUTLS_KP_FLAG_DISALLOW_ANY + * + * This function will check whether the given certificate matches + * the provided key purpose. If @flags contains %GNUTLS_KP_FLAG_ALLOW_ANY then + * it a certificate marked for any purpose will not match. + * + * Returns: zero if the key purpose doesn't match, and non-zero otherwise. + * + * Since: 3.5.6 + **/ +unsigned +gnutls_x509_crt_check_key_purpose(gnutls_x509_crt_t cert, + const char *purpose, + unsigned flags) +{ + return _gnutls_check_key_purpose(cert, purpose, (flags&GNUTLS_KP_FLAG_DISALLOW_ANY)?1:0); +} + +/** + * gnutls_x509_crt_get_preferred_hash_algorithm: + * @crt: Holds the certificate + * @hash: The result of the call with the hash algorithm used for signature + * @mand: If non-zero it means that the algorithm MUST use this hash. May be %NULL. + * + * This function will read the certificate and return the appropriate digest + * algorithm to use for signing with this certificate. Some certificates (i.e. + * DSA might not be able to sign without the preferred algorithm). + * + * Deprecated: Please use gnutls_pubkey_get_preferred_hash_algorithm(). + * + * Returns: the 0 if the hash algorithm is found. A negative error code is + * returned on error. + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crt_get_preferred_hash_algorithm(gnutls_x509_crt_t crt, + gnutls_digest_algorithm_t * + hash, unsigned int *mand) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_get_preferred_hash_algorithm(pubkey, hash, mand); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_get_crl_dist_points: + * @cert: should contain a #gnutls_x509_crt_t type + * @seq: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.) + * @san: is the place where the distribution point will be copied to + * @san_size: holds the size of ret. + * @reason_flags: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t. + * @critical: will be non-zero if the extension is marked as critical (may be null) + * + * This function retrieves the CRL distribution points (2.5.29.31), + * contained in the given certificate in the X509v3 Certificate + * Extensions. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER and updates @ret_size if + * @ret_size is not enough to hold the distribution point, or the + * type of the distribution point if everything was ok. The type is + * one of the enumerated %gnutls_x509_subject_alt_name_t. If the + * certificate does not have an Alternative name with the specified + * sequence number then %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is + * returned. + **/ +int +gnutls_x509_crt_get_crl_dist_points(gnutls_x509_crt_t cert, + unsigned int seq, void *san, + size_t * san_size, + unsigned int *reason_flags, + unsigned int *critical) +{ + int ret; + gnutls_datum_t dist_points = { NULL, 0 }; + unsigned type; + gnutls_x509_crl_dist_points_t cdp = NULL; + gnutls_datum_t t_san; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crl_dist_points_init(&cdp); + if (ret < 0) + return gnutls_assert_val(ret); + + if (reason_flags) + *reason_flags = 0; + + ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.31", 0, + &dist_points, critical); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (dist_points.size == 0 || dist_points.data == NULL) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + ret = gnutls_x509_ext_import_crl_dist_points(&dist_points, cdp, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_crl_dist_points_get(cdp, seq, &type, &t_san, reason_flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_copy_string(&t_san, san, san_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = type; + + cleanup: + _gnutls_free_datum(&dist_points); + if (cdp != NULL) + gnutls_x509_crl_dist_points_deinit(cdp); + + return ret; +} + +/** + * gnutls_x509_crt_get_key_purpose_oid: + * @cert: should contain a #gnutls_x509_crt_t type + * @indx: This specifies which OID to return. Use (0) to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @oid_size: initially holds the size of @oid + * @critical: output flag to indicate criticality of extension + * + * This function will extract the key purpose OIDs of the Certificate + * specified by the given index. These are stored in the Extended Key + * Usage extension (2.5.29.37) See the GNUTLS_KP_* definitions for + * human readable names. + * + * If @oid is null then only the size will be filled. The @oid + * returned will be null terminated, although @oid_size will not + * account for the trailing null. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *oid_size will be updated + * with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_key_purpose_oid(gnutls_x509_crt_t cert, + unsigned indx, void *oid, size_t * oid_size, + unsigned int *critical) +{ + int ret; + gnutls_datum_t ext; + gnutls_x509_key_purposes_t p = NULL; + gnutls_datum_t out; + + if (cert == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (oid) + memset(oid, 0, *oid_size); + else + *oid_size = 0; + + if ((ret = + _gnutls_x509_crt_get_extension(cert, "2.5.29.37", 0, &ext, + critical)) < 0) { + return ret; + } + + if (ext.size == 0 || ext.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_key_purpose_init(&p); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_ext_import_key_purposes(&ext, p, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_key_purpose_get(p, indx, &out); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_copy_string(&out, oid, oid_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_free(ext.data); + if (p!=NULL) + gnutls_x509_key_purpose_deinit(p); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_rsa_raw: + * @crt: Holds the certificate + * @m: will hold the modulus + * @e: will hold the public exponent + * + * This function will export the RSA public key's parameters found in + * the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + **/ +int +gnutls_x509_crt_get_pk_rsa_raw(gnutls_x509_crt_t crt, + gnutls_datum_t * m, gnutls_datum_t * e) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_rsa_raw(pubkey, m, e); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_ecc_raw: + * @crt: Holds the certificate + * @curve: will hold the curve + * @x: will hold the x-coordinate + * @y: will hold the y-coordinate + * + * This function will export the ECC public key's parameters found in + * the given certificate. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * In EdDSA curves the @y parameter will be %NULL and the other parameters + * will be in the native format for the curve. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.4.1 + **/ +int +gnutls_x509_crt_get_pk_ecc_raw(gnutls_x509_crt_t crt, + gnutls_ecc_curve_t *curve, + gnutls_datum_t *x, gnutls_datum_t *y) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_ecc_raw(pubkey, curve, x, y); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_gost_raw: + * @crt: Holds the certificate + * @curve: will hold the curve + * @digest: will hold the digest + * @paramset: will hold the GOST parameter set ID + * @x: will hold the x-coordinate + * @y: will hold the y-coordinate + * + * This function will export the GOST public key's parameters found in + * the given certificate. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.6.3 + **/ +int +gnutls_x509_crt_get_pk_gost_raw(gnutls_x509_crt_t crt, + gnutls_ecc_curve_t *curve, + gnutls_digest_algorithm_t *digest, + gnutls_gost_paramset_t *paramset, + gnutls_datum_t *x, gnutls_datum_t *y) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_gost_raw2(pubkey, curve, digest, + paramset, x, y, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_get_pk_dsa_raw: + * @crt: Holds the certificate + * @p: will hold the p + * @q: will hold the q + * @g: will hold the g + * @y: will hold the y + * + * This function will export the DSA public key's parameters found in + * the given certificate. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + **/ +int +gnutls_x509_crt_get_pk_dsa_raw(gnutls_x509_crt_t crt, + gnutls_datum_t * p, gnutls_datum_t * q, + gnutls_datum_t * g, gnutls_datum_t * y) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_dsa_raw(pubkey, p, q, g, y); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + +/** + * gnutls_x509_crt_list_import2: + * @certs: Will hold the parsed certificate list. + * @size: It will contain the size of the list. + * @data: The PEM encoded certificate. + * @format: One of DER or PEM. + * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags. + * + * This function will convert the given PEM encoded certificate list + * to the native gnutls_x509_crt_t format. The output will be stored + * in @certs which will be allocated and initialized. + * + * If the Certificate is PEM encoded it should have a header of "X509 + * CERTIFICATE", or "CERTIFICATE". + * + * To deinitialize @certs, you need to deinitialize each crt structure + * independently, and use gnutls_free() at @certs. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.0 + **/ +int +gnutls_x509_crt_list_import2(gnutls_x509_crt_t ** certs, + unsigned int *size, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags) +{ + unsigned int init = 1024; + int ret; + + *certs = _gnutls_reallocarray(NULL, init, sizeof(gnutls_x509_crt_t)); + if (*certs == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crt_list_import(*certs, &init, data, format, + flags | GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + *certs = _gnutls_reallocarray_fast(*certs, init, + sizeof(gnutls_x509_crt_t)); + if (*certs == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crt_list_import(*certs, &init, data, + format, flags); + } + + if (ret < 0) { + gnutls_free(*certs); + return ret; + } + + *size = init; + return 0; +} + +/** + * gnutls_x509_crt_list_import: + * @certs: Indicates where the parsed list will be copied to. Must not be initialized. + * @cert_max: Initially must hold the maximum number of certs. It will be updated with the number of certs available. + * @data: The PEM encoded certificate. + * @format: One of DER or PEM. + * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags. + * + * This function will convert the given PEM encoded certificate list + * to the native gnutls_x509_crt_t format. The output will be stored + * in @certs. They will be automatically initialized. + * + * The flag %GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED will cause + * import to fail if the certificates in the provided buffer are more + * than the available structures. The %GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED + * flag will cause the function to fail if the provided list is not + * sorted from subject to issuer. + * + * If the Certificate is PEM encoded it should have a header of "X509 + * CERTIFICATE", or "CERTIFICATE". + * + * Returns: the number of certificates read or a negative error value. + **/ +int +gnutls_x509_crt_list_import(gnutls_x509_crt_t * certs, + unsigned int *cert_max, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags) +{ + int size; + const char *ptr; + gnutls_datum_t tmp; + int ret, nocopy = 0; + unsigned int count = 0, j, copied = 0; + + if (format == GNUTLS_X509_FMT_DER) { + if (*cert_max < 1) { + *cert_max = 1; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + count = 1; /* import only the first one */ + + ret = gnutls_x509_crt_init(&certs[0]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = gnutls_x509_crt_import(certs[0], data, format); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + *cert_max = 1; + return 1; + } + + /* move to the certificate + */ + ptr = memmem(data->data, data->size, + PEM_CERT_SEP, sizeof(PEM_CERT_SEP) - 1); + if (ptr == NULL) + ptr = memmem(data->data, data->size, + PEM_CERT_SEP2, sizeof(PEM_CERT_SEP2) - 1); + + if (ptr == NULL) + return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND); + + count = 0; + + do { + if (count >= *cert_max) { + if (! + (flags & + GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED)) + break; + else + nocopy = 1; + } + + if (!nocopy) { + ret = gnutls_x509_crt_init(&certs[count]); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + tmp.data = (void *) ptr; + tmp.size = + data->size - (ptr - (char *) data->data); + + ret = + gnutls_x509_crt_import(certs[count], &tmp, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + count++; + gnutls_assert(); + goto error; + } + + copied++; + } + + /* now we move ptr after the pem header + */ + ptr++; + /* find the next certificate (if any) + */ + size = data->size - (ptr - (char *) data->data); + + if (size > 0) { + char *ptr2; + + ptr2 = + memmem(ptr, size, PEM_CERT_SEP, + sizeof(PEM_CERT_SEP) - 1); + if (ptr2 == NULL) + ptr2 = memmem(ptr, size, PEM_CERT_SEP2, + sizeof(PEM_CERT_SEP2) - 1); + + ptr = ptr2; + } else + ptr = NULL; + + count++; + } + while (ptr != NULL); + + *cert_max = count; + + if (nocopy == 0) { + if (flags & GNUTLS_X509_CRT_LIST_SORT && *cert_max > 1) { + if (*cert_max > DEFAULT_MAX_VERIFY_DEPTH) { + ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; + goto error; + } + count = _gnutls_sort_clist(certs, *cert_max); + if (count < *cert_max) { + for (j = count; j < *cert_max; j++) { + gnutls_x509_crt_deinit(certs[j]); + } + } + *cert_max = count; + } + + if (flags & GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED) { + ret = _gnutls_check_if_sorted(certs, *cert_max); + if (ret < 0) { + gnutls_assert(); + goto error; + } + } + + return count; + } else { + count = copied; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + error: + for (j = 0; j < count; j++) + gnutls_x509_crt_deinit(certs[j]); + return ret; +} + +/** + * gnutls_x509_crt_get_subject_unique_id: + * @crt: Holds the certificate + * @buf: user allocated memory buffer, will hold the unique id + * @buf_size: size of user allocated memory buffer (on input), will hold + * actual size of the unique ID on return. + * + * This function will extract the subjectUniqueID value (if present) for + * the given certificate. + * + * If the user allocated memory buffer is not large enough to hold the + * full subjectUniqueID, then a GNUTLS_E_SHORT_MEMORY_BUFFER error will be + * returned, and buf_size will be set to the actual length. + * + * This function had a bug prior to 3.4.8 that prevented the setting + * of %NULL @buf to discover the @buf_size. To use this function safely + * with the older versions the @buf must be a valid buffer that can hold + * at least a single byte if @buf_size is zero. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + **/ +int +gnutls_x509_crt_get_subject_unique_id(gnutls_x509_crt_t crt, char *buf, + size_t * buf_size) +{ + int result; + gnutls_datum_t datum = { NULL, 0 }; + + result = + _gnutls_x509_read_value(crt->cert, + "tbsCertificate.subjectUniqueID", + &datum); + if (result < 0) + return gnutls_assert_val(result); + + if (datum.size > *buf_size) { /* then we're not going to fit */ + *buf_size = datum.size; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + } else { + *buf_size = datum.size; + memcpy(buf, datum.data, datum.size); + } + + _gnutls_free_datum(&datum); + + return result; +} + +/** + * gnutls_x509_crt_get_issuer_unique_id: + * @crt: Holds the certificate + * @buf: user allocated memory buffer, will hold the unique id + * @buf_size: size of user allocated memory buffer (on input), will hold + * actual size of the unique ID on return. + * + * This function will extract the issuerUniqueID value (if present) for + * the given certificate. + * + * If the user allocated memory buffer is not large enough to hold the + * full subjectUniqueID, then a GNUTLS_E_SHORT_MEMORY_BUFFER error will be + * returned, and buf_size will be set to the actual length. + * + * This function had a bug prior to 3.4.8 that prevented the setting + * of %NULL @buf to discover the @buf_size. To use this function safely + * with the older versions the @buf must be a valid buffer that can hold + * at least a single byte if @buf_size is zero. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crt_get_issuer_unique_id(gnutls_x509_crt_t crt, char *buf, + size_t * buf_size) +{ + int result; + gnutls_datum_t datum = { NULL, 0 }; + + result = + _gnutls_x509_read_value(crt->cert, + "tbsCertificate.issuerUniqueID", + &datum); + if (result < 0) + return gnutls_assert_val(result); + + if (datum.size > *buf_size) { /* then we're not going to fit */ + *buf_size = datum.size; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + } else { + *buf_size = datum.size; + memcpy(buf, datum.data, datum.size); + } + + _gnutls_free_datum(&datum); + + return result; +} + +static int +legacy_parse_aia(asn1_node src, + unsigned int seq, int what, gnutls_datum_t * data) +{ + int len; + char nptr[MAX_NAME_SIZE]; + int result; + gnutls_datum_t d; + const char *oid = NULL; + + seq++; /* 0->1, 1->2 etc */ + switch (what) { + case GNUTLS_IA_ACCESSMETHOD_OID: + snprintf(nptr, sizeof(nptr), "?%u.accessMethod", seq); + break; + + case GNUTLS_IA_ACCESSLOCATION_GENERALNAME_TYPE: + snprintf(nptr, sizeof(nptr), "?%u.accessLocation", seq); + break; + + case GNUTLS_IA_CAISSUERS_URI: + oid = GNUTLS_OID_AD_CAISSUERS; + FALLTHROUGH; + + case GNUTLS_IA_OCSP_URI: + if (oid == NULL) + oid = GNUTLS_OID_AD_OCSP; + { + char tmpoid[MAX_OID_SIZE]; + snprintf(nptr, sizeof(nptr), "?%u.accessMethod", + seq); + len = sizeof(tmpoid); + result = asn1_read_value(src, nptr, tmpoid, &len); + + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + if ((unsigned) len != strlen(oid) + 1 + || memcmp(tmpoid, oid, len) != 0) + return + gnutls_assert_val + (GNUTLS_E_UNKNOWN_ALGORITHM); + } + FALLTHROUGH; + + case GNUTLS_IA_URI: + snprintf(nptr, sizeof(nptr), + "?%u.accessLocation.uniformResourceIdentifier", + seq); + break; + + default: + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + len = 0; + result = asn1_read_value(src, nptr, NULL, &len); + if (result == ASN1_VALUE_NOT_FOUND + || result == ASN1_ELEMENT_NOT_FOUND) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (result != ASN1_MEM_ERROR) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + d.size = len; + + d.data = gnutls_malloc(d.size); + if (d.data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + result = asn1_read_value(src, nptr, d.data, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + gnutls_free(d.data); + return _gnutls_asn2err(result); + } + + if (data) { + data->data = d.data; + data->size = d.size; + } else + gnutls_free(d.data); + + return 0; +} + +/** + * gnutls_x509_crt_get_authority_info_access: + * @crt: Holds the certificate + * @seq: specifies the sequence number of the access descriptor (0 for the first one, 1 for the second etc.) + * @what: what data to get, a #gnutls_info_access_what_t type. + * @data: output data to be freed with gnutls_free(). + * @critical: pointer to output integer that is set to non-zero if the extension is marked as critical (may be %NULL) + * + * Note that a simpler API to access the authority info data is provided + * by gnutls_x509_aia_get() and gnutls_x509_ext_import_aia(). + * + * This function extracts the Authority Information Access (AIA) + * extension, see RFC 5280 section 4.2.2.1 for more information. The + * AIA extension holds a sequence of AccessDescription (AD) data. + * + * 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. + * + * The type of data returned in @data is specified via @what which + * should be #gnutls_info_access_what_t values. + * + * If @what is %GNUTLS_IA_ACCESSMETHOD_OID then @data will hold the + * accessMethod OID (e.g., "1.3.6.1.5.5.7.48.1"). + * + * If @what is %GNUTLS_IA_ACCESSLOCATION_GENERALNAME_TYPE, @data will + * hold the accessLocation GeneralName type (e.g., + * "uniformResourceIdentifier"). + * + * If @what is %GNUTLS_IA_URI, @data will hold the accessLocation URI + * data. Requesting this @what value leads to an error if the + * accessLocation is not of the "uniformResourceIdentifier" type. + * + * If @what is %GNUTLS_IA_OCSP_URI, @data will hold the OCSP URI. + * Requesting this @what value leads to an error if the accessMethod + * is not 1.3.6.1.5.5.7.48.1 aka OCSP, or if accessLocation is not of + * the "uniformResourceIdentifier" type. In that case %GNUTLS_E_UNKNOWN_ALGORITHM + * will be returned, and @seq should be increased and this function + * called again. + * + * If @what is %GNUTLS_IA_CAISSUERS_URI, @data will hold the caIssuers + * URI. Requesting this @what value leads to an error if the + * accessMethod is not 1.3.6.1.5.5.7.48.2 aka caIssuers, or if + * accessLocation is not of the "uniformResourceIdentifier" type. + * In that case handle as in %GNUTLS_IA_OCSP_URI. + * + * More @what values may be allocated in the future as needed. + * + * If @data is NULL, the function does the same without storing the + * output data, that is, it will set @critical and do error checking + * as usual. + * + * The value of the critical flag is returned in *@critical. Supply a + * NULL @critical if you want the function to make sure the extension + * is non-critical, as required by RFC 5280. + * + * Returns: %GNUTLS_E_SUCCESS on success, %GNUTLS_E_INVALID_REQUEST on + * invalid @crt, %GNUTLS_E_CONSTRAINT_ERROR if the extension is + * incorrectly marked as critical (use a non-NULL @critical to + * override), %GNUTLS_E_UNKNOWN_ALGORITHM if the requested OID does + * not match (e.g., when using %GNUTLS_IA_OCSP_URI), otherwise a + * negative error code. + * + * Since: 3.0 + **/ +int +gnutls_x509_crt_get_authority_info_access(gnutls_x509_crt_t crt, + unsigned int seq, + int what, + gnutls_datum_t * data, + unsigned int *critical) +{ + int ret; + gnutls_datum_t aia; + asn1_node c2 = NULL; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(crt, GNUTLS_OID_AIA, 0, &aia, + critical)) < 0) + return ret; + + if (aia.size == 0 || aia.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (critical && *critical) + return GNUTLS_E_CONSTRAINT_ERROR; + + ret = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.AuthorityInfoAccessSyntax", &c2); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + _gnutls_free_datum(&aia); + return _gnutls_asn2err(ret); + } + + ret = _asn1_strict_der_decode(&c2, aia.data, aia.size, NULL); + /* asn1_print_structure (stdout, c2, "", ASN1_PRINT_ALL); */ + _gnutls_free_datum(&aia); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + asn1_delete_structure(&c2); + return _gnutls_asn2err(ret); + } + + ret = legacy_parse_aia(c2, seq, what, data); + + asn1_delete_structure(&c2); + if (ret < 0) + gnutls_assert(); + + return ret; +} + +/** + * gnutls_x509_crt_set_pin_function: + * @crt: The certificate structure + * @fn: the callback + * @userdata: data associated with the callback + * + * This function will set a callback function to be used when + * it is required to access a protected object. This function overrides + * the global function set using gnutls_pkcs11_set_pin_function(). + * + * Note that this callback is currently used only during the import + * of a PKCS #11 certificate with gnutls_x509_crt_import_url(). + * + * Since: 3.1.0 + * + **/ +void gnutls_x509_crt_set_pin_function(gnutls_x509_crt_t crt, + gnutls_pin_callback_t fn, + void *userdata) +{ + if (crt) { + crt->pin.cb = fn; + crt->pin.data = userdata; + } +} + +/** + * gnutls_x509_crt_import_url: + * @crt: A certificate of type #gnutls_x509_crt_t + * @url: A PKCS 11 url + * @flags: One of GNUTLS_PKCS11_OBJ_* flags for PKCS#11 URLs or zero otherwise + * + * This function will import a certificate present in a PKCS#11 token + * or any type of back-end that supports URLs. + * + * In previous versions of gnutls this function was named + * gnutls_x509_crt_import_pkcs11_url, and the old name is + * an alias to this one. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int +gnutls_x509_crt_import_url(gnutls_x509_crt_t crt, + const char *url, unsigned int flags) +{ + int ret; + unsigned i; + + for (i=0;i<_gnutls_custom_urls_size;i++) { + if (strncmp(url, _gnutls_custom_urls[i].name, _gnutls_custom_urls[i].name_size) == 0) { + if (_gnutls_custom_urls[i].import_crt) { + ret = _gnutls_custom_urls[i].import_crt(crt, url, flags); + goto cleanup; + } + break; + } + } + + if (strncmp(url, SYSTEM_URL, SYSTEM_URL_SIZE) == 0) { + ret = _gnutls_x509_crt_import_system_url(crt, url); +#ifdef ENABLE_PKCS11 + } else if (strncmp(url, PKCS11_URL, PKCS11_URL_SIZE) == 0) { + ret = _gnutls_x509_crt_import_pkcs11_url(crt, url, flags); +#endif + } else { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } + + cleanup: + return ret; +} + +/** + * gnutls_x509_crt_list_import_url: + * @certs: Will hold the allocated certificate list. + * @size: It will contain the size of the list. + * @url: A PKCS 11 url + * @pin_fn: a PIN callback if not globally set + * @pin_fn_userdata: parameter for the PIN callback + * @flags: One of GNUTLS_PKCS11_OBJ_* flags for PKCS#11 URLs or zero otherwise + * + * This function will import a certificate chain present in a PKCS#11 token + * or any type of back-end that supports URLs. The certificates + * must be deinitialized afterwards using gnutls_x509_crt_deinit() + * and the returned pointer must be freed using gnutls_free(). + * + * The URI provided must be the first certificate in the chain; subsequent + * certificates will be retrieved using gnutls_pkcs11_get_raw_issuer() or + * equivalent functionality for the supported URI. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.3 + **/ +int +gnutls_x509_crt_list_import_url(gnutls_x509_crt_t **certs, + unsigned int *size, + const char *url, + gnutls_pin_callback_t pin_fn, + void *pin_fn_userdata, + unsigned int flags) +{ + int ret; + unsigned i; + gnutls_x509_crt_t crts[DEFAULT_MAX_VERIFY_DEPTH]; + gnutls_datum_t issuer = {NULL, 0}; + unsigned total = 0; + + memset(crts, 0, sizeof(crts)); + + ret = gnutls_x509_crt_init(&crts[0]); + if (ret < 0) + return gnutls_assert_val(ret); + + gnutls_x509_crt_set_pin_function(crts[0], pin_fn, pin_fn_userdata); + + total = 1; + + ret = gnutls_x509_crt_import_url(crts[0], url, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + for (i=1;i<DEFAULT_MAX_VERIFY_DEPTH;i++) { + ret = _gnutls_get_raw_issuer(url, crts[i-1], &issuer, flags|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY); + if (ret < 0) { + issuer.data = NULL; + break; + } + + if (gnutls_x509_crt_equals2(crts[i-1], &issuer)) { + gnutls_free(issuer.data); + break; + } + + ret = gnutls_x509_crt_init(&crts[i]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + total++; + + gnutls_x509_crt_set_pin_function(crts[i], pin_fn, pin_fn_userdata); + + ret = gnutls_x509_crt_import(crts[i], &issuer, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + gnutls_free(issuer.data); + } + + *certs = _gnutls_reallocarray(NULL, total, sizeof(gnutls_x509_crt_t)); + if (*certs == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + memcpy(*certs, crts, total*sizeof(gnutls_x509_crt_t)); + *size = total; + + return 0; + cleanup: + gnutls_free(issuer.data); + for (i=0;i<total;i++) + gnutls_x509_crt_deinit(crts[i]); + + return ret; +} + +/*- + * gnutls_x509_crt_verify_data3: + * @crt: Holds the certificate to verify with + * @algo: The signature algorithm used + * @flags: Zero or an OR list of #gnutls_certificate_verify_flags + * @data: holds the signed data + * @signature: contains the signature + * + * This function will verify the given signed data, using the + * parameters from the certificate. + * + * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED + * is returned, %GNUTLS_E_EXPIRED or %GNUTLS_E_NOT_YET_ACTIVATED on expired + * or not yet activated certificate and zero or positive code on success. + * + * Since: 3.5.6 + -*/ +int +gnutls_x509_crt_verify_data3(gnutls_x509_crt_t crt, + gnutls_sign_algorithm_t algo, + gnutls_typed_vdata_st *vdata, + unsigned int vdata_size, + const gnutls_datum_t *data, + const gnutls_datum_t *signature, + unsigned int flags) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_verify_data2(pubkey, algo, flags, data, signature); + gnutls_pubkey_deinit(pubkey); + + if (ret >= 0) { + time_t now = gnutls_time(0); + int res; + unsigned usage, i; + + if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) || + !(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) { + if (now > gnutls_x509_crt_get_expiration_time(crt)) { + return gnutls_assert_val(GNUTLS_E_EXPIRED); + } + + if (now < gnutls_x509_crt_get_activation_time(crt)) { + return gnutls_assert_val(GNUTLS_E_NOT_YET_ACTIVATED); + } + } + + res = gnutls_x509_crt_get_key_usage(crt, &usage, NULL); + if (res >= 0) { + if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { + return gnutls_assert_val(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + } + } + + for (i=0;i<vdata_size;i++) { + if (vdata[i].type == GNUTLS_DT_KEY_PURPOSE_OID) { + res = _gnutls_check_key_purpose(crt, (char *)vdata[i].data, 0); + if (res == 0) + return gnutls_assert_val(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); + break; + } + } + } + + return ret; +} + +/** + * gnutls_x509_crt_verify_data2: + * @crt: Holds the certificate to verify with + * @algo: The signature algorithm used + * @flags: Zero or an OR list of #gnutls_certificate_verify_flags + * @data: holds the signed data + * @signature: contains the signature + * + * This function will verify the given signed data, using the + * parameters from the certificate. + * + * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED + * is returned, %GNUTLS_E_EXPIRED or %GNUTLS_E_NOT_YET_ACTIVATED on expired + * or not yet activated certificate and zero or positive code on success. + * + * Note that since GnuTLS 3.5.6 this function introduces checks in the + * end certificate (@crt), including time checks and key usage checks. + * + * Since: 3.4.0 + **/ +int +gnutls_x509_crt_verify_data2(gnutls_x509_crt_t crt, + gnutls_sign_algorithm_t algo, + unsigned int flags, + const gnutls_datum_t *data, + const gnutls_datum_t *signature) +{ + return gnutls_x509_crt_verify_data3(crt, algo, NULL, 0, + data, signature, flags); +} + +/** + * gnutls_x509_crt_set_flags: + * @cert: A type #gnutls_x509_crt_t + * @flags: flags from the %gnutls_x509_crt_flags + * + * This function will set flags for the specified certificate. + * Currently this is useful for the %GNUTLS_X509_CRT_FLAG_IGNORE_SANITY + * which allows importing certificates even if they have known issues. + * + * Since: 3.6.0 + * + **/ +void gnutls_x509_crt_set_flags(gnutls_x509_crt_t cert, + unsigned int flags) +{ + cert->flags = flags; +} + |