xref: /titanic_41/usr/src/cmd/cmd-crypto/pktool/common.c (revision 1d4b38e0077763e7c9b20768eacb841957e787bc)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains the functions that are shared among
31  * the various services this tool will ultimately provide.
32  * The functions in this file return PKCS#11 CK_RV errors.
33  * Only one session and one login per token is supported
34  * at this time.
35  */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <cryptoutil.h>
42 #include <security/cryptoki.h>
43 #include "common.h"
44 #include "biginteger.h"
45 
46 /* True and false for attribute templates. */
47 CK_BBOOL	pk_true = B_TRUE;
48 CK_BBOOL	pk_false = B_FALSE;
49 
50 /* Local status variables. */
51 static boolean_t	initialized = B_FALSE;
52 static boolean_t	session_opened = B_FALSE;
53 static boolean_t	session_writable = B_FALSE;
54 static boolean_t	logged_in = B_FALSE;
55 
56 /* Supporting structures and global variables for getopt_av(). */
57 typedef struct	av_opts_s {
58 	int		shortnm;	/* short name character */
59 	char		*longnm;	/* long name string, NOT terminated */
60 	int		longnm_len;	/* length of long name string */
61 	boolean_t	has_arg;	/* takes optional argument */
62 } av_opts;
63 static av_opts		*opts_av = NULL;
64 static const char	*_save_optstr = NULL;
65 static int		_save_numopts = 0;
66 
67 int			optind_av = 1;
68 char			*optarg_av = NULL;
69 
70 /*
71  * Perform PKCS#11 setup here.  Currently only C_Initialize is required,
72  * along with setting/resetting state variables.
73  */
74 CK_RV
75 init_pk11(void)
76 {
77 	CK_RV		rv = CKR_OK;
78 
79 	cryptodebug("inside init_pk11");
80 
81 	/* If C_Initialize() already called, nothing to do here. */
82 	if (initialized == B_TRUE)
83 		return (CKR_OK);
84 
85 	/* Reset state variables because C_Initialize() not yet done. */
86 	session_opened = B_FALSE;
87 	session_writable = B_FALSE;
88 	logged_in = B_FALSE;
89 
90 	/* Initialize PKCS#11 library. */
91 	cryptodebug("calling C_Initialize()");
92 	if ((rv = C_Initialize(NULL_PTR)) != CKR_OK &&
93 	    rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
94 		return (rv);
95 	}
96 
97 	initialized = B_TRUE;
98 	return (CKR_OK);
99 }
100 
101 /*
102  * Finalize PKCS#11 library and reset state variables.  Open sessions,
103  * if any, are closed, and thereby any logins are logged out also.
104  */
105 void
106 final_pk11(CK_SESSION_HANDLE sess)
107 {
108 	cryptodebug("inside final_pk11");
109 
110 	/* If the library wasn't initialized, nothing to do here. */
111 	if (!initialized)
112 		return;
113 
114 	/* Make sure the sesion is closed first. */
115 	close_sess(sess);
116 
117 	cryptodebug("calling C_Finalize()");
118 	(void) C_Finalize(NULL);
119 	initialized = B_FALSE;
120 }
121 
122 /*
123  * Create a PKCS#11 session on the given slot, and set state information.
124  * If session is already open, check that the read-only/read-write state
125  * requested matches that of the session.  If it doesn't, make it so.
126  */
127 CK_RV
128 open_sess(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_SESSION_HANDLE_PTR sess)
129 {
130 	CK_RV		rv = CKR_OK;
131 
132 	cryptodebug("inside open_sess");
133 
134 	/* If the session is already open, check the session flags. */
135 	if (session_opened) {
136 		/*
137 		 * If requesting R/W session and it is currently R/O,
138 		 * need to close the session and reopen it R/W.  The
139 		 * other cases are considered acceptable:
140 		 *	sess_flags		current state
141 		 *	----------		-------------
142 		 *	~CKF_RW_SESSION		!session_writable
143 		 *	~CKF_RW_SESSION		session_writable
144 		 *	CKF_RW_SESSION		session_writable
145 		 */
146 		if ((sess_flags & CKF_RW_SESSION) && !session_writable)
147 			close_sess(*sess);
148 		else
149 			return (CKR_OK);
150 	}
151 
152 	/* Make sure the PKCS#11 is already initialized. */
153 	if (!initialized)
154 		if ((rv = init_pk11()) != CKR_OK)
155 			return (rv);
156 
157 	/* Create a session for subsequent operations. */
158 	cryptodebug("calling C_OpenSession()");
159 	if ((rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION|sess_flags,
160 	    NULL, NULL, sess)) != CKR_OK)
161 		return (rv);
162 	session_opened = B_TRUE;
163 	session_writable = (sess_flags & CKF_RW_SESSION) ? B_TRUE : B_FALSE;
164 	return (CKR_OK);
165 }
166 
167 /*
168  * Close PKCS#11 session and reset state variables.  Any logins are
169  * logged out.
170  */
171 void
172 close_sess(CK_SESSION_HANDLE sess)
173 {
174 	cryptodebug("inside close_sess");
175 
176 	if (sess == NULL) {
177 		cryptodebug("session handle is null");
178 		return;
179 	}
180 
181 	/* If session is already closed, nothing to do here. */
182 	session_writable = B_FALSE;
183 	if (!session_opened)
184 		return;
185 
186 	/* Make sure user is logged out of token. */
187 	logout_token(sess);
188 
189 	cryptodebug("calling C_CloseSession()");
190 	(void) C_CloseSession(sess);
191 	session_opened = B_FALSE;
192 }
193 
194 /*
195  * Log user into token in given slot.  If this first login ever for this
196  * token, the initial PIN is "changeme", C_Login() will succeed, but all
197  * PKCS#11 calls following the C_Login() will fail with CKR_PIN_EXPIRED.
198  */
199 CK_RV
200 login_token(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pinlen,
201 	    CK_SESSION_HANDLE_PTR sess)
202 {
203 	CK_RV		rv = CKR_OK;
204 
205 	cryptodebug("inside login_token");
206 
207 	/* If already logged in, nothing to do here. */
208 	if (logged_in)
209 		return (CKR_OK);
210 
211 	/* Make sure we have a session first, assume R/O is enough. */
212 	if (!session_opened)
213 		if ((rv = open_sess(slot_id, CKF_SERIAL_SESSION, sess)) !=
214 		    CKR_OK)
215 			return (rv);
216 
217 	/* Log the user into the token. */
218 	cryptodebug("calling C_Login()");
219 	if ((rv = C_Login(*sess, CKU_USER, pin, pinlen)) != CKR_OK) {
220 		cryptodebug("C_Login returns %s", pkcs11_strerror(rv));
221 		return (rv);
222 	}
223 
224 	logged_in = B_TRUE;
225 	return (CKR_OK);
226 }
227 
228 /*
229  * Log user out of token and reset status variable.
230  */
231 void
232 logout_token(CK_SESSION_HANDLE sess)
233 {
234 	cryptodebug("inside logout_token");
235 
236 	if (sess == NULL) {
237 		cryptodebug("session handle is null");
238 		return;
239 	}
240 
241 	/* If already logged out, nothing to do here. */
242 	if (!logged_in)
243 		return;
244 
245 	cryptodebug("calling C_Logout()");
246 	(void) C_Logout(sess);
247 	logged_in = B_FALSE;
248 }
249 
250 /*
251  * Shortcut function to get from an uninitialized state to user logged in.
252  * If the library is already initialized, the session is already opened,
253  * or the user is already logged in, those steps are skipped and the next
254  * step is checked.
255  */
256 CK_RV
257 quick_start(CK_SLOT_ID slot_id, CK_FLAGS sess_flags, CK_UTF8CHAR_PTR pin,
258 	    CK_ULONG pinlen, CK_SESSION_HANDLE_PTR sess)
259 {
260 	CK_RV		rv = CKR_OK;
261 
262 	cryptodebug("inside quick_start");
263 
264 	/* Call open_sess() explicitly if R/W session is needed. */
265 	if (sess_flags & CKF_RW_SESSION)
266 		if ((rv = open_sess(slot_id, sess_flags, sess)) != CKR_OK)
267 			return (rv);
268 
269 	if ((rv = login_token(slot_id, pin, pinlen, sess)) != CKR_OK)
270 		return (rv);
271 
272 	return (CKR_OK);
273 }
274 
275 /*
276  * Shortcut function to go from any state to uninitialized PKCS#11 library.
277  */
278 void
279 quick_finish(CK_SESSION_HANDLE sess)
280 {
281 	cryptodebug("inside quick_finish");
282 
283 	/* All the needed calls are done implicitly. */
284 	final_pk11(sess);
285 }
286 
287 /*
288  * Gets PIN from user.  Caller needs to free the returned PIN when done.
289  * If two prompts are given, the PIN is confirmed with second prompt.
290  * Note that getphassphrase() may return data in static memory area.
291  */
292 CK_RV
293 get_pin(char *prompt1, char *prompt2, CK_UTF8CHAR_PTR *pin, CK_ULONG *pinlen)
294 {
295 	char		*save_phrase, *phrase1, *phrase2;
296 
297 	cryptodebug("inside get_pin");
298 
299 	/* Prompt user for a PIN. */
300 	if (prompt1 == NULL) {
301 		cryptodebug("no passphrase prompt given");
302 		return (CKR_ARGUMENTS_BAD);
303 	}
304 	if ((phrase1 = getpassphrase(prompt1)) == NULL) {
305 		cryptodebug("getpassphrase() failed");
306 		return (CKR_FUNCTION_FAILED);
307 	}
308 
309 	/* Duplicate 1st PIN in separate chunk of memory. */
310 	if ((save_phrase = strdup(phrase1)) == NULL)
311 		return (CKR_HOST_MEMORY);
312 
313 	/* If second prompt given, PIN confirmation is requested. */
314 	if (prompt2 != NULL) {
315 		if ((phrase2 = getpassphrase(prompt2)) == NULL) {
316 			cryptodebug("getpassphrase() confirmation failed");
317 			free(save_phrase);
318 			return (CKR_FUNCTION_FAILED);
319 		}
320 		if (strcmp(save_phrase, phrase2) != 0) {
321 			cryptodebug("passphrases do not match");
322 			free(save_phrase);
323 			return (CKR_PIN_INCORRECT);
324 		}
325 	}
326 
327 	*pin = (CK_UTF8CHAR_PTR)save_phrase;
328 	*pinlen = strlen(save_phrase);
329 	return (CKR_OK);
330 }
331 
332 /*
333  * Gets yes/no response from user.  If either no prompt is supplied, a
334  * default prompt is used.  If not message for invalid input is supplied,
335  * a default will not be provided.  If the user provides no response,
336  * the input default B_TRUE == yes, B_FALSE == no is returned.
337  * Otherwise, B_TRUE is returned for yes, and B_FALSE for no.
338  */
339 boolean_t
340 yesno(char *prompt, char *invalid, boolean_t dflt)
341 {
342 	char		*response, buf[1024];
343 	char		*yes = gettext("yes");
344 	char		*no = gettext("no");
345 
346 	cryptodebug("inside yesno");
347 
348 	if (prompt == NULL)
349 		prompt = gettext("Enter (y)es or (n)o? ");
350 
351 	for (;;) {
352 		/* Prompt user. */
353 		(void) printf("%s", prompt);
354 		(void) fflush(stdout);
355 
356 		/* Get the response. */
357 		if ((response = fgets(buf, sizeof (buf), stdin)) == NULL)
358 			break;		/* go to default response */
359 
360 		/* Skip any leading white space. */
361 		while (isspace(*response))
362 			response++;
363 		if (*response == '\0')
364 			break;		/* go to default response */
365 
366 		/* Is it valid input?  Return appropriately. */
367 		if (strncasecmp(response, yes, 1) == 0)
368 			return (B_TRUE);
369 		if (strncasecmp(response, no, 1) == 0)
370 			return (B_FALSE);
371 
372 		/* Indicate invalid input, and try again. */
373 		if (invalid != NULL)
374 		    (void) printf("%s", invalid);
375 	}
376 	return (dflt);
377 }
378 
379 /*
380  * Gets the list of slots which have tokens in them.  Keeps adjusting
381  * the size of the slot list buffer until the call is successful or an
382  * irrecoverable error occurs.
383  */
384 CK_RV
385 get_token_slots(CK_SLOT_ID_PTR *slot_list, CK_ULONG *slot_count)
386 {
387 	CK_ULONG	tmp_count = 0;
388 	CK_SLOT_ID_PTR	tmp_list = NULL_PTR, tmp2_list = NULL_PTR;
389 	int		rv = CKR_OK;
390 
391 	cryptodebug("inside get_token_slots");
392 
393 	if (!initialized)
394 		if ((rv = init_pk11()) != CKR_OK)
395 			return (rv);
396 
397 	/*
398 	 * Get the slot count first because we don't know how many
399 	 * slots there are and how many of those slots even have tokens.
400 	 * Don't specify an arbitrary buffer size for the slot list;
401 	 * it may be too small (see section 11.5 of PKCS#11 spec).
402 	 * Also select only those slots that have tokens in them,
403 	 * because this tool has no need to know about empty slots.
404 	 */
405 	cryptodebug("calling C_GetSlotList() for slot count");
406 	if ((rv = C_GetSlotList(1, NULL_PTR, &tmp_count)) != CKR_OK)
407 		return (rv);
408 
409 	if (tmp_count == 0) {
410 		cryptodebug("no slots with tokens found");
411 		*slot_list = NULL_PTR;
412 		*slot_count = 0;
413 		return (CKR_OK);
414 	}
415 
416 	/* Allocate initial space for the slot list. */
417 	if ((tmp_list = (CK_SLOT_ID_PTR) malloc(tmp_count *
418 	    sizeof (CK_SLOT_ID))) == NULL)
419 		return (CKR_HOST_MEMORY);
420 
421 	/* Then get the slot list itself. */
422 	for (;;) {
423 		cryptodebug("calling C_GetSlotList()");
424 		if ((rv = C_GetSlotList(1, tmp_list, &tmp_count)) == CKR_OK) {
425 			*slot_list = tmp_list;
426 			*slot_count = tmp_count;
427 			break;
428 		}
429 
430 		if (rv != CKR_BUFFER_TOO_SMALL) {
431 			free(tmp_list);
432 			break;
433 		}
434 
435 		/* If the number of slots grew, try again. */
436 		cryptodebug("number of tokens present increased");
437 		if ((tmp2_list = (CK_SLOT_ID_PTR) realloc(tmp_list,
438 		    tmp_count * sizeof (CK_SLOT_ID))) == NULL) {
439 			free(tmp_list);
440 			rv = CKR_HOST_MEMORY;
441 			break;
442 		}
443 		tmp_list = tmp2_list;
444 	}
445 
446 	return (rv);
447 }
448 
449 /*
450  * memcmp_pad_max() is a specialized version of memcmp() which
451  * compares two pieces of data up to a maximum length.  If the
452  * the two data match up the maximum length, they are considered
453  * matching.  Trailing blanks do not cause the match to fail if
454  * one of the data is shorted.
455  *
456  * Examples of matches:
457  *	"one"           |
458  *	"one      "     |
459  *	                ^maximum length
460  *
461  *	"Number One     |  X"	(X is beyond maximum length)
462  *	"Number One   " |
463  *	                ^maximum length
464  *
465  * Examples of mismatches:
466  *	" one"
467  *	"one"
468  *
469  *	"Number One    X|"
470  *	"Number One     |"
471  *	                ^maximum length
472  */
473 static int
474 memcmp_pad_max(void *d1, uint_t d1_len, void *d2, uint_t d2_len, uint_t max_sz)
475 {
476 	uint_t		len, extra_len;
477 	char		*marker;
478 
479 	/* No point in comparing anything beyond max_sz */
480 	if (d1_len > max_sz)
481 		d1_len = max_sz;
482 	if (d2_len > max_sz)
483 		d2_len = max_sz;
484 
485 	/* Find shorter of the two data. */
486 	if (d1_len <= d2_len) {
487 		len = d1_len;
488 		extra_len = d2_len;
489 		marker = d2;
490 	} else {	/* d1_len > d2_len */
491 		len = d2_len;
492 		extra_len = d1_len;
493 		marker = d1;
494 	}
495 
496 	/* Have a match in the shortest length of data? */
497 	if (memcmp(d1, d2, len) != 0)
498 		/* CONSTCOND */
499 		return (!0);
500 
501 	/* If the rest of longer data is nulls or blanks, call it a match. */
502 	while (len < extra_len)
503 		if (!isspace(marker[len++]))
504 			/* CONSTCOND */
505 			return (!0);
506 	return (0);
507 }
508 
509 /*
510  * Locate a token slot whose token matches the label, manufacturer ID, and
511  * serial number given.  Token label must be specified, manufacturer ID and
512  * serial number are optional.  When the token is located, the PIN state
513  * is also returned to determine if it still has the default PIN.
514  */
515 CK_RV
516 find_token_slot(char *token_name, char *manuf_id, char *serial_no,
517 		CK_SLOT_ID *slot_id, CK_FLAGS *pin_state)
518 {
519 	CK_SLOT_ID_PTR	slot_list;
520 	CK_TOKEN_INFO	token_info;
521 	CK_ULONG	slot_count = 0;
522 	int		rv = CKR_OK;
523 	int		i;
524 	uint_t		len, max_sz;
525 	boolean_t	tok_match = B_FALSE,
526 			man_match = B_FALSE,
527 			ser_match = B_FALSE;
528 
529 	cryptodebug("inside find_token_slot");
530 
531 	if (token_name == NULL)
532 		return (CKR_ARGUMENTS_BAD);
533 
534 	/* Get a list of all slots with tokens present. */
535 	if ((rv = get_token_slots(&slot_list, &slot_count)) != CKR_OK)
536 		return (rv);
537 
538 	/* If there are no such slots, the desired token won't be found. */
539 	if (slot_count == 0)
540 		return (CKR_TOKEN_NOT_PRESENT);
541 
542 	/* Search the slot list for the token. */
543 	for (i = 0; i < slot_count; i++) {
544 		cryptodebug("calling C_GetTokenInfo()");
545 		if ((rv = C_GetTokenInfo(slot_list[i], &token_info)) !=
546 		    CKR_OK) {
547 			cryptodebug("token in slot %d returns %s", i,
548 			    pkcs11_strerror(rv));
549 			continue;
550 		}
551 
552 		/* See if the token label matches. */
553 		len = strlen(token_name);
554 		max_sz = sizeof (token_info.label);
555 		if (memcmp_pad_max(&(token_info.label), max_sz, token_name, len,
556 		    max_sz) == 0)
557 			tok_match = B_TRUE;
558 
559 		/*
560 		 * If manufacturer id was given, see if it actually matches.
561 		 * If no manufacturer id was given, assume match is true.
562 		 */
563 		if (manuf_id) {
564 			len = strlen(manuf_id);
565 			max_sz = sizeof ((char *)(token_info.manufacturerID));
566 			if (memcmp_pad_max(&(token_info.manufacturerID), max_sz,
567 			    manuf_id, len, max_sz) == 0)
568 				man_match = B_TRUE;
569 		} else
570 			man_match = B_TRUE;
571 
572 		/*
573 		 * If serial number was given, see if it actually matches.
574 		 * If no serial number was given, assume match is true.
575 		 */
576 		if (serial_no) {
577 			len = strlen(serial_no);
578 			max_sz = sizeof ((char *)(token_info.serialNumber));
579 			if (memcmp_pad_max(&(token_info.serialNumber), max_sz,
580 			    serial_no, len, max_sz) == 0)
581 				ser_match = B_TRUE;
582 		} else
583 			ser_match = B_TRUE;
584 
585 		cryptodebug("slot %d:", i);
586 		cryptodebug("\tlabel = \"%.32s\"%s", token_info.label,
587 		    tok_match ? " match" : "");
588 		cryptodebug("\tmanuf = \"%.32s\"%s", token_info.manufacturerID,
589 		    man_match ? " match" : "");
590 		cryptodebug("\tserno = \"%.16s\"%s", token_info.serialNumber,
591 		    ser_match ? " match" : "");
592 		cryptodebug("\tmodel = \"%.16s\"", token_info.model);
593 
594 		cryptodebug("\tCKF_USER_PIN_INITIALIZED = %s",
595 		    (token_info.flags & CKF_USER_PIN_INITIALIZED) ?
596 		    "true" : "false");
597 		cryptodebug("\tCKF_USER_PIN_TO_BE_CHANGED = %s",
598 		    (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED) ?
599 		    "true" : "false");
600 
601 		if (tok_match && man_match && ser_match)
602 			break;		/* found it! */
603 	}
604 
605 	/* Scanned the whole list without finding the token. */
606 	if (i == slot_count) {
607 		cryptodebug("token not found");
608 		free(slot_list);
609 		return (CKR_TOKEN_NOT_PRESENT);
610 	}
611 
612 	/* Return slot id where token was found and its PIN state. */
613 	cryptodebug("token found at slot %d", i);
614 	*slot_id = slot_list[i];
615 	*pin_state = (token_info.flags & CKF_USER_PIN_TO_BE_CHANGED);
616 	free(slot_list);
617 	return (CKR_OK);
618 }
619 
620 /*
621  * Returns pointer to either null-terminator or next unescaped colon.  The
622  * string to be extracted starts at the beginning and goes until one character
623  * before this pointer.  If NULL is returned, the string itself is NULL.
624  */
625 static char *
626 find_unescaped_colon(char *str)
627 {
628 	char *end;
629 
630 	if (str == NULL)
631 		return (NULL);
632 
633 	while ((end = strchr(str, ':')) != NULL) {
634 		if (end != str && *(end-1) != '\\')
635 			return (end);
636 		str = end + 1;		/* could point to null-terminator */
637 	}
638 	if (end == NULL)
639 		end = strchr(str, '\0');
640 	return (end);
641 }
642 
643 /*
644  * Compresses away any characters escaped with backslash from given string.
645  * The string is altered in-place.  Example, "ab\:\\e" becomes "ab:\e".
646  */
647 static void
648 unescape_str(char *str)
649 {
650 	boolean_t	escaped = B_FALSE;
651 	char		*mark;
652 
653 	if (str == NULL)
654 		return;
655 
656 	for (mark = str; *str != '\0'; str++) {
657 		if (*str != '\\' || escaped == B_TRUE) {
658 			*mark++ = *str;
659 			escaped = B_FALSE;
660 		} else {
661 			escaped = B_TRUE;
662 		}
663 	}
664 	*mark = '\0';
665 }
666 
667 /*
668  * Given a colon-separated token specifier, this functions splits it into
669  * its label, manufacturer ID (if any), and serial number (if any).  Literal
670  * colons within the label/manuf/serial can be escaped with a backslash.
671  * Fields can left blank and trailing colons can be omitted, however leading
672  * colons are required as placeholders.  For example, these are equivalent:
673  *	(a) "lbl", "lbl:", "lbl::"	(b) "lbl:man", "lbl:man:"
674  * but these are not:
675  *	(c) "man", ":man"	(d) "ser", "::ser"
676  * Furthermore, the token label is required always.
677  *
678  * The buffer containing the token specifier is altered by replacing the
679  * colons to null-terminators, and pointers returned are pointers into this
680  * string.  No new memory is allocated.
681  */
682 int
683 parse_token_spec(char *token_spec, char **token_name, char **manuf_id,
684 	char **serial_no)
685 {
686 	char	*mark;
687 
688 	if (token_spec == NULL || *token_spec == '\0') {
689 		cryptodebug("token specifier is empty");
690 		return (-1);
691 	}
692 
693 	*token_name = NULL;
694 	*manuf_id = NULL;
695 	*serial_no = NULL;
696 
697 	/* Token label (required) */
698 	mark = find_unescaped_colon(token_spec);
699 	*token_name = token_spec;
700 	if (*mark != '\0')
701 		*mark++ = '\0';		/* mark points to next field, if any */
702 	unescape_str(*token_name);
703 
704 	if (*(*token_name) == '\0') {	/* token label is required */
705 		cryptodebug("no token label found");
706 		return (-1);
707 	}
708 
709 	if (*mark == '\0' || *(mark+1) == '\0')		/* no more fields */
710 		return (0);
711 	token_spec = mark;
712 
713 	/* Manufacturer identifier (optional) */
714 	mark = find_unescaped_colon(token_spec);
715 	*manuf_id = token_spec;
716 	if (*mark != '\0')
717 		*mark++ = '\0';		/* mark points to next field, if any */
718 	unescape_str(*manuf_id);
719 
720 	if (*mark == '\0' || *(mark+1) == '\0')		/* no more fields */
721 		return (0);
722 	token_spec = mark;
723 
724 	/* Serial number (optional) */
725 	mark = find_unescaped_colon(token_spec);
726 	*serial_no = token_spec;
727 	if (*mark != '\0')
728 		*mark++ = '\0';		/* null-terminate, just in case */
729 	unescape_str(*serial_no);
730 
731 	return (0);
732 }
733 
734 /*
735  * Constructs a fully qualified token name from its label, manufacturer ID
736  * (if any), and its serial number (if any).  Note that the given buf must
737  * be big enough.  Do NOT i18n/l10n.
738  *
739  * FULL_NAME_LEN is defined in common.h to be 91 because a fully qualified
740  * token name adds up this way:
741  * =32(label) + 32(manuf) + 16(serial) + 4("", ) + 4("", ) + 3("" and nul)
742  */
743 void
744 full_token_name(char *token_name, char *manuf_id, char *serial_no, char *buf)
745 {
746 	char		*marker = buf;
747 	int		n_written = 0;
748 	int		space_left = FULL_NAME_LEN;
749 
750 	if (!token_name)
751 		return;
752 
753 	n_written = sprintf(buf, "\"%.32s\"", token_name);
754 	marker += n_written;
755 	space_left -= n_written;
756 
757 	n_written = sprintf(marker, ", \"%.32s\"", manuf_id ? manuf_id : "");
758 	marker += n_written;
759 	space_left -= n_written;
760 
761 	n_written = sprintf(marker, ", \"%.16s\"", serial_no ? serial_no : "");
762 	marker += n_written;
763 	space_left -= n_written;
764 
765 	/* space_left should always be >= 1 */
766 }
767 
768 /*
769  * Find how many token objects with the given label.
770  */
771 CK_RV
772 find_obj_count(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label,
773     CK_ULONG *count)
774 {
775 	CK_RV			rv = CKR_OK;
776 	CK_ATTRIBUTE		attrs[4] = {
777 		{ CKA_TOKEN, &pk_true, sizeof (pk_true) },
778 		{ 0, NULL, 0 },
779 		{ 0, NULL, 0 },
780 		{ 0, NULL, 0 }
781 	    };
782 	CK_ULONG	num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE);
783 	CK_ULONG	cur_attr = 1;		/* CKA_TOKEN already set */
784 	CK_OBJECT_CLASS		obj_class;
785 	CK_OBJECT_HANDLE	tmp_obj;
786 	CK_ULONG		obj_count = 0;
787 
788 	cryptodebug("inside find_obj_count");
789 
790 	if (!session_opened || sess == NULL) {
791 		cryptodebug("session handle is null");
792 		return (CKR_SESSION_HANDLE_INVALID);
793 	}
794 
795 	if (label) {
796 		cryptodebug("object label was specified");
797 		attrs[cur_attr].type = CKA_LABEL;
798 		attrs[cur_attr].pValue = label;
799 		attrs[cur_attr].ulValueLen = strlen((char *)label);
800 		cur_attr++;
801 	}
802 
803 	if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) {
804 		cryptodebug("only searching for private objects");
805 		attrs[cur_attr].type = CKA_PRIVATE;
806 		attrs[cur_attr].pValue = &pk_true;
807 		attrs[cur_attr].ulValueLen = sizeof (pk_true);
808 		cur_attr++;
809 	}
810 
811 	/*
812 	 * If "certs and all keys" is not specified, but at least either
813 	 * "certs" or some "keys" is specified, then go into this block.
814 	 * If all certs and keys were specified, there's no point in
815 	 * putting that fact in the attribute template -- leave that open,
816 	 * and all certs and keys will be matched automatically.
817 	 * In other words, only if at least one of 0x10,0x20,0x40,0x80
818 	 * bits is off, go into this code block.
819 	 *
820 	 * NOTE:  For now, only one of cert or key types is allowed.
821 	 * This needs to change in the future.
822 	 */
823 	if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) &&
824 	    ((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) {
825 		if (obj_type & PK_CERT_OBJ) {
826 			cryptodebug("only searching for certificates");
827 			obj_class = CKO_CERTIFICATE;
828 		} else if (obj_type & PK_PRIKEY_OBJ) {
829 			cryptodebug("only searching for private keys");
830 			obj_class = CKO_PRIVATE_KEY;
831 		} else if (obj_type & PK_PUBKEY_OBJ) {
832 			cryptodebug("only searching for public keys");
833 			obj_class = CKO_PUBLIC_KEY;
834 		} else if (obj_type & PK_SECKEY_OBJ) {
835 			cryptodebug("only searching for secret keys");
836 			obj_class = CKO_SECRET_KEY;
837 		}
838 
839 		attrs[cur_attr].type = CKA_CLASS;
840 		attrs[cur_attr].pValue = &obj_class;
841 		attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS);
842 		cur_attr++;
843 	}
844 
845 	/*
846 	 * This can't happen now.  When finding objects is enhanced in the
847 	 * future. this could lead to buffer overruns.
848 	 */
849 	if (cur_attr > num_attrs)
850 		cryptodebug("internal error:  attr template overrun");
851 
852 	cryptodebug("calling C_FindObjectsInit");
853 	if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK)
854 		return (rv);
855 
856 	/* Look for the object, checking if there are more than one. */
857 	cryptodebug("calling C_FindObjects");
858 	for (*count = 0; /* empty */; (*count)++) {
859 		if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) !=
860 		    CKR_OK)
861 			break;
862 
863 		/* No more found. */
864 		if (obj_count == 0)
865 			break;
866 	}
867 
868 	cryptodebug("%d matching objects found", *count);
869 
870 	cryptodebug("calling C_FindObjectsFinal");
871 	(void) C_FindObjectsFinal(sess);
872 	return (rv);
873 }
874 
875 /*
876  * Find the token object with the given label.
877  */
878 CK_RV
879 find_objs(CK_SESSION_HANDLE sess, int obj_type, CK_BYTE *label,
880     CK_OBJECT_HANDLE_PTR *obj, CK_ULONG *count)
881 {
882 	CK_RV			rv = CKR_OK;
883 	CK_ATTRIBUTE		attrs[4] = {
884 		{ CKA_TOKEN, &pk_true, sizeof (pk_true) },
885 		{ 0, NULL, 0 },
886 		{ 0, NULL, 0 },
887 		{ 0, NULL, 0 }
888 	    };
889 	CK_ULONG	num_attrs = sizeof (attrs) / sizeof (CK_ATTRIBUTE);
890 	CK_ULONG	cur_attr = 1;		/* CKA_TOKEN already set */
891 	CK_OBJECT_CLASS		obj_class;
892 	CK_OBJECT_HANDLE	tmp_obj;
893 	CK_ULONG		obj_count = 0;
894 	int			i;
895 
896 	cryptodebug("inside find_obj");
897 
898 	if ((rv = find_obj_count(sess, obj_type, label, count)) != CKR_OK)
899 		return (rv);
900 
901 	if (*count == 0)
902 		return (CKR_OK);
903 
904 	if ((*obj = (CK_OBJECT_HANDLE_PTR) malloc((*count) *
905 	    sizeof (CK_OBJECT_HANDLE))) == NULL) {
906 		cryptodebug("no memory for found object");
907 		return (CKR_HOST_MEMORY);
908 	}
909 
910 	if (label) {
911 		cryptodebug("object label was specified");
912 		attrs[cur_attr].type = CKA_LABEL;
913 		attrs[cur_attr].pValue = label;
914 		attrs[cur_attr].ulValueLen = strlen((char *)label);
915 		cur_attr++;
916 	}
917 
918 	if ((obj_type & PK_PRIVATE_OBJ) && !(obj_type & PK_PUBLIC_OBJ)) {
919 		cryptodebug("only searching for private objects");
920 		attrs[cur_attr].type = CKA_PRIVATE;
921 		attrs[cur_attr].pValue = &pk_true;
922 		attrs[cur_attr].ulValueLen = sizeof (pk_true);
923 		cur_attr++;
924 	}
925 
926 	/*
927 	 * If "certs and all keys" is not specified, but at least either
928 	 * "certs" or some "keys" is specified, then go into this block.
929 	 * If all certs and keys were specified, there's no point in
930 	 * putting that fact in the attribute template -- leave that open,
931 	 * and all certs and keys will be matched automatically.
932 	 * In other words, only if at least one of 0x10,0x20,0x40,0x80
933 	 * bits is off, go into this code block.
934 	 *
935 	 * NOTE:  For now, only one of cert or key types is allowed.
936 	 * This needs to change in the future.
937 	 */
938 	if ((obj_type & (PK_CERT_OBJ|PK_KEY_OBJ)) != (PK_CERT_OBJ|PK_KEY_OBJ) &&
939 	    ((obj_type & PK_CERT_OBJ) || (obj_type & PK_KEY_OBJ))) {
940 		if (obj_type & PK_CERT_OBJ) {
941 			cryptodebug("only searching for certificates");
942 			obj_class = CKO_CERTIFICATE;
943 		} else if (obj_type & PK_PRIKEY_OBJ) {
944 			cryptodebug("only searching for private keys");
945 			obj_class = CKO_PRIVATE_KEY;
946 		} else if (obj_type & PK_PUBKEY_OBJ) {
947 			cryptodebug("only searching for public keys");
948 			obj_class = CKO_PUBLIC_KEY;
949 		} else if (obj_type & PK_SECKEY_OBJ) {
950 			cryptodebug("only searching for secret keys");
951 			obj_class = CKO_SECRET_KEY;
952 		}
953 
954 		attrs[cur_attr].type = CKA_CLASS;
955 		attrs[cur_attr].pValue = &obj_class;
956 		attrs[cur_attr].ulValueLen = sizeof (CK_OBJECT_CLASS);
957 		cur_attr++;
958 	}
959 
960 	/*
961 	 * This can't happen now.  When finding objects is enhanced in the
962 	 * future. this could lead to buffer overruns.
963 	 */
964 	if (cur_attr > num_attrs)
965 		cryptodebug("internal error:  attr template overrun");
966 
967 	cryptodebug("calling C_FindObjectsInit");
968 	if ((rv = C_FindObjectsInit(sess, attrs, cur_attr)) != CKR_OK) {
969 		free(*obj);
970 		return (rv);
971 	}
972 
973 	/*
974 	 * Find all the matching objects.  The loop goes 1 more beyond
975 	 * the number of objects found to determine if any new objects
976 	 * were created since the time the object count was done.
977 	 */
978 	cryptodebug("calling C_FindObjects");
979 	for (i = 0; i < (*count) + 1; i++) {
980 		if ((rv = C_FindObjects(sess, &tmp_obj, 1, &obj_count)) !=
981 		    CKR_OK)
982 			break;
983 
984 		/* No more found. */
985 		if (obj_count == 0)
986 			break;
987 
988 		/*
989 		 * Save the object in the list being created, as long as
990 		 * we don't overrun the size of the list.
991 		 */
992 		if (i < *count)
993 		    (*obj)[i] = tmp_obj;
994 		else
995 		    cryptodebug("number of objects changed since last count");
996 	}
997 
998 	if (rv != CKR_OK) {
999 		free(*obj);
1000 	} else {
1001 		/*
1002 		 * There are three cases to handle:  (1) fewer objects were
1003 		 * found than originally counted => change *count to the
1004 		 * smaller number; (2) the number of objects found matches
1005 		 * the number originally counted => do nothing; (3) more
1006 		 * objects found than originally counted => list passed
1007 		 * in is too small to contain the extra object(s), flag
1008 		 * that in the debug output but don't change number of
1009 		 * objects returned.  The caller can double-check by
1010 		 * calling find_obj_count() after this function to make
1011 		 * sure the numbers match, if desired.
1012 		 */
1013 		/* Case 1:  Fewer objects. */
1014 		if (i < *count) {
1015 			cryptodebug("%d objects found, expected %d", i, *count);
1016 			*count = i;
1017 		/* Case 3:  More objects. */
1018 		} else if (i > *count) {
1019 			cryptodebug("at least %d objects found, expected %d",
1020 			    i, *count);
1021 		}
1022 		/*
1023 		 * Case 2:  Same number of objects.
1024 		 *
1025 		 * else if (i == *count)
1026 		 *	;
1027 		 */
1028 	}
1029 
1030 	cryptodebug("calling C_FindObjectsFinal");
1031 	(void) C_FindObjectsFinal(sess);
1032 	return (rv);
1033 }
1034 
1035 char *
1036 class_str(CK_OBJECT_CLASS class)
1037 {
1038 	switch (class) {
1039 	case CKO_DATA:		return (gettext("data"));
1040 	case CKO_CERTIFICATE:	return (gettext("certificate"));
1041 	case CKO_PUBLIC_KEY:	return (gettext("public key"));
1042 	case CKO_PRIVATE_KEY:	return (gettext("private key"));
1043 	case CKO_SECRET_KEY:	return (gettext("secret key"));
1044 	case CKO_DOMAIN_PARAMETERS:	return (gettext("domain parameter"));
1045 	default:		return (gettext("unknown object"));
1046 	}
1047 }
1048 
1049 char *
1050 keytype_str(CK_KEY_TYPE keytype)
1051 {
1052 	switch (keytype) {
1053 	case CKK_RSA:		return (gettext("RSA"));
1054 	case CKK_DSA:		return (gettext("DSA"));
1055 	case CKK_DH:		return (gettext("Diffie-Hellman"));
1056 	case CKK_X9_42_DH:	return (gettext("X9.42 Diffie-Hellman"));
1057 	case CKK_GENERIC_SECRET:	return (gettext("generic"));
1058 	case CKK_RC2:		return (gettext("RC2"));
1059 	case CKK_RC4:		return (gettext("RC4"));
1060 	case CKK_DES:		return (gettext("DES"));
1061 	case CKK_DES2:		return (gettext("Double-DES"));
1062 	case CKK_DES3:		return (gettext("Triple-DES"));
1063 	case CKK_RC5:		return (gettext("RC5"));
1064 	case CKK_AES:		return (gettext("AES"));
1065 	default:		return (gettext("typeless"));
1066 	}
1067 }
1068 
1069 char *
1070 attr_str(CK_ATTRIBUTE_TYPE attrtype)
1071 {
1072 	switch (attrtype) {
1073 	case CKA_PRIVATE:		return (gettext("private"));
1074 	case CKA_LOCAL:			return (gettext("local"));
1075 	case CKA_SENSITIVE:		return (gettext("sensitive"));
1076 	case CKA_EXTRACTABLE:		return (gettext("extractable"));
1077 	case CKA_ENCRYPT:		return (gettext("encrypt"));
1078 	case CKA_DECRYPT:		return (gettext("decrypt"));
1079 	case CKA_WRAP:			return (gettext("wrap"));
1080 	case CKA_UNWRAP:		return (gettext("unwrap"));
1081 	case CKA_SIGN:			return (gettext("sign"));
1082 	case CKA_SIGN_RECOVER:		return (gettext("sign-recover"));
1083 	case CKA_VERIFY:		return (gettext("verify"));
1084 	case CKA_VERIFY_RECOVER:	return (gettext("verify-recover"));
1085 	case CKA_DERIVE:		return (gettext("derive"));
1086 	case CKA_ALWAYS_SENSITIVE:	return (gettext("always sensitive"));
1087 	case CKA_NEVER_EXTRACTABLE:	return (gettext("never extractable"));
1088 	default:		return (gettext("unknown capability"));
1089 	}
1090 }
1091 
1092 /*
1093  * Convert a byte string into a string of octets formatted like this:
1094  *	oo oo oo oo oo ... oo
1095  * where each "oo" is an octet is space separated and in the form:
1096  *	[0-f][0-f] if the octet is a non-printable character
1097  *	<space><char> if the octet is a printable character
1098  *
1099  * Note:  octets_sz must be 3 * str_sz + 1, or at least as long as "blank"
1100  */
1101 void
1102 octetify(CK_BYTE *str, CK_ULONG str_sz, char *octets, int octets_sz,
1103     boolean_t stop_on_nul, boolean_t do_ascii, int limit, char *indent,
1104     char *blank)
1105 {
1106 	char		*marker;
1107 	int		nc;
1108 	int		newline;
1109 	int		indent_len;
1110 	boolean_t	first = B_TRUE;
1111 
1112 	cryptodebug("inside octetify");
1113 
1114 	cryptodebug(stop_on_nul ? "stopping on first nul found" :
1115 	    "continuing to full length of buffer");
1116 	cryptodebug(do_ascii ? "using ascii chars where printable" :
1117 	    "using only hex octets");
1118 	cryptodebug("every %d characters indent with \"%s\"\n ", limit, indent);
1119 	cryptodebug("return \"%s\" if buffer is null or empty", blank);
1120 
1121 	/* If string is empty, write as much of the blank string and leave. */
1122 	if (str_sz == 0) {
1123 		(void) snprintf(octets, octets_sz, "%s", blank);
1124 		return;
1125 	}
1126 
1127 	/* If only limit or indent is set, pick default for the other. */
1128 	if (limit > 0 && indent == NULL)
1129 		indent = "\n";
1130 	if (indent != NULL && limit == 0)
1131 		limit = 60;
1132 	indent_len = strlen(indent);
1133 
1134 	for (marker = octets, newline = 0, first = B_TRUE;
1135 	    (stop_on_nul && *str != '\0') ||
1136 	    (!stop_on_nul && str_sz > 0 && octets_sz > 0);
1137 	    str++, str_sz--, marker += nc, octets_sz -= nc) {
1138 		if (!first) {
1139 			if (limit > 0 && ((marker - octets) / limit) >
1140 			    newline) {
1141 				nc = snprintf(marker, indent_len, "%s", indent);
1142 				newline++;
1143 				continue;
1144 			}
1145 			nc = sprintf(marker,
1146 			    ((do_ascii && isprint(*str) && !isspace(*str)) ?
1147 			    "%s%c" : "%s%02x"), (do_ascii ? " " : ":"), *str);
1148 		} else {
1149 			nc = sprintf(marker,
1150 			    ((do_ascii && isprint(*str) && !isspace(*str)) ?
1151 			    "%c" : "%02x"), *str);
1152 			first = B_FALSE;
1153 		}
1154 	}
1155 	*marker = '\0';
1156 }
1157 
1158 /*
1159  * Copies a biginteger_t to a template attribute.
1160  * Should be a macro instead of a function.
1161  */
1162 void
1163 copy_bigint_to_attr(biginteger_t big, CK_ATTRIBUTE_PTR attr)
1164 {
1165 	attr->pValue = big.big_value;
1166 	attr->ulValueLen = big.big_value_len;
1167 }
1168 
1169 /*
1170  * Copies a string and its length to a template attribute.
1171  * Should be a macro instead of a function.
1172  */
1173 void
1174 copy_string_to_attr(CK_BYTE *buf, CK_ULONG buflen, CK_ATTRIBUTE_PTR attr)
1175 {
1176 	attr->pValue = buf;
1177 	attr->ulValueLen = buflen;
1178 }
1179 
1180 /*
1181  * Copies a template attribute to a biginteger_t.
1182  * Should be a macro instead of a function.
1183  */
1184 void
1185 copy_attr_to_bigint(CK_ATTRIBUTE_PTR attr, biginteger_t *big)
1186 {
1187 	big->big_value = attr->pValue;
1188 	big->big_value_len = attr->ulValueLen;
1189 }
1190 
1191 /*
1192  * Copies a template attribute to a string and its length.
1193  * Should be a macro instead of a function.
1194  */
1195 void
1196 copy_attr_to_string(CK_ATTRIBUTE_PTR attr, CK_BYTE **buf, CK_ULONG *buflen)
1197 {
1198 	*buf = attr->pValue;
1199 	*buflen = attr->ulValueLen;
1200 }
1201 
1202 /*
1203  * Copies a template attribute to a date and its length.
1204  * Should be a macro instead of a function.
1205  */
1206 void
1207 copy_attr_to_date(CK_ATTRIBUTE_PTR attr, CK_DATE **buf, CK_ULONG *buflen)
1208 {
1209 	*buf = (CK_DATE *)attr->pValue;
1210 	*buflen = attr->ulValueLen;
1211 }
1212 
1213 /*
1214  * Breaks out the getopt-style option string into a structure that can be
1215  * traversed later for calls to getopt_av().  Option string is NOT altered,
1216  * but the struct fields point to locations within option string.
1217  */
1218 static int
1219 populate_opts(char *optstring)
1220 {
1221 	int		i;
1222 	av_opts		*temp;
1223 	char		*marker;
1224 
1225 	if (optstring == NULL || *optstring == '\0')
1226 		return (0);
1227 
1228 	/*
1229 	 * This tries to imitate getopt(3c) Each option must conform to:
1230 	 * <short name char> [ ':' ] [ '(' <long name string> ')' ]
1231 	 * If long name is missing, the short name is used for long name.
1232 	 */
1233 	for (i = 0; *optstring != '\0'; i++) {
1234 		if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) :
1235 		    realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) {
1236 			free(opts_av);
1237 			opts_av = NULL;
1238 			return (0);
1239 		} else
1240 			opts_av = (av_opts *)temp;
1241 
1242 		marker = optstring;		/* may need optstring later */
1243 
1244 		opts_av[i].shortnm = *marker++;	/* set short name */
1245 
1246 		if (*marker == ':') {		/* check for opt arg */
1247 			marker++;
1248 			opts_av[i].has_arg = B_TRUE;
1249 		}
1250 
1251 		if (*marker == '(') {		/* check and set long name */
1252 			marker++;
1253 			opts_av[i].longnm = marker;
1254 			opts_av[i].longnm_len = strcspn(marker, ")");
1255 			optstring = marker + opts_av[i].longnm_len + 1;
1256 		} else {
1257 			/* use short name option character */
1258 			opts_av[i].longnm = optstring;
1259 			opts_av[i].longnm_len = 1;
1260 			optstring = marker;
1261 		}
1262 	}
1263 
1264 	return (i);
1265 }
1266 
1267 /*
1268  * getopt_av() is very similar to getopt(3c) in that the takes an option
1269  * string, compares command line arguments for matches, and returns a single
1270  * letter option when a match is found.  However, getopt_av() differs from
1271  * getopt(3c) by requiring that only longname options and values be found
1272  * on the command line and all leading dashes are omitted.  In other words,
1273  * it tries to enforce only longname "option=value" arguments on the command
1274  * line.  Boolean options are not allowed either.
1275  */
1276 int
1277 getopt_av(int argc, char * const *argv, const char *optstring)
1278 {
1279 	int	i;
1280 	int	len;
1281 
1282 	if (optind_av >= argc)
1283 		return (EOF);
1284 
1285 	/* First time or when optstring changes from previous one */
1286 	if (_save_optstr != optstring) {
1287 		if (opts_av != NULL)
1288 		    free(opts_av);
1289 		opts_av = NULL;
1290 		_save_optstr = optstring;
1291 		_save_numopts = populate_opts((char *)optstring);
1292 	}
1293 
1294 	for (i = 0; i < _save_numopts; i++) {
1295 		if (strcmp(argv[optind_av], "--") == 0) {
1296 			optind_av++;
1297 			break;
1298 		}
1299 
1300 		len = strcspn(argv[optind_av], "=");
1301 
1302 		if (len == opts_av[i].longnm_len && strncmp(argv[optind_av],
1303 		    opts_av[i].longnm, opts_av[i].longnm_len) == 0) {
1304 			/* matched */
1305 			if (!opts_av[i].has_arg) {
1306 				optind_av++;
1307 				return (opts_av[i].shortnm);
1308 			}
1309 
1310 			/* needs optarg */
1311 			if (argv[optind_av][len] == '=') {
1312 				optarg_av = &(argv[optind_av][len+1]);
1313 				optind_av++;
1314 				return (opts_av[i].shortnm);
1315 			}
1316 
1317 			optarg_av = NULL;
1318 			optind_av++;
1319 			return ((int)'?');
1320 		}
1321 	}
1322 
1323 	return (EOF);
1324 }
1325