blob: b49034952c04c7c6155e033c576afc8d5778953b [file] [log] [blame]
djm@openbsd.orgfcb1b092019-01-20 23:12:35 +00001/* $OpenBSD: ssh-pkcs11.c,v 1.36 2019/01/20 23:12:35 djm Exp $ */
Damien Miller7ea845e2010-02-12 09:21:02 +11002/*
3 * Copyright (c) 2010 Markus Friedl. All rights reserved.
djm@openbsd.org93f02102019-01-20 22:51:37 +00004 * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
Damien Miller7ea845e2010-02-12 09:21:02 +11005 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
Damien Miller8ad0fbd2010-02-12 09:49:06 +110019#include "includes.h"
20
Damien Millerdfa41562010-02-12 10:06:28 +110021#ifdef ENABLE_PKCS11
22
Damien Miller8ad0fbd2010-02-12 09:49:06 +110023#ifdef HAVE_SYS_TIME_H
24# include <sys/time.h>
25#endif
djm@openbsd.org93f02102019-01-20 22:51:37 +000026
27#include <sys/types.h>
Damien Miller7ea845e2010-02-12 09:21:02 +110028#include <stdarg.h>
29#include <stdio.h>
30
djm@openbsd.org93f02102019-01-20 22:51:37 +000031#include <ctype.h>
Damien Miller7ea845e2010-02-12 09:21:02 +110032#include <string.h>
33#include <dlfcn.h>
34
Damien Miller8ad0fbd2010-02-12 09:49:06 +110035#include "openbsd-compat/sys-queue.h"
Damien Miller48f54b92018-09-13 12:13:50 +100036#include "openbsd-compat/openssl-compat.h"
Damien Miller8ad0fbd2010-02-12 09:49:06 +110037
djm@openbsd.org93f02102019-01-20 22:51:37 +000038#include <openssl/ecdsa.h>
Damien Millerd2252c72013-11-04 07:41:48 +110039#include <openssl/x509.h>
djm@openbsd.org93f02102019-01-20 22:51:37 +000040#include <openssl/err.h>
Damien Millerd2252c72013-11-04 07:41:48 +110041
Damien Miller7ea845e2010-02-12 09:21:02 +110042#define CRYPTOKI_COMPAT
43#include "pkcs11.h"
44
45#include "log.h"
46#include "misc.h"
djm@openbsd.org1129dcf2015-01-15 09:40:00 +000047#include "sshkey.h"
Damien Miller7ea845e2010-02-12 09:21:02 +110048#include "ssh-pkcs11.h"
49#include "xmalloc.h"
50
51struct pkcs11_slotinfo {
52 CK_TOKEN_INFO token;
53 CK_SESSION_HANDLE session;
54 int logged_in;
55};
56
57struct pkcs11_provider {
58 char *name;
59 void *handle;
60 CK_FUNCTION_LIST *function_list;
61 CK_INFO info;
62 CK_ULONG nslots;
63 CK_SLOT_ID *slotlist;
64 struct pkcs11_slotinfo *slotinfo;
65 int valid;
66 int refcount;
67 TAILQ_ENTRY(pkcs11_provider) next;
68};
69
70TAILQ_HEAD(, pkcs11_provider) pkcs11_providers;
71
72struct pkcs11_key {
73 struct pkcs11_provider *provider;
74 CK_ULONG slotidx;
Damien Miller7ea845e2010-02-12 09:21:02 +110075 char *keyid;
76 int keyid_len;
77};
78
79int pkcs11_interactive = 0;
80
djm@openbsd.org93f02102019-01-20 22:51:37 +000081#ifdef HAVE_DLOPEN
82static void
83ossl_error(const char *msg)
84{
djm@openbsd.org65294092019-01-20 23:11:11 +000085 unsigned long e;
djm@openbsd.org93f02102019-01-20 22:51:37 +000086
djm@openbsd.org65294092019-01-20 23:11:11 +000087 while ((e = ERR_get_error()) != 0)
88 error("%s: %s: %.100s", __func__, msg,
89 ERR_error_string(e, NULL));
djm@openbsd.org93f02102019-01-20 22:51:37 +000090}
91#endif
92
Damien Miller7ea845e2010-02-12 09:21:02 +110093int
94pkcs11_init(int interactive)
95{
96 pkcs11_interactive = interactive;
97 TAILQ_INIT(&pkcs11_providers);
98 return (0);
99}
100
101/*
djm@openbsd.org93f02102019-01-20 22:51:37 +0000102 * finalize a provider shared library, it's no longer usable.
Damien Miller7ea845e2010-02-12 09:21:02 +1100103 * however, there might still be keys referencing this provider,
djm@openbsd.org93f02102019-01-20 22:51:37 +0000104 * so the actual freeing of memory is handled by pkcs11_provider_unref().
Damien Miller7ea845e2010-02-12 09:21:02 +1100105 * this is called when a provider gets unregistered.
106 */
107static void
108pkcs11_provider_finalize(struct pkcs11_provider *p)
109{
110 CK_RV rv;
111 CK_ULONG i;
112
113 debug("pkcs11_provider_finalize: %p refcount %d valid %d",
114 p, p->refcount, p->valid);
115 if (!p->valid)
116 return;
117 for (i = 0; i < p->nslots; i++) {
118 if (p->slotinfo[i].session &&
119 (rv = p->function_list->C_CloseSession(
120 p->slotinfo[i].session)) != CKR_OK)
121 error("C_CloseSession failed: %lu", rv);
122 }
123 if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
124 error("C_Finalize failed: %lu", rv);
125 p->valid = 0;
126 p->function_list = NULL;
127 dlclose(p->handle);
128}
129
130/*
131 * remove a reference to the provider.
132 * called when a key gets destroyed or when the provider is unregistered.
133 */
134static void
135pkcs11_provider_unref(struct pkcs11_provider *p)
136{
137 debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
138 if (--p->refcount <= 0) {
139 if (p->valid)
140 error("pkcs11_provider_unref: %p still valid", p);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000141 free(p->name);
Darren Tuckera627d422013-06-02 07:31:17 +1000142 free(p->slotlist);
143 free(p->slotinfo);
144 free(p);
Damien Miller7ea845e2010-02-12 09:21:02 +1100145 }
146}
147
148/* unregister all providers, keys might still point to the providers */
149void
150pkcs11_terminate(void)
151{
152 struct pkcs11_provider *p;
153
154 while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
155 TAILQ_REMOVE(&pkcs11_providers, p, next);
156 pkcs11_provider_finalize(p);
157 pkcs11_provider_unref(p);
158 }
159}
160
161/* lookup provider by name */
162static struct pkcs11_provider *
163pkcs11_provider_lookup(char *provider_id)
164{
165 struct pkcs11_provider *p;
166
167 TAILQ_FOREACH(p, &pkcs11_providers, next) {
168 debug("check %p %s", p, p->name);
169 if (!strcmp(provider_id, p->name))
170 return (p);
171 }
172 return (NULL);
173}
174
175/* unregister provider by name */
176int
177pkcs11_del_provider(char *provider_id)
178{
179 struct pkcs11_provider *p;
180
181 if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
182 TAILQ_REMOVE(&pkcs11_providers, p, next);
183 pkcs11_provider_finalize(p);
184 pkcs11_provider_unref(p);
185 return (0);
186 }
187 return (-1);
188}
189
djm@openbsd.org58622a82019-01-20 23:10:33 +0000190/* release a wrapped object */
191static void
192pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
193 long argl, void *argp)
Damien Miller7ea845e2010-02-12 09:21:02 +1100194{
djm@openbsd.org58622a82019-01-20 23:10:33 +0000195 struct pkcs11_key *k11 = ptr;
Damien Miller7ea845e2010-02-12 09:21:02 +1100196
djm@openbsd.org58622a82019-01-20 23:10:33 +0000197 debug("%s: parent %p ptr %p idx %d", __func__, parent, ptr, idx);
198 if (k11 == NULL)
199 return;
200 if (k11->provider)
201 pkcs11_provider_unref(k11->provider);
202 free(k11->keyid);
203 free(k11);
Damien Miller7ea845e2010-02-12 09:21:02 +1100204}
205
Damien Miller031c9102010-04-16 15:54:44 +1000206/* find a single 'obj' for given attributes */
207static int
208pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
209 CK_ULONG nattr, CK_OBJECT_HANDLE *obj)
210{
211 CK_FUNCTION_LIST *f;
212 CK_SESSION_HANDLE session;
213 CK_ULONG nfound = 0;
214 CK_RV rv;
215 int ret = -1;
216
217 f = p->function_list;
218 session = p->slotinfo[slotidx].session;
219 if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
220 error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
221 return (-1);
222 }
223 if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK ||
224 nfound != 1) {
225 debug("C_FindObjects failed (nfound %lu nattr %lu): %lu",
226 nfound, nattr, rv);
227 } else
228 ret = 0;
229 if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
230 error("C_FindObjectsFinal failed: %lu", rv);
231 return (ret);
232}
233
Damien Miller7ea845e2010-02-12 09:21:02 +1100234static int
djm@openbsd.org93f02102019-01-20 22:51:37 +0000235pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
Damien Miller7ea845e2010-02-12 09:21:02 +1100236{
Damien Miller7ea845e2010-02-12 09:21:02 +1100237 struct pkcs11_slotinfo *si;
238 CK_FUNCTION_LIST *f;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000239 CK_OBJECT_HANDLE obj;
240 CK_RV rv;
241 CK_OBJECT_CLASS private_key_class;
242 CK_BBOOL true_val;
243 CK_MECHANISM mech;
244 CK_ATTRIBUTE key_filter[3];
djm@openbsd.orga71ba582015-05-27 05:15:02 +0000245 char *pin = NULL, prompt[1024];
Damien Miller7ea845e2010-02-12 09:21:02 +1100246
Damien Miller7ea845e2010-02-12 09:21:02 +1100247 if (!k11->provider || !k11->provider->valid) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000248 error("no pkcs11 (valid) provider found");
Damien Miller7ea845e2010-02-12 09:21:02 +1100249 return (-1);
250 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000251
Damien Miller7ea845e2010-02-12 09:21:02 +1100252 f = k11->provider->function_list;
253 si = &k11->provider->slotinfo[k11->slotidx];
djm@openbsd.org93f02102019-01-20 22:51:37 +0000254
Damien Miller7ea845e2010-02-12 09:21:02 +1100255 if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
256 if (!pkcs11_interactive) {
djm@openbsd.orga71ba582015-05-27 05:15:02 +0000257 error("need pin entry%s", (si->token.flags &
258 CKF_PROTECTED_AUTHENTICATION_PATH) ?
259 " on reader keypad" : "");
Damien Miller7ea845e2010-02-12 09:21:02 +1100260 return (-1);
261 }
djm@openbsd.orga71ba582015-05-27 05:15:02 +0000262 if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
263 verbose("Deferring PIN entry to reader keypad.");
264 else {
265 snprintf(prompt, sizeof(prompt),
266 "Enter PIN for '%s': ", si->token.label);
267 pin = read_passphrase(prompt, RP_ALLOW_EOF);
268 if (pin == NULL)
269 return (-1); /* bail out */
270 }
271 rv = f->C_Login(si->session, CKU_USER, (u_char *)pin,
272 (pin != NULL) ? strlen(pin) : 0);
273 if (pin != NULL) {
274 explicit_bzero(pin, strlen(pin));
Darren Tuckera627d422013-06-02 07:31:17 +1000275 free(pin);
djm@openbsd.orga71ba582015-05-27 05:15:02 +0000276 }
277 if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100278 error("C_Login failed: %lu", rv);
279 return (-1);
280 }
Damien Miller7ea845e2010-02-12 09:21:02 +1100281 si->logged_in = 1;
282 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000283
284 memset(&key_filter, 0, sizeof(key_filter));
285 private_key_class = CKO_PRIVATE_KEY;
286 key_filter[0].type = CKA_CLASS;
287 key_filter[0].pValue = &private_key_class;
288 key_filter[0].ulValueLen = sizeof(private_key_class);
289
290 key_filter[1].type = CKA_ID;
Damien Miller7ea845e2010-02-12 09:21:02 +1100291 key_filter[1].pValue = k11->keyid;
292 key_filter[1].ulValueLen = k11->keyid_len;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000293
294 true_val = CK_TRUE;
295 key_filter[2].type = CKA_SIGN;
296 key_filter[2].pValue = &true_val;
297 key_filter[2].ulValueLen = sizeof(true_val);
298
Damien Miller031c9102010-04-16 15:54:44 +1000299 /* try to find object w/CKA_SIGN first, retry w/o */
300 if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&
301 pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {
302 error("cannot find private key");
djm@openbsd.org93f02102019-01-20 22:51:37 +0000303 return (-1);
Damien Miller7ea845e2010-02-12 09:21:02 +1100304 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000305
306 memset(&mech, 0, sizeof(mech));
307 mech.mechanism = mech_type;
308 mech.pParameter = NULL_PTR;
309 mech.ulParameterLen = 0;
310
311 if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
312 error("C_SignInit failed: %lu", rv);
313 return (-1);
314 }
315
316 return (0);
317}
318
319/* openssl callback doing the actual signing operation */
320static int
321pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
322 int padding)
323{
324 struct pkcs11_key *k11;
325 struct pkcs11_slotinfo *si;
326 CK_FUNCTION_LIST *f;
327 CK_ULONG tlen = 0;
328 CK_RV rv;
329 int rval = -1;
330
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000331 if ((k11 = RSA_get_ex_data(rsa, 0)) == NULL) {
332 error("RSA_get_ex_data failed for rsa %p", rsa);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000333 return (-1);
334 }
335
336 if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) {
337 error("pkcs11_get_key failed");
338 return (-1);
339 }
340
341 f = k11->provider->function_list;
342 si = &k11->provider->slotinfo[k11->slotidx];
343 tlen = RSA_size(rsa);
344
345 /* XXX handle CKR_BUFFER_TOO_SMALL */
346 rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
347 if (rv == CKR_OK)
348 rval = tlen;
349 else
350 error("C_Sign failed: %lu", rv);
351
Damien Miller7ea845e2010-02-12 09:21:02 +1100352 return (rval);
353}
354
355static int
356pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
357 int padding)
358{
359 return (-1);
360}
361
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000362static RSA_METHOD *rsa_method;
djm@openbsd.org58622a82019-01-20 23:10:33 +0000363static int rsa_idx = 0;
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000364
365static int
366pkcs11_rsa_start_wrapper(void)
367{
368 if (rsa_method != NULL)
369 return (0);
370 rsa_method = RSA_meth_dup(RSA_get_default_method());
371 if (rsa_method == NULL)
372 return (-1);
djm@openbsd.org58622a82019-01-20 23:10:33 +0000373 rsa_idx = RSA_get_ex_new_index(0, "ssh-pkcs11-rsa",
374 NULL, NULL, pkcs11_k11_free);
375 if (rsa_idx == -1)
376 return (-1);
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000377 if (!RSA_meth_set1_name(rsa_method, "pkcs11") ||
378 !RSA_meth_set_priv_enc(rsa_method, pkcs11_rsa_private_encrypt) ||
djm@openbsd.org58622a82019-01-20 23:10:33 +0000379 !RSA_meth_set_priv_dec(rsa_method, pkcs11_rsa_private_decrypt)) {
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000380 error("%s: setup pkcs11 method failed", __func__);
381 return (-1);
382 }
383 return (0);
384}
385
Damien Miller7ea845e2010-02-12 09:21:02 +1100386/* redirect private key operations for rsa key to pkcs11 token */
387static int
388pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
389 CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
390{
391 struct pkcs11_key *k11;
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000392
393 if (pkcs11_rsa_start_wrapper() == -1)
394 return (-1);
Damien Miller7ea845e2010-02-12 09:21:02 +1100395
396 k11 = xcalloc(1, sizeof(*k11));
397 k11->provider = provider;
398 provider->refcount++; /* provider referenced by RSA key */
399 k11->slotidx = slotidx;
400 /* identify key object on smartcard */
401 k11->keyid_len = keyid_attrib->ulValueLen;
djm@openbsd.orgd2d772f2016-02-12 00:20:30 +0000402 if (k11->keyid_len > 0) {
403 k11->keyid = xmalloc(k11->keyid_len);
404 memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
405 }
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000406
djm@openbsd.orgfcb1b092019-01-20 23:12:35 +0000407 RSA_set_method(rsa, rsa_method);
djm@openbsd.org58622a82019-01-20 23:10:33 +0000408 RSA_set_ex_data(rsa, rsa_idx, k11);
Damien Miller7ea845e2010-02-12 09:21:02 +1100409 return (0);
410}
411
djm@openbsd.org93f02102019-01-20 22:51:37 +0000412/* openssl callback doing the actual signing operation */
413static ECDSA_SIG *
414ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
415 const BIGNUM *rp, EC_KEY *ec)
416{
417 struct pkcs11_key *k11;
418 struct pkcs11_slotinfo *si;
419 CK_FUNCTION_LIST *f;
420 CK_ULONG siglen = 0, bnlen;
421 CK_RV rv;
422 ECDSA_SIG *ret = NULL;
423 u_char *sig;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000424
425 if ((k11 = EC_KEY_get_ex_data(ec, 0)) == NULL) {
426 ossl_error("EC_KEY_get_key_method_data failed for ec");
427 return (NULL);
428 }
429
430 if (pkcs11_get_key(k11, CKM_ECDSA) == -1) {
431 error("pkcs11_get_key failed");
432 return (NULL);
433 }
434
435 f = k11->provider->function_list;
436 si = &k11->provider->slotinfo[k11->slotidx];
437
438 siglen = ECDSA_size(ec);
439 sig = xmalloc(siglen);
440
441 /* XXX handle CKR_BUFFER_TOO_SMALL */
442 rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen);
443 if (rv != CKR_OK) {
444 error("C_Sign failed: %lu", rv);
445 goto done;
446 }
djm@openbsd.org749aef32019-01-20 23:00:12 +0000447 if (siglen < 64 || siglen > 132 || siglen % 2) {
448 ossl_error("d2i_ECDSA_SIG failed");
449 goto done;
450 }
451 bnlen = siglen/2;
452 if ((ret = ECDSA_SIG_new()) == NULL) {
453 error("ECDSA_SIG_new failed");
454 goto done;
455 }
456 if (BN_bin2bn(sig, bnlen, ret->r) == NULL ||
457 BN_bin2bn(sig+bnlen, bnlen, ret->s) == NULL) {
458 ossl_error("d2i_ECDSA_SIG failed");
459 ECDSA_SIG_free(ret);
460 ret = NULL;
461 goto done;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000462 }
463 done:
464 free(sig);
465
466 return (ret);
467}
468
469static EC_KEY_METHOD *ec_key_method;
djm@openbsd.org445cfce2019-01-20 23:05:52 +0000470static int ec_key_idx = 0;
471
djm@openbsd.org93f02102019-01-20 22:51:37 +0000472static int
473pkcs11_ecdsa_start_wrapper(void)
474{
475 int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
476 unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
477
478 if (ec_key_method != NULL)
479 return (0);
djm@openbsd.org445cfce2019-01-20 23:05:52 +0000480 ec_key_idx = EC_KEY_get_ex_new_index(0, "ssh-pkcs11-ecdsa",
481 NULL, NULL, pkcs11_k11_free);
482 if (ec_key_idx == -1)
483 return (-1);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000484 ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
485 if (ec_key_method == NULL)
486 return (-1);
487 EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL);
488 EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign);
489 return (0);
490}
491
492static int
493pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
494 CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
495{
496 struct pkcs11_key *k11;
497
498 if (pkcs11_ecdsa_start_wrapper() == -1)
499 return (-1);
500
501 k11 = xcalloc(1, sizeof(*k11));
502 k11->provider = provider;
503 provider->refcount++; /* provider referenced by ECDSA key */
504 k11->slotidx = slotidx;
505 /* identify key object on smartcard */
506 k11->keyid_len = keyid_attrib->ulValueLen;
507 k11->keyid = xmalloc(k11->keyid_len);
508 memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000509
djm@openbsd.orgfcb1b092019-01-20 23:12:35 +0000510 EC_KEY_set_method(ec, ec_key_method);
djm@openbsd.org445cfce2019-01-20 23:05:52 +0000511 EC_KEY_set_ex_data(ec, ec_key_idx, k11);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000512
513 return (0);
514}
515
Damien Miller7ea845e2010-02-12 09:21:02 +1100516/* remove trailing spaces */
517static void
Damien Miller746d1a62013-07-18 16:13:02 +1000518rmspace(u_char *buf, size_t len)
Damien Miller7ea845e2010-02-12 09:21:02 +1100519{
520 size_t i;
521
522 if (!len)
523 return;
524 for (i = len - 1; i > 0; i--)
525 if (i == len - 1 || buf[i] == ' ')
526 buf[i] = '\0';
527 else
528 break;
529}
530
531/*
532 * open a pkcs11 session and login if required.
533 * if pin == NULL we delay login until key use
534 */
535static int
djm@openbsd.org93f02102019-01-20 22:51:37 +0000536pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
537 CK_ULONG user)
Damien Miller7ea845e2010-02-12 09:21:02 +1100538{
539 CK_RV rv;
540 CK_FUNCTION_LIST *f;
541 CK_SESSION_HANDLE session;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000542 int login_required, ret;
Damien Miller7ea845e2010-02-12 09:21:02 +1100543
544 f = p->function_list;
545 login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED;
546 if (pin && login_required && !strlen(pin)) {
547 error("pin required");
djm@openbsd.org93f02102019-01-20 22:51:37 +0000548 return (-SSH_PKCS11_ERR_PIN_REQUIRED);
Damien Miller7ea845e2010-02-12 09:21:02 +1100549 }
550 if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
551 CKF_SERIAL_SESSION, NULL, NULL, &session))
552 != CKR_OK) {
553 error("C_OpenSession failed: %lu", rv);
554 return (-1);
555 }
556 if (login_required && pin) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000557 rv = f->C_Login(session, user,
deraadt@openbsd.orgce4f59b2015-02-03 08:07:20 +0000558 (u_char *)pin, strlen(pin));
djm@openbsd.orgcb3bde32015-02-02 22:48:53 +0000559 if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100560 error("C_Login failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000561 ret = (rv == CKR_PIN_LOCKED) ?
562 -SSH_PKCS11_ERR_PIN_LOCKED :
563 -SSH_PKCS11_ERR_LOGIN_FAIL;
Damien Miller7ea845e2010-02-12 09:21:02 +1100564 if ((rv = f->C_CloseSession(session)) != CKR_OK)
565 error("C_CloseSession failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000566 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +1100567 }
568 p->slotinfo[slotidx].logged_in = 1;
569 }
570 p->slotinfo[slotidx].session = session;
571 return (0);
572}
573
Damien Millerd2252c72013-11-04 07:41:48 +1100574static int
djm@openbsd.org1129dcf2015-01-15 09:40:00 +0000575pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)
Damien Millerd2252c72013-11-04 07:41:48 +1100576{
577 int i;
578
579 for (i = 0; i < *nkeys; i++)
djm@openbsd.org1129dcf2015-01-15 09:40:00 +0000580 if (sshkey_equal(key, (*keysp)[i]))
Damien Millerd2252c72013-11-04 07:41:48 +1100581 return (1);
582 return (0);
583}
584
djm@openbsd.org93f02102019-01-20 22:51:37 +0000585static struct sshkey *
586pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
587 CK_OBJECT_HANDLE *obj)
588{
589 CK_ATTRIBUTE key_attr[3];
590 CK_SESSION_HANDLE session;
591 CK_FUNCTION_LIST *f = NULL;
592 CK_RV rv;
djm@openbsd.org24757c12019-01-20 23:01:59 +0000593 ASN1_OCTET_STRING *octet = NULL;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000594 EC_KEY *ec = NULL;
595 EC_GROUP *group = NULL;
596 struct sshkey *key = NULL;
597 const unsigned char *attrp = NULL;
598 int i;
599 int nid;
600
601 memset(&key_attr, 0, sizeof(key_attr));
602 key_attr[0].type = CKA_ID;
603 key_attr[1].type = CKA_EC_POINT;
604 key_attr[2].type = CKA_EC_PARAMS;
605
606 session = p->slotinfo[slotidx].session;
607 f = p->function_list;
608
609 /* figure out size of the attributes */
610 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
611 if (rv != CKR_OK) {
612 error("C_GetAttributeValue failed: %lu", rv);
613 return (NULL);
614 }
615
616 /*
617 * Allow CKA_ID (always first attribute) to be empty, but
618 * ensure that none of the others are zero length.
619 * XXX assumes CKA_ID is always first.
620 */
621 if (key_attr[1].ulValueLen == 0 ||
622 key_attr[2].ulValueLen == 0) {
623 error("invalid attribute length");
624 return (NULL);
625 }
626
627 /* allocate buffers for attributes */
628 for (i = 0; i < 3; i++)
629 if (key_attr[i].ulValueLen > 0)
630 key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
631
632 /* retrieve ID, public point and curve parameters of EC key */
633 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
634 if (rv != CKR_OK) {
635 error("C_GetAttributeValue failed: %lu", rv);
636 goto fail;
637 }
638
639 ec = EC_KEY_new();
640 if (ec == NULL) {
641 error("EC_KEY_new failed");
642 goto fail;
643 }
644
645 attrp = key_attr[2].pValue;
646 group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
647 if (group == NULL) {
648 ossl_error("d2i_ECPKParameters failed");
649 goto fail;
650 }
651
652 if (EC_KEY_set_group(ec, group) == 0) {
653 ossl_error("EC_KEY_set_group failed");
654 goto fail;
655 }
656
657 if (key_attr[1].ulValueLen <= 2) {
658 error("CKA_EC_POINT too small");
659 goto fail;
660 }
661
djm@openbsd.org24757c12019-01-20 23:01:59 +0000662 attrp = key_attr[1].pValue;
663 octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
664 if (octet == NULL) {
665 ossl_error("d2i_ASN1_OCTET_STRING failed");
666 goto fail;
667 }
668 attrp = octet->data;
669 if (o2i_ECPublicKey(&ec, &attrp, octet->length) == NULL) {
670 ossl_error("o2i_ECPublicKey failed");
671 goto fail;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000672 }
673
674 nid = sshkey_ecdsa_key_to_nid(ec);
675 if (nid < 0) {
676 error("couldn't get curve nid");
677 goto fail;
678 }
679
680 if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
681 goto fail;
682
683 key = sshkey_new(KEY_UNSPEC);
684 if (key == NULL) {
685 error("sshkey_new failed");
686 goto fail;
687 }
688
689 key->ecdsa = ec;
690 key->ecdsa_nid = nid;
691 key->type = KEY_ECDSA;
692 key->flags |= SSHKEY_FLAG_EXT;
693 ec = NULL; /* now owned by key */
694
695fail:
696 for (i = 0; i < 3; i++)
697 free(key_attr[i].pValue);
698 if (ec)
699 EC_KEY_free(ec);
700 if (group)
701 EC_GROUP_free(group);
djm@openbsd.org24757c12019-01-20 23:01:59 +0000702 if (octet)
703 ASN1_OCTET_STRING_free(octet);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000704
705 return (key);
706}
707
708static struct sshkey *
709pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
710 CK_OBJECT_HANDLE *obj)
711{
712 CK_ATTRIBUTE key_attr[3];
713 CK_SESSION_HANDLE session;
714 CK_FUNCTION_LIST *f = NULL;
715 CK_RV rv;
716 RSA *rsa = NULL;
717 BIGNUM *rsa_n, *rsa_e;
718 struct sshkey *key = NULL;
719 int i;
720
721 memset(&key_attr, 0, sizeof(key_attr));
722 key_attr[0].type = CKA_ID;
723 key_attr[1].type = CKA_MODULUS;
724 key_attr[2].type = CKA_PUBLIC_EXPONENT;
725
726 session = p->slotinfo[slotidx].session;
727 f = p->function_list;
728
729 /* figure out size of the attributes */
730 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
731 if (rv != CKR_OK) {
732 error("C_GetAttributeValue failed: %lu", rv);
733 return (NULL);
734 }
735
736 /*
737 * Allow CKA_ID (always first attribute) to be empty, but
738 * ensure that none of the others are zero length.
739 * XXX assumes CKA_ID is always first.
740 */
741 if (key_attr[1].ulValueLen == 0 ||
742 key_attr[2].ulValueLen == 0) {
743 error("invalid attribute length");
744 return (NULL);
745 }
746
747 /* allocate buffers for attributes */
748 for (i = 0; i < 3; i++)
749 if (key_attr[i].ulValueLen > 0)
750 key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
751
752 /* retrieve ID, modulus and public exponent of RSA key */
753 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
754 if (rv != CKR_OK) {
755 error("C_GetAttributeValue failed: %lu", rv);
756 goto fail;
757 }
758
759 rsa = RSA_new();
760 if (rsa == NULL) {
761 error("RSA_new failed");
762 goto fail;
763 }
764
765 rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
766 rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
767 if (rsa_n == NULL || rsa_e == NULL) {
768 error("BN_bin2bn failed");
769 goto fail;
770 }
771 if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
772 fatal("%s: set key", __func__);
773 rsa_n = rsa_e = NULL; /* transferred */
774
775 if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
776 goto fail;
777
778 key = sshkey_new(KEY_UNSPEC);
779 if (key == NULL) {
780 error("sshkey_new failed");
781 goto fail;
782 }
783
784 key->rsa = rsa;
785 key->type = KEY_RSA;
786 key->flags |= SSHKEY_FLAG_EXT;
787 rsa = NULL; /* now owned by key */
788
789fail:
790 for (i = 0; i < 3; i++)
791 free(key_attr[i].pValue);
792 RSA_free(rsa);
793
794 return (key);
795}
796
797static struct sshkey *
798pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
799 CK_OBJECT_HANDLE *obj)
800{
801 CK_ATTRIBUTE cert_attr[3];
802 CK_SESSION_HANDLE session;
803 CK_FUNCTION_LIST *f = NULL;
804 CK_RV rv;
805 X509 *x509 = NULL;
806 EVP_PKEY *evp;
807 RSA *rsa = NULL;
808 EC_KEY *ec = NULL;
809 struct sshkey *key = NULL;
810 int i;
811 int nid;
812 const u_char *cp;
813
814 memset(&cert_attr, 0, sizeof(cert_attr));
815 cert_attr[0].type = CKA_ID;
816 cert_attr[1].type = CKA_SUBJECT;
817 cert_attr[2].type = CKA_VALUE;
818
819 session = p->slotinfo[slotidx].session;
820 f = p->function_list;
821
822 /* figure out size of the attributes */
823 rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
824 if (rv != CKR_OK) {
825 error("C_GetAttributeValue failed: %lu", rv);
826 return (NULL);
827 }
828
829 /*
830 * Allow CKA_ID (always first attribute) to be empty, but
831 * ensure that none of the others are zero length.
832 * XXX assumes CKA_ID is always first.
833 */
834 if (cert_attr[1].ulValueLen == 0 ||
835 cert_attr[2].ulValueLen == 0) {
836 error("invalid attribute length");
837 return (NULL);
838 }
839
840 /* allocate buffers for attributes */
841 for (i = 0; i < 3; i++)
842 if (cert_attr[i].ulValueLen > 0)
843 cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
844
845 /* retrieve ID, subject and value of certificate */
846 rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
847 if (rv != CKR_OK) {
848 error("C_GetAttributeValue failed: %lu", rv);
849 goto fail;
850 }
851
852 x509 = X509_new();
853 if (x509 == NULL) {
854 error("x509_new failed");
855 goto fail;
856 }
857
858 cp = cert_attr[2].pValue;
859 if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) {
860 error("d2i_x509 failed");
861 goto fail;
862 }
863
864 evp = X509_get_pubkey(x509);
865 if (evp == NULL) {
866 error("X509_get_pubkey failed");
867 goto fail;
868 }
869
870 if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) {
871 if (EVP_PKEY_get0_RSA(evp) == NULL) {
872 error("invalid x509; no rsa key");
873 goto fail;
874 }
875 if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) {
876 error("RSAPublicKey_dup failed");
877 goto fail;
878 }
879
880 if (pkcs11_rsa_wrap(p, slotidx, &cert_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 } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) {
djm@openbsd.org8a246752019-01-20 23:03:26 +0000894 if (EVP_PKEY_get0_EC_KEY(evp) == NULL) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000895 error("invalid x509; no ec key");
896 goto fail;
897 }
djm@openbsd.org8a246752019-01-20 23:03:26 +0000898 if ((ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(evp))) == NULL) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000899 error("EC_KEY_dup failed");
900 goto fail;
901 }
902
903 nid = sshkey_ecdsa_key_to_nid(ec);
904 if (nid < 0) {
905 error("couldn't get curve nid");
906 goto fail;
907 }
908
909 if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
910 goto fail;
911
912 key = sshkey_new(KEY_UNSPEC);
913 if (key == NULL) {
914 error("sshkey_new failed");
915 goto fail;
916 }
917
918 key->ecdsa = ec;
919 key->ecdsa_nid = nid;
920 key->type = KEY_ECDSA;
921 key->flags |= SSHKEY_FLAG_EXT;
922 ec = NULL; /* now owned by key */
923 } else
924 error("unknown certificate key type");
925
926fail:
927 for (i = 0; i < 3; i++)
928 free(cert_attr[i].pValue);
929 X509_free(x509);
930 RSA_free(rsa);
931 EC_KEY_free(ec);
932
933 return (key);
934}
935
936#if 0
Damien Millerd2252c72013-11-04 07:41:48 +1100937static int
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000938have_rsa_key(const RSA *rsa)
939{
940 const BIGNUM *rsa_n, *rsa_e;
941
942 RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
943 return rsa_n != NULL && rsa_e != NULL;
944}
djm@openbsd.org93f02102019-01-20 22:51:37 +0000945#endif
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000946
djm@openbsd.org93f02102019-01-20 22:51:37 +0000947/*
948 * lookup certificates for token in slot identified by slotidx,
949 * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
950 * keysp points to an (possibly empty) array with *nkeys keys.
951 */
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000952static int
djm@openbsd.org93f02102019-01-20 22:51:37 +0000953pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
djm@openbsd.org1129dcf2015-01-15 09:40:00 +0000954 struct sshkey ***keysp, int *nkeys)
Damien Miller7ea845e2010-02-12 09:21:02 +1100955{
djm@openbsd.org93f02102019-01-20 22:51:37 +0000956 struct sshkey *key = NULL;
957 CK_OBJECT_CLASS key_class;
958 CK_ATTRIBUTE key_attr[1];
959 CK_SESSION_HANDLE session;
960 CK_FUNCTION_LIST *f = NULL;
961 CK_RV rv;
962 CK_OBJECT_HANDLE obj;
963 CK_ULONG n = 0;
964 int ret = -1;
Tim Rice179eee02010-03-04 12:48:05 -0800965
djm@openbsd.org93f02102019-01-20 22:51:37 +0000966 memset(&key_attr, 0, sizeof(key_attr));
967 memset(&obj, 0, sizeof(obj));
968
969 key_class = CKO_CERTIFICATE;
970 key_attr[0].type = CKA_CLASS;
971 key_attr[0].pValue = &key_class;
972 key_attr[0].ulValueLen = sizeof(key_class);
973
Damien Miller7ea845e2010-02-12 09:21:02 +1100974 session = p->slotinfo[slotidx].session;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000975 f = p->function_list;
976
977 rv = f->C_FindObjectsInit(session, key_attr, 1);
978 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100979 error("C_FindObjectsInit failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000980 goto fail;
Damien Miller7ea845e2010-02-12 09:21:02 +1100981 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000982
Damien Miller7ea845e2010-02-12 09:21:02 +1100983 while (1) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000984 CK_CERTIFICATE_TYPE ck_cert_type;
985
986 rv = f->C_FindObjects(session, &obj, 1, &n);
987 if (rv != CKR_OK) {
988 error("C_FindObjects failed: %lu", rv);
989 goto fail;
Damien Miller7ea845e2010-02-12 09:21:02 +1100990 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000991 if (n == 0)
Damien Miller7ea845e2010-02-12 09:21:02 +1100992 break;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000993
994 memset(&ck_cert_type, 0, sizeof(ck_cert_type));
995 memset(&key_attr, 0, sizeof(key_attr));
996 key_attr[0].type = CKA_CERTIFICATE_TYPE;
997 key_attr[0].pValue = &ck_cert_type;
998 key_attr[0].ulValueLen = sizeof(ck_cert_type);
999
1000 rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
1001 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +11001002 error("C_GetAttributeValue failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001003 goto fail;
djm@openbsd.org63ebcd02015-07-18 08:02:17 +00001004 }
1005
djm@openbsd.org93f02102019-01-20 22:51:37 +00001006 switch (ck_cert_type) {
1007 case CKC_X_509:
1008 key = pkcs11_fetch_x509_pubkey(p, slotidx, &obj);
1009 break;
1010 default:
1011 /* XXX print key type? */
1012 error("skipping unsupported certificate type");
1013 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001014
djm@openbsd.org93f02102019-01-20 22:51:37 +00001015 if (key == NULL) {
1016 error("failed to fetch key");
1017 continue;
1018 }
1019
1020 if (pkcs11_key_included(keysp, nkeys, key)) {
1021 sshkey_free(key);
Damien Miller7ea845e2010-02-12 09:21:02 +11001022 } else {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001023 /* expand key array and add key */
1024 *keysp = xrecallocarray(*keysp, *nkeys,
1025 *nkeys + 1, sizeof(struct sshkey *));
1026 (*keysp)[*nkeys] = key;
1027 *nkeys = *nkeys + 1;
1028 debug("have %d keys", *nkeys);
Damien Millerd2252c72013-11-04 07:41:48 +11001029 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001030 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001031
1032 ret = 0;
1033fail:
1034 rv = f->C_FindObjectsFinal(session);
1035 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +11001036 error("C_FindObjectsFinal failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001037 ret = -1;
1038 }
1039
1040 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +11001041}
1042
djm@openbsd.org93f02102019-01-20 22:51:37 +00001043/*
1044 * lookup public keys for token in slot identified by slotidx,
1045 * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
1046 * keysp points to an (possibly empty) array with *nkeys keys.
1047 */
1048static int
1049pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
1050 struct sshkey ***keysp, int *nkeys)
1051{
1052 struct sshkey *key = NULL;
1053 CK_OBJECT_CLASS key_class;
1054 CK_ATTRIBUTE key_attr[1];
1055 CK_SESSION_HANDLE session;
1056 CK_FUNCTION_LIST *f = NULL;
1057 CK_RV rv;
1058 CK_OBJECT_HANDLE obj;
1059 CK_ULONG n = 0;
1060 int ret = -1;
1061
1062 memset(&key_attr, 0, sizeof(key_attr));
1063 memset(&obj, 0, sizeof(obj));
1064
1065 key_class = CKO_PUBLIC_KEY;
1066 key_attr[0].type = CKA_CLASS;
1067 key_attr[0].pValue = &key_class;
1068 key_attr[0].ulValueLen = sizeof(key_class);
1069
1070 session = p->slotinfo[slotidx].session;
1071 f = p->function_list;
1072
1073 rv = f->C_FindObjectsInit(session, key_attr, 1);
1074 if (rv != CKR_OK) {
1075 error("C_FindObjectsInit failed: %lu", rv);
1076 goto fail;
1077 }
1078
1079 while (1) {
1080 CK_KEY_TYPE ck_key_type;
1081
1082 rv = f->C_FindObjects(session, &obj, 1, &n);
1083 if (rv != CKR_OK) {
1084 error("C_FindObjects failed: %lu", rv);
1085 goto fail;
1086 }
1087 if (n == 0)
1088 break;
1089
1090 memset(&ck_key_type, 0, sizeof(ck_key_type));
1091 memset(&key_attr, 0, sizeof(key_attr));
1092 key_attr[0].type = CKA_KEY_TYPE;
1093 key_attr[0].pValue = &ck_key_type;
1094 key_attr[0].ulValueLen = sizeof(ck_key_type);
1095
1096 rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
1097 if (rv != CKR_OK) {
1098 error("C_GetAttributeValue failed: %lu", rv);
1099 goto fail;
1100 }
1101
1102 switch (ck_key_type) {
1103 case CKK_RSA:
1104 key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
1105 break;
1106 case CKK_ECDSA:
1107 key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
1108 break;
1109 default:
1110 /* XXX print key type? */
1111 error("skipping unsupported key type");
1112 }
1113
1114 if (key == NULL) {
1115 error("failed to fetch key");
1116 continue;
1117 }
1118
1119 if (pkcs11_key_included(keysp, nkeys, key)) {
1120 sshkey_free(key);
1121 } else {
1122 /* expand key array and add key */
1123 *keysp = xrecallocarray(*keysp, *nkeys,
1124 *nkeys + 1, sizeof(struct sshkey *));
1125 (*keysp)[*nkeys] = key;
1126 *nkeys = *nkeys + 1;
1127 debug("have %d keys", *nkeys);
1128 }
1129 }
1130
1131 ret = 0;
1132fail:
1133 rv = f->C_FindObjectsFinal(session);
1134 if (rv != CKR_OK) {
1135 error("C_FindObjectsFinal failed: %lu", rv);
1136 ret = -1;
1137 }
1138
1139 return (ret);
1140}
1141
1142#ifdef WITH_PKCS11_KEYGEN
1143#define FILL_ATTR(attr, idx, typ, val, len) \
1144 { (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; }
1145
1146static struct sshkey *
1147pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
1148 char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
1149{
1150 struct pkcs11_slotinfo *si;
1151 char *plabel = label ? label : "";
1152 int npub = 0, npriv = 0;
1153 CK_RV rv;
1154 CK_FUNCTION_LIST *f;
1155 CK_SESSION_HANDLE session;
1156 CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE;
1157 CK_OBJECT_HANDLE pubKey, privKey;
1158 CK_ATTRIBUTE tpub[16], tpriv[16];
1159 CK_MECHANISM mech = {
1160 CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0
1161 };
1162 CK_BYTE pubExponent[] = {
1163 0x01, 0x00, 0x01 /* RSA_F4 in bytes */
1164 };
1165 pubkey_filter[0].pValue = &pubkey_class;
1166 cert_filter[0].pValue = &cert_class;
1167
1168 *err = 0;
1169
1170 FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
1171 FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
1172 FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
1173 FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
1174 FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
1175 sizeof(false_val));
1176 FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
1177 FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
1178 FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits));
1179 FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent,
1180 sizeof(pubExponent));
1181 FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
1182
1183 FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
1184 FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
1185 FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
1186 FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
1187 FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
1188 FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
1189 FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
1190 sizeof(false_val));
1191 FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
1192 FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
1193 FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
1194
1195 f = p->function_list;
1196 si = &p->slotinfo[slotidx];
1197 session = si->session;
1198
1199 if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
1200 &pubKey, &privKey)) != CKR_OK) {
1201 error("%s: key generation failed: error 0x%lx", __func__, rv);
1202 *err = rv;
1203 return NULL;
1204 }
1205
1206 return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey);
1207}
1208
1209static int
1210pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen)
1211{
1212 size_t i, len;
1213 char ptr[3];
1214
1215 if (dest)
1216 *dest = NULL;
1217 if (rlen)
1218 *rlen = 0;
1219
1220 if ((len = strlen(hex)) % 2)
1221 return -1;
1222 len /= 2;
1223
1224 *dest = xmalloc(len);
1225
1226 ptr[2] = '\0';
1227 for (i = 0; i < len; i++) {
1228 ptr[0] = hex[2 * i];
1229 ptr[1] = hex[(2 * i) + 1];
1230 if (!isxdigit(ptr[0]) || !isxdigit(ptr[1]))
1231 return -1;
1232 (*dest)[i] = (unsigned char)strtoul(ptr, NULL, 16);
1233 }
1234
1235 if (rlen)
1236 *rlen = len;
1237
1238 return 0;
1239}
1240
1241static struct ec_curve_info {
1242 const char *name;
1243 const char *oid;
1244 const char *oid_encoded;
1245 size_t size;
1246} ec_curve_infos[] = {
1247 {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256},
1248 {"secp384r1", "1.3.132.0.34", "06052B81040022", 384},
1249 {"secp521r1", "1.3.132.0.35", "06052B81040023", 521},
djm@openbsd.org65294092019-01-20 23:11:11 +00001250 {NULL, NULL, NULL, 0},
djm@openbsd.org93f02102019-01-20 22:51:37 +00001251};
1252
1253static struct sshkey *
1254pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
1255 char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
1256{
1257 struct pkcs11_slotinfo *si;
1258 char *plabel = label ? label : "";
1259 int i;
1260 size_t ecparams_size;
1261 unsigned char *ecparams = NULL;
1262 int npub = 0, npriv = 0;
1263 CK_RV rv;
1264 CK_FUNCTION_LIST *f;
1265 CK_SESSION_HANDLE session;
1266 CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE;
1267 CK_OBJECT_HANDLE pubKey, privKey;
1268 CK_MECHANISM mech = {
1269 CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
1270 };
1271 CK_ATTRIBUTE tpub[16], tpriv[16];
1272
1273 *err = 0;
1274
1275 for (i = 0; ec_curve_infos[i].name; i++) {
1276 if (ec_curve_infos[i].size == bits)
1277 break;
1278 }
1279 if (!ec_curve_infos[i].name) {
1280 error("%s: invalid key size %lu", __func__, bits);
1281 return NULL;
1282 }
1283 if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams,
1284 &ecparams_size) == -1) {
1285 error("%s: invalid oid", __func__);
1286 return NULL;
1287 }
1288
1289 FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
1290 FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
1291 FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
1292 FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
1293 FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
1294 sizeof(false_val));
1295 FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
1296 FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
1297 FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size);
1298 FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
1299
1300 FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
1301 FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
1302 FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
1303 FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
1304 FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
1305 FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
1306 FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
1307 sizeof(false_val));
1308 FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
1309 FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
1310 FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
1311
1312 f = p->function_list;
1313 si = &p->slotinfo[slotidx];
1314 session = si->session;
1315
1316 if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
1317 &pubKey, &privKey)) != CKR_OK) {
1318 error("%s: key generation failed: error 0x%lx", __func__, rv);
1319 *err = rv;
1320 return NULL;
1321 }
1322
1323 return pkcs11_fetch_ecdsa_pubkey(p, slotidx, &pubKey);
1324}
1325#endif /* WITH_PKCS11_KEYGEN */
1326
1327/*
1328 * register a new provider, fails if provider already exists. if
1329 * keyp is provided, fetch keys.
1330 */
1331static int
1332pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
1333 struct pkcs11_provider **providerp, CK_ULONG user)
Damien Miller7ea845e2010-02-12 09:21:02 +11001334{
1335 int nkeys, need_finalize = 0;
djm@openbsd.org93f02102019-01-20 22:51:37 +00001336 int ret = -1;
Damien Miller7ea845e2010-02-12 09:21:02 +11001337 struct pkcs11_provider *p = NULL;
1338 void *handle = NULL;
1339 CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
1340 CK_RV rv;
1341 CK_FUNCTION_LIST *f = NULL;
1342 CK_TOKEN_INFO *token;
1343 CK_ULONG i;
1344
djm@openbsd.org93f02102019-01-20 22:51:37 +00001345 if (providerp == NULL)
1346 goto fail;
1347 *providerp = NULL;
1348
1349 if (keyp != NULL)
1350 *keyp = NULL;
1351
Damien Miller7ea845e2010-02-12 09:21:02 +11001352 if (pkcs11_provider_lookup(provider_id) != NULL) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001353 debug("%s: provider already registered: %s",
1354 __func__, provider_id);
Damien Miller7ea845e2010-02-12 09:21:02 +11001355 goto fail;
1356 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001357 /* open shared pkcs11-library */
Damien Miller7ea845e2010-02-12 09:21:02 +11001358 if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
1359 error("dlopen %s failed: %s", provider_id, dlerror());
1360 goto fail;
1361 }
1362 if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
1363 error("dlsym(C_GetFunctionList) failed: %s", dlerror());
1364 goto fail;
1365 }
1366 p = xcalloc(1, sizeof(*p));
1367 p->name = xstrdup(provider_id);
1368 p->handle = handle;
1369 /* setup the pkcs11 callbacks */
1370 if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001371 error("C_GetFunctionList for provider %s failed: %lu",
1372 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001373 goto fail;
1374 }
1375 p->function_list = f;
1376 if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001377 error("C_Initialize for provider %s failed: %lu",
1378 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001379 goto fail;
1380 }
1381 need_finalize = 1;
1382 if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001383 error("C_GetInfo for provider %s failed: %lu",
1384 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001385 goto fail;
1386 }
1387 rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
1388 rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001389 debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d"
Damien Miller7ea845e2010-02-12 09:21:02 +11001390 " libraryDescription <%s> libraryVersion %d.%d",
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001391 provider_id,
Damien Miller7ea845e2010-02-12 09:21:02 +11001392 p->info.manufacturerID,
1393 p->info.cryptokiVersion.major,
1394 p->info.cryptokiVersion.minor,
1395 p->info.libraryDescription,
1396 p->info.libraryVersion.major,
1397 p->info.libraryVersion.minor);
1398 if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
1399 error("C_GetSlotList failed: %lu", rv);
1400 goto fail;
1401 }
1402 if (p->nslots == 0) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001403 error("%s: provider %s returned no slots", __func__,
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001404 provider_id);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001405 ret = -SSH_PKCS11_ERR_NO_SLOTS;
Damien Miller7ea845e2010-02-12 09:21:02 +11001406 goto fail;
1407 }
1408 p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
1409 if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
1410 != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001411 error("C_GetSlotList for provider %s failed: %lu",
1412 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001413 goto fail;
1414 }
1415 p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
1416 p->valid = 1;
1417 nkeys = 0;
1418 for (i = 0; i < p->nslots; i++) {
1419 token = &p->slotinfo[i].token;
1420 if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
1421 != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001422 error("C_GetTokenInfo for provider %s slot %lu "
1423 "failed: %lu", provider_id, (unsigned long)i, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001424 continue;
1425 }
djm@openbsd.orgb15fd982015-07-18 08:00:21 +00001426 if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001427 debug2("%s: ignoring uninitialised token in "
1428 "provider %s slot %lu", __func__,
1429 provider_id, (unsigned long)i);
djm@openbsd.orgb15fd982015-07-18 08:00:21 +00001430 continue;
1431 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001432 rmspace(token->label, sizeof(token->label));
1433 rmspace(token->manufacturerID, sizeof(token->manufacturerID));
1434 rmspace(token->model, sizeof(token->model));
1435 rmspace(token->serialNumber, sizeof(token->serialNumber));
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001436 debug("provider %s slot %lu: label <%s> manufacturerID <%s> "
1437 "model <%s> serial <%s> flags 0x%lx",
1438 provider_id, (unsigned long)i,
Damien Miller7ea845e2010-02-12 09:21:02 +11001439 token->label, token->manufacturerID, token->model,
1440 token->serialNumber, token->flags);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001441 /*
1442 * open session, login with pin and retrieve public
1443 * keys (if keyp is provided)
1444 */
1445 if ((ret = pkcs11_open_session(p, i, pin, user)) == 0) {
1446 if (keyp == NULL)
1447 continue;
Damien Miller7ea845e2010-02-12 09:21:02 +11001448 pkcs11_fetch_keys(p, i, keyp, &nkeys);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001449 pkcs11_fetch_certs(p, i, keyp, &nkeys);
1450 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001451 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001452
1453 /* now owned by caller */
1454 *providerp = p;
1455
1456 TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
1457 p->refcount++; /* add to provider list */
1458
1459 return (nkeys);
Damien Miller7ea845e2010-02-12 09:21:02 +11001460fail:
1461 if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001462 error("C_Finalize for provider %s failed: %lu",
1463 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001464 if (p) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001465 free(p->name);
Darren Tuckera627d422013-06-02 07:31:17 +10001466 free(p->slotlist);
1467 free(p->slotinfo);
1468 free(p);
Damien Miller7ea845e2010-02-12 09:21:02 +11001469 }
1470 if (handle)
1471 dlclose(handle);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001472 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +11001473}
Damien Millerdfa41562010-02-12 10:06:28 +11001474
djm@openbsd.org93f02102019-01-20 22:51:37 +00001475/*
1476 * register a new provider and get number of keys hold by the token,
1477 * fails if provider already exists
1478 */
1479int
1480pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
1481{
1482 struct pkcs11_provider *p = NULL;
1483 int nkeys;
Darren Tucker0dd24e02011-09-04 19:59:26 +10001484
djm@openbsd.org93f02102019-01-20 22:51:37 +00001485 nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER);
1486
1487 /* no keys found or some other error, de-register provider */
1488 if (nkeys <= 0 && p != NULL) {
1489 TAILQ_REMOVE(&pkcs11_providers, p, next);
1490 pkcs11_provider_finalize(p);
1491 pkcs11_provider_unref(p);
1492 }
1493 if (nkeys == 0)
1494 debug("%s: provider %s returned no keys", __func__,
1495 provider_id);
1496
1497 return (nkeys);
1498}
1499
1500#ifdef WITH_PKCS11_KEYGEN
1501struct sshkey *
1502pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
1503 unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err)
1504{
1505 struct pkcs11_provider *p = NULL;
1506 struct pkcs11_slotinfo *si;
1507 CK_FUNCTION_LIST *f;
1508 CK_SESSION_HANDLE session;
1509 struct sshkey *k = NULL;
1510 int ret = -1, reset_pin = 0, reset_provider = 0;
1511 CK_RV rv;
1512
1513 *err = 0;
1514
1515 if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
1516 debug("%s: provider \"%s\" available", __func__, provider_id);
1517 else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p,
1518 CKU_SO)) < 0) {
1519 debug("%s: could not register provider %s", __func__,
1520 provider_id);
1521 goto out;
1522 } else
1523 reset_provider = 1;
1524
1525 f = p->function_list;
1526 si = &p->slotinfo[slotidx];
1527 session = si->session;
1528
1529 if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
1530 CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
1531 debug("%s: could not supply SO pin: %lu", __func__, rv);
1532 reset_pin = 0;
1533 } else
1534 reset_pin = 1;
1535
1536 switch (type) {
1537 case KEY_RSA:
1538 if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label,
1539 bits, keyid, err)) == NULL) {
1540 debug("%s: failed to generate RSA key", __func__);
1541 goto out;
1542 }
1543 break;
1544 case KEY_ECDSA:
1545 if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label,
1546 bits, keyid, err)) == NULL) {
1547 debug("%s: failed to generate ECDSA key", __func__);
1548 goto out;
1549 }
1550 break;
1551 default:
1552 *err = SSH_PKCS11_ERR_GENERIC;
1553 debug("%s: unknown type %d", __func__, type);
1554 goto out;
1555 }
1556
1557out:
1558 if (reset_pin)
1559 f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
1560 CK_INVALID_HANDLE);
1561
1562 if (reset_provider)
1563 pkcs11_del_provider(provider_id);
1564
1565 return (k);
1566}
1567
1568struct sshkey *
1569pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
1570 unsigned char keyid, u_int32_t *err)
1571{
1572 struct pkcs11_provider *p = NULL;
1573 struct pkcs11_slotinfo *si;
1574 struct sshkey *k = NULL;
1575 int reset_pin = 0, reset_provider = 0;
1576 CK_ULONG nattrs;
1577 CK_FUNCTION_LIST *f;
1578 CK_SESSION_HANDLE session;
1579 CK_ATTRIBUTE attrs[16];
1580 CK_OBJECT_CLASS key_class;
1581 CK_KEY_TYPE key_type;
1582 CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE;
1583 CK_RV rv;
1584
1585 *err = 0;
1586
1587 if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
1588 debug("%s: using provider \"%s\"", __func__, provider_id);
1589 } else if (pkcs11_register_provider(provider_id, pin, NULL, &p,
1590 CKU_SO) < 0) {
1591 debug("%s: could not register provider %s", __func__,
1592 provider_id);
1593 goto out;
1594 } else
1595 reset_provider = 1;
1596
1597 f = p->function_list;
1598 si = &p->slotinfo[slotidx];
1599 session = si->session;
1600
1601 if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
1602 CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
1603 debug("%s: could not supply SO pin: %lu", __func__, rv);
1604 reset_pin = 0;
1605 } else
1606 reset_pin = 1;
1607
1608 /* private key */
1609 nattrs = 0;
1610 key_class = CKO_PRIVATE_KEY;
1611 FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
1612 FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
1613
1614 if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
1615 obj != CK_INVALID_HANDLE) {
1616 if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
1617 debug("%s: could not destroy private key 0x%hhx",
1618 __func__, keyid);
1619 *err = rv;
1620 goto out;
1621 }
1622 }
1623
1624 /* public key */
1625 nattrs = 0;
1626 key_class = CKO_PUBLIC_KEY;
1627 FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
1628 FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
1629
1630 if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
1631 obj != CK_INVALID_HANDLE) {
1632
1633 /* get key type */
1634 nattrs = 0;
1635 FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type,
1636 sizeof(key_type));
1637 rv = f->C_GetAttributeValue(session, obj, attrs, nattrs);
1638 if (rv != CKR_OK) {
1639 debug("%s: could not get key type of public key 0x%hhx",
1640 __func__, keyid);
1641 *err = rv;
1642 key_type = -1;
1643 }
1644 if (key_type == CKK_RSA)
1645 k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
1646 else if (key_type == CKK_ECDSA)
1647 k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
1648
1649 if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
1650 debug("%s: could not destroy public key 0x%hhx",
1651 __func__, keyid);
1652 *err = rv;
1653 goto out;
1654 }
1655 }
1656
1657out:
1658 if (reset_pin)
1659 f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
1660 CK_INVALID_HANDLE);
1661
1662 if (reset_provider)
1663 pkcs11_del_provider(provider_id);
1664
1665 return (k);
1666}
1667#endif /* WITH_PKCS11_KEYGEN */
1668#else /* HAVE_DLOPEN */
Darren Tucker0dd24e02011-09-04 19:59:26 +10001669int
1670pkcs11_init(int interactive)
1671{
djm@openbsd.org93f02102019-01-20 22:51:37 +00001672 error("%s: dlopen() not supported", __func__);
1673 return (-1);
1674}
1675
1676int
1677pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
1678{
1679 error("%s: dlopen() not supported", __func__);
1680 return (-1);
Darren Tucker0dd24e02011-09-04 19:59:26 +10001681}
1682
1683void
1684pkcs11_terminate(void)
1685{
djm@openbsd.org93f02102019-01-20 22:51:37 +00001686 error("%s: dlopen() not supported", __func__);
Darren Tucker0dd24e02011-09-04 19:59:26 +10001687}
djm@openbsd.org93f02102019-01-20 22:51:37 +00001688#endif /* HAVE_DLOPEN */