1 /* $OpenBSD: ssh-pkcs11.c,v 1.22 2016/02/12 00:20:30 djm Exp $ */
3 * Copyright (c) 2010 Markus Friedl. All rights reserved.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 #include <sys/types.h>
23 #ifdef HAVE_SYS_TIME_H
24 # include <sys/time.h>
32 #include "openbsd-compat/sys-queue.h"
34 #include <openssl/x509.h>
36 #define CRYPTOKI_COMPAT
42 #include "ssh-pkcs11.h"
45 struct pkcs11_slotinfo
{
47 CK_SESSION_HANDLE session
;
51 struct pkcs11_provider
{
54 CK_FUNCTION_LIST
*function_list
;
58 struct pkcs11_slotinfo
*slotinfo
;
61 TAILQ_ENTRY(pkcs11_provider
) next
;
64 TAILQ_HEAD(, pkcs11_provider
) pkcs11_providers
;
67 struct pkcs11_provider
*provider
;
69 int (*orig_finish
)(RSA
*rsa
);
70 RSA_METHOD rsa_method
;
75 int pkcs11_interactive
= 0;
78 pkcs11_init(int interactive
)
80 pkcs11_interactive
= interactive
;
81 TAILQ_INIT(&pkcs11_providers
);
86 * finalize a provider shared libarary, it's no longer usable.
87 * however, there might still be keys referencing this provider,
88 * so the actuall freeing of memory is handled by pkcs11_provider_unref().
89 * this is called when a provider gets unregistered.
92 pkcs11_provider_finalize(struct pkcs11_provider
*p
)
97 debug("pkcs11_provider_finalize: %p refcount %d valid %d",
98 p
, p
->refcount
, p
->valid
);
101 for (i
= 0; i
< p
->nslots
; i
++) {
102 if (p
->slotinfo
[i
].session
&&
103 (rv
= p
->function_list
->C_CloseSession(
104 p
->slotinfo
[i
].session
)) != CKR_OK
)
105 error("C_CloseSession failed: %lu", rv
);
107 if ((rv
= p
->function_list
->C_Finalize(NULL
)) != CKR_OK
)
108 error("C_Finalize failed: %lu", rv
);
110 p
->function_list
= NULL
;
115 * remove a reference to the provider.
116 * called when a key gets destroyed or when the provider is unregistered.
119 pkcs11_provider_unref(struct pkcs11_provider
*p
)
121 debug("pkcs11_provider_unref: %p refcount %d", p
, p
->refcount
);
122 if (--p
->refcount
<= 0) {
124 error("pkcs11_provider_unref: %p still valid", p
);
131 /* unregister all providers, keys might still point to the providers */
133 pkcs11_terminate(void)
135 struct pkcs11_provider
*p
;
137 while ((p
= TAILQ_FIRST(&pkcs11_providers
)) != NULL
) {
138 TAILQ_REMOVE(&pkcs11_providers
, p
, next
);
139 pkcs11_provider_finalize(p
);
140 pkcs11_provider_unref(p
);
144 /* lookup provider by name */
145 static struct pkcs11_provider
*
146 pkcs11_provider_lookup(char *provider_id
)
148 struct pkcs11_provider
*p
;
150 TAILQ_FOREACH(p
, &pkcs11_providers
, next
) {
151 debug("check %p %s", p
, p
->name
);
152 if (!strcmp(provider_id
, p
->name
))
158 /* unregister provider by name */
160 pkcs11_del_provider(char *provider_id
)
162 struct pkcs11_provider
*p
;
164 if ((p
= pkcs11_provider_lookup(provider_id
)) != NULL
) {
165 TAILQ_REMOVE(&pkcs11_providers
, p
, next
);
166 pkcs11_provider_finalize(p
);
167 pkcs11_provider_unref(p
);
173 /* openssl callback for freeing an RSA key */
175 pkcs11_rsa_finish(RSA
*rsa
)
177 struct pkcs11_key
*k11
;
180 if ((k11
= RSA_get_app_data(rsa
)) != NULL
) {
181 if (k11
->orig_finish
)
182 rv
= k11
->orig_finish(rsa
);
184 pkcs11_provider_unref(k11
->provider
);
191 /* find a single 'obj' for given attributes */
193 pkcs11_find(struct pkcs11_provider
*p
, CK_ULONG slotidx
, CK_ATTRIBUTE
*attr
,
194 CK_ULONG nattr
, CK_OBJECT_HANDLE
*obj
)
197 CK_SESSION_HANDLE session
;
202 f
= p
->function_list
;
203 session
= p
->slotinfo
[slotidx
].session
;
204 if ((rv
= f
->C_FindObjectsInit(session
, attr
, nattr
)) != CKR_OK
) {
205 error("C_FindObjectsInit failed (nattr %lu): %lu", nattr
, rv
);
208 if ((rv
= f
->C_FindObjects(session
, obj
, 1, &nfound
)) != CKR_OK
||
210 debug("C_FindObjects failed (nfound %lu nattr %lu): %lu",
214 if ((rv
= f
->C_FindObjectsFinal(session
)) != CKR_OK
)
215 error("C_FindObjectsFinal failed: %lu", rv
);
219 /* openssl callback doing the actual signing operation */
221 pkcs11_rsa_private_encrypt(int flen
, const u_char
*from
, u_char
*to
, RSA
*rsa
,
224 struct pkcs11_key
*k11
;
225 struct pkcs11_slotinfo
*si
;
227 CK_OBJECT_HANDLE obj
;
230 CK_OBJECT_CLASS private_key_class
= CKO_PRIVATE_KEY
;
231 CK_BBOOL true_val
= CK_TRUE
;
232 CK_MECHANISM mech
= {
233 CKM_RSA_PKCS
, NULL_PTR
, 0
235 CK_ATTRIBUTE key_filter
[] = {
236 {CKA_CLASS
, NULL
, sizeof(private_key_class
) },
238 {CKA_SIGN
, NULL
, sizeof(true_val
) }
240 char *pin
= NULL
, prompt
[1024];
243 key_filter
[0].pValue
= &private_key_class
;
244 key_filter
[2].pValue
= &true_val
;
246 if ((k11
= RSA_get_app_data(rsa
)) == NULL
) {
247 error("RSA_get_app_data failed for rsa %p", rsa
);
250 if (!k11
->provider
|| !k11
->provider
->valid
) {
251 error("no pkcs11 (valid) provider for rsa %p", rsa
);
254 f
= k11
->provider
->function_list
;
255 si
= &k11
->provider
->slotinfo
[k11
->slotidx
];
256 if ((si
->token
.flags
& CKF_LOGIN_REQUIRED
) && !si
->logged_in
) {
257 if (!pkcs11_interactive
) {
258 error("need pin entry%s", (si
->token
.flags
&
259 CKF_PROTECTED_AUTHENTICATION_PATH
) ?
260 " on reader keypad" : "");
263 if (si
->token
.flags
& CKF_PROTECTED_AUTHENTICATION_PATH
)
264 verbose("Deferring PIN entry to reader keypad.");
266 snprintf(prompt
, sizeof(prompt
),
267 "Enter PIN for '%s': ", si
->token
.label
);
268 pin
= read_passphrase(prompt
, RP_ALLOW_EOF
);
270 return (-1); /* bail out */
272 rv
= f
->C_Login(si
->session
, CKU_USER
, (u_char
*)pin
,
273 (pin
!= NULL
) ? strlen(pin
) : 0);
275 explicit_bzero(pin
, strlen(pin
));
278 if (rv
!= CKR_OK
&& rv
!= CKR_USER_ALREADY_LOGGED_IN
) {
279 error("C_Login failed: %lu", rv
);
284 key_filter
[1].pValue
= k11
->keyid
;
285 key_filter
[1].ulValueLen
= k11
->keyid_len
;
286 /* try to find object w/CKA_SIGN first, retry w/o */
287 if (pkcs11_find(k11
->provider
, k11
->slotidx
, key_filter
, 3, &obj
) < 0 &&
288 pkcs11_find(k11
->provider
, k11
->slotidx
, key_filter
, 2, &obj
) < 0) {
289 error("cannot find private key");
290 } else if ((rv
= f
->C_SignInit(si
->session
, &mech
, obj
)) != CKR_OK
) {
291 error("C_SignInit failed: %lu", rv
);
293 /* XXX handle CKR_BUFFER_TOO_SMALL */
294 tlen
= RSA_size(rsa
);
295 rv
= f
->C_Sign(si
->session
, (CK_BYTE
*)from
, flen
, to
, &tlen
);
299 error("C_Sign failed: %lu", rv
);
305 pkcs11_rsa_private_decrypt(int flen
, const u_char
*from
, u_char
*to
, RSA
*rsa
,
311 /* redirect private key operations for rsa key to pkcs11 token */
313 pkcs11_rsa_wrap(struct pkcs11_provider
*provider
, CK_ULONG slotidx
,
314 CK_ATTRIBUTE
*keyid_attrib
, RSA
*rsa
)
316 struct pkcs11_key
*k11
;
317 const RSA_METHOD
*def
= RSA_get_default_method();
319 k11
= xcalloc(1, sizeof(*k11
));
320 k11
->provider
= provider
;
321 provider
->refcount
++; /* provider referenced by RSA key */
322 k11
->slotidx
= slotidx
;
323 /* identify key object on smartcard */
324 k11
->keyid_len
= keyid_attrib
->ulValueLen
;
325 if (k11
->keyid_len
> 0) {
326 k11
->keyid
= xmalloc(k11
->keyid_len
);
327 memcpy(k11
->keyid
, keyid_attrib
->pValue
, k11
->keyid_len
);
329 k11
->orig_finish
= def
->finish
;
330 memcpy(&k11
->rsa_method
, def
, sizeof(k11
->rsa_method
));
331 k11
->rsa_method
.name
= "pkcs11";
332 k11
->rsa_method
.rsa_priv_enc
= pkcs11_rsa_private_encrypt
;
333 k11
->rsa_method
.rsa_priv_dec
= pkcs11_rsa_private_decrypt
;
334 k11
->rsa_method
.finish
= pkcs11_rsa_finish
;
335 RSA_set_method(rsa
, &k11
->rsa_method
);
336 RSA_set_app_data(rsa
, k11
);
340 /* remove trailing spaces */
342 rmspace(u_char
*buf
, size_t len
)
348 for (i
= len
- 1; i
> 0; i
--)
349 if (i
== len
- 1 || buf
[i
] == ' ')
356 * open a pkcs11 session and login if required.
357 * if pin == NULL we delay login until key use
360 pkcs11_open_session(struct pkcs11_provider
*p
, CK_ULONG slotidx
, char *pin
)
364 CK_SESSION_HANDLE session
;
367 f
= p
->function_list
;
368 login_required
= p
->slotinfo
[slotidx
].token
.flags
& CKF_LOGIN_REQUIRED
;
369 if (pin
&& login_required
&& !strlen(pin
)) {
370 error("pin required");
373 if ((rv
= f
->C_OpenSession(p
->slotlist
[slotidx
], CKF_RW_SESSION
|
374 CKF_SERIAL_SESSION
, NULL
, NULL
, &session
))
376 error("C_OpenSession failed: %lu", rv
);
379 if (login_required
&& pin
) {
380 rv
= f
->C_Login(session
, CKU_USER
,
381 (u_char
*)pin
, strlen(pin
));
382 if (rv
!= CKR_OK
&& rv
!= CKR_USER_ALREADY_LOGGED_IN
) {
383 error("C_Login failed: %lu", rv
);
384 if ((rv
= f
->C_CloseSession(session
)) != CKR_OK
)
385 error("C_CloseSession failed: %lu", rv
);
388 p
->slotinfo
[slotidx
].logged_in
= 1;
390 p
->slotinfo
[slotidx
].session
= session
;
395 * lookup public keys for token in slot identified by slotidx,
396 * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
397 * keysp points to an (possibly empty) array with *nkeys keys.
399 static int pkcs11_fetch_keys_filter(struct pkcs11_provider
*, CK_ULONG
,
400 CK_ATTRIBUTE
[], CK_ATTRIBUTE
[3], struct sshkey
***, int *)
401 __attribute__((__bounded__(__minbytes__
,4, 3 * sizeof(CK_ATTRIBUTE
))));
404 pkcs11_fetch_keys(struct pkcs11_provider
*p
, CK_ULONG slotidx
,
405 struct sshkey
***keysp
, int *nkeys
)
407 CK_OBJECT_CLASS pubkey_class
= CKO_PUBLIC_KEY
;
408 CK_OBJECT_CLASS cert_class
= CKO_CERTIFICATE
;
409 CK_ATTRIBUTE pubkey_filter
[] = {
410 { CKA_CLASS
, NULL
, sizeof(pubkey_class
) }
412 CK_ATTRIBUTE cert_filter
[] = {
413 { CKA_CLASS
, NULL
, sizeof(cert_class
) }
415 CK_ATTRIBUTE pubkey_attribs
[] = {
417 { CKA_MODULUS
, NULL
, 0 },
418 { CKA_PUBLIC_EXPONENT
, NULL
, 0 }
420 CK_ATTRIBUTE cert_attribs
[] = {
422 { CKA_SUBJECT
, NULL
, 0 },
423 { CKA_VALUE
, NULL
, 0 }
425 pubkey_filter
[0].pValue
= &pubkey_class
;
426 cert_filter
[0].pValue
= &cert_class
;
428 if (pkcs11_fetch_keys_filter(p
, slotidx
, pubkey_filter
, pubkey_attribs
,
430 pkcs11_fetch_keys_filter(p
, slotidx
, cert_filter
, cert_attribs
,
437 pkcs11_key_included(struct sshkey
***keysp
, int *nkeys
, struct sshkey
*key
)
441 for (i
= 0; i
< *nkeys
; i
++)
442 if (sshkey_equal(key
, (*keysp
)[i
]))
448 pkcs11_fetch_keys_filter(struct pkcs11_provider
*p
, CK_ULONG slotidx
,
449 CK_ATTRIBUTE filter
[], CK_ATTRIBUTE attribs
[3],
450 struct sshkey
***keysp
, int *nkeys
)
459 CK_OBJECT_HANDLE obj
;
461 CK_SESSION_HANDLE session
;
464 f
= p
->function_list
;
465 session
= p
->slotinfo
[slotidx
].session
;
466 /* setup a filter the looks for public keys */
467 if ((rv
= f
->C_FindObjectsInit(session
, filter
, 1)) != CKR_OK
) {
468 error("C_FindObjectsInit failed: %lu", rv
);
472 /* XXX 3 attributes in attribs[] */
473 for (i
= 0; i
< 3; i
++) {
474 attribs
[i
].pValue
= NULL
;
475 attribs
[i
].ulValueLen
= 0;
477 if ((rv
= f
->C_FindObjects(session
, &obj
, 1, &nfound
)) != CKR_OK
480 /* found a key, so figure out size of the attributes */
481 if ((rv
= f
->C_GetAttributeValue(session
, obj
, attribs
, 3))
483 error("C_GetAttributeValue failed: %lu", rv
);
487 * Allow CKA_ID (always first attribute) to be empty, but
488 * ensure that none of the others are zero length.
489 * XXX assumes CKA_ID is always first.
491 if (attribs
[1].ulValueLen
== 0 ||
492 attribs
[2].ulValueLen
== 0) {
495 /* allocate buffers for attributes */
496 for (i
= 0; i
< 3; i
++) {
497 if (attribs
[i
].ulValueLen
> 0) {
498 attribs
[i
].pValue
= xmalloc(
499 attribs
[i
].ulValueLen
);
504 * retrieve ID, modulus and public exponent of RSA key,
505 * or ID, subject and value for certificates.
508 if ((rv
= f
->C_GetAttributeValue(session
, obj
, attribs
, 3))
510 error("C_GetAttributeValue failed: %lu", rv
);
511 } else if (attribs
[1].type
== CKA_MODULUS
) {
512 if ((rsa
= RSA_new()) == NULL
) {
513 error("RSA_new failed");
515 rsa
->n
= BN_bin2bn(attribs
[1].pValue
,
516 attribs
[1].ulValueLen
, NULL
);
517 rsa
->e
= BN_bin2bn(attribs
[2].pValue
,
518 attribs
[2].ulValueLen
, NULL
);
521 cp
= attribs
[2].pValue
;
522 if ((x509
= X509_new()) == NULL
) {
523 error("X509_new failed");
524 } else if (d2i_X509(&x509
, &cp
, attribs
[2].ulValueLen
)
526 error("d2i_X509 failed");
527 } else if ((evp
= X509_get_pubkey(x509
)) == NULL
||
528 evp
->type
!= EVP_PKEY_RSA
||
529 evp
->pkey
.rsa
== NULL
) {
530 debug("X509_get_pubkey failed or no rsa");
531 } else if ((rsa
= RSAPublicKey_dup(evp
->pkey
.rsa
))
533 error("RSAPublicKey_dup");
538 if (rsa
&& rsa
->n
&& rsa
->e
&&
539 pkcs11_rsa_wrap(p
, slotidx
, &attribs
[0], rsa
) == 0) {
540 key
= sshkey_new(KEY_UNSPEC
);
543 key
->flags
|= SSHKEY_FLAG_EXT
;
544 if (pkcs11_key_included(keysp
, nkeys
, key
)) {
547 /* expand key array and add key */
548 *keysp
= xreallocarray(*keysp
, *nkeys
+ 1,
549 sizeof(struct sshkey
*));
550 (*keysp
)[*nkeys
] = key
;
552 debug("have %d keys", *nkeys
);
557 for (i
= 0; i
< 3; i
++)
558 free(attribs
[i
].pValue
);
560 if ((rv
= f
->C_FindObjectsFinal(session
)) != CKR_OK
)
561 error("C_FindObjectsFinal failed: %lu", rv
);
565 /* register a new provider, fails if provider already exists */
567 pkcs11_add_provider(char *provider_id
, char *pin
, struct sshkey
***keyp
)
569 int nkeys
, need_finalize
= 0;
570 struct pkcs11_provider
*p
= NULL
;
572 CK_RV (*getfunctionlist
)(CK_FUNCTION_LIST
**);
574 CK_FUNCTION_LIST
*f
= NULL
;
575 CK_TOKEN_INFO
*token
;
579 if (pkcs11_provider_lookup(provider_id
) != NULL
) {
580 error("provider already registered: %s", provider_id
);
583 /* open shared pkcs11-libarary */
584 if ((handle
= dlopen(provider_id
, RTLD_NOW
)) == NULL
) {
585 error("dlopen %s failed: %s", provider_id
, dlerror());
588 if ((getfunctionlist
= dlsym(handle
, "C_GetFunctionList")) == NULL
) {
589 error("dlsym(C_GetFunctionList) failed: %s", dlerror());
592 p
= xcalloc(1, sizeof(*p
));
593 p
->name
= xstrdup(provider_id
);
595 /* setup the pkcs11 callbacks */
596 if ((rv
= (*getfunctionlist
)(&f
)) != CKR_OK
) {
597 error("C_GetFunctionList failed: %lu", rv
);
600 p
->function_list
= f
;
601 if ((rv
= f
->C_Initialize(NULL
)) != CKR_OK
) {
602 error("C_Initialize failed: %lu", rv
);
606 if ((rv
= f
->C_GetInfo(&p
->info
)) != CKR_OK
) {
607 error("C_GetInfo failed: %lu", rv
);
610 rmspace(p
->info
.manufacturerID
, sizeof(p
->info
.manufacturerID
));
611 rmspace(p
->info
.libraryDescription
, sizeof(p
->info
.libraryDescription
));
612 debug("manufacturerID <%s> cryptokiVersion %d.%d"
613 " libraryDescription <%s> libraryVersion %d.%d",
614 p
->info
.manufacturerID
,
615 p
->info
.cryptokiVersion
.major
,
616 p
->info
.cryptokiVersion
.minor
,
617 p
->info
.libraryDescription
,
618 p
->info
.libraryVersion
.major
,
619 p
->info
.libraryVersion
.minor
);
620 if ((rv
= f
->C_GetSlotList(CK_TRUE
, NULL
, &p
->nslots
)) != CKR_OK
) {
621 error("C_GetSlotList failed: %lu", rv
);
624 if (p
->nslots
== 0) {
628 p
->slotlist
= xcalloc(p
->nslots
, sizeof(CK_SLOT_ID
));
629 if ((rv
= f
->C_GetSlotList(CK_TRUE
, p
->slotlist
, &p
->nslots
))
631 error("C_GetSlotList failed: %lu", rv
);
634 p
->slotinfo
= xcalloc(p
->nslots
, sizeof(struct pkcs11_slotinfo
));
637 for (i
= 0; i
< p
->nslots
; i
++) {
638 token
= &p
->slotinfo
[i
].token
;
639 if ((rv
= f
->C_GetTokenInfo(p
->slotlist
[i
], token
))
641 error("C_GetTokenInfo failed: %lu", rv
);
644 if ((token
->flags
& CKF_TOKEN_INITIALIZED
) == 0) {
645 debug2("%s: ignoring uninitialised token in slot %lu",
646 __func__
, (unsigned long)i
);
649 rmspace(token
->label
, sizeof(token
->label
));
650 rmspace(token
->manufacturerID
, sizeof(token
->manufacturerID
));
651 rmspace(token
->model
, sizeof(token
->model
));
652 rmspace(token
->serialNumber
, sizeof(token
->serialNumber
));
653 debug("label <%s> manufacturerID <%s> model <%s> serial <%s>"
655 token
->label
, token
->manufacturerID
, token
->model
,
656 token
->serialNumber
, token
->flags
);
657 /* open session, login with pin and retrieve public keys */
658 if (pkcs11_open_session(p
, i
, pin
) == 0)
659 pkcs11_fetch_keys(p
, i
, keyp
, &nkeys
);
662 TAILQ_INSERT_TAIL(&pkcs11_providers
, p
, next
);
663 p
->refcount
++; /* add to provider list */
667 /* don't add the provider, since it does not have any keys */
669 if (need_finalize
&& (rv
= f
->C_Finalize(NULL
)) != CKR_OK
)
670 error("C_Finalize failed: %lu", rv
);
684 pkcs11_init(int interactive
)
690 pkcs11_terminate(void)
695 #endif /* ENABLE_PKCS11 */