xref: /onnv-gate/usr/src/cmd/cmd-crypto/pktool/common.c (revision 864:75df3e9f93de)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
2317Sdinak  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * This file contains the functions that are shared among
310Sstevel@tonic-gate  * the various services this tool will ultimately provide.
3217Sdinak  * The functions in this file return PKCS#11 CK_RV errors.
3317Sdinak  * Only one session and one login per token is supported
3417Sdinak  * at this time.
350Sstevel@tonic-gate  */
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include <stdio.h>
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <string.h>
400Sstevel@tonic-gate #include <ctype.h>
410Sstevel@tonic-gate #include <cryptoutil.h>
420Sstevel@tonic-gate #include <security/cryptoki.h>
430Sstevel@tonic-gate #include "common.h"
4417Sdinak #include "biginteger.h"
450Sstevel@tonic-gate 
4617Sdinak /* True and false for attribute templates. */
4717Sdinak CK_BBOOL	pk_true = B_TRUE;
4817Sdinak CK_BBOOL	pk_false = B_FALSE;
4917Sdinak 
5017Sdinak /* Local status variables. */
5117Sdinak static boolean_t	initialized = B_FALSE;
5217Sdinak static boolean_t	session_opened = B_FALSE;
5317Sdinak static boolean_t	session_writable = B_FALSE;
5417Sdinak static boolean_t	logged_in = B_FALSE;
5517Sdinak 
56*864Sdinak /* Supporting structures and global variables for getopt_av(). */
57*864Sdinak typedef struct	av_opts_s {
58*864Sdinak 	int		shortnm;	/* short name character */
59*864Sdinak 	char		*longnm;	/* long name string, NOT terminated */
60*864Sdinak 	int		longnm_len;	/* length of long name string */
61*864Sdinak 	boolean_t	has_arg;	/* takes optional argument */
62*864Sdinak } av_opts;
63*864Sdinak static av_opts		*opts_av = NULL;
64*864Sdinak static const char	*_save_optstr = NULL;
65*864Sdinak static int		_save_numopts = 0;
66*864Sdinak 
67*864Sdinak int			optind_av = 1;
68*864Sdinak char			*optarg_av = NULL;
69*864Sdinak 
7017Sdinak /*
7117Sdinak  * Perform PKCS#11 setup here.  Currently only C_Initialize is required,
7217Sdinak  * along with setting/resetting state variables.
7317Sdinak  */
7417Sdinak CK_RV
7517Sdinak init_pk11(void)
7617Sdinak {
7717Sdinak 	CK_RV		rv = CKR_OK;
7817Sdinak 
7917Sdinak 	cryptodebug("inside init_pk11");
8017Sdinak 
8117Sdinak 	/* If C_Initialize() already called, nothing to do here. */
8217Sdinak 	if (initialized == B_TRUE)
8317Sdinak 		return (CKR_OK);
8417Sdinak 
8517Sdinak 	/* Reset state variables because C_Initialize() not yet done. */
8617Sdinak 	session_opened = B_FALSE;
8717Sdinak 	session_writable = B_FALSE;
8817Sdinak 	logged_in = B_FALSE;
8917Sdinak 
9017Sdinak 	/* Initialize PKCS#11 library. */
9117Sdinak 	cryptodebug("calling C_Initialize()");
9217Sdinak 	if ((rv = C_Initialize(NULL_PTR)) != CKR_OK &&
9317Sdinak 	    rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
9417Sdinak 		return (rv);
9517Sdinak 	}
9617Sdinak 
9717Sdinak 	initialized = B_TRUE;
9817Sdinak 	return (CKR_OK);
9917Sdinak }
10017Sdinak 
10117Sdinak /*
10217Sdinak  * Finalize PKCS#11 library and reset state variables.  Open sessions,
10317Sdinak  * if any, are closed, and thereby any logins are logged out also.
10417Sdinak  */
10517Sdinak void
10617Sdinak final_pk11(CK_SESSION_HANDLE sess)
10717Sdinak {
10817Sdinak 	cryptodebug("inside final_pk11");
10917Sdinak 
11017Sdinak 	/* If the library wasn't initialized, nothing to do here. */
11117Sdinak 	if (!initialized)
11217Sdinak 		return;
11317Sdinak 
11417Sdinak 	/* Make sure the sesion is closed first. */
11517Sdinak 	close_sess(sess);
11617Sdinak 
11717Sdinak 	cryptodebug("calling C_Finalize()");
11817Sdinak 	(void) C_Finalize(NULL);
11917Sdinak 	initialized = B_FALSE;
12017Sdinak }
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate /*
12317Sdinak  * Create a PKCS#11 session on the given slot, and set state information.
12417Sdinak  * If session is already open, check that the read-only/read-write state
12517Sdinak  * requested matches that of the session.  If it doesn't, make it so.
1260Sstevel@tonic-gate  */
12717Sdinak CK_RV
12817Sdinak open_sess(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_SESSION_HANDLE_PTR sess)
1290Sstevel@tonic-gate {
13017Sdinak 	CK_RV		rv = CKR_OK;
13117Sdinak 
13217Sdinak 	cryptodebug("inside open_sess");
13317Sdinak 
13417Sdinak 	/* If the session is already open, check the session flags. */
13517Sdinak 	if (session_opened) {
13617Sdinak 		/*
13717Sdinak 		 * If requesting R/W session and it is currently R/O,
13817Sdinak 		 * need to close the session and reopen it R/W.  The
13917Sdinak 		 * other cases are considered acceptable:
14017Sdinak 		 *	sess_flags		current state
14117Sdinak 		 *	----------		-------------
14217Sdinak 		 *	~CKF_RW_SESSION		!session_writable
14317Sdinak 		 *	~CKF_RW_SESSION		session_writable
14417Sdinak 		 *	CKF_RW_SESSION		session_writable
14517Sdinak 		 */
14617Sdinak 		if ((sess_flags & CKF_RW_SESSION) && !session_writable)
14717Sdinak 			close_sess(*sess);
14817Sdinak 		else
14917Sdinak 			return (CKR_OK);
15017Sdinak 	}
15117Sdinak 
15217Sdinak 	/* Make sure the PKCS#11 is already initialized. */
15317Sdinak 	if (!initialized)
15417Sdinak 		if ((rv = init_pk11()) != CKR_OK)
15517Sdinak 			return (rv);
15617Sdinak 
15717Sdinak 	/* Create a session for subsequent operations. */
15817Sdinak 	cryptodebug("calling C_OpenSession()");
15917Sdinak 	if ((rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION|sess_flags,
16017Sdinak 	    NULL, NULL, sess)) != CKR_OK)
16117Sdinak 		return (rv);
16217Sdinak 	session_opened = B_TRUE;
16317Sdinak 	session_writable = (sess_flags & CKF_RW_SESSION) ? B_TRUE : B_FALSE;
16417Sdinak 	return (CKR_OK);
16517Sdinak }
16617Sdinak 
16717Sdinak /*
16817Sdinak  * Close PKCS#11 session and reset state variables.  Any logins are
16917Sdinak  * logged out.
17017Sdinak  */
17117Sdinak void
17217Sdinak close_sess(CK_SESSION_HANDLE sess)
17317Sdinak {
17417Sdinak 	cryptodebug("inside close_sess");
17517Sdinak 
17617Sdinak 	if (sess == NULL) {
17717Sdinak 		cryptodebug("session handle is null");
17817Sdinak 		return;
17917Sdinak 	}
18017Sdinak 
18117Sdinak 	/* If session is already closed, nothing to do here. */
18217Sdinak 	session_writable = B_FALSE;
18317Sdinak 	if (!session_opened)
18417Sdinak 		return;
1850Sstevel@tonic-gate 
18617Sdinak 	/* Make sure user is logged out of token. */
18717Sdinak 	logout_token(sess);
18817Sdinak 
18917Sdinak 	cryptodebug("calling C_CloseSession()");
19017Sdinak 	(void) C_CloseSession(sess);
19117Sdinak 	session_opened = B_FALSE;
19217Sdinak }
19317Sdinak 
19417Sdinak /*
19517Sdinak  * Log user into token in given slot.  If this first login ever for this
19617Sdinak  * token, the initial PIN is "changeme", C_Login() will succeed, but all
19717Sdinak  * PKCS#11 calls following the C_Login() will fail with CKR_PIN_EXPIRED.
19817Sdinak  */
19917Sdinak CK_RV
20017Sdinak login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pinlen,
20117Sdinak 	    CK_SESSION_HANDLE_PTR sess)
20217Sdinak {
20317Sdinak 	CK_RV		rv = CKR_OK;
20417Sdinak 
20517Sdinak 	cryptodebug("inside login_token");
20617Sdinak 
20717Sdinak 	/* If already logged in, nothing to do here. */
20817Sdinak 	if (logged_in)
20917Sdinak 		return (CKR_OK);
21017Sdinak 
21117Sdinak 	/* Make sure we have a session first, assume R/O is enough. */
21217Sdinak 	if (!session_opened)
21317Sdinak 		if ((rv = open_sess(slot_id, CKF_SERIAL_SESSION, sess)) !=
21417Sdinak 		    CKR_OK)
21517Sdinak 			return (rv);
2160Sstevel@tonic-gate 
21717Sdinak 	/* Log the user into the token. */
21817Sdinak 	cryptodebug("calling C_Login()");
21917Sdinak 	if ((rv = C_Login(*sess, CKU_USER, pin, pinlen)) != CKR_OK) {
22017Sdinak 		cryptodebug("C_Login returns %s", pkcs11_strerror(rv));
22117Sdinak 		return (rv);
22217Sdinak 	}
22317Sdinak 
22417Sdinak 	logged_in = B_TRUE;
22517Sdinak 	return (CKR_OK);
22617Sdinak }
2270Sstevel@tonic-gate 
22817Sdinak /*
22917Sdinak  * Log user out of token and reset status variable.
23017Sdinak  */
23117Sdinak void
23217Sdinak logout_token(CK_SESSION_HANDLE sess)
23317Sdinak {
23417Sdinak 	cryptodebug("inside logout_token");
23517Sdinak 
23617Sdinak 	if (sess == NULL) {
23717Sdinak 		cryptodebug("session handle is null");
23817Sdinak 		return;
23917Sdinak 	}
24017Sdinak 
24117Sdinak 	/* If already logged out, nothing to do here. */
24217Sdinak 	if (!logged_in)
24317Sdinak 		return;
24417Sdinak 
24517Sdinak 	cryptodebug("calling C_Logout()");
24617Sdinak 	(void) C_Logout(sess);
24717Sdinak 	logged_in = B_FALSE;
2480Sstevel@tonic-gate }
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate /*
25117Sdinak  * Shortcut function to get from an uninitialized state to user logged in.
25217Sdinak  * If the library is already initialized, the session is already opened,
25317Sdinak  * or the user is already logged in, those steps are skipped and the next
25417Sdinak  * step is checked.
2550Sstevel@tonic-gate  */
25617Sdinak CK_RV
25717Sdinak quick_start(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_UTF8CHAR_PTR pin,
25817Sdinak 	    CK_ULONG pinlen, CK_SESSION_HANDLE_PTR sess)
25917Sdinak {
26017Sdinak 	CK_RV		rv = CKR_OK;
26117Sdinak 
26217Sdinak 	cryptodebug("inside quick_start");
26317Sdinak 
26417Sdinak 	/* Call open_sess() explicitly if R/W session is needed. */
26517Sdinak 	if (sess_flags & CKF_RW_SESSION)
26617Sdinak 		if ((rv = open_sess(slot_id, sess_flags, sess)) != CKR_OK)
26717Sdinak 			return (rv);
26817Sdinak 
26917Sdinak 	if ((rv = login_token(slot_id, pin, pinlen, sess)) != CKR_OK)
27017Sdinak 		return (rv);
27117Sdinak 
27217Sdinak 	return (CKR_OK);
27317Sdinak }
27417Sdinak 
27517Sdinak /*
27617Sdinak  * Shortcut function to go from any state to uninitialized PKCS#11 library.
27717Sdinak  */
27817Sdinak void
27917Sdinak quick_finish(CK_SESSION_HANDLE sess)
2800Sstevel@tonic-gate {
28117Sdinak 	cryptodebug("inside quick_finish");
28217Sdinak 
28317Sdinak 	/* All the needed calls are done implicitly. */
28417Sdinak 	final_pk11(sess);
28517Sdinak }
2860Sstevel@tonic-gate 
28717Sdinak /*
28817Sdinak  * Gets PIN from user.  Caller needs to free the returned PIN when done.
28917Sdinak  * If two prompts are given, the PIN is confirmed with second prompt.
29017Sdinak  * Note that getphassphrase() may return data in static memory area.
29117Sdinak  */
29217Sdinak CK_RV
29317Sdinak get_pin(char *prompt1, char *prompt2, CK_UTF8CHAR_PTR *pin, CK_ULONG *pinlen)
29417Sdinak {
29517Sdinak 	char		*save_phrase, *phrase1, *phrase2;
29617Sdinak 
29717Sdinak 	cryptodebug("inside get_pin");
2980Sstevel@tonic-gate 
29917Sdinak 	/* Prompt user for a PIN. */
30017Sdinak 	if (prompt1 == NULL) {
30117Sdinak 		cryptodebug("no passphrase prompt given");
30217Sdinak 		return (CKR_ARGUMENTS_BAD);
30317Sdinak 	}
30417Sdinak 	if ((phrase1 = getpassphrase(prompt1)) == NULL) {
30517Sdinak 		cryptodebug("getpassphrase() failed");
30617Sdinak 		return (CKR_FUNCTION_FAILED);
30717Sdinak 	}
30817Sdinak 
30917Sdinak 	/* Duplicate 1st PIN in separate chunk of memory. */
31017Sdinak 	if ((save_phrase = strdup(phrase1)) == NULL)
31117Sdinak 		return (CKR_HOST_MEMORY);
31217Sdinak 
31317Sdinak 	/* If second prompt given, PIN confirmation is requested. */
31417Sdinak 	if (prompt2 != NULL) {
31517Sdinak 		if ((phrase2 = getpassphrase(prompt2)) == NULL) {
31617Sdinak 			cryptodebug("getpassphrase() confirmation failed");
31717Sdinak 			free(save_phrase);
31817Sdinak 			return (CKR_FUNCTION_FAILED);
31917Sdinak 		}
32017Sdinak 		if (strcmp(save_phrase, phrase2) != 0) {
32117Sdinak 			cryptodebug("passphrases do not match");
32217Sdinak 			free(save_phrase);
32317Sdinak 			return (CKR_PIN_INCORRECT);
32417Sdinak 		}
3250Sstevel@tonic-gate 	}
3260Sstevel@tonic-gate 
32717Sdinak 	*pin = (CK_UTF8CHAR_PTR)save_phrase;
32817Sdinak 	*pinlen = strlen(save_phrase);
32917Sdinak 	return (CKR_OK);
33017Sdinak }
33117Sdinak 
33217Sdinak /*
33317Sdinak  * Gets yes/no response from user.  If either no prompt is supplied, a
33417Sdinak  * default prompt is used.  If not message for invalid input is supplied,
33517Sdinak  * a default will not be provided.  If the user provides no response,
33617Sdinak  * the input default B_TRUE == yes, B_FALSE == no is returned.
33717Sdinak  * Otherwise, B_TRUE is returned for yes, and B_FALSE for no.
33817Sdinak  */
33917Sdinak boolean_t
34017Sdinak yesno(char *prompt, char *invalid, boolean_t dflt)
34117Sdinak {
34217Sdinak 	char		*response, buf[1024];
34317Sdinak 	char		*yes = gettext("yes");
34417Sdinak 	char		*no = gettext("no");
34517Sdinak 
34617Sdinak 	cryptodebug("inside yesno");
34717Sdinak 
34817Sdinak 	if (prompt == NULL)
34917Sdinak 		prompt = gettext("Enter (y)es or (n)o? ");
35017Sdinak 
35117Sdinak 	for (;;) {
35217Sdinak 		/* Prompt user. */
35317Sdinak 		(void) printf("%s", prompt);
35417Sdinak 		(void) fflush(stdout);
35517Sdinak 
35617Sdinak 		/* Get the response. */
35717Sdinak 		if ((response = fgets(buf, sizeof (buf), stdin)) == NULL)
35817Sdinak 			break;		/* go to default response */
35917Sdinak 
36017Sdinak 		/* Skip any leading white space. */
36117Sdinak 		while (isspace(*response))
36217Sdinak 			response++;
36317Sdinak 		if (*response == '\0')
36417Sdinak 			break;		/* go to default response */
36517Sdinak 
36617Sdinak 		/* Is it valid input?  Return appropriately. */
36717Sdinak 		if (strncasecmp(response, yes, 1) == 0)
36817Sdinak 			return (B_TRUE);
36917Sdinak 		if (strncasecmp(response, no, 1) == 0)
37017Sdinak 			return (B_FALSE);
37117Sdinak 
37217Sdinak 		/* Indicate invalid input, and try again. */
37317Sdinak 		if (invalid != NULL)
37417Sdinak 		    (void) printf("%s", invalid);
37517Sdinak 	}
37617Sdinak 	return (dflt);
37717Sdinak }
37817Sdinak 
37917Sdinak /*
38017Sdinak  * Gets the list of slots which have tokens in them.  Keeps adjusting
38117Sdinak  * the size of the slot list buffer until the call is successful or an
38217Sdinak  * irrecoverable error occurs.
38317Sdinak  */
38417Sdinak CK_RV
38517Sdinak get_token_slots(CK_SLOT_ID_PTR *slot_list, CK_ULONG *slot_count)
38617Sdinak {
38717Sdinak 	CK_ULONG	tmp_count = 0;
38817Sdinak 	CK_SLOT_ID_PTR	tmp_list = NULL_PTR, tmp2_list = NULL_PTR;
38917Sdinak 	int		rv = CKR_OK;
39017Sdinak 
39117Sdinak 	cryptodebug("inside get_token_slots");
39217Sdinak 
39317Sdinak 	if (!initialized)
39417Sdinak 		if ((rv = init_pk11()) != CKR_OK)
39517Sdinak 			return (rv);
39617Sdinak 
39717Sdinak 	/*
39817Sdinak 	 * Get the slot count first because we don't know how many
39917Sdinak 	 * slots there are and how many of those slots even have tokens.
40017Sdinak 	 * Don't specify an arbitrary buffer size for the slot list;
40117Sdinak 	 * it may be too small (see section 11.5 of PKCS#11 spec).
40217Sdinak 	 * Also select only those slots that have tokens in them,
40317Sdinak 	 * because this tool has no need to know about empty slots.
40417Sdinak 	 */
40517Sdinak 	cryptodebug("calling C_GetSlotList() for slot count");
40617Sdinak 	if ((rv = C_GetSlotList(1, NULL_PTR, &tmp_count)) != CKR_OK)
40717Sdinak 		return (rv);
40817Sdinak 
40917Sdinak 	if (tmp_count == 0) {
41017Sdinak 		cryptodebug("no slots with tokens found");
41117Sdinak 		*slot_list = NULL_PTR;
41217Sdinak 		*slot_count = 0;
41317Sdinak 		return (CKR_OK);
41417Sdinak 	}
41517Sdinak 
41617Sdinak 	/* Allocate initial space for the slot list. */
41717Sdinak 	if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count *
41817Sdinak 	    sizeof (CK_SLOT_ID))) == NULL)
41917Sdinak 		return (CKR_HOST_MEMORY);
42017Sdinak 
42117Sdinak 	/* Then get the slot list itself. */
42217Sdinak 	for (;;) {
42317Sdinak 		cryptodebug("calling C_GetSlotList()");
42417Sdinak 		if ((rv = C_GetSlotList(1, tmp_list, &tmp_count)) == CKR_OK) {
42517Sdinak 			*slot_list = tmp_list;
42617Sdinak 			*slot_count = tmp_count;
42717Sdinak 			break;
42817Sdinak 		}
42917Sdinak 
43017Sdinak 		if (rv != CKR_BUFFER_TOO_SMALL) {
43117Sdinak 			free(tmp_list);
43217Sdinak 			break;
43317Sdinak 		}
43417Sdinak 
43517Sdinak 		/* If the number of slots grew, try again. */
43617Sdinak 		cryptodebug("number of tokens present increased");
43717Sdinak 		if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list,
43817Sdinak 		    tmp_count * sizeof (CK_SLOT_ID))) == NULL) {
43917Sdinak 			free(tmp_list);
44017Sdinak 			rv = CKR_HOST_MEMORY;
44117Sdinak 			break;
44217Sdinak 		}
44317Sdinak 		tmp_list = tmp2_list;
44417Sdinak 	}
44517Sdinak 
44617Sdinak 	return (rv);
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate /*
4500Sstevel@tonic-gate  * memcmp_pad_max() is a specialized version of memcmp() which
4510Sstevel@tonic-gate  * compares two pieces of data up to a maximum length.  If the
4520Sstevel@tonic-gate  * the two data match up the maximum length, they are considered
4530Sstevel@tonic-gate  * matching.  Trailing blanks do not cause the match to fail if
4540Sstevel@tonic-gate  * one of the data is shorted.
4550Sstevel@tonic-gate  *
4560Sstevel@tonic-gate  * Examples of matches:
4570Sstevel@tonic-gate  *	"one"           |
4580Sstevel@tonic-gate  *	"one      "     |
4590Sstevel@tonic-gate  *	                ^maximum length
4600Sstevel@tonic-gate  *
4610Sstevel@tonic-gate  *	"Number One     |  X"	(X is beyond maximum length)
4620Sstevel@tonic-gate  *	"Number One   " |
4630Sstevel@tonic-gate  *	                ^maximum length
4640Sstevel@tonic-gate  *
4650Sstevel@tonic-gate  * Examples of mismatches:
4660Sstevel@tonic-gate  *	" one"
4670Sstevel@tonic-gate  *	"one"
4680Sstevel@tonic-gate  *
4690Sstevel@tonic-gate  *	"Number One    X|"
4700Sstevel@tonic-gate  *	"Number One     |"
4710Sstevel@tonic-gate  *	                ^maximum length
4720Sstevel@tonic-gate  */
4730Sstevel@tonic-gate static int
4740Sstevel@tonic-gate memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz)
4750Sstevel@tonic-gate {
4760Sstevel@tonic-gate 	uint_t		len, extra_len;
4770Sstevel@tonic-gate 	char		*marker;
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	/* No point in comparing anything beyond max_sz */
4800Sstevel@tonic-gate 	if (d1_len > max_sz)
4810Sstevel@tonic-gate 		d1_len = max_sz;
4820Sstevel@tonic-gate 	if (d2_len > max_sz)
4830Sstevel@tonic-gate 		d2_len = max_sz;
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	/* Find shorter of the two data. */
4860Sstevel@tonic-gate 	if (d1_len <= d2_len) {
4870Sstevel@tonic-gate 		len = d1_len;
4880Sstevel@tonic-gate 		extra_len = d2_len;
4890Sstevel@tonic-gate 		marker = d2;
4900Sstevel@tonic-gate 	} else {	/* d1_len > d2_len */
4910Sstevel@tonic-gate 		len = d2_len;
4920Sstevel@tonic-gate 		extra_len = d1_len;
4930Sstevel@tonic-gate 		marker = d1;
4940Sstevel@tonic-gate 	}
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 	/* Have a match in the shortest length of data? */
4970Sstevel@tonic-gate 	if (memcmp(d1, d2, len) != 0)
4980Sstevel@tonic-gate 		/* CONSTCOND */
4990Sstevel@tonic-gate 		return (!0);
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 	/* If the rest of longer data is nulls or blanks, call it a match. */
5020Sstevel@tonic-gate 	while (len < extra_len)
5030Sstevel@tonic-gate 		if (!isspace(marker[len++]))
5040Sstevel@tonic-gate 			/* CONSTCOND */
5050Sstevel@tonic-gate 			return (!0);
5060Sstevel@tonic-gate 	return (0);
5070Sstevel@tonic-gate }
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate /*
51017Sdinak  * Locate a token slot whose token matches the label, manufacturer ID, and
51117Sdinak  * serial number given.  Token label must be specified, manufacturer ID and
51217Sdinak  * serial number are optional.  When the token is located, the PIN state
51317Sdinak  * is also returned to determine if it still has the default PIN.
5140Sstevel@tonic-gate  */
51517Sdinak CK_RV
5160Sstevel@tonic-gate find_token_slot(char *token_name, char *manuf_id, char *serial_no,
5170Sstevel@tonic-gate 		CK_SLOT_ID *slot_id, CK_FLAGS *pin_state)
5180Sstevel@tonic-gate {
5190Sstevel@tonic-gate 	CK_SLOT_ID_PTR	slot_list;
5200Sstevel@tonic-gate 	CK_TOKEN_INFO	token_info;
5210Sstevel@tonic-gate 	CK_ULONG	slot_count = 0;
52217Sdinak 	int		rv = CKR_OK;
5230Sstevel@tonic-gate 	int		i;
5240Sstevel@tonic-gate 	uint_t		len, max_sz;
5250Sstevel@tonic-gate 	boolean_t	tok_match = B_FALSE,
5260Sstevel@tonic-gate 			man_match = B_FALSE,
5270Sstevel@tonic-gate 			ser_match = B_FALSE;
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	cryptodebug("inside find_token_slot");
5300Sstevel@tonic-gate 
53117Sdinak 	if (token_name == NULL)
53217Sdinak 		return (CKR_ARGUMENTS_BAD);
5330Sstevel@tonic-gate 
53417Sdinak 	/* Get a list of all slots with tokens present. */
53517Sdinak 	if ((rv = get_token_slots(&slot_list, &slot_count)) != CKR_OK)
53617Sdinak 		return (rv);
5370Sstevel@tonic-gate 
53817Sdinak 	/* If there are no such slots, the desired token won't be found. */
53917Sdinak 	if (slot_count == 0)
54017Sdinak 		return (CKR_TOKEN_NOT_PRESENT);
5410Sstevel@tonic-gate 
54217Sdinak 	/* Search the slot list for the token. */
5430Sstevel@tonic-gate 	for (i = 0; i < slot_count; i++) {
54417Sdinak 		cryptodebug("calling C_GetTokenInfo()");
54517Sdinak 		if ((rv = C_GetTokenInfo(slot_list[i], &token_info)) !=
54617Sdinak 		    CKR_OK) {
54717Sdinak 			cryptodebug("token in slot %d returns %s", i,
54817Sdinak 			    pkcs11_strerror(rv));
5490Sstevel@tonic-gate 			continue;
5500Sstevel@tonic-gate 		}
5510Sstevel@tonic-gate 
55217Sdinak 		/* See if the token label matches. */
5530Sstevel@tonic-gate 		len = strlen(token_name);
5540Sstevel@tonic-gate 		max_sz = sizeof (token_info.label);
5550Sstevel@tonic-gate 		if (memcmp_pad_max(&(token_info.label), max_sz, token_name, len,
5560Sstevel@tonic-gate 		    max_sz) == 0)
5570Sstevel@tonic-gate 			tok_match = B_TRUE;
5580Sstevel@tonic-gate 
55917Sdinak 		/*
56017Sdinak 		 * If manufacturer id was given, see if it actually matches.
56117Sdinak 		 * If no manufacturer id was given, assume match is true.
56217Sdinak 		 */
56317Sdinak 		if (manuf_id) {
56417Sdinak 			len = strlen(manuf_id);
56517Sdinak 			max_sz = sizeof ((char *)(token_info.manufacturerID));
56617Sdinak 			if (memcmp_pad_max(&(token_info.manufacturerID), max_sz,
56717Sdinak 			    manuf_id, len, max_sz) == 0)
56817Sdinak 				man_match = B_TRUE;
56917Sdinak 		} else
57017Sdinak 			man_match = B_TRUE;
57117Sdinak 
57217Sdinak 		/*
57317Sdinak 		 * If serial number was given, see if it actually matches.
57417Sdinak 		 * If no serial number was given, assume match is true.
57517Sdinak 		 */
57617Sdinak 		if (serial_no) {
57717Sdinak 			len = strlen(serial_no);
57817Sdinak 			max_sz = sizeof ((char *)(token_info.serialNumber));
57917Sdinak 			if (memcmp_pad_max(&(token_info.serialNumber), max_sz,
58017Sdinak 			    serial_no, len, max_sz) == 0)
58117Sdinak 				ser_match = B_TRUE;
58217Sdinak 		} else
58317Sdinak 			ser_match = B_TRUE;
58417Sdinak 
5850Sstevel@tonic-gate 		cryptodebug("slot %d:", i);
58617Sdinak 		cryptodebug("\tlabel = \"%.32s\"%s", token_info.label,
58717Sdinak 		    tok_match ? " match" : "");
58817Sdinak 		cryptodebug("\tmanuf = \"%.32s\"%s", token_info.manufacturerID,
58917Sdinak 		    man_match ? " match" : "");
59017Sdinak 		cryptodebug("\tserno = \"%.16s\"%s", token_info.serialNumber,
59117Sdinak 		    ser_match ? " match" : "");
5920Sstevel@tonic-gate 		cryptodebug("\tmodel = \"%.16s\"", token_info.model);
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 		cryptodebug("\tCKF_USER_PIN_INITIALIZED = %s",
5950Sstevel@tonic-gate 		    (token_info.flags & CKF_USER_PIN_INITIALIZED) ?
5960Sstevel@tonic-gate 		    "true" : "false");
5970Sstevel@tonic-gate 		cryptodebug("\tCKF_USER_PIN_TO_BE_CHANGED = %s",
5980Sstevel@tonic-gate 		    (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ?
5990Sstevel@tonic-gate 		    "true" : "false");
6000Sstevel@tonic-gate 
60117Sdinak 		if (tok_match && man_match && ser_match)
60217Sdinak 			break;		/* found it! */
6030Sstevel@tonic-gate 	}
6040Sstevel@tonic-gate 
60517Sdinak 	/* Scanned the whole list without finding the token. */
6060Sstevel@tonic-gate 	if (i == slot_count) {
60717Sdinak 		cryptodebug("token not found");
6080Sstevel@tonic-gate 		free(slot_list);
60917Sdinak 		return (CKR_TOKEN_NOT_PRESENT);
6100Sstevel@tonic-gate 	}
6110Sstevel@tonic-gate 
61217Sdinak 	/* Return slot id where token was found and its PIN state. */
61317Sdinak 	cryptodebug("token found at slot %d", i);
6140Sstevel@tonic-gate 	*slot_id = slot_list[i];
6150Sstevel@tonic-gate 	*pin_state = (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED);
6160Sstevel@tonic-gate 	free(slot_list);
61717Sdinak 	return (CKR_OK);
61817Sdinak }
61917Sdinak 
62017Sdinak /*
621*864Sdinak  * Returns pointer to either null-terminator or next unescaped colon.  The
622*864Sdinak  * string to be extracted starts at the beginning and goes until one character
623*864Sdinak  * before this pointer.  If NULL is returned, the string itself is NULL.
624*864Sdinak  */
625*864Sdinak static char *
626*864Sdinak find_unescaped_colon(char *str)
627*864Sdinak {
628*864Sdinak 	char *end;
629*864Sdinak 
630*864Sdinak 	if (str == NULL)
631*864Sdinak 		return (NULL);
632*864Sdinak 
633*864Sdinak 	while ((end = strchr(str, ':')) != NULL) {
634*864Sdinak 		if (end != str && *(end-1) != '\\')
635*864Sdinak 			return (end);
636*864Sdinak 		str = end + 1;		/* could point to null-terminator */
637*864Sdinak 	}
638*864Sdinak 	if (end == NULL)
639*864Sdinak 		end = strchr(str, '\0');
640*864Sdinak 	return (end);
641*864Sdinak }
642*864Sdinak 
643*864Sdinak /*
644*864Sdinak  * Compresses away any characters escaped with backslash from given string.
645*864Sdinak  * The string is altered in-place.  Example, "ab\:\\e" becomes "ab:\e".
646*864Sdinak  */
647*864Sdinak static void
648*864Sdinak unescape_str(char *str)
649*864Sdinak {
650*864Sdinak 	boolean_t	escaped = B_FALSE;
651*864Sdinak 	char		*mark;
652*864Sdinak 
653*864Sdinak 	if (str == NULL)
654*864Sdinak 		return;
655*864Sdinak 
656*864Sdinak 	for (mark = str; *str != '\0'; str++) {
657*864Sdinak 		if (*str != '\\' || escaped == B_TRUE) {
658*864Sdinak 			*mark++ = *str;
659*864Sdinak 			escaped = B_FALSE;
660*864Sdinak 		} else {
661*864Sdinak 			escaped = B_TRUE;
662*864Sdinak 		}
663*864Sdinak 	}
664*864Sdinak 	*mark = '\0';
665*864Sdinak }
666*864Sdinak 
667*864Sdinak /*
668*864Sdinak  * Given a colon-separated token specifier, this functions splits it into
669*864Sdinak  * its label, manufacturer ID (if any), and serial number (if any).  Literal
670*864Sdinak  * colons within the label/manuf/serial can be escaped with a backslash.
671*864Sdinak  * Fields can left blank and trailing colons can be omitted, however leading
672*864Sdinak  * colons are required as placeholders.  For example, these are equivalent:
673*864Sdinak  *	(a) "lbl", "lbl:", "lbl::"	(b) "lbl:man", "lbl:man:"
674*864Sdinak  * but these are not:
675*864Sdinak  *	(c) "man", ":man"	(d) "ser", "::ser"
676*864Sdinak  * Furthermore, the token label is required always.
677*864Sdinak  *
678*864Sdinak  * The buffer containing the token specifier is altered by replacing the
679*864Sdinak  * colons to null-terminators, and pointers returned are pointers into this
680*864Sdinak  * string.  No new memory is allocated.
681*864Sdinak  */
682*864Sdinak int
683*864Sdinak parse_token_spec(char *token_spec, char **token_name, char **manuf_id,
684*864Sdinak 	char **serial_no)
685*864Sdinak {
686*864Sdinak 	char	*mark;
687*864Sdinak 
688*864Sdinak 	if (token_spec == NULL || *token_spec == '\0') {
689*864Sdinak 		cryptodebug("token specifier is empty");
690*864Sdinak 		return (-1);
691*864Sdinak 	}
692*864Sdinak 
693*864Sdinak 	*token_name = NULL;
694*864Sdinak 	*manuf_id = NULL;
695*864Sdinak 	*serial_no = NULL;
696*864Sdinak 
697*864Sdinak 	/* Token label (required) */
698*864Sdinak 	mark = find_unescaped_colon(token_spec);
699*864Sdinak 	*token_name = token_spec;
700*864Sdinak 	if (*mark != '\0')
701*864Sdinak 		*mark++ = '\0';		/* mark points to next field, if any */
702*864Sdinak 	unescape_str(*token_name);
703*864Sdinak 
704*864Sdinak 	if (*(*token_name) == '\0') {	/* token label is required */
705*864Sdinak 		cryptodebug("no token label found");
706*864Sdinak 		return (-1);
707*864Sdinak 	}
708*864Sdinak 
709*864Sdinak 	if (*mark == '\0' || *(mark+1) == '\0')		/* no more fields */
710*864Sdinak 		return (0);
711*864Sdinak 	token_spec = mark;
712*864Sdinak 
713*864Sdinak 	/* Manufacturer identifier (optional) */
714*864Sdinak 	mark = find_unescaped_colon(token_spec);
715*864Sdinak 	*manuf_id = token_spec;
716*864Sdinak 	if (*mark != '\0')
717*864Sdinak 		*mark++ = '\0';		/* mark points to next field, if any */
718*864Sdinak 	unescape_str(*manuf_id);
719*864Sdinak 
720*864Sdinak 	if (*mark == '\0' || *(mark+1) == '\0')		/* no more fields */
721*864Sdinak 		return (0);
722*864Sdinak 	token_spec = mark;
723*864Sdinak 
724*864Sdinak 	/* Serial number (optional) */
725*864Sdinak 	mark = find_unescaped_colon(token_spec);
726*864Sdinak 	*serial_no = token_spec;
727*864Sdinak 	if (*mark != '\0')
728*864Sdinak 		*mark++ = '\0';		/* null-terminate, just in case */
729*864Sdinak 	unescape_str(*serial_no);
730*864Sdinak 
731*864Sdinak 	return (0);
732*864Sdinak }
733*864Sdinak 
734*864Sdinak /*
73517Sdinak  * Constructs a fully qualified token name from its label, manufacturer ID
73617Sdinak  * (if any), and its serial number (if any).  Note that the given buf must
73717Sdinak  * be big enough.  Do NOT i18n/l10n.
73817Sdinak  *
73917Sdinak  * FULL_NAME_LEN is defined in common.h to be 91 because a fully qualified
74017Sdinak  * token name adds up this way:
74117Sdinak  * =32(label) + 32(manuf) + 16(serial) + 4("", ) + 4("", ) + 3("" and nul)
74217Sdinak  */
74317Sdinak void
74417Sdinak full_token_name(char *token_name, char *manuf_id, char *serial_no, char *buf)
74517Sdinak {
74617Sdinak 	char		*marker = buf;
74717Sdinak 	int		n_written = 0;
74817Sdinak 	int		space_left = FULL_NAME_LEN;
74917Sdinak 
75017Sdinak 	if (!token_name)
75117Sdinak 		return;
75217Sdinak 
75317Sdinak 	n_written = sprintf(buf, "\"%.32s\"", token_name);
75417Sdinak 	marker += n_written;
75517Sdinak 	space_left -= n_written;
75617Sdinak 
75717Sdinak 	n_written = sprintf(marker, ", \"%.32s\"", manuf_id ? manuf_id : "");
75817Sdinak 	marker += n_written;
75917Sdinak 	space_left -= n_written;
76017Sdinak 
76117Sdinak 	n_written = sprintf(marker, ", \"%.16s\"", serial_no ? serial_no : "");
76217Sdinak 	marker += n_written;
76317Sdinak 	space_left -= n_written;
76417Sdinak 
76517Sdinak 	/* space_left should always be >= 1 */
76617Sdinak }
76717Sdinak 
76817Sdinak /*
76917Sdinak  * Find how many token objects with the given label.
77017Sdinak  */
77117Sdinak CK_RV
77217Sdinak find_obj_count(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label,
77317Sdinak     CK_ULONG *count)
77417Sdinak {
77517Sdinak 	CK_RV			rv = CKR_OK;
77617Sdinak 	CK_ATTRIBUTE		attrs[4] = {
77717Sdinak 		{ CKA_TOKEN, &pk_true, sizeof (pk_true) },
77817Sdinak 		{ 0, NULL, 0 },
77917Sdinak 		{ 0, NULL, 0 },
78017Sdinak 		{ 0, NULL, 0 }
78117Sdinak 	    };
78217Sdinak 	CK_ULONG	num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE);
78317Sdinak 	CK_ULONG	cur_attr = 1;		/* CKA_TOKEN already set */
78417Sdinak 	CK_OBJECT_CLASS		obj_class;
78517Sdinak 	CK_OBJECT_HANDLE	tmp_obj;
78617Sdinak 	CK_ULONG		obj_count = 0;
78717Sdinak 
78817Sdinak 	cryptodebug("inside find_obj_count");
78917Sdinak 
79017Sdinak 	if (!session_opened || sess == NULL) {
79117Sdinak 		cryptodebug("session handle is null");
79217Sdinak 		return (CKR_SESSION_HANDLE_INVALID);
79317Sdinak 	}
79417Sdinak 
79517Sdinak 	if (label) {
79617Sdinak 		cryptodebug("object label was specified");
79717Sdinak 		attrs[cur_attr].type = CKA_LABEL;
79817Sdinak 		attrs[cur_attr].pValue = label;
79917Sdinak 		attrs[cur_attr].ulValueLen = strlen((char *)label);
80017Sdinak 		cur_attr++;
80117Sdinak 	}
80217Sdinak 
80317Sdinak 	if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) {
80417Sdinak 		cryptodebug("only searching for private objects");
80517Sdinak 		attrs[cur_attr].type = CKA_PRIVATE;
80617Sdinak 		attrs[cur_attr].pValue = &pk_true;
80717Sdinak 		attrs[cur_attr].ulValueLen = sizeof (pk_true);
80817Sdinak 		cur_attr++;
80917Sdinak 	}
81017Sdinak 
81117Sdinak 	/*
81217Sdinak 	 * If "certs and all keys" is not specified, but at least either
81317Sdinak 	 * "certs" or some "keys" is specified, then go into this block.
81417Sdinak 	 * If all certs and keys were specified, there's no point in
81517Sdinak 	 * putting that fact in the attribute template -- leave that open,
81617Sdinak 	 * and all certs and keys will be matched automatically.
81717Sdinak 	 * In other words, only if at least one of 0x10,0x20,0x40,0x80
81817Sdinak 	 * bits is off, go into this code block.
81917Sdinak 	 *
82017Sdinak 	 * NOTE:  For now, only one of cert or key types is allowed.
82117Sdinak 	 * This needs to change in the future.
82217Sdinak 	 */
82317Sdinak 	if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) &&
82417Sdinak 	    ((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) {
82517Sdinak 		if (obj_type & PK_CERT_OBJ) {
82617Sdinak 			cryptodebug("only searching for certificates");
82717Sdinak 			obj_class = CKO_CERTIFICATE;
82817Sdinak 		} else if (obj_type & PK_PRIKEY_OBJ) {
82917Sdinak 			cryptodebug("only searching for private keys");
83017Sdinak 			obj_class = CKO_PRIVATE_KEY;
83117Sdinak 		} else if (obj_type & PK_PUBKEY_OBJ) {
83217Sdinak 			cryptodebug("only searching for public keys");
83317Sdinak 			obj_class = CKO_PUBLIC_KEY;
83417Sdinak 		} else if (obj_type & PK_SECKEY_OBJ) {
83517Sdinak 			cryptodebug("only searching for secret keys");
83617Sdinak 			obj_class = CKO_SECRET_KEY;
83717Sdinak 		}
83817Sdinak 
83917Sdinak 		attrs[cur_attr].type = CKA_CLASS;
84017Sdinak 		attrs[cur_attr].pValue = &obj_class;
84117Sdinak 		attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS);
84217Sdinak 		cur_attr++;
84317Sdinak 	}
84417Sdinak 
84517Sdinak 	/*
84617Sdinak 	 * This can't happen now.  When finding objects is enhanced in the
84717Sdinak 	 * future. this could lead to buffer overruns.
84817Sdinak 	 */
84917Sdinak 	if (cur_attr > num_attrs)
85017Sdinak 		cryptodebug("internal error:  attr template overrun");
85117Sdinak 
85217Sdinak 	cryptodebug("calling C_FindObjectsInit");
85317Sdinak 	if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK)
85417Sdinak 		return (rv);
85517Sdinak 
85617Sdinak 	/* Look for the object, checking if there are more than one. */
85717Sdinak 	cryptodebug("calling C_FindObjects");
85817Sdinak 	for (*count = 0; /* empty */; (*count)++) {
85917Sdinak 		if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) !=
86017Sdinak 		    CKR_OK)
86117Sdinak 			break;
86217Sdinak 
86317Sdinak 		/* No more found. */
86417Sdinak 		if (obj_count == 0)
86517Sdinak 			break;
86617Sdinak 	}
86717Sdinak 
86817Sdinak 	cryptodebug("%d matching objects found", *count);
86917Sdinak 
87017Sdinak 	cryptodebug("calling C_FindObjectsFinal");
87117Sdinak 	(void) C_FindObjectsFinal(sess);
87217Sdinak 	return (rv);
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate /*
87617Sdinak  * Find the token object with the given label.
8770Sstevel@tonic-gate  */
87817Sdinak CK_RV
87917Sdinak find_objs(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label,
88017Sdinak     CK_OBJECT_HANDLE_PTR *obj, CK_ULONG *count)
8810Sstevel@tonic-gate {
88217Sdinak 	CK_RV			rv = CKR_OK;
88317Sdinak 	CK_ATTRIBUTE		attrs[4] = {
88417Sdinak 		{ CKA_TOKEN, &pk_true, sizeof (pk_true) },
88517Sdinak 		{ 0, NULL, 0 },
88617Sdinak 		{ 0, NULL, 0 },
88717Sdinak 		{ 0, NULL, 0 }
88817Sdinak 	    };
88917Sdinak 	CK_ULONG	num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE);
89017Sdinak 	CK_ULONG	cur_attr = 1;		/* CKA_TOKEN already set */
89117Sdinak 	CK_OBJECT_CLASS		obj_class;
89217Sdinak 	CK_OBJECT_HANDLE	tmp_obj;
89317Sdinak 	CK_ULONG		obj_count = 0;
89417Sdinak 	int			i;
89517Sdinak 
89617Sdinak 	cryptodebug("inside find_obj");
89717Sdinak 
89817Sdinak 	if ((rv = find_obj_count(sess, obj_type, label, count)) != CKR_OK)
89917Sdinak 		return (rv);
90017Sdinak 
90117Sdinak 	if (*count == 0)
90217Sdinak 		return (CKR_OK);
90317Sdinak 
90417Sdinak 	if ((*obj = (CK_OBJECT_HANDLE_PTR) malloc((*count) *
90517Sdinak 	    sizeof (CK_OBJECT_HANDLE))) == NULL) {
90617Sdinak 		cryptodebug("no memory for found object");
90717Sdinak 		return (CKR_HOST_MEMORY);
90817Sdinak 	}
90917Sdinak 
91017Sdinak 	if (label) {
91117Sdinak 		cryptodebug("object label was specified");
91217Sdinak 		attrs[cur_attr].type = CKA_LABEL;
91317Sdinak 		attrs[cur_attr].pValue = label;
91417Sdinak 		attrs[cur_attr].ulValueLen = strlen((char *)label);
91517Sdinak 		cur_attr++;
91617Sdinak 	}
9170Sstevel@tonic-gate 
91817Sdinak 	if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) {
91917Sdinak 		cryptodebug("only searching for private objects");
92017Sdinak 		attrs[cur_attr].type = CKA_PRIVATE;
92117Sdinak 		attrs[cur_attr].pValue = &pk_true;
92217Sdinak 		attrs[cur_attr].ulValueLen = sizeof (pk_true);
92317Sdinak 		cur_attr++;
92417Sdinak 	}
9250Sstevel@tonic-gate 
92617Sdinak 	/*
92717Sdinak 	 * If "certs and all keys" is not specified, but at least either
92817Sdinak 	 * "certs" or some "keys" is specified, then go into this block.
92917Sdinak 	 * If all certs and keys were specified, there's no point in
93017Sdinak 	 * putting that fact in the attribute template -- leave that open,
93117Sdinak 	 * and all certs and keys will be matched automatically.
93217Sdinak 	 * In other words, only if at least one of 0x10,0x20,0x40,0x80
93317Sdinak 	 * bits is off, go into this code block.
93417Sdinak 	 *
93517Sdinak 	 * NOTE:  For now, only one of cert or key types is allowed.
93617Sdinak 	 * This needs to change in the future.
93717Sdinak 	 */
93817Sdinak 	if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) &&
93917Sdinak 	    ((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) {
94017Sdinak 		if (obj_type & PK_CERT_OBJ) {
94117Sdinak 			cryptodebug("only searching for certificates");
94217Sdinak 			obj_class = CKO_CERTIFICATE;
94317Sdinak 		} else if (obj_type & PK_PRIKEY_OBJ) {
94417Sdinak 			cryptodebug("only searching for private keys");
94517Sdinak 			obj_class = CKO_PRIVATE_KEY;
94617Sdinak 		} else if (obj_type & PK_PUBKEY_OBJ) {
94717Sdinak 			cryptodebug("only searching for public keys");
94817Sdinak 			obj_class = CKO_PUBLIC_KEY;
94917Sdinak 		} else if (obj_type & PK_SECKEY_OBJ) {
95017Sdinak 			cryptodebug("only searching for secret keys");
95117Sdinak 			obj_class = CKO_SECRET_KEY;
95217Sdinak 		}
95317Sdinak 
95417Sdinak 		attrs[cur_attr].type = CKA_CLASS;
95517Sdinak 		attrs[cur_attr].pValue = &obj_class;
95617Sdinak 		attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS);
95717Sdinak 		cur_attr++;
95817Sdinak 	}
95917Sdinak 
96017Sdinak 	/*
96117Sdinak 	 * This can't happen now.  When finding objects is enhanced in the
96217Sdinak 	 * future. this could lead to buffer overruns.
96317Sdinak 	 */
96417Sdinak 	if (cur_attr > num_attrs)
96517Sdinak 		cryptodebug("internal error:  attr template overrun");
96617Sdinak 
96717Sdinak 	cryptodebug("calling C_FindObjectsInit");
96817Sdinak 	if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK) {
96917Sdinak 		free(*obj);
97017Sdinak 		return (rv);
9710Sstevel@tonic-gate 	}
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	/*
97417Sdinak 	 * Find all the matching objects.  The loop goes 1 more beyond
97517Sdinak 	 * the number of objects found to determine if any new objects
97617Sdinak 	 * were created since the time the object count was done.
9770Sstevel@tonic-gate 	 */
97817Sdinak 	cryptodebug("calling C_FindObjects");
97917Sdinak 	for (i = 0; i < (*count) + 1; i++) {
98017Sdinak 		if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) !=
98117Sdinak 		    CKR_OK)
98217Sdinak 			break;
98317Sdinak 
98417Sdinak 		/* No more found. */
98517Sdinak 		if (obj_count == 0)
98617Sdinak 			break;
98717Sdinak 
98817Sdinak 		/*
98917Sdinak 		 * Save the object in the list being created, as long as
99017Sdinak 		 * we don't overrun the size of the list.
99117Sdinak 		 */
99217Sdinak 		if (i < *count)
99317Sdinak 		    (*obj)[i] = tmp_obj;
99417Sdinak 		else
99517Sdinak 		    cryptodebug("number of objects changed since last count");
99617Sdinak 	}
99717Sdinak 
99817Sdinak 	if (rv != CKR_OK) {
99917Sdinak 		free(*obj);
100017Sdinak 	} else {
100117Sdinak 		/*
100217Sdinak 		 * There are three cases to handle:  (1) fewer objects were
100317Sdinak 		 * found than originally counted => change *count to the
100417Sdinak 		 * smaller number; (2) the number of objects found matches
100517Sdinak 		 * the number originally counted => do nothing; (3) more
100617Sdinak 		 * objects found than originally counted => list passed
100717Sdinak 		 * in is too small to contain the extra object(s), flag
100817Sdinak 		 * that in the debug output but don't change number of
100917Sdinak 		 * objects returned.  The caller can double-check by
101017Sdinak 		 * calling find_obj_count() after this function to make
101117Sdinak 		 * sure the numbers match, if desired.
101217Sdinak 		 */
101317Sdinak 		/* Case 1:  Fewer objects. */
101417Sdinak 		if (i < *count) {
101517Sdinak 			cryptodebug("%d objects found, expected %d", i, *count);
101617Sdinak 			*count = i;
101717Sdinak 		/* Case 3:  More objects. */
101817Sdinak 		} else if (i > *count) {
101917Sdinak 			cryptodebug("at least %d objects found, expected %d",
102017Sdinak 			    i, *count);
102117Sdinak 		}
102217Sdinak 		/*
102317Sdinak 		 * Case 2:  Same number of objects.
102417Sdinak 		 *
102517Sdinak 		 * else if (i == *count)
102617Sdinak 		 *	;
102717Sdinak 		 */
10280Sstevel@tonic-gate 	}
10290Sstevel@tonic-gate 
103017Sdinak 	cryptodebug("calling C_FindObjectsFinal");
103117Sdinak 	(void) C_FindObjectsFinal(sess);
103217Sdinak 	return (rv);
103317Sdinak }
103417Sdinak 
103517Sdinak char *
103617Sdinak class_str(CK_OBJECT_CLASS class)
103717Sdinak {
103817Sdinak 	switch (class) {
103917Sdinak 	case CKO_DATA:		return (gettext("data"));
104017Sdinak 	case CKO_CERTIFICATE:	return (gettext("certificate"));
104117Sdinak 	case CKO_PUBLIC_KEY:	return (gettext("public key"));
104217Sdinak 	case CKO_PRIVATE_KEY:	return (gettext("private key"));
104317Sdinak 	case CKO_SECRET_KEY:	return (gettext("secret key"));
104417Sdinak 	case CKO_DOMAIN_PARAMETERS:	return (gettext("domain parameter"));
104517Sdinak 	default:		return (gettext("unknown object"));
104617Sdinak 	}
104717Sdinak }
104817Sdinak 
104917Sdinak char *
105017Sdinak keytype_str(CK_KEY_TYPE keytype)
105117Sdinak {
105217Sdinak 	switch (keytype) {
105317Sdinak 	case CKK_RSA:		return (gettext("RSA"));
105417Sdinak 	case CKK_DSA:		return (gettext("DSA"));
105517Sdinak 	case CKK_DH:		return (gettext("Diffie-Hellman"));
105617Sdinak 	case CKK_X9_42_DH:	return (gettext("X9.42 Diffie-Hellman"));
105717Sdinak 	case CKK_GENERIC_SECRET:	return (gettext("generic"));
105817Sdinak 	case CKK_RC2:		return (gettext("RC2"));
105917Sdinak 	case CKK_RC4:		return (gettext("RC4"));
106017Sdinak 	case CKK_DES:		return (gettext("DES"));
106117Sdinak 	case CKK_DES2:		return (gettext("Double-DES"));
106217Sdinak 	case CKK_DES3:		return (gettext("Triple-DES"));
106317Sdinak 	case CKK_RC5:		return (gettext("RC5"));
106417Sdinak 	case CKK_AES:		return (gettext("AES"));
106517Sdinak 	default:		return (gettext("typeless"));
106617Sdinak 	}
106717Sdinak }
106817Sdinak 
106917Sdinak char *
107017Sdinak attr_str(CK_ATTRIBUTE_TYPE attrtype)
107117Sdinak {
107217Sdinak 	switch (attrtype) {
107317Sdinak 	case CKA_PRIVATE:		return (gettext("private"));
107417Sdinak 	case CKA_LOCAL:			return (gettext("local"));
107517Sdinak 	case CKA_SENSITIVE:		return (gettext("sensitive"));
107617Sdinak 	case CKA_EXTRACTABLE:		return (gettext("extractable"));
107717Sdinak 	case CKA_ENCRYPT:		return (gettext("encrypt"));
107817Sdinak 	case CKA_DECRYPT:		return (gettext("decrypt"));
107917Sdinak 	case CKA_WRAP:			return (gettext("wrap"));
108017Sdinak 	case CKA_UNWRAP:		return (gettext("unwrap"));
108117Sdinak 	case CKA_SIGN:			return (gettext("sign"));
108217Sdinak 	case CKA_SIGN_RECOVER:		return (gettext("sign-recover"));
108317Sdinak 	case CKA_VERIFY:		return (gettext("verify"));
108417Sdinak 	case CKA_VERIFY_RECOVER:	return (gettext("verify-recover"));
108517Sdinak 	case CKA_DERIVE:		return (gettext("derive"));
108617Sdinak 	case CKA_ALWAYS_SENSITIVE:	return (gettext("always sensitive"));
108717Sdinak 	case CKA_NEVER_EXTRACTABLE:	return (gettext("never extractable"));
108817Sdinak 	default:		return (gettext("unknown capability"));
108917Sdinak 	}
10900Sstevel@tonic-gate }
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate /*
109317Sdinak  * Convert a byte string into a string of octets formatted like this:
109417Sdinak  *	oo oo oo oo oo ... oo
109517Sdinak  * where each "oo" is an octet is space separated and in the form:
109617Sdinak  *	[0-f][0-f] if the octet is a non-printable character
109717Sdinak  *	<space><char> if the octet is a printable character
109817Sdinak  *
109917Sdinak  * Note:  octets_sz must be 3 * str_sz + 1, or at least as long as "blank"
11000Sstevel@tonic-gate  */
11010Sstevel@tonic-gate void
110217Sdinak octetify(CK_BYTE *str, CK_ULONG str_sz, char *octets, int octets_sz,
110317Sdinak     boolean_t stop_on_nul, boolean_t do_ascii, int limit, char *indent,
110417Sdinak     char *blank)
11050Sstevel@tonic-gate {
110617Sdinak 	char		*marker;
110717Sdinak 	int		nc;
110817Sdinak 	int		newline;
110917Sdinak 	int		indent_len;
111017Sdinak 	boolean_t	first = B_TRUE;
111117Sdinak 
111217Sdinak 	cryptodebug("inside octetify");
111317Sdinak 
111417Sdinak 	cryptodebug(stop_on_nul ? "stopping on first nul found" :
111517Sdinak 	    "continuing to full length of buffer");
111617Sdinak 	cryptodebug(do_ascii ? "using ascii chars where printable" :
111717Sdinak 	    "using only hex octets");
111817Sdinak 	cryptodebug("every %d characters indent with \"%s\"\n ", limit, indent);
111917Sdinak 	cryptodebug("return \"%s\" if buffer is null or empty", blank);
112017Sdinak 
112117Sdinak 	/* If string is empty, write as much of the blank string and leave. */
112217Sdinak 	if (str_sz == 0) {
112317Sdinak 		(void) snprintf(octets, octets_sz, "%s", blank);
112417Sdinak 		return;
112517Sdinak 	}
112617Sdinak 
112717Sdinak 	/* If only limit or indent is set, pick default for the other. */
112817Sdinak 	if (limit > 0 && indent == NULL)
112917Sdinak 		indent = "\n";
113017Sdinak 	if (indent != NULL && limit == 0)
113117Sdinak 		limit = 60;
113217Sdinak 	indent_len = strlen(indent);
11330Sstevel@tonic-gate 
113417Sdinak 	for (marker = octets, newline = 0, first = B_TRUE;
113517Sdinak 	    (stop_on_nul && *str != '\0') ||
113617Sdinak 	    (!stop_on_nul && str_sz > 0 && octets_sz > 0);
113717Sdinak 	    str++, str_sz--, marker += nc, octets_sz -= nc) {
113817Sdinak 		if (!first) {
113917Sdinak 			if (limit > 0 && ((marker - octets) / limit) >
114017Sdinak 			    newline) {
114117Sdinak 				nc = snprintf(marker, indent_len, "%s", indent);
114217Sdinak 				newline++;
114317Sdinak 				continue;
114417Sdinak 			}
114517Sdinak 			nc = sprintf(marker,
114617Sdinak 			    ((do_ascii && isprint(*str) && !isspace(*str)) ?
114717Sdinak 			    "%s%c" : "%s%02x"), (do_ascii ? " " : ":"), *str);
114817Sdinak 		} else {
114917Sdinak 			nc = sprintf(marker,
115017Sdinak 			    ((do_ascii && isprint(*str) && !isspace(*str)) ?
115117Sdinak 			    "%c" : "%02x"), *str);
115217Sdinak 			first = B_FALSE;
115317Sdinak 		}
11540Sstevel@tonic-gate 	}
115517Sdinak 	*marker = '\0';
115617Sdinak }
115717Sdinak 
115817Sdinak /*
115917Sdinak  * Copies a biginteger_t to a template attribute.
116017Sdinak  * Should be a macro instead of a function.
116117Sdinak  */
116217Sdinak void
116317Sdinak copy_bigint_to_attr(biginteger_t big, CK_ATTRIBUTE_PTR attr)
116417Sdinak {
116517Sdinak 	attr->pValue = big.big_value;
116617Sdinak 	attr->ulValueLen = big.big_value_len;
116717Sdinak }
116817Sdinak 
116917Sdinak /*
117017Sdinak  * Copies a string and its length to a template attribute.
117117Sdinak  * Should be a macro instead of a function.
117217Sdinak  */
117317Sdinak void
117417Sdinak copy_string_to_attr(CK_BYTE *buf, CK_ULONG buflen, CK_ATTRIBUTE_PTR attr)
117517Sdinak {
117617Sdinak 	attr->pValue = buf;
117717Sdinak 	attr->ulValueLen = buflen;
11780Sstevel@tonic-gate }
117917Sdinak 
118017Sdinak /*
118117Sdinak  * Copies a template attribute to a biginteger_t.
118217Sdinak  * Should be a macro instead of a function.
118317Sdinak  */
118417Sdinak void
118517Sdinak copy_attr_to_bigint(CK_ATTRIBUTE_PTR attr, biginteger_t *big)
118617Sdinak {
118717Sdinak 	big->big_value = attr->pValue;
118817Sdinak 	big->big_value_len = attr->ulValueLen;
118917Sdinak }
119017Sdinak 
119117Sdinak /*
119217Sdinak  * Copies a template attribute to a string and its length.
119317Sdinak  * Should be a macro instead of a function.
119417Sdinak  */
119517Sdinak void
119617Sdinak copy_attr_to_string(CK_ATTRIBUTE_PTR attr, CK_BYTE **buf, CK_ULONG *buflen)
119717Sdinak {
119817Sdinak 	*buf = attr->pValue;
119917Sdinak 	*buflen = attr->ulValueLen;
120017Sdinak }
120117Sdinak 
120217Sdinak /*
120317Sdinak  * Copies a template attribute to a date and its length.
120417Sdinak  * Should be a macro instead of a function.
120517Sdinak  */
120617Sdinak void
120717Sdinak copy_attr_to_date(CK_ATTRIBUTE_PTR attr, CK_DATE **buf, CK_ULONG *buflen)
120817Sdinak {
120917Sdinak 	*buf = (CK_DATE *)attr->pValue;
121017Sdinak 	*buflen = attr->ulValueLen;
121117Sdinak }
1212*864Sdinak 
1213*864Sdinak /*
1214*864Sdinak  * Breaks out the getopt-style option string into a structure that can be
1215*864Sdinak  * traversed later for calls to getopt_av().  Option string is NOT altered,
1216*864Sdinak  * but the struct fields point to locations within option string.
1217*864Sdinak  */
1218*864Sdinak static int
1219*864Sdinak populate_opts(char *optstring)
1220*864Sdinak {
1221*864Sdinak 	int		i;
1222*864Sdinak 	av_opts		*temp;
1223*864Sdinak 	char		*marker;
1224*864Sdinak 
1225*864Sdinak 	if (optstring == NULL || *optstring == '\0')
1226*864Sdinak 		return (0);
1227*864Sdinak 
1228*864Sdinak 	/*
1229*864Sdinak 	 * This tries to imitate getopt(3c) Each option must conform to:
1230*864Sdinak 	 * <short name char> [ ':' ] [ '(' <long name string> ')' ]
1231*864Sdinak 	 * If long name is missing, the short name is used for long name.
1232*864Sdinak 	 */
1233*864Sdinak 	for (i = 0; *optstring != '\0'; i++) {
1234*864Sdinak 		if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) :
1235*864Sdinak 		    realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) {
1236*864Sdinak 			free(opts_av);
1237*864Sdinak 			opts_av = NULL;
1238*864Sdinak 			return (0);
1239*864Sdinak 		} else
1240*864Sdinak 			opts_av = (av_opts *)temp;
1241*864Sdinak 
1242*864Sdinak 		marker = optstring;		/* may need optstring later */
1243*864Sdinak 
1244*864Sdinak 		opts_av[i].shortnm = *marker++;	/* set short name */
1245*864Sdinak 
1246*864Sdinak 		if (*marker == ':') {		/* check for opt arg */
1247*864Sdinak 			marker++;
1248*864Sdinak 			opts_av[i].has_arg = B_TRUE;
1249*864Sdinak 		}
1250*864Sdinak 
1251*864Sdinak 		if (*marker == '(') {		/* check and set long name */
1252*864Sdinak 			marker++;
1253*864Sdinak 			opts_av[i].longnm = marker;
1254*864Sdinak 			opts_av[i].longnm_len = strcspn(marker, ")");
1255*864Sdinak 			optstring = marker + opts_av[i].longnm_len + 1;
1256*864Sdinak 		} else {
1257*864Sdinak 			/* use short name option character */
1258*864Sdinak 			opts_av[i].longnm = optstring;
1259*864Sdinak 			opts_av[i].longnm_len = 1;
1260*864Sdinak 			optstring = marker;
1261*864Sdinak 		}
1262*864Sdinak 	}
1263*864Sdinak 
1264*864Sdinak 	return (i);
1265*864Sdinak }
1266*864Sdinak 
1267*864Sdinak /*
1268*864Sdinak  * getopt_av() is very similar to getopt(3c) in that the takes an option
1269*864Sdinak  * string, compares command line arguments for matches, and returns a single
1270*864Sdinak  * letter option when a match is found.  However, getopt_av() differs from
1271*864Sdinak  * getopt(3c) by requiring that only longname options and values be found
1272*864Sdinak  * on the command line and all leading dashes are omitted.  In other words,
1273*864Sdinak  * it tries to enforce only longname "option=value" arguments on the command
1274*864Sdinak  * line.  Boolean options are not allowed either.
1275*864Sdinak  */
1276*864Sdinak int
1277*864Sdinak getopt_av(int argc, char * const *argv, const char *optstring)
1278*864Sdinak {
1279*864Sdinak 	int	i;
1280*864Sdinak 	int	len;
1281*864Sdinak 
1282*864Sdinak 	if (optind_av >= argc)
1283*864Sdinak 		return (EOF);
1284*864Sdinak 
1285*864Sdinak 	/* First time or when optstring changes from previous one */
1286*864Sdinak 	if (_save_optstr != optstring) {
1287*864Sdinak 		if (opts_av != NULL)
1288*864Sdinak 		    free(opts_av);
1289*864Sdinak 		opts_av = NULL;
1290*864Sdinak 		_save_optstr = optstring;
1291*864Sdinak 		_save_numopts = populate_opts((char *)optstring);
1292*864Sdinak 	}
1293*864Sdinak 
1294*864Sdinak 	for (i = 0; i < _save_numopts; i++) {
1295*864Sdinak 		if (strcmp(argv[optind_av], "--") == 0) {
1296*864Sdinak 			optind_av++;
1297*864Sdinak 			break;
1298*864Sdinak 		}
1299*864Sdinak 
1300*864Sdinak 		len = strcspn(argv[optind_av], "=");
1301*864Sdinak 
1302*864Sdinak 		if (len == opts_av[i].longnm_len && strncmp(argv[optind_av],
1303*864Sdinak 		    opts_av[i].longnm, opts_av[i].longnm_len) == 0) {
1304*864Sdinak 			/* matched */
1305*864Sdinak 			if (!opts_av[i].has_arg) {
1306*864Sdinak 				optind_av++;
1307*864Sdinak 				return (opts_av[i].shortnm);
1308*864Sdinak 			}
1309*864Sdinak 
1310*864Sdinak 			/* needs optarg */
1311*864Sdinak 			if (argv[optind_av][len] == '=') {
1312*864Sdinak 				optarg_av = &(argv[optind_av][len+1]);
1313*864Sdinak 				optind_av++;
1314*864Sdinak 				return (opts_av[i].shortnm);
1315*864Sdinak 			}
1316*864Sdinak 
1317*864Sdinak 			optarg_av = NULL;
1318*864Sdinak 			optind_av++;
1319*864Sdinak 			return ((int)'?');
1320*864Sdinak 		}
1321*864Sdinak 	}
1322*864Sdinak 
1323*864Sdinak 	return (EOF);
1324*864Sdinak }
1325