Files
bind9/lib/isc/pk11.c
Ondřej Surý 9d15323e24 Add small tweaks to the code to fix compilation when ISC assertions are disabled
While implementing the new unit testing framework cmocka, it was found that the
BIND 9 code doesn't compile when assertions are disabled or replaced with any
function (such as mock_assert() from cmocka unit testing framework) that's not
directly recognized as assertion by the compiler.

This made the compiler to complain about blocks of code that was recognized as
unreachable before, but now it isn't.

The changes in this commit include:

* assigns default values to couple of local variables,
* moves some return statements around INSIST assertions,
* adds __builtin_unreachable(); annotations after some INSIST assertions,
* fixes one broken assertion (= instead of ==)

(cherry picked from commit fbd2e47f51)
(cherry picked from commit b222783ae9)
2018-11-08 22:00:49 +07:00

1401 lines
33 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* 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 http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <config.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.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/string.h>
#include <isc/thread.h>
#include <isc/util.h>
#include <dst/result.h>
#include <pk11/pk11.h>
#include <pk11/internal.h>
#include <pk11/result.h>
#include <pk11/site.h>
#include <pkcs11/cryptoki.h>
#include <pkcs11/pkcs11.h>
#include <pkcs11/eddsa.h>
/* was 32 octets, Petr Spacek suggested 1024, SoftHSMv2 uses 256... */
#ifndef PINLEN
#define PINLEN 256
#endif
#ifndef PK11_NO_LOGERR
#define PK11_NO_LOGERR 1
#endif
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 *rand_token;
static pk11_token_t *best_rsa_token;
static pk11_token_t *best_dsa_token;
static pk11_token_t *best_dh_token;
static pk11_token_t *digest_token;
static pk11_token_t *best_ec_token;
static pk11_token_t *best_gost_token;
static pk11_token_t *aes_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 WIN32
static const char *lib_name = PK11_LIB_LOCATION;
#else
static const char *lib_name = PK11_LIB_LOCATION ".dll";
#endif
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;
RUNTIME_CHECK(isc_mutex_init(&alloclock) == ISC_R_SUCCESS);
RUNTIME_CHECK(isc_mutex_init(&sessionlock) == ISC_R_SUCCESS);
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)
allocsize += (int)size;
}
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();
#ifdef PKCS11CRYPTO
if (rand_token == NULL) {
result = PK11_R_NORANDOMSERVICE;
goto unlock;
}
if (digest_token == NULL) {
result = PK11_R_NODIGESTSERVICE;
goto unlock;
}
#if defined(AES_CC)
if (aes_token == NULL) {
result = PK11_R_NOAESSERVICE;
goto unlock;
}
#endif
#endif /* PKCS11CRYPTO */
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 == rand_token)
rand_token = NULL;
if (token == best_rsa_token)
best_rsa_token = NULL;
if (token == best_dsa_token)
best_dsa_token = NULL;
if (token == best_dh_token)
best_dh_token = NULL;
if (token == digest_token)
digest_token = NULL;
if (token == best_ec_token)
best_ec_token = NULL;
if (token == best_gost_token)
best_gost_token = NULL;
if (token == aes_token)
aes_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_rand_bytes(unsigned char *buf, int num) {
isc_result_t ret;
CK_RV rv;
pk11_context_t ctx;
ret = pk11_get_session(&ctx, OP_RAND, false, false,
false, NULL, 0);
if ((ret != ISC_R_SUCCESS) &&
(ret != PK11_R_NODIGESTSERVICE) &&
(ret != PK11_R_NOAESSERVICE))
return (ret);
RUNTIME_CHECK(ctx.session != CK_INVALID_HANDLE);
rv = pkcs_C_GenerateRandom(ctx.session,
(CK_BYTE_PTR) buf, (CK_ULONG) num);
pk11_return_session(&ctx);
if (rv == CKR_OK)
return (ISC_R_SUCCESS);
else
return (DST_R_CRYPTOFAILURE);
}
#define SEEDSIZE 1024
static CK_BYTE seed[SEEDSIZE];
void
pk11_rand_seed_fromfile(const char *randomfile) {
pk11_context_t ctx;
FILE *stream = NULL;
size_t cc = 0;
isc_result_t ret;
ret = pk11_get_session(&ctx, OP_RAND, false, false,
false, NULL, 0);
if ((ret != ISC_R_SUCCESS) &&
(ret != PK11_R_NODIGESTSERVICE) &&
(ret != PK11_R_NOAESSERVICE))
return;
RUNTIME_CHECK(ctx.session != CK_INVALID_HANDLE);
ret = isc_stdio_open(randomfile, "r", &stream);
if (ret != ISC_R_SUCCESS)
goto cleanup;
ret = isc_stdio_read(seed, 1, SEEDSIZE, stream, &cc);
if (ret!= ISC_R_SUCCESS)
goto cleanup;
ret = isc_stdio_close(stream);
stream = NULL;
if (ret!= ISC_R_SUCCESS)
goto cleanup;
(void) pkcs_C_SeedRandom(ctx.session, seed, (CK_ULONG) cc);
cleanup:
if (stream != NULL)
(void) isc_stdio_close(stream);
pk11_return_session(&ctx);
}
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;
#ifdef PKCS11CRYPTO
isc_result_t service_ret = ISC_R_SUCCESS;
#else
UNUSED(need_services);
#endif
memset(ctx, 0, sizeof(pk11_context_t));
ctx->handle = NULL;
ctx->session = CK_INVALID_HANDLE;
ret = pk11_initialize(NULL, NULL);
#ifdef PKCS11CRYPTO
if (ret == PK11_R_NORANDOMSERVICE ||
ret == PK11_R_NODIGESTSERVICE ||
ret == PK11_R_NOAESSERVICE) {
if (need_services)
return (ret);
service_ret = ret;
}
else
#endif /* PKCS11CRYPTO */
if (ret != ISC_R_SUCCESS)
return (ret);
LOCK(&sessionlock);
/* wait for initialization to finish */
UNLOCK(&sessionlock);
switch(optype) {
#ifdef PKCS11CRYPTO
case OP_RAND:
token = rand_token;
break;
case OP_DIGEST:
token = digest_token;
break;
case OP_AES:
token = aes_token;
break;
case OP_ANY:
for (token = ISC_LIST_HEAD(tokens);
token != NULL;
token = ISC_LIST_NEXT(token, link))
if (token->slotid == slot)
break;
break;
#endif
default:
for (token = ISC_LIST_HEAD(tokens);
token != NULL;
token = ISC_LIST_NEXT(token, link))
if (token->slotid == slot)
break;
#ifdef PKCS11CRYPTO
if ((token == NULL) ||
((token->operations & (1 << optype)) == 0))
return (ISC_R_NOTFOUND);
#endif
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));
if (sp == NULL)
return (ISC_R_NOMEMORY);
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;
#ifdef PKCS11CRYPTO
if (ret == ISC_R_SUCCESS)
ret = service_ret;
#endif
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) {
ret = ISC_R_NOPERM;
#if PK11_NO_LOGERR
pk11_error_fatalcheck(__FILE__, __LINE__,
"pkcs_C_Login", rv);
#endif
} 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);
RUNTIME_CHECK(slotList != NULL);
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));
RUNTIME_CHECK(token != NULL);
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);
if ((tokenInfo.flags & CKF_RNG) == 0) {
PK11_TRACE("no CKF_RNG\n");
goto try_rsa;
}
token->operations |= 1 << OP_RAND;
if (rand_token == NULL)
rand_token = token;
try_rsa:
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)) {
#if !defined(PK11_MD5_DISABLE) && !defined(PK11_RSA_PKCS_REPLACE)
bad = true;
#endif
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)) {
#ifndef PK11_RSA_PKCS_REPLACE
bad = true;
#endif
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)) {
#ifndef PK11_RSA_PKCS_REPLACE
bad = true;
#endif
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)) {
#ifndef PK11_RSA_PKCS_REPLACE
bad = true;
#endif
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)) {
#ifdef PK11_RSA_PKCS_REPLACE
bad = true;
#endif
PK11_TRACEM(CKM_RSA_PKCS);
}
if (bad)
goto try_dsa;
token->operations |= 1 << OP_RSA;
if (best_rsa_token == NULL)
best_rsa_token = token;
try_dsa:
bad = false;
rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_PARAMETER_GEN,
&mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_GENERATE) == 0)) {
#ifndef PK11_DSA_PARAMETER_GEN_SKIP
bad = true;
#endif
PK11_TRACEM(CKM_DSA_PARAMETER_GEN);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_KEY_PAIR_GEN,
&mechInfo);
if ((rv != CKR_OK) ||
((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) {
bad = true;
PK11_TRACEM(CKM_DSA_PARAMETER_GEN);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_SHA1, &mechInfo);
if ((rv != CKR_OK) ||
((mechInfo.flags & CKF_SIGN) == 0) ||
((mechInfo.flags & CKF_VERIFY) == 0)) {
bad = true;
PK11_TRACEM(CKM_DSA_SHA1);
}
if (bad)
goto try_dh;
#ifndef PK11_DSA_DISABLE
token->operations |= 1 << OP_DSA;
if (best_dsa_token == NULL)
best_dsa_token = token;
#endif
try_dh:
bad = false;
rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_PARAMETER_GEN,
&mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_GENERATE) == 0)) {
PK11_TRACEM(CKM_DH_PKCS_PARAMETER_GEN);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_KEY_PAIR_GEN,
&mechInfo);
if ((rv != CKR_OK) ||
((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) {
#ifndef PK11_DH_PKCS_PARAMETER_GEN_SKIP
bad = true;
#endif
PK11_TRACEM(CKM_DH_PKCS_KEY_PAIR_GEN);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_DERIVE,
&mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DERIVE) == 0)) {
bad = true;
PK11_TRACEM(CKM_DH_PKCS_DERIVE);
}
if (bad)
goto try_digest;
#ifndef PK11_DH_DISABLE
token->operations |= 1 << OP_DH;
if (best_dh_token == NULL)
best_dh_token = token;
#endif
try_digest:
bad = false;
rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) {
#ifndef PK11_MD5_DISABLE
bad = true;
#endif
PK11_TRACEM(CKM_MD5);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA_1, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) {
bad = true;
PK11_TRACEM(CKM_SHA_1);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA224, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) {
bad = true;
PK11_TRACEM(CKM_SHA224);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) {
bad = true;
PK11_TRACEM(CKM_SHA256);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA384, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) {
bad = true;
PK11_TRACEM(CKM_SHA384);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) {
bad = true;
PK11_TRACEM(CKM_SHA512);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5_HMAC, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) {
#if !defined(PK11_MD5_DISABLE) && !defined(PK11_MD5_HMAC_REPLACE)
bad = true;
#endif
PK11_TRACEM(CKM_MD5_HMAC);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA_1_HMAC, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) {
#ifndef PK11_SHA_1_HMAC_REPLACE
bad = true;
#endif
PK11_TRACEM(CKM_SHA_1_HMAC);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA224_HMAC, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) {
#ifndef PK11_SHA224_HMAC_REPLACE
bad = true;
#endif
PK11_TRACEM(CKM_SHA224_HMAC);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256_HMAC, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) {
#ifndef PK11_SHA256_HMAC_REPLACE
bad = true;
#endif
PK11_TRACEM(CKM_SHA256_HMAC);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA384_HMAC, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) {
#ifndef PK11_SHA384_HMAC_REPLACE
bad = true;
#endif
PK11_TRACEM(CKM_SHA384_HMAC);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512_HMAC, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) {
#ifndef PK11_SHA512_HMAC_REPLACE
bad = true;
#endif
PK11_TRACEM(CKM_SHA512_HMAC);
}
if (!bad) {
token->operations |= 1 << OP_DIGEST;
if (digest_token == NULL)
digest_token = token;
}
/* ECDSA requires digest */
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)
goto try_gost;
token->operations |= 1 << OP_EC;
if (best_ec_token == NULL)
best_ec_token = token;
try_gost:
bad = false;
/* does GOST require digest too? */
rv = pkcs_C_GetMechanismInfo(slot, CKM_GOSTR3411, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) {
bad = true;
PK11_TRACEM(CKM_GOSTR3411);
}
rv = pkcs_C_GetMechanismInfo(slot, CKM_GOSTR3410_KEY_PAIR_GEN,
&mechInfo);
if ((rv != CKR_OK) ||
((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) {
bad = true;
PK11_TRACEM(CKM_GOSTR3410_KEY_PAIR_GEN);
}
rv = pkcs_C_GetMechanismInfo(slot,
CKM_GOSTR3410_WITH_GOSTR3411,
&mechInfo);
if ((rv != CKR_OK) ||
((mechInfo.flags & CKF_SIGN) == 0) ||
((mechInfo.flags & CKF_VERIFY) == 0)) {
bad = true;
PK11_TRACEM(CKM_GOSTR3410_WITH_GOSTR3411);
}
if (bad)
goto try_eddsa;
token->operations |= 1 << OP_GOST;
if (best_gost_token == NULL)
best_gost_token = token;
try_eddsa:
#if defined(CKM_EDDSA_KEY_PAIR_GEN) && defined(CKM_EDDSA) && defined(CKK_EDDSA)
bad = false;
rv = pkcs_C_GetMechanismInfo(slot, CKM_EDDSA_KEY_PAIR_GEN,
&mechInfo);
if ((rv != CKR_OK) ||
((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) {
bad = true;
PK11_TRACEM(CKM_EDDSA_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)
goto try_aes;
try_aes:
#endif
bad = false;
rv = pkcs_C_GetMechanismInfo(slot, CKM_AES_ECB, &mechInfo);
if ((rv != CKR_OK) || ((mechInfo.flags & CKF_ENCRYPT) == 0)) {
bad = true;
PK11_TRACEM(CKM_AES_ECB);
}
if (bad)
continue;
token->operations |= 1 << OP_AES;
if (aes_token == NULL)
aes_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_RAND:
token = rand_token;
break;
case OP_RSA:
token = best_rsa_token;
break;
case OP_DSA:
token = best_dsa_token;
break;
case OP_DH:
token = best_dh_token;
break;
case OP_DIGEST:
token = digest_token;
break;
case OP_EC:
token = best_ec_token;
break;
case OP_GOST:
token = best_gost_token;
break;
case OP_AES:
token = aes_token;
break;
default:
break;
}
if (token == NULL)
return (0);
return (token->slotid);
}
unsigned int
pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt) {
unsigned int bitcnt, i;
CK_BYTE top;
if (bytecnt == 0)
return (0);
bitcnt = bytecnt * 8;
for (i = 0; i < bytecnt; i++) {
top = data[i];
if (top == 0) {
bitcnt -= 8;
continue;
}
if (top & 0x80)
return (bitcnt);
if (top & 0x40)
return (bitcnt - 1);
if (top & 0x20)
return (bitcnt - 2);
if (top & 0x10)
return (bitcnt - 3);
if (top & 0x08)
return (bitcnt - 4);
if (top & 0x04)
return (bitcnt - 5);
if (top & 0x02)
return (bitcnt - 6);
if (top & 0x01)
return (bitcnt - 7);
break;
}
INSIST(0);
ISC_UNREACHABLE();
}
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;
INSIST(len != NULL);
for (p = c = x; p[0] != '\0'; p++, c++) {
switch (p[0]) {
case '%':
v = 0;
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;
obj->repr = isc_mem_get(mctx, (cnt + 1) * sizeof(*attr));
if (obj->repr == NULL) {
obj->repr = old;
return (NULL);
}
memset(obj->repr, 0, (cnt + 1) * sizeof(*attr));
memmove(obj->repr, old, cnt * sizeof(*attr));
attr = obj->repr + cnt;
attr->ulValueLen = (CK_ULONG) len;
attr->pValue = isc_mem_get(mctx, len);
if (attr->pValue == NULL) {
memset(obj->repr, 0, (cnt + 1) * sizeof(*attr));
isc_mem_put(mctx, obj->repr, (cnt + 1) * sizeof(*attr));
obj->repr = old;
return (NULL);
}
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);
if (uri == NULL)
return (ISC_R_NOMEMORY);
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_DSA)
token = best_dsa_token;
else if (optype == OP_DH)
token = best_dh_token;
else if (optype == OP_EC)
token = best_ec_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("\trand_token=%p\n", rand_token);
printf("\tbest_rsa_token=%p\n", best_rsa_token);
printf("\tbest_dsa_token=%p\n", best_dsa_token);
printf("\tbest_dh_token=%p\n", best_dh_token);
printf("\tdigest_token=%p\n", digest_token);
printf("\tbest_ec_token=%p\n", best_ec_token);
printf("\tbest_gost_token=%p\n", best_gost_token);
printf("\taes_token=%p\n", aes_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_RAND)) {
if (!first)
printf(",");
first = false;
printf("RAND");
}
if (token->operations & (1 << OP_RSA)) {
first = false;
printf("RSA");
}
if (token->operations & (1 << OP_DSA)) {
if (!first)
printf(",");
first = false;
printf("DSA");
}
if (token->operations & (1 << OP_DH)) {
if (!first)
printf(",");
first = false;
printf("DH");
}
if (token->operations & (1 << OP_DIGEST)) {
if (!first)
printf(",");
first = false;
printf("DIGEST");
}
if (token->operations & (1 << OP_EC)) {
if (!first)
printf(",");
first = false;
printf("EC");
}
if (token->operations & (1 << OP_AES)) {
if (!first)
printf(",");
first = false;
printf("AES");
}
printf(")\n");
}
}