|
Packit |
aea12f |
/*
|
|
Packit |
aea12f |
* GnuTLS PKCS#11 support
|
|
Packit |
aea12f |
* Copyright (C) 2010-2016 Free Software Foundation, Inc.
|
|
Packit |
aea12f |
* Copyright (C) 2016 Red Hat
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Authors: Nikos Mavrogiannopoulos
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* GnuTLS is free software; you can redistribute it and/or
|
|
Packit |
aea12f |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
aea12f |
* the License, or (at your option) any later version.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
aea12f |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
aea12f |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
aea12f |
* Lesser General Public License for more details.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
aea12f |
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#include "gnutls_int.h"
|
|
Packit |
aea12f |
#include <gnutls/pkcs11.h>
|
|
Packit |
aea12f |
#include <global.h>
|
|
Packit |
aea12f |
#include "errors.h"
|
|
Packit |
aea12f |
#include "x509/common.h"
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
#include <pkcs11_int.h>
|
|
Packit |
aea12f |
#include <p11-kit/p11-kit.h>
|
|
Packit |
aea12f |
#include "pkcs11x.h"
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
struct find_ext_data_st {
|
|
Packit |
aea12f |
/* in */
|
|
Packit |
aea12f |
gnutls_pkcs11_obj_t obj;
|
|
Packit |
aea12f |
gnutls_datum_t spki;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* out */
|
|
Packit |
aea12f |
gnutls_x509_ext_st *exts;
|
|
Packit |
aea12f |
unsigned int exts_size;
|
|
Packit |
aea12f |
};
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static int override_ext(gnutls_x509_crt_t crt, gnutls_datum_t *ext)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
gnutls_x509_ext_st parsed;
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = _gnutls_x509_decode_ext(ext, &parsed);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* set the new extension */
|
|
Packit |
aea12f |
ret = _gnutls_x509_crt_set_extension(crt, parsed.oid, &parsed.data, parsed.critical);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = 0;
|
|
Packit |
aea12f |
cleanup:
|
|
Packit |
aea12f |
gnutls_x509_ext_deinit(&parsed);
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* This function re-encodes a certificate to contain its stapled extensions.
|
|
Packit |
aea12f |
* That assumes that the certificate is not in the distrusted list.
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
int pkcs11_override_cert_exts(struct pkcs11_session_info *sinfo, gnutls_datum_t *spki, gnutls_datum_t *der)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
gnutls_datum_t new_der = {NULL, 0};
|
|
Packit |
aea12f |
struct ck_attribute a[2];
|
|
Packit |
aea12f |
struct ck_attribute b[1];
|
|
Packit |
aea12f |
unsigned long count;
|
|
Packit |
aea12f |
unsigned ext_data_size = der->size;
|
|
Packit |
aea12f |
uint8_t *ext_data = NULL;
|
|
Packit |
aea12f |
ck_object_class_t class = -1;
|
|
Packit |
aea12f |
gnutls_x509_crt_t crt = NULL;
|
|
Packit |
aea12f |
unsigned finalize = 0;
|
|
Packit |
aea12f |
ck_rv_t rv;
|
|
Packit |
aea12f |
ck_object_handle_t obj;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (sinfo->trusted == 0) {
|
|
Packit |
aea12f |
_gnutls_debug_log("p11: cannot override extensions on a non-p11-kit trust module\n");
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* retrieve the extensions */
|
|
Packit |
aea12f |
class = CKO_X_CERTIFICATE_EXTENSION;
|
|
Packit |
aea12f |
a[0].type = CKA_CLASS;
|
|
Packit |
aea12f |
a[0].value = &class;
|
|
Packit |
aea12f |
a[0].value_len = sizeof class;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
a[1].type = CKA_PUBLIC_KEY_INFO;
|
|
Packit |
aea12f |
a[1].value = spki->data;
|
|
Packit |
aea12f |
a[1].value_len = spki->size;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, 2);
|
|
Packit |
aea12f |
if (rv != CKR_OK) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
_gnutls_debug_log
|
|
Packit |
aea12f |
("p11: FindObjectsInit failed for cert extensions.\n");
|
|
Packit |
aea12f |
ret = pkcs11_rv_to_err(rv);
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
finalize = 1;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
rv = pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count);
|
|
Packit |
aea12f |
if (rv == CKR_OK && count == 1) {
|
|
Packit |
aea12f |
ext_data = gnutls_malloc(ext_data_size);
|
|
Packit |
aea12f |
if (ext_data == NULL) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
ret = GNUTLS_E_MEMORY_ERROR;
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = gnutls_x509_crt_init(&crt;;
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = gnutls_x509_crt_import(crt, der, GNUTLS_X509_FMT_DER);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
do {
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
b[0].type = CKA_VALUE;
|
|
Packit |
aea12f |
b[0].value = ext_data;
|
|
Packit |
aea12f |
b[0].value_len = ext_data_size;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (pkcs11_get_attribute_value
|
|
Packit |
aea12f |
(sinfo->module, sinfo->pks, obj, b, 1) == CKR_OK) {
|
|
Packit |
aea12f |
gnutls_datum_t data = { b[0].value, b[0].value_len };
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = override_ext(crt, &data);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
} while (pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count) == CKR_OK && count == 1);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* overwrite the old certificate with the new */
|
|
Packit |
aea12f |
ret = gnutls_x509_crt_export2(crt, GNUTLS_X509_FMT_DER, &new_der);
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
gnutls_free(der->data);
|
|
Packit |
aea12f |
der->data = new_der.data;
|
|
Packit |
aea12f |
der->size = new_der.size;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = 0;
|
|
Packit |
aea12f |
cleanup:
|
|
Packit |
aea12f |
if (crt != NULL)
|
|
Packit |
aea12f |
gnutls_x509_crt_deinit(crt);
|
|
Packit |
aea12f |
if (finalize != 0)
|
|
Packit |
aea12f |
pkcs11_find_objects_final(sinfo);
|
|
Packit |
aea12f |
gnutls_free(ext_data);
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
static int
|
|
Packit |
aea12f |
find_ext_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo,
|
|
Packit |
aea12f |
struct ck_token_info *tinfo, struct ck_info *lib_info,
|
|
Packit |
aea12f |
void *input)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
struct find_ext_data_st *find_data = input;
|
|
Packit |
aea12f |
struct ck_attribute a[4];
|
|
Packit |
aea12f |
ck_object_class_t class = -1;
|
|
Packit |
aea12f |
unsigned long count;
|
|
Packit |
aea12f |
ck_rv_t rv;
|
|
Packit |
aea12f |
ck_object_handle_t obj;
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
gnutls_datum_t ext;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (tinfo == NULL) { /* we don't support multiple calls */
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* do not bother reading the token if basic fields do not match
|
|
Packit |
aea12f |
*/
|
|
Packit |
aea12f |
if (!p11_kit_uri_match_token_info
|
|
Packit |
aea12f |
(find_data->obj->info, tinfo)
|
|
Packit |
aea12f |
|| !p11_kit_uri_match_module_info(find_data->obj->info,
|
|
Packit |
aea12f |
lib_info)) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/* retrieve the extensions */
|
|
Packit |
aea12f |
class = CKO_X_CERTIFICATE_EXTENSION;
|
|
Packit |
aea12f |
a[0].type = CKA_CLASS;
|
|
Packit |
aea12f |
a[0].value = &class;
|
|
Packit |
aea12f |
a[0].value_len = sizeof class;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
a[1].type = CKA_PUBLIC_KEY_INFO;
|
|
Packit |
aea12f |
a[1].value = find_data->spki.data;
|
|
Packit |
aea12f |
a[1].value_len = find_data->spki.size;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, 2);
|
|
Packit |
aea12f |
if (rv != CKR_OK) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
_gnutls_debug_log
|
|
Packit |
aea12f |
("p11: FindObjectsInit failed for cert extensions.\n");
|
|
Packit |
aea12f |
return pkcs11_rv_to_err(rv);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
while(pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count) == CKR_OK && count == 1) {
|
|
Packit |
aea12f |
rv = pkcs11_get_attribute_avalue(sinfo->module, sinfo->pks, obj, CKA_VALUE, &ext;;
|
|
Packit |
aea12f |
if (rv == CKR_OK) {
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
find_data->exts = gnutls_realloc_fast(find_data->exts, (1+find_data->exts_size)*sizeof(find_data->exts[0]));
|
|
Packit |
aea12f |
if (find_data->exts == NULL) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
ret = pkcs11_rv_to_err(rv);
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (_gnutls_x509_decode_ext(&ext, &find_data->exts[find_data->exts_size]) == 0) {
|
|
Packit |
aea12f |
find_data->exts_size++;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
gnutls_free(ext.data);
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = 0;
|
|
Packit |
aea12f |
cleanup:
|
|
Packit |
aea12f |
pkcs11_find_objects_final(sinfo);
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
/**
|
|
Packit |
aea12f |
* gnutls_pkcs11_obj_get_exts:
|
|
Packit |
aea12f |
* @obj: should contain a #gnutls_pkcs11_obj_t type
|
|
Packit |
aea12f |
* @exts: a pointer to a %gnutls_x509_ext_st pointer
|
|
Packit |
aea12f |
* @exts_size: will be updated with the number of @exts
|
|
Packit |
aea12f |
* @flags: Or sequence of %GNUTLS_PKCS11_OBJ_* flags
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* This function will return information about attached extensions
|
|
Packit |
aea12f |
* that associate to the provided object (which should be a certificate).
|
|
Packit |
aea12f |
* The extensions are the attached p11-kit trust module extensions.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Each element of @exts must be deinitialized using gnutls_x509_ext_deinit()
|
|
Packit |
aea12f |
* while @exts should be deallocated using gnutls_free().
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code on error.
|
|
Packit |
aea12f |
*
|
|
Packit |
aea12f |
* Since: 3.3.8
|
|
Packit |
aea12f |
**/
|
|
Packit |
aea12f |
int
|
|
Packit |
aea12f |
gnutls_pkcs11_obj_get_exts(gnutls_pkcs11_obj_t obj,
|
|
Packit |
aea12f |
gnutls_x509_ext_st **exts, unsigned int *exts_size,
|
|
Packit |
aea12f |
unsigned int flags)
|
|
Packit |
aea12f |
{
|
|
Packit |
aea12f |
int ret;
|
|
Packit |
aea12f |
gnutls_datum_t spki = {NULL, 0};
|
|
Packit |
aea12f |
struct find_ext_data_st find_data;
|
|
Packit |
aea12f |
unsigned deinit_spki = 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
PKCS11_CHECK_INIT;
|
|
Packit |
aea12f |
memset(&find_data, 0, sizeof(find_data));
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
*exts_size = 0;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (obj->type != GNUTLS_PKCS11_OBJ_X509_CRT && obj->type != GNUTLS_PKCS11_OBJ_PUBKEY)
|
|
Packit |
aea12f |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
if (obj->type == GNUTLS_PKCS11_OBJ_PUBKEY) {
|
|
Packit |
aea12f |
spki.data = obj->raw.data;
|
|
Packit |
aea12f |
spki.size = obj->raw.size;
|
|
Packit |
aea12f |
} else {
|
|
Packit |
aea12f |
ret = _gnutls_x509_raw_crt_to_raw_pubkey(&obj->raw, &spki);
|
|
Packit |
aea12f |
if (ret < 0)
|
|
Packit |
aea12f |
return gnutls_assert_val(ret);
|
|
Packit |
aea12f |
deinit_spki = 1;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
find_data.spki.data = spki.data;
|
|
Packit |
aea12f |
find_data.spki.size = spki.size;
|
|
Packit |
aea12f |
find_data.obj = obj;
|
|
Packit |
aea12f |
ret =
|
|
Packit |
aea12f |
_pkcs11_traverse_tokens(find_ext_cb, &find_data, obj->info,
|
|
Packit |
aea12f |
&obj->pin,
|
|
Packit |
aea12f |
pkcs11_obj_flags_to_int(flags));
|
|
Packit |
aea12f |
if (ret < 0) {
|
|
Packit |
aea12f |
gnutls_assert();
|
|
Packit |
aea12f |
goto cleanup;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
*exts = find_data.exts;
|
|
Packit |
aea12f |
*exts_size = find_data.exts_size;
|
|
Packit |
aea12f |
|
|
Packit |
aea12f |
ret = 0;
|
|
Packit |
aea12f |
cleanup:
|
|
Packit |
aea12f |
if (deinit_spki)
|
|
Packit |
aea12f |
gnutls_free(spki.data);
|
|
Packit |
aea12f |
return ret;
|
|
Packit |
aea12f |
}
|
|
Packit |
aea12f |
|