1/* 2 neon PKCS#11 support 3 Copyright (C) 2008, Joe Orton <joe@manyfish.co.uk> 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public 7 License as published by the Free Software Foundation; either 8 version 2 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public 16 License along with this library; if not, write to the Free 17 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 18 MA 02111-1307, USA 19*/ 20 21#include "config.h" 22 23#include "ne_pkcs11.h" 24 25#ifdef HAVE_PAKCHOIS 26#include <string.h> 27 28#include <pakchois.h> 29 30#include "ne_internal.h" 31#include "ne_alloc.h" 32#include "ne_private.h" 33#include "ne_privssl.h" 34 35struct ne_ssl_pkcs11_provider_s { 36 pakchois_module_t *module; 37 ne_ssl_pkcs11_pin_fn pin_fn; 38 void *pin_data; 39 pakchois_session_t *session; 40 ne_ssl_client_cert *clicert; 41 ck_object_handle_t privkey; 42 ck_key_type_t keytype; 43}; 44 45/* To do list for PKCS#11 support: 46 47 - propagate error strings back to ne_session; use new 48 pakchois_error() for pakchois API 0.2 49 - add API to specify a particular slot number to use for clicert 50 - add API to specify a particular cert ID for clicert 51 - find a certificate which has an issuer matching the 52 CA dnames given by GnuTLS 53 - make sure subject name matches between pubkey and privkey 54 - check error handling & fail gracefully if the token is 55 ejected mid-session 56 - add API to enumerate/search provided certs and allow 57 direct choice? (or just punt) 58 - the session<->provider interface requires that 59 one clicert is used for all sessions. remove this limitation 60 - add API to import all CA certs as trusted 61 (CKA_CERTIFICATE_CATEGORY seems to be unused unfortunately; 62 just add all X509 certs with CKA_TRUSTED set to true)) 63 - make DSA work 64 65*/ 66 67#ifdef HAVE_OPENSSL 68 69#include <openssl/rsa.h> 70#include <openssl/err.h> 71 72#define PK11_RSA_ERR (RSA_F_RSA_EAY_PRIVATE_ENCRYPT) 73 74/* RSA_METHOD ->rsa_sign calback. */ 75static int pk11_rsa_sign(int type, 76 const unsigned char *m, unsigned int mlen, 77 unsigned char *sigret, unsigned int *siglen, 78 const RSA *r) 79{ 80 ne_ssl_pkcs11_provider *prov = (ne_ssl_pkcs11_provider *)r->meth->app_data; 81 ck_rv_t rv; 82 struct ck_mechanism mech; 83 unsigned long len; 84 85 if (!prov->session || prov->privkey == CK_INVALID_HANDLE) { 86 NE_DEBUG(NE_DBG_SSL, "pk11: Cannot sign, no session/key.\n"); 87 RSAerr(PK11_RSA_ERR,ERR_R_RSA_LIB); 88 return 0; 89 } 90 91 mech.mechanism = CKM_RSA_PKCS; 92 mech.parameter = NULL; 93 mech.parameter_len = 0; 94 95 /* Initialize signing operation; using the private key discovered 96 * earlier. */ 97 rv = pakchois_sign_init(prov->session, &mech, prov->privkey); 98 if (rv != CKR_OK) { 99 NE_DEBUG(NE_DBG_SSL, "pk11: SignInit failed: %lx.\n", rv); 100 RSAerr(PK11_RSA_ERR, ERR_R_RSA_LIB); 101 return 0; 102 } 103 104 len = *siglen = RSA_size(r); 105 rv = pakchois_sign(prov->session, (unsigned char *)m, mlen, sigret, &len); 106 if (rv != CKR_OK) { 107 NE_DEBUG(NE_DBG_SSL, "pk11: Sign failed.\n"); 108 RSAerr(PK11_RSA_ERR, ERR_R_RSA_LIB); 109 return 0; 110 } 111 112 NE_DEBUG(NE_DBG_SSL, "pk11: Signed successfully.\n"); 113 return 1; 114} 115 116/* RSA_METHOD ->rsa_init implementation; called during RSA_new(rsa). */ 117static int pk11_rsa_init(RSA *rsa) 118{ 119 /* Ensures that RSA_sign() uses meth->rsa_sign: */ 120 rsa->flags |= RSA_FLAG_SIGN_VER; 121 return 1; 122} 123 124/* RSA_METHOD ->rsa_finish implementation; called during 125 * RSA_free(rsa). */ 126static int pk11_rsa_finish(RSA *rsa) 127{ 128 RSA_METHOD *meth = (RSA_METHOD *)rsa->meth; 129 130 /* Freeing the dynamically allocated method here works as well as 131 * doing anything else: */ 132 ne_free(meth); 133 /* Does not appear that rsa->meth will be used after this, but in 134 * case it is, ensure a NULL pointer dereference rather than a 135 * random pointer dereference. */ 136 rsa->meth = NULL; 137 138 return 0; 139} 140 141/* Return an RSA_METHOD which will use the PKCS#11 provider to 142 * implement the signing operation. */ 143static RSA_METHOD *pk11_rsa_method(ne_ssl_pkcs11_provider *prov) 144{ 145 RSA_METHOD *m = ne_calloc(sizeof *m); 146 147 m->name = "neon PKCS#11"; 148 m->rsa_sign = pk11_rsa_sign; 149 150 m->init = pk11_rsa_init; 151 m->finish = pk11_rsa_finish; 152 153 /* This is hopefully under complete control of the RSA_METHOD, 154 * otherwise there is nowhere to put this. */ 155 m->app_data = (char *)prov; 156 157 m->flags = RSA_METHOD_FLAG_NO_CHECK; 158 159 return m; 160} 161#endif 162 163static int pk11_find_x509(ne_ssl_pkcs11_provider *prov, 164 pakchois_session_t *pks, 165 unsigned char *certid, unsigned long *cid_len) 166{ 167 struct ck_attribute a[3]; 168 ck_object_class_t class; 169 ck_certificate_type_t type; 170 ck_rv_t rv; 171 ck_object_handle_t obj; 172 unsigned long count; 173 int found = 0; 174 175 /* Find objects with cert class and X.509 cert type. */ 176 class = CKO_CERTIFICATE; 177 type = CKC_X_509; 178 179 a[0].type = CKA_CLASS; 180 a[0].value = &class; 181 a[0].value_len = sizeof class; 182 a[1].type = CKA_CERTIFICATE_TYPE; 183 a[1].value = &type; 184 a[1].value_len = sizeof type; 185 186 rv = pakchois_find_objects_init(pks, a, 2); 187 if (rv != CKR_OK) { 188 NE_DEBUG(NE_DBG_SSL, "pk11: FindObjectsInit failed.\n"); 189 return 0; 190 } 191 192 while (pakchois_find_objects(pks, &obj, 1, &count) == CKR_OK 193 && count == 1) { 194 unsigned char value[8192], subject[8192]; 195 196 a[0].type = CKA_VALUE; 197 a[0].value = value; 198 a[0].value_len = sizeof value; 199 a[1].type = CKA_ID; 200 a[1].value = certid; 201 a[1].value_len = *cid_len; 202 a[2].type = CKA_SUBJECT; 203 a[2].value = subject; 204 a[2].value_len = sizeof subject; 205 206 if (pakchois_get_attribute_value(pks, obj, a, 3) == CKR_OK) { 207 ne_ssl_client_cert *cc; 208 209#ifdef HAVE_GNUTLS 210 cc = ne__ssl_clicert_exkey_import(value, a[0].value_len); 211#else 212 cc = ne__ssl_clicert_exkey_import(value, a[0].value_len, pk11_rsa_method(prov)); 213#endif 214 if (cc) { 215 NE_DEBUG(NE_DBG_SSL, "pk11: Imported X.509 cert.\n"); 216 prov->clicert = cc; 217 found = 1; 218 *cid_len = a[1].value_len; 219 break; 220 } 221 } 222 else { 223 NE_DEBUG(NE_DBG_SSL, "pk11: Skipped cert, missing attrs.\n"); 224 } 225 } 226 227 pakchois_find_objects_final(pks); 228 return found; 229} 230 231#ifdef HAVE_OPENSSL 232/* No DSA support for OpenSSL (yet, anyway). */ 233#define KEYTYPE_IS_DSA(kt) (0) 234#else 235#define KEYTYPE_IS_DSA(kt) (kt == CKK_DSA) 236#endif 237 238static int pk11_find_pkey(ne_ssl_pkcs11_provider *prov, 239 pakchois_session_t *pks, 240 unsigned char *certid, unsigned long cid_len) 241{ 242 struct ck_attribute a[3]; 243 ck_object_class_t class; 244 ck_rv_t rv; 245 ck_object_handle_t obj; 246 unsigned long count; 247 int found = 0; 248 249 class = CKO_PRIVATE_KEY; 250 251 /* Find an object with private key class and a certificate ID 252 * which matches the certificate. */ 253 /* FIXME: also match the cert subject. */ 254 a[0].type = CKA_CLASS; 255 a[0].value = &class; 256 a[0].value_len = sizeof class; 257 a[1].type = CKA_ID; 258 a[1].value = certid; 259 a[1].value_len = cid_len; 260 261 rv = pakchois_find_objects_init(pks, a, 2); 262 if (rv != CKR_OK) { 263 NE_DEBUG(NE_DBG_SSL, "pk11: FindObjectsInit failed.\n"); 264 /* TODO: error propagation */ 265 return 0; 266 } 267 268 rv = pakchois_find_objects(pks, &obj, 1, &count); 269 if (rv == CKR_OK && count == 1) { 270 NE_DEBUG(NE_DBG_SSL, "pk11: Found private key.\n"); 271 272 a[0].type = CKA_KEY_TYPE; 273 a[0].value = &prov->keytype; 274 a[0].value_len = sizeof prov->keytype; 275 276 if (pakchois_get_attribute_value(pks, obj, a, 1) == CKR_OK 277 && (prov->keytype == CKK_RSA || KEYTYPE_IS_DSA(prov->keytype))) { 278 found = 1; 279 prov->privkey = obj; 280 } 281 else { 282 NE_DEBUG(NE_DBG_SSL, "pk11: Could not determine key type.\n"); 283 } 284 } 285 286 pakchois_find_objects_final(pks); 287 288 return found; 289} 290 291static int find_client_cert(ne_ssl_pkcs11_provider *prov, 292 pakchois_session_t *pks) 293{ 294 unsigned char certid[8192]; 295 unsigned long cid_len = sizeof certid; 296 297 /* TODO: match cert subject too. */ 298 return pk11_find_x509(prov, pks, certid, &cid_len) 299 && pk11_find_pkey(prov, pks, certid, cid_len); 300} 301 302#ifdef HAVE_GNUTLS 303/* Callback invoked by GnuTLS to provide the signature. The signature 304 * operation is handled here by the PKCS#11 provider. */ 305static int pk11_sign_callback(gnutls_session_t session, 306 void *userdata, 307 gnutls_certificate_type_t cert_type, 308 const gnutls_datum_t *cert, 309 const gnutls_datum_t *hash, 310 gnutls_datum_t *signature) 311{ 312 ne_ssl_pkcs11_provider *prov = userdata; 313 ck_rv_t rv; 314 struct ck_mechanism mech; 315 unsigned long siglen; 316 317 if (!prov->session || prov->privkey == CK_INVALID_HANDLE) { 318 NE_DEBUG(NE_DBG_SSL, "pk11: Cannot sign, no session/key.\n"); 319 return GNUTLS_E_NO_CERTIFICATE_FOUND; 320 } 321 322 mech.mechanism = prov->keytype == CKK_DSA ? CKM_DSA : CKM_RSA_PKCS; 323 mech.parameter = NULL; 324 mech.parameter_len = 0; 325 326 /* Initialize signing operation; using the private key discovered 327 * earlier. */ 328 rv = pakchois_sign_init(prov->session, &mech, prov->privkey); 329 if (rv != CKR_OK) { 330 NE_DEBUG(NE_DBG_SSL, "pk11: SignInit failed: %lx.\n", rv); 331 return GNUTLS_E_PK_SIGN_FAILED; 332 } 333 334 /* Work out how long the signature must be: */ 335 rv = pakchois_sign(prov->session, hash->data, hash->size, NULL, &siglen); 336 if (rv != CKR_OK) { 337 NE_DEBUG(NE_DBG_SSL, "pk11: Sign1 failed.\n"); 338 return GNUTLS_E_PK_SIGN_FAILED; 339 } 340 341 signature->data = gnutls_malloc(siglen); 342 signature->size = siglen; 343 344 rv = pakchois_sign(prov->session, hash->data, hash->size, 345 signature->data, &siglen); 346 if (rv != CKR_OK) { 347 NE_DEBUG(NE_DBG_SSL, "pk11: Sign2 failed.\n"); 348 return GNUTLS_E_PK_SIGN_FAILED; 349 } 350 351 NE_DEBUG(NE_DBG_SSL, "pk11: Signed successfully.\n"); 352 353 return 0; 354} 355#endif 356 357static void terminate_string(unsigned char *str, size_t len) 358{ 359 unsigned char *ptr = str + len - 1; 360 361 while ((*ptr == ' ' || *ptr == '\t' || *ptr == '\0') && ptr >= str) 362 ptr--; 363 364 if (ptr == str - 1) 365 str[0] = '\0'; 366 else if (ptr == str + len - 1) 367 str[len-1] = '\0'; 368 else 369 ptr[1] = '\0'; 370} 371 372static int pk11_login(ne_ssl_pkcs11_provider *prov, ck_slot_id_t slot_id, 373 pakchois_session_t *pks, struct ck_slot_info *sinfo) 374{ 375 struct ck_token_info tinfo; 376 int attempt = 0; 377 ck_rv_t rv; 378 379 if (pakchois_get_token_info(prov->module, slot_id, &tinfo) != CKR_OK) { 380 NE_DEBUG(NE_DBG_SSL, "pk11: GetTokenInfo failed\n"); 381 /* TODO: propagate error. */ 382 return -1; 383 } 384 385 if ((tinfo.flags & CKF_LOGIN_REQUIRED) == 0) { 386 NE_DEBUG(NE_DBG_SSL, "pk11: No login required.\n"); 387 return 0; 388 } 389 390 /* For a token with a "protected" (out-of-band) authentication 391 * path, calling login with a NULL username is all that is 392 * required. */ 393 if (tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) { 394 if (pakchois_login(pks, CKU_USER, NULL, 0) == CKR_OK) { 395 return 0; 396 } 397 else { 398 NE_DEBUG(NE_DBG_SSL, "pk11: Protected login failed.\n"); 399 /* TODO: error propagation. */ 400 return -1; 401 } 402 } 403 404 /* Otherwise, PIN entry is necessary for login, so fail if there's 405 * no callback. */ 406 if (!prov->pin_fn) { 407 NE_DEBUG(NE_DBG_SSL, "pk11: No pin callback but login required.\n"); 408 /* TODO: propagate error. */ 409 return -1; 410 } 411 412 terminate_string(sinfo->slot_description, sizeof sinfo->slot_description); 413 414 do { 415 char pin[NE_SSL_P11PINLEN]; 416 unsigned int flags = 0; 417 418 /* If login has been attempted once already, check the token 419 * status again, the flags might change. */ 420 if (attempt) { 421 if (pakchois_get_token_info(prov->module, slot_id, 422 &tinfo) != CKR_OK) { 423 NE_DEBUG(NE_DBG_SSL, "pk11: GetTokenInfo failed\n"); 424 /* TODO: propagate error. */ 425 return -1; 426 } 427 } 428 429 if (tinfo.flags & CKF_USER_PIN_COUNT_LOW) 430 flags |= NE_SSL_P11PIN_COUNT_LOW; 431 if (tinfo.flags & CKF_USER_PIN_FINAL_TRY) 432 flags |= NE_SSL_P11PIN_FINAL_TRY; 433 434 terminate_string(tinfo.label, sizeof tinfo.label); 435 436 if (prov->pin_fn(prov->pin_data, attempt++, 437 (char *)sinfo->slot_description, 438 (char *)tinfo.label, flags, pin)) { 439 return -1; 440 } 441 442 rv = pakchois_login(pks, CKU_USER, (unsigned char *)pin, strlen(pin)); 443 444 /* Try to scrub the pin off the stack. Clever compilers will 445 * probably optimize this away, oh well. */ 446 memset(pin, 0, sizeof pin); 447 } while (rv == CKR_PIN_INCORRECT); 448 449 NE_DEBUG(NE_DBG_SSL, "pk11: Login result = %lu\n", rv); 450 451 return (rv == CKR_OK || rv == CKR_USER_ALREADY_LOGGED_IN) ? 0 : -1; 452} 453 454static void pk11_provide(void *userdata, ne_session *sess, 455 const ne_ssl_dname *const *dnames, 456 int dncount) 457{ 458 ne_ssl_pkcs11_provider *prov = userdata; 459 ck_slot_id_t *slots; 460 unsigned long scount, n; 461 462 if (prov->clicert) { 463 NE_DEBUG(NE_DBG_SSL, "pk11: Using existing clicert.\n"); 464 ne_ssl_set_clicert(sess, prov->clicert); 465 return; 466 } 467 468 if (pakchois_get_slot_list(prov->module, 1, NULL, &scount) != CKR_OK 469 || scount == 0) { 470 NE_DEBUG(NE_DBG_SSL, "pk11: No slots.\n"); 471 /* TODO: propagate error. */ 472 return; 473 } 474 475 slots = ne_malloc(scount * sizeof *slots); 476 if (pakchois_get_slot_list(prov->module, 1, slots, &scount) != CKR_OK) { 477 ne_free(slots); 478 NE_DEBUG(NE_DBG_SSL, "pk11: Really, no slots?\n"); 479 /* TODO: propagate error. */ 480 return; 481 } 482 483 NE_DEBUG(NE_DBG_SSL, "pk11: Found %ld slots.\n", scount); 484 485 for (n = 0; n < scount; n++) { 486 pakchois_session_t *pks; 487 ck_rv_t rv; 488 struct ck_slot_info sinfo; 489 490 if (pakchois_get_slot_info(prov->module, slots[n], &sinfo) != CKR_OK) { 491 NE_DEBUG(NE_DBG_SSL, "pk11: GetSlotInfo failed\n"); 492 continue; 493 } 494 495 if ((sinfo.flags & CKF_TOKEN_PRESENT) == 0) { 496 NE_DEBUG(NE_DBG_SSL, "pk11: slot empty, ignoring\n"); 497 continue; 498 } 499 500 rv = pakchois_open_session(prov->module, slots[n], 501 CKF_SERIAL_SESSION, 502 NULL, NULL, &pks); 503 if (rv != CKR_OK) { 504 NE_DEBUG(NE_DBG_SSL, "pk11: could not open slot, %ld (%ld: %ld)\n", 505 rv, n, slots[n]); 506 continue; 507 } 508 509 if (pk11_login(prov, slots[n], pks, &sinfo) == 0) { 510 if (find_client_cert(prov, pks)) { 511 NE_DEBUG(NE_DBG_SSL, "pk11: Setup complete.\n"); 512 prov->session = pks; 513 ne_ssl_set_clicert(sess, prov->clicert); 514 ne_free(slots); 515 return; 516 } 517 } 518 519 pakchois_close_session(pks); 520 } 521 522 ne_free(slots); 523} 524 525static int pk11_init(ne_ssl_pkcs11_provider **provider, 526 pakchois_module_t *module) 527{ 528 ne_ssl_pkcs11_provider *prov; 529 530 prov = *provider = ne_calloc(sizeof *prov); 531 prov->module = module; 532 prov->privkey = CK_INVALID_HANDLE; 533 534 return NE_PK11_OK; 535} 536 537int ne_ssl_pkcs11_provider_init(ne_ssl_pkcs11_provider **provider, 538 const char *name) 539{ 540 pakchois_module_t *pm; 541 542 if (pakchois_module_load(&pm, name) == CKR_OK) { 543 return pk11_init(provider, pm); 544 } 545 else { 546 return NE_PK11_FAILED; 547 } 548} 549 550int ne_ssl_pkcs11_nss_provider_init(ne_ssl_pkcs11_provider **provider, 551 const char *name, const char *directory, 552 const char *cert_prefix, 553 const char *key_prefix, 554 const char *secmod_db) 555{ 556 pakchois_module_t *pm; 557 558 if (pakchois_module_nssload(&pm, name, directory, cert_prefix, 559 key_prefix, secmod_db) == CKR_OK) { 560 return pk11_init(provider, pm); 561 } 562 else { 563 return NE_PK11_FAILED; 564 } 565} 566 567void ne_ssl_pkcs11_provider_pin(ne_ssl_pkcs11_provider *provider, 568 ne_ssl_pkcs11_pin_fn fn, 569 void *userdata) 570{ 571 provider->pin_fn = fn; 572 provider->pin_data = userdata; 573} 574 575void ne_ssl_set_pkcs11_provider(ne_session *sess, 576 ne_ssl_pkcs11_provider *provider) 577{ 578#ifdef HAVE_GNUTLS 579 sess->ssl_context->sign_func = pk11_sign_callback; 580 sess->ssl_context->sign_data = provider; 581#endif 582 583 ne_ssl_provide_clicert(sess, pk11_provide, provider); 584} 585 586void ne_ssl_pkcs11_provider_destroy(ne_ssl_pkcs11_provider *prov) 587{ 588 if (prov->session) { 589 pakchois_close_session(prov->session); 590 } 591 if (prov->clicert) { 592 ne_ssl_clicert_free(prov->clicert); 593 } 594 pakchois_module_destroy(prov->module); 595 ne_free(prov); 596} 597 598#else /* !HAVE_PAKCHOIS */ 599 600int ne_ssl_pkcs11_provider_init(ne_ssl_pkcs11_provider **provider, 601 const char *name) 602{ 603 return NE_PK11_NOTIMPL; 604} 605 606int ne_ssl_pkcs11_nss_provider_init(ne_ssl_pkcs11_provider **provider, 607 const char *name, const char *directory, 608 const char *cert_prefix, 609 const char *key_prefix, 610 const char *secmod_db) 611{ 612 return NE_PK11_NOTIMPL; 613} 614 615void ne_ssl_pkcs11_provider_destroy(ne_ssl_pkcs11_provider *provider) { } 616 617void ne_ssl_pkcs11_provider_pin(ne_ssl_pkcs11_provider *provider, 618 ne_ssl_pkcs11_pin_fn fn, 619 void *userdata) { } 620 621void ne_ssl_set_pkcs11_provider(ne_session *sess, 622 ne_ssl_pkcs11_provider *provider) { } 623 624#endif /* HAVE_PAKCHOIS */ 625 626