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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file contains the functions that are shared among 31 * the various services this tool will ultimately provide. 32 */ 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <ctype.h> 38 #include <cryptoutil.h> 39 #include <security/cryptoki.h> 40 #include "common.h" 41 42 /* Global PKCS#11 error value. */ 43 int pk11_errno = 0; 44 45 /* 46 * Gets passphrase from user, caller needs to free when done. 47 */ 48 int 49 get_password(char *prompt, char **password) 50 { 51 char *phrase; 52 53 /* Prompt user for password. */ 54 if ((phrase = getpassphrase(prompt)) == NULL) 55 return (-1); 56 57 /* Duplicate passphrase in separate chunk of memory */ 58 if ((*password = strdup(phrase)) == NULL) 59 return (-1); 60 61 return (strlen(phrase)); 62 } 63 64 /* 65 * Perform any PKCS#11 setup here. Right now, this tool only 66 * requires C_Initialize(). Additional features planned for 67 * this tool will require more initialization and state info 68 * added here. 69 */ 70 int 71 init_pk11(void) 72 { 73 int rv; 74 75 cryptodebug("inside init_pk11"); 76 77 /* Initialize PKCS#11 library. */ 78 if ((rv = C_Initialize(NULL_PTR)) != CKR_OK && 79 rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) { 80 pk11_errno = rv; 81 return (PK_ERR_PK11INIT); 82 } 83 84 return (PK_ERR_NONE); 85 } 86 87 /* 88 * memcmp_pad_max() is a specialized version of memcmp() which 89 * compares two pieces of data up to a maximum length. If the 90 * the two data match up the maximum length, they are considered 91 * matching. Trailing blanks do not cause the match to fail if 92 * one of the data is shorted. 93 * 94 * Examples of matches: 95 * "one" | 96 * "one " | 97 * ^maximum length 98 * 99 * "Number One | X" (X is beyond maximum length) 100 * "Number One " | 101 * ^maximum length 102 * 103 * Examples of mismatches: 104 * " one" 105 * "one" 106 * 107 * "Number One X|" 108 * "Number One |" 109 * ^maximum length 110 */ 111 static int 112 memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz) 113 { 114 115 uint_t len, extra_len; 116 char *marker; 117 118 /* No point in comparing anything beyond max_sz */ 119 if (d1_len > max_sz) 120 d1_len = max_sz; 121 if (d2_len > max_sz) 122 d2_len = max_sz; 123 124 /* Find shorter of the two data. */ 125 if (d1_len <= d2_len) { 126 len = d1_len; 127 extra_len = d2_len; 128 marker = d2; 129 } else { /* d1_len > d2_len */ 130 len = d2_len; 131 extra_len = d1_len; 132 marker = d1; 133 } 134 135 /* Have a match in the shortest length of data? */ 136 if (memcmp(d1, d2, len) != 0) 137 /* CONSTCOND */ 138 return (!0); 139 140 /* If the rest of longer data is nulls or blanks, call it a match. */ 141 while (len < extra_len) 142 if (!isspace(marker[len++])) 143 /* CONSTCOND */ 144 return (!0); 145 return (0); 146 } 147 148 /* 149 * Locate a token slot whose token matches the label, manufacturer 150 * ID, and serial number given. Token label must be specified, 151 * manufacturer ID and serial number are optional. 152 */ 153 int 154 find_token_slot(char *token_name, char *manuf_id, char *serial_no, 155 CK_SLOT_ID *slot_id, CK_FLAGS *pin_state) 156 { 157 CK_SLOT_ID_PTR slot_list; 158 CK_TOKEN_INFO token_info; 159 CK_ULONG slot_count = 0; 160 int rv; 161 int i; 162 uint_t len, max_sz; 163 boolean_t tok_match = B_FALSE, 164 man_match = B_FALSE, 165 ser_match = B_FALSE; 166 167 cryptodebug("inside find_token_slot"); 168 169 /* 170 * Get the slot count first because we don't know how many 171 * slots there are and how many of those slots even have tokens. 172 * Don't specify an arbitrary buffer size for the slot list; 173 * it may be too small (see section 11.5 of PKCS#11 spec). 174 * Also select only those slots that have tokens in them, 175 * because this tool has no need to know about empty slots. 176 */ 177 if ((rv = C_GetSlotList(1, NULL_PTR, &slot_count)) != CKR_OK) { 178 pk11_errno = rv; 179 return (PK_ERR_PK11SLOTS); 180 } 181 182 if (slot_count == 0) 183 return (PK_ERR_NOSLOTS); /* with tokens in them */ 184 185 /* Allocate space for the slot list and get it. */ 186 if ((slot_list = 187 (CK_SLOT_ID_PTR) malloc(slot_count * sizeof (CK_SLOT_ID))) == NULL) 188 return (PK_ERR_NOMEMORY); 189 190 if ((rv = C_GetSlotList(1, slot_list, &slot_count)) != CKR_OK) { 191 /* NOTE: can slot_count change from previous call??? */ 192 pk11_errno = rv; 193 free(slot_list); 194 return (PK_ERR_PK11SLOTS); 195 } 196 197 /* Search for the token. */ 198 for (i = 0; i < slot_count; i++) { 199 if ((rv = 200 C_GetTokenInfo(slot_list[i], &token_info)) != CKR_OK) { 201 cryptodebug("slot %d has no token", i); 202 continue; 203 } 204 205 len = strlen(token_name); 206 max_sz = sizeof (token_info.label); 207 if (memcmp_pad_max(&(token_info.label), max_sz, token_name, len, 208 max_sz) == 0) 209 tok_match = B_TRUE; 210 211 cryptodebug("slot %d:", i); 212 cryptodebug("\tlabel = \"%.32s\"", token_info.label); 213 cryptodebug("\tmanuf = \"%.32s\"", token_info.manufacturerID); 214 cryptodebug("\tserno = \"%.16s\"", token_info.serialNumber); 215 cryptodebug("\tmodel = \"%.16s\"", token_info.model); 216 217 cryptodebug("\tCKF_USER_PIN_INITIALIZED = %s", 218 (token_info.flags & CKF_USER_PIN_INITIALIZED) ? 219 "true" : "false"); 220 cryptodebug("\tCKF_USER_PIN_TO_BE_CHANGED = %s", 221 (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ? 222 "true" : "false"); 223 224 if (manuf_id) { 225 len = strlen(manuf_id); 226 max_sz = sizeof ((char *)(token_info.manufacturerID)); 227 if (memcmp_pad_max(&(token_info.manufacturerID), max_sz, 228 manuf_id, len, max_sz) == 0) 229 man_match = B_TRUE; 230 } 231 232 if (serial_no) { 233 len = strlen(serial_no); 234 max_sz = sizeof ((char *)(token_info.serialNumber)); 235 if (memcmp_pad_max(&(token_info.serialNumber), max_sz, 236 serial_no, len, max_sz) == 0) 237 ser_match = B_TRUE; 238 } 239 240 if (tok_match && 241 (manuf_id ? B_TRUE : B_FALSE) == man_match && 242 (serial_no ? B_TRUE : B_FALSE) == ser_match) 243 break; /* found it! */ 244 } 245 246 if (i == slot_count) { 247 free(slot_list); 248 return (PK_ERR_NOTFOUND); 249 } 250 251 cryptodebug("matched token at slot %d", i); 252 *slot_id = slot_list[i]; 253 *pin_state = (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED); 254 free(slot_list); 255 return (PK_ERR_NONE); 256 } 257 258 /* 259 * Log into the token in given slot and create a session for it. 260 */ 261 int 262 login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pinlen, 263 CK_SESSION_HANDLE_PTR hdl) 264 { 265 int rv; 266 267 cryptodebug("inside login_token"); 268 269 /* Create a read-write session so we can change the PIN. */ 270 if ((rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION|CKF_RW_SESSION, 271 NULL, NULL, hdl)) != CKR_OK) { 272 pk11_errno = rv; 273 return (PK_ERR_PK11SESSION); 274 } 275 276 /* 277 * If the token is newly created, there initial PIN will be "changme", 278 * and all subsequent PKCS#11 calls will fail with CKR_PIN_EXPIRED, 279 * but C_Login() will succeed. 280 */ 281 if ((rv = C_Login(*hdl, CKU_USER, pin, pinlen)) != CKR_OK) { 282 pk11_errno = rv; 283 (void) C_CloseSession(*hdl); 284 cryptodebug("C_Login returns %s", pkcs11_strerror(rv)); 285 if (rv == CKR_USER_PIN_NOT_INITIALIZED) 286 return (PK_ERR_CHANGEPIN); 287 return (PK_ERR_PK11LOGIN); 288 } 289 290 return (PK_ERR_NONE); 291 } 292 293 /* 294 * Log out of the token and close the session. 295 */ 296 void 297 logout_token(CK_SESSION_HANDLE hdl) 298 { 299 cryptodebug("inside logout_token"); 300 301 if (hdl) { 302 (void) C_Logout(hdl); 303 (void) C_CloseSession(hdl); 304 } 305 (void) C_Finalize(NULL); 306 } 307