xref: /netbsd-src/crypto/external/bsd/openssh/dist/ssh-pkcs11.c (revision cef8759bd76c1b621f8eab8faa6f208faabc2e15)
1 /*	$NetBSD: ssh-pkcs11.c,v 1.20 2020/05/28 17:05:49 christos Exp $	*/
2 /* $OpenBSD: ssh-pkcs11.c,v 1.49 2020/03/13 04:16:27 djm Exp $ */
3 /*
4  * Copyright (c) 2010 Markus Friedl.  All rights reserved.
5  * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #include "includes.h"
20 __RCSID("$NetBSD: ssh-pkcs11.c,v 1.20 2020/05/28 17:05:49 christos Exp $");
21 
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/time.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 
28 #include <ctype.h>
29 #include <string.h>
30 #include <dlfcn.h>
31 
32 #include <openssl/ecdsa.h>
33 #include <openssl/x509.h>
34 #include <openssl/err.h>
35 
36 #define CRYPTOKI_COMPAT
37 #include "pkcs11.h"
38 
39 #include "log.h"
40 #include "misc.h"
41 #include "sshkey.h"
42 #include "ssh-pkcs11.h"
43 #include "xmalloc.h"
44 
45 struct pkcs11_slotinfo {
46 	CK_TOKEN_INFO		token;
47 	CK_SESSION_HANDLE	session;
48 	int			logged_in;
49 };
50 
51 struct pkcs11_provider {
52 	char			*name;
53 	void			*handle;
54 	CK_FUNCTION_LIST	*function_list;
55 	CK_INFO			info;
56 	CK_ULONG		nslots;
57 	CK_SLOT_ID		*slotlist;
58 	struct pkcs11_slotinfo	*slotinfo;
59 	int			valid;
60 	int			refcount;
61 	TAILQ_ENTRY(pkcs11_provider) next;
62 };
63 
64 TAILQ_HEAD(, pkcs11_provider) pkcs11_providers;
65 
66 struct pkcs11_key {
67 	struct pkcs11_provider	*provider;
68 	CK_ULONG		slotidx;
69 	char			*keyid;
70 	int			keyid_len;
71 };
72 
73 int pkcs11_interactive = 0;
74 
75 #ifdef HAVE_DLOPEN
76 static void
77 ossl_error(const char *msg)
78 {
79 	unsigned long    e;
80 
81 	error("%s: %s", __func__, msg);
82 	while ((e = ERR_get_error()) != 0)
83 		error("%s: libcrypto error: %.100s", __func__,
84 		    ERR_error_string(e, NULL));
85 }
86 #endif
87 
88 int
89 pkcs11_init(int interactive)
90 {
91 	pkcs11_interactive = interactive;
92 	TAILQ_INIT(&pkcs11_providers);
93 	return (0);
94 }
95 
96 /*
97  * finalize a provider shared library, it's no longer usable.
98  * however, there might still be keys referencing this provider,
99  * so the actual freeing of memory is handled by pkcs11_provider_unref().
100  * this is called when a provider gets unregistered.
101  */
102 static void
103 pkcs11_provider_finalize(struct pkcs11_provider *p)
104 {
105 	CK_RV rv;
106 	CK_ULONG i;
107 
108 	debug("pkcs11_provider_finalize: %p refcount %d valid %d",
109 	    p, p->refcount, p->valid);
110 	if (!p->valid)
111 		return;
112 	for (i = 0; i < p->nslots; i++) {
113 		if (p->slotinfo[i].session &&
114 		    (rv = p->function_list->C_CloseSession(
115 		    p->slotinfo[i].session)) != CKR_OK)
116 			error("C_CloseSession failed: %lu", rv);
117 	}
118 	if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
119 		error("C_Finalize failed: %lu", rv);
120 	p->valid = 0;
121 	p->function_list = NULL;
122 #ifdef HAVE_DLOPEN
123 	dlclose(p->handle);
124 #endif
125 }
126 
127 /*
128  * remove a reference to the provider.
129  * called when a key gets destroyed or when the provider is unregistered.
130  */
131 static void
132 pkcs11_provider_unref(struct pkcs11_provider *p)
133 {
134 	debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
135 	if (--p->refcount <= 0) {
136 		if (p->valid)
137 			error("pkcs11_provider_unref: %p still valid", p);
138 		free(p->name);
139 		free(p->slotlist);
140 		free(p->slotinfo);
141 		free(p);
142 	}
143 }
144 
145 /* unregister all providers, keys might still point to the providers */
146 void
147 pkcs11_terminate(void)
148 {
149 	struct pkcs11_provider *p;
150 
151 	while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
152 		TAILQ_REMOVE(&pkcs11_providers, p, next);
153 		pkcs11_provider_finalize(p);
154 		pkcs11_provider_unref(p);
155 	}
156 }
157 
158 /* lookup provider by name */
159 static struct pkcs11_provider *
160 pkcs11_provider_lookup(char *provider_id)
161 {
162 	struct pkcs11_provider *p;
163 
164 	TAILQ_FOREACH(p, &pkcs11_providers, next) {
165 		debug("check %p %s", p, p->name);
166 		if (!strcmp(provider_id, p->name))
167 			return (p);
168 	}
169 	return (NULL);
170 }
171 
172 /* unregister provider by name */
173 int
174 pkcs11_del_provider(char *provider_id)
175 {
176 	struct pkcs11_provider *p;
177 
178 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
179 		TAILQ_REMOVE(&pkcs11_providers, p, next);
180 		pkcs11_provider_finalize(p);
181 		pkcs11_provider_unref(p);
182 		return (0);
183 	}
184 	return (-1);
185 }
186 
187 #ifdef HAVE_DLOPEN
188 static RSA_METHOD *rsa_method;
189 static int rsa_idx = 0;
190 static EC_KEY_METHOD *ec_key_method;
191 static int ec_key_idx = 0;
192 
193 /* release a wrapped object */
194 static void
195 pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
196     long argl, void *argp)
197 {
198 	struct pkcs11_key	*k11 = ptr;
199 
200 	debug("%s: parent %p ptr %p idx %d", __func__, parent, ptr, idx);
201 	if (k11 == NULL)
202 		return;
203 	if (k11->provider)
204 		pkcs11_provider_unref(k11->provider);
205 	free(k11->keyid);
206 	free(k11);
207 }
208 
209 /* find a single 'obj' for given attributes */
210 static int
211 pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
212     CK_ULONG nattr, CK_OBJECT_HANDLE *obj)
213 {
214 	CK_FUNCTION_LIST	*f;
215 	CK_SESSION_HANDLE	session;
216 	CK_ULONG		nfound = 0;
217 	CK_RV			rv;
218 	int			ret = -1;
219 
220 	f = p->function_list;
221 	session = p->slotinfo[slotidx].session;
222 	if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
223 		error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
224 		return (-1);
225 	}
226 	if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK ||
227 	    nfound != 1) {
228 		debug("C_FindObjects failed (nfound %lu nattr %lu): %lu",
229 		    nfound, nattr, rv);
230 	} else
231 		ret = 0;
232 	if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
233 		error("C_FindObjectsFinal failed: %lu", rv);
234 	return (ret);
235 }
236 
237 static int
238 pkcs11_login_slot(struct pkcs11_provider *provider, struct pkcs11_slotinfo *si,
239     CK_USER_TYPE type)
240 {
241 	char			*pin = NULL, prompt[1024];
242 	CK_RV			 rv;
243 
244 	if (provider == NULL || si == NULL || !provider->valid) {
245 		error("no pkcs11 (valid) provider found");
246 		return (-1);
247 	}
248 
249 	if (!pkcs11_interactive) {
250 		error("need pin entry%s",
251 		    (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) ?
252 		    " on reader keypad" : "");
253 		return (-1);
254 	}
255 	if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
256 		verbose("Deferring PIN entry to reader keypad.");
257 	else {
258 		snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
259 		    si->token.label);
260 		if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) {
261 			debug("%s: no pin specified", __func__);
262 			return (-1);	/* bail out */
263 		}
264 	}
265 	rv = provider->function_list->C_Login(si->session, type, (u_char *)pin,
266 	    (pin != NULL) ? strlen(pin) : 0);
267 	if (pin != NULL)
268 		freezero(pin, strlen(pin));
269 
270 	switch (rv) {
271 	case CKR_OK:
272 	case CKR_USER_ALREADY_LOGGED_IN:
273 		/* success */
274 		break;
275 	case CKR_PIN_LEN_RANGE:
276 		error("PKCS#11 login failed: PIN length out of range");
277 		return -1;
278 	case CKR_PIN_INCORRECT:
279 		error("PKCS#11 login failed: PIN incorrect");
280 		return -1;
281 	case CKR_PIN_LOCKED:
282 		error("PKCS#11 login failed: PIN locked");
283 		return -1;
284 	default:
285 		error("PKCS#11 login failed: error %lu", rv);
286 		return -1;
287 	}
288 	si->logged_in = 1;
289 	return (0);
290 }
291 
292 static int
293 pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type)
294 {
295 	if (k11 == NULL || k11->provider == NULL || !k11->provider->valid) {
296 		error("no pkcs11 (valid) provider found");
297 		return (-1);
298 	}
299 
300 	return pkcs11_login_slot(k11->provider,
301 	    &k11->provider->slotinfo[k11->slotidx], type);
302 }
303 
304 
305 static int
306 pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj,
307     CK_ATTRIBUTE_TYPE type, int *val)
308 {
309 	struct pkcs11_slotinfo	*si;
310 	CK_FUNCTION_LIST	*f;
311 	CK_BBOOL		flag = 0;
312 	CK_ATTRIBUTE		attr;
313 	CK_RV			 rv;
314 
315 	*val = 0;
316 
317 	if (!k11->provider || !k11->provider->valid) {
318 		error("no pkcs11 (valid) provider found");
319 		return (-1);
320 	}
321 
322 	f = k11->provider->function_list;
323 	si = &k11->provider->slotinfo[k11->slotidx];
324 
325 	attr.type = type;
326 	attr.pValue = &flag;
327 	attr.ulValueLen = sizeof(flag);
328 
329 	rv = f->C_GetAttributeValue(si->session, obj, &attr, 1);
330 	if (rv != CKR_OK) {
331 		error("C_GetAttributeValue failed: %lu", rv);
332 		return (-1);
333 	}
334 	*val = flag != 0;
335 	debug("%s: provider %p slot %lu object %lu: attrib %lu = %d",
336 	    __func__, k11->provider, k11->slotidx, obj, type, *val);
337 	return (0);
338 }
339 
340 static int
341 pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
342 {
343 	struct pkcs11_slotinfo	*si;
344 	CK_FUNCTION_LIST	*f;
345 	CK_OBJECT_HANDLE	 obj;
346 	CK_RV			 rv;
347 	CK_OBJECT_CLASS		 private_key_class;
348 	CK_BBOOL		 true_val;
349 	CK_MECHANISM		 mech;
350 	CK_ATTRIBUTE		 key_filter[3];
351 	int			 always_auth = 0;
352 	int			 did_login = 0;
353 
354 	if (!k11->provider || !k11->provider->valid) {
355 		error("no pkcs11 (valid) provider found");
356 		return (-1);
357 	}
358 
359 	f = k11->provider->function_list;
360 	si = &k11->provider->slotinfo[k11->slotidx];
361 
362 	if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
363 		if (pkcs11_login(k11, CKU_USER) < 0) {
364 			error("login failed");
365 			return (-1);
366 		}
367 		did_login = 1;
368 	}
369 
370 	memset(&key_filter, 0, sizeof(key_filter));
371 	private_key_class = CKO_PRIVATE_KEY;
372 	key_filter[0].type = CKA_CLASS;
373 	key_filter[0].pValue = &private_key_class;
374 	key_filter[0].ulValueLen = sizeof(private_key_class);
375 
376 	key_filter[1].type = CKA_ID;
377 	key_filter[1].pValue = k11->keyid;
378 	key_filter[1].ulValueLen = k11->keyid_len;
379 
380 	true_val = CK_TRUE;
381 	key_filter[2].type = CKA_SIGN;
382 	key_filter[2].pValue = &true_val;
383 	key_filter[2].ulValueLen = sizeof(true_val);
384 
385 	/* try to find object w/CKA_SIGN first, retry w/o */
386 	if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&
387 	    pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {
388 		error("cannot find private key");
389 		return (-1);
390 	}
391 
392 	memset(&mech, 0, sizeof(mech));
393 	mech.mechanism = mech_type;
394 	mech.pParameter = NULL_PTR;
395 	mech.ulParameterLen = 0;
396 
397 	if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
398 		error("C_SignInit failed: %lu", rv);
399 		return (-1);
400 	}
401 
402 	pkcs11_check_obj_bool_attrib(k11, obj, CKA_ALWAYS_AUTHENTICATE,
403 	    &always_auth); /* ignore errors here */
404 	if (always_auth && !did_login) {
405 		debug("%s: always-auth key", __func__);
406 		if (pkcs11_login(k11, CKU_CONTEXT_SPECIFIC) < 0) {
407 			error("login failed for always-auth key");
408 			return (-1);
409 		}
410 	}
411 
412 	return (0);
413 }
414 
415 /* openssl callback doing the actual signing operation */
416 static int
417 pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
418     int padding)
419 {
420 	struct pkcs11_key	*k11;
421 	struct pkcs11_slotinfo	*si;
422 	CK_FUNCTION_LIST	*f;
423 	CK_ULONG		tlen = 0;
424 	CK_RV			rv;
425 	int			rval = -1;
426 
427 	if ((k11 = RSA_get_ex_data(rsa, rsa_idx)) == NULL) {
428 		error("RSA_get_ex_data failed for rsa %p", rsa);
429 		return (-1);
430 	}
431 
432 	if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) {
433 		error("pkcs11_get_key failed");
434 		return (-1);
435 	}
436 
437 	f = k11->provider->function_list;
438 	si = &k11->provider->slotinfo[k11->slotidx];
439 	tlen = RSA_size(rsa);
440 
441 	/* XXX handle CKR_BUFFER_TOO_SMALL */
442 	rv = f->C_Sign(si->session, __UNCONST(from), flen, to, &tlen);
443 	if (rv == CKR_OK)
444 		rval = tlen;
445 	else
446 		error("C_Sign failed: %lu", rv);
447 
448 	return (rval);
449 }
450 
451 static int
452 pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
453     int padding)
454 {
455 	return (-1);
456 }
457 
458 static int
459 pkcs11_rsa_start_wrapper(void)
460 {
461 	if (rsa_method != NULL)
462 		return (0);
463 	rsa_method = RSA_meth_dup(RSA_get_default_method());
464 	if (rsa_method == NULL)
465 		return (-1);
466 	rsa_idx = RSA_get_ex_new_index(0, __UNCONST("ssh-pkcs11-rsa"),
467 	    NULL, NULL, pkcs11_k11_free);
468 	if (rsa_idx == -1)
469 		return (-1);
470 	if (!RSA_meth_set1_name(rsa_method, "pkcs11") ||
471 	    !RSA_meth_set_priv_enc(rsa_method, pkcs11_rsa_private_encrypt) ||
472 	    !RSA_meth_set_priv_dec(rsa_method, pkcs11_rsa_private_decrypt)) {
473 		error("%s: setup pkcs11 method failed", __func__);
474 		return (-1);
475 	}
476 	return (0);
477 }
478 
479 /* redirect private key operations for rsa key to pkcs11 token */
480 static int
481 pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
482     CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
483 {
484 	struct pkcs11_key	*k11;
485 
486 	if (pkcs11_rsa_start_wrapper() == -1)
487 		return (-1);
488 
489 	k11 = xcalloc(1, sizeof(*k11));
490 	k11->provider = provider;
491 	provider->refcount++;	/* provider referenced by RSA key */
492 	k11->slotidx = slotidx;
493 	/* identify key object on smartcard */
494 	k11->keyid_len = keyid_attrib->ulValueLen;
495 	if (k11->keyid_len > 0) {
496 		k11->keyid = xmalloc(k11->keyid_len);
497 		memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
498 	}
499 
500 	RSA_set_method(rsa, rsa_method);
501 	RSA_set_ex_data(rsa, rsa_idx, k11);
502 	return (0);
503 }
504 
505 /* openssl callback doing the actual signing operation */
506 static ECDSA_SIG *
507 ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
508     const BIGNUM *rp, EC_KEY *ec)
509 {
510 	struct pkcs11_key	*k11;
511 	struct pkcs11_slotinfo	*si;
512 	CK_FUNCTION_LIST	*f;
513 	CK_ULONG		siglen = 0, bnlen;
514 	CK_RV			rv;
515 	ECDSA_SIG		*ret = NULL;
516 	u_char			*sig;
517 	BIGNUM			*r = NULL, *s = NULL;
518 
519 	if ((k11 = EC_KEY_get_ex_data(ec, ec_key_idx)) == NULL) {
520 		ossl_error("EC_KEY_get_key_method_data failed for ec");
521 		return (NULL);
522 	}
523 
524 	if (pkcs11_get_key(k11, CKM_ECDSA) == -1) {
525 		error("pkcs11_get_key failed");
526 		return (NULL);
527 	}
528 
529 	f = k11->provider->function_list;
530 	si = &k11->provider->slotinfo[k11->slotidx];
531 
532 	siglen = ECDSA_size(ec);
533 	sig = xmalloc(siglen);
534 
535 	/* XXX handle CKR_BUFFER_TOO_SMALL */
536 	rv = f->C_Sign(si->session, __UNCONST(dgst), dgst_len, sig, &siglen);
537 	if (rv != CKR_OK) {
538 		error("C_Sign failed: %lu", rv);
539 		goto done;
540 	}
541 	if (siglen < 64 || siglen > 132 || siglen % 2) {
542 		ossl_error("d2i_ECDSA_SIG failed");
543 		goto done;
544 	}
545 	bnlen = siglen/2;
546 	if ((ret = ECDSA_SIG_new()) == NULL) {
547 		error("ECDSA_SIG_new failed");
548 		goto done;
549 	}
550 	if ((r = BN_bin2bn(sig, bnlen, NULL)) == NULL ||
551 	    (s = BN_bin2bn(sig+bnlen, bnlen, NULL)) == NULL) {
552 		ossl_error("d2i_ECDSA_SIG failed");
553 		ECDSA_SIG_free(ret);
554 		ret = NULL;
555 		goto done;
556 	}
557 	if (!ECDSA_SIG_set0(ret, r, s)) {
558 		error("%s: ECDSA_SIG_set0 failed", __func__);
559 		ECDSA_SIG_free(ret);
560 		ret = NULL;
561 		goto done;
562 	}
563 	r = s = NULL; /* now owned by ret */
564 	/* success */
565  done:
566 	BN_free(r);
567 	BN_free(s);
568 	free(sig);
569 
570 	return (ret);
571 }
572 
573 static int
574 pkcs11_ecdsa_start_wrapper(void)
575 {
576 	int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
577 	    unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
578 
579 	if (ec_key_method != NULL)
580 		return (0);
581 	ec_key_idx = EC_KEY_get_ex_new_index(0, __UNCONST("ssh-pkcs11-ecdsa"),
582 	    NULL, NULL, pkcs11_k11_free);
583 	if (ec_key_idx == -1)
584 		return (-1);
585 	ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
586 	if (ec_key_method == NULL)
587 		return (-1);
588 	EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL);
589 	EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign);
590 	return (0);
591 }
592 
593 static int
594 pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
595     CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
596 {
597 	struct pkcs11_key	*k11;
598 
599 	if (pkcs11_ecdsa_start_wrapper() == -1)
600 		return (-1);
601 
602 	k11 = xcalloc(1, sizeof(*k11));
603 	k11->provider = provider;
604 	provider->refcount++;	/* provider referenced by ECDSA key */
605 	k11->slotidx = slotidx;
606 	/* identify key object on smartcard */
607 	k11->keyid_len = keyid_attrib->ulValueLen;
608 	k11->keyid = xmalloc(k11->keyid_len);
609 	memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
610 
611 	EC_KEY_set_method(ec, ec_key_method);
612 	EC_KEY_set_ex_data(ec, ec_key_idx, k11);
613 
614 	return (0);
615 }
616 
617 /* remove trailing spaces */
618 static void
619 rmspace(u_char *buf, size_t len)
620 {
621 	size_t i;
622 
623 	if (!len)
624 		return;
625 	for (i = len - 1;  i > 0; i--)
626 		if (i == len - 1 || buf[i] == ' ')
627 			buf[i] = '\0';
628 		else
629 			break;
630 }
631 
632 /*
633  * open a pkcs11 session and login if required.
634  * if pin == NULL we delay login until key use
635  */
636 static int
637 pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
638     CK_ULONG user)
639 {
640 	struct pkcs11_slotinfo	*si;
641 	CK_FUNCTION_LIST	*f;
642 	CK_RV			rv;
643 	CK_SESSION_HANDLE	session;
644 	int			login_required, ret;
645 
646 	f = p->function_list;
647 	si = &p->slotinfo[slotidx];
648 
649 	login_required = si->token.flags & CKF_LOGIN_REQUIRED;
650 
651 	/* fail early before opening session */
652 	if (login_required && !pkcs11_interactive &&
653 	    (pin == NULL || strlen(pin) == 0)) {
654 		error("pin required");
655 		return (-SSH_PKCS11_ERR_PIN_REQUIRED);
656 	}
657 	if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
658 	    CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) {
659 		error("C_OpenSession failed: %lu", rv);
660 		return (-1);
661 	}
662 	if (login_required && pin != NULL && strlen(pin) != 0) {
663 		rv = f->C_Login(session, user, (u_char *)pin, strlen(pin));
664 		if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
665 			error("C_Login failed: %lu", rv);
666 			ret = (rv == CKR_PIN_LOCKED) ?
667 			    -SSH_PKCS11_ERR_PIN_LOCKED :
668 			    -SSH_PKCS11_ERR_LOGIN_FAIL;
669 			if ((rv = f->C_CloseSession(session)) != CKR_OK)
670 				error("C_CloseSession failed: %lu", rv);
671 			return (ret);
672 		}
673 		si->logged_in = 1;
674 	}
675 	si->session = session;
676 	return (0);
677 }
678 
679 static int
680 pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)
681 {
682 	int i;
683 
684 	for (i = 0; i < *nkeys; i++)
685 		if (sshkey_equal(key, (*keysp)[i]))
686 			return (1);
687 	return (0);
688 }
689 
690 static struct sshkey *
691 pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
692     CK_OBJECT_HANDLE *obj)
693 {
694 	CK_ATTRIBUTE		 key_attr[3];
695 	CK_SESSION_HANDLE	 session;
696 	CK_FUNCTION_LIST	*f = NULL;
697 	CK_RV			 rv;
698 	ASN1_OCTET_STRING	*octet = NULL;
699 	EC_KEY			*ec = NULL;
700 	EC_GROUP		*group = NULL;
701 	struct sshkey		*key = NULL;
702 	const unsigned char	*attrp = NULL;
703 	int			 i;
704 	int			 nid;
705 
706 	memset(&key_attr, 0, sizeof(key_attr));
707 	key_attr[0].type = CKA_ID;
708 	key_attr[1].type = CKA_EC_POINT;
709 	key_attr[2].type = CKA_EC_PARAMS;
710 
711 	session = p->slotinfo[slotidx].session;
712 	f = p->function_list;
713 
714 	/* figure out size of the attributes */
715 	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
716 	if (rv != CKR_OK) {
717 		error("C_GetAttributeValue failed: %lu", rv);
718 		return (NULL);
719 	}
720 
721 	/*
722 	 * Allow CKA_ID (always first attribute) to be empty, but
723 	 * ensure that none of the others are zero length.
724 	 * XXX assumes CKA_ID is always first.
725 	 */
726 	if (key_attr[1].ulValueLen == 0 ||
727 	    key_attr[2].ulValueLen == 0) {
728 		error("invalid attribute length");
729 		return (NULL);
730 	}
731 
732 	/* allocate buffers for attributes */
733 	for (i = 0; i < 3; i++)
734 		if (key_attr[i].ulValueLen > 0)
735 			key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
736 
737 	/* retrieve ID, public point and curve parameters of EC key */
738 	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
739 	if (rv != CKR_OK) {
740 		error("C_GetAttributeValue failed: %lu", rv);
741 		goto fail;
742 	}
743 
744 	ec = EC_KEY_new();
745 	if (ec == NULL) {
746 		error("EC_KEY_new failed");
747 		goto fail;
748 	}
749 
750 	attrp = key_attr[2].pValue;
751 	group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
752 	if (group == NULL) {
753 		ossl_error("d2i_ECPKParameters failed");
754 		goto fail;
755 	}
756 
757 	if (EC_KEY_set_group(ec, group) == 0) {
758 		ossl_error("EC_KEY_set_group failed");
759 		goto fail;
760 	}
761 
762 	if (key_attr[1].ulValueLen <= 2) {
763 		error("CKA_EC_POINT too small");
764 		goto fail;
765 	}
766 
767 	attrp = key_attr[1].pValue;
768 	octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
769 	if (octet == NULL) {
770 		ossl_error("d2i_ASN1_OCTET_STRING failed");
771 		goto fail;
772 	}
773 	attrp = octet->data;
774 	if (o2i_ECPublicKey(&ec, &attrp, octet->length) == NULL) {
775 		ossl_error("o2i_ECPublicKey failed");
776 		goto fail;
777 	}
778 
779 	nid = sshkey_ecdsa_key_to_nid(ec);
780 	if (nid < 0) {
781 		error("couldn't get curve nid");
782 		goto fail;
783 	}
784 
785 	if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
786 		goto fail;
787 
788 	key = sshkey_new(KEY_UNSPEC);
789 	if (key == NULL) {
790 		error("sshkey_new failed");
791 		goto fail;
792 	}
793 
794 	key->ecdsa = ec;
795 	key->ecdsa_nid = nid;
796 	key->type = KEY_ECDSA;
797 	key->flags |= SSHKEY_FLAG_EXT;
798 	ec = NULL;	/* now owned by key */
799 
800 fail:
801 	for (i = 0; i < 3; i++)
802 		free(key_attr[i].pValue);
803 	if (ec)
804 		EC_KEY_free(ec);
805 	if (group)
806 		EC_GROUP_free(group);
807 	if (octet)
808 		ASN1_OCTET_STRING_free(octet);
809 
810 	return (key);
811 }
812 
813 static struct sshkey *
814 pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
815     CK_OBJECT_HANDLE *obj)
816 {
817 	CK_ATTRIBUTE		 key_attr[3];
818 	CK_SESSION_HANDLE	 session;
819 	CK_FUNCTION_LIST	*f = NULL;
820 	CK_RV			 rv;
821 	RSA			*rsa = NULL;
822 	BIGNUM			*rsa_n, *rsa_e;
823 	struct sshkey		*key = NULL;
824 	int			 i;
825 
826 	memset(&key_attr, 0, sizeof(key_attr));
827 	key_attr[0].type = CKA_ID;
828 	key_attr[1].type = CKA_MODULUS;
829 	key_attr[2].type = CKA_PUBLIC_EXPONENT;
830 
831 	session = p->slotinfo[slotidx].session;
832 	f = p->function_list;
833 
834 	/* figure out size of the attributes */
835 	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
836 	if (rv != CKR_OK) {
837 		error("C_GetAttributeValue failed: %lu", rv);
838 		return (NULL);
839 	}
840 
841 	/*
842 	 * Allow CKA_ID (always first attribute) to be empty, but
843 	 * ensure that none of the others are zero length.
844 	 * XXX assumes CKA_ID is always first.
845 	 */
846 	if (key_attr[1].ulValueLen == 0 ||
847 	    key_attr[2].ulValueLen == 0) {
848 		error("invalid attribute length");
849 		return (NULL);
850 	}
851 
852 	/* allocate buffers for attributes */
853 	for (i = 0; i < 3; i++)
854 		if (key_attr[i].ulValueLen > 0)
855 			key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
856 
857 	/* retrieve ID, modulus and public exponent of RSA key */
858 	rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
859 	if (rv != CKR_OK) {
860 		error("C_GetAttributeValue failed: %lu", rv);
861 		goto fail;
862 	}
863 
864 	rsa = RSA_new();
865 	if (rsa == NULL) {
866 		error("RSA_new failed");
867 		goto fail;
868 	}
869 
870 	rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
871 	rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
872 	if (rsa_n == NULL || rsa_e == NULL) {
873 		error("BN_bin2bn failed");
874 		goto fail;
875 	}
876 	if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
877 		fatal("%s: set key", __func__);
878 	rsa_n = rsa_e = NULL; /* transferred */
879 
880 	if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
881 		goto fail;
882 
883 	key = sshkey_new(KEY_UNSPEC);
884 	if (key == NULL) {
885 		error("sshkey_new failed");
886 		goto fail;
887 	}
888 
889 	key->rsa = rsa;
890 	key->type = KEY_RSA;
891 	key->flags |= SSHKEY_FLAG_EXT;
892 	rsa = NULL;	/* now owned by key */
893 
894 fail:
895 	for (i = 0; i < 3; i++)
896 		free(key_attr[i].pValue);
897 	RSA_free(rsa);
898 
899 	return (key);
900 }
901 
902 static int
903 pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
904     CK_OBJECT_HANDLE *obj, struct sshkey **keyp, char **labelp)
905 {
906 	CK_ATTRIBUTE		 cert_attr[3];
907 	CK_SESSION_HANDLE	 session;
908 	CK_FUNCTION_LIST	*f = NULL;
909 	CK_RV			 rv;
910 	X509			*x509 = NULL;
911 	X509_NAME		*x509_name = NULL;
912 	EVP_PKEY		*evp;
913 	RSA			*rsa = NULL;
914 	EC_KEY			*ec = NULL;
915 	struct sshkey		*key = NULL;
916 	int			 i;
917 	int			 nid;
918 	const u_char		*cp;
919 	char			*subject = NULL;
920 
921 	*keyp = NULL;
922 	*labelp = NULL;
923 
924 	memset(&cert_attr, 0, sizeof(cert_attr));
925 	cert_attr[0].type = CKA_ID;
926 	cert_attr[1].type = CKA_SUBJECT;
927 	cert_attr[2].type = CKA_VALUE;
928 
929 	session = p->slotinfo[slotidx].session;
930 	f = p->function_list;
931 
932 	/* figure out size of the attributes */
933 	rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
934 	if (rv != CKR_OK) {
935 		error("C_GetAttributeValue failed: %lu", rv);
936 		return -1;
937 	}
938 
939 	/*
940 	 * Allow CKA_ID (always first attribute) to be empty, but
941 	 * ensure that none of the others are zero length.
942 	 * XXX assumes CKA_ID is always first.
943 	 */
944 	if (cert_attr[1].ulValueLen == 0 ||
945 	    cert_attr[2].ulValueLen == 0) {
946 		error("invalid attribute length");
947 		return -1;
948 	}
949 
950 	/* allocate buffers for attributes */
951 	for (i = 0; i < 3; i++)
952 		if (cert_attr[i].ulValueLen > 0)
953 			cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
954 
955 	/* retrieve ID, subject and value of certificate */
956 	rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
957 	if (rv != CKR_OK) {
958 		error("C_GetAttributeValue failed: %lu", rv);
959 		goto out;
960 	}
961 
962 	/* Decode DER-encoded cert subject */
963 	cp = cert_attr[2].pValue;
964 	if ((x509_name = d2i_X509_NAME(NULL, &cp,
965 	    cert_attr[1].ulValueLen)) == NULL ||
966 	    (subject = X509_NAME_oneline(x509_name, NULL, 0)) == NULL)
967 		subject = xstrdup("invalid subject");
968 	X509_NAME_free(x509_name);
969 
970 	cp = cert_attr[2].pValue;
971 	if ((x509 = d2i_X509(NULL, &cp, cert_attr[2].ulValueLen)) == NULL) {
972 		error("d2i_x509 failed");
973 		goto out;
974 	}
975 
976 	if ((evp = X509_get_pubkey(x509)) == NULL) {
977 		error("X509_get_pubkey failed");
978 		goto out;
979 	}
980 
981 	if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) {
982 		if (EVP_PKEY_get0_RSA(evp) == NULL) {
983 			error("invalid x509; no rsa key");
984 			goto out;
985 		}
986 		if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) {
987 			error("RSAPublicKey_dup failed");
988 			goto out;
989 		}
990 
991 		if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
992 			goto out;
993 
994 		key = sshkey_new(KEY_UNSPEC);
995 		if (key == NULL) {
996 			error("sshkey_new failed");
997 			goto out;
998 		}
999 
1000 		key->rsa = rsa;
1001 		key->type = KEY_RSA;
1002 		key->flags |= SSHKEY_FLAG_EXT;
1003 		rsa = NULL;	/* now owned by key */
1004 	} else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) {
1005 		if (EVP_PKEY_get0_EC_KEY(evp) == NULL) {
1006 			error("invalid x509; no ec key");
1007 			goto out;
1008 		}
1009 		if ((ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(evp))) == NULL) {
1010 			error("EC_KEY_dup failed");
1011 			goto out;
1012 		}
1013 
1014 		nid = sshkey_ecdsa_key_to_nid(ec);
1015 		if (nid < 0) {
1016 			error("couldn't get curve nid");
1017 			goto out;
1018 		}
1019 
1020 		if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
1021 			goto out;
1022 
1023 		key = sshkey_new(KEY_UNSPEC);
1024 		if (key == NULL) {
1025 			error("sshkey_new failed");
1026 			goto out;
1027 		}
1028 
1029 		key->ecdsa = ec;
1030 		key->ecdsa_nid = nid;
1031 		key->type = KEY_ECDSA;
1032 		key->flags |= SSHKEY_FLAG_EXT;
1033 		ec = NULL;	/* now owned by key */
1034 	} else {
1035 		error("unknown certificate key type");
1036 		goto out;
1037 	}
1038  out:
1039 	for (i = 0; i < 3; i++)
1040 		free(cert_attr[i].pValue);
1041 	X509_free(x509);
1042 	RSA_free(rsa);
1043 	EC_KEY_free(ec);
1044 	if (key == NULL) {
1045 		free(subject);
1046 		return -1;
1047 	}
1048 	/* success */
1049 	*keyp = key;
1050 	*labelp = subject;
1051 	return 0;
1052 }
1053 
1054 #if 0
1055 static int
1056 have_rsa_key(const RSA *rsa)
1057 {
1058 	const BIGNUM *rsa_n, *rsa_e;
1059 
1060 	RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
1061 	return rsa_n != NULL && rsa_e != NULL;
1062 }
1063 #endif
1064 
1065 /*
1066  * lookup certificates for token in slot identified by slotidx,
1067  * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
1068  * keysp points to an (possibly empty) array with *nkeys keys.
1069  */
1070 static int
1071 pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
1072     struct sshkey ***keysp, char ***labelsp, int *nkeys)
1073 {
1074 	struct sshkey		*key = NULL;
1075 	CK_OBJECT_CLASS		 key_class;
1076 	CK_ATTRIBUTE		 key_attr[1];
1077 	CK_SESSION_HANDLE	 session;
1078 	CK_FUNCTION_LIST	*f = NULL;
1079 	CK_RV			 rv;
1080 	CK_OBJECT_HANDLE	 obj;
1081 	CK_ULONG		 n = 0;
1082 	int			 ret = -1;
1083 	char			*label;
1084 
1085 	memset(&key_attr, 0, sizeof(key_attr));
1086 	memset(&obj, 0, sizeof(obj));
1087 
1088 	key_class = CKO_CERTIFICATE;
1089 	key_attr[0].type = CKA_CLASS;
1090 	key_attr[0].pValue = &key_class;
1091 	key_attr[0].ulValueLen = sizeof(key_class);
1092 
1093 	session = p->slotinfo[slotidx].session;
1094 	f = p->function_list;
1095 
1096 	rv = f->C_FindObjectsInit(session, key_attr, 1);
1097 	if (rv != CKR_OK) {
1098 		error("C_FindObjectsInit failed: %lu", rv);
1099 		goto fail;
1100 	}
1101 
1102 	while (1) {
1103 		CK_CERTIFICATE_TYPE	ck_cert_type;
1104 
1105 		rv = f->C_FindObjects(session, &obj, 1, &n);
1106 		if (rv != CKR_OK) {
1107 			error("C_FindObjects failed: %lu", rv);
1108 			goto fail;
1109 		}
1110 		if (n == 0)
1111 			break;
1112 
1113 		memset(&ck_cert_type, 0, sizeof(ck_cert_type));
1114 		memset(&key_attr, 0, sizeof(key_attr));
1115 		key_attr[0].type = CKA_CERTIFICATE_TYPE;
1116 		key_attr[0].pValue = &ck_cert_type;
1117 		key_attr[0].ulValueLen = sizeof(ck_cert_type);
1118 
1119 		rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
1120 		if (rv != CKR_OK) {
1121 			error("C_GetAttributeValue failed: %lu", rv);
1122 			goto fail;
1123 		}
1124 
1125 		key = NULL;
1126 		label = NULL;
1127 		switch (ck_cert_type) {
1128 		case CKC_X_509:
1129 			if (pkcs11_fetch_x509_pubkey(p, slotidx, &obj,
1130 			    &key, &label) != 0) {
1131 				error("failed to fetch key");
1132 				continue;
1133 			}
1134 			break;
1135 		default:
1136 			error("skipping unsupported certificate type %lu",
1137 			    ck_cert_type);
1138 			continue;
1139 		}
1140 
1141 		if (pkcs11_key_included(keysp, nkeys, key)) {
1142 			sshkey_free(key);
1143 		} else {
1144 			/* expand key array and add key */
1145 			*keysp = xrecallocarray(*keysp, *nkeys,
1146 			    *nkeys + 1, sizeof(struct sshkey *));
1147 			(*keysp)[*nkeys] = key;
1148 			if (labelsp != NULL) {
1149 				*labelsp = xrecallocarray(*labelsp, *nkeys,
1150 				    *nkeys + 1, sizeof(char *));
1151 				(*labelsp)[*nkeys] = xstrdup((char *)label);
1152 			}
1153 			*nkeys = *nkeys + 1;
1154 			debug("have %d keys", *nkeys);
1155 		}
1156 	}
1157 
1158 	ret = 0;
1159 fail:
1160 	rv = f->C_FindObjectsFinal(session);
1161 	if (rv != CKR_OK) {
1162 		error("C_FindObjectsFinal failed: %lu", rv);
1163 		ret = -1;
1164 	}
1165 
1166 	return (ret);
1167 }
1168 
1169 /*
1170  * lookup public keys for token in slot identified by slotidx,
1171  * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
1172  * keysp points to an (possibly empty) array with *nkeys keys.
1173  */
1174 static int
1175 pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
1176     struct sshkey ***keysp, char ***labelsp, int *nkeys)
1177 {
1178 	struct sshkey		*key = NULL;
1179 	CK_OBJECT_CLASS		 key_class;
1180 	CK_ATTRIBUTE		 key_attr[2];
1181 	CK_SESSION_HANDLE	 session;
1182 	CK_FUNCTION_LIST	*f = NULL;
1183 	CK_RV			 rv;
1184 	CK_OBJECT_HANDLE	 obj;
1185 	CK_ULONG		 n = 0;
1186 	int			 ret = -1;
1187 
1188 	memset(&key_attr, 0, sizeof(key_attr));
1189 	memset(&obj, 0, sizeof(obj));
1190 
1191 	key_class = CKO_PUBLIC_KEY;
1192 	key_attr[0].type = CKA_CLASS;
1193 	key_attr[0].pValue = &key_class;
1194 	key_attr[0].ulValueLen = sizeof(key_class);
1195 
1196 	session = p->slotinfo[slotidx].session;
1197 	f = p->function_list;
1198 
1199 	rv = f->C_FindObjectsInit(session, key_attr, 1);
1200 	if (rv != CKR_OK) {
1201 		error("C_FindObjectsInit failed: %lu", rv);
1202 		goto fail;
1203 	}
1204 
1205 	while (1) {
1206 		CK_KEY_TYPE	ck_key_type;
1207 		CK_UTF8CHAR	label[256];
1208 
1209 		rv = f->C_FindObjects(session, &obj, 1, &n);
1210 		if (rv != CKR_OK) {
1211 			error("C_FindObjects failed: %lu", rv);
1212 			goto fail;
1213 		}
1214 		if (n == 0)
1215 			break;
1216 
1217 		memset(&ck_key_type, 0, sizeof(ck_key_type));
1218 		memset(&key_attr, 0, sizeof(key_attr));
1219 		key_attr[0].type = CKA_KEY_TYPE;
1220 		key_attr[0].pValue = &ck_key_type;
1221 		key_attr[0].ulValueLen = sizeof(ck_key_type);
1222 		key_attr[1].type = CKA_LABEL;
1223 		key_attr[1].pValue = &label;
1224 		key_attr[1].ulValueLen = sizeof(label) - 1;
1225 
1226 		rv = f->C_GetAttributeValue(session, obj, key_attr, 2);
1227 		if (rv != CKR_OK) {
1228 			error("C_GetAttributeValue failed: %lu", rv);
1229 			goto fail;
1230 		}
1231 
1232 		label[key_attr[1].ulValueLen] = '\0';
1233 
1234 		switch (ck_key_type) {
1235 		case CKK_RSA:
1236 			key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
1237 			break;
1238 		case CKK_ECDSA:
1239 			key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
1240 			break;
1241 		default:
1242 			/* XXX print key type? */
1243 			key = NULL;
1244 			error("skipping unsupported key type");
1245 		}
1246 
1247 		if (key == NULL) {
1248 			error("failed to fetch key");
1249 			continue;
1250 		}
1251 
1252 		if (pkcs11_key_included(keysp, nkeys, key)) {
1253 			sshkey_free(key);
1254 		} else {
1255 			/* expand key array and add key */
1256 			*keysp = xrecallocarray(*keysp, *nkeys,
1257 			    *nkeys + 1, sizeof(struct sshkey *));
1258 			(*keysp)[*nkeys] = key;
1259 			if (labelsp != NULL) {
1260 				*labelsp = xrecallocarray(*labelsp, *nkeys,
1261 				    *nkeys + 1, sizeof(char *));
1262 				(*labelsp)[*nkeys] = xstrdup((char *)label);
1263 			}
1264 			*nkeys = *nkeys + 1;
1265 			debug("have %d keys", *nkeys);
1266 		}
1267 	}
1268 
1269 	ret = 0;
1270 fail:
1271 	rv = f->C_FindObjectsFinal(session);
1272 	if (rv != CKR_OK) {
1273 		error("C_FindObjectsFinal failed: %lu", rv);
1274 		ret = -1;
1275 	}
1276 
1277 	return (ret);
1278 }
1279 
1280 #ifdef WITH_PKCS11_KEYGEN
1281 #define FILL_ATTR(attr, idx, typ, val, len) \
1282 	{ (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; }
1283 
1284 static struct sshkey *
1285 pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
1286     char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
1287 {
1288 	struct pkcs11_slotinfo	*si;
1289 	char			*plabel = label ? label : "";
1290 	int			 npub = 0, npriv = 0;
1291 	CK_RV			 rv;
1292 	CK_FUNCTION_LIST	*f;
1293 	CK_SESSION_HANDLE	 session;
1294 	CK_BBOOL		 true_val = CK_TRUE, false_val = CK_FALSE;
1295 	CK_OBJECT_HANDLE	 pubKey, privKey;
1296 	CK_ATTRIBUTE		 tpub[16], tpriv[16];
1297 	CK_MECHANISM		 mech = {
1298 	    CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0
1299 	};
1300 	CK_BYTE			 pubExponent[] = {
1301 	    0x01, 0x00, 0x01 /* RSA_F4 in bytes */
1302 	};
1303 
1304 	*err = 0;
1305 
1306 	FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
1307 	FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
1308 	FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
1309 	FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
1310 	FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
1311 	    sizeof(false_val));
1312 	FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
1313 	FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
1314 	FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits));
1315 	FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent,
1316 	    sizeof(pubExponent));
1317 	FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
1318 
1319 	FILL_ATTR(tpriv, npriv, CKA_TOKEN,  &true_val, sizeof(true_val));
1320 	FILL_ATTR(tpriv, npriv, CKA_LABEL,  plabel, strlen(plabel));
1321 	FILL_ATTR(tpriv, npriv, CKA_PRIVATE,  &true_val, sizeof(true_val));
1322 	FILL_ATTR(tpriv, npriv, CKA_SENSITIVE,  &true_val, sizeof(true_val));
1323 	FILL_ATTR(tpriv, npriv, CKA_DECRYPT,  &false_val, sizeof(false_val));
1324 	FILL_ATTR(tpriv, npriv, CKA_SIGN,  &true_val, sizeof(true_val));
1325 	FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER,  &false_val,
1326 	    sizeof(false_val));
1327 	FILL_ATTR(tpriv, npriv, CKA_UNWRAP,  &false_val, sizeof(false_val));
1328 	FILL_ATTR(tpriv, npriv, CKA_DERIVE,  &false_val, sizeof(false_val));
1329 	FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
1330 
1331 	f = p->function_list;
1332 	si = &p->slotinfo[slotidx];
1333 	session = si->session;
1334 
1335 	if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
1336 	    &pubKey, &privKey)) != CKR_OK) {
1337 		error("%s: key generation failed: error 0x%lx", __func__, rv);
1338 		*err = rv;
1339 		return NULL;
1340 	}
1341 
1342 	return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey);
1343 }
1344 
1345 static int
1346 pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen)
1347 {
1348 	size_t	i, len;
1349 	char	ptr[3];
1350 
1351 	if (dest)
1352 		*dest = NULL;
1353 	if (rlen)
1354 		*rlen = 0;
1355 
1356 	if ((len = strlen(hex)) % 2)
1357 		return -1;
1358 	len /= 2;
1359 
1360 	*dest = xmalloc(len);
1361 
1362 	ptr[2] = '\0';
1363 	for (i = 0; i < len; i++) {
1364 		ptr[0] = hex[2 * i];
1365 		ptr[1] = hex[(2 * i) + 1];
1366 		if (!isxdigit(ptr[0]) || !isxdigit(ptr[1]))
1367 			return -1;
1368 		(*dest)[i] = (unsigned char)strtoul(ptr, NULL, 16);
1369 	}
1370 
1371 	if (rlen)
1372 		*rlen = len;
1373 
1374 	return 0;
1375 }
1376 
1377 static struct ec_curve_info {
1378 	const char	*name;
1379 	const char	*oid;
1380 	const char	*oid_encoded;
1381 	size_t		 size;
1382 } ec_curve_infos[] = {
1383 	{"prime256v1",	"1.2.840.10045.3.1.7",	"06082A8648CE3D030107", 256},
1384 	{"secp384r1",	"1.3.132.0.34",		"06052B81040022",	384},
1385 	{"secp521r1",	"1.3.132.0.35",		"06052B81040023",	521},
1386 	{NULL,		NULL,			NULL,			0},
1387 };
1388 
1389 static struct sshkey *
1390 pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
1391     char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
1392 {
1393 	struct pkcs11_slotinfo	*si;
1394 	char			*plabel = label ? label : "";
1395 	int			 i;
1396 	size_t			 ecparams_size;
1397 	unsigned char		*ecparams = NULL;
1398 	int			 npub = 0, npriv = 0;
1399 	CK_RV			 rv;
1400 	CK_FUNCTION_LIST	*f;
1401 	CK_SESSION_HANDLE	 session;
1402 	CK_BBOOL		 true_val = CK_TRUE, false_val = CK_FALSE;
1403 	CK_OBJECT_HANDLE	 pubKey, privKey;
1404 	CK_MECHANISM		 mech = {
1405 	    CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
1406 	};
1407 	CK_ATTRIBUTE		 tpub[16], tpriv[16];
1408 
1409 	*err = 0;
1410 
1411 	for (i = 0; ec_curve_infos[i].name; i++) {
1412 		if (ec_curve_infos[i].size == bits)
1413 			break;
1414 	}
1415 	if (!ec_curve_infos[i].name) {
1416 		error("%s: invalid key size %lu", __func__, bits);
1417 		return NULL;
1418 	}
1419 	if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams,
1420 	    &ecparams_size) == -1) {
1421 		error("%s: invalid oid", __func__);
1422 		return NULL;
1423 	}
1424 
1425 	FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
1426 	FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
1427 	FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
1428 	FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
1429 	FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
1430 	    sizeof(false_val));
1431 	FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
1432 	FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
1433 	FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size);
1434 	FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
1435 
1436 	FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
1437 	FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
1438 	FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
1439 	FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
1440 	FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
1441 	FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
1442 	FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
1443 	    sizeof(false_val));
1444 	FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
1445 	FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
1446 	FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
1447 
1448 	f = p->function_list;
1449 	si = &p->slotinfo[slotidx];
1450 	session = si->session;
1451 
1452 	if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
1453 	    &pubKey, &privKey)) != CKR_OK) {
1454 		error("%s: key generation failed: error 0x%lx", __func__, rv);
1455 		*err = rv;
1456 		return NULL;
1457 	}
1458 
1459 	return pkcs11_fetch_ecdsa_pubkey(p, slotidx, &pubKey);
1460 }
1461 #endif /* WITH_PKCS11_KEYGEN */
1462 
1463 /*
1464  * register a new provider, fails if provider already exists. if
1465  * keyp is provided, fetch keys.
1466  */
1467 static int
1468 pkcs11_register_provider(char *provider_id, char *pin,
1469     struct sshkey ***keyp, char ***labelsp,
1470     struct pkcs11_provider **providerp, CK_ULONG user)
1471 {
1472 	int nkeys, need_finalize = 0;
1473 	int ret = -1;
1474 	struct pkcs11_provider *p = NULL;
1475 	void *handle = NULL;
1476 	CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
1477 	CK_RV rv;
1478 	CK_FUNCTION_LIST *f = NULL;
1479 	CK_TOKEN_INFO *token;
1480 	CK_ULONG i;
1481 
1482 	if (providerp == NULL)
1483 		goto fail;
1484 	*providerp = NULL;
1485 
1486 	if (keyp != NULL)
1487 		*keyp = NULL;
1488 	if (labelsp != NULL)
1489 		*labelsp = NULL;
1490 
1491 	if (pkcs11_provider_lookup(provider_id) != NULL) {
1492 		debug("%s: provider already registered: %s",
1493 		    __func__, provider_id);
1494 		goto fail;
1495 	}
1496 	/* open shared pkcs11-library */
1497 	if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
1498 		error("dlopen %s failed: %s", provider_id, dlerror());
1499 		goto fail;
1500 	}
1501 	if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
1502 		error("dlsym(C_GetFunctionList) failed: %s", dlerror());
1503 		goto fail;
1504 	}
1505 	p = xcalloc(1, sizeof(*p));
1506 	p->name = xstrdup(provider_id);
1507 	p->handle = handle;
1508 	/* setup the pkcs11 callbacks */
1509 	if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
1510 		error("C_GetFunctionList for provider %s failed: %lu",
1511 		    provider_id, rv);
1512 		goto fail;
1513 	}
1514 	p->function_list = f;
1515 	if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
1516 		error("C_Initialize for provider %s failed: %lu",
1517 		    provider_id, rv);
1518 		goto fail;
1519 	}
1520 	need_finalize = 1;
1521 	if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
1522 		error("C_GetInfo for provider %s failed: %lu",
1523 		    provider_id, rv);
1524 		goto fail;
1525 	}
1526 	rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
1527 	rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
1528 	debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d"
1529 	    " libraryDescription <%s> libraryVersion %d.%d",
1530 	    provider_id,
1531 	    p->info.manufacturerID,
1532 	    p->info.cryptokiVersion.major,
1533 	    p->info.cryptokiVersion.minor,
1534 	    p->info.libraryDescription,
1535 	    p->info.libraryVersion.major,
1536 	    p->info.libraryVersion.minor);
1537 	if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
1538 		error("C_GetSlotList failed: %lu", rv);
1539 		goto fail;
1540 	}
1541 	if (p->nslots == 0) {
1542 		debug("%s: provider %s returned no slots", __func__,
1543 		    provider_id);
1544 		ret = -SSH_PKCS11_ERR_NO_SLOTS;
1545 		goto fail;
1546 	}
1547 	p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
1548 	if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
1549 	    != CKR_OK) {
1550 		error("C_GetSlotList for provider %s failed: %lu",
1551 		    provider_id, rv);
1552 		goto fail;
1553 	}
1554 	p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
1555 	p->valid = 1;
1556 	nkeys = 0;
1557 	for (i = 0; i < p->nslots; i++) {
1558 		token = &p->slotinfo[i].token;
1559 		if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
1560 		    != CKR_OK) {
1561 			error("C_GetTokenInfo for provider %s slot %lu "
1562 			    "failed: %lu", provider_id, (unsigned long)i, rv);
1563 			continue;
1564 		}
1565 		if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
1566 			debug2("%s: ignoring uninitialised token in "
1567 			    "provider %s slot %lu", __func__,
1568 			    provider_id, (unsigned long)i);
1569 			continue;
1570 		}
1571 		rmspace(token->label, sizeof(token->label));
1572 		rmspace(token->manufacturerID, sizeof(token->manufacturerID));
1573 		rmspace(token->model, sizeof(token->model));
1574 		rmspace(token->serialNumber, sizeof(token->serialNumber));
1575 		debug("provider %s slot %lu: label <%s> manufacturerID <%s> "
1576 		    "model <%s> serial <%s> flags 0x%lx",
1577 		    provider_id, (unsigned long)i,
1578 		    token->label, token->manufacturerID, token->model,
1579 		    token->serialNumber, token->flags);
1580 		/*
1581 		 * open session, login with pin and retrieve public
1582 		 * keys (if keyp is provided)
1583 		 */
1584 		if ((ret = pkcs11_open_session(p, i, pin, user)) != 0 ||
1585 		    keyp == NULL)
1586 			continue;
1587 		pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
1588 		pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
1589 		if (nkeys == 0 && !p->slotinfo[i].logged_in &&
1590 		    pkcs11_interactive) {
1591 			/*
1592 			 * Some tokens require login before they will
1593 			 * expose keys.
1594 			 */
1595 			if (pkcs11_login_slot(p, &p->slotinfo[i],
1596 			    CKU_USER) < 0) {
1597 				error("login failed");
1598 				continue;
1599 			}
1600 			pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
1601 			pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
1602 		}
1603 	}
1604 
1605 	/* now owned by caller */
1606 	*providerp = p;
1607 
1608 	TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
1609 	p->refcount++;	/* add to provider list */
1610 
1611 	return (nkeys);
1612 fail:
1613 	if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
1614 		error("C_Finalize for provider %s failed: %lu",
1615 		    provider_id, rv);
1616 	if (p) {
1617 		free(p->name);
1618 		free(p->slotlist);
1619 		free(p->slotinfo);
1620 		free(p);
1621 	}
1622 	if (handle)
1623 		dlclose(handle);
1624 	if (ret > 0)
1625 		ret = -1;
1626 	return (ret);
1627 }
1628 
1629 /*
1630  * register a new provider and get number of keys hold by the token,
1631  * fails if provider already exists
1632  */
1633 int
1634 pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp,
1635     char ***labelsp)
1636 {
1637 	struct pkcs11_provider *p = NULL;
1638 	int nkeys;
1639 
1640 	nkeys = pkcs11_register_provider(provider_id, pin, keyp, labelsp,
1641 	    &p, CKU_USER);
1642 
1643 	/* no keys found or some other error, de-register provider */
1644 	if (nkeys <= 0 && p != NULL) {
1645 		TAILQ_REMOVE(&pkcs11_providers, p, next);
1646 		pkcs11_provider_finalize(p);
1647 		pkcs11_provider_unref(p);
1648 	}
1649 	if (nkeys == 0)
1650 		debug("%s: provider %s returned no keys", __func__,
1651 		    provider_id);
1652 
1653 	return (nkeys);
1654 }
1655 
1656 #ifdef WITH_PKCS11_KEYGEN
1657 struct sshkey *
1658 pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
1659     unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err)
1660 {
1661 	struct pkcs11_provider	*p = NULL;
1662 	struct pkcs11_slotinfo	*si;
1663 	CK_FUNCTION_LIST	*f;
1664 	CK_SESSION_HANDLE	 session;
1665 	struct sshkey		*k = NULL;
1666 	int			 ret = -1, reset_pin = 0, reset_provider = 0;
1667 	CK_RV			 rv;
1668 
1669 	*err = 0;
1670 
1671 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
1672 		debug("%s: provider \"%s\" available", __func__, provider_id);
1673 	else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, NULL,
1674 	    &p, CKU_SO)) < 0) {
1675 		debug("%s: could not register provider %s", __func__,
1676 		    provider_id);
1677 		goto out;
1678 	} else
1679 		reset_provider = 1;
1680 
1681 	f = p->function_list;
1682 	si = &p->slotinfo[slotidx];
1683 	session = si->session;
1684 
1685 	if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
1686 	    CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
1687 		debug("%s: could not supply SO pin: %lu", __func__, rv);
1688 		reset_pin = 0;
1689 	} else
1690 		reset_pin = 1;
1691 
1692 	switch (type) {
1693 	case KEY_RSA:
1694 		if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label,
1695 		    bits, keyid, err)) == NULL) {
1696 			debug("%s: failed to generate RSA key", __func__);
1697 			goto out;
1698 		}
1699 		break;
1700 	case KEY_ECDSA:
1701 		if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label,
1702 		    bits, keyid, err)) == NULL) {
1703 			debug("%s: failed to generate ECDSA key", __func__);
1704 			goto out;
1705 		}
1706 		break;
1707 	default:
1708 		*err = SSH_PKCS11_ERR_GENERIC;
1709 		debug("%s: unknown type %d", __func__, type);
1710 		goto out;
1711 	}
1712 
1713 out:
1714 	if (reset_pin)
1715 		f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
1716 		    CK_INVALID_HANDLE);
1717 
1718 	if (reset_provider)
1719 		pkcs11_del_provider(provider_id);
1720 
1721 	return (k);
1722 }
1723 
1724 struct sshkey *
1725 pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
1726     unsigned char keyid, u_int32_t *err)
1727 {
1728 	struct pkcs11_provider	*p = NULL;
1729 	struct pkcs11_slotinfo	*si;
1730 	struct sshkey		*k = NULL;
1731 	int			 reset_pin = 0, reset_provider = 0;
1732 	CK_ULONG		 nattrs;
1733 	CK_FUNCTION_LIST	*f;
1734 	CK_SESSION_HANDLE	 session;
1735 	CK_ATTRIBUTE		 attrs[16];
1736 	CK_OBJECT_CLASS		 key_class;
1737 	CK_KEY_TYPE		 key_type;
1738 	CK_OBJECT_HANDLE	 obj = CK_INVALID_HANDLE;
1739 	CK_RV			 rv;
1740 
1741 	*err = 0;
1742 
1743 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
1744 		debug("%s: using provider \"%s\"", __func__, provider_id);
1745 	} else if (pkcs11_register_provider(provider_id, pin, NULL, NULL, &p,
1746 	    CKU_SO) < 0) {
1747 		debug("%s: could not register provider %s", __func__,
1748 		    provider_id);
1749 		goto out;
1750 	} else
1751 		reset_provider = 1;
1752 
1753 	f = p->function_list;
1754 	si = &p->slotinfo[slotidx];
1755 	session = si->session;
1756 
1757 	if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
1758 	    CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
1759 		debug("%s: could not supply SO pin: %lu", __func__, rv);
1760 		reset_pin = 0;
1761 	} else
1762 		reset_pin = 1;
1763 
1764 	/* private key */
1765 	nattrs = 0;
1766 	key_class = CKO_PRIVATE_KEY;
1767 	FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
1768 	FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
1769 
1770 	if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
1771 	    obj != CK_INVALID_HANDLE) {
1772 		if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
1773 			debug("%s: could not destroy private key 0x%hhx",
1774 			    __func__, keyid);
1775 			*err = rv;
1776 			goto out;
1777 		}
1778 	}
1779 
1780 	/* public key */
1781 	nattrs = 0;
1782 	key_class = CKO_PUBLIC_KEY;
1783 	FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
1784 	FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
1785 
1786 	if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
1787 	    obj != CK_INVALID_HANDLE) {
1788 
1789 		/* get key type */
1790 		nattrs = 0;
1791 		FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type,
1792 		    sizeof(key_type));
1793 		rv = f->C_GetAttributeValue(session, obj, attrs, nattrs);
1794 		if (rv != CKR_OK) {
1795 			debug("%s: could not get key type of public key 0x%hhx",
1796 			    __func__, keyid);
1797 			*err = rv;
1798 			key_type = -1;
1799 		}
1800 		if (key_type == CKK_RSA)
1801 			k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
1802 		else if (key_type == CKK_ECDSA)
1803 			k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
1804 
1805 		if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
1806 			debug("%s: could not destroy public key 0x%hhx",
1807 			    __func__, keyid);
1808 			*err = rv;
1809 			goto out;
1810 		}
1811 	}
1812 
1813 out:
1814 	if (reset_pin)
1815 		f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
1816 		    CK_INVALID_HANDLE);
1817 
1818 	if (reset_provider)
1819 		pkcs11_del_provider(provider_id);
1820 
1821 	return (k);
1822 }
1823 #endif /* WITH_PKCS11_KEYGEN */
1824 #else
1825 int
1826 pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
1827 {
1828 	error("dlopen() not supported");
1829 	return (-1);
1830 }
1831 #endif /* HAVE_DLOPEN */
1832