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