diff options
Diffstat (limited to 'lib/isc/pk11.c')
-rw-r--r-- | lib/isc/pk11.c | 1112 |
1 files changed, 1112 insertions, 0 deletions
diff --git a/lib/isc/pk11.c b/lib/isc/pk11.c new file mode 100644 index 0000000..6d77314 --- /dev/null +++ b/lib/isc/pk11.c @@ -0,0 +1,1112 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/once.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/stdio.h> +#include <isc/strerr.h> +#include <isc/string.h> +#include <isc/thread.h> +#include <isc/util.h> + +#include <pk11/internal.h> +#include <pk11/pk11.h> +#include <pk11/result.h> +#include <pk11/site.h> +#include <pkcs11/pkcs11.h> + +#include <dst/result.h> + +/* was 32 octets, Petr Spacek suggested 1024, SoftHSMv2 uses 256... */ +#ifndef PINLEN +#define PINLEN 256 +#endif /* ifndef PINLEN */ + +#ifndef PK11_NO_LOGERR +#define PK11_NO_LOGERR 1 +#endif /* ifndef PK11_NO_LOGERR */ + +LIBISC_EXTERNAL_DATA bool pk11_verbose_init = false; + +static isc_once_t once = ISC_ONCE_INIT; +static isc_mem_t *pk11_mctx = NULL; +static int32_t allocsize = 0; +static bool initialized = false; + +typedef struct pk11_session pk11_session_t; +typedef struct pk11_token pk11_token_t; +typedef ISC_LIST(pk11_session_t) pk11_sessionlist_t; + +struct pk11_session { + unsigned int magic; + CK_SESSION_HANDLE session; + ISC_LINK(pk11_session_t) link; + pk11_token_t *token; +}; + +struct pk11_token { + unsigned int magic; + unsigned int operations; + ISC_LINK(pk11_token_t) link; + CK_SLOT_ID slotid; + pk11_sessionlist_t sessions; + bool logged; + char name[32]; + char manuf[32]; + char model[16]; + char serial[16]; + char pin[PINLEN + 1]; +}; +static ISC_LIST(pk11_token_t) tokens; + +static pk11_token_t *best_rsa_token; +static pk11_token_t *best_ecdsa_token; +static pk11_token_t *best_eddsa_token; + +static isc_result_t +free_all_sessions(void); +static isc_result_t +free_session_list(pk11_sessionlist_t *slist); +static isc_result_t +setup_session(pk11_session_t *sp, pk11_token_t *token, bool rw); +static void +scan_slots(void); +static isc_result_t +token_login(pk11_session_t *sp); +static char * +percent_decode(char *x, size_t *len); +static bool +pk11strcmp(const char *x, size_t lenx, const char *y, size_t leny); +static CK_ATTRIBUTE * +push_attribute(pk11_object_t *obj, isc_mem_t *mctx, size_t len); + +static isc_mutex_t alloclock; +static isc_mutex_t sessionlock; + +static pk11_sessionlist_t actives; + +static CK_C_INITIALIZE_ARGS pk11_init_args = { + NULL_PTR, /* CreateMutex */ + NULL_PTR, /* DestroyMutex */ + NULL_PTR, /* LockMutex */ + NULL_PTR, /* UnlockMutex */ + CKF_OS_LOCKING_OK, /* flags */ + NULL_PTR, /* pReserved */ +}; + +#ifndef PK11_LIB_LOCATION +#define PK11_LIB_LOCATION "unknown_provider" +#endif /* ifndef PK11_LIB_LOCATION */ + +#ifndef WIN32 +static const char *lib_name = PK11_LIB_LOCATION; +#else /* ifndef WIN32 */ +static const char *lib_name = PK11_LIB_LOCATION ".dll"; +#endif /* ifndef WIN32 */ + +void +pk11_set_lib_name(const char *name) { + lib_name = name; +} + +const char * +pk11_get_lib_name(void) { + return (lib_name); +} + +static void +initialize(void) { + char *pk11_provider; + + isc_mutex_init(&alloclock); + isc_mutex_init(&sessionlock); + + pk11_provider = getenv("PKCS11_PROVIDER"); + if (pk11_provider != NULL) { + lib_name = pk11_provider; + } +} + +void * +pk11_mem_get(size_t size) { + void *ptr; + + LOCK(&alloclock); + if (pk11_mctx != NULL) { + ptr = isc_mem_get(pk11_mctx, size); + } else { + ptr = malloc(size); + if (ptr == NULL && size != 0) { + char strbuf[ISC_STRERRORSIZE]; + strerror_r(errno, strbuf, sizeof(strbuf)); + isc_error_fatal(__FILE__, __LINE__, "malloc failed: %s", + strbuf); + } + } + UNLOCK(&alloclock); + + if (ptr != NULL) { + memset(ptr, 0, size); + } + return (ptr); +} + +void +pk11_mem_put(void *ptr, size_t size) { + if (ptr != NULL) { + memset(ptr, 0, size); + } + LOCK(&alloclock); + if (pk11_mctx != NULL) { + isc_mem_put(pk11_mctx, ptr, size); + } else { + if (ptr != NULL) { + allocsize -= (int)size; + } + free(ptr); + } + UNLOCK(&alloclock); +} + +isc_result_t +pk11_initialize(isc_mem_t *mctx, const char *engine) { + isc_result_t result = ISC_R_SUCCESS; + CK_RV rv; + + RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS); + + LOCK(&sessionlock); + LOCK(&alloclock); + if ((mctx != NULL) && (pk11_mctx == NULL) && (allocsize == 0)) { + isc_mem_attach(mctx, &pk11_mctx); + } + UNLOCK(&alloclock); + if (initialized) { + goto unlock; + } else { + initialized = true; + } + + ISC_LIST_INIT(tokens); + ISC_LIST_INIT(actives); + + if (engine != NULL) { + lib_name = engine; + } + + /* Initialize the CRYPTOKI library */ + rv = pkcs_C_Initialize((CK_VOID_PTR)&pk11_init_args); + + if (rv == 0xfe) { + result = PK11_R_NOPROVIDER; + fprintf(stderr, "Can't load PKCS#11 provider: %s\n", + pk11_get_load_error_message()); + goto unlock; + } + if (rv != CKR_OK) { + result = PK11_R_INITFAILED; + goto unlock; + } + + scan_slots(); +unlock: + UNLOCK(&sessionlock); + return (result); +} + +isc_result_t +pk11_finalize(void) { + pk11_token_t *token, *next; + isc_result_t ret; + + ret = free_all_sessions(); + (void)pkcs_C_Finalize(NULL_PTR); + token = ISC_LIST_HEAD(tokens); + while (token != NULL) { + next = ISC_LIST_NEXT(token, link); + ISC_LIST_UNLINK(tokens, token, link); + if (token == best_rsa_token) { + best_rsa_token = NULL; + } + if (token == best_ecdsa_token) { + best_ecdsa_token = NULL; + } + if (token == best_eddsa_token) { + best_eddsa_token = NULL; + } + pk11_mem_put(token, sizeof(*token)); + token = next; + } + if (pk11_mctx != NULL) { + isc_mem_detach(&pk11_mctx); + } + initialized = false; + return (ret); +} + +isc_result_t +pk11_get_session(pk11_context_t *ctx, pk11_optype_t optype, bool need_services, + bool rw, bool logon, const char *pin, CK_SLOT_ID slot) { + pk11_token_t *token = NULL; + pk11_sessionlist_t *freelist; + pk11_session_t *sp; + isc_result_t ret; + UNUSED(need_services); + + memset(ctx, 0, sizeof(pk11_context_t)); + ctx->handle = NULL; + ctx->session = CK_INVALID_HANDLE; + + ret = pk11_initialize(NULL, NULL); + if (ret != ISC_R_SUCCESS) { + return (ret); + } + + LOCK(&sessionlock); + /* wait for initialization to finish */ + UNLOCK(&sessionlock); + + switch (optype) { + case OP_ANY: + for (token = ISC_LIST_HEAD(tokens); token != NULL; + token = ISC_LIST_NEXT(token, link)) + { + if (token->slotid == slot) { + break; + } + } + break; + default: + for (token = ISC_LIST_HEAD(tokens); token != NULL; + token = ISC_LIST_NEXT(token, link)) + { + if (token->slotid == slot) { + break; + } + } + break; + } + if (token == NULL) { + return (ISC_R_NOTFOUND); + } + + /* Override the token's PIN */ + if (logon && pin != NULL && *pin != '\0') { + if (strlen(pin) > PINLEN) { + return (ISC_R_RANGE); + } + /* + * We want to zero out the old pin before + * overwriting with a new one. + */ + memset(token->pin, 0, sizeof(token->pin)); + strlcpy(token->pin, pin, sizeof(token->pin)); + } + + freelist = &token->sessions; + + LOCK(&sessionlock); + sp = ISC_LIST_HEAD(*freelist); + if (sp != NULL) { + ISC_LIST_UNLINK(*freelist, sp, link); + ISC_LIST_APPEND(actives, sp, link); + UNLOCK(&sessionlock); + if (logon) { + ret = token_login(sp); + } + ctx->handle = sp; + ctx->session = sp->session; + return (ret); + } + UNLOCK(&sessionlock); + + sp = pk11_mem_get(sizeof(*sp)); + sp->magic = SES_MAGIC; + sp->token = token; + sp->session = CK_INVALID_HANDLE; + ISC_LINK_INIT(sp, link); + ret = setup_session(sp, token, rw); + if ((ret == ISC_R_SUCCESS) && logon) { + ret = token_login(sp); + } + LOCK(&sessionlock); + ISC_LIST_APPEND(actives, sp, link); + UNLOCK(&sessionlock); + ctx->handle = sp; + ctx->session = sp->session; + return (ret); +} + +void +pk11_return_session(pk11_context_t *ctx) { + pk11_session_t *sp = (pk11_session_t *)ctx->handle; + + if (sp == NULL) { + return; + } + ctx->handle = NULL; + ctx->session = CK_INVALID_HANDLE; + + LOCK(&sessionlock); + ISC_LIST_UNLINK(actives, sp, link); + UNLOCK(&sessionlock); + if (sp->session == CK_INVALID_HANDLE) { + pk11_mem_put(sp, sizeof(*sp)); + return; + } + + LOCK(&sessionlock); + ISC_LIST_APPEND(sp->token->sessions, sp, link); + UNLOCK(&sessionlock); +} + +static isc_result_t +free_all_sessions(void) { + pk11_token_t *token; + isc_result_t ret = ISC_R_SUCCESS; + isc_result_t oret; + + for (token = ISC_LIST_HEAD(tokens); token != NULL; + token = ISC_LIST_NEXT(token, link)) + { + oret = free_session_list(&token->sessions); + if (oret != ISC_R_SUCCESS) { + ret = oret; + } + } + if (!ISC_LIST_EMPTY(actives)) { + ret = ISC_R_ADDRINUSE; + oret = free_session_list(&actives); + if (oret != ISC_R_SUCCESS) { + ret = oret; + } + } + return (ret); +} + +static isc_result_t +free_session_list(pk11_sessionlist_t *slist) { + pk11_session_t *sp; + CK_RV rv; + isc_result_t ret; + + ret = ISC_R_SUCCESS; + LOCK(&sessionlock); + while (!ISC_LIST_EMPTY(*slist)) { + sp = ISC_LIST_HEAD(*slist); + ISC_LIST_UNLINK(*slist, sp, link); + UNLOCK(&sessionlock); + if (sp->session != CK_INVALID_HANDLE) { + rv = pkcs_C_CloseSession(sp->session); + if (rv != CKR_OK) { + ret = DST_R_CRYPTOFAILURE; + } + } + LOCK(&sessionlock); + pk11_mem_put(sp, sizeof(*sp)); + } + UNLOCK(&sessionlock); + + return (ret); +} + +static isc_result_t +setup_session(pk11_session_t *sp, pk11_token_t *token, bool rw) { + CK_RV rv; + CK_FLAGS flags = CKF_SERIAL_SESSION; + + if (rw) { + flags += CKF_RW_SESSION; + } + + rv = pkcs_C_OpenSession(token->slotid, flags, NULL_PTR, NULL_PTR, + &sp->session); + if (rv != CKR_OK) { + return (DST_R_CRYPTOFAILURE); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +token_login(pk11_session_t *sp) { + CK_RV rv; + pk11_token_t *token = sp->token; + isc_result_t ret = ISC_R_SUCCESS; + + LOCK(&sessionlock); + if (!token->logged) { + rv = pkcs_C_Login(sp->session, CKU_USER, + (CK_UTF8CHAR_PTR)token->pin, + (CK_ULONG)strlen(token->pin)); + if (rv != CKR_OK) { +#if PK11_NO_LOGERR + pk11_error_fatalcheck(__FILE__, __LINE__, + "pkcs_C_Login", rv); +#else /* if PK11_NO_LOGERR */ + ret = ISC_R_NOPERM; +#endif /* if PK11_NO_LOGERR */ + } else { + token->logged = true; + } + } + UNLOCK(&sessionlock); + return (ret); +} + +#define PK11_TRACE(fmt) \ + if (pk11_verbose_init) \ + fprintf(stderr, fmt) +#define PK11_TRACE1(fmt, arg) \ + if (pk11_verbose_init) \ + fprintf(stderr, fmt, arg) +#define PK11_TRACE2(fmt, arg1, arg2) \ + if (pk11_verbose_init) \ + fprintf(stderr, fmt, arg1, arg2) +#define PK11_TRACEM(mech) \ + if (pk11_verbose_init) \ + fprintf(stderr, #mech ": 0x%lx\n", rv) + +static void +scan_slots(void) { + CK_MECHANISM_INFO mechInfo; + CK_TOKEN_INFO tokenInfo; + CK_RV rv; + CK_SLOT_ID slot; + CK_SLOT_ID_PTR slotList; + CK_ULONG slotCount; + pk11_token_t *token; + unsigned int i; + bool bad; + + slotCount = 0; + PK11_FATALCHECK(pkcs_C_GetSlotList, (CK_FALSE, NULL_PTR, &slotCount)); + PK11_TRACE1("slotCount=%lu\n", slotCount); + /* it's not an error if we didn't find any providers */ + if (slotCount == 0) { + return; + } + slotList = pk11_mem_get(sizeof(CK_SLOT_ID) * slotCount); + PK11_FATALCHECK(pkcs_C_GetSlotList, (CK_FALSE, slotList, &slotCount)); + + for (i = 0; i < slotCount; i++) { + slot = slotList[i]; + PK11_TRACE2("slot#%u=0x%lx\n", i, slot); + + rv = pkcs_C_GetTokenInfo(slot, &tokenInfo); + if (rv != CKR_OK) { + continue; + } + token = pk11_mem_get(sizeof(*token)); + token->magic = TOK_MAGIC; + token->slotid = slot; + ISC_LINK_INIT(token, link); + ISC_LIST_INIT(token->sessions); + memmove(token->name, tokenInfo.label, 32); + memmove(token->manuf, tokenInfo.manufacturerID, 32); + memmove(token->model, tokenInfo.model, 16); + memmove(token->serial, tokenInfo.serialNumber, 16); + ISC_LIST_APPEND(tokens, token, link); + + /* Check for RSA support */ + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) + { + bad = true; + PK11_TRACEM(CKM_RSA_PKCS_KEY_PAIR_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5_RSA_PKCS, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) + { + bad = true; + PK11_TRACEM(CKM_MD5_RSA_PKCS); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA1_RSA_PKCS, + &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) + { + bad = true; + PK11_TRACEM(CKM_SHA1_RSA_PKCS); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256_RSA_PKCS, + &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) + { + bad = true; + PK11_TRACEM(CKM_SHA256_RSA_PKCS); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512_RSA_PKCS, + &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) + { + bad = true; + PK11_TRACEM(CKM_SHA512_RSA_PKCS); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_RSA_PKCS, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) + { + bad = true; + PK11_TRACEM(CKM_RSA_PKCS); + } + if (!bad) { + token->operations |= 1 << OP_RSA; + if (best_rsa_token == NULL) { + best_rsa_token = token; + } + } + + /* Check for ECDSA support */ + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_EC_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) + { + bad = true; + PK11_TRACEM(CKM_EC_KEY_PAIR_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_ECDSA, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) + { + bad = true; + PK11_TRACEM(CKM_ECDSA); + } + if (!bad) { + token->operations |= 1 << OP_ECDSA; + if (best_ecdsa_token == NULL) { + best_ecdsa_token = token; + } + } + + /* Check for EDDSA support */ + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_EC_EDWARDS_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) + { + bad = true; + PK11_TRACEM(CKM_EC_EDWARDS_KEY_PAIR_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_EDDSA, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) + { + bad = true; + PK11_TRACEM(CKM_EDDSA); + } + if (!bad) { + token->operations |= 1 << OP_EDDSA; + if (best_eddsa_token == NULL) { + best_eddsa_token = token; + } + } + } + + if (slotList != NULL) { + pk11_mem_put(slotList, sizeof(CK_SLOT_ID) * slotCount); + } +} + +CK_SLOT_ID +pk11_get_best_token(pk11_optype_t optype) { + pk11_token_t *token = NULL; + + switch (optype) { + case OP_RSA: + token = best_rsa_token; + break; + case OP_ECDSA: + token = best_ecdsa_token; + break; + case OP_EDDSA: + token = best_eddsa_token; + break; + default: + break; + } + if (token == NULL) { + return (0); + } + return (token->slotid); +} + +isc_result_t +pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt, unsigned int *bits) { + unsigned int bitcnt, i; + CK_BYTE top; + + if (bytecnt == 0) { + *bits = 0; + return (ISC_R_SUCCESS); + } + bitcnt = bytecnt * 8; + for (i = 0; i < bytecnt; i++) { + top = data[i]; + if (top == 0) { + bitcnt -= 8; + continue; + } + if (top & 0x80) { + *bits = bitcnt; + return (ISC_R_SUCCESS); + } + if (top & 0x40) { + *bits = bitcnt - 1; + return (ISC_R_SUCCESS); + } + if (top & 0x20) { + *bits = bitcnt - 2; + return (ISC_R_SUCCESS); + } + if (top & 0x10) { + *bits = bitcnt - 3; + return (ISC_R_SUCCESS); + } + if (top & 0x08) { + *bits = bitcnt - 4; + return (ISC_R_SUCCESS); + } + if (top & 0x04) { + *bits = bitcnt - 5; + return (ISC_R_SUCCESS); + } + if (top & 0x02) { + *bits = bitcnt - 6; + return (ISC_R_SUCCESS); + } + if (top & 0x01) { + *bits = bitcnt - 7; + return (ISC_R_SUCCESS); + } + break; + } + return (ISC_R_RANGE); +} + +CK_ATTRIBUTE * +pk11_attribute_first(const pk11_object_t *obj) { + return (obj->repr); +} + +CK_ATTRIBUTE * +pk11_attribute_next(const pk11_object_t *obj, CK_ATTRIBUTE *attr) { + CK_ATTRIBUTE *next; + + next = attr + 1; + if ((next - obj->repr) >= obj->attrcnt) { + return (NULL); + } + return (next); +} + +CK_ATTRIBUTE * +pk11_attribute_bytype(const pk11_object_t *obj, CK_ATTRIBUTE_TYPE type) { + CK_ATTRIBUTE *attr; + + for (attr = pk11_attribute_first(obj); attr != NULL; + attr = pk11_attribute_next(obj, attr)) + { + if (attr->type == type) { + return (attr); + } + } + return (NULL); +} + +static char * +percent_decode(char *x, size_t *len) { + char *p, *c; + unsigned char v = 0; + + INSIST(len != NULL); + + for (p = c = x; p[0] != '\0'; p++, c++) { + switch (p[0]) { + case '%': + switch (p[1]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + v = (p[1] - '0') << 4; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + v = (p[1] - 'A' + 10) << 4; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + v = (p[1] - 'a' + 10) << 4; + break; + default: + return (NULL); + } + switch (p[2]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + v |= (p[2] - '0') & 0x0f; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + v = (p[2] - 'A' + 10) & 0x0f; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + v = (p[2] - 'a' + 10) & 0x0f; + break; + default: + return (NULL); + } + p += 2; + *c = (char)v; + (*len)++; + break; + default: + *c = *p; + (*len)++; + } + } + return (x); +} + +static bool +pk11strcmp(const char *x, size_t lenx, const char *y, size_t leny) { + char buf[32]; + + INSIST((leny == 32) || (leny == 16)); + + memset(buf, ' ', 32); + if (lenx > leny) { + lenx = leny; + } + memmove(buf, x, lenx); + return (memcmp(buf, y, leny) == 0); +} + +static CK_ATTRIBUTE * +push_attribute(pk11_object_t *obj, isc_mem_t *mctx, size_t len) { + CK_ATTRIBUTE *old = obj->repr; + CK_ATTRIBUTE *attr; + CK_BYTE cnt = obj->attrcnt; + + REQUIRE(old != NULL || cnt == 0); + + obj->repr = isc_mem_get(mctx, (cnt + 1) * sizeof(*attr)); + memset(obj->repr, 0, (cnt + 1) * sizeof(*attr)); + if (old != NULL) { + memmove(obj->repr, old, cnt * sizeof(*attr)); + } + attr = obj->repr + cnt; + attr->ulValueLen = (CK_ULONG)len; + attr->pValue = isc_mem_get(mctx, len); + memset(attr->pValue, 0, len); + if (old != NULL) { + memset(old, 0, cnt * sizeof(*attr)); + isc_mem_put(mctx, old, cnt * sizeof(*attr)); + } + obj->attrcnt++; + return (attr); +} + +#define DST_RET(a) \ + { \ + ret = a; \ + goto err; \ + } + +isc_result_t +pk11_parse_uri(pk11_object_t *obj, const char *label, isc_mem_t *mctx, + pk11_optype_t optype) { + CK_ATTRIBUTE *attr; + pk11_token_t *token = NULL; + char *uri, *p, *a, *na, *v; + size_t len, l; + FILE *stream = NULL; + char pin[PINLEN + 1]; + bool gotpin = false; + isc_result_t ret; + + /* get values to work on */ + len = strlen(label) + 1; + uri = isc_mem_get(mctx, len); + memmove(uri, label, len); + + /* get the URI scheme */ + p = strchr(uri, ':'); + if (p == NULL) { + DST_RET(PK11_R_NOPROVIDER); + } + *p++ = '\0'; + if (strcmp(uri, "pkcs11") != 0) { + DST_RET(PK11_R_NOPROVIDER); + } + + /* get attributes */ + for (na = p; na != NULL;) { + a = na; + p = strchr(a, ';'); + if (p == NULL) { + /* last attribute */ + na = NULL; + } else { + *p++ = '\0'; + na = p; + } + p = strchr(a, '='); + if (p != NULL) { + *p++ = '\0'; + v = p; + } else { + v = a; + } + l = 0; + v = percent_decode(v, &l); + if (v == NULL) { + DST_RET(PK11_R_NOPROVIDER); + } + if ((a == v) || (strcmp(a, "object") == 0)) { + /* object: CKA_LABEL */ + attr = pk11_attribute_bytype(obj, CKA_LABEL); + if (attr != NULL) { + DST_RET(PK11_R_NOPROVIDER); + } + attr = push_attribute(obj, mctx, l); + if (attr == NULL) { + DST_RET(ISC_R_NOMEMORY); + } + attr->type = CKA_LABEL; + memmove(attr->pValue, v, l); + } else if (strcmp(a, "token") == 0) { + /* token: CK_TOKEN_INFO label */ + if (token == NULL) { + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + { + if (pk11strcmp(v, l, token->name, 32)) { + break; + } + } + } + } else if (strcmp(a, "manufacturer") == 0) { + /* manufacturer: CK_TOKEN_INFO manufacturerID */ + if (token == NULL) { + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + { + if (pk11strcmp(v, l, token->manuf, 32)) + { + break; + } + } + } + } else if (strcmp(a, "serial") == 0) { + /* serial: CK_TOKEN_INFO serialNumber */ + if (token == NULL) { + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + { + if (pk11strcmp(v, l, token->serial, 16)) + { + break; + } + } + } + } else if (strcmp(a, "model") == 0) { + /* model: CK_TOKEN_INFO model */ + if (token == NULL) { + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + { + if (pk11strcmp(v, l, token->model, 16)) + { + break; + } + } + } + } else if (strcmp(a, "library-manufacturer") == 0) { + /* ignored */ + } else if (strcmp(a, "library-description") == 0) { + /* ignored */ + } else if (strcmp(a, "library-version") == 0) { + /* ignored */ + } else if (strcmp(a, "object-type") == 0) { + /* object-type: CKA_CLASS */ + /* only private makes sense */ + if (strcmp(v, "private") != 0) { + DST_RET(PK11_R_NOPROVIDER); + } + } else if (strcmp(a, "id") == 0) { + /* id: CKA_ID */ + attr = pk11_attribute_bytype(obj, CKA_ID); + if (attr != NULL) { + DST_RET(PK11_R_NOPROVIDER); + } + attr = push_attribute(obj, mctx, l); + if (attr == NULL) { + DST_RET(ISC_R_NOMEMORY); + } + attr->type = CKA_ID; + memmove(attr->pValue, v, l); + } else if (strcmp(a, "pin-source") == 0) { + /* pin-source: PIN */ + ret = isc_stdio_open(v, "r", &stream); + if (ret != ISC_R_SUCCESS) { + goto err; + } + memset(pin, 0, PINLEN + 1); + ret = isc_stdio_read(pin, 1, PINLEN + 1, stream, &l); + if ((ret != ISC_R_SUCCESS) && (ret != ISC_R_EOF)) { + goto err; + } + if (l > PINLEN) { + DST_RET(ISC_R_RANGE); + } + ret = isc_stdio_close(stream); + stream = NULL; + if (ret != ISC_R_SUCCESS) { + goto err; + } + gotpin = true; + } else { + DST_RET(PK11_R_NOPROVIDER); + } + } + + if ((pk11_attribute_bytype(obj, CKA_LABEL) == NULL) && + (pk11_attribute_bytype(obj, CKA_ID) == NULL)) + { + DST_RET(ISC_R_NOTFOUND); + } + + if (token == NULL) { + if (optype == OP_RSA) { + token = best_rsa_token; + } else if (optype == OP_ECDSA) { + token = best_ecdsa_token; + } else if (optype == OP_EDDSA) { + token = best_eddsa_token; + } + } + if (token == NULL) { + DST_RET(ISC_R_NOTFOUND); + } + obj->slot = token->slotid; + if (gotpin) { + memmove(token->pin, pin, PINLEN + 1); + obj->reqlogon = true; + } + + ret = ISC_R_SUCCESS; + +err: + if (stream != NULL) { + (void)isc_stdio_close(stream); + } + isc_mem_put(mctx, uri, len); + return (ret); +} + +void +pk11_error_fatalcheck(const char *file, int line, const char *funcname, + CK_RV rv) { + isc_error_fatal(file, line, "%s: Error = 0x%.8lX\n", funcname, rv); +} + +void +pk11_dump_tokens(void) { + pk11_token_t *token; + bool first; + + printf("DEFAULTS\n"); + printf("\tbest_rsa_token=%p\n", best_rsa_token); + printf("\tbest_ecdsa_token=%p\n", best_ecdsa_token); + printf("\tbest_eddsa_token=%p\n", best_eddsa_token); + + for (token = ISC_LIST_HEAD(tokens); token != NULL; + token = ISC_LIST_NEXT(token, link)) + { + printf("\nTOKEN\n"); + printf("\taddress=%p\n", token); + printf("\tslotID=%lu\n", token->slotid); + printf("\tlabel=%.32s\n", token->name); + printf("\tmanufacturerID=%.32s\n", token->manuf); + printf("\tmodel=%.16s\n", token->model); + printf("\tserialNumber=%.16s\n", token->serial); + printf("\tsupported operations=0x%x (", token->operations); + first = true; + if (token->operations & (1 << OP_RSA)) { + first = false; + printf("RSA"); + } + if (token->operations & (1 << OP_ECDSA)) { + if (!first) { + printf(","); + } + printf("EC"); + } + printf(")\n"); + } +} |