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