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