blob: 7dc828978f34aadabf4835bd061055df637cc739 [file] [log] [blame]
djm@openbsd.org63297642019-01-21 00:47:34 +00001/* $OpenBSD: ssh-pkcs11.c,v 1.37 2019/01/21 00:47:34 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 +000081static void
82ossl_error(const char *msg)
83{
djm@openbsd.org65294092019-01-20 23:11:11 +000084 unsigned long e;
djm@openbsd.org93f02102019-01-20 22:51:37 +000085
djm@openbsd.org65294092019-01-20 23:11:11 +000086 while ((e = ERR_get_error()) != 0)
87 error("%s: %s: %.100s", __func__, msg,
88 ERR_error_string(e, NULL));
djm@openbsd.org93f02102019-01-20 22:51:37 +000089}
djm@openbsd.org93f02102019-01-20 22:51:37 +000090
Damien Miller7ea845e2010-02-12 09:21:02 +110091int
92pkcs11_init(int interactive)
93{
94 pkcs11_interactive = interactive;
95 TAILQ_INIT(&pkcs11_providers);
96 return (0);
97}
98
99/*
djm@openbsd.org93f02102019-01-20 22:51:37 +0000100 * finalize a provider shared library, it's no longer usable.
Damien Miller7ea845e2010-02-12 09:21:02 +1100101 * however, there might still be keys referencing this provider,
djm@openbsd.org93f02102019-01-20 22:51:37 +0000102 * so the actual freeing of memory is handled by pkcs11_provider_unref().
Damien Miller7ea845e2010-02-12 09:21:02 +1100103 * this is called when a provider gets unregistered.
104 */
105static void
106pkcs11_provider_finalize(struct pkcs11_provider *p)
107{
108 CK_RV rv;
109 CK_ULONG i;
110
111 debug("pkcs11_provider_finalize: %p refcount %d valid %d",
112 p, p->refcount, p->valid);
113 if (!p->valid)
114 return;
115 for (i = 0; i < p->nslots; i++) {
116 if (p->slotinfo[i].session &&
117 (rv = p->function_list->C_CloseSession(
118 p->slotinfo[i].session)) != CKR_OK)
119 error("C_CloseSession failed: %lu", rv);
120 }
121 if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
122 error("C_Finalize failed: %lu", rv);
123 p->valid = 0;
124 p->function_list = NULL;
125 dlclose(p->handle);
126}
127
128/*
129 * remove a reference to the provider.
130 * called when a key gets destroyed or when the provider is unregistered.
131 */
132static void
133pkcs11_provider_unref(struct pkcs11_provider *p)
134{
135 debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
136 if (--p->refcount <= 0) {
137 if (p->valid)
138 error("pkcs11_provider_unref: %p still valid", p);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000139 free(p->name);
Darren Tuckera627d422013-06-02 07:31:17 +1000140 free(p->slotlist);
141 free(p->slotinfo);
142 free(p);
Damien Miller7ea845e2010-02-12 09:21:02 +1100143 }
144}
145
146/* unregister all providers, keys might still point to the providers */
147void
148pkcs11_terminate(void)
149{
150 struct pkcs11_provider *p;
151
152 while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
153 TAILQ_REMOVE(&pkcs11_providers, p, next);
154 pkcs11_provider_finalize(p);
155 pkcs11_provider_unref(p);
156 }
157}
158
159/* lookup provider by name */
160static struct pkcs11_provider *
161pkcs11_provider_lookup(char *provider_id)
162{
163 struct pkcs11_provider *p;
164
165 TAILQ_FOREACH(p, &pkcs11_providers, next) {
166 debug("check %p %s", p, p->name);
167 if (!strcmp(provider_id, p->name))
168 return (p);
169 }
170 return (NULL);
171}
172
173/* unregister provider by name */
174int
175pkcs11_del_provider(char *provider_id)
176{
177 struct pkcs11_provider *p;
178
179 if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
180 TAILQ_REMOVE(&pkcs11_providers, p, next);
181 pkcs11_provider_finalize(p);
182 pkcs11_provider_unref(p);
183 return (0);
184 }
185 return (-1);
186}
187
djm@openbsd.org58622a82019-01-20 23:10:33 +0000188/* release a wrapped object */
189static void
190pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
191 long argl, void *argp)
Damien Miller7ea845e2010-02-12 09:21:02 +1100192{
djm@openbsd.org58622a82019-01-20 23:10:33 +0000193 struct pkcs11_key *k11 = ptr;
Damien Miller7ea845e2010-02-12 09:21:02 +1100194
djm@openbsd.org58622a82019-01-20 23:10:33 +0000195 debug("%s: parent %p ptr %p idx %d", __func__, parent, ptr, idx);
196 if (k11 == NULL)
197 return;
198 if (k11->provider)
199 pkcs11_provider_unref(k11->provider);
200 free(k11->keyid);
201 free(k11);
Damien Miller7ea845e2010-02-12 09:21:02 +1100202}
203
Damien Miller031c9102010-04-16 15:54:44 +1000204/* find a single 'obj' for given attributes */
205static int
206pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
207 CK_ULONG nattr, CK_OBJECT_HANDLE *obj)
208{
209 CK_FUNCTION_LIST *f;
210 CK_SESSION_HANDLE session;
211 CK_ULONG nfound = 0;
212 CK_RV rv;
213 int ret = -1;
214
215 f = p->function_list;
216 session = p->slotinfo[slotidx].session;
217 if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
218 error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
219 return (-1);
220 }
221 if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK ||
222 nfound != 1) {
223 debug("C_FindObjects failed (nfound %lu nattr %lu): %lu",
224 nfound, nattr, rv);
225 } else
226 ret = 0;
227 if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
228 error("C_FindObjectsFinal failed: %lu", rv);
229 return (ret);
230}
231
Damien Miller7ea845e2010-02-12 09:21:02 +1100232static int
djm@openbsd.org93f02102019-01-20 22:51:37 +0000233pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
Damien Miller7ea845e2010-02-12 09:21:02 +1100234{
Damien Miller7ea845e2010-02-12 09:21:02 +1100235 struct pkcs11_slotinfo *si;
236 CK_FUNCTION_LIST *f;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000237 CK_OBJECT_HANDLE obj;
238 CK_RV rv;
239 CK_OBJECT_CLASS private_key_class;
240 CK_BBOOL true_val;
241 CK_MECHANISM mech;
242 CK_ATTRIBUTE key_filter[3];
djm@openbsd.orga71ba582015-05-27 05:15:02 +0000243 char *pin = NULL, prompt[1024];
Damien Miller7ea845e2010-02-12 09:21:02 +1100244
Damien Miller7ea845e2010-02-12 09:21:02 +1100245 if (!k11->provider || !k11->provider->valid) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000246 error("no pkcs11 (valid) provider found");
Damien Miller7ea845e2010-02-12 09:21:02 +1100247 return (-1);
248 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000249
Damien Miller7ea845e2010-02-12 09:21:02 +1100250 f = k11->provider->function_list;
251 si = &k11->provider->slotinfo[k11->slotidx];
djm@openbsd.org93f02102019-01-20 22:51:37 +0000252
Damien Miller7ea845e2010-02-12 09:21:02 +1100253 if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
254 if (!pkcs11_interactive) {
djm@openbsd.orga71ba582015-05-27 05:15:02 +0000255 error("need pin entry%s", (si->token.flags &
256 CKF_PROTECTED_AUTHENTICATION_PATH) ?
257 " on reader keypad" : "");
Damien Miller7ea845e2010-02-12 09:21:02 +1100258 return (-1);
259 }
djm@openbsd.orga71ba582015-05-27 05:15:02 +0000260 if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
261 verbose("Deferring PIN entry to reader keypad.");
262 else {
263 snprintf(prompt, sizeof(prompt),
264 "Enter PIN for '%s': ", si->token.label);
265 pin = read_passphrase(prompt, RP_ALLOW_EOF);
266 if (pin == NULL)
267 return (-1); /* bail out */
268 }
269 rv = f->C_Login(si->session, CKU_USER, (u_char *)pin,
270 (pin != NULL) ? strlen(pin) : 0);
271 if (pin != NULL) {
272 explicit_bzero(pin, strlen(pin));
Darren Tuckera627d422013-06-02 07:31:17 +1000273 free(pin);
djm@openbsd.orga71ba582015-05-27 05:15:02 +0000274 }
275 if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100276 error("C_Login failed: %lu", rv);
277 return (-1);
278 }
Damien Miller7ea845e2010-02-12 09:21:02 +1100279 si->logged_in = 1;
280 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000281
282 memset(&key_filter, 0, sizeof(key_filter));
283 private_key_class = CKO_PRIVATE_KEY;
284 key_filter[0].type = CKA_CLASS;
285 key_filter[0].pValue = &private_key_class;
286 key_filter[0].ulValueLen = sizeof(private_key_class);
287
288 key_filter[1].type = CKA_ID;
Damien Miller7ea845e2010-02-12 09:21:02 +1100289 key_filter[1].pValue = k11->keyid;
290 key_filter[1].ulValueLen = k11->keyid_len;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000291
292 true_val = CK_TRUE;
293 key_filter[2].type = CKA_SIGN;
294 key_filter[2].pValue = &true_val;
295 key_filter[2].ulValueLen = sizeof(true_val);
296
Damien Miller031c9102010-04-16 15:54:44 +1000297 /* try to find object w/CKA_SIGN first, retry w/o */
298 if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&
299 pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {
300 error("cannot find private key");
djm@openbsd.org93f02102019-01-20 22:51:37 +0000301 return (-1);
Damien Miller7ea845e2010-02-12 09:21:02 +1100302 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000303
304 memset(&mech, 0, sizeof(mech));
305 mech.mechanism = mech_type;
306 mech.pParameter = NULL_PTR;
307 mech.ulParameterLen = 0;
308
309 if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
310 error("C_SignInit failed: %lu", rv);
311 return (-1);
312 }
313
314 return (0);
315}
316
317/* openssl callback doing the actual signing operation */
318static int
319pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
320 int padding)
321{
322 struct pkcs11_key *k11;
323 struct pkcs11_slotinfo *si;
324 CK_FUNCTION_LIST *f;
325 CK_ULONG tlen = 0;
326 CK_RV rv;
327 int rval = -1;
328
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000329 if ((k11 = RSA_get_ex_data(rsa, 0)) == NULL) {
330 error("RSA_get_ex_data failed for rsa %p", rsa);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000331 return (-1);
332 }
333
334 if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) {
335 error("pkcs11_get_key failed");
336 return (-1);
337 }
338
339 f = k11->provider->function_list;
340 si = &k11->provider->slotinfo[k11->slotidx];
341 tlen = RSA_size(rsa);
342
343 /* XXX handle CKR_BUFFER_TOO_SMALL */
344 rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
345 if (rv == CKR_OK)
346 rval = tlen;
347 else
348 error("C_Sign failed: %lu", rv);
349
Damien Miller7ea845e2010-02-12 09:21:02 +1100350 return (rval);
351}
352
353static int
354pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
355 int padding)
356{
357 return (-1);
358}
359
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000360static RSA_METHOD *rsa_method;
djm@openbsd.org58622a82019-01-20 23:10:33 +0000361static int rsa_idx = 0;
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000362
363static int
364pkcs11_rsa_start_wrapper(void)
365{
366 if (rsa_method != NULL)
367 return (0);
368 rsa_method = RSA_meth_dup(RSA_get_default_method());
369 if (rsa_method == NULL)
370 return (-1);
djm@openbsd.org58622a82019-01-20 23:10:33 +0000371 rsa_idx = RSA_get_ex_new_index(0, "ssh-pkcs11-rsa",
372 NULL, NULL, pkcs11_k11_free);
373 if (rsa_idx == -1)
374 return (-1);
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000375 if (!RSA_meth_set1_name(rsa_method, "pkcs11") ||
376 !RSA_meth_set_priv_enc(rsa_method, pkcs11_rsa_private_encrypt) ||
djm@openbsd.org58622a82019-01-20 23:10:33 +0000377 !RSA_meth_set_priv_dec(rsa_method, pkcs11_rsa_private_decrypt)) {
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000378 error("%s: setup pkcs11 method failed", __func__);
379 return (-1);
380 }
381 return (0);
382}
383
Damien Miller7ea845e2010-02-12 09:21:02 +1100384/* redirect private key operations for rsa key to pkcs11 token */
385static int
386pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
387 CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
388{
389 struct pkcs11_key *k11;
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000390
391 if (pkcs11_rsa_start_wrapper() == -1)
392 return (-1);
Damien Miller7ea845e2010-02-12 09:21:02 +1100393
394 k11 = xcalloc(1, sizeof(*k11));
395 k11->provider = provider;
396 provider->refcount++; /* provider referenced by RSA key */
397 k11->slotidx = slotidx;
398 /* identify key object on smartcard */
399 k11->keyid_len = keyid_attrib->ulValueLen;
djm@openbsd.orgd2d772f2016-02-12 00:20:30 +0000400 if (k11->keyid_len > 0) {
401 k11->keyid = xmalloc(k11->keyid_len);
402 memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
403 }
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000404
djm@openbsd.orgfcb1b092019-01-20 23:12:35 +0000405 RSA_set_method(rsa, rsa_method);
djm@openbsd.org58622a82019-01-20 23:10:33 +0000406 RSA_set_ex_data(rsa, rsa_idx, k11);
Damien Miller7ea845e2010-02-12 09:21:02 +1100407 return (0);
408}
409
Damien Millere2cb4452019-01-21 11:32:28 +1100410#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +0000411/* openssl callback doing the actual signing operation */
412static ECDSA_SIG *
413ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
414 const BIGNUM *rp, EC_KEY *ec)
415{
416 struct pkcs11_key *k11;
417 struct pkcs11_slotinfo *si;
418 CK_FUNCTION_LIST *f;
419 CK_ULONG siglen = 0, bnlen;
420 CK_RV rv;
421 ECDSA_SIG *ret = NULL;
422 u_char *sig;
djm@openbsd.org63297642019-01-21 00:47:34 +0000423 BIGNUM *r = NULL, *s = NULL;
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 }
djm@openbsd.org63297642019-01-21 00:47:34 +0000456 if ((r = BN_bin2bn(sig, bnlen, NULL)) == NULL ||
457 (s = BN_bin2bn(sig+bnlen, bnlen, NULL)) == NULL) {
djm@openbsd.org749aef32019-01-20 23:00:12 +0000458 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 }
djm@openbsd.org63297642019-01-21 00:47:34 +0000463 if (!ECDSA_SIG_set0(ret, r, s)) {
464 error("%s: ECDSA_SIG_set0 failed", __func__);
465 ECDSA_SIG_free(ret);
466 ret = NULL;
467 goto done;
468 }
469 r = s = NULL; /* now owned by ret */
470 /* success */
djm@openbsd.org93f02102019-01-20 22:51:37 +0000471 done:
djm@openbsd.org63297642019-01-21 00:47:34 +0000472 BN_free(r);
473 BN_free(s);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000474 free(sig);
475
476 return (ret);
477}
478
479static EC_KEY_METHOD *ec_key_method;
djm@openbsd.org445cfce2019-01-20 23:05:52 +0000480static int ec_key_idx = 0;
481
djm@openbsd.org93f02102019-01-20 22:51:37 +0000482static int
483pkcs11_ecdsa_start_wrapper(void)
484{
485 int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
486 unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
487
488 if (ec_key_method != NULL)
489 return (0);
djm@openbsd.org445cfce2019-01-20 23:05:52 +0000490 ec_key_idx = EC_KEY_get_ex_new_index(0, "ssh-pkcs11-ecdsa",
491 NULL, NULL, pkcs11_k11_free);
492 if (ec_key_idx == -1)
493 return (-1);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000494 ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
495 if (ec_key_method == NULL)
496 return (-1);
497 EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL);
498 EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign);
499 return (0);
500}
501
502static int
503pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
504 CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
505{
506 struct pkcs11_key *k11;
507
508 if (pkcs11_ecdsa_start_wrapper() == -1)
509 return (-1);
510
511 k11 = xcalloc(1, sizeof(*k11));
512 k11->provider = provider;
513 provider->refcount++; /* provider referenced by ECDSA key */
514 k11->slotidx = slotidx;
515 /* identify key object on smartcard */
516 k11->keyid_len = keyid_attrib->ulValueLen;
517 k11->keyid = xmalloc(k11->keyid_len);
518 memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000519
djm@openbsd.orgfcb1b092019-01-20 23:12:35 +0000520 EC_KEY_set_method(ec, ec_key_method);
djm@openbsd.org445cfce2019-01-20 23:05:52 +0000521 EC_KEY_set_ex_data(ec, ec_key_idx, k11);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000522
523 return (0);
524}
Damien Millere2cb4452019-01-21 11:32:28 +1100525#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +0000526
Damien Miller7ea845e2010-02-12 09:21:02 +1100527/* remove trailing spaces */
528static void
Damien Miller746d1a62013-07-18 16:13:02 +1000529rmspace(u_char *buf, size_t len)
Damien Miller7ea845e2010-02-12 09:21:02 +1100530{
531 size_t i;
532
533 if (!len)
534 return;
535 for (i = len - 1; i > 0; i--)
536 if (i == len - 1 || buf[i] == ' ')
537 buf[i] = '\0';
538 else
539 break;
540}
541
542/*
543 * open a pkcs11 session and login if required.
544 * if pin == NULL we delay login until key use
545 */
546static int
djm@openbsd.org93f02102019-01-20 22:51:37 +0000547pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
548 CK_ULONG user)
Damien Miller7ea845e2010-02-12 09:21:02 +1100549{
550 CK_RV rv;
551 CK_FUNCTION_LIST *f;
552 CK_SESSION_HANDLE session;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000553 int login_required, ret;
Damien Miller7ea845e2010-02-12 09:21:02 +1100554
555 f = p->function_list;
556 login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED;
557 if (pin && login_required && !strlen(pin)) {
558 error("pin required");
djm@openbsd.org93f02102019-01-20 22:51:37 +0000559 return (-SSH_PKCS11_ERR_PIN_REQUIRED);
Damien Miller7ea845e2010-02-12 09:21:02 +1100560 }
561 if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
562 CKF_SERIAL_SESSION, NULL, NULL, &session))
563 != CKR_OK) {
564 error("C_OpenSession failed: %lu", rv);
565 return (-1);
566 }
567 if (login_required && pin) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000568 rv = f->C_Login(session, user,
deraadt@openbsd.orgce4f59b2015-02-03 08:07:20 +0000569 (u_char *)pin, strlen(pin));
djm@openbsd.orgcb3bde32015-02-02 22:48:53 +0000570 if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100571 error("C_Login failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000572 ret = (rv == CKR_PIN_LOCKED) ?
573 -SSH_PKCS11_ERR_PIN_LOCKED :
574 -SSH_PKCS11_ERR_LOGIN_FAIL;
Damien Miller7ea845e2010-02-12 09:21:02 +1100575 if ((rv = f->C_CloseSession(session)) != CKR_OK)
576 error("C_CloseSession failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000577 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +1100578 }
579 p->slotinfo[slotidx].logged_in = 1;
580 }
581 p->slotinfo[slotidx].session = session;
582 return (0);
583}
584
Damien Millerd2252c72013-11-04 07:41:48 +1100585static int
djm@openbsd.org1129dcf2015-01-15 09:40:00 +0000586pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)
Damien Millerd2252c72013-11-04 07:41:48 +1100587{
588 int i;
589
590 for (i = 0; i < *nkeys; i++)
djm@openbsd.org1129dcf2015-01-15 09:40:00 +0000591 if (sshkey_equal(key, (*keysp)[i]))
Damien Millerd2252c72013-11-04 07:41:48 +1100592 return (1);
593 return (0);
594}
595
Damien Millere2cb4452019-01-21 11:32:28 +1100596#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +0000597static struct sshkey *
598pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
599 CK_OBJECT_HANDLE *obj)
600{
601 CK_ATTRIBUTE key_attr[3];
602 CK_SESSION_HANDLE session;
603 CK_FUNCTION_LIST *f = NULL;
604 CK_RV rv;
djm@openbsd.org24757c12019-01-20 23:01:59 +0000605 ASN1_OCTET_STRING *octet = NULL;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000606 EC_KEY *ec = NULL;
607 EC_GROUP *group = NULL;
608 struct sshkey *key = NULL;
609 const unsigned char *attrp = NULL;
610 int i;
611 int nid;
612
613 memset(&key_attr, 0, sizeof(key_attr));
614 key_attr[0].type = CKA_ID;
615 key_attr[1].type = CKA_EC_POINT;
616 key_attr[2].type = CKA_EC_PARAMS;
617
618 session = p->slotinfo[slotidx].session;
619 f = p->function_list;
620
621 /* figure out size of the attributes */
622 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
623 if (rv != CKR_OK) {
624 error("C_GetAttributeValue failed: %lu", rv);
625 return (NULL);
626 }
627
628 /*
629 * Allow CKA_ID (always first attribute) to be empty, but
630 * ensure that none of the others are zero length.
631 * XXX assumes CKA_ID is always first.
632 */
633 if (key_attr[1].ulValueLen == 0 ||
634 key_attr[2].ulValueLen == 0) {
635 error("invalid attribute length");
636 return (NULL);
637 }
638
639 /* allocate buffers for attributes */
640 for (i = 0; i < 3; i++)
641 if (key_attr[i].ulValueLen > 0)
642 key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
643
644 /* retrieve ID, public point and curve parameters of EC key */
645 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
646 if (rv != CKR_OK) {
647 error("C_GetAttributeValue failed: %lu", rv);
648 goto fail;
649 }
650
651 ec = EC_KEY_new();
652 if (ec == NULL) {
653 error("EC_KEY_new failed");
654 goto fail;
655 }
656
657 attrp = key_attr[2].pValue;
658 group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
659 if (group == NULL) {
660 ossl_error("d2i_ECPKParameters failed");
661 goto fail;
662 }
663
664 if (EC_KEY_set_group(ec, group) == 0) {
665 ossl_error("EC_KEY_set_group failed");
666 goto fail;
667 }
668
669 if (key_attr[1].ulValueLen <= 2) {
670 error("CKA_EC_POINT too small");
671 goto fail;
672 }
673
djm@openbsd.org24757c12019-01-20 23:01:59 +0000674 attrp = key_attr[1].pValue;
675 octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
676 if (octet == NULL) {
677 ossl_error("d2i_ASN1_OCTET_STRING failed");
678 goto fail;
679 }
680 attrp = octet->data;
681 if (o2i_ECPublicKey(&ec, &attrp, octet->length) == NULL) {
682 ossl_error("o2i_ECPublicKey failed");
683 goto fail;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000684 }
685
686 nid = sshkey_ecdsa_key_to_nid(ec);
687 if (nid < 0) {
688 error("couldn't get curve nid");
689 goto fail;
690 }
691
692 if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
693 goto fail;
694
695 key = sshkey_new(KEY_UNSPEC);
696 if (key == NULL) {
697 error("sshkey_new failed");
698 goto fail;
699 }
700
701 key->ecdsa = ec;
702 key->ecdsa_nid = nid;
703 key->type = KEY_ECDSA;
704 key->flags |= SSHKEY_FLAG_EXT;
705 ec = NULL; /* now owned by key */
706
707fail:
708 for (i = 0; i < 3; i++)
709 free(key_attr[i].pValue);
710 if (ec)
711 EC_KEY_free(ec);
712 if (group)
713 EC_GROUP_free(group);
djm@openbsd.org24757c12019-01-20 23:01:59 +0000714 if (octet)
715 ASN1_OCTET_STRING_free(octet);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000716
717 return (key);
718}
Damien Millere2cb4452019-01-21 11:32:28 +1100719#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +0000720
721static struct sshkey *
722pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
723 CK_OBJECT_HANDLE *obj)
724{
725 CK_ATTRIBUTE key_attr[3];
726 CK_SESSION_HANDLE session;
727 CK_FUNCTION_LIST *f = NULL;
728 CK_RV rv;
729 RSA *rsa = NULL;
730 BIGNUM *rsa_n, *rsa_e;
731 struct sshkey *key = NULL;
732 int i;
733
734 memset(&key_attr, 0, sizeof(key_attr));
735 key_attr[0].type = CKA_ID;
736 key_attr[1].type = CKA_MODULUS;
737 key_attr[2].type = CKA_PUBLIC_EXPONENT;
738
739 session = p->slotinfo[slotidx].session;
740 f = p->function_list;
741
742 /* figure out size of the attributes */
743 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
744 if (rv != CKR_OK) {
745 error("C_GetAttributeValue failed: %lu", rv);
746 return (NULL);
747 }
748
749 /*
750 * Allow CKA_ID (always first attribute) to be empty, but
751 * ensure that none of the others are zero length.
752 * XXX assumes CKA_ID is always first.
753 */
754 if (key_attr[1].ulValueLen == 0 ||
755 key_attr[2].ulValueLen == 0) {
756 error("invalid attribute length");
757 return (NULL);
758 }
759
760 /* allocate buffers for attributes */
761 for (i = 0; i < 3; i++)
762 if (key_attr[i].ulValueLen > 0)
763 key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
764
765 /* retrieve ID, modulus and public exponent of RSA key */
766 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
767 if (rv != CKR_OK) {
768 error("C_GetAttributeValue failed: %lu", rv);
769 goto fail;
770 }
771
772 rsa = RSA_new();
773 if (rsa == NULL) {
774 error("RSA_new failed");
775 goto fail;
776 }
777
778 rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
779 rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
780 if (rsa_n == NULL || rsa_e == NULL) {
781 error("BN_bin2bn failed");
782 goto fail;
783 }
784 if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
785 fatal("%s: set key", __func__);
786 rsa_n = rsa_e = NULL; /* transferred */
787
788 if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
789 goto fail;
790
791 key = sshkey_new(KEY_UNSPEC);
792 if (key == NULL) {
793 error("sshkey_new failed");
794 goto fail;
795 }
796
797 key->rsa = rsa;
798 key->type = KEY_RSA;
799 key->flags |= SSHKEY_FLAG_EXT;
800 rsa = NULL; /* now owned by key */
801
802fail:
803 for (i = 0; i < 3; i++)
804 free(key_attr[i].pValue);
805 RSA_free(rsa);
806
807 return (key);
808}
809
810static struct sshkey *
811pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
812 CK_OBJECT_HANDLE *obj)
813{
814 CK_ATTRIBUTE cert_attr[3];
815 CK_SESSION_HANDLE session;
816 CK_FUNCTION_LIST *f = NULL;
817 CK_RV rv;
818 X509 *x509 = NULL;
819 EVP_PKEY *evp;
820 RSA *rsa = NULL;
821 EC_KEY *ec = NULL;
822 struct sshkey *key = NULL;
823 int i;
Damien Millere2cb4452019-01-21 11:32:28 +1100824#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +0000825 int nid;
Damien Millere2cb4452019-01-21 11:32:28 +1100826#endif
djm@openbsd.org93f02102019-01-20 22:51:37 +0000827 const u_char *cp;
828
829 memset(&cert_attr, 0, sizeof(cert_attr));
830 cert_attr[0].type = CKA_ID;
831 cert_attr[1].type = CKA_SUBJECT;
832 cert_attr[2].type = CKA_VALUE;
833
834 session = p->slotinfo[slotidx].session;
835 f = p->function_list;
836
837 /* figure out size of the attributes */
838 rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
839 if (rv != CKR_OK) {
840 error("C_GetAttributeValue failed: %lu", rv);
841 return (NULL);
842 }
843
844 /*
845 * Allow CKA_ID (always first attribute) to be empty, but
846 * ensure that none of the others are zero length.
847 * XXX assumes CKA_ID is always first.
848 */
849 if (cert_attr[1].ulValueLen == 0 ||
850 cert_attr[2].ulValueLen == 0) {
851 error("invalid attribute length");
852 return (NULL);
853 }
854
855 /* allocate buffers for attributes */
856 for (i = 0; i < 3; i++)
857 if (cert_attr[i].ulValueLen > 0)
858 cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
859
860 /* retrieve ID, subject and value of certificate */
861 rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
862 if (rv != CKR_OK) {
863 error("C_GetAttributeValue failed: %lu", rv);
864 goto fail;
865 }
866
867 x509 = X509_new();
868 if (x509 == NULL) {
869 error("x509_new failed");
870 goto fail;
871 }
872
873 cp = cert_attr[2].pValue;
874 if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) {
875 error("d2i_x509 failed");
876 goto fail;
877 }
878
879 evp = X509_get_pubkey(x509);
880 if (evp == NULL) {
881 error("X509_get_pubkey failed");
882 goto fail;
883 }
884
885 if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) {
886 if (EVP_PKEY_get0_RSA(evp) == NULL) {
887 error("invalid x509; no rsa key");
888 goto fail;
889 }
890 if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) {
891 error("RSAPublicKey_dup failed");
892 goto fail;
893 }
894
895 if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
896 goto fail;
897
898 key = sshkey_new(KEY_UNSPEC);
899 if (key == NULL) {
900 error("sshkey_new failed");
901 goto fail;
902 }
903
904 key->rsa = rsa;
905 key->type = KEY_RSA;
906 key->flags |= SSHKEY_FLAG_EXT;
907 rsa = NULL; /* now owned by key */
Damien Millere2cb4452019-01-21 11:32:28 +1100908#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +0000909 } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) {
djm@openbsd.org8a246752019-01-20 23:03:26 +0000910 if (EVP_PKEY_get0_EC_KEY(evp) == NULL) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000911 error("invalid x509; no ec key");
912 goto fail;
913 }
djm@openbsd.org8a246752019-01-20 23:03:26 +0000914 if ((ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(evp))) == NULL) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000915 error("EC_KEY_dup failed");
916 goto fail;
917 }
918
919 nid = sshkey_ecdsa_key_to_nid(ec);
920 if (nid < 0) {
921 error("couldn't get curve nid");
922 goto fail;
923 }
924
925 if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
926 goto fail;
927
928 key = sshkey_new(KEY_UNSPEC);
929 if (key == NULL) {
930 error("sshkey_new failed");
931 goto fail;
932 }
933
934 key->ecdsa = ec;
935 key->ecdsa_nid = nid;
936 key->type = KEY_ECDSA;
937 key->flags |= SSHKEY_FLAG_EXT;
938 ec = NULL; /* now owned by key */
Damien Millere2cb4452019-01-21 11:32:28 +1100939#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +0000940 } else
941 error("unknown certificate key type");
942
943fail:
944 for (i = 0; i < 3; i++)
945 free(cert_attr[i].pValue);
946 X509_free(x509);
947 RSA_free(rsa);
948 EC_KEY_free(ec);
949
950 return (key);
951}
952
953#if 0
Damien Millerd2252c72013-11-04 07:41:48 +1100954static int
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000955have_rsa_key(const RSA *rsa)
956{
957 const BIGNUM *rsa_n, *rsa_e;
958
959 RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
960 return rsa_n != NULL && rsa_e != NULL;
961}
djm@openbsd.org93f02102019-01-20 22:51:37 +0000962#endif
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000963
djm@openbsd.org93f02102019-01-20 22:51:37 +0000964/*
965 * lookup certificates for token in slot identified by slotidx,
966 * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
967 * keysp points to an (possibly empty) array with *nkeys keys.
968 */
djm@openbsd.org482d23b2018-09-13 02:08:33 +0000969static int
djm@openbsd.org93f02102019-01-20 22:51:37 +0000970pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
djm@openbsd.org1129dcf2015-01-15 09:40:00 +0000971 struct sshkey ***keysp, int *nkeys)
Damien Miller7ea845e2010-02-12 09:21:02 +1100972{
djm@openbsd.org93f02102019-01-20 22:51:37 +0000973 struct sshkey *key = NULL;
974 CK_OBJECT_CLASS key_class;
975 CK_ATTRIBUTE key_attr[1];
976 CK_SESSION_HANDLE session;
977 CK_FUNCTION_LIST *f = NULL;
978 CK_RV rv;
979 CK_OBJECT_HANDLE obj;
980 CK_ULONG n = 0;
981 int ret = -1;
Tim Rice179eee02010-03-04 12:48:05 -0800982
djm@openbsd.org93f02102019-01-20 22:51:37 +0000983 memset(&key_attr, 0, sizeof(key_attr));
984 memset(&obj, 0, sizeof(obj));
985
986 key_class = CKO_CERTIFICATE;
987 key_attr[0].type = CKA_CLASS;
988 key_attr[0].pValue = &key_class;
989 key_attr[0].ulValueLen = sizeof(key_class);
990
Damien Miller7ea845e2010-02-12 09:21:02 +1100991 session = p->slotinfo[slotidx].session;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000992 f = p->function_list;
993
994 rv = f->C_FindObjectsInit(session, key_attr, 1);
995 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100996 error("C_FindObjectsInit failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000997 goto fail;
Damien Miller7ea845e2010-02-12 09:21:02 +1100998 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000999
Damien Miller7ea845e2010-02-12 09:21:02 +11001000 while (1) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001001 CK_CERTIFICATE_TYPE ck_cert_type;
1002
1003 rv = f->C_FindObjects(session, &obj, 1, &n);
1004 if (rv != CKR_OK) {
1005 error("C_FindObjects failed: %lu", rv);
1006 goto fail;
Damien Miller7ea845e2010-02-12 09:21:02 +11001007 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001008 if (n == 0)
Damien Miller7ea845e2010-02-12 09:21:02 +11001009 break;
djm@openbsd.org93f02102019-01-20 22:51:37 +00001010
1011 memset(&ck_cert_type, 0, sizeof(ck_cert_type));
1012 memset(&key_attr, 0, sizeof(key_attr));
1013 key_attr[0].type = CKA_CERTIFICATE_TYPE;
1014 key_attr[0].pValue = &ck_cert_type;
1015 key_attr[0].ulValueLen = sizeof(ck_cert_type);
1016
1017 rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
1018 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +11001019 error("C_GetAttributeValue failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001020 goto fail;
djm@openbsd.org63ebcd02015-07-18 08:02:17 +00001021 }
1022
djm@openbsd.org93f02102019-01-20 22:51:37 +00001023 switch (ck_cert_type) {
1024 case CKC_X_509:
1025 key = pkcs11_fetch_x509_pubkey(p, slotidx, &obj);
1026 break;
1027 default:
1028 /* XXX print key type? */
1029 error("skipping unsupported certificate type");
1030 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001031
djm@openbsd.org93f02102019-01-20 22:51:37 +00001032 if (key == NULL) {
1033 error("failed to fetch key");
1034 continue;
1035 }
1036
1037 if (pkcs11_key_included(keysp, nkeys, key)) {
1038 sshkey_free(key);
Damien Miller7ea845e2010-02-12 09:21:02 +11001039 } else {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001040 /* expand key array and add key */
1041 *keysp = xrecallocarray(*keysp, *nkeys,
1042 *nkeys + 1, sizeof(struct sshkey *));
1043 (*keysp)[*nkeys] = key;
1044 *nkeys = *nkeys + 1;
1045 debug("have %d keys", *nkeys);
Damien Millerd2252c72013-11-04 07:41:48 +11001046 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001047 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001048
1049 ret = 0;
1050fail:
1051 rv = f->C_FindObjectsFinal(session);
1052 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +11001053 error("C_FindObjectsFinal failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001054 ret = -1;
1055 }
1056
1057 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +11001058}
1059
djm@openbsd.org93f02102019-01-20 22:51:37 +00001060/*
1061 * lookup public keys for token in slot identified by slotidx,
1062 * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
1063 * keysp points to an (possibly empty) array with *nkeys keys.
1064 */
1065static int
1066pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
1067 struct sshkey ***keysp, int *nkeys)
1068{
1069 struct sshkey *key = NULL;
1070 CK_OBJECT_CLASS key_class;
1071 CK_ATTRIBUTE key_attr[1];
1072 CK_SESSION_HANDLE session;
1073 CK_FUNCTION_LIST *f = NULL;
1074 CK_RV rv;
1075 CK_OBJECT_HANDLE obj;
1076 CK_ULONG n = 0;
1077 int ret = -1;
1078
1079 memset(&key_attr, 0, sizeof(key_attr));
1080 memset(&obj, 0, sizeof(obj));
1081
1082 key_class = CKO_PUBLIC_KEY;
1083 key_attr[0].type = CKA_CLASS;
1084 key_attr[0].pValue = &key_class;
1085 key_attr[0].ulValueLen = sizeof(key_class);
1086
1087 session = p->slotinfo[slotidx].session;
1088 f = p->function_list;
1089
1090 rv = f->C_FindObjectsInit(session, key_attr, 1);
1091 if (rv != CKR_OK) {
1092 error("C_FindObjectsInit failed: %lu", rv);
1093 goto fail;
1094 }
1095
1096 while (1) {
1097 CK_KEY_TYPE ck_key_type;
1098
1099 rv = f->C_FindObjects(session, &obj, 1, &n);
1100 if (rv != CKR_OK) {
1101 error("C_FindObjects failed: %lu", rv);
1102 goto fail;
1103 }
1104 if (n == 0)
1105 break;
1106
1107 memset(&ck_key_type, 0, sizeof(ck_key_type));
1108 memset(&key_attr, 0, sizeof(key_attr));
1109 key_attr[0].type = CKA_KEY_TYPE;
1110 key_attr[0].pValue = &ck_key_type;
1111 key_attr[0].ulValueLen = sizeof(ck_key_type);
1112
1113 rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
1114 if (rv != CKR_OK) {
1115 error("C_GetAttributeValue failed: %lu", rv);
1116 goto fail;
1117 }
1118
1119 switch (ck_key_type) {
1120 case CKK_RSA:
1121 key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
1122 break;
Damien Millere2cb4452019-01-21 11:32:28 +11001123#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +00001124 case CKK_ECDSA:
1125 key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
1126 break;
Damien Millere2cb4452019-01-21 11:32:28 +11001127#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +00001128 default:
1129 /* XXX print key type? */
1130 error("skipping unsupported key type");
1131 }
1132
1133 if (key == NULL) {
1134 error("failed to fetch key");
1135 continue;
1136 }
1137
1138 if (pkcs11_key_included(keysp, nkeys, key)) {
1139 sshkey_free(key);
1140 } else {
1141 /* expand key array and add key */
1142 *keysp = xrecallocarray(*keysp, *nkeys,
1143 *nkeys + 1, sizeof(struct sshkey *));
1144 (*keysp)[*nkeys] = key;
1145 *nkeys = *nkeys + 1;
1146 debug("have %d keys", *nkeys);
1147 }
1148 }
1149
1150 ret = 0;
1151fail:
1152 rv = f->C_FindObjectsFinal(session);
1153 if (rv != CKR_OK) {
1154 error("C_FindObjectsFinal failed: %lu", rv);
1155 ret = -1;
1156 }
1157
1158 return (ret);
1159}
1160
1161#ifdef WITH_PKCS11_KEYGEN
1162#define FILL_ATTR(attr, idx, typ, val, len) \
1163 { (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; }
1164
1165static struct sshkey *
1166pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
1167 char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
1168{
1169 struct pkcs11_slotinfo *si;
1170 char *plabel = label ? label : "";
1171 int npub = 0, npriv = 0;
1172 CK_RV rv;
1173 CK_FUNCTION_LIST *f;
1174 CK_SESSION_HANDLE session;
1175 CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE;
1176 CK_OBJECT_HANDLE pubKey, privKey;
1177 CK_ATTRIBUTE tpub[16], tpriv[16];
1178 CK_MECHANISM mech = {
1179 CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0
1180 };
1181 CK_BYTE pubExponent[] = {
1182 0x01, 0x00, 0x01 /* RSA_F4 in bytes */
1183 };
1184 pubkey_filter[0].pValue = &pubkey_class;
1185 cert_filter[0].pValue = &cert_class;
1186
1187 *err = 0;
1188
1189 FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
1190 FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
1191 FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
1192 FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
1193 FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
1194 sizeof(false_val));
1195 FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
1196 FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
1197 FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits));
1198 FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent,
1199 sizeof(pubExponent));
1200 FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
1201
1202 FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
1203 FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
1204 FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
1205 FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
1206 FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
1207 FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
1208 FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
1209 sizeof(false_val));
1210 FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
1211 FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
1212 FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
1213
1214 f = p->function_list;
1215 si = &p->slotinfo[slotidx];
1216 session = si->session;
1217
1218 if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
1219 &pubKey, &privKey)) != CKR_OK) {
1220 error("%s: key generation failed: error 0x%lx", __func__, rv);
1221 *err = rv;
1222 return NULL;
1223 }
1224
1225 return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey);
1226}
1227
1228static int
1229pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen)
1230{
1231 size_t i, len;
1232 char ptr[3];
1233
1234 if (dest)
1235 *dest = NULL;
1236 if (rlen)
1237 *rlen = 0;
1238
1239 if ((len = strlen(hex)) % 2)
1240 return -1;
1241 len /= 2;
1242
1243 *dest = xmalloc(len);
1244
1245 ptr[2] = '\0';
1246 for (i = 0; i < len; i++) {
1247 ptr[0] = hex[2 * i];
1248 ptr[1] = hex[(2 * i) + 1];
1249 if (!isxdigit(ptr[0]) || !isxdigit(ptr[1]))
1250 return -1;
1251 (*dest)[i] = (unsigned char)strtoul(ptr, NULL, 16);
1252 }
1253
1254 if (rlen)
1255 *rlen = len;
1256
1257 return 0;
1258}
1259
1260static struct ec_curve_info {
1261 const char *name;
1262 const char *oid;
1263 const char *oid_encoded;
1264 size_t size;
1265} ec_curve_infos[] = {
1266 {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256},
1267 {"secp384r1", "1.3.132.0.34", "06052B81040022", 384},
1268 {"secp521r1", "1.3.132.0.35", "06052B81040023", 521},
djm@openbsd.org65294092019-01-20 23:11:11 +00001269 {NULL, NULL, NULL, 0},
djm@openbsd.org93f02102019-01-20 22:51:37 +00001270};
1271
1272static struct sshkey *
1273pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
1274 char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
1275{
1276 struct pkcs11_slotinfo *si;
1277 char *plabel = label ? label : "";
1278 int i;
1279 size_t ecparams_size;
1280 unsigned char *ecparams = NULL;
1281 int npub = 0, npriv = 0;
1282 CK_RV rv;
1283 CK_FUNCTION_LIST *f;
1284 CK_SESSION_HANDLE session;
1285 CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE;
1286 CK_OBJECT_HANDLE pubKey, privKey;
1287 CK_MECHANISM mech = {
1288 CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
1289 };
1290 CK_ATTRIBUTE tpub[16], tpriv[16];
1291
1292 *err = 0;
1293
1294 for (i = 0; ec_curve_infos[i].name; i++) {
1295 if (ec_curve_infos[i].size == bits)
1296 break;
1297 }
1298 if (!ec_curve_infos[i].name) {
1299 error("%s: invalid key size %lu", __func__, bits);
1300 return NULL;
1301 }
1302 if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams,
1303 &ecparams_size) == -1) {
1304 error("%s: invalid oid", __func__);
1305 return NULL;
1306 }
1307
1308 FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
1309 FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
1310 FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
1311 FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
1312 FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
1313 sizeof(false_val));
1314 FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
1315 FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
1316 FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size);
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_ecdsa_pubkey(p, slotidx, &pubKey);
1343}
1344#endif /* WITH_PKCS11_KEYGEN */
1345
1346/*
1347 * register a new provider, fails if provider already exists. if
1348 * keyp is provided, fetch keys.
1349 */
1350static int
1351pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
1352 struct pkcs11_provider **providerp, CK_ULONG user)
Damien Miller7ea845e2010-02-12 09:21:02 +11001353{
1354 int nkeys, need_finalize = 0;
djm@openbsd.org93f02102019-01-20 22:51:37 +00001355 int ret = -1;
Damien Miller7ea845e2010-02-12 09:21:02 +11001356 struct pkcs11_provider *p = NULL;
1357 void *handle = NULL;
1358 CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
1359 CK_RV rv;
1360 CK_FUNCTION_LIST *f = NULL;
1361 CK_TOKEN_INFO *token;
1362 CK_ULONG i;
1363
djm@openbsd.org93f02102019-01-20 22:51:37 +00001364 if (providerp == NULL)
1365 goto fail;
1366 *providerp = NULL;
1367
1368 if (keyp != NULL)
1369 *keyp = NULL;
1370
Damien Miller7ea845e2010-02-12 09:21:02 +11001371 if (pkcs11_provider_lookup(provider_id) != NULL) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001372 debug("%s: provider already registered: %s",
1373 __func__, provider_id);
Damien Miller7ea845e2010-02-12 09:21:02 +11001374 goto fail;
1375 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001376 /* open shared pkcs11-library */
Damien Miller7ea845e2010-02-12 09:21:02 +11001377 if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
1378 error("dlopen %s failed: %s", provider_id, dlerror());
1379 goto fail;
1380 }
1381 if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
1382 error("dlsym(C_GetFunctionList) failed: %s", dlerror());
1383 goto fail;
1384 }
1385 p = xcalloc(1, sizeof(*p));
1386 p->name = xstrdup(provider_id);
1387 p->handle = handle;
1388 /* setup the pkcs11 callbacks */
1389 if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001390 error("C_GetFunctionList for provider %s failed: %lu",
1391 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001392 goto fail;
1393 }
1394 p->function_list = f;
1395 if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001396 error("C_Initialize for provider %s failed: %lu",
1397 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001398 goto fail;
1399 }
1400 need_finalize = 1;
1401 if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001402 error("C_GetInfo for provider %s failed: %lu",
1403 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001404 goto fail;
1405 }
1406 rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
1407 rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001408 debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d"
Damien Miller7ea845e2010-02-12 09:21:02 +11001409 " libraryDescription <%s> libraryVersion %d.%d",
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001410 provider_id,
Damien Miller7ea845e2010-02-12 09:21:02 +11001411 p->info.manufacturerID,
1412 p->info.cryptokiVersion.major,
1413 p->info.cryptokiVersion.minor,
1414 p->info.libraryDescription,
1415 p->info.libraryVersion.major,
1416 p->info.libraryVersion.minor);
1417 if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
1418 error("C_GetSlotList failed: %lu", rv);
1419 goto fail;
1420 }
1421 if (p->nslots == 0) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001422 error("%s: provider %s returned no slots", __func__,
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001423 provider_id);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001424 ret = -SSH_PKCS11_ERR_NO_SLOTS;
Damien Miller7ea845e2010-02-12 09:21:02 +11001425 goto fail;
1426 }
1427 p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
1428 if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
1429 != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001430 error("C_GetSlotList for provider %s failed: %lu",
1431 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001432 goto fail;
1433 }
1434 p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
1435 p->valid = 1;
1436 nkeys = 0;
1437 for (i = 0; i < p->nslots; i++) {
1438 token = &p->slotinfo[i].token;
1439 if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
1440 != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001441 error("C_GetTokenInfo for provider %s slot %lu "
1442 "failed: %lu", provider_id, (unsigned long)i, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001443 continue;
1444 }
djm@openbsd.orgb15fd982015-07-18 08:00:21 +00001445 if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001446 debug2("%s: ignoring uninitialised token in "
1447 "provider %s slot %lu", __func__,
1448 provider_id, (unsigned long)i);
djm@openbsd.orgb15fd982015-07-18 08:00:21 +00001449 continue;
1450 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001451 rmspace(token->label, sizeof(token->label));
1452 rmspace(token->manufacturerID, sizeof(token->manufacturerID));
1453 rmspace(token->model, sizeof(token->model));
1454 rmspace(token->serialNumber, sizeof(token->serialNumber));
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001455 debug("provider %s slot %lu: label <%s> manufacturerID <%s> "
1456 "model <%s> serial <%s> flags 0x%lx",
1457 provider_id, (unsigned long)i,
Damien Miller7ea845e2010-02-12 09:21:02 +11001458 token->label, token->manufacturerID, token->model,
1459 token->serialNumber, token->flags);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001460 /*
1461 * open session, login with pin and retrieve public
1462 * keys (if keyp is provided)
1463 */
1464 if ((ret = pkcs11_open_session(p, i, pin, user)) == 0) {
1465 if (keyp == NULL)
1466 continue;
Damien Miller7ea845e2010-02-12 09:21:02 +11001467 pkcs11_fetch_keys(p, i, keyp, &nkeys);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001468 pkcs11_fetch_certs(p, i, keyp, &nkeys);
1469 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001470 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001471
1472 /* now owned by caller */
1473 *providerp = p;
1474
1475 TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
1476 p->refcount++; /* add to provider list */
1477
1478 return (nkeys);
Damien Miller7ea845e2010-02-12 09:21:02 +11001479fail:
1480 if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001481 error("C_Finalize for provider %s failed: %lu",
1482 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001483 if (p) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001484 free(p->name);
Darren Tuckera627d422013-06-02 07:31:17 +10001485 free(p->slotlist);
1486 free(p->slotinfo);
1487 free(p);
Damien Miller7ea845e2010-02-12 09:21:02 +11001488 }
1489 if (handle)
1490 dlclose(handle);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001491 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +11001492}
Damien Millerdfa41562010-02-12 10:06:28 +11001493
djm@openbsd.org93f02102019-01-20 22:51:37 +00001494/*
1495 * register a new provider and get number of keys hold by the token,
1496 * fails if provider already exists
1497 */
1498int
1499pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
1500{
1501 struct pkcs11_provider *p = NULL;
1502 int nkeys;
Darren Tucker0dd24e02011-09-04 19:59:26 +10001503
djm@openbsd.org93f02102019-01-20 22:51:37 +00001504 nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER);
1505
1506 /* no keys found or some other error, de-register provider */
1507 if (nkeys <= 0 && p != NULL) {
1508 TAILQ_REMOVE(&pkcs11_providers, p, next);
1509 pkcs11_provider_finalize(p);
1510 pkcs11_provider_unref(p);
1511 }
1512 if (nkeys == 0)
1513 debug("%s: provider %s returned no keys", __func__,
1514 provider_id);
1515
1516 return (nkeys);
1517}
1518
1519#ifdef WITH_PKCS11_KEYGEN
1520struct sshkey *
1521pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
1522 unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err)
1523{
1524 struct pkcs11_provider *p = NULL;
1525 struct pkcs11_slotinfo *si;
1526 CK_FUNCTION_LIST *f;
1527 CK_SESSION_HANDLE session;
1528 struct sshkey *k = NULL;
1529 int ret = -1, reset_pin = 0, reset_provider = 0;
1530 CK_RV rv;
1531
1532 *err = 0;
1533
1534 if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
1535 debug("%s: provider \"%s\" available", __func__, provider_id);
1536 else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p,
1537 CKU_SO)) < 0) {
1538 debug("%s: could not register provider %s", __func__,
1539 provider_id);
1540 goto out;
1541 } else
1542 reset_provider = 1;
1543
1544 f = p->function_list;
1545 si = &p->slotinfo[slotidx];
1546 session = si->session;
1547
1548 if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
1549 CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
1550 debug("%s: could not supply SO pin: %lu", __func__, rv);
1551 reset_pin = 0;
1552 } else
1553 reset_pin = 1;
1554
1555 switch (type) {
1556 case KEY_RSA:
1557 if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label,
1558 bits, keyid, err)) == NULL) {
1559 debug("%s: failed to generate RSA key", __func__);
1560 goto out;
1561 }
1562 break;
1563 case KEY_ECDSA:
1564 if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label,
1565 bits, keyid, err)) == NULL) {
1566 debug("%s: failed to generate ECDSA key", __func__);
1567 goto out;
1568 }
1569 break;
1570 default:
1571 *err = SSH_PKCS11_ERR_GENERIC;
1572 debug("%s: unknown type %d", __func__, type);
1573 goto out;
1574 }
1575
1576out:
1577 if (reset_pin)
1578 f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
1579 CK_INVALID_HANDLE);
1580
1581 if (reset_provider)
1582 pkcs11_del_provider(provider_id);
1583
1584 return (k);
1585}
1586
1587struct sshkey *
1588pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
1589 unsigned char keyid, u_int32_t *err)
1590{
1591 struct pkcs11_provider *p = NULL;
1592 struct pkcs11_slotinfo *si;
1593 struct sshkey *k = NULL;
1594 int reset_pin = 0, reset_provider = 0;
1595 CK_ULONG nattrs;
1596 CK_FUNCTION_LIST *f;
1597 CK_SESSION_HANDLE session;
1598 CK_ATTRIBUTE attrs[16];
1599 CK_OBJECT_CLASS key_class;
1600 CK_KEY_TYPE key_type;
1601 CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE;
1602 CK_RV rv;
1603
1604 *err = 0;
1605
1606 if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
1607 debug("%s: using provider \"%s\"", __func__, provider_id);
1608 } else if (pkcs11_register_provider(provider_id, pin, NULL, &p,
1609 CKU_SO) < 0) {
1610 debug("%s: could not register provider %s", __func__,
1611 provider_id);
1612 goto out;
1613 } else
1614 reset_provider = 1;
1615
1616 f = p->function_list;
1617 si = &p->slotinfo[slotidx];
1618 session = si->session;
1619
1620 if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
1621 CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
1622 debug("%s: could not supply SO pin: %lu", __func__, rv);
1623 reset_pin = 0;
1624 } else
1625 reset_pin = 1;
1626
1627 /* private key */
1628 nattrs = 0;
1629 key_class = CKO_PRIVATE_KEY;
1630 FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
1631 FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
1632
1633 if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
1634 obj != CK_INVALID_HANDLE) {
1635 if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
1636 debug("%s: could not destroy private key 0x%hhx",
1637 __func__, keyid);
1638 *err = rv;
1639 goto out;
1640 }
1641 }
1642
1643 /* public key */
1644 nattrs = 0;
1645 key_class = CKO_PUBLIC_KEY;
1646 FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
1647 FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
1648
1649 if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
1650 obj != CK_INVALID_HANDLE) {
1651
1652 /* get key type */
1653 nattrs = 0;
1654 FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type,
1655 sizeof(key_type));
1656 rv = f->C_GetAttributeValue(session, obj, attrs, nattrs);
1657 if (rv != CKR_OK) {
1658 debug("%s: could not get key type of public key 0x%hhx",
1659 __func__, keyid);
1660 *err = rv;
1661 key_type = -1;
1662 }
1663 if (key_type == CKK_RSA)
1664 k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
1665 else if (key_type == CKK_ECDSA)
1666 k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
1667
1668 if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
1669 debug("%s: could not destroy public key 0x%hhx",
1670 __func__, keyid);
1671 *err = rv;
1672 goto out;
1673 }
1674 }
1675
1676out:
1677 if (reset_pin)
1678 f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
1679 CK_INVALID_HANDLE);
1680
1681 if (reset_provider)
1682 pkcs11_del_provider(provider_id);
1683
1684 return (k);
1685}
1686#endif /* WITH_PKCS11_KEYGEN */
Damien Miller5de6ac22019-01-21 11:44:19 +11001687#else /* ENABLE_PKCS11 */
Darren Tucker0dd24e02011-09-04 19:59:26 +10001688int
1689pkcs11_init(int interactive)
1690{
djm@openbsd.org93f02102019-01-20 22:51:37 +00001691 error("%s: dlopen() not supported", __func__);
1692 return (-1);
1693}
1694
1695int
1696pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
1697{
1698 error("%s: dlopen() not supported", __func__);
1699 return (-1);
Darren Tucker0dd24e02011-09-04 19:59:26 +10001700}
1701
1702void
1703pkcs11_terminate(void)
1704{
djm@openbsd.org93f02102019-01-20 22:51:37 +00001705 error("%s: dlopen() not supported", __func__);
Darren Tucker0dd24e02011-09-04 19:59:26 +10001706}
Damien Miller5de6ac22019-01-21 11:44:19 +11001707#endif /* ENABLE_PKCS11 */