blob: d9413bc4f47c085c63cbcc99382797762b82d3d9 [file] [log] [blame]
markus@openbsd.org2aee9a42019-03-08 17:24:43 +00001/* $OpenBSD: ssh-pkcs11.c,v 1.43 2019/03/08 17:24:43 markus 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
Damien Miller2efcf812019-01-21 11:57:21 +110081#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +000082static 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.org662be402019-01-21 02:05:38 +000087 error("%s: %s", __func__, msg);
djm@openbsd.org65294092019-01-20 23:11:11 +000088 while ((e = ERR_get_error()) != 0)
djm@openbsd.org662be402019-01-21 02:05:38 +000089 error("%s: libcrypto error: %.100s", __func__,
djm@openbsd.org65294092019-01-20 23:11:11 +000090 ERR_error_string(e, NULL));
djm@openbsd.org93f02102019-01-20 22:51:37 +000091}
Damien Miller2efcf812019-01-21 11:57:21 +110092#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +000093
Damien Miller7ea845e2010-02-12 09:21:02 +110094int
95pkcs11_init(int interactive)
96{
97 pkcs11_interactive = interactive;
98 TAILQ_INIT(&pkcs11_providers);
99 return (0);
100}
101
102/*
djm@openbsd.org93f02102019-01-20 22:51:37 +0000103 * finalize a provider shared library, it's no longer usable.
Damien Miller7ea845e2010-02-12 09:21:02 +1100104 * however, there might still be keys referencing this provider,
djm@openbsd.org93f02102019-01-20 22:51:37 +0000105 * so the actual freeing of memory is handled by pkcs11_provider_unref().
Damien Miller7ea845e2010-02-12 09:21:02 +1100106 * this is called when a provider gets unregistered.
107 */
108static void
109pkcs11_provider_finalize(struct pkcs11_provider *p)
110{
111 CK_RV rv;
112 CK_ULONG i;
113
114 debug("pkcs11_provider_finalize: %p refcount %d valid %d",
115 p, p->refcount, p->valid);
116 if (!p->valid)
117 return;
118 for (i = 0; i < p->nslots; i++) {
119 if (p->slotinfo[i].session &&
120 (rv = p->function_list->C_CloseSession(
121 p->slotinfo[i].session)) != CKR_OK)
122 error("C_CloseSession failed: %lu", rv);
123 }
124 if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
125 error("C_Finalize failed: %lu", rv);
126 p->valid = 0;
127 p->function_list = NULL;
128 dlclose(p->handle);
129}
130
131/*
132 * remove a reference to the provider.
133 * called when a key gets destroyed or when the provider is unregistered.
134 */
135static void
136pkcs11_provider_unref(struct pkcs11_provider *p)
137{
138 debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
139 if (--p->refcount <= 0) {
140 if (p->valid)
141 error("pkcs11_provider_unref: %p still valid", p);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000142 free(p->name);
Darren Tuckera627d422013-06-02 07:31:17 +1000143 free(p->slotlist);
144 free(p->slotinfo);
145 free(p);
Damien Miller7ea845e2010-02-12 09:21:02 +1100146 }
147}
148
149/* unregister all providers, keys might still point to the providers */
150void
151pkcs11_terminate(void)
152{
153 struct pkcs11_provider *p;
154
155 while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
156 TAILQ_REMOVE(&pkcs11_providers, p, next);
157 pkcs11_provider_finalize(p);
158 pkcs11_provider_unref(p);
159 }
160}
161
162/* lookup provider by name */
163static struct pkcs11_provider *
164pkcs11_provider_lookup(char *provider_id)
165{
166 struct pkcs11_provider *p;
167
168 TAILQ_FOREACH(p, &pkcs11_providers, next) {
169 debug("check %p %s", p, p->name);
170 if (!strcmp(provider_id, p->name))
171 return (p);
172 }
173 return (NULL);
174}
175
176/* unregister provider by name */
177int
178pkcs11_del_provider(char *provider_id)
179{
180 struct pkcs11_provider *p;
181
182 if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
183 TAILQ_REMOVE(&pkcs11_providers, p, next);
184 pkcs11_provider_finalize(p);
185 pkcs11_provider_unref(p);
186 return (0);
187 }
188 return (-1);
189}
190
djm@openbsd.orgce46c3a2019-01-21 02:01:03 +0000191static RSA_METHOD *rsa_method;
192static int rsa_idx = 0;
Damien Miller23490a62019-01-21 15:05:43 +1100193#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.orgce46c3a2019-01-21 02:01:03 +0000194static EC_KEY_METHOD *ec_key_method;
195static int ec_key_idx = 0;
Darren Tuckerb6dd3272019-01-21 13:50:17 +1100196#endif
djm@openbsd.orgce46c3a2019-01-21 02:01:03 +0000197
djm@openbsd.org58622a82019-01-20 23:10:33 +0000198/* release a wrapped object */
199static void
200pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
201 long argl, void *argp)
Damien Miller7ea845e2010-02-12 09:21:02 +1100202{
Darren Tucker5590f532019-04-26 18:22:10 +1000203 struct pkcs11_key *k11 = ptr;
Damien Miller7ea845e2010-02-12 09:21:02 +1100204
Darren Tucker5590f532019-04-26 18:22:10 +1000205 debug("%s: parent %p ptr %p idx %d", __func__, parent, ptr, idx);
206 if (k11 == NULL)
207 return;
208 if (k11->provider)
209 pkcs11_provider_unref(k11->provider);
210 free(k11->keyid);
211 free(k11);
Damien Miller7ea845e2010-02-12 09:21:02 +1100212}
213
Damien Miller031c9102010-04-16 15:54:44 +1000214/* find a single 'obj' for given attributes */
215static int
216pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
217 CK_ULONG nattr, CK_OBJECT_HANDLE *obj)
218{
219 CK_FUNCTION_LIST *f;
220 CK_SESSION_HANDLE session;
221 CK_ULONG nfound = 0;
222 CK_RV rv;
223 int ret = -1;
224
225 f = p->function_list;
226 session = p->slotinfo[slotidx].session;
227 if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
228 error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
229 return (-1);
230 }
231 if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK ||
232 nfound != 1) {
233 debug("C_FindObjects failed (nfound %lu nattr %lu): %lu",
234 nfound, nattr, rv);
235 } else
236 ret = 0;
237 if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
238 error("C_FindObjectsFinal failed: %lu", rv);
239 return (ret);
240}
241
Damien Miller7ea845e2010-02-12 09:21:02 +1100242static int
djm@openbsd.org21621712019-01-22 12:00:50 +0000243pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type)
244{
245 struct pkcs11_slotinfo *si;
246 CK_FUNCTION_LIST *f;
247 char *pin = NULL, prompt[1024];
248 CK_RV rv;
249
250 if (!k11->provider || !k11->provider->valid) {
251 error("no pkcs11 (valid) provider found");
252 return (-1);
253 }
254
255 f = k11->provider->function_list;
256 si = &k11->provider->slotinfo[k11->slotidx];
257
258 if (!pkcs11_interactive) {
259 error("need pin entry%s",
260 (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) ?
261 " on reader keypad" : "");
262 return (-1);
263 }
264 if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
265 verbose("Deferring PIN entry to reader keypad.");
266 else {
267 snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
268 si->token.label);
269 if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) {
270 debug("%s: no pin specified", __func__);
271 return (-1); /* bail out */
272 }
273 }
274 rv = f->C_Login(si->session, type, (u_char *)pin,
275 (pin != NULL) ? strlen(pin) : 0);
276 if (pin != NULL)
277 freezero(pin, strlen(pin));
278 if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
279 error("C_Login failed: %lu", rv);
280 return (-1);
281 }
282 si->logged_in = 1;
283 return (0);
284}
285
286static int
287pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj,
288 CK_ATTRIBUTE_TYPE type, int *val)
289{
290 struct pkcs11_slotinfo *si;
291 CK_FUNCTION_LIST *f;
292 CK_BBOOL flag = 0;
293 CK_ATTRIBUTE attr;
294 CK_RV rv;
295
296 *val = 0;
297
298 if (!k11->provider || !k11->provider->valid) {
299 error("no pkcs11 (valid) provider found");
300 return (-1);
301 }
302
303 f = k11->provider->function_list;
304 si = &k11->provider->slotinfo[k11->slotidx];
305
306 attr.type = type;
307 attr.pValue = &flag;
308 attr.ulValueLen = sizeof(flag);
309
310 rv = f->C_GetAttributeValue(si->session, obj, &attr, 1);
311 if (rv != CKR_OK) {
312 error("C_GetAttributeValue failed: %lu", rv);
313 return (-1);
314 }
315 *val = flag != 0;
316 debug("%s: provider %p slot %lu object %lu: attrib %lu = %d",
317 __func__, k11->provider, k11->slotidx, obj, type, *val);
318 return (0);
319}
320
321static int
djm@openbsd.org93f02102019-01-20 22:51:37 +0000322pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
Damien Miller7ea845e2010-02-12 09:21:02 +1100323{
Damien Miller7ea845e2010-02-12 09:21:02 +1100324 struct pkcs11_slotinfo *si;
325 CK_FUNCTION_LIST *f;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000326 CK_OBJECT_HANDLE obj;
327 CK_RV rv;
328 CK_OBJECT_CLASS private_key_class;
329 CK_BBOOL true_val;
330 CK_MECHANISM mech;
331 CK_ATTRIBUTE key_filter[3];
djm@openbsd.org21621712019-01-22 12:00:50 +0000332 int always_auth = 0;
333 int did_login = 0;
Damien Miller7ea845e2010-02-12 09:21:02 +1100334
Damien Miller7ea845e2010-02-12 09:21:02 +1100335 if (!k11->provider || !k11->provider->valid) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000336 error("no pkcs11 (valid) provider found");
Damien Miller7ea845e2010-02-12 09:21:02 +1100337 return (-1);
338 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000339
Damien Miller7ea845e2010-02-12 09:21:02 +1100340 f = k11->provider->function_list;
341 si = &k11->provider->slotinfo[k11->slotidx];
djm@openbsd.org93f02102019-01-20 22:51:37 +0000342
Damien Miller7ea845e2010-02-12 09:21:02 +1100343 if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
djm@openbsd.org21621712019-01-22 12:00:50 +0000344 if (pkcs11_login(k11, CKU_USER) < 0) {
345 error("login failed");
Damien Miller7ea845e2010-02-12 09:21:02 +1100346 return (-1);
347 }
djm@openbsd.org21621712019-01-22 12:00:50 +0000348 did_login = 1;
Damien Miller7ea845e2010-02-12 09:21:02 +1100349 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000350
351 memset(&key_filter, 0, sizeof(key_filter));
352 private_key_class = CKO_PRIVATE_KEY;
353 key_filter[0].type = CKA_CLASS;
354 key_filter[0].pValue = &private_key_class;
355 key_filter[0].ulValueLen = sizeof(private_key_class);
356
357 key_filter[1].type = CKA_ID;
Damien Miller7ea845e2010-02-12 09:21:02 +1100358 key_filter[1].pValue = k11->keyid;
359 key_filter[1].ulValueLen = k11->keyid_len;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000360
361 true_val = CK_TRUE;
362 key_filter[2].type = CKA_SIGN;
363 key_filter[2].pValue = &true_val;
364 key_filter[2].ulValueLen = sizeof(true_val);
365
Damien Miller031c9102010-04-16 15:54:44 +1000366 /* try to find object w/CKA_SIGN first, retry w/o */
367 if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&
368 pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {
369 error("cannot find private key");
djm@openbsd.org93f02102019-01-20 22:51:37 +0000370 return (-1);
Damien Miller7ea845e2010-02-12 09:21:02 +1100371 }
djm@openbsd.org93f02102019-01-20 22:51:37 +0000372
373 memset(&mech, 0, sizeof(mech));
374 mech.mechanism = mech_type;
375 mech.pParameter = NULL_PTR;
376 mech.ulParameterLen = 0;
377
378 if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
379 error("C_SignInit failed: %lu", rv);
380 return (-1);
381 }
382
djm@openbsd.org21621712019-01-22 12:00:50 +0000383 pkcs11_check_obj_bool_attrib(k11, obj, CKA_ALWAYS_AUTHENTICATE,
384 &always_auth); /* ignore errors here */
385 if (always_auth && !did_login) {
386 debug("%s: always-auth key", __func__);
387 if (pkcs11_login(k11, CKU_CONTEXT_SPECIFIC) < 0) {
388 error("login failed for always-auth key");
389 return (-1);
390 }
391 }
392
djm@openbsd.org93f02102019-01-20 22:51:37 +0000393 return (0);
394}
395
396/* openssl callback doing the actual signing operation */
397static int
398pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
399 int padding)
400{
401 struct pkcs11_key *k11;
402 struct pkcs11_slotinfo *si;
403 CK_FUNCTION_LIST *f;
404 CK_ULONG tlen = 0;
405 CK_RV rv;
406 int rval = -1;
407
djm@openbsd.orgce46c3a2019-01-21 02:01:03 +0000408 if ((k11 = RSA_get_ex_data(rsa, rsa_idx)) == NULL) {
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000409 error("RSA_get_ex_data failed for rsa %p", rsa);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000410 return (-1);
411 }
412
413 if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) {
414 error("pkcs11_get_key failed");
415 return (-1);
416 }
417
418 f = k11->provider->function_list;
419 si = &k11->provider->slotinfo[k11->slotidx];
420 tlen = RSA_size(rsa);
421
422 /* XXX handle CKR_BUFFER_TOO_SMALL */
423 rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
424 if (rv == CKR_OK)
425 rval = tlen;
426 else
427 error("C_Sign failed: %lu", rv);
428
Damien Miller7ea845e2010-02-12 09:21:02 +1100429 return (rval);
430}
431
432static int
433pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
434 int padding)
435{
436 return (-1);
437}
438
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000439static int
440pkcs11_rsa_start_wrapper(void)
441{
442 if (rsa_method != NULL)
443 return (0);
444 rsa_method = RSA_meth_dup(RSA_get_default_method());
445 if (rsa_method == NULL)
446 return (-1);
djm@openbsd.org58622a82019-01-20 23:10:33 +0000447 rsa_idx = RSA_get_ex_new_index(0, "ssh-pkcs11-rsa",
448 NULL, NULL, pkcs11_k11_free);
449 if (rsa_idx == -1)
450 return (-1);
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000451 if (!RSA_meth_set1_name(rsa_method, "pkcs11") ||
452 !RSA_meth_set_priv_enc(rsa_method, pkcs11_rsa_private_encrypt) ||
djm@openbsd.org58622a82019-01-20 23:10:33 +0000453 !RSA_meth_set_priv_dec(rsa_method, pkcs11_rsa_private_decrypt)) {
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000454 error("%s: setup pkcs11 method failed", __func__);
455 return (-1);
456 }
457 return (0);
458}
459
Damien Miller7ea845e2010-02-12 09:21:02 +1100460/* redirect private key operations for rsa key to pkcs11 token */
461static int
462pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
463 CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
464{
465 struct pkcs11_key *k11;
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000466
467 if (pkcs11_rsa_start_wrapper() == -1)
468 return (-1);
Damien Miller7ea845e2010-02-12 09:21:02 +1100469
470 k11 = xcalloc(1, sizeof(*k11));
471 k11->provider = provider;
472 provider->refcount++; /* provider referenced by RSA key */
473 k11->slotidx = slotidx;
474 /* identify key object on smartcard */
475 k11->keyid_len = keyid_attrib->ulValueLen;
djm@openbsd.orgd2d772f2016-02-12 00:20:30 +0000476 if (k11->keyid_len > 0) {
477 k11->keyid = xmalloc(k11->keyid_len);
478 memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
479 }
djm@openbsd.orgf1185422019-01-20 23:08:24 +0000480
djm@openbsd.orgfcb1b092019-01-20 23:12:35 +0000481 RSA_set_method(rsa, rsa_method);
djm@openbsd.org58622a82019-01-20 23:10:33 +0000482 RSA_set_ex_data(rsa, rsa_idx, k11);
Damien Miller7ea845e2010-02-12 09:21:02 +1100483 return (0);
484}
485
Damien Millere2cb4452019-01-21 11:32:28 +1100486#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +0000487/* openssl callback doing the actual signing operation */
488static ECDSA_SIG *
489ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
490 const BIGNUM *rp, EC_KEY *ec)
491{
492 struct pkcs11_key *k11;
493 struct pkcs11_slotinfo *si;
494 CK_FUNCTION_LIST *f;
495 CK_ULONG siglen = 0, bnlen;
496 CK_RV rv;
497 ECDSA_SIG *ret = NULL;
498 u_char *sig;
djm@openbsd.org63297642019-01-21 00:47:34 +0000499 BIGNUM *r = NULL, *s = NULL;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000500
djm@openbsd.orgce46c3a2019-01-21 02:01:03 +0000501 if ((k11 = EC_KEY_get_ex_data(ec, ec_key_idx)) == NULL) {
djm@openbsd.org93f02102019-01-20 22:51:37 +0000502 ossl_error("EC_KEY_get_key_method_data failed for ec");
503 return (NULL);
504 }
505
506 if (pkcs11_get_key(k11, CKM_ECDSA) == -1) {
507 error("pkcs11_get_key failed");
508 return (NULL);
509 }
510
511 f = k11->provider->function_list;
512 si = &k11->provider->slotinfo[k11->slotidx];
513
514 siglen = ECDSA_size(ec);
515 sig = xmalloc(siglen);
516
517 /* XXX handle CKR_BUFFER_TOO_SMALL */
518 rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen);
519 if (rv != CKR_OK) {
520 error("C_Sign failed: %lu", rv);
521 goto done;
522 }
djm@openbsd.org749aef32019-01-20 23:00:12 +0000523 if (siglen < 64 || siglen > 132 || siglen % 2) {
524 ossl_error("d2i_ECDSA_SIG failed");
525 goto done;
526 }
527 bnlen = siglen/2;
528 if ((ret = ECDSA_SIG_new()) == NULL) {
529 error("ECDSA_SIG_new failed");
530 goto done;
531 }
djm@openbsd.org63297642019-01-21 00:47:34 +0000532 if ((r = BN_bin2bn(sig, bnlen, NULL)) == NULL ||
533 (s = BN_bin2bn(sig+bnlen, bnlen, NULL)) == NULL) {
djm@openbsd.org749aef32019-01-20 23:00:12 +0000534 ossl_error("d2i_ECDSA_SIG failed");
535 ECDSA_SIG_free(ret);
536 ret = NULL;
537 goto done;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000538 }
djm@openbsd.org63297642019-01-21 00:47:34 +0000539 if (!ECDSA_SIG_set0(ret, r, s)) {
540 error("%s: ECDSA_SIG_set0 failed", __func__);
541 ECDSA_SIG_free(ret);
542 ret = NULL;
543 goto done;
544 }
545 r = s = NULL; /* now owned by ret */
546 /* success */
djm@openbsd.org93f02102019-01-20 22:51:37 +0000547 done:
djm@openbsd.org63297642019-01-21 00:47:34 +0000548 BN_free(r);
549 BN_free(s);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000550 free(sig);
551
552 return (ret);
553}
554
djm@openbsd.org93f02102019-01-20 22:51:37 +0000555static int
556pkcs11_ecdsa_start_wrapper(void)
557{
558 int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
559 unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
560
561 if (ec_key_method != NULL)
562 return (0);
djm@openbsd.org445cfce2019-01-20 23:05:52 +0000563 ec_key_idx = EC_KEY_get_ex_new_index(0, "ssh-pkcs11-ecdsa",
564 NULL, NULL, pkcs11_k11_free);
565 if (ec_key_idx == -1)
566 return (-1);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000567 ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
568 if (ec_key_method == NULL)
569 return (-1);
570 EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL);
571 EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign);
572 return (0);
573}
574
575static int
576pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
577 CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
578{
579 struct pkcs11_key *k11;
580
581 if (pkcs11_ecdsa_start_wrapper() == -1)
582 return (-1);
583
584 k11 = xcalloc(1, sizeof(*k11));
585 k11->provider = provider;
586 provider->refcount++; /* provider referenced by ECDSA key */
587 k11->slotidx = slotidx;
588 /* identify key object on smartcard */
589 k11->keyid_len = keyid_attrib->ulValueLen;
590 k11->keyid = xmalloc(k11->keyid_len);
591 memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000592
djm@openbsd.orgfcb1b092019-01-20 23:12:35 +0000593 EC_KEY_set_method(ec, ec_key_method);
djm@openbsd.org445cfce2019-01-20 23:05:52 +0000594 EC_KEY_set_ex_data(ec, ec_key_idx, k11);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000595
596 return (0);
597}
Damien Millere2cb4452019-01-21 11:32:28 +1100598#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +0000599
Damien Miller7ea845e2010-02-12 09:21:02 +1100600/* remove trailing spaces */
601static void
Damien Miller746d1a62013-07-18 16:13:02 +1000602rmspace(u_char *buf, size_t len)
Damien Miller7ea845e2010-02-12 09:21:02 +1100603{
604 size_t i;
605
606 if (!len)
607 return;
608 for (i = len - 1; i > 0; i--)
609 if (i == len - 1 || buf[i] == ' ')
610 buf[i] = '\0';
611 else
612 break;
613}
614
615/*
616 * open a pkcs11 session and login if required.
617 * if pin == NULL we delay login until key use
618 */
619static int
djm@openbsd.org93f02102019-01-20 22:51:37 +0000620pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
621 CK_ULONG user)
Damien Miller7ea845e2010-02-12 09:21:02 +1100622{
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000623 struct pkcs11_slotinfo *si;
Damien Miller7ea845e2010-02-12 09:21:02 +1100624 CK_FUNCTION_LIST *f;
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000625 CK_RV rv;
Damien Miller7ea845e2010-02-12 09:21:02 +1100626 CK_SESSION_HANDLE session;
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000627 int login_required, have_pinpad, ret;
djm@openbsd.org7a7fdca2019-02-04 23:37:54 +0000628 char prompt[1024], *xpin = NULL;
Damien Miller7ea845e2010-02-12 09:21:02 +1100629
630 f = p->function_list;
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000631 si = &p->slotinfo[slotidx];
632
633 have_pinpad = si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH;
634 login_required = si->token.flags & CKF_LOGIN_REQUIRED;
635
636 /* fail early before opening session */
djm@openbsd.org7a7fdca2019-02-04 23:37:54 +0000637 if (login_required && !have_pinpad && !pkcs11_interactive &&
638 (pin == NULL || strlen(pin) == 0)) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100639 error("pin required");
djm@openbsd.org93f02102019-01-20 22:51:37 +0000640 return (-SSH_PKCS11_ERR_PIN_REQUIRED);
Damien Miller7ea845e2010-02-12 09:21:02 +1100641 }
642 if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000643 CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100644 error("C_OpenSession failed: %lu", rv);
645 return (-1);
646 }
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000647 if (login_required) {
648 if (have_pinpad && (pin == NULL || strlen(pin) == 0)) {
649 /* defer PIN entry to the reader keypad */
650 rv = f->C_Login(session, CKU_USER, NULL_PTR, 0);
651 } else {
djm@openbsd.org7a7fdca2019-02-04 23:37:54 +0000652 if (pkcs11_interactive) {
653 snprintf(prompt, sizeof(prompt),
654 "Enter PIN for '%s': ", si->token.label);
655 if ((xpin = read_passphrase(prompt,
656 RP_ALLOW_EOF)) == NULL) {
657 debug("%s: no pin specified",
658 __func__);
659 return (-SSH_PKCS11_ERR_PIN_REQUIRED);
660 }
661 pin = xpin;
662 }
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000663 rv = f->C_Login(session, CKU_USER,
664 (u_char *)pin, strlen(pin));
djm@openbsd.org7a7fdca2019-02-04 23:37:54 +0000665 if (xpin != NULL)
666 freezero(xpin, strlen(xpin));
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000667 }
djm@openbsd.orgcb3bde32015-02-02 22:48:53 +0000668 if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
Damien Miller7ea845e2010-02-12 09:21:02 +1100669 error("C_Login failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000670 ret = (rv == CKR_PIN_LOCKED) ?
671 -SSH_PKCS11_ERR_PIN_LOCKED :
672 -SSH_PKCS11_ERR_LOGIN_FAIL;
Damien Miller7ea845e2010-02-12 09:21:02 +1100673 if ((rv = f->C_CloseSession(session)) != CKR_OK)
674 error("C_CloseSession failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000675 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +1100676 }
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000677 si->logged_in = 1;
Damien Miller7ea845e2010-02-12 09:21:02 +1100678 }
djm@openbsd.org41923ce2019-01-22 12:03:58 +0000679 si->session = session;
Damien Miller7ea845e2010-02-12 09:21:02 +1100680 return (0);
681}
682
Damien Millerd2252c72013-11-04 07:41:48 +1100683static int
djm@openbsd.org1129dcf2015-01-15 09:40:00 +0000684pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)
Damien Millerd2252c72013-11-04 07:41:48 +1100685{
686 int i;
687
688 for (i = 0; i < *nkeys; i++)
djm@openbsd.org1129dcf2015-01-15 09:40:00 +0000689 if (sshkey_equal(key, (*keysp)[i]))
Damien Millerd2252c72013-11-04 07:41:48 +1100690 return (1);
691 return (0);
692}
693
Damien Millere2cb4452019-01-21 11:32:28 +1100694#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +0000695static struct sshkey *
696pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
697 CK_OBJECT_HANDLE *obj)
698{
699 CK_ATTRIBUTE key_attr[3];
700 CK_SESSION_HANDLE session;
701 CK_FUNCTION_LIST *f = NULL;
702 CK_RV rv;
djm@openbsd.org24757c12019-01-20 23:01:59 +0000703 ASN1_OCTET_STRING *octet = NULL;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000704 EC_KEY *ec = NULL;
705 EC_GROUP *group = NULL;
706 struct sshkey *key = NULL;
707 const unsigned char *attrp = NULL;
708 int i;
709 int nid;
710
711 memset(&key_attr, 0, sizeof(key_attr));
712 key_attr[0].type = CKA_ID;
713 key_attr[1].type = CKA_EC_POINT;
714 key_attr[2].type = CKA_EC_PARAMS;
715
716 session = p->slotinfo[slotidx].session;
717 f = p->function_list;
718
719 /* figure out size of the attributes */
720 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
721 if (rv != CKR_OK) {
722 error("C_GetAttributeValue failed: %lu", rv);
723 return (NULL);
724 }
725
726 /*
727 * Allow CKA_ID (always first attribute) to be empty, but
728 * ensure that none of the others are zero length.
729 * XXX assumes CKA_ID is always first.
730 */
731 if (key_attr[1].ulValueLen == 0 ||
732 key_attr[2].ulValueLen == 0) {
733 error("invalid attribute length");
734 return (NULL);
735 }
736
737 /* allocate buffers for attributes */
738 for (i = 0; i < 3; i++)
739 if (key_attr[i].ulValueLen > 0)
740 key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
741
742 /* retrieve ID, public point and curve parameters of EC key */
743 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
744 if (rv != CKR_OK) {
745 error("C_GetAttributeValue failed: %lu", rv);
746 goto fail;
747 }
748
749 ec = EC_KEY_new();
750 if (ec == NULL) {
751 error("EC_KEY_new failed");
752 goto fail;
753 }
754
755 attrp = key_attr[2].pValue;
756 group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
757 if (group == NULL) {
758 ossl_error("d2i_ECPKParameters failed");
759 goto fail;
760 }
761
762 if (EC_KEY_set_group(ec, group) == 0) {
763 ossl_error("EC_KEY_set_group failed");
764 goto fail;
765 }
766
767 if (key_attr[1].ulValueLen <= 2) {
768 error("CKA_EC_POINT too small");
769 goto fail;
770 }
771
djm@openbsd.org24757c12019-01-20 23:01:59 +0000772 attrp = key_attr[1].pValue;
773 octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
774 if (octet == NULL) {
775 ossl_error("d2i_ASN1_OCTET_STRING failed");
776 goto fail;
777 }
778 attrp = octet->data;
779 if (o2i_ECPublicKey(&ec, &attrp, octet->length) == NULL) {
780 ossl_error("o2i_ECPublicKey failed");
781 goto fail;
djm@openbsd.org93f02102019-01-20 22:51:37 +0000782 }
783
784 nid = sshkey_ecdsa_key_to_nid(ec);
785 if (nid < 0) {
786 error("couldn't get curve nid");
787 goto fail;
788 }
789
790 if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
791 goto fail;
792
793 key = sshkey_new(KEY_UNSPEC);
794 if (key == NULL) {
795 error("sshkey_new failed");
796 goto fail;
797 }
798
799 key->ecdsa = ec;
800 key->ecdsa_nid = nid;
801 key->type = KEY_ECDSA;
802 key->flags |= SSHKEY_FLAG_EXT;
803 ec = NULL; /* now owned by key */
804
805fail:
806 for (i = 0; i < 3; i++)
807 free(key_attr[i].pValue);
808 if (ec)
809 EC_KEY_free(ec);
810 if (group)
811 EC_GROUP_free(group);
djm@openbsd.org24757c12019-01-20 23:01:59 +0000812 if (octet)
813 ASN1_OCTET_STRING_free(octet);
djm@openbsd.org93f02102019-01-20 22:51:37 +0000814
815 return (key);
816}
Damien Millere2cb4452019-01-21 11:32:28 +1100817#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +0000818
819static struct sshkey *
820pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
821 CK_OBJECT_HANDLE *obj)
822{
823 CK_ATTRIBUTE key_attr[3];
824 CK_SESSION_HANDLE session;
825 CK_FUNCTION_LIST *f = NULL;
826 CK_RV rv;
827 RSA *rsa = NULL;
828 BIGNUM *rsa_n, *rsa_e;
829 struct sshkey *key = NULL;
830 int i;
831
832 memset(&key_attr, 0, sizeof(key_attr));
833 key_attr[0].type = CKA_ID;
834 key_attr[1].type = CKA_MODULUS;
835 key_attr[2].type = CKA_PUBLIC_EXPONENT;
836
837 session = p->slotinfo[slotidx].session;
838 f = p->function_list;
839
840 /* figure out size of the attributes */
841 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
842 if (rv != CKR_OK) {
843 error("C_GetAttributeValue failed: %lu", rv);
844 return (NULL);
845 }
846
847 /*
848 * Allow CKA_ID (always first attribute) to be empty, but
849 * ensure that none of the others are zero length.
850 * XXX assumes CKA_ID is always first.
851 */
852 if (key_attr[1].ulValueLen == 0 ||
853 key_attr[2].ulValueLen == 0) {
854 error("invalid attribute length");
855 return (NULL);
856 }
857
858 /* allocate buffers for attributes */
859 for (i = 0; i < 3; i++)
860 if (key_attr[i].ulValueLen > 0)
861 key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
862
863 /* retrieve ID, modulus and public exponent of RSA key */
864 rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
865 if (rv != CKR_OK) {
866 error("C_GetAttributeValue failed: %lu", rv);
867 goto fail;
868 }
869
870 rsa = RSA_new();
871 if (rsa == NULL) {
872 error("RSA_new failed");
873 goto fail;
874 }
875
876 rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
877 rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
878 if (rsa_n == NULL || rsa_e == NULL) {
879 error("BN_bin2bn failed");
880 goto fail;
881 }
882 if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
883 fatal("%s: set key", __func__);
884 rsa_n = rsa_e = NULL; /* transferred */
885
886 if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
887 goto fail;
888
889 key = sshkey_new(KEY_UNSPEC);
890 if (key == NULL) {
891 error("sshkey_new failed");
892 goto fail;
893 }
894
895 key->rsa = rsa;
896 key->type = KEY_RSA;
897 key->flags |= SSHKEY_FLAG_EXT;
898 rsa = NULL; /* now owned by key */
899
900fail:
901 for (i = 0; i < 3; i++)
902 free(key_attr[i].pValue);
903 RSA_free(rsa);
904
905 return (key);
906}
907
908static struct sshkey *
909pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
910 CK_OBJECT_HANDLE *obj)
911{
912 CK_ATTRIBUTE cert_attr[3];
913 CK_SESSION_HANDLE session;
914 CK_FUNCTION_LIST *f = NULL;
915 CK_RV rv;
916 X509 *x509 = NULL;
917 EVP_PKEY *evp;
918 RSA *rsa = NULL;
Darren Tucker97370f62019-05-17 10:54:51 +1000919#ifdef OPENSSL_HAS_ECC
djm@openbsd.org93f02102019-01-20 22:51:37 +0000920 EC_KEY *ec = NULL;
Darren Tucker97370f62019-05-17 10:54:51 +1000921#endif
djm@openbsd.org93f02102019-01-20 22:51:37 +0000922 struct sshkey *key = NULL;
923 int i;
Damien Millere2cb4452019-01-21 11:32:28 +1100924#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +0000925 int nid;
Damien Millere2cb4452019-01-21 11:32:28 +1100926#endif
djm@openbsd.org93f02102019-01-20 22:51:37 +0000927 const u_char *cp;
928
929 memset(&cert_attr, 0, sizeof(cert_attr));
930 cert_attr[0].type = CKA_ID;
931 cert_attr[1].type = CKA_SUBJECT;
932 cert_attr[2].type = CKA_VALUE;
933
934 session = p->slotinfo[slotidx].session;
935 f = p->function_list;
936
937 /* figure out size of the attributes */
938 rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
939 if (rv != CKR_OK) {
940 error("C_GetAttributeValue failed: %lu", rv);
941 return (NULL);
942 }
943
944 /*
945 * Allow CKA_ID (always first attribute) to be empty, but
946 * ensure that none of the others are zero length.
947 * XXX assumes CKA_ID is always first.
948 */
949 if (cert_attr[1].ulValueLen == 0 ||
950 cert_attr[2].ulValueLen == 0) {
951 error("invalid attribute length");
952 return (NULL);
953 }
954
955 /* allocate buffers for attributes */
956 for (i = 0; i < 3; i++)
957 if (cert_attr[i].ulValueLen > 0)
958 cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
959
960 /* retrieve ID, subject and value of certificate */
961 rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
962 if (rv != CKR_OK) {
963 error("C_GetAttributeValue failed: %lu", rv);
964 goto fail;
965 }
966
967 x509 = X509_new();
968 if (x509 == NULL) {
969 error("x509_new failed");
970 goto fail;
971 }
972
973 cp = cert_attr[2].pValue;
974 if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) {
975 error("d2i_x509 failed");
976 goto fail;
977 }
978
979 evp = X509_get_pubkey(x509);
980 if (evp == NULL) {
981 error("X509_get_pubkey failed");
982 goto fail;
983 }
984
985 if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) {
986 if (EVP_PKEY_get0_RSA(evp) == NULL) {
987 error("invalid x509; no rsa key");
988 goto fail;
989 }
990 if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) {
991 error("RSAPublicKey_dup failed");
992 goto fail;
993 }
994
995 if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
996 goto fail;
997
998 key = sshkey_new(KEY_UNSPEC);
999 if (key == NULL) {
1000 error("sshkey_new failed");
1001 goto fail;
1002 }
1003
1004 key->rsa = rsa;
1005 key->type = KEY_RSA;
1006 key->flags |= SSHKEY_FLAG_EXT;
1007 rsa = NULL; /* now owned by key */
Damien Millere2cb4452019-01-21 11:32:28 +11001008#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +00001009 } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) {
djm@openbsd.org8a246752019-01-20 23:03:26 +00001010 if (EVP_PKEY_get0_EC_KEY(evp) == NULL) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001011 error("invalid x509; no ec key");
1012 goto fail;
1013 }
djm@openbsd.org8a246752019-01-20 23:03:26 +00001014 if ((ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(evp))) == NULL) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001015 error("EC_KEY_dup failed");
1016 goto fail;
1017 }
1018
1019 nid = sshkey_ecdsa_key_to_nid(ec);
1020 if (nid < 0) {
1021 error("couldn't get curve nid");
1022 goto fail;
1023 }
1024
1025 if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
1026 goto fail;
1027
1028 key = sshkey_new(KEY_UNSPEC);
1029 if (key == NULL) {
1030 error("sshkey_new failed");
1031 goto fail;
1032 }
1033
1034 key->ecdsa = ec;
1035 key->ecdsa_nid = nid;
1036 key->type = KEY_ECDSA;
1037 key->flags |= SSHKEY_FLAG_EXT;
1038 ec = NULL; /* now owned by key */
Damien Millere2cb4452019-01-21 11:32:28 +11001039#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +00001040 } else
1041 error("unknown certificate key type");
1042
1043fail:
1044 for (i = 0; i < 3; i++)
1045 free(cert_attr[i].pValue);
1046 X509_free(x509);
1047 RSA_free(rsa);
Darren Tucker97370f62019-05-17 10:54:51 +10001048#ifdef OPENSSL_HAS_ECC
djm@openbsd.org93f02102019-01-20 22:51:37 +00001049 EC_KEY_free(ec);
Darren Tucker97370f62019-05-17 10:54:51 +10001050#endif
djm@openbsd.org93f02102019-01-20 22:51:37 +00001051
1052 return (key);
1053}
1054
1055#if 0
Damien Millerd2252c72013-11-04 07:41:48 +11001056static int
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001057have_rsa_key(const RSA *rsa)
1058{
1059 const BIGNUM *rsa_n, *rsa_e;
1060
1061 RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
1062 return rsa_n != NULL && rsa_e != NULL;
1063}
djm@openbsd.org93f02102019-01-20 22:51:37 +00001064#endif
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001065
djm@openbsd.org93f02102019-01-20 22:51:37 +00001066/*
1067 * lookup certificates for token in slot identified by slotidx,
1068 * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
1069 * keysp points to an (possibly empty) array with *nkeys keys.
1070 */
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001071static int
djm@openbsd.org93f02102019-01-20 22:51:37 +00001072pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
djm@openbsd.org1129dcf2015-01-15 09:40:00 +00001073 struct sshkey ***keysp, int *nkeys)
Damien Miller7ea845e2010-02-12 09:21:02 +11001074{
djm@openbsd.org93f02102019-01-20 22:51:37 +00001075 struct sshkey *key = NULL;
1076 CK_OBJECT_CLASS key_class;
1077 CK_ATTRIBUTE key_attr[1];
1078 CK_SESSION_HANDLE session;
1079 CK_FUNCTION_LIST *f = NULL;
1080 CK_RV rv;
1081 CK_OBJECT_HANDLE obj;
1082 CK_ULONG n = 0;
1083 int ret = -1;
Tim Rice179eee02010-03-04 12:48:05 -08001084
djm@openbsd.org93f02102019-01-20 22:51:37 +00001085 memset(&key_attr, 0, sizeof(key_attr));
1086 memset(&obj, 0, sizeof(obj));
1087
1088 key_class = CKO_CERTIFICATE;
1089 key_attr[0].type = CKA_CLASS;
1090 key_attr[0].pValue = &key_class;
1091 key_attr[0].ulValueLen = sizeof(key_class);
1092
Damien Miller7ea845e2010-02-12 09:21:02 +11001093 session = p->slotinfo[slotidx].session;
djm@openbsd.org93f02102019-01-20 22:51:37 +00001094 f = p->function_list;
1095
1096 rv = f->C_FindObjectsInit(session, key_attr, 1);
1097 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +11001098 error("C_FindObjectsInit failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001099 goto fail;
Damien Miller7ea845e2010-02-12 09:21:02 +11001100 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001101
Damien Miller7ea845e2010-02-12 09:21:02 +11001102 while (1) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001103 CK_CERTIFICATE_TYPE ck_cert_type;
1104
1105 rv = f->C_FindObjects(session, &obj, 1, &n);
1106 if (rv != CKR_OK) {
1107 error("C_FindObjects failed: %lu", rv);
1108 goto fail;
Damien Miller7ea845e2010-02-12 09:21:02 +11001109 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001110 if (n == 0)
Damien Miller7ea845e2010-02-12 09:21:02 +11001111 break;
djm@openbsd.org93f02102019-01-20 22:51:37 +00001112
1113 memset(&ck_cert_type, 0, sizeof(ck_cert_type));
1114 memset(&key_attr, 0, sizeof(key_attr));
1115 key_attr[0].type = CKA_CERTIFICATE_TYPE;
1116 key_attr[0].pValue = &ck_cert_type;
1117 key_attr[0].ulValueLen = sizeof(ck_cert_type);
1118
1119 rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
1120 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +11001121 error("C_GetAttributeValue failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001122 goto fail;
djm@openbsd.org63ebcd02015-07-18 08:02:17 +00001123 }
1124
djm@openbsd.org93f02102019-01-20 22:51:37 +00001125 switch (ck_cert_type) {
1126 case CKC_X_509:
1127 key = pkcs11_fetch_x509_pubkey(p, slotidx, &obj);
1128 break;
1129 default:
1130 /* XXX print key type? */
markus@openbsd.org2aee9a42019-03-08 17:24:43 +00001131 key = NULL;
djm@openbsd.org93f02102019-01-20 22:51:37 +00001132 error("skipping unsupported certificate type");
1133 }
djm@openbsd.org482d23b2018-09-13 02:08:33 +00001134
djm@openbsd.org93f02102019-01-20 22:51:37 +00001135 if (key == NULL) {
1136 error("failed to fetch key");
1137 continue;
1138 }
1139
1140 if (pkcs11_key_included(keysp, nkeys, key)) {
1141 sshkey_free(key);
Damien Miller7ea845e2010-02-12 09:21:02 +11001142 } else {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001143 /* expand key array and add key */
1144 *keysp = xrecallocarray(*keysp, *nkeys,
1145 *nkeys + 1, sizeof(struct sshkey *));
1146 (*keysp)[*nkeys] = key;
1147 *nkeys = *nkeys + 1;
1148 debug("have %d keys", *nkeys);
Damien Millerd2252c72013-11-04 07:41:48 +11001149 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001150 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001151
1152 ret = 0;
1153fail:
1154 rv = f->C_FindObjectsFinal(session);
1155 if (rv != CKR_OK) {
Damien Miller7ea845e2010-02-12 09:21:02 +11001156 error("C_FindObjectsFinal failed: %lu", rv);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001157 ret = -1;
1158 }
1159
1160 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +11001161}
1162
djm@openbsd.org93f02102019-01-20 22:51:37 +00001163/*
1164 * lookup public keys for token in slot identified by slotidx,
1165 * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
1166 * keysp points to an (possibly empty) array with *nkeys keys.
1167 */
1168static int
1169pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
1170 struct sshkey ***keysp, int *nkeys)
1171{
1172 struct sshkey *key = NULL;
1173 CK_OBJECT_CLASS key_class;
1174 CK_ATTRIBUTE key_attr[1];
1175 CK_SESSION_HANDLE session;
1176 CK_FUNCTION_LIST *f = NULL;
1177 CK_RV rv;
1178 CK_OBJECT_HANDLE obj;
1179 CK_ULONG n = 0;
1180 int ret = -1;
1181
1182 memset(&key_attr, 0, sizeof(key_attr));
1183 memset(&obj, 0, sizeof(obj));
1184
1185 key_class = CKO_PUBLIC_KEY;
1186 key_attr[0].type = CKA_CLASS;
1187 key_attr[0].pValue = &key_class;
1188 key_attr[0].ulValueLen = sizeof(key_class);
1189
1190 session = p->slotinfo[slotidx].session;
1191 f = p->function_list;
1192
1193 rv = f->C_FindObjectsInit(session, key_attr, 1);
1194 if (rv != CKR_OK) {
1195 error("C_FindObjectsInit failed: %lu", rv);
1196 goto fail;
1197 }
1198
1199 while (1) {
1200 CK_KEY_TYPE ck_key_type;
1201
1202 rv = f->C_FindObjects(session, &obj, 1, &n);
1203 if (rv != CKR_OK) {
1204 error("C_FindObjects failed: %lu", rv);
1205 goto fail;
1206 }
1207 if (n == 0)
1208 break;
1209
1210 memset(&ck_key_type, 0, sizeof(ck_key_type));
1211 memset(&key_attr, 0, sizeof(key_attr));
1212 key_attr[0].type = CKA_KEY_TYPE;
1213 key_attr[0].pValue = &ck_key_type;
1214 key_attr[0].ulValueLen = sizeof(ck_key_type);
1215
1216 rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
1217 if (rv != CKR_OK) {
1218 error("C_GetAttributeValue failed: %lu", rv);
1219 goto fail;
1220 }
1221
1222 switch (ck_key_type) {
1223 case CKK_RSA:
1224 key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
1225 break;
Damien Millere2cb4452019-01-21 11:32:28 +11001226#ifdef HAVE_EC_KEY_METHOD_NEW
djm@openbsd.org93f02102019-01-20 22:51:37 +00001227 case CKK_ECDSA:
1228 key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
1229 break;
Damien Millere2cb4452019-01-21 11:32:28 +11001230#endif /* HAVE_EC_KEY_METHOD_NEW */
djm@openbsd.org93f02102019-01-20 22:51:37 +00001231 default:
1232 /* XXX print key type? */
markus@openbsd.org2aee9a42019-03-08 17:24:43 +00001233 key = NULL;
djm@openbsd.org93f02102019-01-20 22:51:37 +00001234 error("skipping unsupported key type");
1235 }
1236
1237 if (key == NULL) {
1238 error("failed to fetch key");
1239 continue;
1240 }
1241
1242 if (pkcs11_key_included(keysp, nkeys, key)) {
1243 sshkey_free(key);
1244 } else {
1245 /* expand key array and add key */
1246 *keysp = xrecallocarray(*keysp, *nkeys,
1247 *nkeys + 1, sizeof(struct sshkey *));
1248 (*keysp)[*nkeys] = key;
1249 *nkeys = *nkeys + 1;
1250 debug("have %d keys", *nkeys);
1251 }
1252 }
1253
1254 ret = 0;
1255fail:
1256 rv = f->C_FindObjectsFinal(session);
1257 if (rv != CKR_OK) {
1258 error("C_FindObjectsFinal failed: %lu", rv);
1259 ret = -1;
1260 }
1261
1262 return (ret);
1263}
1264
1265#ifdef WITH_PKCS11_KEYGEN
1266#define FILL_ATTR(attr, idx, typ, val, len) \
1267 { (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; }
1268
1269static struct sshkey *
1270pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
1271 char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
1272{
1273 struct pkcs11_slotinfo *si;
1274 char *plabel = label ? label : "";
1275 int npub = 0, npriv = 0;
1276 CK_RV rv;
1277 CK_FUNCTION_LIST *f;
1278 CK_SESSION_HANDLE session;
1279 CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE;
1280 CK_OBJECT_HANDLE pubKey, privKey;
1281 CK_ATTRIBUTE tpub[16], tpriv[16];
1282 CK_MECHANISM mech = {
1283 CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0
1284 };
1285 CK_BYTE pubExponent[] = {
1286 0x01, 0x00, 0x01 /* RSA_F4 in bytes */
1287 };
1288 pubkey_filter[0].pValue = &pubkey_class;
1289 cert_filter[0].pValue = &cert_class;
1290
1291 *err = 0;
1292
1293 FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
1294 FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
1295 FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
1296 FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
1297 FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
1298 sizeof(false_val));
1299 FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
1300 FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
1301 FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits));
1302 FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent,
1303 sizeof(pubExponent));
1304 FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
1305
1306 FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
1307 FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
1308 FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
1309 FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
1310 FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
1311 FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
1312 FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
1313 sizeof(false_val));
1314 FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
1315 FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
1316 FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
1317
1318 f = p->function_list;
1319 si = &p->slotinfo[slotidx];
1320 session = si->session;
1321
1322 if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
1323 &pubKey, &privKey)) != CKR_OK) {
1324 error("%s: key generation failed: error 0x%lx", __func__, rv);
1325 *err = rv;
1326 return NULL;
1327 }
1328
1329 return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey);
1330}
1331
1332static int
1333pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen)
1334{
1335 size_t i, len;
1336 char ptr[3];
1337
1338 if (dest)
1339 *dest = NULL;
1340 if (rlen)
1341 *rlen = 0;
1342
1343 if ((len = strlen(hex)) % 2)
1344 return -1;
1345 len /= 2;
1346
1347 *dest = xmalloc(len);
1348
1349 ptr[2] = '\0';
1350 for (i = 0; i < len; i++) {
1351 ptr[0] = hex[2 * i];
1352 ptr[1] = hex[(2 * i) + 1];
1353 if (!isxdigit(ptr[0]) || !isxdigit(ptr[1]))
1354 return -1;
1355 (*dest)[i] = (unsigned char)strtoul(ptr, NULL, 16);
1356 }
1357
1358 if (rlen)
1359 *rlen = len;
1360
1361 return 0;
1362}
1363
1364static struct ec_curve_info {
1365 const char *name;
1366 const char *oid;
1367 const char *oid_encoded;
1368 size_t size;
1369} ec_curve_infos[] = {
1370 {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256},
1371 {"secp384r1", "1.3.132.0.34", "06052B81040022", 384},
1372 {"secp521r1", "1.3.132.0.35", "06052B81040023", 521},
djm@openbsd.org65294092019-01-20 23:11:11 +00001373 {NULL, NULL, NULL, 0},
djm@openbsd.org93f02102019-01-20 22:51:37 +00001374};
1375
1376static struct sshkey *
1377pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
1378 char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
1379{
1380 struct pkcs11_slotinfo *si;
1381 char *plabel = label ? label : "";
1382 int i;
1383 size_t ecparams_size;
1384 unsigned char *ecparams = NULL;
1385 int npub = 0, npriv = 0;
1386 CK_RV rv;
1387 CK_FUNCTION_LIST *f;
1388 CK_SESSION_HANDLE session;
1389 CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE;
1390 CK_OBJECT_HANDLE pubKey, privKey;
1391 CK_MECHANISM mech = {
1392 CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
1393 };
1394 CK_ATTRIBUTE tpub[16], tpriv[16];
1395
1396 *err = 0;
1397
1398 for (i = 0; ec_curve_infos[i].name; i++) {
1399 if (ec_curve_infos[i].size == bits)
1400 break;
1401 }
1402 if (!ec_curve_infos[i].name) {
1403 error("%s: invalid key size %lu", __func__, bits);
1404 return NULL;
1405 }
1406 if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams,
1407 &ecparams_size) == -1) {
1408 error("%s: invalid oid", __func__);
1409 return NULL;
1410 }
1411
1412 FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
1413 FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
1414 FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
1415 FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
1416 FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
1417 sizeof(false_val));
1418 FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
1419 FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
1420 FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size);
1421 FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
1422
1423 FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
1424 FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
1425 FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
1426 FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
1427 FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
1428 FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
1429 FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
1430 sizeof(false_val));
1431 FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
1432 FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
1433 FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
1434
1435 f = p->function_list;
1436 si = &p->slotinfo[slotidx];
1437 session = si->session;
1438
1439 if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
1440 &pubKey, &privKey)) != CKR_OK) {
1441 error("%s: key generation failed: error 0x%lx", __func__, rv);
1442 *err = rv;
1443 return NULL;
1444 }
1445
1446 return pkcs11_fetch_ecdsa_pubkey(p, slotidx, &pubKey);
1447}
1448#endif /* WITH_PKCS11_KEYGEN */
1449
1450/*
1451 * register a new provider, fails if provider already exists. if
1452 * keyp is provided, fetch keys.
1453 */
1454static int
1455pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
1456 struct pkcs11_provider **providerp, CK_ULONG user)
Damien Miller7ea845e2010-02-12 09:21:02 +11001457{
1458 int nkeys, need_finalize = 0;
djm@openbsd.org93f02102019-01-20 22:51:37 +00001459 int ret = -1;
Damien Miller7ea845e2010-02-12 09:21:02 +11001460 struct pkcs11_provider *p = NULL;
1461 void *handle = NULL;
1462 CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
1463 CK_RV rv;
1464 CK_FUNCTION_LIST *f = NULL;
1465 CK_TOKEN_INFO *token;
1466 CK_ULONG i;
1467
djm@openbsd.org93f02102019-01-20 22:51:37 +00001468 if (providerp == NULL)
1469 goto fail;
1470 *providerp = NULL;
1471
1472 if (keyp != NULL)
1473 *keyp = NULL;
1474
Damien Miller7ea845e2010-02-12 09:21:02 +11001475 if (pkcs11_provider_lookup(provider_id) != NULL) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001476 debug("%s: provider already registered: %s",
1477 __func__, provider_id);
Damien Miller7ea845e2010-02-12 09:21:02 +11001478 goto fail;
1479 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001480 /* open shared pkcs11-library */
Damien Miller7ea845e2010-02-12 09:21:02 +11001481 if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
1482 error("dlopen %s failed: %s", provider_id, dlerror());
1483 goto fail;
1484 }
1485 if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) {
1486 error("dlsym(C_GetFunctionList) failed: %s", dlerror());
1487 goto fail;
1488 }
1489 p = xcalloc(1, sizeof(*p));
1490 p->name = xstrdup(provider_id);
1491 p->handle = handle;
1492 /* setup the pkcs11 callbacks */
1493 if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001494 error("C_GetFunctionList for provider %s failed: %lu",
1495 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001496 goto fail;
1497 }
1498 p->function_list = f;
1499 if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001500 error("C_Initialize for provider %s failed: %lu",
1501 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001502 goto fail;
1503 }
1504 need_finalize = 1;
1505 if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001506 error("C_GetInfo for provider %s failed: %lu",
1507 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001508 goto fail;
1509 }
1510 rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
1511 rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001512 debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d"
Damien Miller7ea845e2010-02-12 09:21:02 +11001513 " libraryDescription <%s> libraryVersion %d.%d",
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001514 provider_id,
Damien Miller7ea845e2010-02-12 09:21:02 +11001515 p->info.manufacturerID,
1516 p->info.cryptokiVersion.major,
1517 p->info.cryptokiVersion.minor,
1518 p->info.libraryDescription,
1519 p->info.libraryVersion.major,
1520 p->info.libraryVersion.minor);
1521 if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
1522 error("C_GetSlotList failed: %lu", rv);
1523 goto fail;
1524 }
1525 if (p->nslots == 0) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001526 error("%s: provider %s returned no slots", __func__,
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001527 provider_id);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001528 ret = -SSH_PKCS11_ERR_NO_SLOTS;
Damien Miller7ea845e2010-02-12 09:21:02 +11001529 goto fail;
1530 }
1531 p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
1532 if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
1533 != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001534 error("C_GetSlotList for provider %s failed: %lu",
1535 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001536 goto fail;
1537 }
1538 p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
1539 p->valid = 1;
1540 nkeys = 0;
1541 for (i = 0; i < p->nslots; i++) {
1542 token = &p->slotinfo[i].token;
1543 if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
1544 != CKR_OK) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001545 error("C_GetTokenInfo for provider %s slot %lu "
1546 "failed: %lu", provider_id, (unsigned long)i, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001547 continue;
1548 }
djm@openbsd.orgb15fd982015-07-18 08:00:21 +00001549 if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001550 debug2("%s: ignoring uninitialised token in "
1551 "provider %s slot %lu", __func__,
1552 provider_id, (unsigned long)i);
djm@openbsd.orgb15fd982015-07-18 08:00:21 +00001553 continue;
1554 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001555 rmspace(token->label, sizeof(token->label));
1556 rmspace(token->manufacturerID, sizeof(token->manufacturerID));
1557 rmspace(token->model, sizeof(token->model));
1558 rmspace(token->serialNumber, sizeof(token->serialNumber));
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001559 debug("provider %s slot %lu: label <%s> manufacturerID <%s> "
1560 "model <%s> serial <%s> flags 0x%lx",
1561 provider_id, (unsigned long)i,
Damien Miller7ea845e2010-02-12 09:21:02 +11001562 token->label, token->manufacturerID, token->model,
1563 token->serialNumber, token->flags);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001564 /*
1565 * open session, login with pin and retrieve public
1566 * keys (if keyp is provided)
1567 */
1568 if ((ret = pkcs11_open_session(p, i, pin, user)) == 0) {
1569 if (keyp == NULL)
1570 continue;
Damien Miller7ea845e2010-02-12 09:21:02 +11001571 pkcs11_fetch_keys(p, i, keyp, &nkeys);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001572 pkcs11_fetch_certs(p, i, keyp, &nkeys);
1573 }
Damien Miller7ea845e2010-02-12 09:21:02 +11001574 }
djm@openbsd.org93f02102019-01-20 22:51:37 +00001575
1576 /* now owned by caller */
1577 *providerp = p;
1578
1579 TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
1580 p->refcount++; /* add to provider list */
1581
1582 return (nkeys);
Damien Miller7ea845e2010-02-12 09:21:02 +11001583fail:
1584 if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
djm@openbsd.orgefb494e2016-10-28 03:33:52 +00001585 error("C_Finalize for provider %s failed: %lu",
1586 provider_id, rv);
Damien Miller7ea845e2010-02-12 09:21:02 +11001587 if (p) {
djm@openbsd.org93f02102019-01-20 22:51:37 +00001588 free(p->name);
Darren Tuckera627d422013-06-02 07:31:17 +10001589 free(p->slotlist);
1590 free(p->slotinfo);
1591 free(p);
Damien Miller7ea845e2010-02-12 09:21:02 +11001592 }
1593 if (handle)
1594 dlclose(handle);
djm@openbsd.org93f02102019-01-20 22:51:37 +00001595 return (ret);
Damien Miller7ea845e2010-02-12 09:21:02 +11001596}
Damien Millerdfa41562010-02-12 10:06:28 +11001597
djm@openbsd.org93f02102019-01-20 22:51:37 +00001598/*
1599 * register a new provider and get number of keys hold by the token,
1600 * fails if provider already exists
1601 */
1602int
1603pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
1604{
1605 struct pkcs11_provider *p = NULL;
1606 int nkeys;
Darren Tucker0dd24e02011-09-04 19:59:26 +10001607
djm@openbsd.org93f02102019-01-20 22:51:37 +00001608 nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER);
1609
1610 /* no keys found or some other error, de-register provider */
1611 if (nkeys <= 0 && p != NULL) {
1612 TAILQ_REMOVE(&pkcs11_providers, p, next);
1613 pkcs11_provider_finalize(p);
1614 pkcs11_provider_unref(p);
1615 }
1616 if (nkeys == 0)
1617 debug("%s: provider %s returned no keys", __func__,
1618 provider_id);
1619
1620 return (nkeys);
1621}
1622
1623#ifdef WITH_PKCS11_KEYGEN
1624struct sshkey *
1625pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
1626 unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err)
1627{
1628 struct pkcs11_provider *p = NULL;
1629 struct pkcs11_slotinfo *si;
1630 CK_FUNCTION_LIST *f;
1631 CK_SESSION_HANDLE session;
1632 struct sshkey *k = NULL;
1633 int ret = -1, reset_pin = 0, reset_provider = 0;
1634 CK_RV rv;
1635
1636 *err = 0;
1637
1638 if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
1639 debug("%s: provider \"%s\" available", __func__, provider_id);
1640 else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p,
1641 CKU_SO)) < 0) {
1642 debug("%s: could not register provider %s", __func__,
1643 provider_id);
1644 goto out;
1645 } else
1646 reset_provider = 1;
1647
1648 f = p->function_list;
1649 si = &p->slotinfo[slotidx];
1650 session = si->session;
1651
1652 if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
1653 CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
1654 debug("%s: could not supply SO pin: %lu", __func__, rv);
1655 reset_pin = 0;
1656 } else
1657 reset_pin = 1;
1658
1659 switch (type) {
1660 case KEY_RSA:
1661 if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label,
1662 bits, keyid, err)) == NULL) {
1663 debug("%s: failed to generate RSA key", __func__);
1664 goto out;
1665 }
1666 break;
1667 case KEY_ECDSA:
1668 if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label,
1669 bits, keyid, err)) == NULL) {
1670 debug("%s: failed to generate ECDSA key", __func__);
1671 goto out;
1672 }
1673 break;
1674 default:
1675 *err = SSH_PKCS11_ERR_GENERIC;
1676 debug("%s: unknown type %d", __func__, type);
1677 goto out;
1678 }
1679
1680out:
1681 if (reset_pin)
1682 f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
1683 CK_INVALID_HANDLE);
1684
1685 if (reset_provider)
1686 pkcs11_del_provider(provider_id);
1687
1688 return (k);
1689}
1690
1691struct sshkey *
1692pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
1693 unsigned char keyid, u_int32_t *err)
1694{
1695 struct pkcs11_provider *p = NULL;
1696 struct pkcs11_slotinfo *si;
1697 struct sshkey *k = NULL;
1698 int reset_pin = 0, reset_provider = 0;
1699 CK_ULONG nattrs;
1700 CK_FUNCTION_LIST *f;
1701 CK_SESSION_HANDLE session;
1702 CK_ATTRIBUTE attrs[16];
1703 CK_OBJECT_CLASS key_class;
1704 CK_KEY_TYPE key_type;
1705 CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE;
1706 CK_RV rv;
1707
1708 *err = 0;
1709
1710 if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
1711 debug("%s: using provider \"%s\"", __func__, provider_id);
1712 } else if (pkcs11_register_provider(provider_id, pin, NULL, &p,
1713 CKU_SO) < 0) {
1714 debug("%s: could not register provider %s", __func__,
1715 provider_id);
1716 goto out;
1717 } else
1718 reset_provider = 1;
1719
1720 f = p->function_list;
1721 si = &p->slotinfo[slotidx];
1722 session = si->session;
1723
1724 if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
1725 CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
1726 debug("%s: could not supply SO pin: %lu", __func__, rv);
1727 reset_pin = 0;
1728 } else
1729 reset_pin = 1;
1730
1731 /* private key */
1732 nattrs = 0;
1733 key_class = CKO_PRIVATE_KEY;
1734 FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
1735 FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
1736
1737 if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
1738 obj != CK_INVALID_HANDLE) {
1739 if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
1740 debug("%s: could not destroy private key 0x%hhx",
1741 __func__, keyid);
1742 *err = rv;
1743 goto out;
1744 }
1745 }
1746
1747 /* public key */
1748 nattrs = 0;
1749 key_class = CKO_PUBLIC_KEY;
1750 FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
1751 FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
1752
1753 if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
1754 obj != CK_INVALID_HANDLE) {
1755
1756 /* get key type */
1757 nattrs = 0;
1758 FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type,
1759 sizeof(key_type));
1760 rv = f->C_GetAttributeValue(session, obj, attrs, nattrs);
1761 if (rv != CKR_OK) {
1762 debug("%s: could not get key type of public key 0x%hhx",
1763 __func__, keyid);
1764 *err = rv;
1765 key_type = -1;
1766 }
1767 if (key_type == CKK_RSA)
1768 k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
1769 else if (key_type == CKK_ECDSA)
1770 k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
1771
1772 if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
1773 debug("%s: could not destroy public key 0x%hhx",
1774 __func__, keyid);
1775 *err = rv;
1776 goto out;
1777 }
1778 }
1779
1780out:
1781 if (reset_pin)
1782 f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
1783 CK_INVALID_HANDLE);
1784
1785 if (reset_provider)
1786 pkcs11_del_provider(provider_id);
1787
1788 return (k);
1789}
1790#endif /* WITH_PKCS11_KEYGEN */
Damien Miller5de6ac22019-01-21 11:44:19 +11001791#else /* ENABLE_PKCS11 */
Darren Tucker0dd24e02011-09-04 19:59:26 +10001792int
1793pkcs11_init(int interactive)
1794{
djm@openbsd.org93f02102019-01-20 22:51:37 +00001795 error("%s: dlopen() not supported", __func__);
1796 return (-1);
1797}
1798
1799int
1800pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
1801{
1802 error("%s: dlopen() not supported", __func__);
1803 return (-1);
Darren Tucker0dd24e02011-09-04 19:59:26 +10001804}
1805
1806void
1807pkcs11_terminate(void)
1808{
djm@openbsd.org93f02102019-01-20 22:51:37 +00001809 error("%s: dlopen() not supported", __func__);
Darren Tucker0dd24e02011-09-04 19:59:26 +10001810}
Damien Miller5de6ac22019-01-21 11:44:19 +11001811#endif /* ENABLE_PKCS11 */