1 /* $OpenBSD: ssh-pkcs11.c,v 1.6 2010/06/08 21:32:19 markus Exp $ */ 2 /* 3 * Copyright (c) 2010 Markus Friedl. All rights reserved. 4 * 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. 8 * 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. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/queue.h> 20 #include <stdarg.h> 21 #include <stdio.h> 22 23 #include <string.h> 24 #include <dlfcn.h> 25 26 #define CRYPTOKI_COMPAT 27 #include "pkcs11.h" 28 29 #include "log.h" 30 #include "misc.h" 31 #include "key.h" 32 #include "ssh-pkcs11.h" 33 #include "xmalloc.h" 34 35 struct pkcs11_slotinfo { 36 CK_TOKEN_INFO token; 37 CK_SESSION_HANDLE session; 38 int logged_in; 39 }; 40 41 struct pkcs11_provider { 42 char *name; 43 void *handle; 44 CK_FUNCTION_LIST *function_list; 45 CK_INFO info; 46 CK_ULONG nslots; 47 CK_SLOT_ID *slotlist; 48 struct pkcs11_slotinfo *slotinfo; 49 int valid; 50 int refcount; 51 TAILQ_ENTRY(pkcs11_provider) next; 52 }; 53 54 TAILQ_HEAD(, pkcs11_provider) pkcs11_providers; 55 56 struct pkcs11_key { 57 struct pkcs11_provider *provider; 58 CK_ULONG slotidx; 59 int (*orig_finish)(RSA *rsa); 60 RSA_METHOD rsa_method; 61 char *keyid; 62 int keyid_len; 63 }; 64 65 int pkcs11_interactive = 0; 66 67 int 68 pkcs11_init(int interactive) 69 { 70 pkcs11_interactive = interactive; 71 TAILQ_INIT(&pkcs11_providers); 72 return (0); 73 } 74 75 /* 76 * finalize a provider shared libarary, it's no longer usable. 77 * however, there might still be keys referencing this provider, 78 * so the actuall freeing of memory is handled by pkcs11_provider_unref(). 79 * this is called when a provider gets unregistered. 80 */ 81 static void 82 pkcs11_provider_finalize(struct pkcs11_provider *p) 83 { 84 CK_RV rv; 85 CK_ULONG i; 86 87 debug("pkcs11_provider_finalize: %p refcount %d valid %d", 88 p, p->refcount, p->valid); 89 if (!p->valid) 90 return; 91 for (i = 0; i < p->nslots; i++) { 92 if (p->slotinfo[i].session && 93 (rv = p->function_list->C_CloseSession( 94 p->slotinfo[i].session)) != CKR_OK) 95 error("C_CloseSession failed: %lu", rv); 96 } 97 if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) 98 error("C_Finalize failed: %lu", rv); 99 p->valid = 0; 100 p->function_list = NULL; 101 #ifdef HAVE_DLOPEN 102 dlclose(p->handle); 103 #endif 104 } 105 106 /* 107 * remove a reference to the provider. 108 * called when a key gets destroyed or when the provider is unregistered. 109 */ 110 static void 111 pkcs11_provider_unref(struct pkcs11_provider *p) 112 { 113 debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); 114 if (--p->refcount <= 0) { 115 if (p->valid) 116 error("pkcs11_provider_unref: %p still valid", p); 117 xfree(p->slotlist); 118 xfree(p->slotinfo); 119 xfree(p); 120 } 121 } 122 123 /* unregister all providers, keys might still point to the providers */ 124 void 125 pkcs11_terminate(void) 126 { 127 struct pkcs11_provider *p; 128 129 while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) { 130 TAILQ_REMOVE(&pkcs11_providers, p, next); 131 pkcs11_provider_finalize(p); 132 pkcs11_provider_unref(p); 133 } 134 } 135 136 /* lookup provider by name */ 137 static struct pkcs11_provider * 138 pkcs11_provider_lookup(char *provider_id) 139 { 140 struct pkcs11_provider *p; 141 142 TAILQ_FOREACH(p, &pkcs11_providers, next) { 143 debug("check %p %s", p, p->name); 144 if (!strcmp(provider_id, p->name)) 145 return (p); 146 } 147 return (NULL); 148 } 149 150 /* unregister provider by name */ 151 int 152 pkcs11_del_provider(char *provider_id) 153 { 154 struct pkcs11_provider *p; 155 156 if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { 157 TAILQ_REMOVE(&pkcs11_providers, p, next); 158 pkcs11_provider_finalize(p); 159 pkcs11_provider_unref(p); 160 return (0); 161 } 162 return (-1); 163 } 164 165 /* openssl callback for freeing an RSA key */ 166 static int 167 pkcs11_rsa_finish(RSA *rsa) 168 { 169 struct pkcs11_key *k11; 170 int rv = -1; 171 172 if ((k11 = RSA_get_app_data(rsa)) != NULL) { 173 if (k11->orig_finish) 174 rv = k11->orig_finish(rsa); 175 if (k11->provider) 176 pkcs11_provider_unref(k11->provider); 177 if (k11->keyid) 178 xfree(k11->keyid); 179 xfree(k11); 180 } 181 return (rv); 182 } 183 184 /* find a single 'obj' for given attributes */ 185 static int 186 pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, 187 CK_ULONG nattr, CK_OBJECT_HANDLE *obj) 188 { 189 CK_FUNCTION_LIST *f; 190 CK_SESSION_HANDLE session; 191 CK_ULONG nfound = 0; 192 CK_RV rv; 193 int ret = -1; 194 195 f = p->function_list; 196 session = p->slotinfo[slotidx].session; 197 if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { 198 error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); 199 return (-1); 200 } 201 if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK || 202 nfound != 1) { 203 debug("C_FindObjects failed (nfound %lu nattr %lu): %lu", 204 nfound, nattr, rv); 205 } else 206 ret = 0; 207 if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) 208 error("C_FindObjectsFinal failed: %lu", rv); 209 return (ret); 210 } 211 212 /* openssl callback doing the actual signing operation */ 213 static int 214 pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, 215 int padding) 216 { 217 struct pkcs11_key *k11; 218 struct pkcs11_slotinfo *si; 219 CK_FUNCTION_LIST *f; 220 CK_OBJECT_HANDLE obj; 221 CK_ULONG tlen = 0; 222 CK_RV rv; 223 CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; 224 CK_BBOOL true = CK_TRUE; 225 CK_MECHANISM mech = { 226 CKM_RSA_PKCS, NULL_PTR, 0 227 }; 228 CK_ATTRIBUTE key_filter[] = { 229 {CKA_CLASS, &private_key_class, sizeof(private_key_class) }, 230 {CKA_ID, NULL, 0}, 231 {CKA_SIGN, &true, sizeof(true) } 232 }; 233 char *pin, prompt[1024]; 234 int rval = -1; 235 236 if ((k11 = RSA_get_app_data(rsa)) == NULL) { 237 error("RSA_get_app_data failed for rsa %p", rsa); 238 return (-1); 239 } 240 if (!k11->provider || !k11->provider->valid) { 241 error("no pkcs11 (valid) provider for rsa %p", rsa); 242 return (-1); 243 } 244 f = k11->provider->function_list; 245 si = &k11->provider->slotinfo[k11->slotidx]; 246 if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { 247 if (!pkcs11_interactive) { 248 error("need pin"); 249 return (-1); 250 } 251 snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ", 252 si->token.label); 253 pin = read_passphrase(prompt, RP_ALLOW_EOF); 254 if (pin == NULL) 255 return (-1); /* bail out */ 256 if ((rv = f->C_Login(si->session, CKU_USER, pin, strlen(pin))) 257 != CKR_OK) { 258 xfree(pin); 259 error("C_Login failed: %lu", rv); 260 return (-1); 261 } 262 xfree(pin); 263 si->logged_in = 1; 264 } 265 key_filter[1].pValue = k11->keyid; 266 key_filter[1].ulValueLen = k11->keyid_len; 267 /* try to find object w/CKA_SIGN first, retry w/o */ 268 if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && 269 pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { 270 error("cannot find private key"); 271 } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { 272 error("C_SignInit failed: %lu", rv); 273 } else { 274 /* XXX handle CKR_BUFFER_TOO_SMALL */ 275 tlen = RSA_size(rsa); 276 rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); 277 if (rv == CKR_OK) 278 rval = tlen; 279 else 280 error("C_Sign failed: %lu", rv); 281 } 282 return (rval); 283 } 284 285 static int 286 pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, 287 int padding) 288 { 289 return (-1); 290 } 291 292 /* redirect private key operations for rsa key to pkcs11 token */ 293 static int 294 pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, 295 CK_ATTRIBUTE *keyid_attrib, RSA *rsa) 296 { 297 struct pkcs11_key *k11; 298 const RSA_METHOD *def = RSA_get_default_method(); 299 300 k11 = xcalloc(1, sizeof(*k11)); 301 k11->provider = provider; 302 provider->refcount++; /* provider referenced by RSA key */ 303 k11->slotidx = slotidx; 304 /* identify key object on smartcard */ 305 k11->keyid_len = keyid_attrib->ulValueLen; 306 k11->keyid = xmalloc(k11->keyid_len); 307 memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); 308 k11->orig_finish = def->finish; 309 memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); 310 k11->rsa_method.name = "pkcs11"; 311 k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt; 312 k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt; 313 k11->rsa_method.finish = pkcs11_rsa_finish; 314 RSA_set_method(rsa, &k11->rsa_method); 315 RSA_set_app_data(rsa, k11); 316 return (0); 317 } 318 319 /* remove trailing spaces */ 320 static void 321 rmspace(char *buf, size_t len) 322 { 323 size_t i; 324 325 if (!len) 326 return; 327 for (i = len - 1; i > 0; i--) 328 if (i == len - 1 || buf[i] == ' ') 329 buf[i] = '\0'; 330 else 331 break; 332 } 333 334 /* 335 * open a pkcs11 session and login if required. 336 * if pin == NULL we delay login until key use 337 */ 338 static int 339 pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) 340 { 341 CK_RV rv; 342 CK_FUNCTION_LIST *f; 343 CK_SESSION_HANDLE session; 344 int login_required; 345 346 f = p->function_list; 347 login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; 348 if (pin && login_required && !strlen(pin)) { 349 error("pin required"); 350 return (-1); 351 } 352 if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| 353 CKF_SERIAL_SESSION, NULL, NULL, &session)) 354 != CKR_OK) { 355 error("C_OpenSession failed: %lu", rv); 356 return (-1); 357 } 358 if (login_required && pin) { 359 if ((rv = f->C_Login(session, CKU_USER, pin, strlen(pin))) 360 != CKR_OK) { 361 error("C_Login failed: %lu", rv); 362 if ((rv = f->C_CloseSession(session)) != CKR_OK) 363 error("C_CloseSession failed: %lu", rv); 364 return (-1); 365 } 366 p->slotinfo[slotidx].logged_in = 1; 367 } 368 p->slotinfo[slotidx].session = session; 369 return (0); 370 } 371 372 /* 373 * lookup public keys for token in slot identified by slotidx, 374 * add 'wrapped' public keys to the 'keysp' array and increment nkeys. 375 * keysp points to an (possibly empty) array with *nkeys keys. 376 */ 377 static int 378 pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp, 379 int *nkeys) 380 { 381 Key *key; 382 RSA *rsa; 383 int i; 384 CK_RV rv; 385 CK_OBJECT_HANDLE obj; 386 CK_ULONG nfound; 387 CK_SESSION_HANDLE session; 388 CK_FUNCTION_LIST *f; 389 CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; 390 CK_ATTRIBUTE pubkey_filter[] = { 391 { CKA_CLASS, &pubkey_class, sizeof(pubkey_class) } 392 }; 393 CK_ATTRIBUTE attribs[] = { 394 { CKA_ID, NULL, 0 }, 395 { CKA_MODULUS, NULL, 0 }, 396 { CKA_PUBLIC_EXPONENT, NULL, 0 } 397 }; 398 399 f = p->function_list; 400 session = p->slotinfo[slotidx].session; 401 /* setup a filter the looks for public keys */ 402 if ((rv = f->C_FindObjectsInit(session, pubkey_filter, 1)) != CKR_OK) { 403 error("C_FindObjectsInit failed: %lu", rv); 404 return (-1); 405 } 406 while (1) { 407 /* XXX 3 attributes in attribs[] */ 408 for (i = 0; i < 3; i++) { 409 attribs[i].pValue = NULL; 410 attribs[i].ulValueLen = 0; 411 } 412 if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK 413 || nfound == 0) 414 break; 415 /* found a key, so figure out size of the attributes */ 416 if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) 417 != CKR_OK) { 418 error("C_GetAttributeValue failed: %lu", rv); 419 continue; 420 } 421 /* check that none of the attributes are zero length */ 422 if (attribs[0].ulValueLen == 0 || 423 attribs[1].ulValueLen == 0 || 424 attribs[2].ulValueLen == 0) { 425 continue; 426 } 427 /* allocate buffers for attributes */ 428 for (i = 0; i < 3; i++) 429 attribs[i].pValue = xmalloc(attribs[i].ulValueLen); 430 /* retrieve ID, modulus and public exponent of RSA key */ 431 if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) 432 != CKR_OK) { 433 error("C_GetAttributeValue failed: %lu", rv); 434 } else if ((rsa = RSA_new()) == NULL) { 435 error("RSA_new failed"); 436 } else { 437 rsa->n = BN_bin2bn(attribs[1].pValue, 438 attribs[1].ulValueLen, NULL); 439 rsa->e = BN_bin2bn(attribs[2].pValue, 440 attribs[2].ulValueLen, NULL); 441 if (rsa->n && rsa->e && 442 pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { 443 key = key_new(KEY_UNSPEC); 444 key->rsa = rsa; 445 key->type = KEY_RSA; 446 key->flags |= KEY_FLAG_EXT; 447 /* expand key array and add key */ 448 *keysp = xrealloc(*keysp, *nkeys + 1, 449 sizeof(Key *)); 450 (*keysp)[*nkeys] = key; 451 *nkeys = *nkeys + 1; 452 debug("have %d keys", *nkeys); 453 } else { 454 RSA_free(rsa); 455 } 456 } 457 for (i = 0; i < 3; i++) 458 xfree(attribs[i].pValue); 459 } 460 if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) 461 error("C_FindObjectsFinal failed: %lu", rv); 462 return (0); 463 } 464 465 #ifdef HAVE_DLOPEN 466 /* register a new provider, fails if provider already exists */ 467 int 468 pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) 469 { 470 int nkeys, need_finalize = 0; 471 struct pkcs11_provider *p = NULL; 472 void *handle = NULL; 473 CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); 474 CK_RV rv; 475 CK_FUNCTION_LIST *f = NULL; 476 CK_TOKEN_INFO *token; 477 CK_ULONG i; 478 479 *keyp = NULL; 480 if (pkcs11_provider_lookup(provider_id) != NULL) { 481 error("provider already registered: %s", provider_id); 482 goto fail; 483 } 484 /* open shared pkcs11-libarary */ 485 if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { 486 error("dlopen %s failed: %s", provider_id, dlerror()); 487 goto fail; 488 } 489 if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { 490 error("dlsym(C_GetFunctionList) failed: %s", dlerror()); 491 goto fail; 492 } 493 p = xcalloc(1, sizeof(*p)); 494 p->name = xstrdup(provider_id); 495 p->handle = handle; 496 /* setup the pkcs11 callbacks */ 497 if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { 498 error("C_GetFunctionList failed: %lu", rv); 499 goto fail; 500 } 501 p->function_list = f; 502 if ((rv = f->C_Initialize(NULL)) != CKR_OK) { 503 error("C_Initialize failed: %lu", rv); 504 goto fail; 505 } 506 need_finalize = 1; 507 if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { 508 error("C_GetInfo failed: %lu", rv); 509 goto fail; 510 } 511 rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); 512 rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); 513 debug("manufacturerID <%s> cryptokiVersion %d.%d" 514 " libraryDescription <%s> libraryVersion %d.%d", 515 p->info.manufacturerID, 516 p->info.cryptokiVersion.major, 517 p->info.cryptokiVersion.minor, 518 p->info.libraryDescription, 519 p->info.libraryVersion.major, 520 p->info.libraryVersion.minor); 521 if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { 522 error("C_GetSlotList failed: %lu", rv); 523 goto fail; 524 } 525 if (p->nslots == 0) { 526 error("no slots"); 527 goto fail; 528 } 529 p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); 530 if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) 531 != CKR_OK) { 532 error("C_GetSlotList failed: %lu", rv); 533 goto fail; 534 } 535 p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); 536 p->valid = 1; 537 nkeys = 0; 538 for (i = 0; i < p->nslots; i++) { 539 token = &p->slotinfo[i].token; 540 if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) 541 != CKR_OK) { 542 error("C_GetTokenInfo failed: %lu", rv); 543 continue; 544 } 545 rmspace(token->label, sizeof(token->label)); 546 rmspace(token->manufacturerID, sizeof(token->manufacturerID)); 547 rmspace(token->model, sizeof(token->model)); 548 rmspace(token->serialNumber, sizeof(token->serialNumber)); 549 debug("label <%s> manufacturerID <%s> model <%s> serial <%s>" 550 " flags 0x%lx", 551 token->label, token->manufacturerID, token->model, 552 token->serialNumber, token->flags); 553 /* open session, login with pin and retrieve public keys */ 554 if (pkcs11_open_session(p, i, pin) == 0) 555 pkcs11_fetch_keys(p, i, keyp, &nkeys); 556 } 557 if (nkeys > 0) { 558 TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); 559 p->refcount++; /* add to provider list */ 560 return (nkeys); 561 } 562 error("no keys"); 563 /* don't add the provider, since it does not have any keys */ 564 fail: 565 if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) 566 error("C_Finalize failed: %lu", rv); 567 if (p) { 568 if (p->slotlist) 569 xfree(p->slotlist); 570 if (p->slotinfo) 571 xfree(p->slotinfo); 572 xfree(p); 573 } 574 if (handle) 575 dlclose(handle); 576 return (-1); 577 } 578 #else 579 int 580 pkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) 581 { 582 error("dlopen() not supported"); 583 return (-1); 584 } 585 #endif 586