1 /* $OpenBSD: ssh-pkcs11.c,v 1.4 2010/02/24 06:12:53 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 #define CRYPTOKI_COMPAT
40 #include "ssh-pkcs11.h"
43 struct pkcs11_slotinfo
{
45 CK_SESSION_HANDLE session
;
49 struct pkcs11_provider
{
52 CK_FUNCTION_LIST
*function_list
;
56 struct pkcs11_slotinfo
*slotinfo
;
59 TAILQ_ENTRY(pkcs11_provider
) next
;
62 TAILQ_HEAD(, pkcs11_provider
) pkcs11_providers
;
65 struct pkcs11_provider
*provider
;
67 int (*orig_finish
)(RSA
*rsa
);
68 RSA_METHOD rsa_method
;
73 int pkcs11_interactive
= 0;
76 pkcs11_init(int interactive
)
78 pkcs11_interactive
= interactive
;
79 TAILQ_INIT(&pkcs11_providers
);
84 * finalize a provider shared libarary, it's no longer usable.
85 * however, there might still be keys referencing this provider,
86 * so the actuall freeing of memory is handled by pkcs11_provider_unref().
87 * this is called when a provider gets unregistered.
90 pkcs11_provider_finalize(struct pkcs11_provider
*p
)
95 debug("pkcs11_provider_finalize: %p refcount %d valid %d",
96 p
, p
->refcount
, p
->valid
);
99 for (i
= 0; i
< p
->nslots
; i
++) {
100 if (p
->slotinfo
[i
].session
&&
101 (rv
= p
->function_list
->C_CloseSession(
102 p
->slotinfo
[i
].session
)) != CKR_OK
)
103 error("C_CloseSession failed: %lu", rv
);
105 if ((rv
= p
->function_list
->C_Finalize(NULL
)) != CKR_OK
)
106 error("C_Finalize failed: %lu", rv
);
108 p
->function_list
= NULL
;
113 * remove a reference to the provider.
114 * called when a key gets destroyed or when the provider is unregistered.
117 pkcs11_provider_unref(struct pkcs11_provider
*p
)
119 debug("pkcs11_provider_unref: %p refcount %d", p
, p
->refcount
);
120 if (--p
->refcount
<= 0) {
122 error("pkcs11_provider_unref: %p still valid", p
);
129 /* unregister all providers, keys might still point to the providers */
131 pkcs11_terminate(void)
133 struct pkcs11_provider
*p
;
135 while ((p
= TAILQ_FIRST(&pkcs11_providers
)) != NULL
) {
136 TAILQ_REMOVE(&pkcs11_providers
, p
, next
);
137 pkcs11_provider_finalize(p
);
138 pkcs11_provider_unref(p
);
142 /* lookup provider by name */
143 static struct pkcs11_provider
*
144 pkcs11_provider_lookup(char *provider_id
)
146 struct pkcs11_provider
*p
;
148 TAILQ_FOREACH(p
, &pkcs11_providers
, next
) {
149 debug("check %p %s", p
, p
->name
);
150 if (!strcmp(provider_id
, p
->name
))
156 /* unregister provider by name */
158 pkcs11_del_provider(char *provider_id
)
160 struct pkcs11_provider
*p
;
162 if ((p
= pkcs11_provider_lookup(provider_id
)) != NULL
) {
163 TAILQ_REMOVE(&pkcs11_providers
, p
, next
);
164 pkcs11_provider_finalize(p
);
165 pkcs11_provider_unref(p
);
171 /* openssl callback for freeing an RSA key */
173 pkcs11_rsa_finish(RSA
*rsa
)
175 struct pkcs11_key
*k11
;
178 if ((k11
= RSA_get_app_data(rsa
)) != NULL
) {
179 if (k11
->orig_finish
)
180 rv
= k11
->orig_finish(rsa
);
182 pkcs11_provider_unref(k11
->provider
);
190 /* openssl callback doing the actual signing operation */
192 pkcs11_rsa_private_encrypt(int flen
, const u_char
*from
, u_char
*to
, RSA
*rsa
,
195 struct pkcs11_key
*k11
;
196 struct pkcs11_slotinfo
*si
;
198 CK_OBJECT_HANDLE obj
;
199 CK_ULONG tlen
= 0, nfound
= 0;
201 CK_OBJECT_CLASS private_key_class
= CKO_PRIVATE_KEY
;
202 CK_BBOOL true_val
= CK_TRUE
;
203 CK_MECHANISM mech
= {
204 CKM_RSA_PKCS
, NULL_PTR
, 0
206 CK_ATTRIBUTE key_filter
[] = {
207 {CKA_CLASS
, NULL
, sizeof(private_key_class
) },
209 {CKA_SIGN
, NULL
, sizeof(true_val
) }
211 char *pin
, prompt
[1024];
214 /* some compilers complain about non-constant initializer so we
215 use NULL in CK_ATTRIBUTE above and set the values here */
216 key_filter
[0].pValue
= &private_key_class
;
217 key_filter
[2].pValue
= &true_val
;
219 if ((k11
= RSA_get_app_data(rsa
)) == NULL
) {
220 error("RSA_get_app_data failed for rsa %p", rsa
);
223 if (!k11
->provider
|| !k11
->provider
->valid
) {
224 error("no pkcs11 (valid) provider for rsa %p", rsa
);
227 f
= k11
->provider
->function_list
;
228 si
= &k11
->provider
->slotinfo
[k11
->slotidx
];
229 if ((si
->token
.flags
& CKF_LOGIN_REQUIRED
) && !si
->logged_in
) {
230 if (!pkcs11_interactive
) {
234 snprintf(prompt
, sizeof(prompt
), "Enter PIN for '%s': ",
236 pin
= read_passphrase(prompt
, RP_ALLOW_EOF
);
238 return (-1); /* bail out */
239 if ((rv
= f
->C_Login(si
->session
, CKU_USER
, pin
, strlen(pin
)))
242 error("C_Login failed: %lu", rv
);
248 key_filter
[1].pValue
= k11
->keyid
;
249 key_filter
[1].ulValueLen
= k11
->keyid_len
;
250 if ((rv
= f
->C_FindObjectsInit(si
->session
, key_filter
, 3)) != CKR_OK
) {
251 error("C_FindObjectsInit failed: %lu", rv
);
254 if ((rv
= f
->C_FindObjects(si
->session
, &obj
, 1, &nfound
)) != CKR_OK
||
256 error("C_FindObjects failed (%lu nfound): %lu", nfound
, rv
);
257 } else if ((rv
= f
->C_SignInit(si
->session
, &mech
, obj
)) != CKR_OK
) {
258 error("C_SignInit failed: %lu", rv
);
260 /* XXX handle CKR_BUFFER_TOO_SMALL */
261 tlen
= RSA_size(rsa
);
262 rv
= f
->C_Sign(si
->session
, (CK_BYTE
*)from
, flen
, to
, &tlen
);
266 error("C_Sign failed: %lu", rv
);
268 if ((rv
= f
->C_FindObjectsFinal(si
->session
)) != CKR_OK
)
269 error("C_FindObjectsFinal failed: %lu", rv
);
274 pkcs11_rsa_private_decrypt(int flen
, const u_char
*from
, u_char
*to
, RSA
*rsa
,
280 /* redirect private key operations for rsa key to pkcs11 token */
282 pkcs11_rsa_wrap(struct pkcs11_provider
*provider
, CK_ULONG slotidx
,
283 CK_ATTRIBUTE
*keyid_attrib
, RSA
*rsa
)
285 struct pkcs11_key
*k11
;
286 const RSA_METHOD
*def
= RSA_get_default_method();
288 k11
= xcalloc(1, sizeof(*k11
));
289 k11
->provider
= provider
;
290 provider
->refcount
++; /* provider referenced by RSA key */
291 k11
->slotidx
= slotidx
;
292 /* identify key object on smartcard */
293 k11
->keyid_len
= keyid_attrib
->ulValueLen
;
294 k11
->keyid
= xmalloc(k11
->keyid_len
);
295 memcpy(k11
->keyid
, keyid_attrib
->pValue
, k11
->keyid_len
);
296 k11
->orig_finish
= def
->finish
;
297 memcpy(&k11
->rsa_method
, def
, sizeof(k11
->rsa_method
));
298 k11
->rsa_method
.name
= "pkcs11";
299 k11
->rsa_method
.rsa_priv_enc
= pkcs11_rsa_private_encrypt
;
300 k11
->rsa_method
.rsa_priv_dec
= pkcs11_rsa_private_decrypt
;
301 k11
->rsa_method
.finish
= pkcs11_rsa_finish
;
302 RSA_set_method(rsa
, &k11
->rsa_method
);
303 RSA_set_app_data(rsa
, k11
);
307 /* remove trailing spaces */
309 rmspace(char *buf
, size_t len
)
315 for (i
= len
- 1; i
> 0; i
--)
316 if (i
== len
- 1 || buf
[i
] == ' ')
323 * open a pkcs11 session and login if required.
324 * if pin == NULL we delay login until key use
327 pkcs11_open_session(struct pkcs11_provider
*p
, CK_ULONG slotidx
, char *pin
)
331 CK_SESSION_HANDLE session
;
334 f
= p
->function_list
;
335 login_required
= p
->slotinfo
[slotidx
].token
.flags
& CKF_LOGIN_REQUIRED
;
336 if (pin
&& login_required
&& !strlen(pin
)) {
337 error("pin required");
340 if ((rv
= f
->C_OpenSession(p
->slotlist
[slotidx
], CKF_RW_SESSION
|
341 CKF_SERIAL_SESSION
, NULL
, NULL
, &session
))
343 error("C_OpenSession failed: %lu", rv
);
346 if (login_required
&& pin
) {
347 if ((rv
= f
->C_Login(session
, CKU_USER
, pin
, strlen(pin
)))
349 error("C_Login failed: %lu", rv
);
350 if ((rv
= f
->C_CloseSession(session
)) != CKR_OK
)
351 error("C_CloseSession failed: %lu", rv
);
354 p
->slotinfo
[slotidx
].logged_in
= 1;
356 p
->slotinfo
[slotidx
].session
= session
;
361 * lookup public keys for token in slot identified by slotidx,
362 * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
363 * keysp points to an (possibly empty) array with *nkeys keys.
366 pkcs11_fetch_keys(struct pkcs11_provider
*p
, CK_ULONG slotidx
, Key
***keysp
,
373 CK_OBJECT_HANDLE obj
;
375 CK_SESSION_HANDLE session
;
377 CK_OBJECT_CLASS pubkey_class
= CKO_PUBLIC_KEY
;
378 CK_ATTRIBUTE pubkey_filter
[] = {
379 { CKA_CLASS
, NULL
, sizeof(pubkey_class
) }
381 CK_ATTRIBUTE attribs
[] = {
383 { CKA_MODULUS
, NULL
, 0 },
384 { CKA_PUBLIC_EXPONENT
, NULL
, 0 }
387 /* some compilers complain about non-constant initializer so we
388 use NULL in CK_ATTRIBUTE above and set the value here */
389 pubkey_filter
[0].pValue
= &pubkey_class
;
391 f
= p
->function_list
;
392 session
= p
->slotinfo
[slotidx
].session
;
393 /* setup a filter the looks for public keys */
394 if ((rv
= f
->C_FindObjectsInit(session
, pubkey_filter
, 1)) != CKR_OK
) {
395 error("C_FindObjectsInit failed: %lu", rv
);
399 /* XXX 3 attributes in attribs[] */
400 for (i
= 0; i
< 3; i
++) {
401 attribs
[i
].pValue
= NULL
;
402 attribs
[i
].ulValueLen
= 0;
404 if ((rv
= f
->C_FindObjects(session
, &obj
, 1, &nfound
)) != CKR_OK
407 /* found a key, so figure out size of the attributes */
408 if ((rv
= f
->C_GetAttributeValue(session
, obj
, attribs
, 3))
410 error("C_GetAttributeValue failed: %lu", rv
);
413 /* allocate buffers for attributes, XXX check ulValueLen? */
414 for (i
= 0; i
< 3; i
++)
415 attribs
[i
].pValue
= xmalloc(attribs
[i
].ulValueLen
);
416 /* retrieve ID, modulus and public exponent of RSA key */
417 if ((rv
= f
->C_GetAttributeValue(session
, obj
, attribs
, 3))
419 error("C_GetAttributeValue failed: %lu", rv
);
420 } else if ((rsa
= RSA_new()) == NULL
) {
421 error("RSA_new failed");
423 rsa
->n
= BN_bin2bn(attribs
[1].pValue
,
424 attribs
[1].ulValueLen
, NULL
);
425 rsa
->e
= BN_bin2bn(attribs
[2].pValue
,
426 attribs
[2].ulValueLen
, NULL
);
427 if (rsa
->n
&& rsa
->e
&&
428 pkcs11_rsa_wrap(p
, slotidx
, &attribs
[0], rsa
) == 0) {
429 key
= key_new(KEY_UNSPEC
);
432 key
->flags
|= KEY_FLAG_EXT
;
433 /* expand key array and add key */
434 *keysp
= xrealloc(*keysp
, *nkeys
+ 1,
436 (*keysp
)[*nkeys
] = key
;
438 debug("have %d keys", *nkeys
);
443 for (i
= 0; i
< 3; i
++)
444 xfree(attribs
[i
].pValue
);
446 if ((rv
= f
->C_FindObjectsFinal(session
)) != CKR_OK
)
447 error("C_FindObjectsFinal failed: %lu", rv
);
451 /* register a new provider, fails if provider already exists */
453 pkcs11_add_provider(char *provider_id
, char *pin
, Key
***keyp
)
455 int nkeys
, need_finalize
= 0;
456 struct pkcs11_provider
*p
= NULL
;
458 CK_RV (*getfunctionlist
)(CK_FUNCTION_LIST
**);
460 CK_FUNCTION_LIST
*f
= NULL
;
461 CK_TOKEN_INFO
*token
;
465 if (pkcs11_provider_lookup(provider_id
) != NULL
) {
466 error("provider already registered: %s", provider_id
);
469 /* open shared pkcs11-libarary */
470 if ((handle
= dlopen(provider_id
, RTLD_NOW
)) == NULL
) {
471 error("dlopen %s failed: %s", provider_id
, dlerror());
474 if ((getfunctionlist
= dlsym(handle
, "C_GetFunctionList")) == NULL
) {
475 error("dlsym(C_GetFunctionList) failed: %s", dlerror());
478 p
= xcalloc(1, sizeof(*p
));
479 p
->name
= xstrdup(provider_id
);
481 /* setup the pkcs11 callbacks */
482 if ((rv
= (*getfunctionlist
)(&f
)) != CKR_OK
) {
483 error("C_GetFunctionList failed: %lu", rv
);
486 p
->function_list
= f
;
487 if ((rv
= f
->C_Initialize(NULL
)) != CKR_OK
) {
488 error("C_Initialize failed: %lu", rv
);
492 if ((rv
= f
->C_GetInfo(&p
->info
)) != CKR_OK
) {
493 error("C_GetInfo failed: %lu", rv
);
496 rmspace(p
->info
.manufacturerID
, sizeof(p
->info
.manufacturerID
));
497 rmspace(p
->info
.libraryDescription
, sizeof(p
->info
.libraryDescription
));
498 debug("manufacturerID <%s> cryptokiVersion %d.%d"
499 " libraryDescription <%s> libraryVersion %d.%d",
500 p
->info
.manufacturerID
,
501 p
->info
.cryptokiVersion
.major
,
502 p
->info
.cryptokiVersion
.minor
,
503 p
->info
.libraryDescription
,
504 p
->info
.libraryVersion
.major
,
505 p
->info
.libraryVersion
.minor
);
506 if ((rv
= f
->C_GetSlotList(CK_TRUE
, NULL
, &p
->nslots
)) != CKR_OK
) {
507 error("C_GetSlotList failed: %lu", rv
);
510 if (p
->nslots
== 0) {
514 p
->slotlist
= xcalloc(p
->nslots
, sizeof(CK_SLOT_ID
));
515 if ((rv
= f
->C_GetSlotList(CK_TRUE
, p
->slotlist
, &p
->nslots
))
517 error("C_GetSlotList failed: %lu", rv
);
520 p
->slotinfo
= xcalloc(p
->nslots
, sizeof(struct pkcs11_slotinfo
));
523 for (i
= 0; i
< p
->nslots
; i
++) {
524 token
= &p
->slotinfo
[i
].token
;
525 if ((rv
= f
->C_GetTokenInfo(p
->slotlist
[i
], token
))
527 error("C_GetTokenInfo failed: %lu", rv
);
530 rmspace(token
->label
, sizeof(token
->label
));
531 rmspace(token
->manufacturerID
, sizeof(token
->manufacturerID
));
532 rmspace(token
->model
, sizeof(token
->model
));
533 rmspace(token
->serialNumber
, sizeof(token
->serialNumber
));
534 debug("label <%s> manufacturerID <%s> model <%s> serial <%s>"
536 token
->label
, token
->manufacturerID
, token
->model
,
537 token
->serialNumber
, token
->flags
);
538 /* open session, login with pin and retrieve public keys */
539 if (pkcs11_open_session(p
, i
, pin
) == 0)
540 pkcs11_fetch_keys(p
, i
, keyp
, &nkeys
);
543 TAILQ_INSERT_TAIL(&pkcs11_providers
, p
, next
);
544 p
->refcount
++; /* add to provider list */
548 /* don't add the provider, since it does not have any keys */
550 if (need_finalize
&& (rv
= f
->C_Finalize(NULL
)) != CKR_OK
)
551 error("C_Finalize failed: %lu", rv
);
564 #endif /* ENABLE_PKCS11 */