diff options
Diffstat (limited to '')
-rw-r--r-- | lib/pkcs11.c | 4794 |
1 files changed, 4794 insertions, 0 deletions
diff --git a/lib/pkcs11.c b/lib/pkcs11.c new file mode 100644 index 0000000..3ece1d9 --- /dev/null +++ b/lib/pkcs11.c @@ -0,0 +1,4794 @@ +/* + * GnuTLS PKCS#11 support + * Copyright (C) 2010-2014 Free Software Foundation, Inc. + * Copyright (C) 2008 Joe Orton <joe@manyfish.co.uk> + * Copyright (C) 2013 Nikos Mavrogiannopoulos + * Copyright (C) 2014-2017 Red Hat + * + * Authors: Nikos Mavrogiannopoulos, Stef Walter + * + * Inspired and some parts (pkcs11_login) based on neon PKCS #11 support + * by Joe Orton. More ideas came from the pkcs11-helper library by + * Alon Bar-Lev. + * + * 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/> + */ + +#include "gnutls_int.h" +#include <gnutls/pkcs11.h> +#include <string.h> +#include "errors.h" +#include <datum.h> +#include <x509/common.h> +#include <locks.h> + +#include <pin.h> +#include <pkcs11_int.h> +#include "pkcs11x.h" +#include <system-keys.h> +#include "x509/x509_int.h" + +#include <atfork.h> +#include "intprops.h" + +#define MAX_PROVIDERS 16 + +#define MAX_SLOTS 48 + +GNUTLS_STATIC_MUTEX(pkcs11_mutex); + +struct gnutls_pkcs11_provider_st { + struct ck_function_list *module; + unsigned active; + unsigned custom_init; + unsigned trusted; /* in the sense of p11-kit trusted: + * it can be used for verification */ + struct ck_info info; +}; + +struct find_flags_data_st { + struct p11_kit_uri *info; + unsigned int slot_flags; /* Slot Information Flags */ + unsigned int token_flags; /* Token Information Flags */ + unsigned int trusted; +}; + +struct find_single_obj_st { + gnutls_pkcs11_obj_t obj; + bool overwrite_exts; /* only valid if looking for a certificate */ +}; + +struct find_obj_session_st { + gnutls_pkcs11_obj_t obj; + struct ck_function_list *ptr; + ck_session_handle_t pks; + ck_object_handle_t ohandle; + unsigned long slot_id; +}; + +struct find_multi_obj_st { + gnutls_pkcs11_obj_t *p_list; + unsigned int current; + unsigned int flags; + struct p11_kit_uri *info; + bool overwrite_exts; /* only valid if looking for a certificate */ +}; + +struct find_token_num { + struct p11_kit_uri *info; + unsigned int seq; /* which one we are looking for */ + unsigned int current; /* which one are we now */ +}; + +struct find_token_modname { + struct p11_kit_uri *info; + char *modname; + void *ptr; + unsigned long slot_id; +}; + +struct find_pkey_list_st { + gnutls_buffer_st *key_ids; + size_t key_ids_size; +}; + +struct find_cert_st { + gnutls_datum_t dn; + gnutls_datum_t issuer_dn; + gnutls_datum_t key_id; + gnutls_datum_t serial; + + unsigned need_import; + gnutls_pkcs11_obj_t obj; + gnutls_x509_crt_t crt; /* used when compare flag is specified */ + unsigned flags; +}; + + +static struct gnutls_pkcs11_provider_st providers[MAX_PROVIDERS]; +static unsigned int active_providers = 0; + +static init_level_t providers_initialized = PROV_UNINITIALIZED; +static unsigned int pkcs11_forkid = 0; + +static int _gnutls_pkcs11_reinit(void); + +gnutls_pkcs11_token_callback_t _gnutls_token_func; +void *_gnutls_token_data; + +static int auto_load(unsigned trusted); + +int pkcs11_rv_to_err(ck_rv_t rv) +{ + switch (rv) { + case CKR_OK: + return 0; + case CKR_HOST_MEMORY: + return GNUTLS_E_MEMORY_ERROR; + case CKR_SLOT_ID_INVALID: + return GNUTLS_E_PKCS11_SLOT_ERROR; + case CKR_ARGUMENTS_BAD: + case CKR_MECHANISM_PARAM_INVALID: + return GNUTLS_E_INVALID_REQUEST; + case CKR_NEED_TO_CREATE_THREADS: + case CKR_CANT_LOCK: + case CKR_FUNCTION_NOT_PARALLEL: + case CKR_MUTEX_BAD: + case CKR_MUTEX_NOT_LOCKED: + return GNUTLS_E_LOCKING_ERROR; + case CKR_ATTRIBUTE_READ_ONLY: + case CKR_ATTRIBUTE_SENSITIVE: + case CKR_ATTRIBUTE_TYPE_INVALID: + case CKR_ATTRIBUTE_VALUE_INVALID: + return GNUTLS_E_PKCS11_ATTRIBUTE_ERROR; + case CKR_DEVICE_ERROR: + case CKR_DEVICE_MEMORY: + case CKR_DEVICE_REMOVED: + return GNUTLS_E_PKCS11_DEVICE_ERROR; + case CKR_DATA_INVALID: + case CKR_DATA_LEN_RANGE: + case CKR_ENCRYPTED_DATA_INVALID: + case CKR_ENCRYPTED_DATA_LEN_RANGE: + case CKR_OBJECT_HANDLE_INVALID: + return GNUTLS_E_PKCS11_DATA_ERROR; + case CKR_FUNCTION_NOT_SUPPORTED: + case CKR_MECHANISM_INVALID: + return GNUTLS_E_PKCS11_UNSUPPORTED_FEATURE_ERROR; + case CKR_KEY_HANDLE_INVALID: + case CKR_KEY_SIZE_RANGE: + case CKR_KEY_TYPE_INCONSISTENT: + case CKR_KEY_NOT_NEEDED: + case CKR_KEY_CHANGED: + case CKR_KEY_NEEDED: + case CKR_KEY_INDIGESTIBLE: + case CKR_KEY_FUNCTION_NOT_PERMITTED: + case CKR_KEY_NOT_WRAPPABLE: + case CKR_KEY_UNEXTRACTABLE: + return GNUTLS_E_PKCS11_KEY_ERROR; + case CKR_PIN_INCORRECT: + case CKR_PIN_INVALID: + case CKR_PIN_LEN_RANGE: + return GNUTLS_E_PKCS11_PIN_ERROR; + case CKR_PIN_EXPIRED: + return GNUTLS_E_PKCS11_PIN_EXPIRED; + case CKR_PIN_LOCKED: + return GNUTLS_E_PKCS11_PIN_LOCKED; + case CKR_SESSION_CLOSED: + case CKR_SESSION_COUNT: + case CKR_SESSION_HANDLE_INVALID: + case CKR_SESSION_PARALLEL_NOT_SUPPORTED: + case CKR_SESSION_READ_ONLY: + case CKR_SESSION_EXISTS: + case CKR_SESSION_READ_ONLY_EXISTS: + case CKR_SESSION_READ_WRITE_SO_EXISTS: + return GNUTLS_E_PKCS11_SESSION_ERROR; + case CKR_SIGNATURE_INVALID: + case CKR_SIGNATURE_LEN_RANGE: + return GNUTLS_E_PKCS11_SIGNATURE_ERROR; + case CKR_TOKEN_NOT_PRESENT: + case CKR_TOKEN_NOT_RECOGNIZED: + case CKR_TOKEN_WRITE_PROTECTED: + return GNUTLS_E_PKCS11_TOKEN_ERROR; + case CKR_USER_ALREADY_LOGGED_IN: + case CKR_USER_NOT_LOGGED_IN: + case CKR_USER_PIN_NOT_INITIALIZED: + case CKR_USER_TYPE_INVALID: + case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: + case CKR_USER_TOO_MANY_TYPES: + return GNUTLS_E_PKCS11_USER_ERROR; + case CKR_BUFFER_TOO_SMALL: + return GNUTLS_E_SHORT_MEMORY_BUFFER; + default: + return GNUTLS_E_PKCS11_ERROR; + } +} + + +static int scan_slots(struct gnutls_pkcs11_provider_st *p, + ck_slot_id_t * slots, unsigned long *nslots) +{ + ck_rv_t rv; + + rv = pkcs11_get_slot_list(p->module, 1, slots, nslots); + if (rv != CKR_OK) { + gnutls_assert(); + return pkcs11_rv_to_err(rv); + } + return 0; +} + +static int +pkcs11_add_module(const char* name, struct ck_function_list *module, unsigned custom_init, const char *params) +{ + unsigned int i; + struct ck_info info; + + if (active_providers >= MAX_PROVIDERS) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + + memset(&info, 0, sizeof(info)); + pkcs11_get_module_info(module, &info); + + /* initially check if this module is a duplicate */ + for (i = 0; i < active_providers; i++) { + /* already loaded, skip the rest */ + if (module == providers[i].module || + memcmp(&info, &providers[i].info, sizeof(info)) == 0) { + _gnutls_debug_log("p11: module %s is already loaded.\n", name); + return GNUTLS_E_INT_RET_0; + } + } + + active_providers++; + providers[active_providers - 1].module = module; + providers[active_providers - 1].active = 1; + providers[active_providers - 1].trusted = 0; + providers[active_providers - 1].custom_init = custom_init; + + if (p11_kit_module_get_flags(module) & P11_KIT_MODULE_TRUSTED || + (params != NULL && strstr(params, "trusted") != 0)) + providers[active_providers - 1].trusted = 1; + + memcpy(&providers[active_providers - 1].info, &info, sizeof(info)); + + return 0; +} + +/* Returns: + * - negative error code on error, + * - 0 on success + * - 1 on success (and a fork was detected - cb was run) + * + * The output value of the callback will be returned if it is + * a negative one (indicating failure). +*/ +int _gnutls_pkcs11_check_init(init_level_t req_level, void *priv, pkcs11_reinit_function cb) +{ + int ret, sret = 0; + + ret = gnutls_static_mutex_lock(&pkcs11_mutex); + if (ret != 0) + return gnutls_assert_val(GNUTLS_E_LOCKING_ERROR); + + if (providers_initialized > PROV_UNINITIALIZED) { + ret = 0; + + if (_gnutls_detect_fork(pkcs11_forkid)) { + /* if we are initialized but a fork is detected */ + ret = _gnutls_pkcs11_reinit(); + if (ret == 0) { + sret = 1; + if (cb) { + int ret2 = cb(priv); + if (ret2 < 0) + ret = ret2; + } + pkcs11_forkid = _gnutls_get_forkid(); + } + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + /* Possible Transitions: PROV_UNINITIALIZED -> PROV_INIT_MANUAL -> PROV_INIT_MANUAL_TRUSTED + * PROV_UNINITIALIZED -> PROV_INIT_TRUSTED -> PROV_INIT_ALL + * + * request for PROV_INIT_TRUSTED may result to PROV_INIT_MANUAL_TRUSTED + * request for PROV_INIT_ALL may result to PROV_INIT_MANUAL or PROV_INIT_MANUAL_TRUSTED + */ + switch(req_level) { + case PROV_UNINITIALIZED: + case PROV_INIT_MANUAL: + break; + case PROV_INIT_TRUSTED: + case PROV_INIT_MANUAL_TRUSTED: + if (providers_initialized < PROV_INIT_MANUAL_TRUSTED) { + _gnutls_debug_log("Initializing needed PKCS #11 modules\n"); + ret = auto_load(1); + if (ret < 0) { + gnutls_assert(); + } + + if (providers_initialized == PROV_INIT_MANUAL) + providers_initialized = PROV_INIT_MANUAL_TRUSTED; + else + providers_initialized = PROV_INIT_TRUSTED; + + goto cleanup; + } + break; + case PROV_INIT_ALL: + if (providers_initialized == PROV_INIT_TRUSTED || + providers_initialized == PROV_UNINITIALIZED) { + _gnutls_debug_log("Initializing all PKCS #11 modules\n"); + ret = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_AUTO, NULL); + if (ret < 0) { + gnutls_assert(); + } + + providers_initialized = PROV_INIT_ALL; + goto cleanup; + } + break; + } + + ret = sret; + + cleanup: + (void)gnutls_static_mutex_unlock(&pkcs11_mutex); + + return ret; +} + + +/** + * gnutls_pkcs11_add_provider: + * @name: The filename of the module + * @params: should be NULL or a known string (see description) + * + * This function will load and add a PKCS 11 module to the module + * list used in gnutls. After this function is called the module will + * be used for PKCS 11 operations. + * + * When loading a module to be used for certificate verification, + * use the string 'trusted' as @params. + * + * Note that this function is not thread safe. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + **/ +int gnutls_pkcs11_add_provider(const char *name, const char *params) +{ + struct ck_function_list *module; + unsigned custom_init = 0, flags = 0; + struct ck_c_initialize_args args; + const char *p; + int ret; + + if (params && (p = strstr(params, "p11-kit:")) != 0) { + memset (&args, 0, sizeof (args)); + args.reserved = (char*)(p + sizeof("p11-kit:")-1); + args.flags = CKF_OS_LOCKING_OK; + + custom_init = 1; + flags = P11_KIT_MODULE_UNMANAGED; + } + + module = p11_kit_module_load(name, P11_KIT_MODULE_CRITICAL|flags); + if (module == NULL) { + gnutls_assert(); + _gnutls_debug_log("p11: Cannot load provider %s\n", name); + return GNUTLS_E_PKCS11_LOAD_ERROR; + } + + _gnutls_debug_log + ("p11: Initializing module: %s\n", name); + + /* check if we have special information for a p11-kit trust module */ + if (custom_init) { + ret = module->C_Initialize(&args); + } else { + ret = p11_kit_module_initialize(module); + } + + if (ret != CKR_OK) { + p11_kit_module_release(module); + gnutls_assert(); + return pkcs11_rv_to_err(ret); + } + + ret = pkcs11_add_module(name, module, custom_init, params); + if (ret != 0) { + if (ret == GNUTLS_E_INT_RET_0) + ret = 0; + if (!custom_init) + p11_kit_module_finalize(module); + else + module->C_Finalize(NULL); + p11_kit_module_release(module); + gnutls_assert(); + } + + return ret; +} + +static +int add_obj_attrs(struct p11_kit_uri *info, struct ck_attribute a[4], unsigned *a_vals, ck_object_class_t *class, ck_certificate_type_t *type) +{ + struct ck_attribute *attr; + + *type = -1; + *class = CKO_CERTIFICATE; + + /* find the object that matches the URL */ + *a_vals = 0; + attr = p11_kit_uri_get_attribute(info, CKA_ID); + if (attr) { + memcpy(a + (*a_vals), attr, sizeof(struct ck_attribute)); + (*a_vals)++; + } + + attr = p11_kit_uri_get_attribute(info, CKA_LABEL); + if (attr) { + memcpy(a + (*a_vals), attr, sizeof(struct ck_attribute)); + (*a_vals)++; + } + + if (!(*a_vals)) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Find objects with given class and type */ + attr = p11_kit_uri_get_attribute(info, CKA_CLASS); + if (attr) { + if (attr->value + && attr->value_len == sizeof(ck_object_class_t)) + memcpy(class, attr->value, sizeof(ck_object_class_t)); + if (*class == CKO_CERTIFICATE) + *type = CKC_X_509; + memcpy(a + (*a_vals), attr, sizeof(struct ck_attribute)); + (*a_vals)++; + } + + if (*type != (ck_certificate_type_t) - 1) { + a[(*a_vals)].type = CKA_CERTIFICATE_TYPE; + a[(*a_vals)].value = type; + a[(*a_vals)].value_len = sizeof *type; + (*a_vals)++; + } + + return 0; +} + +/** + * gnutls_pkcs11_obj_set_info: + * @obj: should contain a #gnutls_pkcs11_obj_t type + * @itype: Denotes the type of information to be set + * @data: the data to set + * @data_size: the size of data + * @flags: Or sequence of GNUTLS_PKCS11_OBJ_* flags + * + * This function will set attributes on the provided object. + * Available options for @itype are %GNUTLS_PKCS11_OBJ_LABEL, + * %GNUTLS_PKCS11_OBJ_ID_HEX, and %GNUTLS_PKCS11_OBJ_ID. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code on error. + * + * Since: 3.4.0 + **/ +int +gnutls_pkcs11_obj_set_info(gnutls_pkcs11_obj_t obj, + gnutls_pkcs11_obj_info_t itype, + const void *data, size_t data_size, + unsigned flags) +{ + struct p11_kit_uri *info = obj->info; + struct pkcs11_session_info sinfo; + struct ck_attribute a[4]; + ck_object_handle_t ctx[2]; + ck_certificate_type_t type; + ck_object_class_t class; + unsigned long count; + size_t size; + unsigned a_vals; + char tmp[128]; + ck_rv_t rv; + int ret; + + PKCS11_CHECK_INIT; + + ret = + pkcs11_open_session(&sinfo, NULL, info, + SESSION_WRITE | + pkcs11_obj_flags_to_int(flags)); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = add_obj_attrs(info, a, &a_vals, &class, &type); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + rv = pkcs11_find_objects_init(sinfo.module, sinfo.pks, a, + a_vals); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: FindObjectsInit failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + rv = pkcs11_find_objects(sinfo.module, sinfo.pks, ctx, 2, &count); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: FindObjects failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + if (count > 1 || count == 0) { + gnutls_assert(); + if (count > 1) + _gnutls_debug_log("p11: More than one objects match (%d)\n", (int)count); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + switch (itype) { + case GNUTLS_PKCS11_OBJ_ID_HEX: + size = sizeof(tmp); + ret = _gnutls_hex2bin(data, data_size, (uint8_t*)tmp, &size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + data = tmp; + data_size = size; + + FALLTHROUGH; + case GNUTLS_PKCS11_OBJ_ID: + a[0].type = CKA_ID; + a[0].value = (void*)data; + a[0].value_len = data_size; + + rv = pkcs11_set_attribute_value(sinfo.module, sinfo.pks, ctx[0], a, 1); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: set_attribute_value failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + break; + case GNUTLS_PKCS11_OBJ_LABEL: + a[0].type = CKA_LABEL; + a[0].value = (void*)data; + a[0].value_len = data_size; + + rv = pkcs11_set_attribute_value(sinfo.module, sinfo.pks, ctx[0], a, 1); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: set_attribute_value failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + break; + default: + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + ret = 0; + cleanup: + pkcs11_close_session(&sinfo); + return ret; +} + +/** + * gnutls_pkcs11_obj_get_info: + * @obj: should contain a #gnutls_pkcs11_obj_t type + * @itype: Denotes the type of information requested + * @output: where output will be stored + * @output_size: contains the maximum size of the output buffer and will be + * overwritten with the actual size. + * + * This function will return information about the PKCS11 certificate + * such as the label, id as well as token information where the key is + * stored. + * + * When output is text, a null terminated string is written to @output and its + * string length is written to @output_size (without null terminator). If the + * buffer is too small, @output_size will contain the expected buffer size + * (with null terminator for text) and return %GNUTLS_E_SHORT_MEMORY_BUFFER. + * + * In versions previously to 3.6.0 this function included the null terminator + * to @output_size. After 3.6.0 the output size doesn't include the terminator character. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code on error. + * + * Since: 2.12.0 + **/ +int +gnutls_pkcs11_obj_get_info(gnutls_pkcs11_obj_t obj, + gnutls_pkcs11_obj_info_t itype, + void *output, size_t * output_size) +{ + return pkcs11_get_info(obj->info, itype, output, output_size); +} + +static int +find_obj_session_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, struct ck_info *lib_info, + void *input) +{ + struct find_obj_session_st *find_data = input; + struct ck_attribute a[4]; + ck_rv_t rv; + ck_object_handle_t ctx = CK_INVALID_HANDLE; + unsigned long count; + unsigned a_vals; + ck_certificate_type_t type; + ck_object_class_t class; + int found = 0, ret; + + if (tinfo == NULL) { /* we don't support multiple calls */ + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* do not bother reading the token if basic fields do not match + */ + if (!p11_kit_uri_match_token_info(find_data->obj->info, tinfo) || + !p11_kit_uri_match_module_info(find_data->obj->info, + lib_info)) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = add_obj_attrs(find_data->obj->info, a, &a_vals, &class, &type); + if (ret < 0) + return gnutls_assert_val(ret); + + rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, + a_vals); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: FindObjectsInit failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + if (pkcs11_find_objects(sinfo->module, sinfo->pks, &ctx, 1, &count) == CKR_OK && + count == 1) { + find_data->ptr = sinfo->module; + find_data->pks = sinfo->pks; + find_data->slot_id = sinfo->sid; + find_data->ohandle = ctx; + found = 1; + } + + if (found == 0) { + gnutls_assert(); + if (count > 1) + ret = GNUTLS_E_TOO_MANY_MATCHES; + else + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + } else { + ret = 0; + } + + cleanup: + pkcs11_find_objects_final(sinfo); + + return ret; +} + + +/** + * gnutls_pkcs11_obj_get_ptr: + * @obj: should contain a #gnutls_pkcs11_obj_t type + * @ptr: will contain the CK_FUNCTION_LIST_PTR pointer (may be %NULL) + * @session: will contain the CK_SESSION_HANDLE of the object + * @ohandle: will contain the CK_OBJECT_HANDLE of the object + * @slot_id: the identifier of the slot (may be %NULL) + * @flags: Or sequence of GNUTLS_PKCS11_OBJ_* flags + * + * Obtains the PKCS#11 session handles of an object. @session and @ohandle + * must be deinitialized by the caller. The returned pointers are + * independent of the @obj lifetime. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code + * on error. + * + * Since: 3.6.3 + **/ +int +gnutls_pkcs11_obj_get_ptr(gnutls_pkcs11_obj_t obj, void **ptr, + void **session, void **ohandle, + unsigned long *slot_id, + unsigned int flags) +{ + int ret; + struct find_obj_session_st find_data; + + PKCS11_CHECK_INIT; + memset(&find_data, 0, sizeof(find_data)); + + find_data.obj = obj; + + ret = + _pkcs11_traverse_tokens(find_obj_session_cb, &find_data, obj->info, + &obj->pin, + SESSION_NO_CLOSE|pkcs11_obj_flags_to_int(flags)); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (ptr) + *ptr = find_data.ptr; + + *ohandle = (void*)find_data.ohandle; + *session = (void*)find_data.pks; + + if (slot_id) + *slot_id = find_data.slot_id; + + return 0; +} + +int +pkcs11_get_info(struct p11_kit_uri *info, + gnutls_pkcs11_obj_info_t itype, void *output, + size_t * output_size) +{ + struct ck_attribute *attr = NULL; + struct ck_version *version = NULL; + const uint8_t *str = NULL; + size_t str_max = 0; + int terminate = 0; + int hexify = 0; + size_t length = 0; + const char *data = NULL; + char buf[32]; + + /* + * Either attr, str or version is valid by the time switch + * finishes + */ + + switch (itype) { + case GNUTLS_PKCS11_OBJ_ID: + attr = p11_kit_uri_get_attribute(info, CKA_ID); + break; + case GNUTLS_PKCS11_OBJ_ID_HEX: + attr = p11_kit_uri_get_attribute(info, CKA_ID); + hexify = 1; + terminate = 1; + break; + case GNUTLS_PKCS11_OBJ_LABEL: + attr = p11_kit_uri_get_attribute(info, CKA_LABEL); + terminate = 1; + break; + case GNUTLS_PKCS11_OBJ_TOKEN_LABEL: + str = p11_kit_uri_get_token_info(info)->label; + str_max = 32; + break; + case GNUTLS_PKCS11_OBJ_TOKEN_SERIAL: + str = p11_kit_uri_get_token_info(info)->serial_number; + str_max = 16; + break; + case GNUTLS_PKCS11_OBJ_TOKEN_MANUFACTURER: + str = p11_kit_uri_get_token_info(info)->manufacturer_id; + str_max = 32; + break; + case GNUTLS_PKCS11_OBJ_TOKEN_MODEL: + str = p11_kit_uri_get_token_info(info)->model; + str_max = 16; + break; + case GNUTLS_PKCS11_OBJ_LIBRARY_DESCRIPTION: + str = + p11_kit_uri_get_module_info(info)->library_description; + str_max = 32; + break; + case GNUTLS_PKCS11_OBJ_LIBRARY_VERSION: + version = + &p11_kit_uri_get_module_info(info)->library_version; + break; + case GNUTLS_PKCS11_OBJ_LIBRARY_MANUFACTURER: + str = p11_kit_uri_get_module_info(info)->manufacturer_id; + str_max = 32; + break; + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (attr != NULL) { + data = attr->value; + length = attr->value_len; + } else if (str != NULL) { + data = (void *) str; + length = p11_kit_space_strlen(str, str_max); + terminate = 1; + } else if (version != NULL) { + data = buf; + length = + snprintf(buf, sizeof(buf), "%d.%d", + (int) version->major, (int) version->minor); + terminate = 1; + } else { + *output_size = 0; + if (output) + ((uint8_t *) output)[0] = 0; + return 0; + } + + if (hexify) { + /* terminate is assumed with hexify */ + if (*output_size < length * 3) { + *output_size = length * 3; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + if (output && length > 0) + _gnutls_bin2hex(data, length, output, *output_size, + ":"); + *output_size = length * 3; + return 0; + } else { + if (*output_size < length + terminate) { + *output_size = length + terminate; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + if (output) { + memcpy(output, data, length); + if (terminate) + ((unsigned char *) output)[length] = '\0'; + } + *output_size = length; + } + + return 0; +} + +static int init = 0; + +/* tries to load modules from /etc/gnutls/pkcs11.conf if it exists + */ +static void compat_load(const char *configfile) +{ + FILE *fp; + int ret; + char line[512]; + const char *library; + + if (configfile == NULL) + configfile = "/etc/gnutls/pkcs11.conf"; + + fp = fopen(configfile, "re"); + if (fp == NULL) { + gnutls_assert(); + return; + } + + _gnutls_debug_log("Loading PKCS #11 libraries from %s\n", + configfile); + while (fgets(line, sizeof(line), fp) != NULL) { + if (strncmp(line, "load", sizeof("load") - 1) == 0) { + char *p; + p = strchr(line, '='); + if (p == NULL) + continue; + + library = ++p; + p = strchr(line, '\n'); + if (p != NULL) + *p = 0; + + ret = gnutls_pkcs11_add_provider(library, NULL); + if (ret < 0) { + gnutls_assert(); + _gnutls_debug_log + ("Cannot load provider: %s\n", + library); + continue; + } + } + } + fclose(fp); + + return; +} + +static int auto_load(unsigned trusted) +{ + struct ck_function_list **modules; + int i, ret; + char* name; + + modules = p11_kit_modules_load_and_initialize(trusted?P11_KIT_MODULE_TRUSTED:0); + if (modules == NULL) { + gnutls_assert(); + _gnutls_debug_log + ("Cannot initialize registered modules: %s\n", + p11_kit_message()); + return GNUTLS_E_PKCS11_LOAD_ERROR; + } + + for (i = 0; modules[i] != NULL; i++) { + name = p11_kit_module_get_name(modules[i]); + _gnutls_debug_log + ("p11: Initializing module: %s\n", name); + + ret = pkcs11_add_module(name, modules[i], 0, NULL); + if (ret < 0) { + gnutls_assert(); + _gnutls_debug_log + ("Cannot load PKCS #11 module: %s\n", name); + } + free(name); + } + + /* Shallow free */ + free(modules); + return 0; +} + +/** + * gnutls_pkcs11_init: + * @flags: An ORed sequence of %GNUTLS_PKCS11_FLAG_* + * @deprecated_config_file: either NULL or the location of a deprecated + * configuration file + * + * This function will initialize the PKCS 11 subsystem in gnutls. It will + * read configuration files if %GNUTLS_PKCS11_FLAG_AUTO is used or allow + * you to independently load PKCS 11 modules using gnutls_pkcs11_add_provider() + * if %GNUTLS_PKCS11_FLAG_MANUAL is specified. + * + * You don't need to call this function since GnuTLS 3.3.0 because it is being called + * during the first request PKCS 11 operation. That call will assume the %GNUTLS_PKCS11_FLAG_AUTO + * flag. If another flags are required then it must be called independently + * prior to any PKCS 11 operation. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + **/ +int +gnutls_pkcs11_init(unsigned int flags, const char *deprecated_config_file) +{ + int ret = 0; + + if (init != 0) { + init++; + return 0; + } + init++; + + pkcs11_forkid = _gnutls_get_forkid(); + + p11_kit_pin_register_callback(P11_KIT_PIN_FALLBACK, + p11_kit_pin_file_callback, NULL, + NULL); + + if (flags == GNUTLS_PKCS11_FLAG_MANUAL) { + /* if manual configuration is requested then don't + * bother loading any other providers */ + providers_initialized = PROV_INIT_MANUAL; + return 0; + } else if (flags & GNUTLS_PKCS11_FLAG_AUTO) { + if (deprecated_config_file == NULL) + ret = auto_load(0); + + compat_load(deprecated_config_file); + + providers_initialized = PROV_INIT_ALL; + + return ret; + } else if (flags & GNUTLS_PKCS11_FLAG_AUTO_TRUSTED) { + ret = auto_load(1); + + providers_initialized = PROV_INIT_TRUSTED; + + return ret; + } + + return 0; +} + +static int _gnutls_pkcs11_reinit(void) +{ + unsigned i; + ck_rv_t rv; + + for (i = 0; i < active_providers; i++) { + if (providers[i].module != NULL) { + rv = p11_kit_module_initialize(providers + [i].module); + if (rv == CKR_OK || rv == CKR_CRYPTOKI_ALREADY_INITIALIZED) { + providers[i].active = 1; + } else { + providers[i].active = 0; + _gnutls_debug_log + ("Cannot re-initialize registered module '%.*s': %s\n", + (int)32, providers[i].info.library_description, + p11_kit_strerror(rv)); + } + } + } + + return 0; +} + +/** + * gnutls_pkcs11_reinit: + * + * This function will reinitialize the PKCS 11 subsystem in gnutls. + * This is required by PKCS 11 when an application uses fork(). The + * reinitialization function must be called on the child. + * + * Note that since GnuTLS 3.3.0, the reinitialization of the PKCS #11 + * subsystem occurs automatically after fork. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.0 + **/ +int gnutls_pkcs11_reinit(void) +{ + int ret; + + /* make sure that we don't call more than once after a fork */ + if (_gnutls_detect_fork(pkcs11_forkid) == 0) + return 0; + + ret = _gnutls_pkcs11_reinit(); + + pkcs11_forkid = _gnutls_get_forkid(); + + return ret; +} + +/** + * gnutls_pkcs11_deinit: + * + * This function will deinitialize the PKCS 11 subsystem in gnutls. + * This function is only needed if you need to deinitialize the + * subsystem without calling gnutls_global_deinit(). + * + * Since: 2.12.0 + **/ +void gnutls_pkcs11_deinit(void) +{ + unsigned int i; + + if (init == 0) + return; + + init--; + if (init > 0) + return; + + for (i = 0; i < active_providers; i++) { + if (providers[i].active) { + + if (!providers[i].custom_init) + p11_kit_module_finalize(providers[i].module); + else + providers[i].module->C_Finalize(NULL); + } + p11_kit_module_release(providers[i].module); + } + active_providers = 0; + providers_initialized = PROV_UNINITIALIZED; + + gnutls_pkcs11_set_pin_function(NULL, NULL); + gnutls_pkcs11_set_token_function(NULL, NULL); + p11_kit_pin_unregister_callback(P11_KIT_PIN_FALLBACK, + p11_kit_pin_file_callback, NULL); +} + +/** + * gnutls_pkcs11_set_token_function: + * @fn: The token callback + * @userdata: data to be supplied to callback + * + * This function will set a callback function to be used when a token + * needs to be inserted to continue PKCS 11 operations. + * + * Since: 2.12.0 + **/ +void +gnutls_pkcs11_set_token_function(gnutls_pkcs11_token_callback_t fn, + void *userdata) +{ + _gnutls_token_func = fn; + _gnutls_token_data = userdata; +} + +int pkcs11_url_to_info(const char *url, struct p11_kit_uri **info, unsigned flags) +{ + int allocated = 0; + int ret; + struct ck_attribute at; + ck_object_class_t klass; + + if (*info == NULL) { + *info = p11_kit_uri_new(); + if (*info == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + allocated = 1; + } + + ret = p11_kit_uri_parse(url, P11_KIT_URI_FOR_ANY, *info); + if (ret < 0) { + if (allocated) { + p11_kit_uri_free(*info); + *info = NULL; + } + gnutls_assert(); + return ret == P11_KIT_URI_NO_MEMORY ? + GNUTLS_E_MEMORY_ERROR : GNUTLS_E_PARSING_ERROR; + } + + /* check for incomplete/invalid URIs */ + if (flags & GNUTLS_PKCS11_OBJ_FLAG_EXPECT_CERT) { + klass = CKO_CERTIFICATE; + at.type = CKA_CLASS; + at.value = &klass; + at.value_len = sizeof (klass); + p11_kit_uri_set_attribute (*info, &at); + } else if (flags & GNUTLS_PKCS11_OBJ_FLAG_EXPECT_PRIVKEY) { + klass = CKO_PRIVATE_KEY; + at.type = CKA_CLASS; + at.value = &klass; + at.value_len = sizeof (klass); + p11_kit_uri_set_attribute (*info, &at); + } else if (flags & GNUTLS_PKCS11_OBJ_FLAG_EXPECT_PUBKEY) { + klass = CKO_PUBLIC_KEY; + at.type = CKA_CLASS; + at.value = &klass; + at.value_len = sizeof (klass); + p11_kit_uri_set_attribute (*info, &at); + } + + return 0; +} + +int +pkcs11_info_to_url(struct p11_kit_uri *info, + gnutls_pkcs11_url_type_t detailed, char **url) +{ + p11_kit_uri_type_t type = 0; + int ret; + + switch (detailed) { + case GNUTLS_PKCS11_URL_GENERIC: + type = P11_KIT_URI_FOR_OBJECT_ON_TOKEN; + break; + case GNUTLS_PKCS11_URL_LIB: + type = P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE; + break; + case GNUTLS_PKCS11_URL_LIB_VERSION: + type = + P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE | + P11_KIT_URI_FOR_MODULE_WITH_VERSION; + break; + } + + ret = p11_kit_uri_format(info, type, url); + if (ret < 0) { + gnutls_assert(); + return ret == P11_KIT_URI_NO_MEMORY ? + GNUTLS_E_MEMORY_ERROR : GNUTLS_E_INTERNAL_ERROR; + } + + return 0; +} + +/** + * gnutls_pkcs11_obj_init: + * @obj: A pointer to the type to be initialized + * + * This function will initialize a pkcs11 certificate structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + **/ +int gnutls_pkcs11_obj_init(gnutls_pkcs11_obj_t * obj) +{ + *obj = gnutls_calloc(1, sizeof(struct gnutls_pkcs11_obj_st)); + if (*obj == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + (*obj)->info = p11_kit_uri_new(); + if ((*obj)->info == NULL) { + gnutls_free(*obj); + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +/** + * gnutls_pkcs11_obj_set_pin_function: + * @obj: The object structure + * @fn: the callback + * @userdata: data associated with the callback + * + * This function will set a callback function to be used when + * required to access the object. This function overrides the global + * set using gnutls_pkcs11_set_pin_function(). + * + * Since: 3.1.0 + **/ +void +gnutls_pkcs11_obj_set_pin_function(gnutls_pkcs11_obj_t obj, + gnutls_pin_callback_t fn, + void *userdata) +{ + obj->pin.cb = fn; + obj->pin.data = userdata; +} + +/** + * gnutls_pkcs11_obj_deinit: + * @obj: The type to be deinitialized + * + * This function will deinitialize a certificate structure. + * + * Since: 2.12.0 + **/ +void gnutls_pkcs11_obj_deinit(gnutls_pkcs11_obj_t obj) +{ + unsigned i; + for (i=0;i<obj->pubkey_size;i++) + _gnutls_free_datum(&obj->pubkey[i]); + _gnutls_free_datum(&obj->raw); + p11_kit_uri_free(obj->info); + free(obj); +} + +/** + * gnutls_pkcs11_obj_export: + * @obj: Holds the object + * @output_data: will contain the object data + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the PKCS11 object data. It is normal for + * data to be inaccessible and in that case %GNUTLS_E_INVALID_REQUEST + * will be returned. + * + * 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. + * + * Returns: In case of failure a negative error code will be + * returned, and %GNUTLS_E_SUCCESS (0) on success. + * + * Since: 2.12.0 + **/ +int +gnutls_pkcs11_obj_export(gnutls_pkcs11_obj_t obj, + void *output_data, size_t * output_data_size) +{ + if (obj == NULL || obj->raw.data == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (output_data == NULL || *output_data_size < obj->raw.size) { + *output_data_size = obj->raw.size; + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + *output_data_size = obj->raw.size; + + memcpy(output_data, obj->raw.data, obj->raw.size); + return 0; +} + +/** + * gnutls_pkcs11_obj_export2: + * @obj: Holds the object + * @out: will contain the object data + * + * This function will export the PKCS11 object data. It is normal for + * data to be inaccessible and in that case %GNUTLS_E_INVALID_REQUEST + * will be returned. + * + * The output buffer is allocated using gnutls_malloc(). + * + * Returns: In case of failure a negative error code will be + * returned, and %GNUTLS_E_SUCCESS (0) on success. + * + * Since: 3.1.3 + **/ +int +gnutls_pkcs11_obj_export2(gnutls_pkcs11_obj_t obj, gnutls_datum_t * out) +{ + return gnutls_pkcs11_obj_export3(obj, GNUTLS_X509_FMT_DER, out); +} + +/** + * gnutls_pkcs11_obj_export3: + * @obj: Holds the object + * @out: will contain the object data + * @fmt: The format of the exported data + * + * This function will export the PKCS11 object data. It is normal for + * data to be inaccessible and in that case %GNUTLS_E_INVALID_REQUEST + * will be returned. + * + * The output buffer is allocated using gnutls_malloc(). + * + * Returns: In case of failure a negative error code will be + * returned, and %GNUTLS_E_SUCCESS (0) on success. + * + * Since: 3.2.7 + **/ +int +gnutls_pkcs11_obj_export3(gnutls_pkcs11_obj_t obj, + gnutls_x509_crt_fmt_t fmt, gnutls_datum_t * out) +{ + int ret; + + if (obj == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + switch (obj->type) { + case GNUTLS_PKCS11_OBJ_X509_CRT: + if (obj->raw.data == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (fmt == GNUTLS_X509_FMT_PEM) { + return + gnutls_pem_base64_encode2(PEM_X509_CERT2, + &obj->raw, out); + } else { + return _gnutls_set_datum(out, obj->raw.data, + obj->raw.size); + } + case GNUTLS_PKCS11_OBJ_PUBKEY:{ + /* that approach allows to return a public key even if + * CKA_VALUE is not set */ + gnutls_pubkey_t pubkey; + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = + gnutls_pubkey_import_pkcs11(pubkey, + obj, 0); + if (ret < 0) { + gnutls_assert(); + goto pcleanup; + } + + ret = + gnutls_pubkey_export2(pubkey, fmt, + out); + + pcleanup: + gnutls_pubkey_deinit(pubkey); + return ret; + } + default: + if (obj->raw.data == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if (fmt == GNUTLS_X509_FMT_PEM) { + return gnutls_pem_base64_encode2("DATA", + &obj->raw, + out); + } else { + return _gnutls_set_datum(out, obj->raw.data, + obj->raw.size); + } + } +} + + +int +pkcs11_find_slot(struct ck_function_list **module, ck_slot_id_t * slot, + struct p11_kit_uri *info, + struct ck_token_info *_tinfo, + struct ck_slot_info *_slot_info, + unsigned int *trusted) +{ + unsigned int x, z; + int ret; + unsigned long nslots; + ck_slot_id_t slots[MAX_SLOTS]; + + for (x = 0; x < active_providers; x++) { + if (providers[x].active == 0) + continue; + + if (!p11_kit_uri_match_module_info(info, + &providers[x].info)) { + continue; + } + + nslots = sizeof(slots) / sizeof(slots[0]); + ret = scan_slots(&providers[x], slots, &nslots); + if (ret < 0) { + gnutls_assert(); + continue; + } + + for (z = 0; z < nslots; z++) { + struct ck_token_info tinfo; + struct ck_slot_info sinfo; + + if (pkcs11_get_token_info + (providers[x].module, slots[z], + &tinfo) != CKR_OK) { + continue; + } + + if (!p11_kit_uri_match_token_info(info, &tinfo)) { + continue; + } + + if (pkcs11_get_slot_info + (providers[x].module, slots[z], &sinfo) != CKR_OK) { + continue; + } + + /* ok found */ + *module = providers[x].module; + *slot = slots[z]; + + if (trusted) + *trusted = providers[x].trusted; + + if (_tinfo != NULL) + memcpy(_tinfo, &tinfo, sizeof(tinfo)); + + if (_slot_info != NULL) + memcpy(_slot_info, &sinfo, sizeof(sinfo)); + + return 0; + } + } + + gnutls_assert(); + return GNUTLS_E_PKCS11_REQUESTED_OBJECT_NOT_AVAILBLE; +} + +int +pkcs11_open_session(struct pkcs11_session_info *sinfo, + struct pin_info_st *pin_info, + struct p11_kit_uri *info, unsigned int flags) +{ + ck_rv_t rv; + int ret; + ck_session_handle_t pks = 0; + struct ck_function_list *module; + ck_slot_id_t slot; + struct ck_token_info tinfo; + + memset(sinfo, 0, sizeof(*sinfo)); + + ret = pkcs11_find_slot(&module, &slot, info, &tinfo, + &sinfo->slot_info, + &sinfo->trusted); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + rv = (module)->C_OpenSession(slot, ((flags & SESSION_WRITE) + ? CKF_RW_SESSION : 0) | + CKF_SERIAL_SESSION, NULL, NULL, &pks); + if (rv != CKR_OK) { + gnutls_assert(); + return pkcs11_rv_to_err(rv); + } + + /* ok found */ + sinfo->pks = pks; + sinfo->module = module; + sinfo->sid = slot; + sinfo->init = 1; + memcpy(&sinfo->tinfo, &tinfo, sizeof(sinfo->tinfo)); + + ret = + pkcs11_login(sinfo, pin_info, info, + flags); + if (ret < 0) { + gnutls_assert(); + pkcs11_close_session(sinfo); + return ret; + } + + return 0; +} + + +int +_pkcs11_traverse_tokens(find_func_t find_func, void *input, + struct p11_kit_uri *info, + struct pin_info_st *pin_info, unsigned int flags) +{ + ck_rv_t rv; + unsigned int found = 0, x, z; + int ret; + ck_session_handle_t pks = 0; + struct pkcs11_session_info sinfo; + struct ck_function_list *module = NULL; + unsigned long nslots; + ck_slot_id_t slots[MAX_SLOTS]; + + for (x = 0; x < active_providers; x++) { + if (providers[x].active == 0) + continue; + + if (flags & SESSION_TRUSTED && providers[x].trusted == 0) + continue; + + if (info && !p11_kit_uri_match_module_info(info, &providers[x].info)) { + continue; + } + + nslots = sizeof(slots) / sizeof(slots[0]); + ret = scan_slots(&providers[x], slots, &nslots); + if (ret < 0) { + gnutls_assert(); + continue; + } + + module = providers[x].module; + for (z = 0; z < nslots; z++) { + struct ck_token_info l_tinfo; + struct ck_slot_info l_sinfo; + + if (pkcs11_get_token_info(module, slots[z], + &l_tinfo) != CKR_OK) { + continue; + } + + if (info && !p11_kit_uri_match_token_info(info, &l_tinfo)) { + continue; + } + + if (pkcs11_get_slot_info(module, slots[z], + &l_sinfo) != CKR_OK) { + continue; + } + + rv = (module)->C_OpenSession(slots[z], + ((flags & SESSION_WRITE) ? CKF_RW_SESSION : 0) + | CKF_SERIAL_SESSION, + NULL, NULL, &pks); + if (rv != CKR_OK) { + continue; + } + + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.module = module; + sinfo.pks = pks; + sinfo.sid = slots[z]; + sinfo.trusted = providers[x].trusted; + + memcpy(&sinfo.tinfo, &l_tinfo, sizeof(sinfo.tinfo)); + memcpy(&sinfo.slot_info, &l_sinfo, sizeof(sinfo.slot_info)); + + ret = + pkcs11_login(&sinfo, pin_info, + info, flags); + if (ret < 0) { + gnutls_assert(); + pkcs11_close_session(&sinfo); + + /* treat the error as fatal only if + * the token requires login */ + if (l_tinfo.flags & CKF_LOGIN_REQUIRED) + return ret; + continue; + } + + ret = + find_func(providers[x].module, &sinfo, &l_tinfo, &providers[x].info, input); + + if (ret == 0) { + found = 1; + goto finish; + } else { + pkcs11_close_session(&sinfo); + pks = 0; + } + } + } + + finish: + /* final call */ + + if (found == 0) { + if (module) { + sinfo.module = module; + sinfo.pks = pks; + ret = find_func(providers[x].module, &sinfo, NULL, NULL, input); + } else + ret = + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } else { + ret = 0; + } + + if (pks != 0 && module != NULL) { + if (ret != 0 || !(flags & SESSION_NO_CLOSE)) + pkcs11_close_session(&sinfo); + } + + return ret; +} + +ck_object_class_t pkcs11_type_to_class(gnutls_pkcs11_obj_type_t type) +{ + switch (type) { + case GNUTLS_PKCS11_OBJ_X509_CRT: + return CKO_CERTIFICATE; + case GNUTLS_PKCS11_OBJ_X509_CRT_EXTENSION: + return CKO_X_CERTIFICATE_EXTENSION; + case GNUTLS_PKCS11_OBJ_PUBKEY: + return CKO_PUBLIC_KEY; + case GNUTLS_PKCS11_OBJ_PRIVKEY: + return CKO_PRIVATE_KEY; + case GNUTLS_PKCS11_OBJ_SECRET_KEY: + return CKO_SECRET_KEY; + case GNUTLS_PKCS11_OBJ_DATA: + return CKO_DATA; + default: + return -1; + } +} + +static gnutls_pkcs11_obj_type_t pkcs11_class_to_type(ck_object_class_t class) +{ + switch (class) { + case CKO_CERTIFICATE: + return GNUTLS_PKCS11_OBJ_X509_CRT; + case CKO_X_CERTIFICATE_EXTENSION: + return GNUTLS_PKCS11_OBJ_X509_CRT_EXTENSION; + case CKO_PUBLIC_KEY: + return GNUTLS_PKCS11_OBJ_PUBKEY; + case CKO_PRIVATE_KEY: + return GNUTLS_PKCS11_OBJ_PRIVKEY; + case CKO_SECRET_KEY: + return GNUTLS_PKCS11_OBJ_SECRET_KEY; + case CKO_DATA: + return GNUTLS_PKCS11_OBJ_DATA; + default: + _gnutls_debug_log("unknown pkcs11 object class %x\n", (unsigned)class); + return GNUTLS_PKCS11_OBJ_UNKNOWN; + } +} + +/* imports an object from a token to a pkcs11_obj_t type. + */ +static int +pkcs11_obj_import(ck_object_class_t class, gnutls_pkcs11_obj_t obj, + const gnutls_datum_t * data, + const gnutls_datum_t * id, + const gnutls_datum_t * label, + struct ck_token_info *tinfo, struct ck_info *lib_info) +{ + struct ck_attribute attr; + int ret; + + obj->type = pkcs11_class_to_type(class); + + attr.type = CKA_CLASS; + attr.value = &class; + attr.value_len = sizeof(class); + ret = p11_kit_uri_set_attribute(obj->info, &attr); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + if (data && data->data && data->size) { + ret = _gnutls_set_datum(&obj->raw, data->data, data->size); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + /* copy the token and library info into the uri */ + memcpy(p11_kit_uri_get_token_info(obj->info), tinfo, + sizeof(struct ck_token_info)); + memcpy(p11_kit_uri_get_module_info(obj->info), lib_info, + sizeof(struct ck_info)); + + if (label && label->data && label->size) { + attr.type = CKA_LABEL; + attr.value = label->data; + attr.value_len = label->size; + ret = p11_kit_uri_set_attribute(obj->info, &attr); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + } + + if (id && id->data && id->size) { + attr.type = CKA_ID; + attr.value = id->data; + attr.value_len = id->size; + ret = p11_kit_uri_set_attribute(obj->info, &attr); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + } + + return 0; +} + +int pkcs11_read_pubkey(struct ck_function_list *module, + ck_session_handle_t pks, ck_object_handle_t ctx, + ck_key_type_t key_type, gnutls_pkcs11_obj_t pobj) +{ + struct ck_attribute a[4]; + uint8_t *tmp1; + uint8_t *tmp2 = NULL; + size_t tmp1_size, tmp2_size; + int ret; + ck_rv_t rv; + + tmp1_size = tmp2_size = MAX_PK_PARAM_SIZE; + tmp1 = gnutls_calloc(1, tmp1_size); + if (tmp1 == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + tmp2 = gnutls_calloc(1, tmp2_size); + if (tmp2 == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + switch (key_type) { + case CKK_RSA: + a[0].type = CKA_MODULUS; + a[0].value = tmp1; + a[0].value_len = tmp1_size; + a[1].type = CKA_PUBLIC_EXPONENT; + a[1].value = tmp2; + a[1].value_len = tmp2_size; + + if (pkcs11_get_attribute_value(module, pks, ctx, a, 2) == + CKR_OK) { + + pobj->pubkey[0].data = a[0].value; + pobj->pubkey[0].size = a[0].value_len; + + pobj->pubkey[1].data = a[1].value; + pobj->pubkey[1].size = a[1].value_len; + + pobj->pubkey_size = 2; + } else { + gnutls_assert(); + ret = GNUTLS_E_PKCS11_ERROR; + goto cleanup; + } + break; + case CKK_DSA: + a[0].type = CKA_PRIME; + a[0].value = tmp1; + a[0].value_len = tmp1_size; + a[1].type = CKA_SUBPRIME; + a[1].value = tmp2; + a[1].value_len = tmp2_size; + + if ((rv = pkcs11_get_attribute_value(module, pks, ctx, a, 2)) == + CKR_OK) { + ret = + _gnutls_set_datum(&pobj->pubkey[0], a[0].value, + a[0].value_len); + + if (ret >= 0) + ret = + _gnutls_set_datum(&pobj->pubkey + [1], a[1].value, + a[1].value_len); + + if (ret < 0) { + gnutls_assert(); + _gnutls_free_datum(&pobj->pubkey[1]); + _gnutls_free_datum(&pobj->pubkey[0]); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + pobj->pubkey_size = 2; + } else { + gnutls_assert(); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + a[0].type = CKA_BASE; + a[0].value = tmp1; + a[0].value_len = tmp1_size; + a[1].type = CKA_VALUE; + a[1].value = tmp2; + a[1].value_len = tmp2_size; + + if ((rv = pkcs11_get_attribute_value(module, pks, ctx, a, 2)) == + CKR_OK) { + pobj->pubkey[2].data = a[0].value; + pobj->pubkey[2].size = a[0].value_len; + + pobj->pubkey[3].data = a[1].value; + pobj->pubkey[3].size = a[1].value_len; + + pobj->pubkey_size = 4; + } else { + gnutls_assert(); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + break; + case CKK_ECDSA: + a[0].type = CKA_EC_PARAMS; + a[0].value = tmp1; + a[0].value_len = tmp1_size; + + a[1].type = CKA_EC_POINT; + a[1].value = tmp2; + a[1].value_len = tmp2_size; + + if ((rv = pkcs11_get_attribute_value(module, pks, ctx, a, 2)) == + CKR_OK) { + + pobj->pubkey[0].data = a[0].value; + pobj->pubkey[0].size = a[0].value_len; + + pobj->pubkey[1].data = a[1].value; + pobj->pubkey[1].size = a[1].value_len; + + pobj->pubkey_size = 2; + } else { + gnutls_assert(); + + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + break; +#ifdef HAVE_CKM_EDDSA + case CKK_EC_EDWARDS: + a[0].type = CKA_EC_PARAMS; + a[0].value = tmp1; + a[0].value_len = tmp1_size; + + a[1].type = CKA_EC_POINT; + a[1].value = tmp2; + a[1].value_len = tmp2_size; + + if ((rv = pkcs11_get_attribute_value(module, pks, ctx, a, 2)) == + CKR_OK) { + + pobj->pubkey[0].data = a[0].value; + pobj->pubkey[0].size = a[0].value_len; + + pobj->pubkey[1].data = a[1].value; + pobj->pubkey[1].size = a[1].value_len; + + pobj->pubkey_size = 2; + } else { + gnutls_assert(); + + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + break; +#endif + default: + _gnutls_debug_log("requested reading public key of unsupported type %u\n", (unsigned)key_type); + ret = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + goto cleanup; + } + + return 0; + +cleanup: + gnutls_free(tmp1); + gnutls_free(tmp2); + + return ret; +} + +static int +pkcs11_obj_import_pubkey(struct ck_function_list *module, + ck_session_handle_t pks, + ck_object_handle_t ctx, + gnutls_pkcs11_obj_t pobj, + gnutls_datum_t *data, + const gnutls_datum_t *id, + const gnutls_datum_t *label, + struct ck_token_info *tinfo, + struct ck_info *lib_info) +{ + struct ck_attribute a[4]; + ck_key_type_t key_type; + int ret; + ck_bool_t tval; + + a[0].type = CKA_KEY_TYPE; + a[0].value = &key_type; + a[0].value_len = sizeof(key_type); + + if (pkcs11_get_attribute_value(module, pks, ctx, a, 1) == CKR_OK) { + pobj->pk_algorithm = key_type_to_pk(key_type); + + ret = + pkcs11_read_pubkey(module, pks, ctx, key_type, + pobj); + if (ret < 0) + return gnutls_assert_val(ret); + } + + /* read key usage flags */ + a[0].type = CKA_ENCRYPT; + a[0].value = &tval; + a[0].value_len = sizeof(tval); + + if (pkcs11_get_attribute_value(module, pks, ctx, a, 1) == CKR_OK) { + if (tval != 0) { + pobj->key_usage |= GNUTLS_KEY_DATA_ENCIPHERMENT; + } + } + + a[0].type = CKA_VERIFY; + a[0].value = &tval; + a[0].value_len = sizeof(tval); + + if (pkcs11_get_attribute_value(module, pks, ctx, a, 1) == CKR_OK) { + if (tval != 0) { + pobj->key_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE | + GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN + | GNUTLS_KEY_NON_REPUDIATION; + } + } + + a[0].type = CKA_VERIFY_RECOVER; + a[0].value = &tval; + a[0].value_len = sizeof(tval); + + if (pkcs11_get_attribute_value(module, pks, ctx, a, 1) == CKR_OK) { + if (tval != 0) { + pobj->key_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE | + GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN + | GNUTLS_KEY_NON_REPUDIATION; + } + } + + a[0].type = CKA_DERIVE; + a[0].value = &tval; + a[0].value_len = sizeof(tval); + + if (pkcs11_get_attribute_value(module, pks, ctx, a, 1) == CKR_OK) { + if (tval != 0) { + pobj->key_usage |= GNUTLS_KEY_KEY_AGREEMENT; + } + } + + a[0].type = CKA_WRAP; + a[0].value = &tval; + a[0].value_len = sizeof(tval); + + if (pkcs11_get_attribute_value(module, pks, ctx, a, 1) == CKR_OK) { + if (tval != 0) { + pobj->key_usage |= GNUTLS_KEY_KEY_ENCIPHERMENT; + } + } + + ret = pkcs11_obj_import(CKO_PUBLIC_KEY, pobj, data, id, label, + tinfo, lib_info); + return ret; +} + +static int +pkcs11_import_object(ck_object_handle_t ctx, ck_object_class_t class, + struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, struct ck_info *lib_info, + gnutls_pkcs11_obj_t pobj) +{ + ck_bool_t b; + int rv, ret; + struct ck_attribute a[4]; + unsigned long category = 0; + char label_tmp[PKCS11_LABEL_SIZE]; + char id_tmp[PKCS11_ID_SIZE]; + gnutls_datum_t id, label, data = {NULL, 0}; + + /* now figure out flags */ + pobj->flags = 0; + a[0].type = CKA_WRAP; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_KEY_WRAP; + + a[0].type = CKA_UNWRAP; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_KEY_WRAP; + + a[0].type = CKA_PRIVATE; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE; + + a[0].type = CKA_TRUSTED; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED; + + if (sinfo->trusted) { /* only p11-kit "trusted" modules support this flag */ + a[0].type = CKA_X_DISTRUSTED; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_DISTRUSTED; + } + + a[0].type = CKA_SENSITIVE; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK) { + if (b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE; + else + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_SENSITIVE; + } + + a[0].type = CKA_EXTRACTABLE; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_EXTRACTABLE; + + a[0].type = CKA_NEVER_EXTRACTABLE; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_NEVER_EXTRACTABLE; + + a[0].type = CKA_CERTIFICATE_CATEGORY; + a[0].value = &category; + a[0].value_len = sizeof(category); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && category == 2) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_CA; + + a[0].type = CKA_ALWAYS_AUTHENTICATE; + a[0].value = &b; + a[0].value_len = sizeof(b); + + rv = pkcs11_get_attribute_value(sinfo->module, sinfo->pks, ctx, a, 1); + if (rv == CKR_OK && b != 0) + pobj->flags |= GNUTLS_PKCS11_OBJ_FLAG_MARK_ALWAYS_AUTH; + + /* now recover the object label/id */ + a[0].type = CKA_LABEL; + a[0].value = label_tmp; + a[0].value_len = sizeof(label_tmp); + rv = pkcs11_get_attribute_value + (sinfo->module, sinfo->pks, ctx, a, 1); + if (rv != CKR_OK) { + gnutls_assert(); + label.data = NULL; + label.size = 0; + } else { + label.data = a[0].value; + label.size = a[0].value_len; + } + + a[0].type = CKA_ID; + a[0].value = id_tmp; + a[0].value_len = sizeof(id_tmp); + rv = pkcs11_get_attribute_value + (sinfo->module, sinfo->pks, ctx, a, 1); + if (rv != CKR_OK) { + gnutls_assert(); + id.data = NULL; + id.size = 0; + } else { + id.data = a[0].value; + id.size = a[0].value_len; + } + + if (label.data == NULL && id.data == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + rv = pkcs11_get_attribute_avalue + (sinfo->module, sinfo->pks, ctx, CKA_VALUE, &data); + if (rv != CKR_OK) { + gnutls_assert(); + /* data will be null */ + } + + if (class == CKO_PUBLIC_KEY) { + ret = + pkcs11_obj_import_pubkey(sinfo->module, + sinfo->pks, + ctx, + pobj, + &data, + &id, &label, + tinfo, + lib_info); + } else { + ret = + pkcs11_obj_import(class, + pobj, + &data, &id, &label, + tinfo, + lib_info); + } + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(data.data); + return ret; +} + +static int +find_single_obj_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, struct ck_info *lib_info, + void *input) +{ + struct find_single_obj_st *find_data = input; + struct ck_attribute a[4]; + ck_certificate_type_t type; + ck_object_class_t class; + ck_rv_t rv; + ck_object_handle_t ctx = CK_INVALID_HANDLE; + unsigned long count; + unsigned a_vals; + int found = 0, ret; + + if (tinfo == NULL) { /* we don't support multiple calls */ + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* do not bother reading the token if basic fields do not match + */ + if (!p11_kit_uri_match_token_info + (find_data->obj->info, tinfo) + || !p11_kit_uri_match_module_info(find_data->obj->info, + lib_info)) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = add_obj_attrs(find_data->obj->info, a, &a_vals, &class, &type); + if (ret < 0) + return gnutls_assert_val(ret); + + rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, + a_vals); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: FindObjectsInit failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + if (pkcs11_find_objects(sinfo->module, sinfo->pks, &ctx, 1, &count) == CKR_OK && + count == 1) { + ret = pkcs11_import_object(ctx, class, sinfo, tinfo, lib_info, find_data->obj); + if (ret >= 0) { + found = 1; + } + } else { + _gnutls_debug_log + ("p11: Skipped object, missing attrs.\n"); + } + + if (found == 0) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } else { + ret = 0; + } + + cleanup: + pkcs11_find_objects_final(sinfo); + + if (ret == 0 && find_data->overwrite_exts && find_data->obj->raw.size > 0 && ctx != CK_INVALID_HANDLE) { + gnutls_datum_t spki; + rv = pkcs11_get_attribute_avalue(sinfo->module, sinfo->pks, ctx, CKA_PUBLIC_KEY_INFO, &spki); + if (rv == CKR_OK) { + ret = pkcs11_override_cert_exts(sinfo, &spki, &find_data->obj->raw); + gnutls_free(spki.data); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + } + + return ret; +} + +unsigned int pkcs11_obj_flags_to_int(unsigned int flags) +{ + unsigned int ret_flags = 0; + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_LOGIN) + ret_flags |= SESSION_LOGIN | SESSION_FORCE_LOGIN; + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_LOGIN_SO) + ret_flags |= SESSION_LOGIN | SESSION_SO | SESSION_FORCE_LOGIN | SESSION_WRITE; + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE) + ret_flags |= SESSION_TRUSTED; + + return ret_flags; +} + +/** + * gnutls_pkcs11_obj_import_url: + * @obj: The structure to store the object + * @url: a PKCS 11 url identifying the key + * @flags: Or sequence of GNUTLS_PKCS11_OBJ_* flags + * + * This function will "import" a PKCS 11 URL identifying an object (e.g. certificate) + * to the #gnutls_pkcs11_obj_t type. This does not involve any + * parsing (such as X.509 or OpenPGP) since the #gnutls_pkcs11_obj_t is + * format agnostic. Only data are transferred. + * + * If the flag %GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT is specified + * any certificate read, will have its extensions overwritten by any + * stapled extensions in the trust module. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + **/ +int +gnutls_pkcs11_obj_import_url(gnutls_pkcs11_obj_t obj, const char *url, + unsigned int flags) +{ + int ret; + struct find_single_obj_st find_data; + + PKCS11_CHECK_INIT; + memset(&find_data, 0, sizeof(find_data)); + + /* fill in the find data structure */ + find_data.obj = obj; + + ret = pkcs11_url_to_info(url, &obj->info, flags); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT) { + find_data.overwrite_exts = 1; + } + + ret = + _pkcs11_traverse_tokens(find_single_obj_cb, &find_data, obj->info, + &obj->pin, + pkcs11_obj_flags_to_int(flags)); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +static int +find_token_num_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, + struct ck_info *lib_info, void *input) +{ + struct find_token_num *find_data = input; + + if (tinfo == NULL) { /* we don't support multiple calls */ + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (find_data->current == find_data->seq) { + memcpy(p11_kit_uri_get_token_info(find_data->info), + tinfo, sizeof(struct ck_token_info)); + memcpy(p11_kit_uri_get_module_info(find_data->info), + lib_info, sizeof(struct ck_info)); + return 0; + } + + find_data->current++; + /* search the token for the id */ + + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; /* non zero is enough */ +} + +static int +find_token_modname_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, + struct ck_info *lib_info, void *input) +{ + struct find_token_modname *find_data = input; + + if (tinfo == NULL) { /* we don't support multiple calls */ + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (!p11_kit_uri_match_token_info(find_data->info, tinfo) + || !p11_kit_uri_match_module_info(find_data->info, + lib_info)) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + find_data->modname = p11_kit_config_option(module, "module"); + find_data->ptr = module; + find_data->slot_id = sinfo->sid; + return 0; +} + +/* Internal symbol used by tests */ +int +_gnutls_pkcs11_token_get_url(unsigned int seq, + gnutls_pkcs11_url_type_t detailed, char **url, + unsigned flags); + +/** + * _gnutls_pkcs11_token_get_url: + * @seq: sequence number starting from 0 + * @detailed: non zero if a detailed URL is required + * @url: will contain an allocated url + * @flags: zero or 1. When 1 no initialization is performed. + * + * This function will return the URL for each token available + * in system. The url has to be released using gnutls_free() + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if the sequence number + * exceeds the available tokens, otherwise a negative error value. + * + **/ +int +_gnutls_pkcs11_token_get_url(unsigned int seq, + gnutls_pkcs11_url_type_t detailed, char **url, + unsigned flags) +{ + int ret; + struct find_token_num tn; + + if (!(flags & 1)) { + PKCS11_CHECK_INIT; + } + + memset(&tn, 0, sizeof(tn)); + tn.seq = seq; + tn.info = p11_kit_uri_new(); + + ret = _pkcs11_traverse_tokens(find_token_num_cb, &tn, NULL, NULL, 0); + if (ret < 0) { + p11_kit_uri_free(tn.info); + gnutls_assert(); + return ret; + } + + ret = pkcs11_info_to_url(tn.info, detailed, url); + p11_kit_uri_free(tn.info); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs11_token_get_url: + * @seq: sequence number starting from 0 + * @detailed: non zero if a detailed URL is required + * @url: will contain an allocated url + * + * This function will return the URL for each token available + * in system. The url has to be released using gnutls_free() + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if the sequence number + * exceeds the available tokens, otherwise a negative error value. + * + * Since: 2.12.0 + **/ +int +gnutls_pkcs11_token_get_url(unsigned int seq, + gnutls_pkcs11_url_type_t detailed, char **url) +{ + return _gnutls_pkcs11_token_get_url(seq, detailed, url, 0); +} + +/** + * gnutls_pkcs11_token_get_info: + * @url: should contain a PKCS 11 URL + * @ttype: Denotes the type of information requested + * @output: where output will be stored + * @output_size: contains the maximum size of the output buffer and will be + * overwritten with the actual size. + * + * This function will return information about the PKCS 11 token such + * as the label, id, etc. + * + * When output is text, a null terminated string is written to @output and its + * string length is written to @output_size (without null terminator). If the + * buffer is too small, @output_size will contain the expected buffer size + * (with null terminator for text) and return %GNUTLS_E_SHORT_MEMORY_BUFFER. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code + * on error. + * + * Since: 2.12.0 + **/ +int +gnutls_pkcs11_token_get_info(const char *url, + gnutls_pkcs11_token_info_t ttype, + void *output, size_t * output_size) +{ + struct p11_kit_uri *info = NULL; + const uint8_t *str; + char *temp_str = NULL; + size_t len; + int ret; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(url, &info, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + switch (ttype) { + case GNUTLS_PKCS11_TOKEN_LABEL: + str = p11_kit_uri_get_token_info(info)->label; + len = p11_kit_space_strlen(str, 32); + break; + case GNUTLS_PKCS11_TOKEN_SERIAL: + str = p11_kit_uri_get_token_info(info)->serial_number; + len = p11_kit_space_strlen(str, 16); + break; + case GNUTLS_PKCS11_TOKEN_MANUFACTURER: + str = p11_kit_uri_get_token_info(info)->manufacturer_id; + len = p11_kit_space_strlen(str, 32); + break; + case GNUTLS_PKCS11_TOKEN_MODEL: + str = p11_kit_uri_get_token_info(info)->model; + len = p11_kit_space_strlen(str, 16); + break; + case GNUTLS_PKCS11_TOKEN_MODNAME: { + struct find_token_modname tn; + + memset(&tn, 0, sizeof(tn)); + tn.info = info; + + ret = _pkcs11_traverse_tokens(find_token_modname_cb, &tn, NULL, NULL, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + temp_str = tn.modname; + if (temp_str) { + str = (uint8_t *)temp_str; + len = strlen(temp_str); + } else { + gnutls_assert(); + len = 0; + } + break; + } + default: + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if (len < *output_size) { + if (len) + memcpy(output, str, len); + ((char *) output)[len] = '\0'; + *output_size = len; + ret = 0; + } else { + *output_size = len + 1; + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + cleanup: + free(temp_str); + p11_kit_uri_free(info); + return ret; +} + +/** + * gnutls_pkcs11_token_get_ptr: + * @url: should contain a PKCS#11 URL identifying a token + * @ptr: will contain the CK_FUNCTION_LIST_PTR pointer + * @slot_id: will contain the slot_id (may be %NULL) + * @flags: should be zero + * + * This function will return the function pointer of the specified + * token by the URL. The returned pointers are valid until + * gnutls is deinitialized, c.f. _global_deinit(). + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code + * on error. + * + * Since: 3.6.3 + **/ +int +gnutls_pkcs11_token_get_ptr(const char *url, void **ptr, unsigned long *slot_id, + unsigned int flags) +{ + struct p11_kit_uri *info = NULL; + int ret; + struct find_token_modname tn; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(url, &info, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + memset(&tn, 0, sizeof(tn)); + tn.info = info; + + ret = _pkcs11_traverse_tokens(find_token_modname_cb, &tn, NULL, NULL, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (ptr) + *ptr = tn.ptr; + if (slot_id) + *slot_id = tn.slot_id; + + ret = 0; + + cleanup: + free(tn.modname); + p11_kit_uri_free(info); + return ret; +} + +/** + * gnutls_pkcs11_obj_export_url: + * @obj: Holds the PKCS 11 certificate + * @detailed: non zero if a detailed URL is required + * @url: will contain an allocated url + * + * This function will export a URL identifying the given object. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + **/ +int +gnutls_pkcs11_obj_export_url(gnutls_pkcs11_obj_t obj, + gnutls_pkcs11_url_type_t detailed, char **url) +{ + int ret; + + ret = pkcs11_info_to_url(obj->info, detailed, url); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs11_obj_get_type: + * @obj: Holds the PKCS 11 object + * + * This function will return the type of the object being + * stored in the structure. + * + * Returns: The type of the object + * + * Since: 2.12.0 + **/ +gnutls_pkcs11_obj_type_t +gnutls_pkcs11_obj_get_type(gnutls_pkcs11_obj_t obj) +{ + return obj->type; +} + +static int +retrieve_pin_from_source(const char *pinfile, + struct ck_token_info *token_info, int attempts, + ck_user_type_t user_type, + struct p11_kit_pin **pin) +{ + unsigned int flags = 0; + struct p11_kit_uri *token_uri; + struct p11_kit_pin *result; + char *label; + + label = + p11_kit_space_strdup(token_info->label, + sizeof(token_info->label)); + if (label == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + token_uri = p11_kit_uri_new(); + if (token_uri == NULL) { + free(label); + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy(p11_kit_uri_get_token_info(token_uri), token_info, + sizeof(struct ck_token_info)); + + if (attempts) + flags |= P11_KIT_PIN_FLAGS_RETRY; + if (user_type == CKU_USER) { + flags |= P11_KIT_PIN_FLAGS_USER_LOGIN; + if (token_info->flags & CKF_USER_PIN_COUNT_LOW) + flags |= P11_KIT_PIN_FLAGS_MANY_TRIES; + if (token_info->flags & CKF_USER_PIN_FINAL_TRY) + flags |= P11_KIT_PIN_FLAGS_FINAL_TRY; + } else if (user_type == CKU_SO) { + flags |= P11_KIT_PIN_FLAGS_SO_LOGIN; + if (token_info->flags & CKF_SO_PIN_COUNT_LOW) + flags |= P11_KIT_PIN_FLAGS_MANY_TRIES; + if (token_info->flags & CKF_SO_PIN_FINAL_TRY) + flags |= P11_KIT_PIN_FLAGS_FINAL_TRY; + } else if (user_type == CKU_CONTEXT_SPECIFIC) { + flags |= P11_KIT_PIN_FLAGS_CONTEXT_LOGIN; + } + + result = p11_kit_pin_request(pinfile, token_uri, label, flags); + p11_kit_uri_free(token_uri); + free(label); + + if (result == NULL) { + gnutls_assert(); + return GNUTLS_E_PKCS11_PIN_ERROR; + } + + *pin = result; + return 0; +} + +static int +retrieve_pin_from_callback(const struct pin_info_st *pin_info, + struct ck_token_info *token_info, + int attempts, ck_user_type_t user_type, + struct p11_kit_pin **pin) +{ + char pin_value[GNUTLS_PKCS11_MAX_PIN_LEN]; + unsigned int flags = 0; + char *token_str; + char *label; + struct p11_kit_uri *token_uri; + int ret = 0; + + label = + p11_kit_space_strdup(token_info->label, + sizeof(token_info->label)); + if (label == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + token_uri = p11_kit_uri_new(); + if (token_uri == NULL) { + free(label); + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy(p11_kit_uri_get_token_info(token_uri), token_info, + sizeof(struct ck_token_info)); + ret = pkcs11_info_to_url(token_uri, 1, &token_str); + p11_kit_uri_free(token_uri); + + if (ret < 0) { + free(label); + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + if (user_type == CKU_USER || user_type == CKU_CONTEXT_SPECIFIC) { + flags |= GNUTLS_PIN_USER; + + if (user_type == CKU_CONTEXT_SPECIFIC) + flags |= GNUTLS_PIN_CONTEXT_SPECIFIC; + if (token_info->flags & CKF_USER_PIN_COUNT_LOW) + flags |= GNUTLS_PIN_COUNT_LOW; + if (token_info->flags & CKF_USER_PIN_FINAL_TRY) + flags |= GNUTLS_PIN_FINAL_TRY; + } else if (user_type == CKU_SO) { + flags |= GNUTLS_PIN_SO; + if (token_info->flags & CKF_SO_PIN_COUNT_LOW) + flags |= GNUTLS_PIN_COUNT_LOW; + if (token_info->flags & CKF_SO_PIN_FINAL_TRY) + flags |= GNUTLS_PIN_FINAL_TRY; + } + + if (attempts > 0) + flags |= GNUTLS_PIN_WRONG; + + if (pin_info && pin_info->cb) + ret = + pin_info->cb(pin_info->data, attempts, + (char *) token_str, label, flags, + pin_value, GNUTLS_PKCS11_MAX_PIN_LEN); + else if (_gnutls_pin_func) + ret = + _gnutls_pin_func(_gnutls_pin_data, attempts, + (char *) token_str, label, flags, + pin_value, GNUTLS_PKCS11_MAX_PIN_LEN); + else + ret = gnutls_assert_val(GNUTLS_E_PKCS11_PIN_ERROR); + + free(token_str); + free(label); + + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_PKCS11_PIN_ERROR); + + *pin = p11_kit_pin_new_for_string(pin_value); + + if (*pin == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +int +pkcs11_retrieve_pin(struct pin_info_st *pin_info, struct p11_kit_uri *info, + struct ck_token_info *token_info, int attempts, + ck_user_type_t user_type, struct p11_kit_pin **pin) +{ + const char *pinfile; + int ret = GNUTLS_E_PKCS11_PIN_ERROR; + + *pin = NULL; + + /* First check for pin-value field */ + pinfile = p11_kit_uri_get_pin_value(info); + if (pinfile != NULL) { + if (attempts > 0) { + _gnutls_debug_log("p11: refusing more than a single attempts with pin-value\n"); + return gnutls_assert_val(GNUTLS_E_PKCS11_PIN_ERROR); + } + + _gnutls_debug_log("p11: Using pin-value to retrieve PIN\n"); + *pin = p11_kit_pin_new_for_string(pinfile); + if (*pin != NULL) + ret = 0; + } else { /* try pin-source */ + /* Check if a pinfile is specified, and use that if possible */ + pinfile = p11_kit_uri_get_pin_source(info); + if (pinfile != NULL) { + if (attempts > 0) { + _gnutls_debug_log("p11: refusing more than a single attempts with pin-source\n"); + return gnutls_assert_val(GNUTLS_E_PKCS11_PIN_ERROR); + } + + _gnutls_debug_log("p11: Using pin-source to retrieve PIN\n"); + ret = + retrieve_pin_from_source(pinfile, token_info, attempts, + user_type, pin); + } + } + + /* The global gnutls pin callback */ + if (ret < 0) + ret = + retrieve_pin_from_callback(pin_info, token_info, + attempts, user_type, pin); + + /* Otherwise, PIN entry is necessary for login, so fail if there's + * no callback. */ + + if (ret < 0) { + gnutls_assert(); + _gnutls_debug_log + ("p11: No suitable pin callback but login required.\n"); + } + + return ret; +} + +int +pkcs11_login(struct pkcs11_session_info *sinfo, + struct pin_info_st *pin_info, + struct p11_kit_uri *info, + unsigned flags) +{ + struct ck_session_info session_info; + int attempt = 0, ret; + ck_user_type_t user_type; + ck_rv_t rv; + + if (!(flags & SESSION_LOGIN)) { + _gnutls_debug_log("p11: No login requested.\n"); + return 0; + } + + if (flags & SESSION_SO) { + user_type = CKU_SO; + } else if (flags & SESSION_CONTEXT_SPECIFIC) { + user_type = CKU_CONTEXT_SPECIFIC; + } else { + user_type = CKU_USER; + } + + if (!(flags & (SESSION_FORCE_LOGIN|SESSION_SO)) && + !(sinfo->tinfo.flags & CKF_LOGIN_REQUIRED)) { + gnutls_assert(); + _gnutls_debug_log("p11: No login required in token.\n"); + return 0; + } + + /* For a token with a "protected" (out-of-band) authentication + * path, calling login with a NULL username is all that is + * required. */ + if (sinfo->tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { + rv = (sinfo->module)->C_Login(sinfo->pks, + user_type, + NULL, 0); + if (rv == CKR_OK || rv == CKR_USER_ALREADY_LOGGED_IN) { + return 0; + } else { + gnutls_assert(); + _gnutls_debug_log + ("p11: Protected login failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + } + + do { + struct p11_kit_pin *pin; + struct ck_token_info tinfo; + + memcpy(&tinfo, &sinfo->tinfo, sizeof(tinfo)); + + if (!(flags & SESSION_CONTEXT_SPECIFIC)) { + /* Check whether the session is already logged in, and if so, just skip */ + rv = (sinfo->module)->C_GetSessionInfo(sinfo->pks, + &session_info); + if (rv == CKR_OK) { + if (flags & SESSION_SO) { + if (session_info.state == CKS_RW_SO_FUNCTIONS) { + ret = 0; + _gnutls_debug_log + ("p11: Already logged in as SO\n"); + goto cleanup; + } + } else if (session_info.state == CKS_RO_USER_FUNCTIONS + || session_info.state == CKS_RW_USER_FUNCTIONS) { + ret = 0; + _gnutls_debug_log + ("p11: Already logged in as user\n"); + goto cleanup; + } + } + } + + /* If login has been attempted once already, check the token + * status again, the flags might change. */ + if (attempt) { + rv = pkcs11_get_token_info(sinfo->module, sinfo->sid, + &tinfo); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log + ("p11: GetTokenInfo failed\n"); + + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + } + + ret = + pkcs11_retrieve_pin(pin_info, info, &tinfo, attempt++, + user_type, &pin); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + rv = (sinfo->module)->C_Login(sinfo->pks, user_type, + (unsigned char *) + p11_kit_pin_get_value(pin, + NULL), + p11_kit_pin_get_length(pin)); + + p11_kit_pin_unref(pin); + } + while (rv == CKR_PIN_INCORRECT); + + _gnutls_debug_log("p11: Login result = %s (%lu)\n", (rv==0)?"ok":p11_kit_strerror(rv), rv); + + ret = (rv == CKR_OK || rv == + CKR_USER_ALREADY_LOGGED_IN) ? 0 : pkcs11_rv_to_err(rv); + + cleanup: + return ret; +} + +int pkcs11_call_token_func(struct p11_kit_uri *info, const unsigned retry) +{ + struct ck_token_info *tinfo; + char *label; + int ret = 0; + + tinfo = p11_kit_uri_get_token_info(info); + label = p11_kit_space_strdup(tinfo->label, sizeof(tinfo->label)); + ret = (_gnutls_token_func) (_gnutls_token_data, label, retry); + free(label); + + return ret; +} + + +static int +find_privkeys(struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, struct find_pkey_list_st *list) +{ + struct ck_attribute a[3]; + ck_object_class_t class; + ck_rv_t rv; + ck_object_handle_t ctx; + unsigned long count, current; + char certid_tmp[PKCS11_ID_SIZE]; + int ret; + + class = CKO_PRIVATE_KEY; + + /* Find an object with private key class and a certificate ID + * which matches the certificate. */ + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof class; + + rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, 1); + if (rv != CKR_OK) { + gnutls_assert(); + return pkcs11_rv_to_err(rv); + } + + list->key_ids_size = 0; + while (pkcs11_find_objects + (sinfo->module, sinfo->pks, &ctx, 1, &count) == CKR_OK + && count == 1) { + list->key_ids_size++; + } + + pkcs11_find_objects_final(sinfo); + + if (list->key_ids_size == 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + list->key_ids = _gnutls_reallocarray(NULL, list->key_ids_size, + sizeof(gnutls_buffer_st)); + if (list->key_ids == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + /* actual search */ + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof class; + + rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, 1); + if (rv != CKR_OK) { + gnutls_assert(); + return pkcs11_rv_to_err(rv); + } + + current = 0; + while (pkcs11_find_objects + (sinfo->module, sinfo->pks, &ctx, 1, &count) == CKR_OK + && count == 1 + && current < list->key_ids_size) { + + a[0].type = CKA_ID; + a[0].value = certid_tmp; + a[0].value_len = sizeof(certid_tmp); + + _gnutls_buffer_init(&list->key_ids[current]); + + if (pkcs11_get_attribute_value + (sinfo->module, sinfo->pks, ctx, a, 1) == CKR_OK) { + ret = _gnutls_buffer_append_data(&list->key_ids[current], + a[0].value, + a[0].value_len); + if (ret < 0) + return gnutls_assert_val(ret); + current++; + } + } + + pkcs11_find_objects_final(sinfo); + + list->key_ids_size = current; + + return 0; +} + +/* Recover certificate list from tokens */ + +#define OBJECTS_A_TIME 8*1024 + +static int +find_multi_objs_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, struct ck_info *lib_info, void *input) +{ + struct find_multi_obj_st *find_data = input; + struct ck_attribute a[16]; + struct ck_attribute *attr; + ck_object_class_t class = (ck_object_class_t) -1; + ck_certificate_type_t type = (ck_certificate_type_t) -1; + ck_bool_t trusted; + unsigned long category; + ck_rv_t rv; + ck_object_handle_t *ctx = NULL; + unsigned long count; + char certid_tmp[PKCS11_ID_SIZE]; + int ret; + struct find_pkey_list_st plist; /* private key holder */ + unsigned int i, tot_values = 0, class_set = 0; + unsigned start_elem; + + if (tinfo == NULL) { + gnutls_assert(); + return 0; + } + + /* do not bother reading the token if basic fields do not match + */ + if (!p11_kit_uri_match_token_info(find_data->info, tinfo) || + !p11_kit_uri_match_module_info(find_data->info, lib_info)) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + memset(&plist, 0, sizeof(plist)); + + if (find_data->flags & GNUTLS_PKCS11_OBJ_FLAG_WITH_PRIVKEY) { + ret = find_privkeys(sinfo, tinfo, &plist); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (plist.key_ids_size == 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + } + + /* Find objects with given class and type */ + attr = p11_kit_uri_get_attribute(find_data->info, CKA_CLASS); + if (attr) { + if (attr->value + && attr->value_len == sizeof(ck_object_class_t)) + class = *((ck_object_class_t *) attr->value); + if (class == CKO_CERTIFICATE) + type = CKC_X_509; + } + + if (find_data->flags & GNUTLS_PKCS11_OBJ_FLAG_CRT) { + class = CKO_CERTIFICATE; + + a[tot_values].type = CKA_CLASS; + a[tot_values].value = &class; + a[tot_values].value_len = sizeof class; + tot_values++; + class_set = 1; + + type = CKC_X_509; + a[tot_values].type = CKA_CERTIFICATE_TYPE; + a[tot_values].value = &type; + a[tot_values].value_len = sizeof type; + tot_values++; + _gnutls_assert_log("p11 attrs: CKA_CLASS (CERT), CKA_CERTIFICATE_TYPE\n"); + } + + if (find_data->flags & GNUTLS_PKCS11_OBJ_FLAG_PUBKEY) { + class = CKO_PUBLIC_KEY; + + a[tot_values].type = CKA_CLASS; + a[tot_values].value = &class; + a[tot_values].value_len = sizeof class; + tot_values++; + class_set = 1; + _gnutls_assert_log("p11 attrs: CKA_CLASS (PUBLIC KEY)\n"); + } + + if (find_data->flags & GNUTLS_PKCS11_OBJ_FLAG_PRIVKEY) { + class = CKO_PRIVATE_KEY; + + a[tot_values].type = CKA_CLASS; + a[tot_values].value = &class; + a[tot_values].value_len = sizeof class; + tot_values++; + class_set = 1; + _gnutls_assert_log("p11 attrs: CKA_CLASS (PRIVATE KEY)\n"); + } + + if (find_data->flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED) { + trusted = 1; + a[tot_values].type = CKA_TRUSTED; + a[tot_values].value = &trusted; + a[tot_values].value_len = sizeof trusted; + tot_values++; + _gnutls_assert_log("p11 attrs: CKA_TRUSTED\n"); + } + + if (find_data->flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_DISTRUSTED) { + if (!sinfo->trusted) { /* only p11-kit trust modules support this */ + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + trusted = 1; + a[tot_values].type = CKA_X_DISTRUSTED; + a[tot_values].value = &trusted; + a[tot_values].value_len = sizeof trusted; + tot_values++; + _gnutls_assert_log("p11 attrs: CKA_X_DISTRUSTED\n"); + } + + if (find_data->flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_CA) { + category = 2; + a[tot_values].type = CKA_CERTIFICATE_CATEGORY; + a[tot_values].value = &category; + a[tot_values].value_len = sizeof category; + tot_values++; + _gnutls_assert_log("p11 attrs: CKA_CERTIFICATE_CATEGORY=CA\n"); + } + + if (class_set == 0 && class != (ck_object_class_t)-1) { + a[tot_values].type = CKA_CLASS; + a[tot_values].value = &class; + a[tot_values].value_len = sizeof class; + tot_values++; + class_set = 1; + _gnutls_assert_log("p11 attrs: CKA_CLASS\n"); + } + + attr = p11_kit_uri_get_attribute(find_data->info, CKA_ID); + if (attr) { + a[tot_values].type = CKA_ID; + a[tot_values].value = attr->value; + a[tot_values].value_len = attr->value_len; + tot_values++; + _gnutls_assert_log("p11 attrs: CKA_ID\n"); + } + + attr = p11_kit_uri_get_attribute(find_data->info, CKA_LABEL); + if (attr) { + a[tot_values].type = CKA_LABEL; + a[tot_values].value = attr->value; + a[tot_values].value_len = attr->value_len; + tot_values++; + _gnutls_assert_log("p11 attrs: CKA_LABEL\n"); + } + + rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, + tot_values); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log("p11: FindObjectsInit failed.\n"); + return pkcs11_rv_to_err(rv); + } + + ctx = _gnutls_reallocarray(NULL, OBJECTS_A_TIME, sizeof(ctx[0])); + if (ctx == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto fail; + } + + start_elem = find_data->current; + + while (pkcs11_find_objects + (sinfo->module, sinfo->pks, ctx, OBJECTS_A_TIME, &count) == CKR_OK + && count > 0) { + unsigned j; + gnutls_datum_t id; + + if (unlikely(INT_ADD_OVERFLOW(find_data->current, count))) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto fail; + } + + find_data->p_list = + _gnutls_reallocarray_fast(find_data->p_list, + find_data->current + count, + sizeof(find_data->p_list[0])); + if (find_data->p_list == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto fail; + } + + for (j=0;j<count;j++) { + a[0].type = CKA_ID; + a[0].value = certid_tmp; + a[0].value_len = sizeof certid_tmp; + + if (pkcs11_get_attribute_value + (sinfo->module, sinfo->pks, ctx[j], a, 1) == CKR_OK) { + id.data = a[0].value; + id.size = a[0].value_len; + } else { + id.data = NULL; + id.size = 0; + } + + if (class_set == 0) { + a[0].type = CKA_CLASS; + a[0].value = &class; + a[0].value_len = sizeof class; + + rv = pkcs11_get_attribute_value(sinfo->module, + sinfo->pks, ctx[j], a, 1); + if (rv != CKR_OK) { + class = -1; + } + } + + if (find_data->flags & GNUTLS_PKCS11_OBJ_FLAG_WITH_PRIVKEY) { + for (i = 0; i < plist.key_ids_size; i++) { + if (plist.key_ids[i].length != + id.size + || memcmp(plist.key_ids[i].data, + id.data, + id.size) != 0) { + /* not found */ + continue; + } + } + } + + ret = + gnutls_pkcs11_obj_init(&find_data->p_list + [find_data->current]); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + ret = pkcs11_import_object(ctx[j], class, sinfo, + tinfo, lib_info, + find_data->p_list[find_data->current]); + if (ret < 0) { + gnutls_assert(); + /* skip the failed object */ + continue; + } + + find_data->current++; + } + } + + pkcs11_find_objects_final(sinfo); + + /* we can have only a search state going on, so we can only overwrite extensions + * after we have read everything. */ + if (find_data->overwrite_exts) { + for (i=start_elem;i<find_data->current;i++) { + if (find_data->p_list[i]->raw.size > 0) { + gnutls_datum_t spki; + rv = pkcs11_get_attribute_avalue(sinfo->module, sinfo->pks, ctx[i], CKA_PUBLIC_KEY_INFO, &spki); + if (rv == CKR_OK) { + ret = pkcs11_override_cert_exts(sinfo, &spki, &find_data->p_list[i]->raw); + gnutls_free(spki.data); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + } + } + } + } + gnutls_free(ctx); + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; /* continue until all tokens have been checked */ + + fail: + gnutls_free(ctx); + pkcs11_find_objects_final(sinfo); + if (plist.key_ids != NULL) { + for (i = 0; i < plist.key_ids_size; i++) { + _gnutls_buffer_clear(&plist.key_ids[i]); + } + gnutls_free(plist.key_ids); + } + if (find_data->p_list != NULL) { + for (i = 0; i < find_data->current; i++) { + gnutls_pkcs11_obj_deinit(find_data->p_list[i]); + } + gnutls_free(find_data->p_list); + } + find_data->p_list = NULL; + find_data->current = 0; + + return ret; +} + +/** + * gnutls_pkcs11_obj_list_import_url3: + * @p_list: An uninitialized object list (may be %NULL) + * @n_list: Initially should hold the maximum size of the list. Will contain the actual size. + * @url: A PKCS 11 url identifying a set of objects + * @flags: Or sequence of GNUTLS_PKCS11_OBJ_* flags + * + * This function will initialize and set values to an object list + * by using all objects identified by a PKCS 11 URL. + * + * This function will enumerate all the objects specified by the PKCS#11 URL + * provided. It expects an already allocated @p_list which has *@n_list elements, + * and that value will be updated to the actual number of present objects. The + * @p_list objects will be initialized and set by this function. + * To obtain a list of all available objects use a @url of 'pkcs11:'. + * + * All returned objects must be deinitialized using gnutls_pkcs11_obj_deinit(). + * + * The supported in this function @flags are %GNUTLS_PKCS11_OBJ_FLAG_LOGIN, + * %GNUTLS_PKCS11_OBJ_FLAG_LOGIN_SO, %GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE, + * %GNUTLS_PKCS11_OBJ_FLAG_CRT, %GNUTLS_PKCS11_OBJ_FLAG_PUBKEY, %GNUTLS_PKCS11_OBJ_FLAG_PRIVKEY, + * %GNUTLS_PKCS11_OBJ_FLAG_WITH_PRIVKEY, %GNUTLS_PKCS11_OBJ_FLAG_MARK_CA, + * %GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED, and since 3.5.1 the %GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT. + * + * On versions of GnuTLS prior to 3.4.0 the equivalent function was + * gnutls_pkcs11_obj_list_import_url(). That is also available on this version + * as a macro which maps to this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int +gnutls_pkcs11_obj_list_import_url3(gnutls_pkcs11_obj_t * p_list, + unsigned int *n_list, + const char *url, + unsigned int flags) +{ + gnutls_pkcs11_obj_t *list1 = NULL; + unsigned int n_list1, i; + int ret; + + ret = gnutls_pkcs11_obj_list_import_url4(&list1, &n_list1, url, flags); + if (ret < 0) + return gnutls_assert_val(ret); + + if (n_list1 > *n_list) { + *n_list = n_list1; + for (i=0;i<n_list1;i++) { + gnutls_pkcs11_obj_deinit(list1[i]); + } + gnutls_free(list1); + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + } + + *n_list = n_list1; + if (p_list && list1) + memcpy(p_list, list1, n_list1*sizeof(p_list[0])); + gnutls_free(list1); + + return 0; +} + +/** + * gnutls_pkcs11_obj_list_import_url4: + * @p_list: An uninitialized object list (may be NULL) + * @n_list: It will contain the size of the list. + * @url: A PKCS 11 url identifying a set of objects + * @flags: Or sequence of GNUTLS_PKCS11_OBJ_* flags + * + * This function will enumerate all the objects specified by the PKCS#11 URL + * provided. It will initialize and set values to the object pointer list (@p_list) + * provided. To obtain a list of all available objects use a @url of 'pkcs11:'. + * + * All returned objects must be deinitialized using gnutls_pkcs11_obj_deinit(), + * and @p_list must be deinitialized using gnutls_free(). + * + * The supported in this function @flags are %GNUTLS_PKCS11_OBJ_FLAG_LOGIN, + * %GNUTLS_PKCS11_OBJ_FLAG_LOGIN_SO, %GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE, + * %GNUTLS_PKCS11_OBJ_FLAG_CRT, %GNUTLS_PKCS11_OBJ_FLAG_PUBKEY, %GNUTLS_PKCS11_OBJ_FLAG_PRIVKEY, + * %GNUTLS_PKCS11_OBJ_FLAG_WITH_PRIVKEY, %GNUTLS_PKCS11_OBJ_FLAG_MARK_CA, + * %GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED, and since 3.5.1 the %GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT. + * + * On versions of GnuTLS prior to 3.4.0 the equivalent function was + * gnutls_pkcs11_obj_list_import_url2(). That is also available on this version + * as a macro which maps to this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int +gnutls_pkcs11_obj_list_import_url4(gnutls_pkcs11_obj_t ** p_list, + unsigned int *n_list, + const char *url, + unsigned int flags) +{ + int ret; + struct find_multi_obj_st priv; + + PKCS11_CHECK_INIT_FLAGS(flags); + + memset(&priv, 0, sizeof(priv)); + + /* fill in the find data structure */ + priv.flags = flags; + + if (url == NULL || url[0] == 0) { + url = "pkcs11:"; + } + + ret = pkcs11_url_to_info(url, &priv.info, flags); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT) { + priv.overwrite_exts = 1; + } + + ret = + _pkcs11_traverse_tokens(find_multi_objs_cb, &priv, priv.info, + NULL, pkcs11_obj_flags_to_int(flags)); + p11_kit_uri_free(priv.info); + + if (ret < 0) { + gnutls_assert(); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + *p_list = NULL; + *n_list = 0; + ret = 0; + } + return ret; + } + + *n_list = priv.current; + *p_list = priv.p_list; + + return 0; +} + +/** + * gnutls_x509_crt_import_pkcs11: + * @crt: A certificate of type #gnutls_x509_crt_t + * @pkcs11_crt: A PKCS 11 object that contains a certificate + * + * This function will import a PKCS 11 certificate to a #gnutls_x509_crt_t + * structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crt_import_pkcs11(gnutls_x509_crt_t crt, + gnutls_pkcs11_obj_t pkcs11_crt) +{ + return gnutls_x509_crt_import(crt, &pkcs11_crt->raw, + GNUTLS_X509_FMT_DER); +} + +/*- + * _gnutls_x509_crt_import_pkcs11_url: + * @crt: A certificate of type #gnutls_x509_crt_t + * @url: A PKCS 11 url + * @flags: One of GNUTLS_PKCS11_OBJ_* flags + * + * This function will import a PKCS 11 certificate directly from a token + * without involving the #gnutls_pkcs11_obj_t type. This function will + * fail if the certificate stored is not of X.509 type. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + -*/ +int +_gnutls_x509_crt_import_pkcs11_url(gnutls_x509_crt_t crt, + const char *url, unsigned int flags) +{ + gnutls_pkcs11_obj_t pcrt; + int ret; + + ret = gnutls_pkcs11_obj_init(&pcrt); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (crt->pin.cb) + gnutls_pkcs11_obj_set_pin_function(pcrt, crt->pin.cb, + crt->pin.data); + + ret = gnutls_pkcs11_obj_import_url(pcrt, url, flags|GNUTLS_PKCS11_OBJ_FLAG_EXPECT_CERT); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_crt_import(crt, &pcrt->raw, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + + ret = 0; + cleanup: + + gnutls_pkcs11_obj_deinit(pcrt); + return ret; +} + +/** + * gnutls_x509_crt_list_import_pkcs11: + * @certs: A list of certificates of type #gnutls_x509_crt_t + * @cert_max: The maximum size of the list + * @objs: A list of PKCS 11 objects + * @flags: 0 for now + * + * This function will import a PKCS 11 certificate list to a list of + * #gnutls_x509_crt_t type. These must not be initialized. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 2.12.0 + **/ +int +gnutls_x509_crt_list_import_pkcs11(gnutls_x509_crt_t * certs, + unsigned int cert_max, + gnutls_pkcs11_obj_t * const objs, + unsigned int flags) +{ + unsigned int i, j; + int ret; + + for (i = 0; i < cert_max; i++) { + ret = gnutls_x509_crt_init(&certs[i]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_crt_import_pkcs11(certs[i], objs[i]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + return 0; + + cleanup: + for (j = 0; j < i; j++) { + gnutls_x509_crt_deinit(certs[j]); + } + + return ret; +} + +static int +find_flags_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, struct ck_info *lib_info, void *input) +{ + struct find_flags_data_st *find_data = input; + + if (tinfo == NULL) { /* we don't support multiple calls */ + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* do not bother reading the token if basic fields do not match + */ + if (!p11_kit_uri_match_token_info(find_data->info, tinfo) || + !p11_kit_uri_match_module_info(find_data->info, lib_info)) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* found token! */ + if (p11_kit_module_get_flags(sinfo->module) & P11_KIT_MODULE_TRUSTED) + find_data->trusted = 1; + else + find_data->trusted = 0; + find_data->slot_flags = sinfo->slot_info.flags; + find_data->token_flags = sinfo->tinfo.flags; + + return 0; +} + +/** + * gnutls_pkcs11_token_get_flags: + * @url: should contain a PKCS 11 URL + * @flags: The output flags (GNUTLS_PKCS11_TOKEN_*) + * + * This function will return information about the PKCS 11 token flags. + * + * The supported flags are: %GNUTLS_PKCS11_TOKEN_HW and %GNUTLS_PKCS11_TOKEN_TRUSTED. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code on error. + * + * Since: 2.12.0 + **/ +int gnutls_pkcs11_token_get_flags(const char *url, unsigned int *flags) +{ + struct find_flags_data_st find_data; + int ret; + + PKCS11_CHECK_INIT; + + memset(&find_data, 0, sizeof(find_data)); + ret = pkcs11_url_to_info(url, &find_data.info, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _pkcs11_traverse_tokens(find_flags_cb, &find_data, find_data.info, + NULL, 0); + p11_kit_uri_free(find_data.info); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + *flags = 0; + + /* read slot flags */ + if (find_data.slot_flags & CKF_HW_SLOT) + *flags |= GNUTLS_PKCS11_TOKEN_HW; + + /* read token flags */ + if (find_data.token_flags & CKF_RNG) + *flags |= GNUTLS_PKCS11_TOKEN_RNG; + + if (find_data.token_flags & CKF_LOGIN_REQUIRED) + *flags |= GNUTLS_PKCS11_TOKEN_LOGIN_REQUIRED; + + if (find_data.token_flags & CKF_PROTECTED_AUTHENTICATION_PATH) + *flags |= GNUTLS_PKCS11_TOKEN_PROTECTED_AUTHENTICATION_PATH; + + if (find_data.token_flags & CKF_TOKEN_INITIALIZED) + *flags |= GNUTLS_PKCS11_TOKEN_INITIALIZED; + + if (find_data.token_flags & CKF_USER_PIN_COUNT_LOW) + *flags |= GNUTLS_PKCS11_TOKEN_USER_PIN_COUNT_LOW; + + if (find_data.token_flags & CKF_USER_PIN_FINAL_TRY) + *flags |= GNUTLS_PKCS11_TOKEN_USER_PIN_FINAL_TRY; + + if (find_data.token_flags & CKF_USER_PIN_LOCKED) + *flags |= GNUTLS_PKCS11_TOKEN_USER_PIN_LOCKED; + + if (find_data.token_flags & CKF_SO_PIN_COUNT_LOW) + *flags |= GNUTLS_PKCS11_TOKEN_SO_PIN_COUNT_LOW; + + if (find_data.token_flags & CKF_SO_PIN_FINAL_TRY) + *flags |= GNUTLS_PKCS11_TOKEN_SO_PIN_FINAL_TRY; + + if (find_data.token_flags & CKF_SO_PIN_LOCKED) + *flags |= GNUTLS_PKCS11_TOKEN_SO_PIN_LOCKED; + + if (find_data.token_flags & CKF_USER_PIN_INITIALIZED) + *flags |= GNUTLS_PKCS11_TOKEN_USER_PIN_INITIALIZED; + +#ifdef CKF_ERROR_STATE + if (find_data.token_flags & CKF_ERROR_STATE) + *flags |= GNUTLS_PKCS11_TOKEN_ERROR_STATE; +#endif + + /* other flags */ + if (find_data.trusted != 0) + *flags |= GNUTLS_PKCS11_TOKEN_TRUSTED; + + return 0; + +} + +/** + * gnutls_pkcs11_token_get_mechanism: + * @url: should contain a PKCS 11 URL + * @idx: The index of the mechanism + * @mechanism: The PKCS #11 mechanism ID + * + * This function will return the names of the supported mechanisms + * by the token. It should be called with an increasing index until + * it return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code on error. + * + * Since: 2.12.0 + **/ +int +gnutls_pkcs11_token_get_mechanism(const char *url, unsigned int idx, + unsigned long *mechanism) +{ + int ret; + ck_rv_t rv; + struct ck_function_list *module; + ck_slot_id_t slot; + struct ck_token_info tinfo; + struct p11_kit_uri *info = NULL; + unsigned long count; + ck_mechanism_type_t mlist[400]; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(url, &info, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = pkcs11_find_slot(&module, &slot, info, &tinfo, NULL, NULL); + p11_kit_uri_free(info); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + count = sizeof(mlist) / sizeof(mlist[0]); + rv = pkcs11_get_mechanism_list(module, slot, mlist, &count); + if (rv != CKR_OK) { + gnutls_assert(); + return pkcs11_rv_to_err(rv); + } + + if (idx >= count) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + *mechanism = mlist[idx]; + + return 0; +} + +/** + * gnutls_pkcs11_token_check_mechanism: + * @url: should contain a PKCS 11 URL + * @mechanism: The PKCS #11 mechanism ID + * @ptr: if set it should point to a CK_MECHANISM_INFO struct + * @psize: the size of CK_MECHANISM_INFO struct (for safety) + * @flags: must be zero + * + * This function will return whether a mechanism is supported + * by the given token. If the mechanism is supported and + * @ptr is set, it will be updated with the token information. + * + * Returns: Non-zero if the mechanism is supported or zero otherwise. + * + * Since: 3.6.0 + **/ +unsigned +gnutls_pkcs11_token_check_mechanism(const char *url, + unsigned long mechanism, + void *ptr, unsigned psize, unsigned flags) +{ + int ret; + ck_rv_t rv; + struct ck_function_list *module; + ck_slot_id_t slot; + struct ck_token_info tinfo; + struct p11_kit_uri *info = NULL; + struct ck_mechanism_info minfo; + + PKCS11_CHECK_INIT; + + ret = pkcs11_url_to_info(url, &info, 0); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = pkcs11_find_slot(&module, &slot, info, &tinfo, NULL, NULL); + p11_kit_uri_free(info); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + rv = pkcs11_get_mechanism_info(module, slot, mechanism, &minfo); + if (rv != CKR_OK) { + gnutls_assert(); + return 0; + } + + if (ptr) { + if (sizeof(minfo) > psize) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + else if (sizeof(minfo) < psize) + memset(ptr, 0, psize); + memcpy(ptr, &minfo, sizeof(minfo)); + } + + return 1; +} + +/** + * gnutls_pkcs11_type_get_name: + * @type: Holds the PKCS 11 object type, a #gnutls_pkcs11_obj_type_t. + * + * This function will return a human readable description of the + * PKCS11 object type @obj. It will return "Unknown" for unknown + * types. + * + * Returns: human readable string labeling the PKCS11 object type + * @type. + * + * Since: 2.12.0 + **/ +const char *gnutls_pkcs11_type_get_name(gnutls_pkcs11_obj_type_t type) +{ + switch (type) { + case GNUTLS_PKCS11_OBJ_X509_CRT: + return "X.509 Certificate"; + case GNUTLS_PKCS11_OBJ_PUBKEY: + return "Public key"; + case GNUTLS_PKCS11_OBJ_PRIVKEY: + return "Private key"; + case GNUTLS_PKCS11_OBJ_SECRET_KEY: + return "Secret key"; + case GNUTLS_PKCS11_OBJ_DATA: + return "Data"; + case GNUTLS_PKCS11_OBJ_X509_CRT_EXTENSION: + return "X.509 certificate extension"; + case GNUTLS_PKCS11_OBJ_UNKNOWN: + default: + return "Unknown"; + } +} + +static +int check_found_cert(struct find_cert_st *priv, + ck_object_handle_t ctx, + gnutls_datum_t *data, + time_t now, + ck_object_handle_t *cand_ctx) +{ + gnutls_x509_crt_t tcrt = NULL; + unsigned has_ski; + int ret; + + ret = gnutls_x509_crt_init(&tcrt); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = gnutls_x509_crt_import(tcrt, data, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + if (priv->flags & GNUTLS_PKCS11_OBJ_FLAG_COMPARE) { + if (priv->crt == NULL) { + gnutls_assert(); + ret = -1; + goto cleanup; + } + + if (gnutls_x509_crt_equals(priv->crt, tcrt) == 0) { + /* doesn't match */ + _gnutls_debug_log("check_found_cert: cert doesn't match the expected\n"); + ret = -1; + goto cleanup; + } + } + + if (priv->flags & GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY) { + if (priv->crt == NULL) { + gnutls_assert(); + ret = -1; + goto cleanup; + } + + if (_gnutls_check_if_same_key(priv->crt, tcrt, 1) == 0) { + /* doesn't match */ + _gnutls_debug_log("check_found_cert: cert key doesn't match the expected key\n"); + ret = -1; + goto cleanup; + } + } + + if (priv->key_id.size > 0 && + !_gnutls_check_valid_key_id(&priv->key_id, tcrt, now, &has_ski)) { + gnutls_assert(); + if (has_ski) { + _gnutls_debug_log("check_found_cert: cert has invalid key ID\n"); + ret = -1; + } else { + /* That's a possible match; there can be CA certificates without + * an SKI, which match a cert which has AKI. */ + *cand_ctx = ctx; + } + goto cleanup; + } + + ret = 0; +cleanup: + if (tcrt != NULL) + gnutls_x509_crt_deinit(tcrt); + return ret; +} + +static int get_data_and_attrs(struct pkcs11_session_info *sinfo, + ck_object_handle_t object, gnutls_datum_t *data, + char *label, size_t label_size, + uint8_t *id, size_t id_size, + gnutls_datum_t *o_label, gnutls_datum_t *o_id) +{ + ck_rv_t rv; + struct ck_attribute a[2]; + + /* data will contain the certificate */ + rv = pkcs11_get_attribute_avalue(sinfo->module, sinfo->pks, object, CKA_VALUE, data); + if (rv == CKR_OK) { + a[0].type = CKA_LABEL; + a[0].value = label; + a[0].value_len = label_size; + + a[1].type = CKA_ID; + a[1].value = id; + a[1].value_len = id_size; + + if (pkcs11_get_attribute_value(sinfo->module, sinfo->pks, object, a, + 2) == CKR_OK) { + o_label->data = a[0].value; + o_label->size = a[0].value_len; + o_id->data = a[1].value; + o_id->size = a[1].value_len; + + return 0; + } else { + _gnutls_free_datum(data); + _gnutls_debug_log + ("p11: Skipped cert, missing attrs.\n"); + } + } + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; +} + +static int +find_cert_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo, + struct ck_token_info *tinfo, struct ck_info *lib_info, void *input) +{ + struct ck_attribute a[10]; + ck_object_class_t class = -1; + ck_certificate_type_t type = (ck_certificate_type_t) - 1; + ck_rv_t rv; + ck_object_handle_t ctx, cand_ctx = CK_INVALID_HANDLE; + unsigned long count, a_vals; + int found = 0, ret; + struct find_cert_st *priv = input; + char label_tmp[PKCS11_LABEL_SIZE]; + uint8_t id_tmp[PKCS11_ID_SIZE]; + gnutls_datum_t data = {NULL, 0}; + unsigned finalized; + int i, tries; + ck_bool_t trusted = 1; + time_t now; + gnutls_datum_t label = {NULL,0}, id = {NULL,0}; + + if (tinfo == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* the DISTRUSTED flag is p11-kit module specific */ + if (priv->flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) { + if (memcmp(lib_info->manufacturer_id, "PKCS#11 Kit", 11) != 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + } + + if (priv->dn.size == 0 && priv->key_id.size == 0 && priv->issuer_dn.size == 0 && + priv->serial.size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* Find objects with given class and type */ + + if (priv->key_id.size > 0 && priv->dn.size > 0) + tries = 2; + else + tries = 1; + + now = gnutls_time(0); + for (i = 0; i < tries; i++) { + + a_vals = 0; + class = CKO_CERTIFICATE; + a[a_vals].type = CKA_CLASS; + a[a_vals].value = &class; + a[a_vals].value_len = sizeof class; + a_vals++; + + if (priv->flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED) { + a[a_vals].type = CKA_TRUSTED; + a[a_vals].value = &trusted; + a[a_vals].value_len = sizeof trusted; + a_vals++; + } + + if (priv->flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) { + if (!sinfo->trusted) /* only p11-kit "trusted" modules support this flag */ + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + a[a_vals].type = CKA_X_DISTRUSTED; + a[a_vals].value = &trusted; + a[a_vals].value_len = sizeof trusted; + a_vals++; + } + + if (priv->need_import != 0) { + type = CKC_X_509; + a[a_vals].type = CKA_CERTIFICATE_TYPE; + a[a_vals].value = &type; + a[a_vals].value_len = sizeof type; + a_vals++; + } + + if (i == 0 && priv->key_id.size > 0) { + a[a_vals].type = CKA_ID; + a[a_vals].value = priv->key_id.data; + a[a_vals].value_len = priv->key_id.size; + a_vals++; + } + + /* This doesn't do a proper comparison, see + * _gnutls_x509_compare_raw_dn() */ + if (priv->dn.size > 0) { + a[a_vals].type = CKA_SUBJECT; + a[a_vals].value = priv->dn.data; + a[a_vals].value_len = priv->dn.size; + a_vals++; + } + + if (priv->serial.size > 0) { + a[a_vals].type = CKA_SERIAL_NUMBER; + a[a_vals].value = priv->serial.data; + a[a_vals].value_len = priv->serial.size; + a_vals++; + } + + /* Same problem as for priv->dn */ + if (priv->issuer_dn.size > 0) { + a[a_vals].type = CKA_ISSUER; + a[a_vals].value = priv->issuer_dn.data; + a[a_vals].value_len = priv->issuer_dn.size; + a_vals++; + } + + finalized = 0; + rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, + a_vals); + if (rv != CKR_OK) { + gnutls_assert(); + _gnutls_debug_log + ("p11: FindObjectsInit failed.\n"); + ret = pkcs11_rv_to_err(rv); + goto cleanup; + } + + while (pkcs11_find_objects + (sinfo->module, sinfo->pks, &ctx, 1, + &count) == CKR_OK && count == 1) { + + if (priv->need_import == 0 && !(priv->flags & GNUTLS_PKCS11_OBJ_FLAG_COMPARE) + && !(priv->flags & GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY)) { + found = 1; + break; + } + + ret = get_data_and_attrs(sinfo, ctx, &data, + label_tmp, sizeof(label_tmp), + id_tmp, sizeof(id_tmp), + &label, + &id); + if (ret < 0) + continue; + + ret = check_found_cert(priv, ctx, &data, now, &cand_ctx); + if (ret < 0) { + _gnutls_free_datum(&data); + continue; + } + + found = 1; + break; + } + + if (!found && cand_ctx != CK_INVALID_HANDLE) { + /* there was a possible match; let's retrieve that one instead of + * failing */ + ret = get_data_and_attrs(sinfo, cand_ctx, &data, + label_tmp, sizeof(label_tmp), + id_tmp, sizeof(id_tmp), + &label, + &id); + if (ret >= 0) + found = 1; + + /* we do not need to use check_found_cert() because + * in case we have a candidate, we already have checked it + */ + } + + pkcs11_find_objects_final(sinfo); + finalized = 1; + + if (found != 0) { + if (!(priv->flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) && + (priv->flags & GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT) && data.size > 0) { + gnutls_datum_t spki; + rv = pkcs11_get_attribute_avalue(sinfo->module, sinfo->pks, ctx, CKA_PUBLIC_KEY_INFO, &spki); + if (rv == CKR_OK) { + ret = pkcs11_override_cert_exts(sinfo, &spki, &data); + gnutls_free(spki.data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + } + + if (priv->need_import != 0) { + ret = + pkcs11_obj_import(class, priv->obj, + &data, &id, &label, + tinfo, + lib_info); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + break; + } + } + + if (found == 0) { + gnutls_assert(); + ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } else { + ret = 0; + } + + cleanup: + gnutls_free(data.data); + if (finalized == 0) + pkcs11_find_objects_final(sinfo); + + return ret; +} + +/** + * gnutls_pkcs11_get_raw_issuer: + * @url: A PKCS 11 url identifying a token + * @cert: is the certificate to find issuer for + * @issuer: Will hold the issuer if any in an allocated buffer. + * @fmt: The format of the exported issuer. + * @flags: Use zero or flags from %GNUTLS_PKCS11_OBJ_FLAG. + * + * This function will return the issuer of a given certificate, if it + * is stored in the token. By default only marked as trusted issuers + * are returned. If any issuer should be returned specify + * %GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY in @flags. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.2.7 + **/ +int gnutls_pkcs11_get_raw_issuer(const char *url, gnutls_x509_crt_t cert, + gnutls_datum_t * issuer, + gnutls_x509_crt_fmt_t fmt, + unsigned int flags) +{ + int ret; + struct find_cert_st priv; + uint8_t id[PKCS11_ID_SIZE]; + size_t id_size; + struct p11_kit_uri *info = NULL; + + PKCS11_CHECK_INIT_FLAGS(flags); + + memset(&priv, 0, sizeof(priv)); + + if (url == NULL || url[0] == 0) { + url = "pkcs11:"; + } + + ret = pkcs11_url_to_info(url, &info, flags); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + id_size = sizeof(id); + ret = + gnutls_x509_crt_get_authority_key_id(cert, id, &id_size, NULL); + if (ret >= 0) { + priv.key_id.data = id; + priv.key_id.size = id_size; + } + + priv.dn.data = cert->raw_issuer_dn.data; + priv.dn.size = cert->raw_issuer_dn.size; + + if (!(flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY)) + flags |= GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; + + priv.flags = flags; + + ret = gnutls_pkcs11_obj_init(&priv.obj); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + gnutls_pkcs11_obj_set_pin_function(priv.obj, cert->pin.cb, cert->pin.data); + + priv.need_import = 1; + + ret = + _pkcs11_traverse_tokens(find_cert_cb, &priv, info, + &cert->pin, pkcs11_obj_flags_to_int(flags)); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + /* we have failed retrieving the right certificate; if there + * was a close match return that one. */ + priv.flags |= GNUTLS_PKCS11_OBJ_FLAG_FIRST_CLOSE_MATCH; + ret = + _pkcs11_traverse_tokens(find_cert_cb, &priv, info, + &cert->pin, pkcs11_obj_flags_to_int(flags)); + } + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pkcs11_obj_export3(priv.obj, fmt, issuer); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + if (priv.obj) + gnutls_pkcs11_obj_deinit(priv.obj); + if (info) + p11_kit_uri_free(info); + + return ret; +} + +/** + * gnutls_pkcs11_get_raw_issuer_by_dn: + * @url: A PKCS 11 url identifying a token + * @dn: is the DN to search for + * @issuer: Will hold the issuer if any in an allocated buffer. + * @fmt: The format of the exported issuer. + * @flags: Use zero or flags from %GNUTLS_PKCS11_OBJ_FLAG. + * + * This function will return the certificate with the given DN, if it + * is stored in the token. By default only marked as trusted issuers + * are returned. If any issuer should be returned specify + * %GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY in @flags. + * + * The name of the function includes issuer because it can + * be used to discover issuers of certificates. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.0 + **/ +int gnutls_pkcs11_get_raw_issuer_by_dn (const char *url, const gnutls_datum_t *dn, + gnutls_datum_t *issuer, + gnutls_x509_crt_fmt_t fmt, + unsigned int flags) +{ + int ret; + struct find_cert_st priv; + struct p11_kit_uri *info = NULL; + + PKCS11_CHECK_INIT_FLAGS(flags); + + memset(&priv, 0, sizeof(priv)); + + if (url == NULL || url[0] == 0) { + url = "pkcs11:"; + } + + ret = pkcs11_url_to_info(url, &info, flags); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + priv.dn.data = dn->data; + priv.dn.size = dn->size; + + if (!(flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY)) + flags |= GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; + + priv.flags = flags; + + ret = gnutls_pkcs11_obj_init(&priv.obj); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + priv.need_import = 1; + + ret = + _pkcs11_traverse_tokens(find_cert_cb, &priv, info, + NULL, pkcs11_obj_flags_to_int(flags)); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pkcs11_obj_export3(priv.obj, fmt, issuer); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + if (priv.obj) + gnutls_pkcs11_obj_deinit(priv.obj); + if (info) + p11_kit_uri_free(info); + + return ret; +} + +/** + * gnutls_pkcs11_get_raw_issuer_by_subject_key_id: + * @url: A PKCS 11 url identifying a token + * @dn: is the DN to search for (may be %NULL) + * @spki: is the subject key ID to search for + * @issuer: Will hold the issuer if any in an allocated buffer. + * @fmt: The format of the exported issuer. + * @flags: Use zero or flags from %GNUTLS_PKCS11_OBJ_FLAG. + * + * This function will return the certificate with the given DN and @spki, if it + * is stored in the token. By default only marked as trusted issuers + * are returned. If any issuer should be returned specify + * %GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY in @flags. + * + * The name of the function includes issuer because it can + * be used to discover issuers of certificates. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.4.2 + **/ +int gnutls_pkcs11_get_raw_issuer_by_subject_key_id (const char *url, + const gnutls_datum_t *dn, + const gnutls_datum_t *spki, + gnutls_datum_t *issuer, + gnutls_x509_crt_fmt_t fmt, + unsigned int flags) +{ + int ret; + struct find_cert_st priv; + struct p11_kit_uri *info = NULL; + + PKCS11_CHECK_INIT_FLAGS(flags); + + memset(&priv, 0, sizeof(priv)); + + if (url == NULL || url[0] == 0) { + url = "pkcs11:"; + } + + ret = pkcs11_url_to_info(url, &info, flags); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if (dn) { + priv.dn.data = dn->data; + priv.dn.size = dn->size; + } + + priv.key_id.data = spki->data; + priv.key_id.size = spki->size; + + if (!(flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY)) + flags |= GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED; + + priv.flags = flags; + + ret = gnutls_pkcs11_obj_init(&priv.obj); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + priv.need_import = 1; + + ret = + _pkcs11_traverse_tokens(find_cert_cb, &priv, info, + NULL, pkcs11_obj_flags_to_int(flags)); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pkcs11_obj_export3(priv.obj, fmt, issuer); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + + cleanup: + if (priv.obj) + gnutls_pkcs11_obj_deinit(priv.obj); + if (info) + p11_kit_uri_free(info); + + return ret; +} + +unsigned +_gnutls_pkcs11_crt_is_known(const char *url, gnutls_x509_crt_t cert, + unsigned int flags, + gnutls_x509_crt_t *trusted_cert) +{ + int ret; + struct find_cert_st priv; + uint8_t serial[128]; + size_t serial_size; + struct p11_kit_uri *info = NULL; + + PKCS11_CHECK_INIT_FLAGS_RET(flags, 0); + + memset(&priv, 0, sizeof(priv)); + + if (trusted_cert) { + ret = gnutls_pkcs11_obj_init(&priv.obj); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + priv.need_import = 1; + } + + if (url == NULL || url[0] == 0) { + url = "pkcs11:"; + } + + ret = pkcs11_url_to_info(url, &info, 0); + if (ret < 0) { + gnutls_assert(); + return 0; + } + + /* Attempt searching using the issuer DN + serial number */ + serial_size = sizeof(serial); + ret = + gnutls_x509_crt_get_serial(cert, serial, &serial_size); + if (ret < 0) { + gnutls_assert(); + ret = 0; + goto cleanup; + } + + ret = _gnutls_x509_ext_gen_number(serial, serial_size, &priv.serial); + if (ret < 0) { + gnutls_assert(); + ret = 0; + goto cleanup; + } + + priv.crt = cert; + + priv.issuer_dn.data = cert->raw_issuer_dn.data; + priv.issuer_dn.size = cert->raw_issuer_dn.size; + + /* assume PKCS11_OBJ_FLAG_COMPARE everywhere but DISTRUST info */ + if (!(flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) && !(flags & GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY)) { + flags |= GNUTLS_PKCS11_OBJ_FLAG_COMPARE; + } + + priv.flags = flags; + + ret = + _pkcs11_traverse_tokens(find_cert_cb, &priv, info, + NULL, pkcs11_obj_flags_to_int(flags)); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + _gnutls_debug_log("crt_is_known: did not find cert, using issuer DN + serial, using DN only\n"); + /* attempt searching with the subject DN only */ + gnutls_assert(); + if (priv.obj) + gnutls_pkcs11_obj_deinit(priv.obj); + gnutls_free(priv.serial.data); + memset(&priv, 0, sizeof(priv)); + if (trusted_cert) { + ret = gnutls_pkcs11_obj_init(&priv.obj); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + priv.need_import = 1; + } + priv.crt = cert; + priv.flags = flags; + + priv.dn.data = cert->raw_dn.data; + priv.dn.size = cert->raw_dn.size; + ret = + _pkcs11_traverse_tokens(find_cert_cb, &priv, info, + NULL, pkcs11_obj_flags_to_int(flags)); + } + if (ret < 0) { + gnutls_assert(); + _gnutls_debug_log("crt_is_known: did not find any cert\n"); + ret = 0; + goto cleanup; + } + + if (trusted_cert) { + ret = gnutls_x509_crt_init(trusted_cert); + if (ret < 0) { + gnutls_assert(); + ret = 0; + goto cleanup; + } + ret = gnutls_x509_crt_import_pkcs11(*trusted_cert, priv.obj); + if (ret < 0) { + gnutls_assert(); + gnutls_x509_crt_deinit(*trusted_cert); + ret = 0; + goto cleanup; + } + } + ret = 1; + + cleanup: + if (priv.obj) + gnutls_pkcs11_obj_deinit(priv.obj); + if (info) + p11_kit_uri_free(info); + gnutls_free(priv.serial.data); + + return ret; +} + +/** + * gnutls_pkcs11_crt_is_known: + * @url: A PKCS 11 url identifying a token + * @cert: is the certificate to find issuer for + * @flags: Use zero or flags from %GNUTLS_PKCS11_OBJ_FLAG. + * + * This function will check whether the provided certificate is stored + * in the specified token. This is useful in combination with + * %GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED or + * %GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED, + * to check whether a CA is present or a certificate is blacklisted in + * a trust PKCS #11 module. + * + * This function can be used with a @url of "pkcs11:", and in that case all modules + * will be searched. To restrict the modules to the marked as trusted in p11-kit + * use the %GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE flag. + * + * Note that the flag %GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED is + * specific to p11-kit trust modules. + * + * Returns: If the certificate exists non-zero is returned, otherwise zero. + * + * Since: 3.3.0 + **/ +unsigned gnutls_pkcs11_crt_is_known(const char *url, gnutls_x509_crt_t cert, + unsigned int flags) +{ + return _gnutls_pkcs11_crt_is_known(url, cert, flags, NULL); +} + +/** + * gnutls_pkcs11_obj_get_flags: + * @obj: The pkcs11 object + * @oflags: Will hold the output flags + * + * This function will return the flags of the object. + * The @oflags will be flags from %gnutls_pkcs11_obj_flags. That is, + * the %GNUTLS_PKCS11_OBJ_FLAG_MARK_* flags. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.3.7 + **/ +int +gnutls_pkcs11_obj_get_flags(gnutls_pkcs11_obj_t obj, unsigned int *oflags) +{ + *oflags = obj->flags; + + return 0; +} + +/** + * gnutls_pkcs11_obj_flags_get_str: + * @flags: holds the flags + * + * This function given an or-sequence of %GNUTLS_PKCS11_OBJ_FLAG_MARK, + * will return an allocated string with its description. The string + * needs to be deallocated using gnutls_free(). + * + * Returns: If flags is zero %NULL is returned, otherwise an allocated string. + * + * Since: 3.3.7 + **/ +char *gnutls_pkcs11_obj_flags_get_str(unsigned int flags) +{ + gnutls_buffer_st str; + gnutls_datum_t out; + int ret; + + if (flags == 0) + return NULL; + + _gnutls_buffer_init(&str); + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_KEY_WRAP) + _gnutls_buffer_append_str(&str, "CKA_WRAP/UNWRAP; "); + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_CA) + _gnutls_buffer_append_str(&str, "CKA_CERTIFICATE_CATEGORY=CA; "); + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE) + _gnutls_buffer_append_str(&str, "CKA_PRIVATE; "); + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_ALWAYS_AUTH) + _gnutls_buffer_append_str(&str, "CKA_ALWAYS_AUTH; "); + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED) + _gnutls_buffer_append_str(&str, "CKA_TRUSTED; "); + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_DISTRUSTED) + _gnutls_buffer_append_str(&str, "CKA_X_DISTRUSTED; "); + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_EXTRACTABLE) + _gnutls_buffer_append_str(&str, "CKA_EXTRACTABLE; "); + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_NEVER_EXTRACTABLE) + _gnutls_buffer_append_str(&str, "CKA_NEVER_EXTRACTABLE; "); + + if (flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE) + _gnutls_buffer_append_str(&str, "CKA_SENSITIVE; "); + + ret = _gnutls_buffer_to_datum(&str, &out, 1); + if (ret < 0) { + gnutls_assert(); + goto fail; + } + + return (char*)out.data; +fail: + return NULL; + +} |