1255767Sdes/* $OpenBSD: ssh-pkcs11.c,v 1.8 2013/07/12 00:20:00 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 34204861Sdes#define CRYPTOKI_COMPAT 35204861Sdes#include "pkcs11.h" 36204861Sdes 37204861Sdes#include "log.h" 38204861Sdes#include "misc.h" 39204861Sdes#include "key.h" 40204861Sdes#include "ssh-pkcs11.h" 41204861Sdes#include "xmalloc.h" 42204861Sdes 43204861Sdesstruct pkcs11_slotinfo { 44204861Sdes CK_TOKEN_INFO token; 45204861Sdes CK_SESSION_HANDLE session; 46204861Sdes int logged_in; 47204861Sdes}; 48204861Sdes 49204861Sdesstruct pkcs11_provider { 50204861Sdes char *name; 51204861Sdes void *handle; 52204861Sdes CK_FUNCTION_LIST *function_list; 53204861Sdes CK_INFO info; 54204861Sdes CK_ULONG nslots; 55204861Sdes CK_SLOT_ID *slotlist; 56204861Sdes struct pkcs11_slotinfo *slotinfo; 57204861Sdes int valid; 58204861Sdes int refcount; 59204861Sdes TAILQ_ENTRY(pkcs11_provider) next; 60204861Sdes}; 61204861Sdes 62204861SdesTAILQ_HEAD(, pkcs11_provider) pkcs11_providers; 63204861Sdes 64204861Sdesstruct pkcs11_key { 65204861Sdes struct pkcs11_provider *provider; 66204861Sdes CK_ULONG slotidx; 67204861Sdes int (*orig_finish)(RSA *rsa); 68204861Sdes RSA_METHOD rsa_method; 69204861Sdes char *keyid; 70204861Sdes int keyid_len; 71204861Sdes}; 72204861Sdes 73204861Sdesint pkcs11_interactive = 0; 74204861Sdes 75204861Sdesint 76204861Sdespkcs11_init(int interactive) 77204861Sdes{ 78204861Sdes pkcs11_interactive = interactive; 79204861Sdes TAILQ_INIT(&pkcs11_providers); 80204861Sdes return (0); 81204861Sdes} 82204861Sdes 83204861Sdes/* 84204861Sdes * finalize a provider shared libarary, it's no longer usable. 85204861Sdes * however, there might still be keys referencing this provider, 86204861Sdes * so the actuall freeing of memory is handled by pkcs11_provider_unref(). 87204861Sdes * this is called when a provider gets unregistered. 88204861Sdes */ 89204861Sdesstatic void 90204861Sdespkcs11_provider_finalize(struct pkcs11_provider *p) 91204861Sdes{ 92204861Sdes CK_RV rv; 93204861Sdes CK_ULONG i; 94204861Sdes 95204861Sdes debug("pkcs11_provider_finalize: %p refcount %d valid %d", 96204861Sdes p, p->refcount, p->valid); 97204861Sdes if (!p->valid) 98204861Sdes return; 99204861Sdes for (i = 0; i < p->nslots; i++) { 100204861Sdes if (p->slotinfo[i].session && 101204861Sdes (rv = p->function_list->C_CloseSession( 102204861Sdes p->slotinfo[i].session)) != CKR_OK) 103204861Sdes error("C_CloseSession failed: %lu", rv); 104204861Sdes } 105204861Sdes if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) 106204861Sdes error("C_Finalize failed: %lu", rv); 107204861Sdes p->valid = 0; 108204861Sdes p->function_list = NULL; 109204861Sdes dlclose(p->handle); 110204861Sdes} 111204861Sdes 112204861Sdes/* 113204861Sdes * remove a reference to the provider. 114204861Sdes * called when a key gets destroyed or when the provider is unregistered. 115204861Sdes */ 116204861Sdesstatic void 117204861Sdespkcs11_provider_unref(struct pkcs11_provider *p) 118204861Sdes{ 119204861Sdes debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); 120204861Sdes if (--p->refcount <= 0) { 121204861Sdes if (p->valid) 122204861Sdes error("pkcs11_provider_unref: %p still valid", p); 123255767Sdes free(p->slotlist); 124255767Sdes free(p->slotinfo); 125255767Sdes free(p); 126204861Sdes } 127204861Sdes} 128204861Sdes 129204861Sdes/* unregister all providers, keys might still point to the providers */ 130204861Sdesvoid 131204861Sdespkcs11_terminate(void) 132204861Sdes{ 133204861Sdes struct pkcs11_provider *p; 134204861Sdes 135204861Sdes while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) { 136204861Sdes TAILQ_REMOVE(&pkcs11_providers, p, next); 137204861Sdes pkcs11_provider_finalize(p); 138204861Sdes pkcs11_provider_unref(p); 139204861Sdes } 140204861Sdes} 141204861Sdes 142204861Sdes/* lookup provider by name */ 143204861Sdesstatic struct pkcs11_provider * 144204861Sdespkcs11_provider_lookup(char *provider_id) 145204861Sdes{ 146204861Sdes struct pkcs11_provider *p; 147204861Sdes 148204861Sdes TAILQ_FOREACH(p, &pkcs11_providers, next) { 149204861Sdes debug("check %p %s", p, p->name); 150204861Sdes if (!strcmp(provider_id, p->name)) 151204861Sdes return (p); 152204861Sdes } 153204861Sdes return (NULL); 154204861Sdes} 155204861Sdes 156204861Sdes/* unregister provider by name */ 157204861Sdesint 158204861Sdespkcs11_del_provider(char *provider_id) 159204861Sdes{ 160204861Sdes struct pkcs11_provider *p; 161204861Sdes 162204861Sdes if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { 163204861Sdes TAILQ_REMOVE(&pkcs11_providers, p, next); 164204861Sdes pkcs11_provider_finalize(p); 165204861Sdes pkcs11_provider_unref(p); 166204861Sdes return (0); 167204861Sdes } 168204861Sdes return (-1); 169204861Sdes} 170204861Sdes 171204861Sdes/* openssl callback for freeing an RSA key */ 172204861Sdesstatic int 173204861Sdespkcs11_rsa_finish(RSA *rsa) 174204861Sdes{ 175204861Sdes struct pkcs11_key *k11; 176204861Sdes int rv = -1; 177204861Sdes 178204861Sdes if ((k11 = RSA_get_app_data(rsa)) != NULL) { 179204861Sdes if (k11->orig_finish) 180204861Sdes rv = k11->orig_finish(rsa); 181204861Sdes if (k11->provider) 182204861Sdes pkcs11_provider_unref(k11->provider); 183255767Sdes free(k11->keyid); 184255767Sdes free(k11); 185204861Sdes } 186204861Sdes return (rv); 187204861Sdes} 188204861Sdes 189215116Sdes/* find a single 'obj' for given attributes */ 190215116Sdesstatic int 191215116Sdespkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, 192215116Sdes CK_ULONG nattr, CK_OBJECT_HANDLE *obj) 193215116Sdes{ 194215116Sdes CK_FUNCTION_LIST *f; 195215116Sdes CK_SESSION_HANDLE session; 196215116Sdes CK_ULONG nfound = 0; 197215116Sdes CK_RV rv; 198215116Sdes int ret = -1; 199215116Sdes 200215116Sdes f = p->function_list; 201215116Sdes session = p->slotinfo[slotidx].session; 202215116Sdes if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { 203215116Sdes error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); 204215116Sdes return (-1); 205215116Sdes } 206215116Sdes if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK || 207215116Sdes nfound != 1) { 208215116Sdes debug("C_FindObjects failed (nfound %lu nattr %lu): %lu", 209215116Sdes nfound, nattr, rv); 210215116Sdes } else 211215116Sdes ret = 0; 212215116Sdes if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) 213215116Sdes error("C_FindObjectsFinal failed: %lu", rv); 214215116Sdes return (ret); 215215116Sdes} 216215116Sdes 217204861Sdes/* openssl callback doing the actual signing operation */ 218204861Sdesstatic int 219204861Sdespkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, 220204861Sdes int padding) 221204861Sdes{ 222204861Sdes struct pkcs11_key *k11; 223204861Sdes struct pkcs11_slotinfo *si; 224204861Sdes CK_FUNCTION_LIST *f; 225204861Sdes CK_OBJECT_HANDLE obj; 226215116Sdes CK_ULONG tlen = 0; 227204861Sdes CK_RV rv; 228204861Sdes CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; 229204861Sdes CK_BBOOL true_val = CK_TRUE; 230204861Sdes CK_MECHANISM mech = { 231204861Sdes CKM_RSA_PKCS, NULL_PTR, 0 232204861Sdes }; 233204861Sdes CK_ATTRIBUTE key_filter[] = { 234204861Sdes {CKA_CLASS, NULL, sizeof(private_key_class) }, 235204861Sdes {CKA_ID, NULL, 0}, 236204861Sdes {CKA_SIGN, NULL, sizeof(true_val) } 237204861Sdes }; 238204861Sdes char *pin, prompt[1024]; 239204861Sdes int rval = -1; 240204861Sdes 241204861Sdes /* some compilers complain about non-constant initializer so we 242204861Sdes use NULL in CK_ATTRIBUTE above and set the values here */ 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) { 258204861Sdes error("need pin"); 259204861Sdes return (-1); 260204861Sdes } 261204861Sdes snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ", 262204861Sdes si->token.label); 263204861Sdes pin = read_passphrase(prompt, RP_ALLOW_EOF); 264204861Sdes if (pin == NULL) 265204861Sdes return (-1); /* bail out */ 266255767Sdes if ((rv = f->C_Login(si->session, CKU_USER, 267255767Sdes (u_char *)pin, strlen(pin))) != CKR_OK) { 268255767Sdes free(pin); 269204861Sdes error("C_Login failed: %lu", rv); 270204861Sdes return (-1); 271204861Sdes } 272255767Sdes free(pin); 273204861Sdes si->logged_in = 1; 274204861Sdes } 275204861Sdes key_filter[1].pValue = k11->keyid; 276204861Sdes key_filter[1].ulValueLen = k11->keyid_len; 277215116Sdes /* try to find object w/CKA_SIGN first, retry w/o */ 278215116Sdes if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && 279215116Sdes pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { 280215116Sdes error("cannot find private key"); 281204861Sdes } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { 282204861Sdes error("C_SignInit failed: %lu", rv); 283204861Sdes } else { 284204861Sdes /* XXX handle CKR_BUFFER_TOO_SMALL */ 285204861Sdes tlen = RSA_size(rsa); 286204861Sdes rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen); 287204861Sdes if (rv == CKR_OK) 288204861Sdes rval = tlen; 289204861Sdes else 290204861Sdes error("C_Sign failed: %lu", rv); 291204861Sdes } 292204861Sdes return (rval); 293204861Sdes} 294204861Sdes 295204861Sdesstatic int 296204861Sdespkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, 297204861Sdes int padding) 298204861Sdes{ 299204861Sdes return (-1); 300204861Sdes} 301204861Sdes 302204861Sdes/* redirect private key operations for rsa key to pkcs11 token */ 303204861Sdesstatic int 304204861Sdespkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, 305204861Sdes CK_ATTRIBUTE *keyid_attrib, RSA *rsa) 306204861Sdes{ 307204861Sdes struct pkcs11_key *k11; 308204861Sdes const RSA_METHOD *def = RSA_get_default_method(); 309204861Sdes 310204861Sdes k11 = xcalloc(1, sizeof(*k11)); 311204861Sdes k11->provider = provider; 312204861Sdes provider->refcount++; /* provider referenced by RSA key */ 313204861Sdes k11->slotidx = slotidx; 314204861Sdes /* identify key object on smartcard */ 315204861Sdes k11->keyid_len = keyid_attrib->ulValueLen; 316204861Sdes k11->keyid = xmalloc(k11->keyid_len); 317204861Sdes memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); 318204861Sdes k11->orig_finish = def->finish; 319204861Sdes memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); 320204861Sdes k11->rsa_method.name = "pkcs11"; 321204861Sdes k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt; 322204861Sdes k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt; 323204861Sdes k11->rsa_method.finish = pkcs11_rsa_finish; 324204861Sdes RSA_set_method(rsa, &k11->rsa_method); 325204861Sdes RSA_set_app_data(rsa, k11); 326204861Sdes return (0); 327204861Sdes} 328204861Sdes 329204861Sdes/* remove trailing spaces */ 330204861Sdesstatic void 331255767Sdesrmspace(u_char *buf, size_t len) 332204861Sdes{ 333204861Sdes size_t i; 334204861Sdes 335204861Sdes if (!len) 336204861Sdes return; 337204861Sdes for (i = len - 1; i > 0; i--) 338204861Sdes if (i == len - 1 || buf[i] == ' ') 339204861Sdes buf[i] = '\0'; 340204861Sdes else 341204861Sdes break; 342204861Sdes} 343204861Sdes 344204861Sdes/* 345204861Sdes * open a pkcs11 session and login if required. 346204861Sdes * if pin == NULL we delay login until key use 347204861Sdes */ 348204861Sdesstatic int 349204861Sdespkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) 350204861Sdes{ 351204861Sdes CK_RV rv; 352204861Sdes CK_FUNCTION_LIST *f; 353204861Sdes CK_SESSION_HANDLE session; 354204861Sdes int login_required; 355204861Sdes 356204861Sdes f = p->function_list; 357204861Sdes login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; 358204861Sdes if (pin && login_required && !strlen(pin)) { 359204861Sdes error("pin required"); 360204861Sdes return (-1); 361204861Sdes } 362204861Sdes if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| 363204861Sdes CKF_SERIAL_SESSION, NULL, NULL, &session)) 364204861Sdes != CKR_OK) { 365204861Sdes error("C_OpenSession failed: %lu", rv); 366204861Sdes return (-1); 367204861Sdes } 368204861Sdes if (login_required && pin) { 369255767Sdes if ((rv = f->C_Login(session, CKU_USER, 370255767Sdes (u_char *)pin, strlen(pin))) != CKR_OK) { 371204861Sdes error("C_Login failed: %lu", rv); 372204861Sdes if ((rv = f->C_CloseSession(session)) != CKR_OK) 373204861Sdes error("C_CloseSession failed: %lu", rv); 374204861Sdes return (-1); 375204861Sdes } 376204861Sdes p->slotinfo[slotidx].logged_in = 1; 377204861Sdes } 378204861Sdes p->slotinfo[slotidx].session = session; 379204861Sdes return (0); 380204861Sdes} 381204861Sdes 382204861Sdes/* 383204861Sdes * lookup public keys for token in slot identified by slotidx, 384204861Sdes * add 'wrapped' public keys to the 'keysp' array and increment nkeys. 385204861Sdes * keysp points to an (possibly empty) array with *nkeys keys. 386204861Sdes */ 387204861Sdesstatic int 388204861Sdespkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, Key ***keysp, 389204861Sdes int *nkeys) 390204861Sdes{ 391204861Sdes Key *key; 392204861Sdes RSA *rsa; 393204861Sdes int i; 394204861Sdes CK_RV rv; 395204861Sdes CK_OBJECT_HANDLE obj; 396204861Sdes CK_ULONG nfound; 397204861Sdes CK_SESSION_HANDLE session; 398204861Sdes CK_FUNCTION_LIST *f; 399204861Sdes CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; 400204861Sdes CK_ATTRIBUTE pubkey_filter[] = { 401204861Sdes { CKA_CLASS, NULL, sizeof(pubkey_class) } 402204861Sdes }; 403204861Sdes CK_ATTRIBUTE attribs[] = { 404204861Sdes { CKA_ID, NULL, 0 }, 405204861Sdes { CKA_MODULUS, NULL, 0 }, 406204861Sdes { CKA_PUBLIC_EXPONENT, NULL, 0 } 407204861Sdes }; 408204861Sdes 409204861Sdes /* some compilers complain about non-constant initializer so we 410204861Sdes use NULL in CK_ATTRIBUTE above and set the value here */ 411204861Sdes pubkey_filter[0].pValue = &pubkey_class; 412204861Sdes 413204861Sdes f = p->function_list; 414204861Sdes session = p->slotinfo[slotidx].session; 415204861Sdes /* setup a filter the looks for public keys */ 416204861Sdes if ((rv = f->C_FindObjectsInit(session, pubkey_filter, 1)) != CKR_OK) { 417204861Sdes error("C_FindObjectsInit failed: %lu", rv); 418204861Sdes return (-1); 419204861Sdes } 420204861Sdes while (1) { 421204861Sdes /* XXX 3 attributes in attribs[] */ 422204861Sdes for (i = 0; i < 3; i++) { 423204861Sdes attribs[i].pValue = NULL; 424204861Sdes attribs[i].ulValueLen = 0; 425204861Sdes } 426204861Sdes if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK 427204861Sdes || nfound == 0) 428204861Sdes break; 429204861Sdes /* found a key, so figure out size of the attributes */ 430204861Sdes if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) 431204861Sdes != CKR_OK) { 432204861Sdes error("C_GetAttributeValue failed: %lu", rv); 433204861Sdes continue; 434204861Sdes } 435215116Sdes /* check that none of the attributes are zero length */ 436215116Sdes if (attribs[0].ulValueLen == 0 || 437215116Sdes attribs[1].ulValueLen == 0 || 438215116Sdes attribs[2].ulValueLen == 0) { 439215116Sdes continue; 440215116Sdes } 441215116Sdes /* allocate buffers for attributes */ 442204861Sdes for (i = 0; i < 3; i++) 443204861Sdes attribs[i].pValue = xmalloc(attribs[i].ulValueLen); 444204861Sdes /* retrieve ID, modulus and public exponent of RSA key */ 445204861Sdes if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) 446204861Sdes != CKR_OK) { 447204861Sdes error("C_GetAttributeValue failed: %lu", rv); 448204861Sdes } else if ((rsa = RSA_new()) == NULL) { 449204861Sdes error("RSA_new failed"); 450204861Sdes } else { 451204861Sdes rsa->n = BN_bin2bn(attribs[1].pValue, 452204861Sdes attribs[1].ulValueLen, NULL); 453204861Sdes rsa->e = BN_bin2bn(attribs[2].pValue, 454204861Sdes attribs[2].ulValueLen, NULL); 455204861Sdes if (rsa->n && rsa->e && 456204861Sdes pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { 457204861Sdes key = key_new(KEY_UNSPEC); 458204861Sdes key->rsa = rsa; 459204861Sdes key->type = KEY_RSA; 460204861Sdes key->flags |= KEY_FLAG_EXT; 461204861Sdes /* expand key array and add key */ 462204861Sdes *keysp = xrealloc(*keysp, *nkeys + 1, 463204861Sdes sizeof(Key *)); 464204861Sdes (*keysp)[*nkeys] = key; 465204861Sdes *nkeys = *nkeys + 1; 466204861Sdes debug("have %d keys", *nkeys); 467204861Sdes } else { 468204861Sdes RSA_free(rsa); 469204861Sdes } 470204861Sdes } 471204861Sdes for (i = 0; i < 3; i++) 472255767Sdes free(attribs[i].pValue); 473204861Sdes } 474204861Sdes if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) 475204861Sdes error("C_FindObjectsFinal failed: %lu", rv); 476204861Sdes return (0); 477204861Sdes} 478204861Sdes 479204861Sdes/* register a new provider, fails if provider already exists */ 480204861Sdesint 481204861Sdespkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) 482204861Sdes{ 483204861Sdes int nkeys, need_finalize = 0; 484204861Sdes struct pkcs11_provider *p = NULL; 485204861Sdes void *handle = NULL; 486204861Sdes CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); 487204861Sdes CK_RV rv; 488204861Sdes CK_FUNCTION_LIST *f = NULL; 489204861Sdes CK_TOKEN_INFO *token; 490204861Sdes CK_ULONG i; 491204861Sdes 492204861Sdes *keyp = NULL; 493204861Sdes if (pkcs11_provider_lookup(provider_id) != NULL) { 494204861Sdes error("provider already registered: %s", provider_id); 495204861Sdes goto fail; 496204861Sdes } 497204861Sdes /* open shared pkcs11-libarary */ 498204861Sdes if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { 499204861Sdes error("dlopen %s failed: %s", provider_id, dlerror()); 500204861Sdes goto fail; 501204861Sdes } 502204861Sdes if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { 503204861Sdes error("dlsym(C_GetFunctionList) failed: %s", dlerror()); 504204861Sdes goto fail; 505204861Sdes } 506204861Sdes p = xcalloc(1, sizeof(*p)); 507204861Sdes p->name = xstrdup(provider_id); 508204861Sdes p->handle = handle; 509204861Sdes /* setup the pkcs11 callbacks */ 510204861Sdes if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { 511204861Sdes error("C_GetFunctionList failed: %lu", rv); 512204861Sdes goto fail; 513204861Sdes } 514204861Sdes p->function_list = f; 515204861Sdes if ((rv = f->C_Initialize(NULL)) != CKR_OK) { 516204861Sdes error("C_Initialize failed: %lu", rv); 517204861Sdes goto fail; 518204861Sdes } 519204861Sdes need_finalize = 1; 520204861Sdes if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { 521204861Sdes error("C_GetInfo failed: %lu", rv); 522204861Sdes goto fail; 523204861Sdes } 524204861Sdes rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); 525204861Sdes rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); 526204861Sdes debug("manufacturerID <%s> cryptokiVersion %d.%d" 527204861Sdes " libraryDescription <%s> libraryVersion %d.%d", 528204861Sdes p->info.manufacturerID, 529204861Sdes p->info.cryptokiVersion.major, 530204861Sdes p->info.cryptokiVersion.minor, 531204861Sdes p->info.libraryDescription, 532204861Sdes p->info.libraryVersion.major, 533204861Sdes p->info.libraryVersion.minor); 534204861Sdes if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { 535204861Sdes error("C_GetSlotList failed: %lu", rv); 536204861Sdes goto fail; 537204861Sdes } 538204861Sdes if (p->nslots == 0) { 539204861Sdes error("no slots"); 540204861Sdes goto fail; 541204861Sdes } 542204861Sdes p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); 543204861Sdes if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) 544204861Sdes != CKR_OK) { 545204861Sdes error("C_GetSlotList failed: %lu", rv); 546204861Sdes goto fail; 547204861Sdes } 548204861Sdes p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); 549204861Sdes p->valid = 1; 550204861Sdes nkeys = 0; 551204861Sdes for (i = 0; i < p->nslots; i++) { 552204861Sdes token = &p->slotinfo[i].token; 553204861Sdes if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) 554204861Sdes != CKR_OK) { 555204861Sdes error("C_GetTokenInfo failed: %lu", rv); 556204861Sdes continue; 557204861Sdes } 558204861Sdes rmspace(token->label, sizeof(token->label)); 559204861Sdes rmspace(token->manufacturerID, sizeof(token->manufacturerID)); 560204861Sdes rmspace(token->model, sizeof(token->model)); 561204861Sdes rmspace(token->serialNumber, sizeof(token->serialNumber)); 562204861Sdes debug("label <%s> manufacturerID <%s> model <%s> serial <%s>" 563204861Sdes " flags 0x%lx", 564204861Sdes token->label, token->manufacturerID, token->model, 565204861Sdes token->serialNumber, token->flags); 566204861Sdes /* open session, login with pin and retrieve public keys */ 567204861Sdes if (pkcs11_open_session(p, i, pin) == 0) 568204861Sdes pkcs11_fetch_keys(p, i, keyp, &nkeys); 569204861Sdes } 570204861Sdes if (nkeys > 0) { 571204861Sdes TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); 572204861Sdes p->refcount++; /* add to provider list */ 573204861Sdes return (nkeys); 574204861Sdes } 575204861Sdes error("no keys"); 576204861Sdes /* don't add the provider, since it does not have any keys */ 577204861Sdesfail: 578204861Sdes if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) 579204861Sdes error("C_Finalize failed: %lu", rv); 580204861Sdes if (p) { 581255767Sdes free(p->slotlist); 582255767Sdes free(p->slotinfo); 583255767Sdes free(p); 584204861Sdes } 585204861Sdes if (handle) 586204861Sdes dlclose(handle); 587204861Sdes return (-1); 588204861Sdes} 589204861Sdes 590226046Sdes#else 591226046Sdes 592226046Sdesint 593226046Sdespkcs11_init(int interactive) 594226046Sdes{ 595226046Sdes return (0); 596226046Sdes} 597226046Sdes 598226046Sdesvoid 599226046Sdespkcs11_terminate(void) 600226046Sdes{ 601226046Sdes return; 602226046Sdes} 603226046Sdes 604204861Sdes#endif /* ENABLE_PKCS11 */ 605