Home | History | Annotate | Line # | Download | only in isc
pk11.c revision 1.1.2.2
      1 /*	$NetBSD: pk11.c,v 1.1.2.2 2024/02/24 13:07:21 martin Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0.  If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #include <errno.h>
     17 #include <inttypes.h>
     18 #include <stdbool.h>
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 
     23 #include <isc/log.h>
     24 #include <isc/mem.h>
     25 #include <isc/once.h>
     26 #include <isc/platform.h>
     27 #include <isc/print.h>
     28 #include <isc/stdio.h>
     29 #include <isc/strerr.h>
     30 #include <isc/string.h>
     31 #include <isc/thread.h>
     32 #include <isc/util.h>
     33 
     34 #include <pk11/internal.h>
     35 #include <pk11/pk11.h>
     36 #include <pk11/result.h>
     37 #include <pk11/site.h>
     38 #include <pkcs11/pkcs11.h>
     39 
     40 #include <dst/result.h>
     41 
     42 /* was 32 octets, Petr Spacek suggested 1024, SoftHSMv2 uses 256... */
     43 #ifndef PINLEN
     44 #define PINLEN 256
     45 #endif /* ifndef PINLEN */
     46 
     47 #ifndef PK11_NO_LOGERR
     48 #define PK11_NO_LOGERR 1
     49 #endif /* ifndef PK11_NO_LOGERR */
     50 
     51 LIBISC_EXTERNAL_DATA bool pk11_verbose_init = false;
     52 
     53 static isc_once_t once = ISC_ONCE_INIT;
     54 static isc_mem_t *pk11_mctx = NULL;
     55 static int32_t allocsize = 0;
     56 static bool initialized = false;
     57 
     58 typedef struct pk11_session pk11_session_t;
     59 typedef struct pk11_token pk11_token_t;
     60 typedef ISC_LIST(pk11_session_t) pk11_sessionlist_t;
     61 
     62 struct pk11_session {
     63 	unsigned int magic;
     64 	CK_SESSION_HANDLE session;
     65 	ISC_LINK(pk11_session_t) link;
     66 	pk11_token_t *token;
     67 };
     68 
     69 struct pk11_token {
     70 	unsigned int magic;
     71 	unsigned int operations;
     72 	ISC_LINK(pk11_token_t) link;
     73 	CK_SLOT_ID slotid;
     74 	pk11_sessionlist_t sessions;
     75 	bool logged;
     76 	char name[32];
     77 	char manuf[32];
     78 	char model[16];
     79 	char serial[16];
     80 	char pin[PINLEN + 1];
     81 };
     82 static ISC_LIST(pk11_token_t) tokens;
     83 
     84 static pk11_token_t *best_rsa_token;
     85 static pk11_token_t *best_ecdsa_token;
     86 static pk11_token_t *best_eddsa_token;
     87 
     88 static isc_result_t
     89 free_all_sessions(void);
     90 static isc_result_t
     91 free_session_list(pk11_sessionlist_t *slist);
     92 static isc_result_t
     93 setup_session(pk11_session_t *sp, pk11_token_t *token, bool rw);
     94 static void
     95 scan_slots(void);
     96 static isc_result_t
     97 token_login(pk11_session_t *sp);
     98 static char *
     99 percent_decode(char *x, size_t *len);
    100 static bool
    101 pk11strcmp(const char *x, size_t lenx, const char *y, size_t leny);
    102 static CK_ATTRIBUTE *
    103 push_attribute(pk11_object_t *obj, isc_mem_t *mctx, size_t len);
    104 
    105 static isc_mutex_t alloclock;
    106 static isc_mutex_t sessionlock;
    107 
    108 static pk11_sessionlist_t actives;
    109 
    110 static CK_C_INITIALIZE_ARGS pk11_init_args = {
    111 	NULL_PTR,	   /* CreateMutex */
    112 	NULL_PTR,	   /* DestroyMutex */
    113 	NULL_PTR,	   /* LockMutex */
    114 	NULL_PTR,	   /* UnlockMutex */
    115 	CKF_OS_LOCKING_OK, /* flags */
    116 	NULL_PTR,	   /* pReserved */
    117 };
    118 
    119 #ifndef PK11_LIB_LOCATION
    120 #define PK11_LIB_LOCATION "unknown_provider"
    121 #endif /* ifndef PK11_LIB_LOCATION */
    122 
    123 #ifndef WIN32
    124 static const char *lib_name = PK11_LIB_LOCATION;
    125 #else  /* ifndef WIN32 */
    126 static const char *lib_name = PK11_LIB_LOCATION ".dll";
    127 #endif /* ifndef WIN32 */
    128 
    129 void
    130 pk11_set_lib_name(const char *name) {
    131 	lib_name = name;
    132 }
    133 
    134 const char *
    135 pk11_get_lib_name(void) {
    136 	return (lib_name);
    137 }
    138 
    139 static void
    140 initialize(void) {
    141 	char *pk11_provider;
    142 
    143 	isc_mutex_init(&alloclock);
    144 	isc_mutex_init(&sessionlock);
    145 
    146 	pk11_provider = getenv("PKCS11_PROVIDER");
    147 	if (pk11_provider != NULL) {
    148 		lib_name = pk11_provider;
    149 	}
    150 }
    151 
    152 void *
    153 pk11_mem_get(size_t size) {
    154 	void *ptr;
    155 
    156 	LOCK(&alloclock);
    157 	if (pk11_mctx != NULL) {
    158 		ptr = isc_mem_get(pk11_mctx, size);
    159 	} else {
    160 		ptr = malloc(size);
    161 		if (ptr == NULL && size != 0) {
    162 			char strbuf[ISC_STRERRORSIZE];
    163 			strerror_r(errno, strbuf, sizeof(strbuf));
    164 			isc_error_fatal(__FILE__, __LINE__, "malloc failed: %s",
    165 					strbuf);
    166 		}
    167 	}
    168 	UNLOCK(&alloclock);
    169 
    170 	if (ptr != NULL) {
    171 		memset(ptr, 0, size);
    172 	}
    173 	return (ptr);
    174 }
    175 
    176 void
    177 pk11_mem_put(void *ptr, size_t size) {
    178 	if (ptr != NULL) {
    179 		memset(ptr, 0, size);
    180 	}
    181 	LOCK(&alloclock);
    182 	if (pk11_mctx != NULL) {
    183 		isc_mem_put(pk11_mctx, ptr, size);
    184 	} else {
    185 		if (ptr != NULL) {
    186 			allocsize -= (int)size;
    187 		}
    188 		free(ptr);
    189 	}
    190 	UNLOCK(&alloclock);
    191 }
    192 
    193 isc_result_t
    194 pk11_initialize(isc_mem_t *mctx, const char *engine) {
    195 	isc_result_t result = ISC_R_SUCCESS;
    196 	CK_RV rv;
    197 
    198 	RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
    199 
    200 	LOCK(&sessionlock);
    201 	LOCK(&alloclock);
    202 	if ((mctx != NULL) && (pk11_mctx == NULL) && (allocsize == 0)) {
    203 		isc_mem_attach(mctx, &pk11_mctx);
    204 	}
    205 	UNLOCK(&alloclock);
    206 	if (initialized) {
    207 		goto unlock;
    208 	} else {
    209 		initialized = true;
    210 	}
    211 
    212 	ISC_LIST_INIT(tokens);
    213 	ISC_LIST_INIT(actives);
    214 
    215 	if (engine != NULL) {
    216 		lib_name = engine;
    217 	}
    218 
    219 	/* Initialize the CRYPTOKI library */
    220 	rv = pkcs_C_Initialize((CK_VOID_PTR)&pk11_init_args);
    221 
    222 	if (rv == 0xfe) {
    223 		result = PK11_R_NOPROVIDER;
    224 		fprintf(stderr, "Can't load PKCS#11 provider: %s\n",
    225 			pk11_get_load_error_message());
    226 		goto unlock;
    227 	}
    228 	if (rv != CKR_OK) {
    229 		result = PK11_R_INITFAILED;
    230 		goto unlock;
    231 	}
    232 
    233 	scan_slots();
    234 unlock:
    235 	UNLOCK(&sessionlock);
    236 	return (result);
    237 }
    238 
    239 isc_result_t
    240 pk11_finalize(void) {
    241 	pk11_token_t *token, *next;
    242 	isc_result_t ret;
    243 
    244 	ret = free_all_sessions();
    245 	(void)pkcs_C_Finalize(NULL_PTR);
    246 	token = ISC_LIST_HEAD(tokens);
    247 	while (token != NULL) {
    248 		next = ISC_LIST_NEXT(token, link);
    249 		ISC_LIST_UNLINK(tokens, token, link);
    250 		if (token == best_rsa_token) {
    251 			best_rsa_token = NULL;
    252 		}
    253 		if (token == best_ecdsa_token) {
    254 			best_ecdsa_token = NULL;
    255 		}
    256 		if (token == best_eddsa_token) {
    257 			best_eddsa_token = NULL;
    258 		}
    259 		pk11_mem_put(token, sizeof(*token));
    260 		token = next;
    261 	}
    262 	if (pk11_mctx != NULL) {
    263 		isc_mem_detach(&pk11_mctx);
    264 	}
    265 	initialized = false;
    266 	return (ret);
    267 }
    268 
    269 isc_result_t
    270 pk11_get_session(pk11_context_t *ctx, pk11_optype_t optype, bool need_services,
    271 		 bool rw, bool logon, const char *pin, CK_SLOT_ID slot) {
    272 	pk11_token_t *token = NULL;
    273 	pk11_sessionlist_t *freelist;
    274 	pk11_session_t *sp;
    275 	isc_result_t ret;
    276 	UNUSED(need_services);
    277 
    278 	memset(ctx, 0, sizeof(pk11_context_t));
    279 	ctx->handle = NULL;
    280 	ctx->session = CK_INVALID_HANDLE;
    281 
    282 	ret = pk11_initialize(NULL, NULL);
    283 	if (ret != ISC_R_SUCCESS) {
    284 		return (ret);
    285 	}
    286 
    287 	LOCK(&sessionlock);
    288 	/* wait for initialization to finish */
    289 	UNLOCK(&sessionlock);
    290 
    291 	switch (optype) {
    292 	case OP_ANY:
    293 		for (token = ISC_LIST_HEAD(tokens); token != NULL;
    294 		     token = ISC_LIST_NEXT(token, link))
    295 		{
    296 			if (token->slotid == slot) {
    297 				break;
    298 			}
    299 		}
    300 		break;
    301 	default:
    302 		for (token = ISC_LIST_HEAD(tokens); token != NULL;
    303 		     token = ISC_LIST_NEXT(token, link))
    304 		{
    305 			if (token->slotid == slot) {
    306 				break;
    307 			}
    308 		}
    309 		break;
    310 	}
    311 	if (token == NULL) {
    312 		return (ISC_R_NOTFOUND);
    313 	}
    314 
    315 	/* Override the token's PIN */
    316 	if (logon && pin != NULL && *pin != '\0') {
    317 		if (strlen(pin) > PINLEN) {
    318 			return (ISC_R_RANGE);
    319 		}
    320 		/*
    321 		 * We want to zero out the old pin before
    322 		 * overwriting with a new one.
    323 		 */
    324 		memset(token->pin, 0, sizeof(token->pin));
    325 		strlcpy(token->pin, pin, sizeof(token->pin));
    326 	}
    327 
    328 	freelist = &token->sessions;
    329 
    330 	LOCK(&sessionlock);
    331 	sp = ISC_LIST_HEAD(*freelist);
    332 	if (sp != NULL) {
    333 		ISC_LIST_UNLINK(*freelist, sp, link);
    334 		ISC_LIST_APPEND(actives, sp, link);
    335 		UNLOCK(&sessionlock);
    336 		if (logon) {
    337 			ret = token_login(sp);
    338 		}
    339 		ctx->handle = sp;
    340 		ctx->session = sp->session;
    341 		return (ret);
    342 	}
    343 	UNLOCK(&sessionlock);
    344 
    345 	sp = pk11_mem_get(sizeof(*sp));
    346 	sp->magic = SES_MAGIC;
    347 	sp->token = token;
    348 	sp->session = CK_INVALID_HANDLE;
    349 	ISC_LINK_INIT(sp, link);
    350 	ret = setup_session(sp, token, rw);
    351 	if ((ret == ISC_R_SUCCESS) && logon) {
    352 		ret = token_login(sp);
    353 	}
    354 	LOCK(&sessionlock);
    355 	ISC_LIST_APPEND(actives, sp, link);
    356 	UNLOCK(&sessionlock);
    357 	ctx->handle = sp;
    358 	ctx->session = sp->session;
    359 	return (ret);
    360 }
    361 
    362 void
    363 pk11_return_session(pk11_context_t *ctx) {
    364 	pk11_session_t *sp = (pk11_session_t *)ctx->handle;
    365 
    366 	if (sp == NULL) {
    367 		return;
    368 	}
    369 	ctx->handle = NULL;
    370 	ctx->session = CK_INVALID_HANDLE;
    371 
    372 	LOCK(&sessionlock);
    373 	ISC_LIST_UNLINK(actives, sp, link);
    374 	UNLOCK(&sessionlock);
    375 	if (sp->session == CK_INVALID_HANDLE) {
    376 		pk11_mem_put(sp, sizeof(*sp));
    377 		return;
    378 	}
    379 
    380 	LOCK(&sessionlock);
    381 	ISC_LIST_APPEND(sp->token->sessions, sp, link);
    382 	UNLOCK(&sessionlock);
    383 }
    384 
    385 static isc_result_t
    386 free_all_sessions(void) {
    387 	pk11_token_t *token;
    388 	isc_result_t ret = ISC_R_SUCCESS;
    389 	isc_result_t oret;
    390 
    391 	for (token = ISC_LIST_HEAD(tokens); token != NULL;
    392 	     token = ISC_LIST_NEXT(token, link))
    393 	{
    394 		oret = free_session_list(&token->sessions);
    395 		if (oret != ISC_R_SUCCESS) {
    396 			ret = oret;
    397 		}
    398 	}
    399 	if (!ISC_LIST_EMPTY(actives)) {
    400 		ret = ISC_R_ADDRINUSE;
    401 		oret = free_session_list(&actives);
    402 		if (oret != ISC_R_SUCCESS) {
    403 			ret = oret;
    404 		}
    405 	}
    406 	return (ret);
    407 }
    408 
    409 static isc_result_t
    410 free_session_list(pk11_sessionlist_t *slist) {
    411 	pk11_session_t *sp;
    412 	CK_RV rv;
    413 	isc_result_t ret;
    414 
    415 	ret = ISC_R_SUCCESS;
    416 	LOCK(&sessionlock);
    417 	while (!ISC_LIST_EMPTY(*slist)) {
    418 		sp = ISC_LIST_HEAD(*slist);
    419 		ISC_LIST_UNLINK(*slist, sp, link);
    420 		UNLOCK(&sessionlock);
    421 		if (sp->session != CK_INVALID_HANDLE) {
    422 			rv = pkcs_C_CloseSession(sp->session);
    423 			if (rv != CKR_OK) {
    424 				ret = DST_R_CRYPTOFAILURE;
    425 			}
    426 		}
    427 		LOCK(&sessionlock);
    428 		pk11_mem_put(sp, sizeof(*sp));
    429 	}
    430 	UNLOCK(&sessionlock);
    431 
    432 	return (ret);
    433 }
    434 
    435 static isc_result_t
    436 setup_session(pk11_session_t *sp, pk11_token_t *token, bool rw) {
    437 	CK_RV rv;
    438 	CK_FLAGS flags = CKF_SERIAL_SESSION;
    439 
    440 	if (rw) {
    441 		flags += CKF_RW_SESSION;
    442 	}
    443 
    444 	rv = pkcs_C_OpenSession(token->slotid, flags, NULL_PTR, NULL_PTR,
    445 				&sp->session);
    446 	if (rv != CKR_OK) {
    447 		return (DST_R_CRYPTOFAILURE);
    448 	}
    449 	return (ISC_R_SUCCESS);
    450 }
    451 
    452 static isc_result_t
    453 token_login(pk11_session_t *sp) {
    454 	CK_RV rv;
    455 	pk11_token_t *token = sp->token;
    456 	isc_result_t ret = ISC_R_SUCCESS;
    457 
    458 	LOCK(&sessionlock);
    459 	if (!token->logged) {
    460 		rv = pkcs_C_Login(sp->session, CKU_USER,
    461 				  (CK_UTF8CHAR_PTR)token->pin,
    462 				  (CK_ULONG)strlen(token->pin));
    463 		if (rv != CKR_OK) {
    464 #if PK11_NO_LOGERR
    465 			pk11_error_fatalcheck(__FILE__, __LINE__,
    466 					      "pkcs_C_Login", rv);
    467 #else  /* if PK11_NO_LOGERR */
    468 			ret = ISC_R_NOPERM;
    469 #endif /* if PK11_NO_LOGERR */
    470 		} else {
    471 			token->logged = true;
    472 		}
    473 	}
    474 	UNLOCK(&sessionlock);
    475 	return (ret);
    476 }
    477 
    478 #define PK11_TRACE(fmt)        \
    479 	if (pk11_verbose_init) \
    480 	fprintf(stderr, fmt)
    481 #define PK11_TRACE1(fmt, arg)  \
    482 	if (pk11_verbose_init) \
    483 	fprintf(stderr, fmt, arg)
    484 #define PK11_TRACE2(fmt, arg1, arg2) \
    485 	if (pk11_verbose_init)       \
    486 	fprintf(stderr, fmt, arg1, arg2)
    487 #define PK11_TRACEM(mech)      \
    488 	if (pk11_verbose_init) \
    489 	fprintf(stderr, #mech ": 0x%lx\n", rv)
    490 
    491 static void
    492 scan_slots(void) {
    493 	CK_MECHANISM_INFO mechInfo;
    494 	CK_TOKEN_INFO tokenInfo;
    495 	CK_RV rv;
    496 	CK_SLOT_ID slot;
    497 	CK_SLOT_ID_PTR slotList;
    498 	CK_ULONG slotCount;
    499 	pk11_token_t *token;
    500 	unsigned int i;
    501 	bool bad;
    502 
    503 	slotCount = 0;
    504 	PK11_FATALCHECK(pkcs_C_GetSlotList, (CK_FALSE, NULL_PTR, &slotCount));
    505 	PK11_TRACE1("slotCount=%lu\n", slotCount);
    506 	/* it's not an error if we didn't find any providers */
    507 	if (slotCount == 0) {
    508 		return;
    509 	}
    510 	slotList = pk11_mem_get(sizeof(CK_SLOT_ID) * slotCount);
    511 	PK11_FATALCHECK(pkcs_C_GetSlotList, (CK_FALSE, slotList, &slotCount));
    512 
    513 	for (i = 0; i < slotCount; i++) {
    514 		slot = slotList[i];
    515 		PK11_TRACE2("slot#%u=0x%lx\n", i, slot);
    516 
    517 		rv = pkcs_C_GetTokenInfo(slot, &tokenInfo);
    518 		if (rv != CKR_OK) {
    519 			continue;
    520 		}
    521 		token = pk11_mem_get(sizeof(*token));
    522 		token->magic = TOK_MAGIC;
    523 		token->slotid = slot;
    524 		ISC_LINK_INIT(token, link);
    525 		ISC_LIST_INIT(token->sessions);
    526 		memmove(token->name, tokenInfo.label, 32);
    527 		memmove(token->manuf, tokenInfo.manufacturerID, 32);
    528 		memmove(token->model, tokenInfo.model, 16);
    529 		memmove(token->serial, tokenInfo.serialNumber, 16);
    530 		ISC_LIST_APPEND(tokens, token, link);
    531 
    532 		/* Check for RSA support */
    533 		bad = false;
    534 		rv = pkcs_C_GetMechanismInfo(slot, CKM_RSA_PKCS_KEY_PAIR_GEN,
    535 					     &mechInfo);
    536 		if ((rv != CKR_OK) ||
    537 		    ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0))
    538 		{
    539 			bad = true;
    540 			PK11_TRACEM(CKM_RSA_PKCS_KEY_PAIR_GEN);
    541 		}
    542 		rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5_RSA_PKCS, &mechInfo);
    543 		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) ||
    544 		    ((mechInfo.flags & CKF_VERIFY) == 0))
    545 		{
    546 			bad = true;
    547 			PK11_TRACEM(CKM_MD5_RSA_PKCS);
    548 		}
    549 		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA1_RSA_PKCS,
    550 					     &mechInfo);
    551 		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) ||
    552 		    ((mechInfo.flags & CKF_VERIFY) == 0))
    553 		{
    554 			bad = true;
    555 			PK11_TRACEM(CKM_SHA1_RSA_PKCS);
    556 		}
    557 		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256_RSA_PKCS,
    558 					     &mechInfo);
    559 		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) ||
    560 		    ((mechInfo.flags & CKF_VERIFY) == 0))
    561 		{
    562 			bad = true;
    563 			PK11_TRACEM(CKM_SHA256_RSA_PKCS);
    564 		}
    565 		rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512_RSA_PKCS,
    566 					     &mechInfo);
    567 		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) ||
    568 		    ((mechInfo.flags & CKF_VERIFY) == 0))
    569 		{
    570 			bad = true;
    571 			PK11_TRACEM(CKM_SHA512_RSA_PKCS);
    572 		}
    573 		rv = pkcs_C_GetMechanismInfo(slot, CKM_RSA_PKCS, &mechInfo);
    574 		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) ||
    575 		    ((mechInfo.flags & CKF_VERIFY) == 0))
    576 		{
    577 			bad = true;
    578 			PK11_TRACEM(CKM_RSA_PKCS);
    579 		}
    580 		if (!bad) {
    581 			token->operations |= 1 << OP_RSA;
    582 			if (best_rsa_token == NULL) {
    583 				best_rsa_token = token;
    584 			}
    585 		}
    586 
    587 		/* Check for ECDSA support */
    588 		bad = false;
    589 		rv = pkcs_C_GetMechanismInfo(slot, CKM_EC_KEY_PAIR_GEN,
    590 					     &mechInfo);
    591 		if ((rv != CKR_OK) ||
    592 		    ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0))
    593 		{
    594 			bad = true;
    595 			PK11_TRACEM(CKM_EC_KEY_PAIR_GEN);
    596 		}
    597 		rv = pkcs_C_GetMechanismInfo(slot, CKM_ECDSA, &mechInfo);
    598 		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) ||
    599 		    ((mechInfo.flags & CKF_VERIFY) == 0))
    600 		{
    601 			bad = true;
    602 			PK11_TRACEM(CKM_ECDSA);
    603 		}
    604 		if (!bad) {
    605 			token->operations |= 1 << OP_ECDSA;
    606 			if (best_ecdsa_token == NULL) {
    607 				best_ecdsa_token = token;
    608 			}
    609 		}
    610 
    611 		/* Check for EDDSA support */
    612 		bad = false;
    613 		rv = pkcs_C_GetMechanismInfo(slot, CKM_EC_EDWARDS_KEY_PAIR_GEN,
    614 					     &mechInfo);
    615 		if ((rv != CKR_OK) ||
    616 		    ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0))
    617 		{
    618 			bad = true;
    619 			PK11_TRACEM(CKM_EC_EDWARDS_KEY_PAIR_GEN);
    620 		}
    621 		rv = pkcs_C_GetMechanismInfo(slot, CKM_EDDSA, &mechInfo);
    622 		if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0) ||
    623 		    ((mechInfo.flags & CKF_VERIFY) == 0))
    624 		{
    625 			bad = true;
    626 			PK11_TRACEM(CKM_EDDSA);
    627 		}
    628 		if (!bad) {
    629 			token->operations |= 1 << OP_EDDSA;
    630 			if (best_eddsa_token == NULL) {
    631 				best_eddsa_token = token;
    632 			}
    633 		}
    634 	}
    635 
    636 	if (slotList != NULL) {
    637 		pk11_mem_put(slotList, sizeof(CK_SLOT_ID) * slotCount);
    638 	}
    639 }
    640 
    641 CK_SLOT_ID
    642 pk11_get_best_token(pk11_optype_t optype) {
    643 	pk11_token_t *token = NULL;
    644 
    645 	switch (optype) {
    646 	case OP_RSA:
    647 		token = best_rsa_token;
    648 		break;
    649 	case OP_ECDSA:
    650 		token = best_ecdsa_token;
    651 		break;
    652 	case OP_EDDSA:
    653 		token = best_eddsa_token;
    654 		break;
    655 	default:
    656 		break;
    657 	}
    658 	if (token == NULL) {
    659 		return (0);
    660 	}
    661 	return (token->slotid);
    662 }
    663 
    664 isc_result_t
    665 pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt, unsigned int *bits) {
    666 	unsigned int bitcnt, i;
    667 	CK_BYTE top;
    668 
    669 	if (bytecnt == 0) {
    670 		*bits = 0;
    671 		return (ISC_R_SUCCESS);
    672 	}
    673 	bitcnt = bytecnt * 8;
    674 	for (i = 0; i < bytecnt; i++) {
    675 		top = data[i];
    676 		if (top == 0) {
    677 			bitcnt -= 8;
    678 			continue;
    679 		}
    680 		if (top & 0x80) {
    681 			*bits = bitcnt;
    682 			return (ISC_R_SUCCESS);
    683 		}
    684 		if (top & 0x40) {
    685 			*bits = bitcnt - 1;
    686 			return (ISC_R_SUCCESS);
    687 		}
    688 		if (top & 0x20) {
    689 			*bits = bitcnt - 2;
    690 			return (ISC_R_SUCCESS);
    691 		}
    692 		if (top & 0x10) {
    693 			*bits = bitcnt - 3;
    694 			return (ISC_R_SUCCESS);
    695 		}
    696 		if (top & 0x08) {
    697 			*bits = bitcnt - 4;
    698 			return (ISC_R_SUCCESS);
    699 		}
    700 		if (top & 0x04) {
    701 			*bits = bitcnt - 5;
    702 			return (ISC_R_SUCCESS);
    703 		}
    704 		if (top & 0x02) {
    705 			*bits = bitcnt - 6;
    706 			return (ISC_R_SUCCESS);
    707 		}
    708 		if (top & 0x01) {
    709 			*bits = bitcnt - 7;
    710 			return (ISC_R_SUCCESS);
    711 		}
    712 		break;
    713 	}
    714 	return (ISC_R_RANGE);
    715 }
    716 
    717 CK_ATTRIBUTE *
    718 pk11_attribute_first(const pk11_object_t *obj) {
    719 	return (obj->repr);
    720 }
    721 
    722 CK_ATTRIBUTE *
    723 pk11_attribute_next(const pk11_object_t *obj, CK_ATTRIBUTE *attr) {
    724 	CK_ATTRIBUTE *next;
    725 
    726 	next = attr + 1;
    727 	if ((next - obj->repr) >= obj->attrcnt) {
    728 		return (NULL);
    729 	}
    730 	return (next);
    731 }
    732 
    733 CK_ATTRIBUTE *
    734 pk11_attribute_bytype(const pk11_object_t *obj, CK_ATTRIBUTE_TYPE type) {
    735 	CK_ATTRIBUTE *attr;
    736 
    737 	for (attr = pk11_attribute_first(obj); attr != NULL;
    738 	     attr = pk11_attribute_next(obj, attr))
    739 	{
    740 		if (attr->type == type) {
    741 			return (attr);
    742 		}
    743 	}
    744 	return (NULL);
    745 }
    746 
    747 static char *
    748 percent_decode(char *x, size_t *len) {
    749 	char *p, *c;
    750 	unsigned char v = 0;
    751 
    752 	INSIST(len != NULL);
    753 
    754 	for (p = c = x; p[0] != '\0'; p++, c++) {
    755 		switch (p[0]) {
    756 		case '%':
    757 			switch (p[1]) {
    758 			case '0':
    759 			case '1':
    760 			case '2':
    761 			case '3':
    762 			case '4':
    763 			case '5':
    764 			case '6':
    765 			case '7':
    766 			case '8':
    767 			case '9':
    768 				v = (p[1] - '0') << 4;
    769 				break;
    770 			case 'A':
    771 			case 'B':
    772 			case 'C':
    773 			case 'D':
    774 			case 'E':
    775 			case 'F':
    776 				v = (p[1] - 'A' + 10) << 4;
    777 				break;
    778 			case 'a':
    779 			case 'b':
    780 			case 'c':
    781 			case 'd':
    782 			case 'e':
    783 			case 'f':
    784 				v = (p[1] - 'a' + 10) << 4;
    785 				break;
    786 			default:
    787 				return (NULL);
    788 			}
    789 			switch (p[2]) {
    790 			case '0':
    791 			case '1':
    792 			case '2':
    793 			case '3':
    794 			case '4':
    795 			case '5':
    796 			case '6':
    797 			case '7':
    798 			case '8':
    799 			case '9':
    800 				v |= (p[2] - '0') & 0x0f;
    801 				break;
    802 			case 'A':
    803 			case 'B':
    804 			case 'C':
    805 			case 'D':
    806 			case 'E':
    807 			case 'F':
    808 				v = (p[2] - 'A' + 10) & 0x0f;
    809 				break;
    810 			case 'a':
    811 			case 'b':
    812 			case 'c':
    813 			case 'd':
    814 			case 'e':
    815 			case 'f':
    816 				v = (p[2] - 'a' + 10) & 0x0f;
    817 				break;
    818 			default:
    819 				return (NULL);
    820 			}
    821 			p += 2;
    822 			*c = (char)v;
    823 			(*len)++;
    824 			break;
    825 		default:
    826 			*c = *p;
    827 			(*len)++;
    828 		}
    829 	}
    830 	return (x);
    831 }
    832 
    833 static bool
    834 pk11strcmp(const char *x, size_t lenx, const char *y, size_t leny) {
    835 	char buf[32];
    836 
    837 	INSIST((leny == 32) || (leny == 16));
    838 
    839 	memset(buf, ' ', 32);
    840 	if (lenx > leny) {
    841 		lenx = leny;
    842 	}
    843 	memmove(buf, x, lenx);
    844 	return (memcmp(buf, y, leny) == 0);
    845 }
    846 
    847 static CK_ATTRIBUTE *
    848 push_attribute(pk11_object_t *obj, isc_mem_t *mctx, size_t len) {
    849 	CK_ATTRIBUTE *old = obj->repr;
    850 	CK_ATTRIBUTE *attr;
    851 	CK_BYTE cnt = obj->attrcnt;
    852 
    853 	REQUIRE(old != NULL || cnt == 0);
    854 
    855 	obj->repr = isc_mem_get(mctx, (cnt + 1) * sizeof(*attr));
    856 	memset(obj->repr, 0, (cnt + 1) * sizeof(*attr));
    857 	if (old != NULL) {
    858 		memmove(obj->repr, old, cnt * sizeof(*attr));
    859 	}
    860 	attr = obj->repr + cnt;
    861 	attr->ulValueLen = (CK_ULONG)len;
    862 	attr->pValue = isc_mem_get(mctx, len);
    863 	memset(attr->pValue, 0, len);
    864 	if (old != NULL) {
    865 		memset(old, 0, cnt * sizeof(*attr));
    866 		isc_mem_put(mctx, old, cnt * sizeof(*attr));
    867 	}
    868 	obj->attrcnt++;
    869 	return (attr);
    870 }
    871 
    872 #define DST_RET(a)        \
    873 	{                 \
    874 		ret = a;  \
    875 		goto err; \
    876 	}
    877 
    878 isc_result_t
    879 pk11_parse_uri(pk11_object_t *obj, const char *label, isc_mem_t *mctx,
    880 	       pk11_optype_t optype) {
    881 	CK_ATTRIBUTE *attr;
    882 	pk11_token_t *token = NULL;
    883 	char *uri, *p, *a, *na, *v;
    884 	size_t len, l;
    885 	FILE *stream = NULL;
    886 	char pin[PINLEN + 1];
    887 	bool gotpin = false;
    888 	isc_result_t ret;
    889 
    890 	/* get values to work on */
    891 	len = strlen(label) + 1;
    892 	uri = isc_mem_get(mctx, len);
    893 	memmove(uri, label, len);
    894 
    895 	/* get the URI scheme */
    896 	p = strchr(uri, ':');
    897 	if (p == NULL) {
    898 		DST_RET(PK11_R_NOPROVIDER);
    899 	}
    900 	*p++ = '\0';
    901 	if (strcmp(uri, "pkcs11") != 0) {
    902 		DST_RET(PK11_R_NOPROVIDER);
    903 	}
    904 
    905 	/* get attributes */
    906 	for (na = p; na != NULL;) {
    907 		a = na;
    908 		p = strchr(a, ';');
    909 		if (p == NULL) {
    910 			/* last attribute */
    911 			na = NULL;
    912 		} else {
    913 			*p++ = '\0';
    914 			na = p;
    915 		}
    916 		p = strchr(a, '=');
    917 		if (p != NULL) {
    918 			*p++ = '\0';
    919 			v = p;
    920 		} else {
    921 			v = a;
    922 		}
    923 		l = 0;
    924 		v = percent_decode(v, &l);
    925 		if (v == NULL) {
    926 			DST_RET(PK11_R_NOPROVIDER);
    927 		}
    928 		if ((a == v) || (strcmp(a, "object") == 0)) {
    929 			/* object: CKA_LABEL */
    930 			attr = pk11_attribute_bytype(obj, CKA_LABEL);
    931 			if (attr != NULL) {
    932 				DST_RET(PK11_R_NOPROVIDER);
    933 			}
    934 			attr = push_attribute(obj, mctx, l);
    935 			if (attr == NULL) {
    936 				DST_RET(ISC_R_NOMEMORY);
    937 			}
    938 			attr->type = CKA_LABEL;
    939 			memmove(attr->pValue, v, l);
    940 		} else if (strcmp(a, "token") == 0) {
    941 			/* token: CK_TOKEN_INFO label */
    942 			if (token == NULL) {
    943 				for (token = ISC_LIST_HEAD(tokens);
    944 				     token != NULL;
    945 				     token = ISC_LIST_NEXT(token, link))
    946 				{
    947 					if (pk11strcmp(v, l, token->name, 32)) {
    948 						break;
    949 					}
    950 				}
    951 			}
    952 		} else if (strcmp(a, "manufacturer") == 0) {
    953 			/* manufacturer: CK_TOKEN_INFO manufacturerID */
    954 			if (token == NULL) {
    955 				for (token = ISC_LIST_HEAD(tokens);
    956 				     token != NULL;
    957 				     token = ISC_LIST_NEXT(token, link))
    958 				{
    959 					if (pk11strcmp(v, l, token->manuf, 32))
    960 					{
    961 						break;
    962 					}
    963 				}
    964 			}
    965 		} else if (strcmp(a, "serial") == 0) {
    966 			/* serial: CK_TOKEN_INFO serialNumber */
    967 			if (token == NULL) {
    968 				for (token = ISC_LIST_HEAD(tokens);
    969 				     token != NULL;
    970 				     token = ISC_LIST_NEXT(token, link))
    971 				{
    972 					if (pk11strcmp(v, l, token->serial, 16))
    973 					{
    974 						break;
    975 					}
    976 				}
    977 			}
    978 		} else if (strcmp(a, "model") == 0) {
    979 			/* model: CK_TOKEN_INFO model */
    980 			if (token == NULL) {
    981 				for (token = ISC_LIST_HEAD(tokens);
    982 				     token != NULL;
    983 				     token = ISC_LIST_NEXT(token, link))
    984 				{
    985 					if (pk11strcmp(v, l, token->model, 16))
    986 					{
    987 						break;
    988 					}
    989 				}
    990 			}
    991 		} else if (strcmp(a, "library-manufacturer") == 0) {
    992 			/* ignored */
    993 		} else if (strcmp(a, "library-description") == 0) {
    994 			/* ignored */
    995 		} else if (strcmp(a, "library-version") == 0) {
    996 			/* ignored */
    997 		} else if (strcmp(a, "object-type") == 0) {
    998 			/* object-type: CKA_CLASS */
    999 			/* only private makes sense */
   1000 			if (strcmp(v, "private") != 0) {
   1001 				DST_RET(PK11_R_NOPROVIDER);
   1002 			}
   1003 		} else if (strcmp(a, "id") == 0) {
   1004 			/* id: CKA_ID */
   1005 			attr = pk11_attribute_bytype(obj, CKA_ID);
   1006 			if (attr != NULL) {
   1007 				DST_RET(PK11_R_NOPROVIDER);
   1008 			}
   1009 			attr = push_attribute(obj, mctx, l);
   1010 			if (attr == NULL) {
   1011 				DST_RET(ISC_R_NOMEMORY);
   1012 			}
   1013 			attr->type = CKA_ID;
   1014 			memmove(attr->pValue, v, l);
   1015 		} else if (strcmp(a, "pin-source") == 0) {
   1016 			/* pin-source: PIN */
   1017 			ret = isc_stdio_open(v, "r", &stream);
   1018 			if (ret != ISC_R_SUCCESS) {
   1019 				goto err;
   1020 			}
   1021 			memset(pin, 0, PINLEN + 1);
   1022 			ret = isc_stdio_read(pin, 1, PINLEN + 1, stream, &l);
   1023 			if ((ret != ISC_R_SUCCESS) && (ret != ISC_R_EOF)) {
   1024 				goto err;
   1025 			}
   1026 			if (l > PINLEN) {
   1027 				DST_RET(ISC_R_RANGE);
   1028 			}
   1029 			ret = isc_stdio_close(stream);
   1030 			stream = NULL;
   1031 			if (ret != ISC_R_SUCCESS) {
   1032 				goto err;
   1033 			}
   1034 			gotpin = true;
   1035 		} else {
   1036 			DST_RET(PK11_R_NOPROVIDER);
   1037 		}
   1038 	}
   1039 
   1040 	if ((pk11_attribute_bytype(obj, CKA_LABEL) == NULL) &&
   1041 	    (pk11_attribute_bytype(obj, CKA_ID) == NULL))
   1042 	{
   1043 		DST_RET(ISC_R_NOTFOUND);
   1044 	}
   1045 
   1046 	if (token == NULL) {
   1047 		if (optype == OP_RSA) {
   1048 			token = best_rsa_token;
   1049 		} else if (optype == OP_ECDSA) {
   1050 			token = best_ecdsa_token;
   1051 		} else if (optype == OP_EDDSA) {
   1052 			token = best_eddsa_token;
   1053 		}
   1054 	}
   1055 	if (token == NULL) {
   1056 		DST_RET(ISC_R_NOTFOUND);
   1057 	}
   1058 	obj->slot = token->slotid;
   1059 	if (gotpin) {
   1060 		memmove(token->pin, pin, PINLEN + 1);
   1061 		obj->reqlogon = true;
   1062 	}
   1063 
   1064 	ret = ISC_R_SUCCESS;
   1065 
   1066 err:
   1067 	if (stream != NULL) {
   1068 		(void)isc_stdio_close(stream);
   1069 	}
   1070 	isc_mem_put(mctx, uri, len);
   1071 	return (ret);
   1072 }
   1073 
   1074 void
   1075 pk11_error_fatalcheck(const char *file, int line, const char *funcname,
   1076 		      CK_RV rv) {
   1077 	isc_error_fatal(file, line, "%s: Error = 0x%.8lX\n", funcname, rv);
   1078 }
   1079 
   1080 void
   1081 pk11_dump_tokens(void) {
   1082 	pk11_token_t *token;
   1083 	bool first;
   1084 
   1085 	printf("DEFAULTS\n");
   1086 	printf("\tbest_rsa_token=%p\n", best_rsa_token);
   1087 	printf("\tbest_ecdsa_token=%p\n", best_ecdsa_token);
   1088 	printf("\tbest_eddsa_token=%p\n", best_eddsa_token);
   1089 
   1090 	for (token = ISC_LIST_HEAD(tokens); token != NULL;
   1091 	     token = ISC_LIST_NEXT(token, link))
   1092 	{
   1093 		printf("\nTOKEN\n");
   1094 		printf("\taddress=%p\n", token);
   1095 		printf("\tslotID=%lu\n", token->slotid);
   1096 		printf("\tlabel=%.32s\n", token->name);
   1097 		printf("\tmanufacturerID=%.32s\n", token->manuf);
   1098 		printf("\tmodel=%.16s\n", token->model);
   1099 		printf("\tserialNumber=%.16s\n", token->serial);
   1100 		printf("\tsupported operations=0x%x (", token->operations);
   1101 		first = true;
   1102 		if (token->operations & (1 << OP_RSA)) {
   1103 			first = false;
   1104 			printf("RSA");
   1105 		}
   1106 		if (token->operations & (1 << OP_ECDSA)) {
   1107 			if (!first) {
   1108 				printf(",");
   1109 			}
   1110 			printf("EC");
   1111 		}
   1112 		printf(")\n");
   1113 	}
   1114 }
   1115