17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 237711facfSdinak * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* 307c478bd9Sstevel@tonic-gate * This file contains the functions that are shared among 317c478bd9Sstevel@tonic-gate * the various services this tool will ultimately provide. 327711facfSdinak * The functions in this file return PKCS#11 CK_RV errors. 337711facfSdinak * Only one session and one login per token is supported 347711facfSdinak * at this time. 357c478bd9Sstevel@tonic-gate */ 367c478bd9Sstevel@tonic-gate 377c478bd9Sstevel@tonic-gate #include <stdio.h> 387c478bd9Sstevel@tonic-gate #include <stdlib.h> 397c478bd9Sstevel@tonic-gate #include <string.h> 407c478bd9Sstevel@tonic-gate #include <ctype.h> 417c478bd9Sstevel@tonic-gate #include <cryptoutil.h> 427c478bd9Sstevel@tonic-gate #include <security/cryptoki.h> 437c478bd9Sstevel@tonic-gate #include "common.h" 447711facfSdinak #include "biginteger.h" 457c478bd9Sstevel@tonic-gate 467711facfSdinak /* True and false for attribute templates. */ 477711facfSdinak CK_BBOOL pk_true = B_TRUE; 487711facfSdinak CK_BBOOL pk_false = B_FALSE; 497711facfSdinak 507711facfSdinak /* Local status variables. */ 517711facfSdinak static boolean_t initialized = B_FALSE; 527711facfSdinak static boolean_t session_opened = B_FALSE; 537711facfSdinak static boolean_t session_writable = B_FALSE; 547711facfSdinak static boolean_t logged_in = B_FALSE; 557c478bd9Sstevel@tonic-gate 56*49e21299Sdinak /* Supporting structures and global variables for getopt_av(). */ 57*49e21299Sdinak typedef struct av_opts_s { 58*49e21299Sdinak int shortnm; /* short name character */ 59*49e21299Sdinak char *longnm; /* long name string, NOT terminated */ 60*49e21299Sdinak int longnm_len; /* length of long name string */ 61*49e21299Sdinak boolean_t has_arg; /* takes optional argument */ 62*49e21299Sdinak } av_opts; 63*49e21299Sdinak static av_opts *opts_av = NULL; 64*49e21299Sdinak static const char *_save_optstr = NULL; 65*49e21299Sdinak static int _save_numopts = 0; 66*49e21299Sdinak 67*49e21299Sdinak int optind_av = 1; 68*49e21299Sdinak char *optarg_av = NULL; 69*49e21299Sdinak 707c478bd9Sstevel@tonic-gate /* 717711facfSdinak * Perform PKCS#11 setup here. Currently only C_Initialize is required, 727711facfSdinak * along with setting/resetting state variables. 737c478bd9Sstevel@tonic-gate */ 747711facfSdinak CK_RV 757711facfSdinak init_pk11(void) 767c478bd9Sstevel@tonic-gate { 777711facfSdinak CK_RV rv = CKR_OK; 787711facfSdinak 797711facfSdinak cryptodebug("inside init_pk11"); 807711facfSdinak 817711facfSdinak /* If C_Initialize() already called, nothing to do here. */ 827711facfSdinak if (initialized == B_TRUE) 837711facfSdinak return (CKR_OK); 847c478bd9Sstevel@tonic-gate 857711facfSdinak /* Reset state variables because C_Initialize() not yet done. */ 867711facfSdinak session_opened = B_FALSE; 877711facfSdinak session_writable = B_FALSE; 887711facfSdinak logged_in = B_FALSE; 897c478bd9Sstevel@tonic-gate 907711facfSdinak /* Initialize PKCS#11 library. */ 917711facfSdinak cryptodebug("calling C_Initialize()"); 927711facfSdinak if ((rv = C_Initialize(NULL_PTR)) != CKR_OK && 937711facfSdinak rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) { 947711facfSdinak return (rv); 957711facfSdinak } 967c478bd9Sstevel@tonic-gate 977711facfSdinak initialized = B_TRUE; 987711facfSdinak return (CKR_OK); 997c478bd9Sstevel@tonic-gate } 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate /* 1027711facfSdinak * Finalize PKCS#11 library and reset state variables. Open sessions, 1037711facfSdinak * if any, are closed, and thereby any logins are logged out also. 1047c478bd9Sstevel@tonic-gate */ 1057711facfSdinak void 1067711facfSdinak final_pk11(CK_SESSION_HANDLE sess) 1077c478bd9Sstevel@tonic-gate { 1087711facfSdinak cryptodebug("inside final_pk11"); 1097c478bd9Sstevel@tonic-gate 1107711facfSdinak /* If the library wasn't initialized, nothing to do here. */ 1117711facfSdinak if (!initialized) 1127711facfSdinak return; 1137c478bd9Sstevel@tonic-gate 1147711facfSdinak /* Make sure the sesion is closed first. */ 1157711facfSdinak close_sess(sess); 1167711facfSdinak 1177711facfSdinak cryptodebug("calling C_Finalize()"); 1187711facfSdinak (void) C_Finalize(NULL); 1197711facfSdinak initialized = B_FALSE; 1207711facfSdinak } 1217711facfSdinak 1227711facfSdinak /* 1237711facfSdinak * Create a PKCS#11 session on the given slot, and set state information. 1247711facfSdinak * If session is already open, check that the read-only/read-write state 1257711facfSdinak * requested matches that of the session. If it doesn't, make it so. 1267711facfSdinak */ 1277711facfSdinak CK_RV 1287711facfSdinak open_sess(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_SESSION_HANDLE_PTR sess) 1297711facfSdinak { 1307711facfSdinak CK_RV rv = CKR_OK; 1317711facfSdinak 1327711facfSdinak cryptodebug("inside open_sess"); 1337711facfSdinak 1347711facfSdinak /* If the session is already open, check the session flags. */ 1357711facfSdinak if (session_opened) { 1367711facfSdinak /* 1377711facfSdinak * If requesting R/W session and it is currently R/O, 1387711facfSdinak * need to close the session and reopen it R/W. The 1397711facfSdinak * other cases are considered acceptable: 1407711facfSdinak * sess_flags current state 1417711facfSdinak * ---------- ------------- 1427711facfSdinak * ~CKF_RW_SESSION !session_writable 1437711facfSdinak * ~CKF_RW_SESSION session_writable 1447711facfSdinak * CKF_RW_SESSION session_writable 1457711facfSdinak */ 1467711facfSdinak if ((sess_flags & CKF_RW_SESSION) && !session_writable) 1477711facfSdinak close_sess(*sess); 1487711facfSdinak else 1497711facfSdinak return (CKR_OK); 1507711facfSdinak } 1517711facfSdinak 1527711facfSdinak /* Make sure the PKCS#11 is already initialized. */ 1537711facfSdinak if (!initialized) 1547711facfSdinak if ((rv = init_pk11()) != CKR_OK) 1557711facfSdinak return (rv); 1567711facfSdinak 1577711facfSdinak /* Create a session for subsequent operations. */ 1587711facfSdinak cryptodebug("calling C_OpenSession()"); 1597711facfSdinak if ((rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION|sess_flags, 1607711facfSdinak NULL, NULL, sess)) != CKR_OK) 1617711facfSdinak return (rv); 1627711facfSdinak session_opened = B_TRUE; 1637711facfSdinak session_writable = (sess_flags & CKF_RW_SESSION) ? B_TRUE : B_FALSE; 1647711facfSdinak return (CKR_OK); 1657711facfSdinak } 1667711facfSdinak 1677711facfSdinak /* 1687711facfSdinak * Close PKCS#11 session and reset state variables. Any logins are 1697711facfSdinak * logged out. 1707711facfSdinak */ 1717711facfSdinak void 1727711facfSdinak close_sess(CK_SESSION_HANDLE sess) 1737711facfSdinak { 1747711facfSdinak cryptodebug("inside close_sess"); 1757711facfSdinak 1767711facfSdinak if (sess == NULL) { 1777711facfSdinak cryptodebug("session handle is null"); 1787711facfSdinak return; 1797711facfSdinak } 1807711facfSdinak 1817711facfSdinak /* If session is already closed, nothing to do here. */ 1827711facfSdinak session_writable = B_FALSE; 1837711facfSdinak if (!session_opened) 1847711facfSdinak return; 1857711facfSdinak 1867711facfSdinak /* Make sure user is logged out of token. */ 1877711facfSdinak logout_token(sess); 1887711facfSdinak 1897711facfSdinak cryptodebug("calling C_CloseSession()"); 1907711facfSdinak (void) C_CloseSession(sess); 1917711facfSdinak session_opened = B_FALSE; 1927711facfSdinak } 1937711facfSdinak 1947711facfSdinak /* 1957711facfSdinak * Log user into token in given slot. If this first login ever for this 1967711facfSdinak * token, the initial PIN is "changeme", C_Login() will succeed, but all 1977711facfSdinak * PKCS#11 calls following the C_Login() will fail with CKR_PIN_EXPIRED. 1987711facfSdinak */ 1997711facfSdinak CK_RV 2007711facfSdinak login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pinlen, 2017711facfSdinak CK_SESSION_HANDLE_PTR sess) 2027711facfSdinak { 2037711facfSdinak CK_RV rv = CKR_OK; 2047711facfSdinak 2057711facfSdinak cryptodebug("inside login_token"); 2067711facfSdinak 2077711facfSdinak /* If already logged in, nothing to do here. */ 2087711facfSdinak if (logged_in) 2097711facfSdinak return (CKR_OK); 2107711facfSdinak 2117711facfSdinak /* Make sure we have a session first, assume R/O is enough. */ 2127711facfSdinak if (!session_opened) 2137711facfSdinak if ((rv = open_sess(slot_id, CKF_SERIAL_SESSION, sess)) != 2147711facfSdinak CKR_OK) 2157711facfSdinak return (rv); 2167711facfSdinak 2177711facfSdinak /* Log the user into the token. */ 2187711facfSdinak cryptodebug("calling C_Login()"); 2197711facfSdinak if ((rv = C_Login(*sess, CKU_USER, pin, pinlen)) != CKR_OK) { 2207711facfSdinak cryptodebug("C_Login returns %s", pkcs11_strerror(rv)); 2217711facfSdinak return (rv); 2227711facfSdinak } 2237711facfSdinak 2247711facfSdinak logged_in = B_TRUE; 2257711facfSdinak return (CKR_OK); 2267711facfSdinak } 2277711facfSdinak 2287711facfSdinak /* 2297711facfSdinak * Log user out of token and reset status variable. 2307711facfSdinak */ 2317711facfSdinak void 2327711facfSdinak logout_token(CK_SESSION_HANDLE sess) 2337711facfSdinak { 2347711facfSdinak cryptodebug("inside logout_token"); 2357711facfSdinak 2367711facfSdinak if (sess == NULL) { 2377711facfSdinak cryptodebug("session handle is null"); 2387711facfSdinak return; 2397c478bd9Sstevel@tonic-gate } 2407c478bd9Sstevel@tonic-gate 2417711facfSdinak /* If already logged out, nothing to do here. */ 2427711facfSdinak if (!logged_in) 2437711facfSdinak return; 2447711facfSdinak 2457711facfSdinak cryptodebug("calling C_Logout()"); 2467711facfSdinak (void) C_Logout(sess); 2477711facfSdinak logged_in = B_FALSE; 2487711facfSdinak } 2497711facfSdinak 2507711facfSdinak /* 2517711facfSdinak * Shortcut function to get from an uninitialized state to user logged in. 2527711facfSdinak * If the library is already initialized, the session is already opened, 2537711facfSdinak * or the user is already logged in, those steps are skipped and the next 2547711facfSdinak * step is checked. 2557711facfSdinak */ 2567711facfSdinak CK_RV 2577711facfSdinak quick_start(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_UTF8CHAR_PTR pin, 2587711facfSdinak CK_ULONG pinlen, CK_SESSION_HANDLE_PTR sess) 2597711facfSdinak { 2607711facfSdinak CK_RV rv = CKR_OK; 2617711facfSdinak 2627711facfSdinak cryptodebug("inside quick_start"); 2637711facfSdinak 2647711facfSdinak /* Call open_sess() explicitly if R/W session is needed. */ 2657711facfSdinak if (sess_flags & CKF_RW_SESSION) 2667711facfSdinak if ((rv = open_sess(slot_id, sess_flags, sess)) != CKR_OK) 2677711facfSdinak return (rv); 2687711facfSdinak 2697711facfSdinak if ((rv = login_token(slot_id, pin, pinlen, sess)) != CKR_OK) 2707711facfSdinak return (rv); 2717711facfSdinak 2727711facfSdinak return (CKR_OK); 2737711facfSdinak } 2747711facfSdinak 2757711facfSdinak /* 2767711facfSdinak * Shortcut function to go from any state to uninitialized PKCS#11 library. 2777711facfSdinak */ 2787711facfSdinak void 2797711facfSdinak quick_finish(CK_SESSION_HANDLE sess) 2807711facfSdinak { 2817711facfSdinak cryptodebug("inside quick_finish"); 2827711facfSdinak 2837711facfSdinak /* All the needed calls are done implicitly. */ 2847711facfSdinak final_pk11(sess); 2857711facfSdinak } 2867711facfSdinak 2877711facfSdinak /* 2887711facfSdinak * Gets PIN from user. Caller needs to free the returned PIN when done. 2897711facfSdinak * If two prompts are given, the PIN is confirmed with second prompt. 2907711facfSdinak * Note that getphassphrase() may return data in static memory area. 2917711facfSdinak */ 2927711facfSdinak CK_RV 2937711facfSdinak get_pin(char *prompt1, char *prompt2, CK_UTF8CHAR_PTR *pin, CK_ULONG *pinlen) 2947711facfSdinak { 2957711facfSdinak char *save_phrase, *phrase1, *phrase2; 2967711facfSdinak 2977711facfSdinak cryptodebug("inside get_pin"); 2987711facfSdinak 2997711facfSdinak /* Prompt user for a PIN. */ 3007711facfSdinak if (prompt1 == NULL) { 3017711facfSdinak cryptodebug("no passphrase prompt given"); 3027711facfSdinak return (CKR_ARGUMENTS_BAD); 3037711facfSdinak } 3047711facfSdinak if ((phrase1 = getpassphrase(prompt1)) == NULL) { 3057711facfSdinak cryptodebug("getpassphrase() failed"); 3067711facfSdinak return (CKR_FUNCTION_FAILED); 3077711facfSdinak } 3087711facfSdinak 3097711facfSdinak /* Duplicate 1st PIN in separate chunk of memory. */ 3107711facfSdinak if ((save_phrase = strdup(phrase1)) == NULL) 3117711facfSdinak return (CKR_HOST_MEMORY); 3127711facfSdinak 3137711facfSdinak /* If second prompt given, PIN confirmation is requested. */ 3147711facfSdinak if (prompt2 != NULL) { 3157711facfSdinak if ((phrase2 = getpassphrase(prompt2)) == NULL) { 3167711facfSdinak cryptodebug("getpassphrase() confirmation failed"); 3177711facfSdinak free(save_phrase); 3187711facfSdinak return (CKR_FUNCTION_FAILED); 3197711facfSdinak } 3207711facfSdinak if (strcmp(save_phrase, phrase2) != 0) { 3217711facfSdinak cryptodebug("passphrases do not match"); 3227711facfSdinak free(save_phrase); 3237711facfSdinak return (CKR_PIN_INCORRECT); 3247711facfSdinak } 3257711facfSdinak } 3267711facfSdinak 3277711facfSdinak *pin = (CK_UTF8CHAR_PTR)save_phrase; 3287711facfSdinak *pinlen = strlen(save_phrase); 3297711facfSdinak return (CKR_OK); 3307711facfSdinak } 3317711facfSdinak 3327711facfSdinak /* 3337711facfSdinak * Gets yes/no response from user. If either no prompt is supplied, a 3347711facfSdinak * default prompt is used. If not message for invalid input is supplied, 3357711facfSdinak * a default will not be provided. If the user provides no response, 3367711facfSdinak * the input default B_TRUE == yes, B_FALSE == no is returned. 3377711facfSdinak * Otherwise, B_TRUE is returned for yes, and B_FALSE for no. 3387711facfSdinak */ 3397711facfSdinak boolean_t 3407711facfSdinak yesno(char *prompt, char *invalid, boolean_t dflt) 3417711facfSdinak { 3427711facfSdinak char *response, buf[1024]; 3437711facfSdinak char *yes = gettext("yes"); 3447711facfSdinak char *no = gettext("no"); 3457711facfSdinak 3467711facfSdinak cryptodebug("inside yesno"); 3477711facfSdinak 3487711facfSdinak if (prompt == NULL) 3497711facfSdinak prompt = gettext("Enter (y)es or (n)o? "); 3507711facfSdinak 3517711facfSdinak for (;;) { 3527711facfSdinak /* Prompt user. */ 3537711facfSdinak (void) printf("%s", prompt); 3547711facfSdinak (void) fflush(stdout); 3557711facfSdinak 3567711facfSdinak /* Get the response. */ 3577711facfSdinak if ((response = fgets(buf, sizeof (buf), stdin)) == NULL) 3587711facfSdinak break; /* go to default response */ 3597711facfSdinak 3607711facfSdinak /* Skip any leading white space. */ 3617711facfSdinak while (isspace(*response)) 3627711facfSdinak response++; 3637711facfSdinak if (*response == '\0') 3647711facfSdinak break; /* go to default response */ 3657711facfSdinak 3667711facfSdinak /* Is it valid input? Return appropriately. */ 3677711facfSdinak if (strncasecmp(response, yes, 1) == 0) 3687711facfSdinak return (B_TRUE); 3697711facfSdinak if (strncasecmp(response, no, 1) == 0) 3707711facfSdinak return (B_FALSE); 3717711facfSdinak 3727711facfSdinak /* Indicate invalid input, and try again. */ 3737711facfSdinak if (invalid != NULL) 3747711facfSdinak (void) printf("%s", invalid); 3757711facfSdinak } 3767711facfSdinak return (dflt); 3777711facfSdinak } 3787711facfSdinak 3797711facfSdinak /* 3807711facfSdinak * Gets the list of slots which have tokens in them. Keeps adjusting 3817711facfSdinak * the size of the slot list buffer until the call is successful or an 3827711facfSdinak * irrecoverable error occurs. 3837711facfSdinak */ 3847711facfSdinak CK_RV 3857711facfSdinak get_token_slots(CK_SLOT_ID_PTR *slot_list, CK_ULONG *slot_count) 3867711facfSdinak { 3877711facfSdinak CK_ULONG tmp_count = 0; 3887711facfSdinak CK_SLOT_ID_PTR tmp_list = NULL_PTR, tmp2_list = NULL_PTR; 3897711facfSdinak int rv = CKR_OK; 3907711facfSdinak 3917711facfSdinak cryptodebug("inside get_token_slots"); 3927711facfSdinak 3937711facfSdinak if (!initialized) 3947711facfSdinak if ((rv = init_pk11()) != CKR_OK) 3957711facfSdinak return (rv); 3967711facfSdinak 3977711facfSdinak /* 3987711facfSdinak * Get the slot count first because we don't know how many 3997711facfSdinak * slots there are and how many of those slots even have tokens. 4007711facfSdinak * Don't specify an arbitrary buffer size for the slot list; 4017711facfSdinak * it may be too small (see section 11.5 of PKCS#11 spec). 4027711facfSdinak * Also select only those slots that have tokens in them, 4037711facfSdinak * because this tool has no need to know about empty slots. 4047711facfSdinak */ 4057711facfSdinak cryptodebug("calling C_GetSlotList() for slot count"); 4067711facfSdinak if ((rv = C_GetSlotList(1, NULL_PTR, &tmp_count)) != CKR_OK) 4077711facfSdinak return (rv); 4087711facfSdinak 4097711facfSdinak if (tmp_count == 0) { 4107711facfSdinak cryptodebug("no slots with tokens found"); 4117711facfSdinak *slot_list = NULL_PTR; 4127711facfSdinak *slot_count = 0; 4137711facfSdinak return (CKR_OK); 4147711facfSdinak } 4157711facfSdinak 4167711facfSdinak /* Allocate initial space for the slot list. */ 4177711facfSdinak if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count * 4187711facfSdinak sizeof (CK_SLOT_ID))) == NULL) 4197711facfSdinak return (CKR_HOST_MEMORY); 4207711facfSdinak 4217711facfSdinak /* Then get the slot list itself. */ 4227711facfSdinak for (;;) { 4237711facfSdinak cryptodebug("calling C_GetSlotList()"); 4247711facfSdinak if ((rv = C_GetSlotList(1, tmp_list, &tmp_count)) == CKR_OK) { 4257711facfSdinak *slot_list = tmp_list; 4267711facfSdinak *slot_count = tmp_count; 4277711facfSdinak break; 4287711facfSdinak } 4297711facfSdinak 4307711facfSdinak if (rv != CKR_BUFFER_TOO_SMALL) { 4317711facfSdinak free(tmp_list); 4327711facfSdinak break; 4337711facfSdinak } 4347711facfSdinak 4357711facfSdinak /* If the number of slots grew, try again. */ 4367711facfSdinak cryptodebug("number of tokens present increased"); 4377711facfSdinak if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list, 4387711facfSdinak tmp_count * sizeof (CK_SLOT_ID))) == NULL) { 4397711facfSdinak free(tmp_list); 4407711facfSdinak rv = CKR_HOST_MEMORY; 4417711facfSdinak break; 4427711facfSdinak } 4437711facfSdinak tmp_list = tmp2_list; 4447711facfSdinak } 4457711facfSdinak 4467711facfSdinak return (rv); 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate /* 4507c478bd9Sstevel@tonic-gate * memcmp_pad_max() is a specialized version of memcmp() which 4517c478bd9Sstevel@tonic-gate * compares two pieces of data up to a maximum length. If the 4527c478bd9Sstevel@tonic-gate * the two data match up the maximum length, they are considered 4537c478bd9Sstevel@tonic-gate * matching. Trailing blanks do not cause the match to fail if 4547c478bd9Sstevel@tonic-gate * one of the data is shorted. 4557c478bd9Sstevel@tonic-gate * 4567c478bd9Sstevel@tonic-gate * Examples of matches: 4577c478bd9Sstevel@tonic-gate * "one" | 4587c478bd9Sstevel@tonic-gate * "one " | 4597c478bd9Sstevel@tonic-gate * ^maximum length 4607c478bd9Sstevel@tonic-gate * 4617c478bd9Sstevel@tonic-gate * "Number One | X" (X is beyond maximum length) 4627c478bd9Sstevel@tonic-gate * "Number One " | 4637c478bd9Sstevel@tonic-gate * ^maximum length 4647c478bd9Sstevel@tonic-gate * 4657c478bd9Sstevel@tonic-gate * Examples of mismatches: 4667c478bd9Sstevel@tonic-gate * " one" 4677c478bd9Sstevel@tonic-gate * "one" 4687c478bd9Sstevel@tonic-gate * 4697c478bd9Sstevel@tonic-gate * "Number One X|" 4707c478bd9Sstevel@tonic-gate * "Number One |" 4717c478bd9Sstevel@tonic-gate * ^maximum length 4727c478bd9Sstevel@tonic-gate */ 4737c478bd9Sstevel@tonic-gate static int 4747c478bd9Sstevel@tonic-gate memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz) 4757c478bd9Sstevel@tonic-gate { 4767c478bd9Sstevel@tonic-gate uint_t len, extra_len; 4777c478bd9Sstevel@tonic-gate char *marker; 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate /* No point in comparing anything beyond max_sz */ 4807c478bd9Sstevel@tonic-gate if (d1_len > max_sz) 4817c478bd9Sstevel@tonic-gate d1_len = max_sz; 4827c478bd9Sstevel@tonic-gate if (d2_len > max_sz) 4837c478bd9Sstevel@tonic-gate d2_len = max_sz; 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* Find shorter of the two data. */ 4867c478bd9Sstevel@tonic-gate if (d1_len <= d2_len) { 4877c478bd9Sstevel@tonic-gate len = d1_len; 4887c478bd9Sstevel@tonic-gate extra_len = d2_len; 4897c478bd9Sstevel@tonic-gate marker = d2; 4907c478bd9Sstevel@tonic-gate } else { /* d1_len > d2_len */ 4917c478bd9Sstevel@tonic-gate len = d2_len; 4927c478bd9Sstevel@tonic-gate extra_len = d1_len; 4937c478bd9Sstevel@tonic-gate marker = d1; 4947c478bd9Sstevel@tonic-gate } 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate /* Have a match in the shortest length of data? */ 4977c478bd9Sstevel@tonic-gate if (memcmp(d1, d2, len) != 0) 4987c478bd9Sstevel@tonic-gate /* CONSTCOND */ 4997c478bd9Sstevel@tonic-gate return (!0); 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate /* If the rest of longer data is nulls or blanks, call it a match. */ 5027c478bd9Sstevel@tonic-gate while (len < extra_len) 5037c478bd9Sstevel@tonic-gate if (!isspace(marker[len++])) 5047c478bd9Sstevel@tonic-gate /* CONSTCOND */ 5057c478bd9Sstevel@tonic-gate return (!0); 5067c478bd9Sstevel@tonic-gate return (0); 5077c478bd9Sstevel@tonic-gate } 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate /* 5107711facfSdinak * Locate a token slot whose token matches the label, manufacturer ID, and 5117711facfSdinak * serial number given. Token label must be specified, manufacturer ID and 5127711facfSdinak * serial number are optional. When the token is located, the PIN state 5137711facfSdinak * is also returned to determine if it still has the default PIN. 5147c478bd9Sstevel@tonic-gate */ 5157711facfSdinak CK_RV 5167c478bd9Sstevel@tonic-gate find_token_slot(char *token_name, char *manuf_id, char *serial_no, 5177c478bd9Sstevel@tonic-gate CK_SLOT_ID *slot_id, CK_FLAGS *pin_state) 5187c478bd9Sstevel@tonic-gate { 5197c478bd9Sstevel@tonic-gate CK_SLOT_ID_PTR slot_list; 5207c478bd9Sstevel@tonic-gate CK_TOKEN_INFO token_info; 5217c478bd9Sstevel@tonic-gate CK_ULONG slot_count = 0; 5227711facfSdinak int rv = CKR_OK; 5237c478bd9Sstevel@tonic-gate int i; 5247c478bd9Sstevel@tonic-gate uint_t len, max_sz; 5257c478bd9Sstevel@tonic-gate boolean_t tok_match = B_FALSE, 5267c478bd9Sstevel@tonic-gate man_match = B_FALSE, 5277c478bd9Sstevel@tonic-gate ser_match = B_FALSE; 5287c478bd9Sstevel@tonic-gate 5297c478bd9Sstevel@tonic-gate cryptodebug("inside find_token_slot"); 5307c478bd9Sstevel@tonic-gate 5317711facfSdinak if (token_name == NULL) 5327711facfSdinak return (CKR_ARGUMENTS_BAD); 5337c478bd9Sstevel@tonic-gate 5347711facfSdinak /* Get a list of all slots with tokens present. */ 5357711facfSdinak if ((rv = get_token_slots(&slot_list, &slot_count)) != CKR_OK) 5367711facfSdinak return (rv); 5377c478bd9Sstevel@tonic-gate 5387711facfSdinak /* If there are no such slots, the desired token won't be found. */ 5397711facfSdinak if (slot_count == 0) 5407711facfSdinak return (CKR_TOKEN_NOT_PRESENT); 5417c478bd9Sstevel@tonic-gate 5427711facfSdinak /* Search the slot list for the token. */ 5437c478bd9Sstevel@tonic-gate for (i = 0; i < slot_count; i++) { 5447711facfSdinak cryptodebug("calling C_GetTokenInfo()"); 5457711facfSdinak if ((rv = C_GetTokenInfo(slot_list[i], &token_info)) != 5467711facfSdinak CKR_OK) { 5477711facfSdinak cryptodebug("token in slot %d returns %s", i, 5487711facfSdinak pkcs11_strerror(rv)); 5497c478bd9Sstevel@tonic-gate continue; 5507c478bd9Sstevel@tonic-gate } 5517c478bd9Sstevel@tonic-gate 5527711facfSdinak /* See if the token label matches. */ 5537c478bd9Sstevel@tonic-gate len = strlen(token_name); 5547c478bd9Sstevel@tonic-gate max_sz = sizeof (token_info.label); 5557c478bd9Sstevel@tonic-gate if (memcmp_pad_max(&(token_info.label), max_sz, token_name, len, 5567c478bd9Sstevel@tonic-gate max_sz) == 0) 5577c478bd9Sstevel@tonic-gate tok_match = B_TRUE; 5587c478bd9Sstevel@tonic-gate 5597711facfSdinak /* 5607711facfSdinak * If manufacturer id was given, see if it actually matches. 5617711facfSdinak * If no manufacturer id was given, assume match is true. 5627711facfSdinak */ 5637c478bd9Sstevel@tonic-gate if (manuf_id) { 5647c478bd9Sstevel@tonic-gate len = strlen(manuf_id); 5657c478bd9Sstevel@tonic-gate max_sz = sizeof ((char *)(token_info.manufacturerID)); 5667c478bd9Sstevel@tonic-gate if (memcmp_pad_max(&(token_info.manufacturerID), max_sz, 5677c478bd9Sstevel@tonic-gate manuf_id, len, max_sz) == 0) 5687c478bd9Sstevel@tonic-gate man_match = B_TRUE; 5697711facfSdinak } else 5707711facfSdinak man_match = B_TRUE; 5717c478bd9Sstevel@tonic-gate 5727711facfSdinak /* 5737711facfSdinak * If serial number was given, see if it actually matches. 5747711facfSdinak * If no serial number was given, assume match is true. 5757711facfSdinak */ 5767c478bd9Sstevel@tonic-gate if (serial_no) { 5777c478bd9Sstevel@tonic-gate len = strlen(serial_no); 5787c478bd9Sstevel@tonic-gate max_sz = sizeof ((char *)(token_info.serialNumber)); 5797c478bd9Sstevel@tonic-gate if (memcmp_pad_max(&(token_info.serialNumber), max_sz, 5807c478bd9Sstevel@tonic-gate serial_no, len, max_sz) == 0) 5817c478bd9Sstevel@tonic-gate ser_match = B_TRUE; 5827711facfSdinak } else 5837711facfSdinak ser_match = B_TRUE; 5847711facfSdinak 5857711facfSdinak cryptodebug("slot %d:", i); 5867711facfSdinak cryptodebug("\tlabel = \"%.32s\"%s", token_info.label, 5877711facfSdinak tok_match ? " match" : ""); 5887711facfSdinak cryptodebug("\tmanuf = \"%.32s\"%s", token_info.manufacturerID, 5897711facfSdinak man_match ? " match" : ""); 5907711facfSdinak cryptodebug("\tserno = \"%.16s\"%s", token_info.serialNumber, 5917711facfSdinak ser_match ? " match" : ""); 5927711facfSdinak cryptodebug("\tmodel = \"%.16s\"", token_info.model); 5937c478bd9Sstevel@tonic-gate 5947711facfSdinak cryptodebug("\tCKF_USER_PIN_INITIALIZED = %s", 5957711facfSdinak (token_info.flags & CKF_USER_PIN_INITIALIZED) ? 5967711facfSdinak "true" : "false"); 5977711facfSdinak cryptodebug("\tCKF_USER_PIN_TO_BE_CHANGED = %s", 5987711facfSdinak (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ? 5997711facfSdinak "true" : "false"); 6007711facfSdinak 6017711facfSdinak if (tok_match && man_match && ser_match) 6027711facfSdinak break; /* found it! */ 6037c478bd9Sstevel@tonic-gate } 6047c478bd9Sstevel@tonic-gate 6057711facfSdinak /* Scanned the whole list without finding the token. */ 6067c478bd9Sstevel@tonic-gate if (i == slot_count) { 6077711facfSdinak cryptodebug("token not found"); 6087c478bd9Sstevel@tonic-gate free(slot_list); 6097711facfSdinak return (CKR_TOKEN_NOT_PRESENT); 6107c478bd9Sstevel@tonic-gate } 6117c478bd9Sstevel@tonic-gate 6127711facfSdinak /* Return slot id where token was found and its PIN state. */ 6137711facfSdinak cryptodebug("token found at slot %d", i); 6147c478bd9Sstevel@tonic-gate *slot_id = slot_list[i]; 6157c478bd9Sstevel@tonic-gate *pin_state = (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED); 6167c478bd9Sstevel@tonic-gate free(slot_list); 6177711facfSdinak return (CKR_OK); 6187c478bd9Sstevel@tonic-gate } 6197c478bd9Sstevel@tonic-gate 620*49e21299Sdinak /* 621*49e21299Sdinak * Returns pointer to either null-terminator or next unescaped colon. The 622*49e21299Sdinak * string to be extracted starts at the beginning and goes until one character 623*49e21299Sdinak * before this pointer. If NULL is returned, the string itself is NULL. 624*49e21299Sdinak */ 625*49e21299Sdinak static char * 626*49e21299Sdinak find_unescaped_colon(char *str) 627*49e21299Sdinak { 628*49e21299Sdinak char *end; 629*49e21299Sdinak 630*49e21299Sdinak if (str == NULL) 631*49e21299Sdinak return (NULL); 632*49e21299Sdinak 633*49e21299Sdinak while ((end = strchr(str, ':')) != NULL) { 634*49e21299Sdinak if (end != str && *(end-1) != '\\') 635*49e21299Sdinak return (end); 636*49e21299Sdinak str = end + 1; /* could point to null-terminator */ 637*49e21299Sdinak } 638*49e21299Sdinak if (end == NULL) 639*49e21299Sdinak end = strchr(str, '\0'); 640*49e21299Sdinak return (end); 641*49e21299Sdinak } 642*49e21299Sdinak 643*49e21299Sdinak /* 644*49e21299Sdinak * Compresses away any characters escaped with backslash from given string. 645*49e21299Sdinak * The string is altered in-place. Example, "ab\:\\e" becomes "ab:\e". 646*49e21299Sdinak */ 647*49e21299Sdinak static void 648*49e21299Sdinak unescape_str(char *str) 649*49e21299Sdinak { 650*49e21299Sdinak boolean_t escaped = B_FALSE; 651*49e21299Sdinak char *mark; 652*49e21299Sdinak 653*49e21299Sdinak if (str == NULL) 654*49e21299Sdinak return; 655*49e21299Sdinak 656*49e21299Sdinak for (mark = str; *str != '\0'; str++) { 657*49e21299Sdinak if (*str != '\\' || escaped == B_TRUE) { 658*49e21299Sdinak *mark++ = *str; 659*49e21299Sdinak escaped = B_FALSE; 660*49e21299Sdinak } else { 661*49e21299Sdinak escaped = B_TRUE; 662*49e21299Sdinak } 663*49e21299Sdinak } 664*49e21299Sdinak *mark = '\0'; 665*49e21299Sdinak } 666*49e21299Sdinak 667*49e21299Sdinak /* 668*49e21299Sdinak * Given a colon-separated token specifier, this functions splits it into 669*49e21299Sdinak * its label, manufacturer ID (if any), and serial number (if any). Literal 670*49e21299Sdinak * colons within the label/manuf/serial can be escaped with a backslash. 671*49e21299Sdinak * Fields can left blank and trailing colons can be omitted, however leading 672*49e21299Sdinak * colons are required as placeholders. For example, these are equivalent: 673*49e21299Sdinak * (a) "lbl", "lbl:", "lbl::" (b) "lbl:man", "lbl:man:" 674*49e21299Sdinak * but these are not: 675*49e21299Sdinak * (c) "man", ":man" (d) "ser", "::ser" 676*49e21299Sdinak * Furthermore, the token label is required always. 677*49e21299Sdinak * 678*49e21299Sdinak * The buffer containing the token specifier is altered by replacing the 679*49e21299Sdinak * colons to null-terminators, and pointers returned are pointers into this 680*49e21299Sdinak * string. No new memory is allocated. 681*49e21299Sdinak */ 682*49e21299Sdinak int 683*49e21299Sdinak parse_token_spec(char *token_spec, char **token_name, char **manuf_id, 684*49e21299Sdinak char **serial_no) 685*49e21299Sdinak { 686*49e21299Sdinak char *mark; 687*49e21299Sdinak 688*49e21299Sdinak if (token_spec == NULL || *token_spec == '\0') { 689*49e21299Sdinak cryptodebug("token specifier is empty"); 690*49e21299Sdinak return (-1); 691*49e21299Sdinak } 692*49e21299Sdinak 693*49e21299Sdinak *token_name = NULL; 694*49e21299Sdinak *manuf_id = NULL; 695*49e21299Sdinak *serial_no = NULL; 696*49e21299Sdinak 697*49e21299Sdinak /* Token label (required) */ 698*49e21299Sdinak mark = find_unescaped_colon(token_spec); 699*49e21299Sdinak *token_name = token_spec; 700*49e21299Sdinak if (*mark != '\0') 701*49e21299Sdinak *mark++ = '\0'; /* mark points to next field, if any */ 702*49e21299Sdinak unescape_str(*token_name); 703*49e21299Sdinak 704*49e21299Sdinak if (*(*token_name) == '\0') { /* token label is required */ 705*49e21299Sdinak cryptodebug("no token label found"); 706*49e21299Sdinak return (-1); 707*49e21299Sdinak } 708*49e21299Sdinak 709*49e21299Sdinak if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */ 710*49e21299Sdinak return (0); 711*49e21299Sdinak token_spec = mark; 712*49e21299Sdinak 713*49e21299Sdinak /* Manufacturer identifier (optional) */ 714*49e21299Sdinak mark = find_unescaped_colon(token_spec); 715*49e21299Sdinak *manuf_id = token_spec; 716*49e21299Sdinak if (*mark != '\0') 717*49e21299Sdinak *mark++ = '\0'; /* mark points to next field, if any */ 718*49e21299Sdinak unescape_str(*manuf_id); 719*49e21299Sdinak 720*49e21299Sdinak if (*mark == '\0' || *(mark+1) == '\0') /* no more fields */ 721*49e21299Sdinak return (0); 722*49e21299Sdinak token_spec = mark; 723*49e21299Sdinak 724*49e21299Sdinak /* Serial number (optional) */ 725*49e21299Sdinak mark = find_unescaped_colon(token_spec); 726*49e21299Sdinak *serial_no = token_spec; 727*49e21299Sdinak if (*mark != '\0') 728*49e21299Sdinak *mark++ = '\0'; /* null-terminate, just in case */ 729*49e21299Sdinak unescape_str(*serial_no); 730*49e21299Sdinak 731*49e21299Sdinak return (0); 732*49e21299Sdinak } 733*49e21299Sdinak 7347c478bd9Sstevel@tonic-gate /* 7357711facfSdinak * Constructs a fully qualified token name from its label, manufacturer ID 7367711facfSdinak * (if any), and its serial number (if any). Note that the given buf must 7377711facfSdinak * be big enough. Do NOT i18n/l10n. 7387711facfSdinak * 7397711facfSdinak * FULL_NAME_LEN is defined in common.h to be 91 because a fully qualified 7407711facfSdinak * token name adds up this way: 7417711facfSdinak * =32(label) + 32(manuf) + 16(serial) + 4("", ) + 4("", ) + 3("" and nul) 7427c478bd9Sstevel@tonic-gate */ 7437711facfSdinak void 7447711facfSdinak full_token_name(char *token_name, char *manuf_id, char *serial_no, char *buf) 7457c478bd9Sstevel@tonic-gate { 7467711facfSdinak char *marker = buf; 7477711facfSdinak int n_written = 0; 7487711facfSdinak int space_left = FULL_NAME_LEN; 7497c478bd9Sstevel@tonic-gate 7507711facfSdinak if (!token_name) 7517711facfSdinak return; 7527711facfSdinak 7537711facfSdinak n_written = sprintf(buf, "\"%.32s\"", token_name); 7547711facfSdinak marker += n_written; 7557711facfSdinak space_left -= n_written; 7567711facfSdinak 7577711facfSdinak n_written = sprintf(marker, ", \"%.32s\"", manuf_id ? manuf_id : ""); 7587711facfSdinak marker += n_written; 7597711facfSdinak space_left -= n_written; 7607c478bd9Sstevel@tonic-gate 7617711facfSdinak n_written = sprintf(marker, ", \"%.16s\"", serial_no ? serial_no : ""); 7627711facfSdinak marker += n_written; 7637711facfSdinak space_left -= n_written; 7647711facfSdinak 7657711facfSdinak /* space_left should always be >= 1 */ 7667711facfSdinak } 7677711facfSdinak 7687711facfSdinak /* 7697711facfSdinak * Find how many token objects with the given label. 7707711facfSdinak */ 7717711facfSdinak CK_RV 7727711facfSdinak find_obj_count(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label, 7737711facfSdinak CK_ULONG *count) 7747711facfSdinak { 7757711facfSdinak CK_RV rv = CKR_OK; 7767711facfSdinak CK_ATTRIBUTE attrs[4] = { 7777711facfSdinak { CKA_TOKEN, &pk_true, sizeof (pk_true) }, 7787711facfSdinak { 0, NULL, 0 }, 7797711facfSdinak { 0, NULL, 0 }, 7807711facfSdinak { 0, NULL, 0 } 7817711facfSdinak }; 7827711facfSdinak CK_ULONG num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE); 7837711facfSdinak CK_ULONG cur_attr = 1; /* CKA_TOKEN already set */ 7847711facfSdinak CK_OBJECT_CLASS obj_class; 7857711facfSdinak CK_OBJECT_HANDLE tmp_obj; 7867711facfSdinak CK_ULONG obj_count = 0; 7877711facfSdinak 7887711facfSdinak cryptodebug("inside find_obj_count"); 7897711facfSdinak 7907711facfSdinak if (!session_opened || sess == NULL) { 7917711facfSdinak cryptodebug("session handle is null"); 7927711facfSdinak return (CKR_SESSION_HANDLE_INVALID); 7937711facfSdinak } 7947711facfSdinak 7957711facfSdinak if (label) { 7967711facfSdinak cryptodebug("object label was specified"); 7977711facfSdinak attrs[cur_attr].type = CKA_LABEL; 7987711facfSdinak attrs[cur_attr].pValue = label; 7997711facfSdinak attrs[cur_attr].ulValueLen = strlen((char *)label); 8007711facfSdinak cur_attr++; 8017711facfSdinak } 8027711facfSdinak 8037711facfSdinak if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) { 8047711facfSdinak cryptodebug("only searching for private objects"); 8057711facfSdinak attrs[cur_attr].type = CKA_PRIVATE; 8067711facfSdinak attrs[cur_attr].pValue = &pk_true; 8077711facfSdinak attrs[cur_attr].ulValueLen = sizeof (pk_true); 8087711facfSdinak cur_attr++; 8097c478bd9Sstevel@tonic-gate } 8107c478bd9Sstevel@tonic-gate 8117c478bd9Sstevel@tonic-gate /* 8127711facfSdinak * If "certs and all keys" is not specified, but at least either 8137711facfSdinak * "certs" or some "keys" is specified, then go into this block. 8147711facfSdinak * If all certs and keys were specified, there's no point in 8157711facfSdinak * putting that fact in the attribute template -- leave that open, 8167711facfSdinak * and all certs and keys will be matched automatically. 8177711facfSdinak * In other words, only if at least one of 0x10,0x20,0x40,0x80 8187711facfSdinak * bits is off, go into this code block. 8197711facfSdinak * 8207711facfSdinak * NOTE: For now, only one of cert or key types is allowed. 8217711facfSdinak * This needs to change in the future. 8227c478bd9Sstevel@tonic-gate */ 8237711facfSdinak if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) && 8247711facfSdinak ((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) { 8257711facfSdinak if (obj_type & PK_CERT_OBJ) { 8267711facfSdinak cryptodebug("only searching for certificates"); 8277711facfSdinak obj_class = CKO_CERTIFICATE; 8287711facfSdinak } else if (obj_type & PK_PRIKEY_OBJ) { 8297711facfSdinak cryptodebug("only searching for private keys"); 8307711facfSdinak obj_class = CKO_PRIVATE_KEY; 8317711facfSdinak } else if (obj_type & PK_PUBKEY_OBJ) { 8327711facfSdinak cryptodebug("only searching for public keys"); 8337711facfSdinak obj_class = CKO_PUBLIC_KEY; 8347711facfSdinak } else if (obj_type & PK_SECKEY_OBJ) { 8357711facfSdinak cryptodebug("only searching for secret keys"); 8367711facfSdinak obj_class = CKO_SECRET_KEY; 8377711facfSdinak } 8387711facfSdinak 8397711facfSdinak attrs[cur_attr].type = CKA_CLASS; 8407711facfSdinak attrs[cur_attr].pValue = &obj_class; 8417711facfSdinak attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS); 8427711facfSdinak cur_attr++; 8437711facfSdinak } 8447711facfSdinak 8457711facfSdinak /* 8467711facfSdinak * This can't happen now. When finding objects is enhanced in the 8477711facfSdinak * future. this could lead to buffer overruns. 8487711facfSdinak */ 8497711facfSdinak if (cur_attr > num_attrs) 8507711facfSdinak cryptodebug("internal error: attr template overrun"); 8517711facfSdinak 8527711facfSdinak cryptodebug("calling C_FindObjectsInit"); 8537711facfSdinak if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK) 8547711facfSdinak return (rv); 8557711facfSdinak 8567711facfSdinak /* Look for the object, checking if there are more than one. */ 8577711facfSdinak cryptodebug("calling C_FindObjects"); 8587711facfSdinak for (*count = 0; /* empty */; (*count)++) { 8597711facfSdinak if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) != 8607711facfSdinak CKR_OK) 8617711facfSdinak break; 8627711facfSdinak 8637711facfSdinak /* No more found. */ 8647711facfSdinak if (obj_count == 0) 8657711facfSdinak break; 8667711facfSdinak } 8677711facfSdinak 8687711facfSdinak cryptodebug("%d matching objects found", *count); 8697711facfSdinak 8707711facfSdinak cryptodebug("calling C_FindObjectsFinal"); 8717711facfSdinak (void) C_FindObjectsFinal(sess); 8727711facfSdinak return (rv); 8737711facfSdinak } 8747711facfSdinak 8757711facfSdinak /* 8767711facfSdinak * Find the token object with the given label. 8777711facfSdinak */ 8787711facfSdinak CK_RV 8797711facfSdinak find_objs(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label, 8807711facfSdinak CK_OBJECT_HANDLE_PTR *obj, CK_ULONG *count) 8817711facfSdinak { 8827711facfSdinak CK_RV rv = CKR_OK; 8837711facfSdinak CK_ATTRIBUTE attrs[4] = { 8847711facfSdinak { CKA_TOKEN, &pk_true, sizeof (pk_true) }, 8857711facfSdinak { 0, NULL, 0 }, 8867711facfSdinak { 0, NULL, 0 }, 8877711facfSdinak { 0, NULL, 0 } 8887711facfSdinak }; 8897711facfSdinak CK_ULONG num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE); 8907711facfSdinak CK_ULONG cur_attr = 1; /* CKA_TOKEN already set */ 8917711facfSdinak CK_OBJECT_CLASS obj_class; 8927711facfSdinak CK_OBJECT_HANDLE tmp_obj; 8937711facfSdinak CK_ULONG obj_count = 0; 8947711facfSdinak int i; 8957711facfSdinak 8967711facfSdinak cryptodebug("inside find_obj"); 8977711facfSdinak 8987711facfSdinak if ((rv = find_obj_count(sess, obj_type, label, count)) != CKR_OK) 8997711facfSdinak return (rv); 9007711facfSdinak 9017711facfSdinak if (*count == 0) 9027711facfSdinak return (CKR_OK); 9037711facfSdinak 9047711facfSdinak if ((*obj = (CK_OBJECT_HANDLE_PTR) malloc((*count) * 9057711facfSdinak sizeof (CK_OBJECT_HANDLE))) == NULL) { 9067711facfSdinak cryptodebug("no memory for found object"); 9077711facfSdinak return (CKR_HOST_MEMORY); 9087711facfSdinak } 9097711facfSdinak 9107711facfSdinak if (label) { 9117711facfSdinak cryptodebug("object label was specified"); 9127711facfSdinak attrs[cur_attr].type = CKA_LABEL; 9137711facfSdinak attrs[cur_attr].pValue = label; 9147711facfSdinak attrs[cur_attr].ulValueLen = strlen((char *)label); 9157711facfSdinak cur_attr++; 9167711facfSdinak } 9177711facfSdinak 9187711facfSdinak if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) { 9197711facfSdinak cryptodebug("only searching for private objects"); 9207711facfSdinak attrs[cur_attr].type = CKA_PRIVATE; 9217711facfSdinak attrs[cur_attr].pValue = &pk_true; 9227711facfSdinak attrs[cur_attr].ulValueLen = sizeof (pk_true); 9237711facfSdinak cur_attr++; 9247711facfSdinak } 9257711facfSdinak 9267711facfSdinak /* 9277711facfSdinak * If "certs and all keys" is not specified, but at least either 9287711facfSdinak * "certs" or some "keys" is specified, then go into this block. 9297711facfSdinak * If all certs and keys were specified, there's no point in 9307711facfSdinak * putting that fact in the attribute template -- leave that open, 9317711facfSdinak * and all certs and keys will be matched automatically. 9327711facfSdinak * In other words, only if at least one of 0x10,0x20,0x40,0x80 9337711facfSdinak * bits is off, go into this code block. 9347711facfSdinak * 9357711facfSdinak * NOTE: For now, only one of cert or key types is allowed. 9367711facfSdinak * This needs to change in the future. 9377711facfSdinak */ 9387711facfSdinak if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) && 9397711facfSdinak ((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) { 9407711facfSdinak if (obj_type & PK_CERT_OBJ) { 9417711facfSdinak cryptodebug("only searching for certificates"); 9427711facfSdinak obj_class = CKO_CERTIFICATE; 9437711facfSdinak } else if (obj_type & PK_PRIKEY_OBJ) { 9447711facfSdinak cryptodebug("only searching for private keys"); 9457711facfSdinak obj_class = CKO_PRIVATE_KEY; 9467711facfSdinak } else if (obj_type & PK_PUBKEY_OBJ) { 9477711facfSdinak cryptodebug("only searching for public keys"); 9487711facfSdinak obj_class = CKO_PUBLIC_KEY; 9497711facfSdinak } else if (obj_type & PK_SECKEY_OBJ) { 9507711facfSdinak cryptodebug("only searching for secret keys"); 9517711facfSdinak obj_class = CKO_SECRET_KEY; 9527711facfSdinak } 9537711facfSdinak 9547711facfSdinak attrs[cur_attr].type = CKA_CLASS; 9557711facfSdinak attrs[cur_attr].pValue = &obj_class; 9567711facfSdinak attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS); 9577711facfSdinak cur_attr++; 9587711facfSdinak } 9597711facfSdinak 9607711facfSdinak /* 9617711facfSdinak * This can't happen now. When finding objects is enhanced in the 9627711facfSdinak * future. this could lead to buffer overruns. 9637711facfSdinak */ 9647711facfSdinak if (cur_attr > num_attrs) 9657711facfSdinak cryptodebug("internal error: attr template overrun"); 9667711facfSdinak 9677711facfSdinak cryptodebug("calling C_FindObjectsInit"); 9687711facfSdinak if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK) { 9697711facfSdinak free(*obj); 9707711facfSdinak return (rv); 9717711facfSdinak } 9727711facfSdinak 9737711facfSdinak /* 9747711facfSdinak * Find all the matching objects. The loop goes 1 more beyond 9757711facfSdinak * the number of objects found to determine if any new objects 9767711facfSdinak * were created since the time the object count was done. 9777711facfSdinak */ 9787711facfSdinak cryptodebug("calling C_FindObjects"); 9797711facfSdinak for (i = 0; i < (*count) + 1; i++) { 9807711facfSdinak if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) != 9817711facfSdinak CKR_OK) 9827711facfSdinak break; 9837711facfSdinak 9847711facfSdinak /* No more found. */ 9857711facfSdinak if (obj_count == 0) 9867711facfSdinak break; 9877711facfSdinak 9887711facfSdinak /* 9897711facfSdinak * Save the object in the list being created, as long as 9907711facfSdinak * we don't overrun the size of the list. 9917711facfSdinak */ 9927711facfSdinak if (i < *count) 9937711facfSdinak (*obj)[i] = tmp_obj; 9947711facfSdinak else 9957711facfSdinak cryptodebug("number of objects changed since last count"); 9967711facfSdinak } 9977711facfSdinak 9987711facfSdinak if (rv != CKR_OK) { 9997711facfSdinak free(*obj); 10007711facfSdinak } else { 10017711facfSdinak /* 10027711facfSdinak * There are three cases to handle: (1) fewer objects were 10037711facfSdinak * found than originally counted => change *count to the 10047711facfSdinak * smaller number; (2) the number of objects found matches 10057711facfSdinak * the number originally counted => do nothing; (3) more 10067711facfSdinak * objects found than originally counted => list passed 10077711facfSdinak * in is too small to contain the extra object(s), flag 10087711facfSdinak * that in the debug output but don't change number of 10097711facfSdinak * objects returned. The caller can double-check by 10107711facfSdinak * calling find_obj_count() after this function to make 10117711facfSdinak * sure the numbers match, if desired. 10127711facfSdinak */ 10137711facfSdinak /* Case 1: Fewer objects. */ 10147711facfSdinak if (i < *count) { 10157711facfSdinak cryptodebug("%d objects found, expected %d", i, *count); 10167711facfSdinak *count = i; 10177711facfSdinak /* Case 3: More objects. */ 10187711facfSdinak } else if (i > *count) { 10197711facfSdinak cryptodebug("at least %d objects found, expected %d", 10207711facfSdinak i, *count); 10217711facfSdinak } 10227711facfSdinak /* 10237711facfSdinak * Case 2: Same number of objects. 10247711facfSdinak * 10257711facfSdinak * else if (i == *count) 10267711facfSdinak * ; 10277711facfSdinak */ 10287711facfSdinak } 10297711facfSdinak 10307711facfSdinak cryptodebug("calling C_FindObjectsFinal"); 10317711facfSdinak (void) C_FindObjectsFinal(sess); 10327711facfSdinak return (rv); 10337711facfSdinak } 10347711facfSdinak 10357711facfSdinak char * 10367711facfSdinak class_str(CK_OBJECT_CLASS class) 10377711facfSdinak { 10387711facfSdinak switch (class) { 10397711facfSdinak case CKO_DATA: return (gettext("data")); 10407711facfSdinak case CKO_CERTIFICATE: return (gettext("certificate")); 10417711facfSdinak case CKO_PUBLIC_KEY: return (gettext("public key")); 10427711facfSdinak case CKO_PRIVATE_KEY: return (gettext("private key")); 10437711facfSdinak case CKO_SECRET_KEY: return (gettext("secret key")); 10447711facfSdinak case CKO_DOMAIN_PARAMETERS: return (gettext("domain parameter")); 10457711facfSdinak default: return (gettext("unknown object")); 10467711facfSdinak } 10477711facfSdinak } 10487711facfSdinak 10497711facfSdinak char * 10507711facfSdinak keytype_str(CK_KEY_TYPE keytype) 10517711facfSdinak { 10527711facfSdinak switch (keytype) { 10537711facfSdinak case CKK_RSA: return (gettext("RSA")); 10547711facfSdinak case CKK_DSA: return (gettext("DSA")); 10557711facfSdinak case CKK_DH: return (gettext("Diffie-Hellman")); 10567711facfSdinak case CKK_X9_42_DH: return (gettext("X9.42 Diffie-Hellman")); 10577711facfSdinak case CKK_GENERIC_SECRET: return (gettext("generic")); 10587711facfSdinak case CKK_RC2: return (gettext("RC2")); 10597711facfSdinak case CKK_RC4: return (gettext("RC4")); 10607711facfSdinak case CKK_DES: return (gettext("DES")); 10617711facfSdinak case CKK_DES2: return (gettext("Double-DES")); 10627711facfSdinak case CKK_DES3: return (gettext("Triple-DES")); 10637711facfSdinak case CKK_RC5: return (gettext("RC5")); 10647711facfSdinak case CKK_AES: return (gettext("AES")); 10657711facfSdinak default: return (gettext("typeless")); 10667c478bd9Sstevel@tonic-gate } 10677711facfSdinak } 10687c478bd9Sstevel@tonic-gate 10697711facfSdinak char * 10707711facfSdinak attr_str(CK_ATTRIBUTE_TYPE attrtype) 10717711facfSdinak { 10727711facfSdinak switch (attrtype) { 10737711facfSdinak case CKA_PRIVATE: return (gettext("private")); 10747711facfSdinak case CKA_LOCAL: return (gettext("local")); 10757711facfSdinak case CKA_SENSITIVE: return (gettext("sensitive")); 10767711facfSdinak case CKA_EXTRACTABLE: return (gettext("extractable")); 10777711facfSdinak case CKA_ENCRYPT: return (gettext("encrypt")); 10787711facfSdinak case CKA_DECRYPT: return (gettext("decrypt")); 10797711facfSdinak case CKA_WRAP: return (gettext("wrap")); 10807711facfSdinak case CKA_UNWRAP: return (gettext("unwrap")); 10817711facfSdinak case CKA_SIGN: return (gettext("sign")); 10827711facfSdinak case CKA_SIGN_RECOVER: return (gettext("sign-recover")); 10837711facfSdinak case CKA_VERIFY: return (gettext("verify")); 10847711facfSdinak case CKA_VERIFY_RECOVER: return (gettext("verify-recover")); 10857711facfSdinak case CKA_DERIVE: return (gettext("derive")); 10867711facfSdinak case CKA_ALWAYS_SENSITIVE: return (gettext("always sensitive")); 10877711facfSdinak case CKA_NEVER_EXTRACTABLE: return (gettext("never extractable")); 10887711facfSdinak default: return (gettext("unknown capability")); 10897711facfSdinak } 10907c478bd9Sstevel@tonic-gate } 10917c478bd9Sstevel@tonic-gate 10927c478bd9Sstevel@tonic-gate /* 10937711facfSdinak * Convert a byte string into a string of octets formatted like this: 10947711facfSdinak * oo oo oo oo oo ... oo 10957711facfSdinak * where each "oo" is an octet is space separated and in the form: 10967711facfSdinak * [0-f][0-f] if the octet is a non-printable character 10977711facfSdinak * <space><char> if the octet is a printable character 10987711facfSdinak * 10997711facfSdinak * Note: octets_sz must be 3 * str_sz + 1, or at least as long as "blank" 11007c478bd9Sstevel@tonic-gate */ 11017c478bd9Sstevel@tonic-gate void 11027711facfSdinak octetify(CK_BYTE *str, CK_ULONG str_sz, char *octets, int octets_sz, 11037711facfSdinak boolean_t stop_on_nul, boolean_t do_ascii, int limit, char *indent, 11047711facfSdinak char *blank) 11057c478bd9Sstevel@tonic-gate { 11067711facfSdinak char *marker; 11077711facfSdinak int nc; 11087711facfSdinak int newline; 11097711facfSdinak int indent_len; 11107711facfSdinak boolean_t first = B_TRUE; 11117711facfSdinak 11127711facfSdinak cryptodebug("inside octetify"); 11137711facfSdinak 11147711facfSdinak cryptodebug(stop_on_nul ? "stopping on first nul found" : 11157711facfSdinak "continuing to full length of buffer"); 11167711facfSdinak cryptodebug(do_ascii ? "using ascii chars where printable" : 11177711facfSdinak "using only hex octets"); 11187711facfSdinak cryptodebug("every %d characters indent with \"%s\"\n ", limit, indent); 11197711facfSdinak cryptodebug("return \"%s\" if buffer is null or empty", blank); 11207c478bd9Sstevel@tonic-gate 11217711facfSdinak /* If string is empty, write as much of the blank string and leave. */ 11227711facfSdinak if (str_sz == 0) { 11237711facfSdinak (void) snprintf(octets, octets_sz, "%s", blank); 11247711facfSdinak return; 11257c478bd9Sstevel@tonic-gate } 11267711facfSdinak 11277711facfSdinak /* If only limit or indent is set, pick default for the other. */ 11287711facfSdinak if (limit > 0 && indent == NULL) 11297711facfSdinak indent = "\n"; 11307711facfSdinak if (indent != NULL && limit == 0) 11317711facfSdinak limit = 60; 11327711facfSdinak indent_len = strlen(indent); 11337711facfSdinak 11347711facfSdinak for (marker = octets, newline = 0, first = B_TRUE; 11357711facfSdinak (stop_on_nul && *str != '\0') || 11367711facfSdinak (!stop_on_nul && str_sz > 0 && octets_sz > 0); 11377711facfSdinak str++, str_sz--, marker += nc, octets_sz -= nc) { 11387711facfSdinak if (!first) { 11397711facfSdinak if (limit > 0 && ((marker - octets) / limit) > 11407711facfSdinak newline) { 11417711facfSdinak nc = snprintf(marker, indent_len, "%s", indent); 11427711facfSdinak newline++; 11437711facfSdinak continue; 11447711facfSdinak } 11457711facfSdinak nc = sprintf(marker, 11467711facfSdinak ((do_ascii && isprint(*str) && !isspace(*str)) ? 11477711facfSdinak "%s%c" : "%s%02x"), (do_ascii ? " " : ":"), *str); 11487711facfSdinak } else { 11497711facfSdinak nc = sprintf(marker, 11507711facfSdinak ((do_ascii && isprint(*str) && !isspace(*str)) ? 11517711facfSdinak "%c" : "%02x"), *str); 11527711facfSdinak first = B_FALSE; 11537711facfSdinak } 11547711facfSdinak } 11557711facfSdinak *marker = '\0'; 11567711facfSdinak } 11577711facfSdinak 11587711facfSdinak /* 11597711facfSdinak * Copies a biginteger_t to a template attribute. 11607711facfSdinak * Should be a macro instead of a function. 11617711facfSdinak */ 11627711facfSdinak void 11637711facfSdinak copy_bigint_to_attr(biginteger_t big, CK_ATTRIBUTE_PTR attr) 11647711facfSdinak { 11657711facfSdinak attr->pValue = big.big_value; 11667711facfSdinak attr->ulValueLen = big.big_value_len; 11677711facfSdinak } 11687711facfSdinak 11697711facfSdinak /* 11707711facfSdinak * Copies a string and its length to a template attribute. 11717711facfSdinak * Should be a macro instead of a function. 11727711facfSdinak */ 11737711facfSdinak void 11747711facfSdinak copy_string_to_attr(CK_BYTE *buf, CK_ULONG buflen, CK_ATTRIBUTE_PTR attr) 11757711facfSdinak { 11767711facfSdinak attr->pValue = buf; 11777711facfSdinak attr->ulValueLen = buflen; 11787711facfSdinak } 11797711facfSdinak 11807711facfSdinak /* 11817711facfSdinak * Copies a template attribute to a biginteger_t. 11827711facfSdinak * Should be a macro instead of a function. 11837711facfSdinak */ 11847711facfSdinak void 11857711facfSdinak copy_attr_to_bigint(CK_ATTRIBUTE_PTR attr, biginteger_t *big) 11867711facfSdinak { 11877711facfSdinak big->big_value = attr->pValue; 11887711facfSdinak big->big_value_len = attr->ulValueLen; 11897711facfSdinak } 11907711facfSdinak 11917711facfSdinak /* 11927711facfSdinak * Copies a template attribute to a string and its length. 11937711facfSdinak * Should be a macro instead of a function. 11947711facfSdinak */ 11957711facfSdinak void 11967711facfSdinak copy_attr_to_string(CK_ATTRIBUTE_PTR attr, CK_BYTE **buf, CK_ULONG *buflen) 11977711facfSdinak { 11987711facfSdinak *buf = attr->pValue; 11997711facfSdinak *buflen = attr->ulValueLen; 12007711facfSdinak } 12017711facfSdinak 12027711facfSdinak /* 12037711facfSdinak * Copies a template attribute to a date and its length. 12047711facfSdinak * Should be a macro instead of a function. 12057711facfSdinak */ 12067711facfSdinak void 12077711facfSdinak copy_attr_to_date(CK_ATTRIBUTE_PTR attr, CK_DATE **buf, CK_ULONG *buflen) 12087711facfSdinak { 12097711facfSdinak *buf = (CK_DATE *)attr->pValue; 12107711facfSdinak *buflen = attr->ulValueLen; 12117c478bd9Sstevel@tonic-gate } 1212*49e21299Sdinak 1213*49e21299Sdinak /* 1214*49e21299Sdinak * Breaks out the getopt-style option string into a structure that can be 1215*49e21299Sdinak * traversed later for calls to getopt_av(). Option string is NOT altered, 1216*49e21299Sdinak * but the struct fields point to locations within option string. 1217*49e21299Sdinak */ 1218*49e21299Sdinak static int 1219*49e21299Sdinak populate_opts(char *optstring) 1220*49e21299Sdinak { 1221*49e21299Sdinak int i; 1222*49e21299Sdinak av_opts *temp; 1223*49e21299Sdinak char *marker; 1224*49e21299Sdinak 1225*49e21299Sdinak if (optstring == NULL || *optstring == '\0') 1226*49e21299Sdinak return (0); 1227*49e21299Sdinak 1228*49e21299Sdinak /* 1229*49e21299Sdinak * This tries to imitate getopt(3c) Each option must conform to: 1230*49e21299Sdinak * <short name char> [ ':' ] [ '(' <long name string> ')' ] 1231*49e21299Sdinak * If long name is missing, the short name is used for long name. 1232*49e21299Sdinak */ 1233*49e21299Sdinak for (i = 0; *optstring != '\0'; i++) { 1234*49e21299Sdinak if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) : 1235*49e21299Sdinak realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) { 1236*49e21299Sdinak free(opts_av); 1237*49e21299Sdinak opts_av = NULL; 1238*49e21299Sdinak return (0); 1239*49e21299Sdinak } else 1240*49e21299Sdinak opts_av = (av_opts *)temp; 1241*49e21299Sdinak 1242*49e21299Sdinak marker = optstring; /* may need optstring later */ 1243*49e21299Sdinak 1244*49e21299Sdinak opts_av[i].shortnm = *marker++; /* set short name */ 1245*49e21299Sdinak 1246*49e21299Sdinak if (*marker == ':') { /* check for opt arg */ 1247*49e21299Sdinak marker++; 1248*49e21299Sdinak opts_av[i].has_arg = B_TRUE; 1249*49e21299Sdinak } 1250*49e21299Sdinak 1251*49e21299Sdinak if (*marker == '(') { /* check and set long name */ 1252*49e21299Sdinak marker++; 1253*49e21299Sdinak opts_av[i].longnm = marker; 1254*49e21299Sdinak opts_av[i].longnm_len = strcspn(marker, ")"); 1255*49e21299Sdinak optstring = marker + opts_av[i].longnm_len + 1; 1256*49e21299Sdinak } else { 1257*49e21299Sdinak /* use short name option character */ 1258*49e21299Sdinak opts_av[i].longnm = optstring; 1259*49e21299Sdinak opts_av[i].longnm_len = 1; 1260*49e21299Sdinak optstring = marker; 1261*49e21299Sdinak } 1262*49e21299Sdinak } 1263*49e21299Sdinak 1264*49e21299Sdinak return (i); 1265*49e21299Sdinak } 1266*49e21299Sdinak 1267*49e21299Sdinak /* 1268*49e21299Sdinak * getopt_av() is very similar to getopt(3c) in that the takes an option 1269*49e21299Sdinak * string, compares command line arguments for matches, and returns a single 1270*49e21299Sdinak * letter option when a match is found. However, getopt_av() differs from 1271*49e21299Sdinak * getopt(3c) by requiring that only longname options and values be found 1272*49e21299Sdinak * on the command line and all leading dashes are omitted. In other words, 1273*49e21299Sdinak * it tries to enforce only longname "option=value" arguments on the command 1274*49e21299Sdinak * line. Boolean options are not allowed either. 1275*49e21299Sdinak */ 1276*49e21299Sdinak int 1277*49e21299Sdinak getopt_av(int argc, char * const *argv, const char *optstring) 1278*49e21299Sdinak { 1279*49e21299Sdinak int i; 1280*49e21299Sdinak int len; 1281*49e21299Sdinak 1282*49e21299Sdinak if (optind_av >= argc) 1283*49e21299Sdinak return (EOF); 1284*49e21299Sdinak 1285*49e21299Sdinak /* First time or when optstring changes from previous one */ 1286*49e21299Sdinak if (_save_optstr != optstring) { 1287*49e21299Sdinak if (opts_av != NULL) 1288*49e21299Sdinak free(opts_av); 1289*49e21299Sdinak opts_av = NULL; 1290*49e21299Sdinak _save_optstr = optstring; 1291*49e21299Sdinak _save_numopts = populate_opts((char *)optstring); 1292*49e21299Sdinak } 1293*49e21299Sdinak 1294*49e21299Sdinak for (i = 0; i < _save_numopts; i++) { 1295*49e21299Sdinak if (strcmp(argv[optind_av], "--") == 0) { 1296*49e21299Sdinak optind_av++; 1297*49e21299Sdinak break; 1298*49e21299Sdinak } 1299*49e21299Sdinak 1300*49e21299Sdinak len = strcspn(argv[optind_av], "="); 1301*49e21299Sdinak 1302*49e21299Sdinak if (len == opts_av[i].longnm_len && strncmp(argv[optind_av], 1303*49e21299Sdinak opts_av[i].longnm, opts_av[i].longnm_len) == 0) { 1304*49e21299Sdinak /* matched */ 1305*49e21299Sdinak if (!opts_av[i].has_arg) { 1306*49e21299Sdinak optind_av++; 1307*49e21299Sdinak return (opts_av[i].shortnm); 1308*49e21299Sdinak } 1309*49e21299Sdinak 1310*49e21299Sdinak /* needs optarg */ 1311*49e21299Sdinak if (argv[optind_av][len] == '=') { 1312*49e21299Sdinak optarg_av = &(argv[optind_av][len+1]); 1313*49e21299Sdinak optind_av++; 1314*49e21299Sdinak return (opts_av[i].shortnm); 1315*49e21299Sdinak } 1316*49e21299Sdinak 1317*49e21299Sdinak optarg_av = NULL; 1318*49e21299Sdinak optind_av++; 1319*49e21299Sdinak return ((int)'?'); 1320*49e21299Sdinak } 1321*49e21299Sdinak } 1322*49e21299Sdinak 1323*49e21299Sdinak return (EOF); 1324*49e21299Sdinak } 1325