/src/hpn-ssh/ssh-pkcs11.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* $OpenBSD: ssh-pkcs11.c,v 1.59 2023/07/27 22:26:49 djm Exp $ */ |
2 | | /* |
3 | | * Copyright (c) 2010 Markus Friedl. All rights reserved. |
4 | | * Copyright (c) 2014 Pedro Martelletto. All rights reserved. |
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 | | |
19 | | #include "includes.h" |
20 | | |
21 | | #ifdef ENABLE_PKCS11 |
22 | | |
23 | | #ifdef HAVE_SYS_TIME_H |
24 | | # include <sys/time.h> |
25 | | #endif |
26 | | |
27 | | #include <sys/types.h> |
28 | | #include <stdarg.h> |
29 | | #include <stdio.h> |
30 | | |
31 | | #include <ctype.h> |
32 | | #include <string.h> |
33 | | #include <dlfcn.h> |
34 | | |
35 | | #include "openbsd-compat/sys-queue.h" |
36 | | #include "openbsd-compat/openssl-compat.h" |
37 | | |
38 | | #include <openssl/ecdsa.h> |
39 | | #include <openssl/x509.h> |
40 | | #include <openssl/err.h> |
41 | | |
42 | | #define CRYPTOKI_COMPAT |
43 | | #include "pkcs11.h" |
44 | | |
45 | | #include "log.h" |
46 | | #include "misc.h" |
47 | | #include "sshkey.h" |
48 | | #include "ssh-pkcs11.h" |
49 | | #include "digest.h" |
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; |
76 | | char *keyid; |
77 | | int keyid_len; |
78 | | }; |
79 | | |
80 | | int pkcs11_interactive = 0; |
81 | | |
82 | | #if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) |
83 | | static void |
84 | | ossl_error(const char *msg) |
85 | 0 | { |
86 | 0 | unsigned long e; |
87 | |
|
88 | 0 | error_f("%s", msg); |
89 | 0 | while ((e = ERR_get_error()) != 0) |
90 | 0 | error_f("libcrypto error: %s", ERR_error_string(e, NULL)); |
91 | 0 | } |
92 | | #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ |
93 | | |
94 | | int |
95 | | pkcs11_init(int interactive) |
96 | 0 | { |
97 | 0 | pkcs11_interactive = interactive; |
98 | 0 | TAILQ_INIT(&pkcs11_providers); |
99 | 0 | return (0); |
100 | 0 | } |
101 | | |
102 | | /* |
103 | | * finalize a provider shared library, it's no longer usable. |
104 | | * however, there might still be keys referencing this provider, |
105 | | * so the actual freeing of memory is handled by pkcs11_provider_unref(). |
106 | | * this is called when a provider gets unregistered. |
107 | | */ |
108 | | static void |
109 | | pkcs11_provider_finalize(struct pkcs11_provider *p) |
110 | 0 | { |
111 | 0 | CK_RV rv; |
112 | 0 | CK_ULONG i; |
113 | |
|
114 | 0 | debug_f("provider \"%s\" refcount %d valid %d", |
115 | 0 | p->name, p->refcount, p->valid); |
116 | 0 | if (!p->valid) |
117 | 0 | return; |
118 | 0 | for (i = 0; i < p->nslots; i++) { |
119 | 0 | if (p->slotinfo[i].session && |
120 | 0 | (rv = p->function_list->C_CloseSession( |
121 | 0 | p->slotinfo[i].session)) != CKR_OK) |
122 | 0 | error("C_CloseSession failed: %lu", rv); |
123 | 0 | } |
124 | 0 | if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) |
125 | 0 | error("C_Finalize failed: %lu", rv); |
126 | 0 | p->valid = 0; |
127 | 0 | p->function_list = NULL; |
128 | 0 | dlclose(p->handle); |
129 | 0 | } |
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 | 0 | { |
138 | 0 | debug_f("provider \"%s\" refcount %d", p->name, p->refcount); |
139 | 0 | if (--p->refcount <= 0) { |
140 | 0 | if (p->valid) |
141 | 0 | error_f("provider \"%s\" still valid", p->name); |
142 | 0 | free(p->name); |
143 | 0 | free(p->slotlist); |
144 | 0 | free(p->slotinfo); |
145 | 0 | free(p); |
146 | 0 | } |
147 | 0 | } |
148 | | |
149 | | /* unregister all providers, keys might still point to the providers */ |
150 | | void |
151 | | pkcs11_terminate(void) |
152 | 0 | { |
153 | 0 | struct pkcs11_provider *p; |
154 | |
|
155 | 0 | while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) { |
156 | 0 | TAILQ_REMOVE(&pkcs11_providers, p, next); |
157 | 0 | pkcs11_provider_finalize(p); |
158 | 0 | pkcs11_provider_unref(p); |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | /* lookup provider by name */ |
163 | | static struct pkcs11_provider * |
164 | | pkcs11_provider_lookup(char *provider_id) |
165 | 0 | { |
166 | 0 | struct pkcs11_provider *p; |
167 | |
|
168 | 0 | TAILQ_FOREACH(p, &pkcs11_providers, next) { |
169 | 0 | debug("check provider \"%s\"", p->name); |
170 | 0 | if (!strcmp(provider_id, p->name)) |
171 | 0 | return (p); |
172 | 0 | } |
173 | 0 | return (NULL); |
174 | 0 | } |
175 | | |
176 | | /* unregister provider by name */ |
177 | | int |
178 | | pkcs11_del_provider(char *provider_id) |
179 | 0 | { |
180 | 0 | struct pkcs11_provider *p; |
181 | |
|
182 | 0 | if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { |
183 | 0 | TAILQ_REMOVE(&pkcs11_providers, p, next); |
184 | 0 | pkcs11_provider_finalize(p); |
185 | 0 | pkcs11_provider_unref(p); |
186 | 0 | return (0); |
187 | 0 | } |
188 | 0 | return (-1); |
189 | 0 | } |
190 | | |
191 | | static RSA_METHOD *rsa_method; |
192 | | static int rsa_idx = 0; |
193 | | #if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) |
194 | | static EC_KEY_METHOD *ec_key_method; |
195 | | static int ec_key_idx = 0; |
196 | | #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ |
197 | | |
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) |
202 | 0 | { |
203 | 0 | struct pkcs11_key *k11 = ptr; |
204 | |
|
205 | 0 | debug_f("parent %p ptr %p idx %d", parent, ptr, idx); |
206 | 0 | if (k11 == NULL) |
207 | 0 | return; |
208 | 0 | if (k11->provider) |
209 | 0 | pkcs11_provider_unref(k11->provider); |
210 | 0 | free(k11->keyid); |
211 | 0 | free(k11); |
212 | 0 | } |
213 | | |
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 | 0 | { |
219 | 0 | CK_FUNCTION_LIST *f; |
220 | 0 | CK_SESSION_HANDLE session; |
221 | 0 | CK_ULONG nfound = 0; |
222 | 0 | CK_RV rv; |
223 | 0 | int ret = -1; |
224 | |
|
225 | 0 | f = p->function_list; |
226 | 0 | session = p->slotinfo[slotidx].session; |
227 | 0 | if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { |
228 | 0 | error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); |
229 | 0 | return (-1); |
230 | 0 | } |
231 | 0 | if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK || |
232 | 0 | nfound != 1) { |
233 | 0 | debug("C_FindObjects failed (nfound %lu nattr %lu): %lu", |
234 | 0 | nfound, nattr, rv); |
235 | 0 | } else |
236 | 0 | ret = 0; |
237 | 0 | if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) |
238 | 0 | error("C_FindObjectsFinal failed: %lu", rv); |
239 | 0 | return (ret); |
240 | 0 | } |
241 | | |
242 | | static int |
243 | | pkcs11_login_slot(struct pkcs11_provider *provider, struct pkcs11_slotinfo *si, |
244 | | CK_USER_TYPE type) |
245 | 0 | { |
246 | 0 | char *pin = NULL, prompt[1024]; |
247 | 0 | CK_RV rv; |
248 | |
|
249 | 0 | if (provider == NULL || si == NULL || !provider->valid) { |
250 | 0 | error("no pkcs11 (valid) provider found"); |
251 | 0 | return (-1); |
252 | 0 | } |
253 | | |
254 | 0 | if (!pkcs11_interactive) { |
255 | 0 | error("need pin entry%s", |
256 | 0 | (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) ? |
257 | 0 | " on reader keypad" : ""); |
258 | 0 | return (-1); |
259 | 0 | } |
260 | 0 | if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) |
261 | 0 | verbose("Deferring PIN entry to reader keypad."); |
262 | 0 | else { |
263 | 0 | snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ", |
264 | 0 | si->token.label); |
265 | 0 | if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) { |
266 | 0 | debug_f("no pin specified"); |
267 | 0 | return (-1); /* bail out */ |
268 | 0 | } |
269 | 0 | } |
270 | 0 | rv = provider->function_list->C_Login(si->session, type, (u_char *)pin, |
271 | 0 | (pin != NULL) ? strlen(pin) : 0); |
272 | 0 | if (pin != NULL) |
273 | 0 | freezero(pin, strlen(pin)); |
274 | |
|
275 | 0 | switch (rv) { |
276 | 0 | case CKR_OK: |
277 | 0 | case CKR_USER_ALREADY_LOGGED_IN: |
278 | | /* success */ |
279 | 0 | break; |
280 | 0 | case CKR_PIN_LEN_RANGE: |
281 | 0 | error("PKCS#11 login failed: PIN length out of range"); |
282 | 0 | return -1; |
283 | 0 | case CKR_PIN_INCORRECT: |
284 | 0 | error("PKCS#11 login failed: PIN incorrect"); |
285 | 0 | return -1; |
286 | 0 | case CKR_PIN_LOCKED: |
287 | 0 | error("PKCS#11 login failed: PIN locked"); |
288 | 0 | return -1; |
289 | 0 | default: |
290 | 0 | error("PKCS#11 login failed: error %lu", rv); |
291 | 0 | return -1; |
292 | 0 | } |
293 | 0 | si->logged_in = 1; |
294 | 0 | return (0); |
295 | 0 | } |
296 | | |
297 | | static int |
298 | | pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type) |
299 | 0 | { |
300 | 0 | if (k11 == NULL || k11->provider == NULL || !k11->provider->valid) { |
301 | 0 | error("no pkcs11 (valid) provider found"); |
302 | 0 | return (-1); |
303 | 0 | } |
304 | | |
305 | 0 | return pkcs11_login_slot(k11->provider, |
306 | 0 | &k11->provider->slotinfo[k11->slotidx], type); |
307 | 0 | } |
308 | | |
309 | | |
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 | 0 | { |
314 | 0 | struct pkcs11_slotinfo *si; |
315 | 0 | CK_FUNCTION_LIST *f; |
316 | 0 | CK_BBOOL flag = 0; |
317 | 0 | CK_ATTRIBUTE attr; |
318 | 0 | CK_RV rv; |
319 | |
|
320 | 0 | *val = 0; |
321 | |
|
322 | 0 | if (!k11->provider || !k11->provider->valid) { |
323 | 0 | error("no pkcs11 (valid) provider found"); |
324 | 0 | return (-1); |
325 | 0 | } |
326 | | |
327 | 0 | f = k11->provider->function_list; |
328 | 0 | si = &k11->provider->slotinfo[k11->slotidx]; |
329 | |
|
330 | 0 | attr.type = type; |
331 | 0 | attr.pValue = &flag; |
332 | 0 | attr.ulValueLen = sizeof(flag); |
333 | |
|
334 | 0 | rv = f->C_GetAttributeValue(si->session, obj, &attr, 1); |
335 | 0 | if (rv != CKR_OK) { |
336 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
337 | 0 | return (-1); |
338 | 0 | } |
339 | 0 | *val = flag != 0; |
340 | 0 | debug_f("provider \"%s\" slot %lu object %lu: attrib %lu = %d", |
341 | 0 | k11->provider->name, k11->slotidx, obj, type, *val); |
342 | 0 | return (0); |
343 | 0 | } |
344 | | |
345 | | static int |
346 | | pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type) |
347 | 0 | { |
348 | 0 | struct pkcs11_slotinfo *si; |
349 | 0 | CK_FUNCTION_LIST *f; |
350 | 0 | CK_OBJECT_HANDLE obj; |
351 | 0 | CK_RV rv; |
352 | 0 | CK_OBJECT_CLASS private_key_class; |
353 | 0 | CK_BBOOL true_val; |
354 | 0 | CK_MECHANISM mech; |
355 | 0 | CK_ATTRIBUTE key_filter[3]; |
356 | 0 | int always_auth = 0; |
357 | 0 | int did_login = 0; |
358 | |
|
359 | 0 | if (!k11->provider || !k11->provider->valid) { |
360 | 0 | error("no pkcs11 (valid) provider found"); |
361 | 0 | return (-1); |
362 | 0 | } |
363 | | |
364 | 0 | f = k11->provider->function_list; |
365 | 0 | si = &k11->provider->slotinfo[k11->slotidx]; |
366 | |
|
367 | 0 | if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { |
368 | 0 | if (pkcs11_login(k11, CKU_USER) < 0) { |
369 | 0 | error("login failed"); |
370 | 0 | return (-1); |
371 | 0 | } |
372 | 0 | did_login = 1; |
373 | 0 | } |
374 | | |
375 | 0 | memset(&key_filter, 0, sizeof(key_filter)); |
376 | 0 | private_key_class = CKO_PRIVATE_KEY; |
377 | 0 | key_filter[0].type = CKA_CLASS; |
378 | 0 | key_filter[0].pValue = &private_key_class; |
379 | 0 | key_filter[0].ulValueLen = sizeof(private_key_class); |
380 | |
|
381 | 0 | key_filter[1].type = CKA_ID; |
382 | 0 | key_filter[1].pValue = k11->keyid; |
383 | 0 | key_filter[1].ulValueLen = k11->keyid_len; |
384 | |
|
385 | 0 | true_val = CK_TRUE; |
386 | 0 | key_filter[2].type = CKA_SIGN; |
387 | 0 | key_filter[2].pValue = &true_val; |
388 | 0 | key_filter[2].ulValueLen = sizeof(true_val); |
389 | | |
390 | | /* try to find object w/CKA_SIGN first, retry w/o */ |
391 | 0 | if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && |
392 | 0 | pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { |
393 | 0 | error("cannot find private key"); |
394 | 0 | return (-1); |
395 | 0 | } |
396 | | |
397 | 0 | memset(&mech, 0, sizeof(mech)); |
398 | 0 | mech.mechanism = mech_type; |
399 | 0 | mech.pParameter = NULL_PTR; |
400 | 0 | mech.ulParameterLen = 0; |
401 | |
|
402 | 0 | if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { |
403 | 0 | error("C_SignInit failed: %lu", rv); |
404 | 0 | return (-1); |
405 | 0 | } |
406 | | |
407 | 0 | pkcs11_check_obj_bool_attrib(k11, obj, CKA_ALWAYS_AUTHENTICATE, |
408 | 0 | &always_auth); /* ignore errors here */ |
409 | 0 | if (always_auth && !did_login) { |
410 | 0 | debug_f("always-auth key"); |
411 | 0 | if (pkcs11_login(k11, CKU_CONTEXT_SPECIFIC) < 0) { |
412 | 0 | error("login failed for always-auth key"); |
413 | 0 | return (-1); |
414 | 0 | } |
415 | 0 | } |
416 | | |
417 | 0 | return (0); |
418 | 0 | } |
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 | 0 | { |
425 | 0 | struct pkcs11_key *k11; |
426 | 0 | struct pkcs11_slotinfo *si; |
427 | 0 | CK_FUNCTION_LIST *f; |
428 | 0 | CK_ULONG tlen = 0; |
429 | 0 | CK_RV rv; |
430 | 0 | int rval = -1; |
431 | |
|
432 | 0 | if ((k11 = RSA_get_ex_data(rsa, rsa_idx)) == NULL) { |
433 | 0 | error("RSA_get_ex_data failed"); |
434 | 0 | return (-1); |
435 | 0 | } |
436 | | |
437 | 0 | if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) { |
438 | 0 | error("pkcs11_get_key failed"); |
439 | 0 | return (-1); |
440 | 0 | } |
441 | | |
442 | 0 | f = k11->provider->function_list; |
443 | 0 | si = &k11->provider->slotinfo[k11->slotidx]; |
444 | 0 | tlen = RSA_size(rsa); |
445 | | |
446 | | /* XXX handle CKR_BUFFER_TOO_SMALL */ |
447 | 0 | rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); |
448 | 0 | if (rv == CKR_OK) |
449 | 0 | rval = tlen; |
450 | 0 | else |
451 | 0 | error("C_Sign failed: %lu", rv); |
452 | |
|
453 | 0 | return (rval); |
454 | 0 | } |
455 | | |
456 | | static int |
457 | | pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, |
458 | | int padding) |
459 | 0 | { |
460 | 0 | return (-1); |
461 | 0 | } |
462 | | |
463 | | static int |
464 | | pkcs11_rsa_start_wrapper(void) |
465 | 0 | { |
466 | 0 | if (rsa_method != NULL) |
467 | 0 | return (0); |
468 | 0 | rsa_method = RSA_meth_dup(RSA_get_default_method()); |
469 | 0 | if (rsa_method == NULL) |
470 | 0 | return (-1); |
471 | 0 | rsa_idx = RSA_get_ex_new_index(0, "ssh-pkcs11-rsa", |
472 | 0 | NULL, NULL, pkcs11_k11_free); |
473 | 0 | if (rsa_idx == -1) |
474 | 0 | return (-1); |
475 | 0 | if (!RSA_meth_set1_name(rsa_method, "pkcs11") || |
476 | 0 | !RSA_meth_set_priv_enc(rsa_method, pkcs11_rsa_private_encrypt) || |
477 | 0 | !RSA_meth_set_priv_dec(rsa_method, pkcs11_rsa_private_decrypt)) { |
478 | 0 | error_f("setup pkcs11 method failed"); |
479 | 0 | return (-1); |
480 | 0 | } |
481 | 0 | return (0); |
482 | 0 | } |
483 | | |
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 | 0 | { |
489 | 0 | struct pkcs11_key *k11; |
490 | |
|
491 | 0 | if (pkcs11_rsa_start_wrapper() == -1) |
492 | 0 | return (-1); |
493 | | |
494 | 0 | k11 = xcalloc(1, sizeof(*k11)); |
495 | 0 | k11->provider = provider; |
496 | 0 | provider->refcount++; /* provider referenced by RSA key */ |
497 | 0 | k11->slotidx = slotidx; |
498 | | /* identify key object on smartcard */ |
499 | 0 | k11->keyid_len = keyid_attrib->ulValueLen; |
500 | 0 | if (k11->keyid_len > 0) { |
501 | 0 | k11->keyid = xmalloc(k11->keyid_len); |
502 | 0 | memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); |
503 | 0 | } |
504 | |
|
505 | 0 | RSA_set_method(rsa, rsa_method); |
506 | 0 | RSA_set_ex_data(rsa, rsa_idx, k11); |
507 | 0 | return (0); |
508 | 0 | } |
509 | | |
510 | | #if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) |
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 | 0 | { |
516 | 0 | struct pkcs11_key *k11; |
517 | 0 | struct pkcs11_slotinfo *si; |
518 | 0 | CK_FUNCTION_LIST *f; |
519 | 0 | CK_ULONG siglen = 0, bnlen; |
520 | 0 | CK_RV rv; |
521 | 0 | ECDSA_SIG *ret = NULL; |
522 | 0 | u_char *sig; |
523 | 0 | BIGNUM *r = NULL, *s = NULL; |
524 | |
|
525 | 0 | if ((k11 = EC_KEY_get_ex_data(ec, ec_key_idx)) == NULL) { |
526 | 0 | ossl_error("EC_KEY_get_ex_data failed for ec"); |
527 | 0 | return (NULL); |
528 | 0 | } |
529 | | |
530 | 0 | if (pkcs11_get_key(k11, CKM_ECDSA) == -1) { |
531 | 0 | error("pkcs11_get_key failed"); |
532 | 0 | return (NULL); |
533 | 0 | } |
534 | | |
535 | 0 | f = k11->provider->function_list; |
536 | 0 | si = &k11->provider->slotinfo[k11->slotidx]; |
537 | |
|
538 | 0 | siglen = ECDSA_size(ec); |
539 | 0 | sig = xmalloc(siglen); |
540 | | |
541 | | /* XXX handle CKR_BUFFER_TOO_SMALL */ |
542 | 0 | rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen); |
543 | 0 | if (rv != CKR_OK) { |
544 | 0 | error("C_Sign failed: %lu", rv); |
545 | 0 | goto done; |
546 | 0 | } |
547 | 0 | if (siglen < 64 || siglen > 132 || siglen % 2) { |
548 | 0 | error_f("bad signature length: %lu", (u_long)siglen); |
549 | 0 | goto done; |
550 | 0 | } |
551 | 0 | bnlen = siglen/2; |
552 | 0 | if ((ret = ECDSA_SIG_new()) == NULL) { |
553 | 0 | error("ECDSA_SIG_new failed"); |
554 | 0 | goto done; |
555 | 0 | } |
556 | 0 | if ((r = BN_bin2bn(sig, bnlen, NULL)) == NULL || |
557 | 0 | (s = BN_bin2bn(sig+bnlen, bnlen, NULL)) == NULL) { |
558 | 0 | ossl_error("BN_bin2bn failed"); |
559 | 0 | ECDSA_SIG_free(ret); |
560 | 0 | ret = NULL; |
561 | 0 | goto done; |
562 | 0 | } |
563 | 0 | if (!ECDSA_SIG_set0(ret, r, s)) { |
564 | 0 | error_f("ECDSA_SIG_set0 failed"); |
565 | 0 | ECDSA_SIG_free(ret); |
566 | 0 | ret = NULL; |
567 | 0 | goto done; |
568 | 0 | } |
569 | 0 | r = s = NULL; /* now owned by ret */ |
570 | | /* success */ |
571 | 0 | done: |
572 | 0 | BN_free(r); |
573 | 0 | BN_free(s); |
574 | 0 | free(sig); |
575 | |
|
576 | 0 | return (ret); |
577 | 0 | } |
578 | | |
579 | | static int |
580 | | pkcs11_ecdsa_start_wrapper(void) |
581 | 0 | { |
582 | 0 | int (*orig_sign)(int, const unsigned char *, int, unsigned char *, |
583 | 0 | unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL; |
584 | |
|
585 | 0 | if (ec_key_method != NULL) |
586 | 0 | return (0); |
587 | 0 | ec_key_idx = EC_KEY_get_ex_new_index(0, "ssh-pkcs11-ecdsa", |
588 | 0 | NULL, NULL, pkcs11_k11_free); |
589 | 0 | if (ec_key_idx == -1) |
590 | 0 | return (-1); |
591 | 0 | ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); |
592 | 0 | if (ec_key_method == NULL) |
593 | 0 | return (-1); |
594 | 0 | EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL); |
595 | 0 | EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign); |
596 | 0 | return (0); |
597 | 0 | } |
598 | | |
599 | | static int |
600 | | pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, |
601 | | CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec) |
602 | 0 | { |
603 | 0 | struct pkcs11_key *k11; |
604 | |
|
605 | 0 | if (pkcs11_ecdsa_start_wrapper() == -1) |
606 | 0 | return (-1); |
607 | | |
608 | 0 | k11 = xcalloc(1, sizeof(*k11)); |
609 | 0 | k11->provider = provider; |
610 | 0 | provider->refcount++; /* provider referenced by ECDSA key */ |
611 | 0 | k11->slotidx = slotidx; |
612 | | /* identify key object on smartcard */ |
613 | 0 | k11->keyid_len = keyid_attrib->ulValueLen; |
614 | 0 | if (k11->keyid_len > 0) { |
615 | 0 | k11->keyid = xmalloc(k11->keyid_len); |
616 | 0 | memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); |
617 | 0 | } |
618 | 0 | EC_KEY_set_method(ec, ec_key_method); |
619 | 0 | EC_KEY_set_ex_data(ec, ec_key_idx, k11); |
620 | |
|
621 | 0 | return (0); |
622 | 0 | } |
623 | | #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ |
624 | | |
625 | | /* remove trailing spaces */ |
626 | | static char * |
627 | | rmspace(u_char *buf, size_t len) |
628 | 0 | { |
629 | 0 | size_t i; |
630 | |
|
631 | 0 | if (len == 0) |
632 | 0 | return buf; |
633 | 0 | for (i = len - 1; i > 0; i--) |
634 | 0 | if (buf[i] == ' ') |
635 | 0 | buf[i] = '\0'; |
636 | 0 | else |
637 | 0 | break; |
638 | 0 | return buf; |
639 | 0 | } |
640 | | /* Used to printf fixed-width, space-padded, unterminated strings using %.*s */ |
641 | | #define RMSPACE(s) (int)sizeof(s), rmspace(s, sizeof(s)) |
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 |
648 | | pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin, |
649 | | CK_ULONG user) |
650 | 0 | { |
651 | 0 | struct pkcs11_slotinfo *si; |
652 | 0 | CK_FUNCTION_LIST *f; |
653 | 0 | CK_RV rv; |
654 | 0 | CK_SESSION_HANDLE session; |
655 | 0 | int login_required, ret; |
656 | |
|
657 | 0 | f = p->function_list; |
658 | 0 | si = &p->slotinfo[slotidx]; |
659 | |
|
660 | 0 | login_required = si->token.flags & CKF_LOGIN_REQUIRED; |
661 | | |
662 | | /* fail early before opening session */ |
663 | 0 | if (login_required && !pkcs11_interactive && |
664 | 0 | (pin == NULL || strlen(pin) == 0)) { |
665 | 0 | error("pin required"); |
666 | 0 | return (-SSH_PKCS11_ERR_PIN_REQUIRED); |
667 | 0 | } |
668 | 0 | if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| |
669 | 0 | CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) { |
670 | 0 | error("C_OpenSession failed: %lu", rv); |
671 | 0 | return (-1); |
672 | 0 | } |
673 | 0 | if (login_required && pin != NULL && strlen(pin) != 0) { |
674 | 0 | rv = f->C_Login(session, user, (u_char *)pin, strlen(pin)); |
675 | 0 | if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { |
676 | 0 | error("C_Login failed: %lu", rv); |
677 | 0 | ret = (rv == CKR_PIN_LOCKED) ? |
678 | 0 | -SSH_PKCS11_ERR_PIN_LOCKED : |
679 | 0 | -SSH_PKCS11_ERR_LOGIN_FAIL; |
680 | 0 | if ((rv = f->C_CloseSession(session)) != CKR_OK) |
681 | 0 | error("C_CloseSession failed: %lu", rv); |
682 | 0 | return (ret); |
683 | 0 | } |
684 | 0 | si->logged_in = 1; |
685 | 0 | } |
686 | 0 | si->session = session; |
687 | 0 | return (0); |
688 | 0 | } |
689 | | |
690 | | static int |
691 | | pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) |
692 | 0 | { |
693 | 0 | int i; |
694 | |
|
695 | 0 | for (i = 0; i < *nkeys; i++) |
696 | 0 | if (sshkey_equal(key, (*keysp)[i])) |
697 | 0 | return (1); |
698 | 0 | return (0); |
699 | 0 | } |
700 | | |
701 | | #if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) |
702 | | static struct sshkey * |
703 | | pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, |
704 | | CK_OBJECT_HANDLE *obj) |
705 | 0 | { |
706 | 0 | CK_ATTRIBUTE key_attr[3]; |
707 | 0 | CK_SESSION_HANDLE session; |
708 | 0 | CK_FUNCTION_LIST *f = NULL; |
709 | 0 | CK_RV rv; |
710 | 0 | ASN1_OCTET_STRING *octet = NULL; |
711 | 0 | EC_KEY *ec = NULL; |
712 | 0 | EC_GROUP *group = NULL; |
713 | 0 | struct sshkey *key = NULL; |
714 | 0 | const unsigned char *attrp = NULL; |
715 | 0 | int i; |
716 | 0 | int nid; |
717 | |
|
718 | 0 | memset(&key_attr, 0, sizeof(key_attr)); |
719 | 0 | key_attr[0].type = CKA_ID; |
720 | 0 | key_attr[1].type = CKA_EC_POINT; |
721 | 0 | key_attr[2].type = CKA_EC_PARAMS; |
722 | |
|
723 | 0 | session = p->slotinfo[slotidx].session; |
724 | 0 | f = p->function_list; |
725 | | |
726 | | /* figure out size of the attributes */ |
727 | 0 | rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); |
728 | 0 | if (rv != CKR_OK) { |
729 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
730 | 0 | return (NULL); |
731 | 0 | } |
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 | 0 | if (key_attr[1].ulValueLen == 0 || |
739 | 0 | key_attr[2].ulValueLen == 0) { |
740 | 0 | error("invalid attribute length"); |
741 | 0 | return (NULL); |
742 | 0 | } |
743 | | |
744 | | /* allocate buffers for attributes */ |
745 | 0 | for (i = 0; i < 3; i++) |
746 | 0 | if (key_attr[i].ulValueLen > 0) |
747 | 0 | key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen); |
748 | | |
749 | | /* retrieve ID, public point and curve parameters of EC key */ |
750 | 0 | rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); |
751 | 0 | if (rv != CKR_OK) { |
752 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
753 | 0 | goto fail; |
754 | 0 | } |
755 | | |
756 | 0 | ec = EC_KEY_new(); |
757 | 0 | if (ec == NULL) { |
758 | 0 | error("EC_KEY_new failed"); |
759 | 0 | goto fail; |
760 | 0 | } |
761 | | |
762 | 0 | attrp = key_attr[2].pValue; |
763 | 0 | group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen); |
764 | 0 | if (group == NULL) { |
765 | 0 | ossl_error("d2i_ECPKParameters failed"); |
766 | 0 | goto fail; |
767 | 0 | } |
768 | | |
769 | 0 | if (EC_KEY_set_group(ec, group) == 0) { |
770 | 0 | ossl_error("EC_KEY_set_group failed"); |
771 | 0 | goto fail; |
772 | 0 | } |
773 | | |
774 | 0 | if (key_attr[1].ulValueLen <= 2) { |
775 | 0 | error("CKA_EC_POINT too small"); |
776 | 0 | goto fail; |
777 | 0 | } |
778 | | |
779 | 0 | attrp = key_attr[1].pValue; |
780 | 0 | octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen); |
781 | 0 | if (octet == NULL) { |
782 | 0 | ossl_error("d2i_ASN1_OCTET_STRING failed"); |
783 | 0 | goto fail; |
784 | 0 | } |
785 | 0 | attrp = octet->data; |
786 | 0 | if (o2i_ECPublicKey(&ec, &attrp, octet->length) == NULL) { |
787 | 0 | ossl_error("o2i_ECPublicKey failed"); |
788 | 0 | goto fail; |
789 | 0 | } |
790 | | |
791 | 0 | nid = sshkey_ecdsa_key_to_nid(ec); |
792 | 0 | if (nid < 0) { |
793 | 0 | error("couldn't get curve nid"); |
794 | 0 | goto fail; |
795 | 0 | } |
796 | | |
797 | 0 | if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec)) |
798 | 0 | goto fail; |
799 | | |
800 | 0 | key = sshkey_new(KEY_UNSPEC); |
801 | 0 | if (key == NULL) { |
802 | 0 | error("sshkey_new failed"); |
803 | 0 | goto fail; |
804 | 0 | } |
805 | | |
806 | 0 | key->ecdsa = ec; |
807 | 0 | key->ecdsa_nid = nid; |
808 | 0 | key->type = KEY_ECDSA; |
809 | 0 | key->flags |= SSHKEY_FLAG_EXT; |
810 | 0 | ec = NULL; /* now owned by key */ |
811 | |
|
812 | 0 | fail: |
813 | 0 | for (i = 0; i < 3; i++) |
814 | 0 | free(key_attr[i].pValue); |
815 | 0 | if (ec) |
816 | 0 | EC_KEY_free(ec); |
817 | 0 | if (group) |
818 | 0 | EC_GROUP_free(group); |
819 | 0 | if (octet) |
820 | 0 | ASN1_OCTET_STRING_free(octet); |
821 | |
|
822 | 0 | return (key); |
823 | 0 | } |
824 | | #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ |
825 | | |
826 | | static struct sshkey * |
827 | | pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, |
828 | | CK_OBJECT_HANDLE *obj) |
829 | 0 | { |
830 | 0 | CK_ATTRIBUTE key_attr[3]; |
831 | 0 | CK_SESSION_HANDLE session; |
832 | 0 | CK_FUNCTION_LIST *f = NULL; |
833 | 0 | CK_RV rv; |
834 | 0 | RSA *rsa = NULL; |
835 | 0 | BIGNUM *rsa_n, *rsa_e; |
836 | 0 | struct sshkey *key = NULL; |
837 | 0 | int i; |
838 | |
|
839 | 0 | memset(&key_attr, 0, sizeof(key_attr)); |
840 | 0 | key_attr[0].type = CKA_ID; |
841 | 0 | key_attr[1].type = CKA_MODULUS; |
842 | 0 | key_attr[2].type = CKA_PUBLIC_EXPONENT; |
843 | |
|
844 | 0 | session = p->slotinfo[slotidx].session; |
845 | 0 | f = p->function_list; |
846 | | |
847 | | /* figure out size of the attributes */ |
848 | 0 | rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); |
849 | 0 | if (rv != CKR_OK) { |
850 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
851 | 0 | return (NULL); |
852 | 0 | } |
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 | 0 | if (key_attr[1].ulValueLen == 0 || |
860 | 0 | key_attr[2].ulValueLen == 0) { |
861 | 0 | error("invalid attribute length"); |
862 | 0 | return (NULL); |
863 | 0 | } |
864 | | |
865 | | /* allocate buffers for attributes */ |
866 | 0 | for (i = 0; i < 3; i++) |
867 | 0 | if (key_attr[i].ulValueLen > 0) |
868 | 0 | key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen); |
869 | | |
870 | | /* retrieve ID, modulus and public exponent of RSA key */ |
871 | 0 | rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); |
872 | 0 | if (rv != CKR_OK) { |
873 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
874 | 0 | goto fail; |
875 | 0 | } |
876 | | |
877 | 0 | rsa = RSA_new(); |
878 | 0 | if (rsa == NULL) { |
879 | 0 | error("RSA_new failed"); |
880 | 0 | goto fail; |
881 | 0 | } |
882 | | |
883 | 0 | rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL); |
884 | 0 | rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL); |
885 | 0 | if (rsa_n == NULL || rsa_e == NULL) { |
886 | 0 | error("BN_bin2bn failed"); |
887 | 0 | goto fail; |
888 | 0 | } |
889 | 0 | if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL)) |
890 | 0 | fatal_f("set key"); |
891 | 0 | rsa_n = rsa_e = NULL; /* transferred */ |
892 | |
|
893 | 0 | if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa)) |
894 | 0 | goto fail; |
895 | | |
896 | 0 | key = sshkey_new(KEY_UNSPEC); |
897 | 0 | if (key == NULL) { |
898 | 0 | error("sshkey_new failed"); |
899 | 0 | goto fail; |
900 | 0 | } |
901 | | |
902 | 0 | key->rsa = rsa; |
903 | 0 | key->type = KEY_RSA; |
904 | 0 | key->flags |= SSHKEY_FLAG_EXT; |
905 | 0 | rsa = NULL; /* now owned by key */ |
906 | |
|
907 | 0 | fail: |
908 | 0 | for (i = 0; i < 3; i++) |
909 | 0 | free(key_attr[i].pValue); |
910 | 0 | RSA_free(rsa); |
911 | |
|
912 | 0 | return (key); |
913 | 0 | } |
914 | | |
915 | | static int |
916 | | pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, |
917 | | CK_OBJECT_HANDLE *obj, struct sshkey **keyp, char **labelp) |
918 | 0 | { |
919 | 0 | CK_ATTRIBUTE cert_attr[3]; |
920 | 0 | CK_SESSION_HANDLE session; |
921 | 0 | CK_FUNCTION_LIST *f = NULL; |
922 | 0 | CK_RV rv; |
923 | 0 | X509 *x509 = NULL; |
924 | 0 | X509_NAME *x509_name = NULL; |
925 | 0 | EVP_PKEY *evp; |
926 | 0 | RSA *rsa = NULL; |
927 | 0 | #ifdef OPENSSL_HAS_ECC |
928 | 0 | EC_KEY *ec = NULL; |
929 | 0 | #endif |
930 | 0 | struct sshkey *key = NULL; |
931 | 0 | int i; |
932 | 0 | #if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) |
933 | 0 | int nid; |
934 | 0 | #endif |
935 | 0 | const u_char *cp; |
936 | 0 | char *subject = NULL; |
937 | |
|
938 | 0 | *keyp = NULL; |
939 | 0 | *labelp = NULL; |
940 | |
|
941 | 0 | memset(&cert_attr, 0, sizeof(cert_attr)); |
942 | 0 | cert_attr[0].type = CKA_ID; |
943 | 0 | cert_attr[1].type = CKA_SUBJECT; |
944 | 0 | cert_attr[2].type = CKA_VALUE; |
945 | |
|
946 | 0 | session = p->slotinfo[slotidx].session; |
947 | 0 | f = p->function_list; |
948 | | |
949 | | /* figure out size of the attributes */ |
950 | 0 | rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3); |
951 | 0 | if (rv != CKR_OK) { |
952 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
953 | 0 | return -1; |
954 | 0 | } |
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 | 0 | if (cert_attr[1].ulValueLen == 0 || |
962 | 0 | cert_attr[2].ulValueLen == 0) { |
963 | 0 | error("invalid attribute length"); |
964 | 0 | return -1; |
965 | 0 | } |
966 | | |
967 | | /* allocate buffers for attributes */ |
968 | 0 | for (i = 0; i < 3; i++) |
969 | 0 | if (cert_attr[i].ulValueLen > 0) |
970 | 0 | cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen); |
971 | | |
972 | | /* retrieve ID, subject and value of certificate */ |
973 | 0 | rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3); |
974 | 0 | if (rv != CKR_OK) { |
975 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
976 | 0 | goto out; |
977 | 0 | } |
978 | | |
979 | | /* Decode DER-encoded cert subject */ |
980 | 0 | cp = cert_attr[1].pValue; |
981 | 0 | if ((x509_name = d2i_X509_NAME(NULL, &cp, |
982 | 0 | cert_attr[1].ulValueLen)) == NULL || |
983 | 0 | (subject = X509_NAME_oneline(x509_name, NULL, 0)) == NULL) |
984 | 0 | subject = xstrdup("invalid subject"); |
985 | 0 | X509_NAME_free(x509_name); |
986 | |
|
987 | 0 | cp = cert_attr[2].pValue; |
988 | 0 | if ((x509 = d2i_X509(NULL, &cp, cert_attr[2].ulValueLen)) == NULL) { |
989 | 0 | error("d2i_x509 failed"); |
990 | 0 | goto out; |
991 | 0 | } |
992 | | |
993 | 0 | if ((evp = X509_get_pubkey(x509)) == NULL) { |
994 | 0 | error("X509_get_pubkey failed"); |
995 | 0 | goto out; |
996 | 0 | } |
997 | | |
998 | 0 | if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) { |
999 | 0 | if (EVP_PKEY_get0_RSA(evp) == NULL) { |
1000 | 0 | error("invalid x509; no rsa key"); |
1001 | 0 | goto out; |
1002 | 0 | } |
1003 | 0 | if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) { |
1004 | 0 | error("RSAPublicKey_dup failed"); |
1005 | 0 | goto out; |
1006 | 0 | } |
1007 | | |
1008 | 0 | if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa)) |
1009 | 0 | goto out; |
1010 | | |
1011 | 0 | key = sshkey_new(KEY_UNSPEC); |
1012 | 0 | if (key == NULL) { |
1013 | 0 | error("sshkey_new failed"); |
1014 | 0 | goto out; |
1015 | 0 | } |
1016 | | |
1017 | 0 | key->rsa = rsa; |
1018 | 0 | key->type = KEY_RSA; |
1019 | 0 | key->flags |= SSHKEY_FLAG_EXT; |
1020 | 0 | rsa = NULL; /* now owned by key */ |
1021 | 0 | #if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) |
1022 | 0 | } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) { |
1023 | 0 | if (EVP_PKEY_get0_EC_KEY(evp) == NULL) { |
1024 | 0 | error("invalid x509; no ec key"); |
1025 | 0 | goto out; |
1026 | 0 | } |
1027 | 0 | if ((ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(evp))) == NULL) { |
1028 | 0 | error("EC_KEY_dup failed"); |
1029 | 0 | goto out; |
1030 | 0 | } |
1031 | | |
1032 | 0 | nid = sshkey_ecdsa_key_to_nid(ec); |
1033 | 0 | if (nid < 0) { |
1034 | 0 | error("couldn't get curve nid"); |
1035 | 0 | goto out; |
1036 | 0 | } |
1037 | | |
1038 | 0 | if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec)) |
1039 | 0 | goto out; |
1040 | | |
1041 | 0 | key = sshkey_new(KEY_UNSPEC); |
1042 | 0 | if (key == NULL) { |
1043 | 0 | error("sshkey_new failed"); |
1044 | 0 | goto out; |
1045 | 0 | } |
1046 | | |
1047 | 0 | key->ecdsa = ec; |
1048 | 0 | key->ecdsa_nid = nid; |
1049 | 0 | key->type = KEY_ECDSA; |
1050 | 0 | key->flags |= SSHKEY_FLAG_EXT; |
1051 | 0 | ec = NULL; /* now owned by key */ |
1052 | 0 | #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ |
1053 | 0 | } else { |
1054 | 0 | error("unknown certificate key type"); |
1055 | 0 | goto out; |
1056 | 0 | } |
1057 | 0 | out: |
1058 | 0 | for (i = 0; i < 3; i++) |
1059 | 0 | free(cert_attr[i].pValue); |
1060 | 0 | X509_free(x509); |
1061 | 0 | RSA_free(rsa); |
1062 | 0 | #ifdef OPENSSL_HAS_ECC |
1063 | 0 | EC_KEY_free(ec); |
1064 | 0 | #endif |
1065 | 0 | if (key == NULL) { |
1066 | 0 | free(subject); |
1067 | 0 | return -1; |
1068 | 0 | } |
1069 | | /* success */ |
1070 | 0 | *keyp = key; |
1071 | 0 | *labelp = subject; |
1072 | 0 | return 0; |
1073 | 0 | } |
1074 | | |
1075 | | #if 0 |
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 | | } |
1084 | | #endif |
1085 | | |
1086 | | static void |
1087 | | note_key(struct pkcs11_provider *p, CK_ULONG slotidx, const char *context, |
1088 | | struct sshkey *key) |
1089 | 0 | { |
1090 | 0 | char *fp; |
1091 | |
|
1092 | 0 | if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, |
1093 | 0 | SSH_FP_DEFAULT)) == NULL) { |
1094 | 0 | error_f("sshkey_fingerprint failed"); |
1095 | 0 | return; |
1096 | 0 | } |
1097 | 0 | debug2("%s: provider %s slot %lu: %s %s", context, p->name, |
1098 | 0 | (u_long)slotidx, sshkey_type(key), fp); |
1099 | 0 | free(fp); |
1100 | 0 | } |
1101 | | |
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 | | */ |
1107 | | static int |
1108 | | pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx, |
1109 | | struct sshkey ***keysp, char ***labelsp, int *nkeys) |
1110 | 0 | { |
1111 | 0 | struct sshkey *key = NULL; |
1112 | 0 | CK_OBJECT_CLASS key_class; |
1113 | 0 | CK_ATTRIBUTE key_attr[1]; |
1114 | 0 | CK_SESSION_HANDLE session; |
1115 | 0 | CK_FUNCTION_LIST *f = NULL; |
1116 | 0 | CK_RV rv; |
1117 | 0 | CK_OBJECT_HANDLE obj; |
1118 | 0 | CK_ULONG n = 0; |
1119 | 0 | int ret = -1; |
1120 | 0 | char *label; |
1121 | |
|
1122 | 0 | memset(&key_attr, 0, sizeof(key_attr)); |
1123 | 0 | memset(&obj, 0, sizeof(obj)); |
1124 | |
|
1125 | 0 | key_class = CKO_CERTIFICATE; |
1126 | 0 | key_attr[0].type = CKA_CLASS; |
1127 | 0 | key_attr[0].pValue = &key_class; |
1128 | 0 | key_attr[0].ulValueLen = sizeof(key_class); |
1129 | |
|
1130 | 0 | session = p->slotinfo[slotidx].session; |
1131 | 0 | f = p->function_list; |
1132 | |
|
1133 | 0 | rv = f->C_FindObjectsInit(session, key_attr, 1); |
1134 | 0 | if (rv != CKR_OK) { |
1135 | 0 | error("C_FindObjectsInit failed: %lu", rv); |
1136 | 0 | goto fail; |
1137 | 0 | } |
1138 | | |
1139 | 0 | while (1) { |
1140 | 0 | CK_CERTIFICATE_TYPE ck_cert_type; |
1141 | |
|
1142 | 0 | rv = f->C_FindObjects(session, &obj, 1, &n); |
1143 | 0 | if (rv != CKR_OK) { |
1144 | 0 | error("C_FindObjects failed: %lu", rv); |
1145 | 0 | goto fail; |
1146 | 0 | } |
1147 | 0 | if (n == 0) |
1148 | 0 | break; |
1149 | | |
1150 | 0 | memset(&ck_cert_type, 0, sizeof(ck_cert_type)); |
1151 | 0 | memset(&key_attr, 0, sizeof(key_attr)); |
1152 | 0 | key_attr[0].type = CKA_CERTIFICATE_TYPE; |
1153 | 0 | key_attr[0].pValue = &ck_cert_type; |
1154 | 0 | key_attr[0].ulValueLen = sizeof(ck_cert_type); |
1155 | |
|
1156 | 0 | rv = f->C_GetAttributeValue(session, obj, key_attr, 1); |
1157 | 0 | if (rv != CKR_OK) { |
1158 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
1159 | 0 | goto fail; |
1160 | 0 | } |
1161 | | |
1162 | 0 | key = NULL; |
1163 | 0 | label = NULL; |
1164 | 0 | switch (ck_cert_type) { |
1165 | 0 | case CKC_X_509: |
1166 | 0 | if (pkcs11_fetch_x509_pubkey(p, slotidx, &obj, |
1167 | 0 | &key, &label) != 0) { |
1168 | 0 | error("failed to fetch key"); |
1169 | 0 | continue; |
1170 | 0 | } |
1171 | 0 | break; |
1172 | 0 | default: |
1173 | 0 | error("skipping unsupported certificate type %lu", |
1174 | 0 | ck_cert_type); |
1175 | 0 | continue; |
1176 | 0 | } |
1177 | 0 | note_key(p, slotidx, __func__, key); |
1178 | 0 | if (pkcs11_key_included(keysp, nkeys, key)) { |
1179 | 0 | debug2_f("key already included");; |
1180 | 0 | sshkey_free(key); |
1181 | 0 | } else { |
1182 | | /* expand key array and add key */ |
1183 | 0 | *keysp = xrecallocarray(*keysp, *nkeys, |
1184 | 0 | *nkeys + 1, sizeof(struct sshkey *)); |
1185 | 0 | (*keysp)[*nkeys] = key; |
1186 | 0 | if (labelsp != NULL) { |
1187 | 0 | *labelsp = xrecallocarray(*labelsp, *nkeys, |
1188 | 0 | *nkeys + 1, sizeof(char *)); |
1189 | 0 | (*labelsp)[*nkeys] = xstrdup((char *)label); |
1190 | 0 | } |
1191 | 0 | *nkeys = *nkeys + 1; |
1192 | 0 | debug("have %d keys", *nkeys); |
1193 | 0 | } |
1194 | 0 | } |
1195 | | |
1196 | 0 | ret = 0; |
1197 | 0 | fail: |
1198 | 0 | rv = f->C_FindObjectsFinal(session); |
1199 | 0 | if (rv != CKR_OK) { |
1200 | 0 | error("C_FindObjectsFinal failed: %lu", rv); |
1201 | 0 | ret = -1; |
1202 | 0 | } |
1203 | |
|
1204 | 0 | return (ret); |
1205 | 0 | } |
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, |
1214 | | struct sshkey ***keysp, char ***labelsp, int *nkeys) |
1215 | 0 | { |
1216 | 0 | struct sshkey *key = NULL; |
1217 | 0 | CK_OBJECT_CLASS key_class; |
1218 | 0 | CK_ATTRIBUTE key_attr[2]; |
1219 | 0 | CK_SESSION_HANDLE session; |
1220 | 0 | CK_FUNCTION_LIST *f = NULL; |
1221 | 0 | CK_RV rv; |
1222 | 0 | CK_OBJECT_HANDLE obj; |
1223 | 0 | CK_ULONG n = 0; |
1224 | 0 | int ret = -1; |
1225 | |
|
1226 | 0 | memset(&key_attr, 0, sizeof(key_attr)); |
1227 | 0 | memset(&obj, 0, sizeof(obj)); |
1228 | |
|
1229 | 0 | key_class = CKO_PUBLIC_KEY; |
1230 | 0 | key_attr[0].type = CKA_CLASS; |
1231 | 0 | key_attr[0].pValue = &key_class; |
1232 | 0 | key_attr[0].ulValueLen = sizeof(key_class); |
1233 | |
|
1234 | 0 | session = p->slotinfo[slotidx].session; |
1235 | 0 | f = p->function_list; |
1236 | |
|
1237 | 0 | rv = f->C_FindObjectsInit(session, key_attr, 1); |
1238 | 0 | if (rv != CKR_OK) { |
1239 | 0 | error("C_FindObjectsInit failed: %lu", rv); |
1240 | 0 | goto fail; |
1241 | 0 | } |
1242 | | |
1243 | 0 | while (1) { |
1244 | 0 | CK_KEY_TYPE ck_key_type; |
1245 | 0 | CK_UTF8CHAR label[256]; |
1246 | |
|
1247 | 0 | rv = f->C_FindObjects(session, &obj, 1, &n); |
1248 | 0 | if (rv != CKR_OK) { |
1249 | 0 | error("C_FindObjects failed: %lu", rv); |
1250 | 0 | goto fail; |
1251 | 0 | } |
1252 | 0 | if (n == 0) |
1253 | 0 | break; |
1254 | | |
1255 | 0 | memset(&ck_key_type, 0, sizeof(ck_key_type)); |
1256 | 0 | memset(&key_attr, 0, sizeof(key_attr)); |
1257 | 0 | key_attr[0].type = CKA_KEY_TYPE; |
1258 | 0 | key_attr[0].pValue = &ck_key_type; |
1259 | 0 | key_attr[0].ulValueLen = sizeof(ck_key_type); |
1260 | 0 | key_attr[1].type = CKA_LABEL; |
1261 | 0 | key_attr[1].pValue = &label; |
1262 | 0 | key_attr[1].ulValueLen = sizeof(label) - 1; |
1263 | |
|
1264 | 0 | rv = f->C_GetAttributeValue(session, obj, key_attr, 2); |
1265 | 0 | if (rv != CKR_OK) { |
1266 | 0 | error("C_GetAttributeValue failed: %lu", rv); |
1267 | 0 | goto fail; |
1268 | 0 | } |
1269 | | |
1270 | 0 | label[key_attr[1].ulValueLen] = '\0'; |
1271 | |
|
1272 | 0 | switch (ck_key_type) { |
1273 | 0 | case CKK_RSA: |
1274 | 0 | key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj); |
1275 | 0 | break; |
1276 | 0 | #if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) |
1277 | 0 | case CKK_ECDSA: |
1278 | 0 | key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj); |
1279 | 0 | break; |
1280 | 0 | #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ |
1281 | 0 | default: |
1282 | | /* XXX print key type? */ |
1283 | 0 | key = NULL; |
1284 | 0 | error("skipping unsupported key type"); |
1285 | 0 | } |
1286 | | |
1287 | 0 | if (key == NULL) { |
1288 | 0 | error("failed to fetch key"); |
1289 | 0 | continue; |
1290 | 0 | } |
1291 | 0 | note_key(p, slotidx, __func__, key); |
1292 | 0 | if (pkcs11_key_included(keysp, nkeys, key)) { |
1293 | 0 | debug2_f("key already included");; |
1294 | 0 | sshkey_free(key); |
1295 | 0 | } else { |
1296 | | /* expand key array and add key */ |
1297 | 0 | *keysp = xrecallocarray(*keysp, *nkeys, |
1298 | 0 | *nkeys + 1, sizeof(struct sshkey *)); |
1299 | 0 | (*keysp)[*nkeys] = key; |
1300 | 0 | if (labelsp != NULL) { |
1301 | 0 | *labelsp = xrecallocarray(*labelsp, *nkeys, |
1302 | 0 | *nkeys + 1, sizeof(char *)); |
1303 | 0 | (*labelsp)[*nkeys] = xstrdup((char *)label); |
1304 | 0 | } |
1305 | 0 | *nkeys = *nkeys + 1; |
1306 | 0 | debug("have %d keys", *nkeys); |
1307 | 0 | } |
1308 | 0 | } |
1309 | | |
1310 | 0 | ret = 0; |
1311 | 0 | fail: |
1312 | 0 | rv = f->C_FindObjectsFinal(session); |
1313 | 0 | if (rv != CKR_OK) { |
1314 | 0 | error("C_FindObjectsFinal failed: %lu", rv); |
1315 | 0 | ret = -1; |
1316 | 0 | } |
1317 | |
|
1318 | 0 | return (ret); |
1319 | 0 | } |
1320 | | |
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) { |
1380 | | error_f("key generation failed: error 0x%lx", rv); |
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}, |
1429 | | {NULL, NULL, NULL, 0}, |
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) { |
1459 | | error_f("invalid key size %lu", bits); |
1460 | | return NULL; |
1461 | | } |
1462 | | if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams, |
1463 | | &ecparams_size) == -1) { |
1464 | | error_f("invalid oid"); |
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) { |
1497 | | error_f("key generation failed: error 0x%lx", rv); |
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 |
1511 | | pkcs11_register_provider(char *provider_id, char *pin, |
1512 | | struct sshkey ***keyp, char ***labelsp, |
1513 | | struct pkcs11_provider **providerp, CK_ULONG user) |
1514 | 0 | { |
1515 | 0 | int nkeys, need_finalize = 0; |
1516 | 0 | int ret = -1; |
1517 | 0 | struct pkcs11_provider *p = NULL; |
1518 | 0 | void *handle = NULL; |
1519 | 0 | CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); |
1520 | 0 | CK_RV rv; |
1521 | 0 | CK_FUNCTION_LIST *f = NULL; |
1522 | 0 | CK_TOKEN_INFO *token; |
1523 | 0 | CK_ULONG i; |
1524 | |
|
1525 | 0 | if (providerp == NULL) |
1526 | 0 | goto fail; |
1527 | 0 | *providerp = NULL; |
1528 | |
|
1529 | 0 | if (keyp != NULL) |
1530 | 0 | *keyp = NULL; |
1531 | 0 | if (labelsp != NULL) |
1532 | 0 | *labelsp = NULL; |
1533 | |
|
1534 | 0 | if (pkcs11_provider_lookup(provider_id) != NULL) { |
1535 | 0 | debug_f("provider already registered: %s", provider_id); |
1536 | 0 | goto fail; |
1537 | 0 | } |
1538 | 0 | if (lib_contains_symbol(provider_id, "C_GetFunctionList") != 0) { |
1539 | 0 | error("provider %s is not a PKCS11 library", provider_id); |
1540 | 0 | goto fail; |
1541 | 0 | } |
1542 | | /* open shared pkcs11-library */ |
1543 | 0 | if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { |
1544 | 0 | error("dlopen %s failed: %s", provider_id, dlerror()); |
1545 | 0 | goto fail; |
1546 | 0 | } |
1547 | 0 | if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) |
1548 | 0 | fatal("dlsym(C_GetFunctionList) failed: %s", dlerror()); |
1549 | 0 | p = xcalloc(1, sizeof(*p)); |
1550 | 0 | p->name = xstrdup(provider_id); |
1551 | 0 | p->handle = handle; |
1552 | | /* setup the pkcs11 callbacks */ |
1553 | 0 | if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { |
1554 | 0 | error("C_GetFunctionList for provider %s failed: %lu", |
1555 | 0 | provider_id, rv); |
1556 | 0 | goto fail; |
1557 | 0 | } |
1558 | 0 | p->function_list = f; |
1559 | 0 | if ((rv = f->C_Initialize(NULL)) != CKR_OK) { |
1560 | 0 | error("C_Initialize for provider %s failed: %lu", |
1561 | 0 | provider_id, rv); |
1562 | 0 | goto fail; |
1563 | 0 | } |
1564 | 0 | need_finalize = 1; |
1565 | 0 | if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { |
1566 | 0 | error("C_GetInfo for provider %s failed: %lu", |
1567 | 0 | provider_id, rv); |
1568 | 0 | goto fail; |
1569 | 0 | } |
1570 | 0 | debug("provider %s: manufacturerID <%.*s> cryptokiVersion %d.%d" |
1571 | 0 | " libraryDescription <%.*s> libraryVersion %d.%d", |
1572 | 0 | provider_id, |
1573 | 0 | RMSPACE(p->info.manufacturerID), |
1574 | 0 | p->info.cryptokiVersion.major, |
1575 | 0 | p->info.cryptokiVersion.minor, |
1576 | 0 | RMSPACE(p->info.libraryDescription), |
1577 | 0 | p->info.libraryVersion.major, |
1578 | 0 | p->info.libraryVersion.minor); |
1579 | 0 | if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { |
1580 | 0 | error("C_GetSlotList failed: %lu", rv); |
1581 | 0 | goto fail; |
1582 | 0 | } |
1583 | 0 | if (p->nslots == 0) { |
1584 | 0 | debug_f("provider %s returned no slots", provider_id); |
1585 | 0 | ret = -SSH_PKCS11_ERR_NO_SLOTS; |
1586 | 0 | goto fail; |
1587 | 0 | } |
1588 | 0 | p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); |
1589 | 0 | if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) |
1590 | 0 | != CKR_OK) { |
1591 | 0 | error("C_GetSlotList for provider %s failed: %lu", |
1592 | 0 | provider_id, rv); |
1593 | 0 | goto fail; |
1594 | 0 | } |
1595 | 0 | p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); |
1596 | 0 | p->valid = 1; |
1597 | 0 | nkeys = 0; |
1598 | 0 | for (i = 0; i < p->nslots; i++) { |
1599 | 0 | token = &p->slotinfo[i].token; |
1600 | 0 | if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) |
1601 | 0 | != CKR_OK) { |
1602 | 0 | error("C_GetTokenInfo for provider %s slot %lu " |
1603 | 0 | "failed: %lu", provider_id, (u_long)i, rv); |
1604 | 0 | continue; |
1605 | 0 | } |
1606 | 0 | if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { |
1607 | 0 | debug2_f("ignoring uninitialised token in " |
1608 | 0 | "provider %s slot %lu", provider_id, (u_long)i); |
1609 | 0 | continue; |
1610 | 0 | } |
1611 | 0 | debug("provider %s slot %lu: label <%.*s> " |
1612 | 0 | "manufacturerID <%.*s> model <%.*s> serial <%.*s> " |
1613 | 0 | "flags 0x%lx", |
1614 | 0 | provider_id, (unsigned long)i, |
1615 | 0 | RMSPACE(token->label), RMSPACE(token->manufacturerID), |
1616 | 0 | RMSPACE(token->model), RMSPACE(token->serialNumber), |
1617 | 0 | token->flags); |
1618 | | /* |
1619 | | * open session, login with pin and retrieve public |
1620 | | * keys (if keyp is provided) |
1621 | | */ |
1622 | 0 | if ((ret = pkcs11_open_session(p, i, pin, user)) != 0 || |
1623 | 0 | keyp == NULL) |
1624 | 0 | continue; |
1625 | 0 | pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys); |
1626 | 0 | pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys); |
1627 | 0 | if (nkeys == 0 && !p->slotinfo[i].logged_in && |
1628 | 0 | pkcs11_interactive) { |
1629 | | /* |
1630 | | * Some tokens require login before they will |
1631 | | * expose keys. |
1632 | | */ |
1633 | 0 | if (pkcs11_login_slot(p, &p->slotinfo[i], |
1634 | 0 | CKU_USER) < 0) { |
1635 | 0 | error("login failed"); |
1636 | 0 | continue; |
1637 | 0 | } |
1638 | 0 | pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys); |
1639 | 0 | pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys); |
1640 | 0 | } |
1641 | 0 | } |
1642 | | |
1643 | | /* now owned by caller */ |
1644 | 0 | *providerp = p; |
1645 | |
|
1646 | 0 | TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); |
1647 | 0 | p->refcount++; /* add to provider list */ |
1648 | |
|
1649 | 0 | return (nkeys); |
1650 | 0 | fail: |
1651 | 0 | if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) |
1652 | 0 | error("C_Finalize for provider %s failed: %lu", |
1653 | 0 | provider_id, rv); |
1654 | 0 | if (p) { |
1655 | 0 | free(p->name); |
1656 | 0 | free(p->slotlist); |
1657 | 0 | free(p->slotinfo); |
1658 | 0 | free(p); |
1659 | 0 | } |
1660 | 0 | if (handle) |
1661 | 0 | dlclose(handle); |
1662 | 0 | if (ret > 0) |
1663 | 0 | ret = -1; |
1664 | 0 | return (ret); |
1665 | 0 | } |
1666 | | |
1667 | | /* |
1668 | | * register a new provider and get number of keys hold by the token, |
1669 | | * fails if provider already exists |
1670 | | */ |
1671 | | int |
1672 | | pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp, |
1673 | | char ***labelsp) |
1674 | 0 | { |
1675 | 0 | struct pkcs11_provider *p = NULL; |
1676 | 0 | int nkeys; |
1677 | |
|
1678 | 0 | nkeys = pkcs11_register_provider(provider_id, pin, keyp, labelsp, |
1679 | 0 | &p, CKU_USER); |
1680 | | |
1681 | | /* no keys found or some other error, de-register provider */ |
1682 | 0 | if (nkeys <= 0 && p != NULL) { |
1683 | 0 | TAILQ_REMOVE(&pkcs11_providers, p, next); |
1684 | 0 | pkcs11_provider_finalize(p); |
1685 | 0 | pkcs11_provider_unref(p); |
1686 | 0 | } |
1687 | 0 | if (nkeys == 0) |
1688 | 0 | debug_f("provider %s returned no keys", provider_id); |
1689 | |
|
1690 | 0 | return (nkeys); |
1691 | 0 | } |
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) |
1709 | | debug_f("provider \"%s\" available", provider_id); |
1710 | | else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, NULL, |
1711 | | &p, CKU_SO)) < 0) { |
1712 | | debug_f("could not register provider %s", provider_id); |
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) { |
1723 | | debug_f("could not supply SO pin: %lu", rv); |
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) { |
1732 | | debug_f("failed to generate RSA key"); |
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) { |
1739 | | debug_f("failed to generate ECDSA key"); |
1740 | | goto out; |
1741 | | } |
1742 | | break; |
1743 | | default: |
1744 | | *err = SSH_PKCS11_ERR_GENERIC; |
1745 | | debug_f("unknown type %d", type); |
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) { |
1780 | | debug_f("using provider \"%s\"", provider_id); |
1781 | | } else if (pkcs11_register_provider(provider_id, pin, NULL, NULL, &p, |
1782 | | CKU_SO) < 0) { |
1783 | | debug_f("could not register provider %s", |
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) { |
1795 | | debug_f("could not supply SO pin: %lu", rv); |
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) { |
1809 | | debug_f("could not destroy private key 0x%hhx", |
1810 | | keyid); |
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) { |
1831 | | debug_f("could not get key type of public key 0x%hhx", |
1832 | | keyid); |
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) { |
1842 | | debug_f("could not destroy public key 0x%hhx", keyid); |
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 */ |
1859 | | #else /* ENABLE_PKCS11 */ |
1860 | | |
1861 | | #include <sys/types.h> |
1862 | | #include <stdarg.h> |
1863 | | #include <stdio.h> |
1864 | | |
1865 | | #include "log.h" |
1866 | | #include "sshkey.h" |
1867 | | |
1868 | | int |
1869 | | pkcs11_init(int interactive) |
1870 | | { |
1871 | | error("%s: dlopen() not supported", __func__); |
1872 | | return (-1); |
1873 | | } |
1874 | | |
1875 | | int |
1876 | | pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp, |
1877 | | char ***labelsp) |
1878 | | { |
1879 | | error("%s: dlopen() not supported", __func__); |
1880 | | return (-1); |
1881 | | } |
1882 | | |
1883 | | void |
1884 | | pkcs11_terminate(void) |
1885 | | { |
1886 | | error("%s: dlopen() not supported", __func__); |
1887 | | } |
1888 | | #endif /* ENABLE_PKCS11 */ |