1263970Sdes/* $OpenBSD: ssh-pkcs11.c,v 1.11 2013/11/13 13:48:20 markus 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 34263970Sdes#include <openssl/x509.h> 35263970Sdes 36204861Sdes#define CRYPTOKI_COMPAT 37204861Sdes#include "pkcs11.h" 38204861Sdes 39204861Sdes#include "log.h" 40204861Sdes#include "misc.h" 41204861Sdes#include "key.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); 125263970Sdes free(p->slotlist); 126263970Sdes free(p->slotinfo); 127263970Sdes 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); 185263970Sdes free(k11->keyid); 186263970Sdes 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; 230263970Sdes 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 }; 240204861Sdes char *pin, 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) { 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 */ 266263970Sdes if ((rv = f->C_Login(si->session, CKU_USER, 267263970Sdes (u_char *)pin, strlen(pin))) != CKR_OK) { 268263970Sdes free(pin); 269204861Sdes error("C_Login failed: %lu", rv); 270204861Sdes return (-1); 271204861Sdes } 272263970Sdes 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 331263970Sdesrmspace(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) { 369263970Sdes if ((rv = f->C_Login(session, CKU_USER, 370263970Sdes (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 */ 387263970Sdesstatic int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, 388263970Sdes CK_ATTRIBUTE [], CK_ATTRIBUTE [3], Key ***, int *) 389263970Sdes __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); 390263970Sdes 391204861Sdesstatic int 392263970Sdespkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, 393263970Sdes Key ***keysp, int *nkeys) 394204861Sdes{ 395263970Sdes CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; 396263970Sdes CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; 397263970Sdes CK_ATTRIBUTE pubkey_filter[] = { 398263970Sdes { CKA_CLASS, NULL, sizeof(pubkey_class) } 399263970Sdes }; 400263970Sdes CK_ATTRIBUTE cert_filter[] = { 401263970Sdes { CKA_CLASS, NULL, sizeof(cert_class) } 402263970Sdes }; 403263970Sdes CK_ATTRIBUTE pubkey_attribs[] = { 404263970Sdes { CKA_ID, NULL, 0 }, 405263970Sdes { CKA_MODULUS, NULL, 0 }, 406263970Sdes { CKA_PUBLIC_EXPONENT, NULL, 0 } 407263970Sdes }; 408263970Sdes CK_ATTRIBUTE cert_attribs[] = { 409263970Sdes { CKA_ID, NULL, 0 }, 410263970Sdes { CKA_SUBJECT, NULL, 0 }, 411263970Sdes { CKA_VALUE, NULL, 0 } 412263970Sdes }; 413263970Sdes pubkey_filter[0].pValue = &pubkey_class; 414263970Sdes cert_filter[0].pValue = &cert_class; 415263970Sdes 416263970Sdes if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs, 417263970Sdes keysp, nkeys) < 0 || 418263970Sdes pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs, 419263970Sdes keysp, nkeys) < 0) 420263970Sdes return (-1); 421263970Sdes return (0); 422263970Sdes} 423263970Sdes 424263970Sdesstatic int 425263970Sdespkcs11_key_included(Key ***keysp, int *nkeys, Key *key) 426263970Sdes{ 427263970Sdes int i; 428263970Sdes 429263970Sdes for (i = 0; i < *nkeys; i++) 430263970Sdes if (key_equal(key, (*keysp)[i])) 431263970Sdes return (1); 432263970Sdes return (0); 433263970Sdes} 434263970Sdes 435263970Sdesstatic int 436263970Sdespkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, 437263970Sdes CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], 438263970Sdes Key ***keysp, int *nkeys) 439263970Sdes{ 440204861Sdes Key *key; 441204861Sdes RSA *rsa; 442263970Sdes X509 *x509; 443263970Sdes EVP_PKEY *evp; 444204861Sdes int i; 445263970Sdes const u_char *cp; 446204861Sdes CK_RV rv; 447204861Sdes CK_OBJECT_HANDLE obj; 448204861Sdes CK_ULONG nfound; 449204861Sdes CK_SESSION_HANDLE session; 450204861Sdes CK_FUNCTION_LIST *f; 451204861Sdes 452204861Sdes f = p->function_list; 453204861Sdes session = p->slotinfo[slotidx].session; 454204861Sdes /* setup a filter the looks for public keys */ 455263970Sdes if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) { 456204861Sdes error("C_FindObjectsInit failed: %lu", rv); 457204861Sdes return (-1); 458204861Sdes } 459204861Sdes while (1) { 460204861Sdes /* XXX 3 attributes in attribs[] */ 461204861Sdes for (i = 0; i < 3; i++) { 462204861Sdes attribs[i].pValue = NULL; 463204861Sdes attribs[i].ulValueLen = 0; 464204861Sdes } 465204861Sdes if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK 466204861Sdes || nfound == 0) 467204861Sdes break; 468204861Sdes /* found a key, so figure out size of the attributes */ 469204861Sdes if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) 470204861Sdes != CKR_OK) { 471204861Sdes error("C_GetAttributeValue failed: %lu", rv); 472204861Sdes continue; 473204861Sdes } 474215116Sdes /* check that none of the attributes are zero length */ 475215116Sdes if (attribs[0].ulValueLen == 0 || 476215116Sdes attribs[1].ulValueLen == 0 || 477215116Sdes attribs[2].ulValueLen == 0) { 478215116Sdes continue; 479215116Sdes } 480215116Sdes /* allocate buffers for attributes */ 481204861Sdes for (i = 0; i < 3; i++) 482204861Sdes attribs[i].pValue = xmalloc(attribs[i].ulValueLen); 483263970Sdes /* 484263970Sdes * retrieve ID, modulus and public exponent of RSA key, 485263970Sdes * or ID, subject and value for certificates. 486263970Sdes */ 487263970Sdes rsa = NULL; 488204861Sdes if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) 489204861Sdes != CKR_OK) { 490204861Sdes error("C_GetAttributeValue failed: %lu", rv); 491263970Sdes } else if (attribs[1].type == CKA_MODULUS ) { 492263970Sdes if ((rsa = RSA_new()) == NULL) { 493263970Sdes error("RSA_new failed"); 494263970Sdes } else { 495263970Sdes rsa->n = BN_bin2bn(attribs[1].pValue, 496263970Sdes attribs[1].ulValueLen, NULL); 497263970Sdes rsa->e = BN_bin2bn(attribs[2].pValue, 498263970Sdes attribs[2].ulValueLen, NULL); 499263970Sdes } 500204861Sdes } else { 501263970Sdes cp = attribs[2].pValue; 502263970Sdes if ((x509 = X509_new()) == NULL) { 503263970Sdes error("X509_new failed"); 504263970Sdes } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen) 505263970Sdes == NULL) { 506263970Sdes error("d2i_X509 failed"); 507263970Sdes } else if ((evp = X509_get_pubkey(x509)) == NULL || 508263970Sdes evp->type != EVP_PKEY_RSA || 509263970Sdes evp->pkey.rsa == NULL) { 510263970Sdes debug("X509_get_pubkey failed or no rsa"); 511263970Sdes } else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa)) 512263970Sdes == NULL) { 513263970Sdes error("RSAPublicKey_dup"); 514263970Sdes } 515263970Sdes if (x509) 516263970Sdes X509_free(x509); 517263970Sdes } 518263970Sdes if (rsa && rsa->n && rsa->e && 519263970Sdes pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { 520263970Sdes key = key_new(KEY_UNSPEC); 521263970Sdes key->rsa = rsa; 522263970Sdes key->type = KEY_RSA; 523263970Sdes key->flags |= KEY_FLAG_EXT; 524263970Sdes if (pkcs11_key_included(keysp, nkeys, key)) { 525263970Sdes key_free(key); 526263970Sdes } else { 527204861Sdes /* expand key array and add key */ 528204861Sdes *keysp = xrealloc(*keysp, *nkeys + 1, 529204861Sdes sizeof(Key *)); 530204861Sdes (*keysp)[*nkeys] = key; 531204861Sdes *nkeys = *nkeys + 1; 532204861Sdes debug("have %d keys", *nkeys); 533204861Sdes } 534263970Sdes } else if (rsa) { 535263970Sdes RSA_free(rsa); 536204861Sdes } 537204861Sdes for (i = 0; i < 3; i++) 538263970Sdes free(attribs[i].pValue); 539204861Sdes } 540204861Sdes if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) 541204861Sdes error("C_FindObjectsFinal failed: %lu", rv); 542204861Sdes return (0); 543204861Sdes} 544204861Sdes 545204861Sdes/* register a new provider, fails if provider already exists */ 546204861Sdesint 547204861Sdespkcs11_add_provider(char *provider_id, char *pin, Key ***keyp) 548204861Sdes{ 549204861Sdes int nkeys, need_finalize = 0; 550204861Sdes struct pkcs11_provider *p = NULL; 551204861Sdes void *handle = NULL; 552204861Sdes CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); 553204861Sdes CK_RV rv; 554204861Sdes CK_FUNCTION_LIST *f = NULL; 555204861Sdes CK_TOKEN_INFO *token; 556204861Sdes CK_ULONG i; 557204861Sdes 558204861Sdes *keyp = NULL; 559204861Sdes if (pkcs11_provider_lookup(provider_id) != NULL) { 560204861Sdes error("provider already registered: %s", provider_id); 561204861Sdes goto fail; 562204861Sdes } 563204861Sdes /* open shared pkcs11-libarary */ 564204861Sdes if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { 565204861Sdes error("dlopen %s failed: %s", provider_id, dlerror()); 566204861Sdes goto fail; 567204861Sdes } 568204861Sdes if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { 569204861Sdes error("dlsym(C_GetFunctionList) failed: %s", dlerror()); 570204861Sdes goto fail; 571204861Sdes } 572204861Sdes p = xcalloc(1, sizeof(*p)); 573204861Sdes p->name = xstrdup(provider_id); 574204861Sdes p->handle = handle; 575204861Sdes /* setup the pkcs11 callbacks */ 576204861Sdes if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { 577204861Sdes error("C_GetFunctionList failed: %lu", rv); 578204861Sdes goto fail; 579204861Sdes } 580204861Sdes p->function_list = f; 581204861Sdes if ((rv = f->C_Initialize(NULL)) != CKR_OK) { 582204861Sdes error("C_Initialize failed: %lu", rv); 583204861Sdes goto fail; 584204861Sdes } 585204861Sdes need_finalize = 1; 586204861Sdes if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { 587204861Sdes error("C_GetInfo failed: %lu", rv); 588204861Sdes goto fail; 589204861Sdes } 590204861Sdes rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); 591204861Sdes rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); 592204861Sdes debug("manufacturerID <%s> cryptokiVersion %d.%d" 593204861Sdes " libraryDescription <%s> libraryVersion %d.%d", 594204861Sdes p->info.manufacturerID, 595204861Sdes p->info.cryptokiVersion.major, 596204861Sdes p->info.cryptokiVersion.minor, 597204861Sdes p->info.libraryDescription, 598204861Sdes p->info.libraryVersion.major, 599204861Sdes p->info.libraryVersion.minor); 600204861Sdes if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { 601204861Sdes error("C_GetSlotList failed: %lu", rv); 602204861Sdes goto fail; 603204861Sdes } 604204861Sdes if (p->nslots == 0) { 605204861Sdes error("no slots"); 606204861Sdes goto fail; 607204861Sdes } 608204861Sdes p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); 609204861Sdes if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) 610204861Sdes != CKR_OK) { 611204861Sdes error("C_GetSlotList failed: %lu", rv); 612204861Sdes goto fail; 613204861Sdes } 614204861Sdes p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); 615204861Sdes p->valid = 1; 616204861Sdes nkeys = 0; 617204861Sdes for (i = 0; i < p->nslots; i++) { 618204861Sdes token = &p->slotinfo[i].token; 619204861Sdes if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) 620204861Sdes != CKR_OK) { 621204861Sdes error("C_GetTokenInfo failed: %lu", rv); 622204861Sdes continue; 623204861Sdes } 624204861Sdes rmspace(token->label, sizeof(token->label)); 625204861Sdes rmspace(token->manufacturerID, sizeof(token->manufacturerID)); 626204861Sdes rmspace(token->model, sizeof(token->model)); 627204861Sdes rmspace(token->serialNumber, sizeof(token->serialNumber)); 628204861Sdes debug("label <%s> manufacturerID <%s> model <%s> serial <%s>" 629204861Sdes " flags 0x%lx", 630204861Sdes token->label, token->manufacturerID, token->model, 631204861Sdes token->serialNumber, token->flags); 632204861Sdes /* open session, login with pin and retrieve public keys */ 633204861Sdes if (pkcs11_open_session(p, i, pin) == 0) 634204861Sdes pkcs11_fetch_keys(p, i, keyp, &nkeys); 635204861Sdes } 636204861Sdes if (nkeys > 0) { 637204861Sdes TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); 638204861Sdes p->refcount++; /* add to provider list */ 639204861Sdes return (nkeys); 640204861Sdes } 641204861Sdes error("no keys"); 642204861Sdes /* don't add the provider, since it does not have any keys */ 643204861Sdesfail: 644204861Sdes if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) 645204861Sdes error("C_Finalize failed: %lu", rv); 646204861Sdes if (p) { 647263970Sdes free(p->slotlist); 648263970Sdes free(p->slotinfo); 649263970Sdes free(p); 650204861Sdes } 651204861Sdes if (handle) 652204861Sdes dlclose(handle); 653204861Sdes return (-1); 654204861Sdes} 655204861Sdes 656247485Sdes#else 657247485Sdes 658247485Sdesint 659247485Sdespkcs11_init(int interactive) 660247485Sdes{ 661247485Sdes return (0); 662247485Sdes} 663247485Sdes 664247485Sdesvoid 665247485Sdespkcs11_terminate(void) 666247485Sdes{ 667247485Sdes return; 668247485Sdes} 669247485Sdes 670204861Sdes#endif /* ENABLE_PKCS11 */ 671