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