1296781Sdes/* $OpenBSD: ssh-pkcs11.c,v 1.22 2016/02/12 00:20:30 djm Exp $ */ 2204861Sdes/* 3204861Sdes * Copyright (c) 2010 Markus Friedl. All rights reserved. 4204861Sdes * 5204861Sdes * Permission to use, copy, modify, and distribute this software for any 6204861Sdes * purpose with or without fee is hereby granted, provided that the above 7204861Sdes * copyright notice and this permission notice appear in all copies. 8204861Sdes * 9204861Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10204861Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11204861Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12204861Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13204861Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14204861Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15204861Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16204861Sdes */ 17204861Sdes 18204861Sdes#include "includes.h" 19204861Sdes 20204861Sdes#ifdef ENABLE_PKCS11 21204861Sdes 22204861Sdes#include <sys/types.h> 23204861Sdes#ifdef HAVE_SYS_TIME_H 24204861Sdes# include <sys/time.h> 25204861Sdes#endif 26204861Sdes#include <stdarg.h> 27204861Sdes#include <stdio.h> 28204861Sdes 29204861Sdes#include <string.h> 30204861Sdes#include <dlfcn.h> 31204861Sdes 32204861Sdes#include "openbsd-compat/sys-queue.h" 33204861Sdes 34262566Sdes#include <openssl/x509.h> 35262566Sdes 36204861Sdes#define CRYPTOKI_COMPAT 37204861Sdes#include "pkcs11.h" 38204861Sdes 39204861Sdes#include "log.h" 40204861Sdes#include "misc.h" 41295367Sdes#include "sshkey.h" 42204861Sdes#include "ssh-pkcs11.h" 43204861Sdes#include "xmalloc.h" 44204861Sdes 45204861Sdesstruct pkcs11_slotinfo { 46204861Sdes CK_TOKEN_INFO token; 47204861Sdes CK_SESSION_HANDLE session; 48204861Sdes int logged_in; 49204861Sdes}; 50204861Sdes 51204861Sdesstruct pkcs11_provider { 52204861Sdes char *name; 53204861Sdes void *handle; 54204861Sdes CK_FUNCTION_LIST *function_list; 55204861Sdes CK_INFO info; 56204861Sdes CK_ULONG nslots; 57204861Sdes CK_SLOT_ID *slotlist; 58204861Sdes struct pkcs11_slotinfo *slotinfo; 59204861Sdes int valid; 60204861Sdes int refcount; 61204861Sdes TAILQ_ENTRY(pkcs11_provider) next; 62204861Sdes}; 63204861Sdes 64204861SdesTAILQ_HEAD(, pkcs11_provider) pkcs11_providers; 65204861Sdes 66204861Sdesstruct pkcs11_key { 67204861Sdes struct pkcs11_provider *provider; 68204861Sdes CK_ULONG slotidx; 69204861Sdes int (*orig_finish)(RSA *rsa); 70204861Sdes RSA_METHOD rsa_method; 71204861Sdes char *keyid; 72204861Sdes int keyid_len; 73204861Sdes}; 74204861Sdes 75204861Sdesint pkcs11_interactive = 0; 76204861Sdes 77204861Sdesint 78204861Sdespkcs11_init(int interactive) 79204861Sdes{ 80204861Sdes pkcs11_interactive = interactive; 81204861Sdes TAILQ_INIT(&pkcs11_providers); 82204861Sdes return (0); 83204861Sdes} 84204861Sdes 85204861Sdes/* 86204861Sdes * finalize a provider shared libarary, it's no longer usable. 87204861Sdes * however, there might still be keys referencing this provider, 88204861Sdes * so the actuall freeing of memory is handled by pkcs11_provider_unref(). 89204861Sdes * this is called when a provider gets unregistered. 90204861Sdes */ 91204861Sdesstatic void 92204861Sdespkcs11_provider_finalize(struct pkcs11_provider *p) 93204861Sdes{ 94204861Sdes CK_RV rv; 95204861Sdes CK_ULONG i; 96204861Sdes 97204861Sdes debug("pkcs11_provider_finalize: %p refcount %d valid %d", 98204861Sdes p, p->refcount, p->valid); 99204861Sdes if (!p->valid) 100204861Sdes return; 101204861Sdes for (i = 0; i < p->nslots; i++) { 102204861Sdes if (p->slotinfo[i].session && 103204861Sdes (rv = p->function_list->C_CloseSession( 104204861Sdes p->slotinfo[i].session)) != CKR_OK) 105204861Sdes error("C_CloseSession failed: %lu", rv); 106204861Sdes } 107204861Sdes if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) 108204861Sdes error("C_Finalize failed: %lu", rv); 109204861Sdes p->valid = 0; 110204861Sdes p->function_list = NULL; 111204861Sdes dlclose(p->handle); 112204861Sdes} 113204861Sdes 114204861Sdes/* 115204861Sdes * remove a reference to the provider. 116204861Sdes * called when a key gets destroyed or when the provider is unregistered. 117204861Sdes */ 118204861Sdesstatic void 119204861Sdespkcs11_provider_unref(struct pkcs11_provider *p) 120204861Sdes{ 121204861Sdes debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); 122204861Sdes if (--p->refcount <= 0) { 123204861Sdes if (p->valid) 124204861Sdes error("pkcs11_provider_unref: %p still valid", p); 125255767Sdes free(p->slotlist); 126255767Sdes free(p->slotinfo); 127255767Sdes free(p); 128204861Sdes } 129204861Sdes} 130204861Sdes 131204861Sdes/* unregister all providers, keys might still point to the providers */ 132204861Sdesvoid 133204861Sdespkcs11_terminate(void) 134204861Sdes{ 135204861Sdes struct pkcs11_provider *p; 136204861Sdes 137204861Sdes while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) { 138204861Sdes TAILQ_REMOVE(&pkcs11_providers, p, next); 139204861Sdes pkcs11_provider_finalize(p); 140204861Sdes pkcs11_provider_unref(p); 141204861Sdes } 142204861Sdes} 143204861Sdes 144204861Sdes/* lookup provider by name */ 145204861Sdesstatic struct pkcs11_provider * 146204861Sdespkcs11_provider_lookup(char *provider_id) 147204861Sdes{ 148204861Sdes struct pkcs11_provider *p; 149204861Sdes 150204861Sdes TAILQ_FOREACH(p, &pkcs11_providers, next) { 151204861Sdes debug("check %p %s", p, p->name); 152204861Sdes if (!strcmp(provider_id, p->name)) 153204861Sdes return (p); 154204861Sdes } 155204861Sdes return (NULL); 156204861Sdes} 157204861Sdes 158204861Sdes/* unregister provider by name */ 159204861Sdesint 160204861Sdespkcs11_del_provider(char *provider_id) 161204861Sdes{ 162204861Sdes struct pkcs11_provider *p; 163204861Sdes 164204861Sdes if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { 165204861Sdes TAILQ_REMOVE(&pkcs11_providers, p, next); 166204861Sdes pkcs11_provider_finalize(p); 167204861Sdes pkcs11_provider_unref(p); 168204861Sdes return (0); 169204861Sdes } 170204861Sdes return (-1); 171204861Sdes} 172204861Sdes 173204861Sdes/* openssl callback for freeing an RSA key */ 174204861Sdesstatic int 175204861Sdespkcs11_rsa_finish(RSA *rsa) 176204861Sdes{ 177204861Sdes struct pkcs11_key *k11; 178204861Sdes int rv = -1; 179204861Sdes 180204861Sdes if ((k11 = RSA_get_app_data(rsa)) != NULL) { 181204861Sdes if (k11->orig_finish) 182204861Sdes rv = k11->orig_finish(rsa); 183204861Sdes if (k11->provider) 184204861Sdes pkcs11_provider_unref(k11->provider); 185255767Sdes free(k11->keyid); 186255767Sdes free(k11); 187204861Sdes } 188204861Sdes return (rv); 189204861Sdes} 190204861Sdes 191215116Sdes/* find a single 'obj' for given attributes */ 192215116Sdesstatic int 193215116Sdespkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, 194215116Sdes CK_ULONG nattr, CK_OBJECT_HANDLE *obj) 195215116Sdes{ 196215116Sdes CK_FUNCTION_LIST *f; 197215116Sdes CK_SESSION_HANDLE session; 198215116Sdes CK_ULONG nfound = 0; 199215116Sdes CK_RV rv; 200215116Sdes int ret = -1; 201215116Sdes 202215116Sdes f = p->function_list; 203215116Sdes session = p->slotinfo[slotidx].session; 204215116Sdes if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { 205215116Sdes error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); 206215116Sdes return (-1); 207215116Sdes } 208215116Sdes if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK || 209215116Sdes nfound != 1) { 210215116Sdes debug("C_FindObjects failed (nfound %lu nattr %lu): %lu", 211215116Sdes nfound, nattr, rv); 212215116Sdes } else 213215116Sdes ret = 0; 214215116Sdes if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) 215215116Sdes error("C_FindObjectsFinal failed: %lu", rv); 216215116Sdes return (ret); 217215116Sdes} 218215116Sdes 219204861Sdes/* openssl callback doing the actual signing operation */ 220204861Sdesstatic int 221204861Sdespkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, 222204861Sdes int padding) 223204861Sdes{ 224204861Sdes struct pkcs11_key *k11; 225204861Sdes struct pkcs11_slotinfo *si; 226204861Sdes CK_FUNCTION_LIST *f; 227204861Sdes CK_OBJECT_HANDLE obj; 228215116Sdes CK_ULONG tlen = 0; 229204861Sdes CK_RV rv; 230262566Sdes CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; 231204861Sdes CK_BBOOL true_val = CK_TRUE; 232204861Sdes CK_MECHANISM mech = { 233204861Sdes CKM_RSA_PKCS, NULL_PTR, 0 234204861Sdes }; 235204861Sdes CK_ATTRIBUTE key_filter[] = { 236204861Sdes {CKA_CLASS, NULL, sizeof(private_key_class) }, 237204861Sdes {CKA_ID, NULL, 0}, 238204861Sdes {CKA_SIGN, NULL, sizeof(true_val) } 239204861Sdes }; 240295367Sdes char *pin = NULL, prompt[1024]; 241204861Sdes int rval = -1; 242204861Sdes 243204861Sdes key_filter[0].pValue = &private_key_class; 244204861Sdes key_filter[2].pValue = &true_val; 245204861Sdes 246204861Sdes if ((k11 = RSA_get_app_data(rsa)) == NULL) { 247204861Sdes error("RSA_get_app_data failed for rsa %p", rsa); 248204861Sdes return (-1); 249204861Sdes } 250204861Sdes if (!k11->provider || !k11->provider->valid) { 251204861Sdes error("no pkcs11 (valid) provider for rsa %p", rsa); 252204861Sdes return (-1); 253204861Sdes } 254204861Sdes f = k11->provider->function_list; 255204861Sdes si = &k11->provider->slotinfo[k11->slotidx]; 256204861Sdes if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { 257204861Sdes if (!pkcs11_interactive) { 258295367Sdes error("need pin entry%s", (si->token.flags & 259295367Sdes CKF_PROTECTED_AUTHENTICATION_PATH) ? 260295367Sdes " on reader keypad" : ""); 261204861Sdes return (-1); 262204861Sdes } 263295367Sdes if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) 264295367Sdes verbose("Deferring PIN entry to reader keypad."); 265295367Sdes else { 266295367Sdes snprintf(prompt, sizeof(prompt), 267295367Sdes "Enter PIN for '%s': ", si->token.label); 268295367Sdes pin = read_passphrase(prompt, RP_ALLOW_EOF); 269295367Sdes if (pin == NULL) 270295367Sdes return (-1); /* bail out */ 271295367Sdes } 272295367Sdes rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, 273295367Sdes (pin != NULL) ? strlen(pin) : 0); 274295367Sdes if (pin != NULL) { 275295367Sdes explicit_bzero(pin, strlen(pin)); 276255767Sdes free(pin); 277295367Sdes } 278295367Sdes if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { 279204861Sdes error("C_Login failed: %lu", rv); 280204861Sdes return (-1); 281204861Sdes } 282204861Sdes si->logged_in = 1; 283204861Sdes } 284204861Sdes key_filter[1].pValue = k11->keyid; 285204861Sdes key_filter[1].ulValueLen = k11->keyid_len; 286215116Sdes /* try to find object w/CKA_SIGN first, retry w/o */ 287215116Sdes if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && 288215116Sdes pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { 289215116Sdes error("cannot find private key"); 290204861Sdes } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { 291204861Sdes error("C_SignInit failed: %lu", rv); 292204861Sdes } else { 293204861Sdes /* XXX handle CKR_BUFFER_TOO_SMALL */ 294204861Sdes tlen = RSA_size(rsa); 295204861Sdes rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); 296204861Sdes if (rv == CKR_OK) 297204861Sdes rval = tlen; 298204861Sdes else 299204861Sdes error("C_Sign failed: %lu", rv); 300204861Sdes } 301204861Sdes return (rval); 302204861Sdes} 303204861Sdes 304204861Sdesstatic int 305204861Sdespkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, 306204861Sdes int padding) 307204861Sdes{ 308204861Sdes return (-1); 309204861Sdes} 310204861Sdes 311204861Sdes/* redirect private key operations for rsa key to pkcs11 token */ 312204861Sdesstatic int 313204861Sdespkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, 314204861Sdes CK_ATTRIBUTE *keyid_attrib, RSA *rsa) 315204861Sdes{ 316204861Sdes struct pkcs11_key *k11; 317204861Sdes const RSA_METHOD *def = RSA_get_default_method(); 318204861Sdes 319204861Sdes k11 = xcalloc(1, sizeof(*k11)); 320204861Sdes k11->provider = provider; 321204861Sdes provider->refcount++; /* provider referenced by RSA key */ 322204861Sdes k11->slotidx = slotidx; 323204861Sdes /* identify key object on smartcard */ 324204861Sdes k11->keyid_len = keyid_attrib->ulValueLen; 325296781Sdes if (k11->keyid_len > 0) { 326296781Sdes k11->keyid = xmalloc(k11->keyid_len); 327296781Sdes memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); 328296781Sdes } 329204861Sdes k11->orig_finish = def->finish; 330204861Sdes memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); 331204861Sdes k11->rsa_method.name = "pkcs11"; 332204861Sdes k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt; 333204861Sdes k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt; 334204861Sdes k11->rsa_method.finish = pkcs11_rsa_finish; 335204861Sdes RSA_set_method(rsa, &k11->rsa_method); 336204861Sdes RSA_set_app_data(rsa, k11); 337204861Sdes return (0); 338204861Sdes} 339204861Sdes 340204861Sdes/* remove trailing spaces */ 341204861Sdesstatic void 342255767Sdesrmspace(u_char *buf, size_t len) 343204861Sdes{ 344204861Sdes size_t i; 345204861Sdes 346204861Sdes if (!len) 347204861Sdes return; 348204861Sdes for (i = len - 1; i > 0; i--) 349204861Sdes if (i == len - 1 || buf[i] == ' ') 350204861Sdes buf[i] = '\0'; 351204861Sdes else 352204861Sdes break; 353204861Sdes} 354204861Sdes 355204861Sdes/* 356204861Sdes * open a pkcs11 session and login if required. 357204861Sdes * if pin == NULL we delay login until key use 358204861Sdes */ 359204861Sdesstatic int 360204861Sdespkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) 361204861Sdes{ 362204861Sdes CK_RV rv; 363204861Sdes CK_FUNCTION_LIST *f; 364204861Sdes CK_SESSION_HANDLE session; 365204861Sdes int login_required; 366204861Sdes 367204861Sdes f = p->function_list; 368204861Sdes login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; 369204861Sdes if (pin && login_required && !strlen(pin)) { 370204861Sdes error("pin required"); 371204861Sdes return (-1); 372204861Sdes } 373204861Sdes if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| 374204861Sdes CKF_SERIAL_SESSION, NULL, NULL, &session)) 375204861Sdes != CKR_OK) { 376204861Sdes error("C_OpenSession failed: %lu", rv); 377204861Sdes return (-1); 378204861Sdes } 379204861Sdes if (login_required && pin) { 380295367Sdes rv = f->C_Login(session, CKU_USER, 381295367Sdes (u_char *)pin, strlen(pin)); 382295367Sdes if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { 383204861Sdes error("C_Login failed: %lu", rv); 384204861Sdes if ((rv = f->C_CloseSession(session)) != CKR_OK) 385204861Sdes error("C_CloseSession failed: %lu", rv); 386204861Sdes return (-1); 387204861Sdes } 388204861Sdes p->slotinfo[slotidx].logged_in = 1; 389204861Sdes } 390204861Sdes p->slotinfo[slotidx].session = session; 391204861Sdes return (0); 392204861Sdes} 393204861Sdes 394204861Sdes/* 395204861Sdes * lookup public keys for token in slot identified by slotidx, 396204861Sdes * add 'wrapped' public keys to the 'keysp' array and increment nkeys. 397204861Sdes * keysp points to an (possibly empty) array with *nkeys keys. 398204861Sdes */ 399262566Sdesstatic int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, 400295367Sdes CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *) 401262566Sdes __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); 402262566Sdes 403204861Sdesstatic int 404262566Sdespkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, 405295367Sdes struct sshkey ***keysp, int *nkeys) 406204861Sdes{ 407262566Sdes CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; 408262566Sdes CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; 409262566Sdes CK_ATTRIBUTE pubkey_filter[] = { 410262566Sdes { CKA_CLASS, NULL, sizeof(pubkey_class) } 411262566Sdes }; 412262566Sdes CK_ATTRIBUTE cert_filter[] = { 413262566Sdes { CKA_CLASS, NULL, sizeof(cert_class) } 414262566Sdes }; 415262566Sdes CK_ATTRIBUTE pubkey_attribs[] = { 416262566Sdes { CKA_ID, NULL, 0 }, 417262566Sdes { CKA_MODULUS, NULL, 0 }, 418262566Sdes { CKA_PUBLIC_EXPONENT, NULL, 0 } 419262566Sdes }; 420262566Sdes CK_ATTRIBUTE cert_attribs[] = { 421262566Sdes { CKA_ID, NULL, 0 }, 422262566Sdes { CKA_SUBJECT, NULL, 0 }, 423262566Sdes { CKA_VALUE, NULL, 0 } 424262566Sdes }; 425262566Sdes pubkey_filter[0].pValue = &pubkey_class; 426262566Sdes cert_filter[0].pValue = &cert_class; 427262566Sdes 428262566Sdes if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs, 429262566Sdes keysp, nkeys) < 0 || 430262566Sdes pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs, 431262566Sdes keysp, nkeys) < 0) 432262566Sdes return (-1); 433262566Sdes return (0); 434262566Sdes} 435262566Sdes 436262566Sdesstatic int 437295367Sdespkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) 438262566Sdes{ 439262566Sdes int i; 440262566Sdes 441262566Sdes for (i = 0; i < *nkeys; i++) 442295367Sdes if (sshkey_equal(key, (*keysp)[i])) 443262566Sdes return (1); 444262566Sdes return (0); 445262566Sdes} 446262566Sdes 447262566Sdesstatic int 448262566Sdespkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, 449262566Sdes CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], 450295367Sdes struct sshkey ***keysp, int *nkeys) 451262566Sdes{ 452295367Sdes struct sshkey *key; 453204861Sdes RSA *rsa; 454262566Sdes X509 *x509; 455262566Sdes EVP_PKEY *evp; 456204861Sdes int i; 457262566Sdes const u_char *cp; 458204861Sdes CK_RV rv; 459204861Sdes CK_OBJECT_HANDLE obj; 460204861Sdes CK_ULONG nfound; 461204861Sdes CK_SESSION_HANDLE session; 462204861Sdes CK_FUNCTION_LIST *f; 463204861Sdes 464204861Sdes f = p->function_list; 465204861Sdes session = p->slotinfo[slotidx].session; 466204861Sdes /* setup a filter the looks for public keys */ 467262566Sdes if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) { 468204861Sdes error("C_FindObjectsInit failed: %lu", rv); 469204861Sdes return (-1); 470204861Sdes } 471204861Sdes while (1) { 472204861Sdes /* XXX 3 attributes in attribs[] */ 473204861Sdes for (i = 0; i < 3; i++) { 474204861Sdes attribs[i].pValue = NULL; 475204861Sdes attribs[i].ulValueLen = 0; 476204861Sdes } 477204861Sdes if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK 478204861Sdes || nfound == 0) 479204861Sdes break; 480204861Sdes /* found a key, so figure out size of the attributes */ 481204861Sdes if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) 482204861Sdes != CKR_OK) { 483204861Sdes error("C_GetAttributeValue failed: %lu", rv); 484204861Sdes continue; 485204861Sdes } 486295367Sdes /* 487295367Sdes * Allow CKA_ID (always first attribute) to be empty, but 488295367Sdes * ensure that none of the others are zero length. 489295367Sdes * XXX assumes CKA_ID is always first. 490295367Sdes */ 491295367Sdes if (attribs[1].ulValueLen == 0 || 492215116Sdes attribs[2].ulValueLen == 0) { 493215116Sdes continue; 494215116Sdes } 495215116Sdes /* allocate buffers for attributes */ 496295367Sdes for (i = 0; i < 3; i++) { 497295367Sdes if (attribs[i].ulValueLen > 0) { 498295367Sdes attribs[i].pValue = xmalloc( 499295367Sdes attribs[i].ulValueLen); 500295367Sdes } 501295367Sdes } 502295367Sdes 503262566Sdes /* 504262566Sdes * retrieve ID, modulus and public exponent of RSA key, 505262566Sdes * or ID, subject and value for certificates. 506262566Sdes */ 507262566Sdes rsa = NULL; 508204861Sdes if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) 509204861Sdes != CKR_OK) { 510204861Sdes error("C_GetAttributeValue failed: %lu", rv); 511262566Sdes } else if (attribs[1].type == CKA_MODULUS ) { 512262566Sdes if ((rsa = RSA_new()) == NULL) { 513262566Sdes error("RSA_new failed"); 514262566Sdes } else { 515262566Sdes rsa->n = BN_bin2bn(attribs[1].pValue, 516262566Sdes attribs[1].ulValueLen, NULL); 517262566Sdes rsa->e = BN_bin2bn(attribs[2].pValue, 518262566Sdes attribs[2].ulValueLen, NULL); 519262566Sdes } 520204861Sdes } else { 521262566Sdes cp = attribs[2].pValue; 522262566Sdes if ((x509 = X509_new()) == NULL) { 523262566Sdes error("X509_new failed"); 524262566Sdes } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen) 525262566Sdes == NULL) { 526262566Sdes error("d2i_X509 failed"); 527262566Sdes } else if ((evp = X509_get_pubkey(x509)) == NULL || 528262566Sdes evp->type != EVP_PKEY_RSA || 529262566Sdes evp->pkey.rsa == NULL) { 530262566Sdes debug("X509_get_pubkey failed or no rsa"); 531262566Sdes } else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa)) 532262566Sdes == NULL) { 533262566Sdes error("RSAPublicKey_dup"); 534262566Sdes } 535262566Sdes if (x509) 536262566Sdes X509_free(x509); 537262566Sdes } 538262566Sdes if (rsa && rsa->n && rsa->e && 539262566Sdes pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { 540295367Sdes key = sshkey_new(KEY_UNSPEC); 541262566Sdes key->rsa = rsa; 542262566Sdes key->type = KEY_RSA; 543295367Sdes key->flags |= SSHKEY_FLAG_EXT; 544262566Sdes if (pkcs11_key_included(keysp, nkeys, key)) { 545295367Sdes sshkey_free(key); 546262566Sdes } else { 547204861Sdes /* expand key array and add key */ 548295367Sdes *keysp = xreallocarray(*keysp, *nkeys + 1, 549295367Sdes sizeof(struct sshkey *)); 550204861Sdes (*keysp)[*nkeys] = key; 551204861Sdes *nkeys = *nkeys + 1; 552204861Sdes debug("have %d keys", *nkeys); 553204861Sdes } 554262566Sdes } else if (rsa) { 555262566Sdes RSA_free(rsa); 556204861Sdes } 557204861Sdes for (i = 0; i < 3; i++) 558255767Sdes free(attribs[i].pValue); 559204861Sdes } 560204861Sdes if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) 561204861Sdes error("C_FindObjectsFinal failed: %lu", rv); 562204861Sdes return (0); 563204861Sdes} 564204861Sdes 565204861Sdes/* register a new provider, fails if provider already exists */ 566204861Sdesint 567295367Sdespkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) 568204861Sdes{ 569204861Sdes int nkeys, need_finalize = 0; 570204861Sdes struct pkcs11_provider *p = NULL; 571204861Sdes void *handle = NULL; 572204861Sdes CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); 573204861Sdes CK_RV rv; 574204861Sdes CK_FUNCTION_LIST *f = NULL; 575204861Sdes CK_TOKEN_INFO *token; 576204861Sdes CK_ULONG i; 577204861Sdes 578204861Sdes *keyp = NULL; 579204861Sdes if (pkcs11_provider_lookup(provider_id) != NULL) { 580204861Sdes error("provider already registered: %s", provider_id); 581204861Sdes goto fail; 582204861Sdes } 583204861Sdes /* open shared pkcs11-libarary */ 584204861Sdes if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { 585204861Sdes error("dlopen %s failed: %s", provider_id, dlerror()); 586204861Sdes goto fail; 587204861Sdes } 588204861Sdes if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { 589204861Sdes error("dlsym(C_GetFunctionList) failed: %s", dlerror()); 590204861Sdes goto fail; 591204861Sdes } 592204861Sdes p = xcalloc(1, sizeof(*p)); 593204861Sdes p->name = xstrdup(provider_id); 594204861Sdes p->handle = handle; 595204861Sdes /* setup the pkcs11 callbacks */ 596204861Sdes if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { 597204861Sdes error("C_GetFunctionList failed: %lu", rv); 598204861Sdes goto fail; 599204861Sdes } 600204861Sdes p->function_list = f; 601204861Sdes if ((rv = f->C_Initialize(NULL)) != CKR_OK) { 602204861Sdes error("C_Initialize failed: %lu", rv); 603204861Sdes goto fail; 604204861Sdes } 605204861Sdes need_finalize = 1; 606204861Sdes if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { 607204861Sdes error("C_GetInfo failed: %lu", rv); 608204861Sdes goto fail; 609204861Sdes } 610204861Sdes rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); 611204861Sdes rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); 612204861Sdes debug("manufacturerID <%s> cryptokiVersion %d.%d" 613204861Sdes " libraryDescription <%s> libraryVersion %d.%d", 614204861Sdes p->info.manufacturerID, 615204861Sdes p->info.cryptokiVersion.major, 616204861Sdes p->info.cryptokiVersion.minor, 617204861Sdes p->info.libraryDescription, 618204861Sdes p->info.libraryVersion.major, 619204861Sdes p->info.libraryVersion.minor); 620204861Sdes if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { 621204861Sdes error("C_GetSlotList failed: %lu", rv); 622204861Sdes goto fail; 623204861Sdes } 624204861Sdes if (p->nslots == 0) { 625204861Sdes error("no slots"); 626204861Sdes goto fail; 627204861Sdes } 628204861Sdes p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); 629204861Sdes if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) 630204861Sdes != CKR_OK) { 631204861Sdes error("C_GetSlotList failed: %lu", rv); 632204861Sdes goto fail; 633204861Sdes } 634204861Sdes p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); 635204861Sdes p->valid = 1; 636204861Sdes nkeys = 0; 637204861Sdes for (i = 0; i < p->nslots; i++) { 638204861Sdes token = &p->slotinfo[i].token; 639204861Sdes if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) 640204861Sdes != CKR_OK) { 641204861Sdes error("C_GetTokenInfo failed: %lu", rv); 642204861Sdes continue; 643204861Sdes } 644295367Sdes if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { 645295367Sdes debug2("%s: ignoring uninitialised token in slot %lu", 646295367Sdes __func__, (unsigned long)i); 647295367Sdes continue; 648295367Sdes } 649204861Sdes rmspace(token->label, sizeof(token->label)); 650204861Sdes rmspace(token->manufacturerID, sizeof(token->manufacturerID)); 651204861Sdes rmspace(token->model, sizeof(token->model)); 652204861Sdes rmspace(token->serialNumber, sizeof(token->serialNumber)); 653204861Sdes debug("label <%s> manufacturerID <%s> model <%s> serial <%s>" 654204861Sdes " flags 0x%lx", 655204861Sdes token->label, token->manufacturerID, token->model, 656204861Sdes token->serialNumber, token->flags); 657204861Sdes /* open session, login with pin and retrieve public keys */ 658204861Sdes if (pkcs11_open_session(p, i, pin) == 0) 659204861Sdes pkcs11_fetch_keys(p, i, keyp, &nkeys); 660204861Sdes } 661204861Sdes if (nkeys > 0) { 662204861Sdes TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); 663204861Sdes p->refcount++; /* add to provider list */ 664204861Sdes return (nkeys); 665204861Sdes } 666204861Sdes error("no keys"); 667204861Sdes /* don't add the provider, since it does not have any keys */ 668204861Sdesfail: 669204861Sdes if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) 670204861Sdes error("C_Finalize failed: %lu", rv); 671204861Sdes if (p) { 672255767Sdes free(p->slotlist); 673255767Sdes free(p->slotinfo); 674255767Sdes free(p); 675204861Sdes } 676204861Sdes if (handle) 677204861Sdes dlclose(handle); 678204861Sdes return (-1); 679204861Sdes} 680204861Sdes 681226046Sdes#else 682226046Sdes 683226046Sdesint 684226046Sdespkcs11_init(int interactive) 685226046Sdes{ 686226046Sdes return (0); 687226046Sdes} 688226046Sdes 689226046Sdesvoid 690226046Sdespkcs11_terminate(void) 691226046Sdes{ 692226046Sdes return; 693226046Sdes} 694226046Sdes 695204861Sdes#endif /* ENABLE_PKCS11 */ 696