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_OK) { 109 if (handle != NULL) { 110 handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; 111 handle->lasterr.errcode = ck_rv; 112 } 113 return (KMF_ERR_INTERNAL); 114 } 115 116 if (tmp_count == 0) { 117 *slot_list = NULL_PTR; 118 *slot_count = 0; 119 return (KMF_OK); 120 } 121 122 /* Allocate initial space for the slot list. */ 123 if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count * 124 sizeof (CK_SLOT_ID))) == NULL) 125 return (KMF_ERR_MEMORY); 126 127 /* Then get the slot list itself. */ 128 for (;;) { 129 ck_rv = C_GetSlotList(1, tmp_list, &tmp_count); 130 if (ck_rv == CKR_OK) { 131 *slot_list = tmp_list; 132 *slot_count = tmp_count; 133 kmf_rv = KMF_OK; 134 break; 135 } 136 137 if (ck_rv != CKR_BUFFER_TOO_SMALL) { 138 free(tmp_list); 139 if (handle != NULL) { 140 handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; 141 handle->lasterr.errcode = ck_rv; 142 } 143 kmf_rv = KMF_ERR_INTERNAL; 144 break; 145 } 146 147 /* 148 * If the number of slots grew, try again. This 149 * is to be consistent with pktool in ONNV. 150 */ 151 if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list, 152 tmp_count * sizeof (CK_SLOT_ID))) == NULL) { 153 free(tmp_list); 154 kmf_rv = KMF_ERR_MEMORY; 155 break; 156 } 157 tmp_list = tmp2_list; 158 } 159 160 return (kmf_rv); 161 } 162 163 /* 164 * Returns pointer to either null-terminator or next unescaped colon. The 165 * string to be extracted starts at the beginning and goes until one character 166 * before this pointer. If NULL is returned, the string itself is NULL. 167 */ 168 static char * 169 find_unescaped_colon(char *str) 170 { 171 char *end; 172 173 if (str == NULL) 174 return (NULL); 175 176 while ((end = strchr(str, ':')) != NULL) { 177 if (end != str && *(end-1) != '\\') 178 return (end); 179 str = end + 1; /* could point to null-terminator */ 180 } 181 if (end == NULL) 182 end = strchr(str, '\0'); 183 return (end); 184 } 185 186 /* 187 * Compresses away any characters escaped with backslash from given string. 188 * The string is altered in-place. Example, "ab\:\\e" becomes "ab:\e". 189 */ 190 static void 191 unescape_str(char *str) 192 { 193 boolean_t escaped = B_FALSE; 194 char *mark; 195 196 if (str == NULL) 197 return; 198 199 for (mark = str; *str != '\0'; str++) { 200 if (*str != '\\' || escaped == B_TRUE) { 201 *mark++ = *str; 202 escaped = B_FALSE; 203 } else { 204 escaped = B_TRUE; 205 } 206 } 207 *mark = '\0'; 208 } 209 210 211 /* 212 * Given a colon-separated token specifier, this functions splits it into 213 * its label, manufacturer ID (if any), and serial number (if any). Literal 214 * colons within the label/manuf/serial can be escaped with a backslash. 215 * Fields can left blank and trailing colons can be omitted, however leading 216 * colons are required as placeholders. For example, these are equivalent: 217 * (a) "lbl", "lbl:", "lbl::" (b) "lbl:man", "lbl:man:" 218 * but these are not: 219 * (c) "man", ":man" (d) "ser", "::ser" 220 * Furthermore, the token label is required always. 221 * 222 * The buffer containing the token specifier is altered by replacing the 223 * colons to null-terminators, and pointers returned are pointers into this 224 * string. No new memory is allocated. 225 */ 226 static int 227 parse_token_spec(char *token_spec, char **token_name, char **manuf_id, 228 char **serial_no) 229 { 230 char *mark; 231 232 if (token_spec == NULL || *token_spec == '\0') { 233 return (-1); 234 } 235 236 *token_name = NULL; 237 *manuf_id = NULL; 238 *serial_no = NULL; 239 240 /* Token label (required) */ 241 mark = find_unescaped_colon(token_spec); 242 *token_name = token_spec; 243 if (*mark != '\0') 244 *mark++ = '\0'; /* mark points to next field, if any */ 245 unescape_str(*token_name); 246 247 if (*(*token_name) == '\0') { /* token label is required */ 248 return (-1); 249 } 250 251 if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */ 252 return (0); 253 token_spec = mark; 254 255 /* Manufacturer identifier (optional) */ 256 mark = find_unescaped_colon(token_spec); 257 *manuf_id = token_spec; 258 if (*mark != '\0') 259 *mark++ = '\0'; /* mark points to next field, if any */ 260 unescape_str(*manuf_id); 261 262 if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */ 263 return (0); 264 token_spec = mark; 265 266 /* Serial number (optional) */ 267 mark = find_unescaped_colon(token_spec); 268 *serial_no = token_spec; 269 if (*mark != '\0') 270 *mark++ = '\0'; /* null-terminate, just in case */ 271 unescape_str(*serial_no); 272 273 return (0); 274 } 275 276 /* 277 * Find slots that match a token identifier. Token labels take the 278 * form of: 279 * token_name:manufacturer:serial_number 280 * manufacterer and serial number are optional. If used, the fields 281 * are delimited by the colon ':' character. 282 */ 283 KMF_RETURN 284 KMF_PK11TokenLookup(KMF_HANDLE_T handle, char *label, CK_SLOT_ID *slot_id) 285 { 286 KMF_RETURN kmf_rv = KMF_OK; 287 CK_RV rv; 288 CK_SLOT_ID_PTR slot_list = NULL; 289 CK_TOKEN_INFO token_info; 290 CK_ULONG slot_count = 0; 291 int i; 292 uint_t len, max_sz; 293 boolean_t metaslot_status_enabled; 294 boolean_t metaslot_migrate_enabled; 295 char *metaslot_slot_info; 296 char *metaslot_token_info; 297 char *tmplabel = NULL; 298 char *token_name = NULL; 299 char *manuf_id = NULL; 300 char *serial_no = NULL; 301 boolean_t tok_match = B_FALSE, 302 man_match = B_FALSE, 303 ser_match = B_FALSE; 304 305 if (slot_id == NULL || label == NULL || !strlen(label)) 306 return (KMF_ERR_BAD_PARAMETER); 307 308 if (handle == NULL) { 309 rv = C_Initialize(NULL); 310 if ((rv != CKR_OK) && 311 (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) { 312 return (KMF_ERR_UNINITIALIZED); 313 } 314 } 315 316 /* 317 * Parse token specifier into token_name, manuf_id, serial_no. 318 * Token_name is required; manuf_id and serial_no are optional. 319 */ 320 tmplabel = strdup(label); 321 if (tmplabel == NULL) 322 return (KMF_ERR_MEMORY); 323 324 if (parse_token_spec(tmplabel, &token_name, &manuf_id, 325 &serial_no) < 0) { 326 free(tmplabel); 327 return (KMF_ERR_BAD_PARAMETER); 328 } 329 330 /* Get a list of all slots with tokens present. */ 331 kmf_rv = kmf_get_token_slots(handle, &slot_list, &slot_count); 332 if (kmf_rv != KMF_OK) { 333 free(tmplabel); 334 return (kmf_rv); 335 } 336 337 /* If there are no such slots, the desired token won't be found. */ 338 if (slot_count == 0) { 339 free(tmplabel); 340 return (KMF_ERR_TOKEN_NOT_PRESENT); 341 } 342 343 /* Search the slot list for the token. */ 344 for (i = 0; i < slot_count; i++) { 345 if (C_GetTokenInfo(slot_list[i], &token_info) != CKR_OK) { 346 continue; 347 } 348 349 /* See if the token label matches. */ 350 len = strlen(token_name); 351 max_sz = sizeof (token_info.label); 352 if (memcmp_pad_max(&(token_info.label), max_sz, token_name, 353 len, max_sz) == 0) 354 tok_match = B_TRUE; 355 /* 356 * If manufacturer id was given, see if it actually matches. 357 * If no manufacturer id was given, assume match is true. 358 */ 359 if (manuf_id) { 360 len = strlen(manuf_id); 361 max_sz = sizeof ((char *)(token_info.manufacturerID)); 362 if (memcmp_pad_max(&(token_info.manufacturerID), max_sz, 363 manuf_id, len, max_sz) == 0) 364 man_match = B_TRUE; 365 } else { 366 man_match = B_TRUE; 367 } 368 369 /* 370 * If serial number was given, see if it actually matches. 371 * If no serial number was given, assume match is true. 372 */ 373 if (serial_no) { 374 len = strlen(serial_no); 375 max_sz = sizeof ((char *)(token_info.serialNumber)); 376 if (memcmp_pad_max(&(token_info.serialNumber), max_sz, 377 serial_no, len, max_sz) == 0) 378 ser_match = B_TRUE; 379 } else { 380 ser_match = B_TRUE; 381 } 382 383 if (tok_match && man_match && ser_match) 384 break; /* found it! */ 385 } 386 387 if (i < slot_count) { 388 /* found the desired token from the slotlist */ 389 *slot_id = slot_list[i]; 390 free(slot_list); 391 free(tmplabel); 392 return (KMF_OK); 393 } 394 395 /* 396 * If we didn't find the token from the slotlist, check if this token 397 * is the one currently hidden by the metaslot. If that's case, 398 * we can just use the metaslot, the slot 0. 399 */ 400 kmf_rv = get_metaslot_info(&metaslot_status_enabled, 401 &metaslot_migrate_enabled, &metaslot_slot_info, 402 &metaslot_token_info); 403 if (kmf_rv) { 404 /* 405 * Failed to get the metaslot info. This usually means that 406 * metaslot is disabled from the system. 407 */ 408 kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT; 409 } else { 410 max_sz = strlen(metaslot_token_info); 411 if (memcmp_pad_max(metaslot_token_info, max_sz, token_name, len, 412 max_sz) == 0) { 413 *slot_id = slot_list[0]; 414 } else { 415 kmf_rv = KMF_ERR_TOKEN_NOT_PRESENT; 416 } 417 free(metaslot_slot_info); 418 free(metaslot_token_info); 419 } 420 421 free(slot_list); 422 free(tmplabel); 423 return (kmf_rv); 424 } 425 426 KMF_RETURN 427 KMF_SetTokenPin(KMF_HANDLE_T handle, KMF_SETPIN_PARAMS *params, 428 KMF_CREDENTIAL *newpin) 429 { 430 KMF_RETURN rv = KMF_OK; 431 KMF_PLUGIN *plugin; 432 433 CLEAR_ERROR(handle, rv); 434 if (rv != KMF_OK) 435 return (rv); 436 437 if (params == NULL || newpin == NULL) 438 return (KMF_ERR_BAD_PARAMETER); 439 440 /* 441 * If setting PKCS#11 token look for the slot. 442 */ 443 if (params->kstype == KMF_KEYSTORE_PK11TOKEN) { 444 rv = KMF_PK11TokenLookup(NULL, params->tokenname, 445 ¶ms->pkcs11parms.slot); 446 if (rv != KMF_OK) 447 return (rv); 448 } 449 450 plugin = FindPlugin(handle, params->kstype); 451 if (plugin == NULL) 452 return (KMF_ERR_PLUGIN_NOTFOUND); 453 if (plugin->funclist->SetTokenPin == NULL) 454 return (KMF_ERR_FUNCTION_NOT_FOUND); 455 456 rv = plugin->funclist->SetTokenPin(handle, params, newpin); 457 458 return (rv); 459 } 460 461 /* 462 * 463 * Name: KMF_SelectToken 464 * 465 * Description: 466 * This function enables the user of PKCS#11 plugin to select a 467 * particular PKCS#11 token. Valid token label are required in order to 468 * successfully complete this function. 469 * All subsequent KMF APIs, which specify PKCS#11 keystore as 470 * the backend, will be performed at the selected token. 471 * 472 * Parameters: 473 * label(input) - pointer to the token label 474 * 475 * Returns: 476 * A KMF_RETURN value indicating success or specifying a particular 477 * error condition. 478 * The value KMF_OK indicates success. All other values represent 479 * an error condition. 480 * 481 */ 482 KMF_RETURN 483 KMF_SelectToken(KMF_HANDLE_T handle, char *label, 484 int readonly) 485 { 486 KMF_RETURN kmf_rv = KMF_OK; 487 CK_RV ck_rv = CKR_OK; 488 CK_SLOT_ID slot_id; 489 CK_SESSION_HANDLE hSession; 490 CK_FLAGS openflags; 491 492 CLEAR_ERROR(handle, kmf_rv); 493 if (kmf_rv != KMF_OK) 494 return (kmf_rv); 495 496 if (label == NULL) { 497 return (KMF_ERR_BAD_PARAMETER); 498 } 499 500 if (!is_pk11_ready()) { 501 return (KMF_ERR_UNINITIALIZED); 502 } 503 504 /* Only one token can be active per thread */ 505 if (handle->pk11handle != NULL) { 506 return (KMF_ERR_TOKEN_SELECTED); 507 } 508 509 /* Find the token with matching label */ 510 kmf_rv = KMF_PK11TokenLookup(handle, label, &slot_id); 511 if (kmf_rv != KMF_OK) { 512 return (kmf_rv); 513 } 514 515 openflags = CKF_SERIAL_SESSION; 516 if (!readonly) 517 openflags |= CKF_RW_SESSION; 518 519 /* Open a session then log the user into the token */ 520 ck_rv = C_OpenSession(slot_id, openflags, NULL, NULL, &hSession); 521 if (ck_rv != CKR_OK) { 522 handle->lasterr.kstype = KMF_KEYSTORE_PK11TOKEN; 523 handle->lasterr.errcode = ck_rv; 524 return (KMF_ERR_INTERNAL); 525 } 526 527 handle->pk11handle = hSession; 528 529 return (kmf_rv); 530 } 531 532 CK_SESSION_HANDLE 533 KMF_GetPK11Handle(KMF_HANDLE_T kmfh) 534 { 535 return (kmfh->pk11handle); 536 } 537