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