1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 * 21 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <ctype.h> 29 #include <security/cryptoki.h> 30 #include <kmfapi.h> 31 #include <kmfapiP.h> 32 #include <cryptoutil.h> 33 34 /* 35 * memcmp_pad_max() is a specialized version of memcmp() which 36 * compares two pieces of data up to a maximum length. If the 37 * the two data match up the maximum length, they are considered 38 * matching. Trailing blanks do not cause the match to fail if 39 * one of the data is shorted. 40 * 41 * Examples of matches: 42 * "one" | 43 * "one " | 44 * ^maximum length 45 * 46 * "Number One | X" (X is beyond maximum length) 47 * "Number One " | 48 * ^maximum length 49 * 50 * Examples of mismatches: 51 * " one" 52 * "one" 53 * 54 * "Number One X|" 55 * "Number One |" 56 * ^maximum length 57 */ 58 static int 59 memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz) 60 { 61 uint_t len, extra_len; 62 char *marker; 63 64 /* No point in comparing anything beyond max_sz */ 65 if (d1_len > max_sz) 66 d1_len = max_sz; 67 if (d2_len > max_sz) 68 d2_len = max_sz; 69 70 /* Find shorter of the two data. */ 71 if (d1_len <= d2_len) { 72 len = d1_len; 73 extra_len = d2_len; 74 marker = d2; 75 } else { /* d1_len > d2_len */ 76 len = d2_len; 77 extra_len = d1_len; 78 marker = d1; 79 } 80 81 /* Have a match in the shortest length of data? */ 82 if (memcmp(d1, d2, len) != 0) 83 /* CONSTCOND */ 84 return (1); 85 86 /* If the rest of longer data is nulls or blanks, call it a match. */ 87 while (len < extra_len && marker[len]) 88 if (!isspace(marker[len++])) 89 /* CONSTCOND */ 90 return (1); 91 return (0); 92 } 93 94 static KMF_RETURN 95 kmf_get_token_slots(KMF_HANDLE *handle, CK_SLOT_ID_PTR *slot_list, 96 CK_ULONG *slot_count) 97 { 98 99 KMF_RETURN kmf_rv = KMF_OK; 100 CK_RV ck_rv = CKR_OK; 101 CK_ULONG tmp_count = 0; 102 CK_SLOT_ID_PTR tmp_list = NULL_PTR, tmp2_list = NULL_PTR; 103 104 ck_rv = C_GetSlotList(1, NULL_PTR, &tmp_count); 105 if (ck_rv == CKR_CRYPTOKI_NOT_INITIALIZED) { 106 ck_rv = C_Initialize(NULL); 107 if ((ck_rv != CKR_OK) && 108 (ck_rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) 109 return (KMF_ERR_UNINITIALIZED); 110 if (ck_rv == CKR_CRYPTOKI_ALREADY_INITIALIZED) 111 ck_rv = CKR_OK; 112 113 ck_rv = C_GetSlotList(1, NULL_PTR, &tmp_count); 114 } 115 if (ck_rv != CKR_OK) { 116 if (handle != NULL) { 117 handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; 118 handle->lasterr.errcode = ck_rv; 119 } 120 return (KMF_ERR_INTERNAL); 121 } 122 123 if (tmp_count == 0) { 124 *slot_list = NULL_PTR; 125 *slot_count = 0; 126 return (KMF_OK); 127 } 128 129 /* Allocate initial space for the slot list. */ 130 if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count * 131 sizeof (CK_SLOT_ID))) == NULL) 132 return (KMF_ERR_MEMORY); 133 134 /* Then get the slot list itself. */ 135 for (;;) { 136 ck_rv = C_GetSlotList(1, tmp_list, &tmp_count); 137 if (ck_rv == CKR_OK) { 138 *slot_list = tmp_list; 139 *slot_count = tmp_count; 140 kmf_rv = KMF_OK; 141 break; 142 } 143 144 if (ck_rv != CKR_BUFFER_TOO_SMALL) { 145 free(tmp_list); 146 if (handle != NULL) { 147 handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; 148 handle->lasterr.errcode = ck_rv; 149 } 150 kmf_rv = KMF_ERR_INTERNAL; 151 break; 152 } 153 154 /* 155 * If the number of slots grew, try again. This 156 * is to be consistent with pktool in ONNV. 157 */ 158 if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list, 159 tmp_count * sizeof (CK_SLOT_ID))) == NULL) { 160 free(tmp_list); 161 kmf_rv = KMF_ERR_MEMORY; 162 break; 163 } 164 tmp_list = tmp2_list; 165 } 166 167 return (kmf_rv); 168 } 169 170 /* 171 * Returns pointer to either null-terminator or next unescaped colon. The 172 * string to be extracted starts at the beginning and goes until one character 173 * before this pointer. If NULL is returned, the string itself is NULL. 174 */ 175 static char * 176 find_unescaped_colon(char *str) 177 { 178 char *end; 179 180 if (str == NULL) 181 return (NULL); 182 183 while ((end = strchr(str, ':')) != NULL) { 184 if (end != str && *(end-1) != '\\') 185 return (end); 186 str = end + 1; /* could point to null-terminator */ 187 } 188 if (end == NULL) 189 end = strchr(str, '\0'); 190 return (end); 191 } 192 193 /* 194 * Compresses away any characters escaped with backslash from given string. 195 * The string is altered in-place. Example, "ab\:\\e" becomes "ab:\e". 196 */ 197 static void 198 unescape_str(char *str) 199 { 200 boolean_t escaped = B_FALSE; 201 char *mark; 202 203 if (str == NULL) 204 return; 205 206 for (mark = str; *str != '\0'; str++) { 207 if (*str != '\\' || escaped == B_TRUE) { 208 *mark++ = *str; 209 escaped = B_FALSE; 210 } else { 211 escaped = B_TRUE; 212 } 213 } 214 *mark = '\0'; 215 } 216 217 218 /* 219 * Given a colon-separated token specifier, this functions splits it into 220 * its label, manufacturer ID (if any), and serial number (if any). Literal 221 * colons within the label/manuf/serial can be escaped with a backslash. 222 * Fields can left blank and trailing colons can be omitted, however leading 223 * colons are required as placeholders. For example, these are equivalent: 224 * (a) "lbl", "lbl:", "lbl::" (b) "lbl:man", "lbl:man:" 225 * but these are not: 226 * (c) "man", ":man" (d) "ser", "::ser" 227 * Furthermore, the token label is required always. 228 * 229 * The buffer containing the token specifier is altered by replacing the 230 * colons to null-terminators, and pointers returned are pointers into this 231 * string. No new memory is allocated. 232 */ 233 static int 234 parse_token_spec(char *token_spec, char **token_name, char **manuf_id, 235 char **serial_no) 236 { 237 char *mark; 238 239 if (token_spec == NULL || *token_spec == '\0') { 240 return (-1); 241 } 242 243 *token_name = NULL; 244 *manuf_id = NULL; 245 *serial_no = NULL; 246 247 /* Token label (required) */ 248 mark = find_unescaped_colon(token_spec); 249 *token_name = token_spec; 250 if (*mark != '\0') 251 *mark++ = '\0'; /* mark points to next field, if any */ 252 unescape_str(*token_name); 253 254 if (*(*token_name) == '\0') { /* token label is required */ 255 return (-1); 256 } 257 258 if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */ 259 return (0); 260 token_spec = mark; 261 262 /* Manufacturer identifier (optional) */ 263 mark = find_unescaped_colon(token_spec); 264 *manuf_id = token_spec; 265 if (*mark != '\0') 266 *mark++ = '\0'; /* mark points to next field, if any */ 267 unescape_str(*manuf_id); 268 269 if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */ 270 return (0); 271 token_spec = mark; 272 273 /* Serial number (optional) */ 274 mark = find_unescaped_colon(token_spec); 275 *serial_no = token_spec; 276 if (*mark != '\0') 277 *mark++ = '\0'; /* null-terminate, just in case */ 278 unescape_str(*serial_no); 279 280 return (0); 281 } 282 283 /* 284 * Find slots that match a token identifier. Token labels take the 285 * form of: 286 * token_name:manufacturer:serial_number 287 * manufacterer and serial number are optional. If used, the fields 288 * are delimited by the colon ':' character. 289 */ 290 KMF_RETURN 291 kmf_pk11_token_lookup(KMF_HANDLE_T handle, char *label, CK_SLOT_ID *slot_id) 292 { 293 KMF_RETURN kmf_rv = KMF_OK; 294 CK_RV rv; 295 CK_SLOT_ID_PTR slot_list = NULL; 296 CK_TOKEN_INFO token_info; 297 CK_ULONG slot_count = 0; 298 int i; 299 uint_t len, max_sz; 300 boolean_t metaslot_status_enabled; 301 boolean_t metaslot_migrate_enabled; 302 char *metaslot_slot_info; 303 char *metaslot_token_info; 304 char *tmplabel = NULL; 305 char *token_name = NULL; 306 char *manuf_id = NULL; 307 char *serial_no = NULL; 308 boolean_t tok_match = B_FALSE; 309 boolean_t man_match = B_FALSE; 310 boolean_t ser_match = B_FALSE; 311 312 if (slot_id == NULL || label == NULL || !strlen(label)) 313 return (KMF_ERR_BAD_PARAMETER); 314 315 if (handle == NULL) { 316 rv = C_Initialize(NULL); 317 if ((rv != CKR_OK) && 318 (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) { 319 return (KMF_ERR_UNINITIALIZED); 320 } 321 } 322 323 /* 324 * Parse token specifier into token_name, manuf_id, serial_no. 325 * Token_name is required; manuf_id and serial_no are optional. 326 */ 327 tmplabel = strdup(label); 328 if (tmplabel == NULL) 329 return (KMF_ERR_MEMORY); 330 331 if (parse_token_spec(tmplabel, &token_name, &manuf_id, 332 &serial_no) < 0) { 333 free(tmplabel); 334 return (KMF_ERR_BAD_PARAMETER); 335 } 336 337 /* Get a list of all slots with tokens present. */ 338 kmf_rv = kmf_get_token_slots(handle, &slot_list, &slot_count); 339 if (kmf_rv != KMF_OK) { 340 free(tmplabel); 341 return (kmf_rv); 342 } 343 344 /* If there are no such slots, the desired token won't be found. */ 345 if (slot_count == 0) { 346 free(tmplabel); 347 return (KMF_ERR_TOKEN_NOT_PRESENT); 348 } 349 350 /* Search the slot list for the token. */ 351 for (i = 0; i < slot_count; i++) { 352 if (C_GetTokenInfo(slot_list[i], &token_info) != CKR_OK) { 353 continue; 354 } 355 356 /* See if the token label matches. */ 357 len = strlen(token_name); 358 max_sz = sizeof (token_info.label); 359 if (memcmp_pad_max(&(token_info.label), max_sz, token_name, 360 len, max_sz) == 0) 361 tok_match = B_TRUE; 362 /* 363 * If manufacturer id was given, see if it actually matches. 364 * If no manufacturer id was given, assume match is true. 365 */ 366 if (manuf_id) { 367 len = strlen(manuf_id); 368 max_sz = sizeof ((char *)(token_info.manufacturerID)); 369 if (memcmp_pad_max(&(token_info.manufacturerID), max_sz, 370 manuf_id, len, max_sz) == 0) 371 man_match = B_TRUE; 372 } else { 373 man_match = B_TRUE; 374 } 375 376 /* 377 * If serial number was given, see if it actually matches. 378 * If no serial number was given, assume match is true. 379 */ 380 if (serial_no) { 381 len = strlen(serial_no); 382 max_sz = sizeof ((char *)(token_info.serialNumber)); 383 if (memcmp_pad_max(&(token_info.serialNumber), max_sz, 384 serial_no, len, max_sz) == 0) 385 ser_match = B_TRUE; 386 } else { 387 ser_match = B_TRUE; 388 } 389 390 if (tok_match && man_match && ser_match) 391 break; /* found it! */ 392 } 393 394 if (i < slot_count) { 395 /* found the desired token from the slotlist */ 396 *slot_id = slot_list[i]; 397 free(slot_list); 398 free(tmplabel); 399 return (KMF_OK); 400 } 401 402 /* 403 * If we didn't find the token from the slotlist, check if this token 404 * is the one currently hidden by the metaslot. If that's case, 405 * we can just use the metaslot, the slot 0. 406 */ 407 kmf_rv = get_metaslot_info(&metaslot_status_enabled, 408 &metaslot_migrate_enabled, &metaslot_slot_info, 409 &metaslot_token_info); 410 if (kmf_rv) { 411 /* 412 * Failed to get the metaslot info. This usually means that 413 * metaslot is disabled from the system. 414 */ 415 kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT; 416 } else { 417 max_sz = strlen(metaslot_token_info); 418 if (memcmp_pad_max(metaslot_token_info, max_sz, token_name, len, 419 max_sz) == 0) { 420 *slot_id = slot_list[0]; 421 } else { 422 kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT; 423 } 424 free(metaslot_slot_info); 425 free(metaslot_token_info); 426 } 427 428 free(slot_list); 429 free(tmplabel); 430 return (kmf_rv); 431 } 432 433 KMF_RETURN 434 kmf_set_token_pin(KMF_HANDLE_T handle, 435 int num_attr, 436 KMF_ATTRIBUTE *attrlist) 437 { 438 KMF_RETURN ret = KMF_OK; 439 KMF_PLUGIN *plugin; 440 KMF_ATTRIBUTE_TESTER required_attrs[] = { 441 {KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}, 442 {KMF_CREDENTIAL_ATTR, FALSE, sizeof (KMF_CREDENTIAL), 443 sizeof (KMF_CREDENTIAL)}, 444 {KMF_NEWPIN_ATTR, FALSE, sizeof (KMF_CREDENTIAL), 445 sizeof (KMF_CREDENTIAL)}, 446 }; 447 448 int num_req_attrs = sizeof (required_attrs) / 449 sizeof (KMF_ATTRIBUTE_TESTER); 450 uint32_t len; 451 KMF_KEYSTORE_TYPE kstype; 452 453 if (handle == NULL) 454 return (KMF_ERR_BAD_PARAMETER); 455 456 CLEAR_ERROR(handle, ret); 457 if (ret != KMF_OK) 458 return (ret); 459 460 ret = test_attributes(num_req_attrs, required_attrs, 461 0, NULL, num_attr, attrlist); 462 if (ret != KMF_OK) 463 return (ret); 464 465 len = sizeof (kstype); 466 ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, num_attr, 467 &kstype, &len); 468 if (ret != KMF_OK) 469 return (ret); 470 471 plugin = FindPlugin(handle, kstype); 472 if (plugin != NULL) { 473 if (plugin->funclist->SetTokenPin != NULL) 474 return (plugin->funclist->SetTokenPin(handle, num_attr, 475 attrlist)); 476 else 477 return (KMF_ERR_FUNCTION_NOT_FOUND); 478 } 479 return (KMF_ERR_PLUGIN_NOTFOUND); 480 } 481 482 /* 483 * Name: kmf_select_token 484 * 485 * Description: 486 * This function enables the user of PKCS#11 plugin to select a 487 * particular PKCS#11 token. Valid token label are required in order to 488 * successfully complete this function. 489 * All subsequent KMF APIs, which specify PKCS#11 keystore as 490 * the backend, will be performed at the selected token. 491 * 492 * Parameters: 493 * label(input) - pointer to the token label 494 * 495 * Returns: 496 * A KMF_RETURN value indicating success or specifying a particular 497 * error condition. 498 * The value KMF_OK indicates success. All other values represent 499 * an error condition. 500 */ 501 KMF_RETURN 502 kmf_select_token(KMF_HANDLE_T handle, char *label, int readonly) 503 { 504 KMF_RETURN kmf_rv = KMF_OK; 505 CK_RV ck_rv = CKR_OK; 506 CK_SLOT_ID slot_id; 507 CK_SESSION_HANDLE hSession; 508 CK_FLAGS openflags; 509 510 CLEAR_ERROR(handle, kmf_rv); 511 if (kmf_rv != KMF_OK) 512 return (kmf_rv); 513 514 if (label == NULL) { 515 return (KMF_ERR_BAD_PARAMETER); 516 } 517 518 kmf_rv = init_pk11(); 519 if (kmf_rv != KMF_OK) { 520 return (kmf_rv); 521 } 522 523 /* Only one token can be active per thread */ 524 if (handle->pk11handle != NULL) { 525 return (KMF_ERR_TOKEN_SELECTED); 526 } 527 528 /* Find the token with matching label */ 529 kmf_rv = kmf_pk11_token_lookup(handle, label, &slot_id); 530 if (kmf_rv != KMF_OK) { 531 return (kmf_rv); 532 } 533 534 openflags = CKF_SERIAL_SESSION; 535 if (!readonly) 536 openflags |= CKF_RW_SESSION; 537 538 /* Open a session then log the user into the token */ 539 ck_rv = C_OpenSession(slot_id, openflags, NULL, NULL, &hSession); 540 if (ck_rv != CKR_OK) { 541 handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; 542 handle->lasterr.errcode = ck_rv; 543 return (KMF_ERR_INTERNAL); 544 } 545 546 handle->pk11handle = hSession; 547 548 return (kmf_rv); 549 } 550 551 CK_SESSION_HANDLE 552 kmf_get_pk11_handle(KMF_HANDLE_T kmfh) 553 { 554 return (kmfh->pk11handle); 555 } 556 557 KMF_RETURN 558 kmf_pk11_init_token(KMF_HANDLE_T handle, 559 char *currlabel, char *newlabel, 560 CK_UTF8CHAR_PTR sopin, CK_ULONG sopinlen) 561 { 562 KMF_RETURN ret = KMF_OK; 563 CK_RV ckrv; 564 CK_SLOT_ID slot_id = 0; 565 566 CLEAR_ERROR(handle, ret); 567 if (ret != KMF_OK) 568 return (ret); 569 570 /* 571 * It is best to try and lookup tokens by label. 572 */ 573 if (currlabel != NULL) { 574 ret = kmf_pk11_token_lookup(handle, currlabel, &slot_id); 575 if (ret != KMF_OK) 576 return (ret); 577 } else { 578 /* We can't determine which slot to initialize */ 579 return (KMF_ERR_TOKEN_NOT_PRESENT); 580 } 581 582 /* Initialize and set the new label (if given) */ 583 ckrv = C_InitToken(slot_id, sopin, sopinlen, 584 (CK_UTF8CHAR_PTR)(newlabel ? newlabel : currlabel)); 585 586 if (ckrv != CKR_OK) { 587 if (ckrv == CKR_PIN_INCORRECT) 588 return (KMF_ERR_AUTH_FAILED); 589 else 590 return (KMF_ERR_INTERNAL); 591 } 592 593 return (ret); 594 } 595