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