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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * Developer command for adding the signature section to an ELF object
28 * PSARC 2001/488
29 *
30 * DEBUG Information:
31 * This command uses the cryptodebug() function from libcryptoutil.
32 * Set SUNW_CRYPTO_DEBUG to stderr or syslog for all debug to go to auth.debug
33 */
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <stdarg.h>
38#include <limits.h>
39#include <time.h>
40#include <unistd.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <fcntl.h>
44#include <libintl.h>
45#include <locale.h>
46#include <errno.h>
47#include <strings.h>
48
49#include <cryptoutil.h>
50#include <sys/crypto/elfsign.h>
51#include <libelfsign.h>
52
53#include <kmfapi.h>
54
55#define	SIGN		"sign"
56#define	SIGN_OPTS	"c:e:F:k:P:T:v"
57#define	VERIFY		"verify"
58#define	VERIFY_OPTS	"c:e:v"
59#define	REQUEST		"request"
60#define	REQUEST_OPTS	"i:k:r:T:"
61#define	LIST		"list"
62#define	LIST_OPTS	"c:e:f:"
63
64enum cmd_e {
65	ES_SIGN,
66	ES_VERIFY,
67	ES_REQUEST,
68	ES_LIST
69};
70
71enum field_e {
72	FLD_UNKNOWN,
73	FLD_SUBJECT,
74	FLD_ISSUER,
75	FLD_FORMAT,
76	FLD_SIGNER,
77	FLD_TIME
78};
79
80#define	MIN_ARGS	3	/* The minimum # args to do anything */
81#define	ES_DEFAULT_KEYSIZE 1024
82
83static struct {
84	enum cmd_e	cmd;	/* sub command: sign | verify | request */
85	char	*cert;		/* -c <certificate_file> | */
86				/* -r <certificate_request_file> */
87	char	**elfobj;	/* -e <elf_object> */
88	int	elfcnt;
89	enum ES_ACTION	es_action;
90	ELFsign_t	ess;	/* libelfsign opaque "state" */
91	int	extracnt;
92	enum field_e	field;	/* -f <field> */
93	char internal_req;	/* Sun internal certificate request */
94	char	*pinpath;	/* -P <pin> */
95	char	*privpath;	/* -k <private_key> */
96	char	*token_label;	/* -T <token_label> */
97	boolean_t verbose;	/* chatty output */
98} cmd_info;
99
100enum ret_e {
101	EXIT_OKAY,
102	EXIT_INVALID_ARG,
103	EXIT_VERIFY_FAILED,
104	EXIT_CANT_OPEN_ELF_OBJECT,
105	EXIT_BAD_CERT,
106	EXIT_BAD_PRIVATEKEY,
107	EXIT_SIGN_FAILED,
108	EXIT_VERIFY_FAILED_UNSIGNED,
109	EXIT_CSR_FAILED,
110	EXIT_MEMORY_ERROR
111};
112
113struct field_s {
114	char	*name;
115	enum field_e	field;
116} fields[] = {
117	{ "subject", FLD_SUBJECT },
118	{ "issuer", FLD_ISSUER },
119	{ "format", FLD_FORMAT },
120	{ "signer", FLD_SIGNER },
121	{ "time", FLD_TIME },
122	NULL, 0
123};
124
125typedef enum ret_e ret_t;
126
127static void usage(void);
128static ret_t getelfobj(char *);
129static char *getpin(void);
130static ret_t do_sign(char *);
131static ret_t do_verify(char *);
132static ret_t do_cert_request(char *);
133static ret_t do_list(char *);
134static void es_error(const char *fmt, ...);
135static char *time_str(time_t t);
136static void sig_info_print(struct ELFsign_sig_info *esip);
137
138int
139main(int argc, char **argv)
140{
141	extern char *optarg;
142	char *scmd = NULL;
143	char *opts;		/* The set of flags for cmd */
144	int errflag = 0;	/* We had an options parse error */
145	char c;			/* current getopts flag */
146	ret_t (*action)(char *);	/* Function pointer for the action */
147	ret_t ret;
148
149	(void) setlocale(LC_ALL, "");
150#if !defined(TEXT_DOMAIN)	/* Should be defiend by cc -D */
151#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
152#endif
153	(void) textdomain(TEXT_DOMAIN);
154
155	cryptodebug_init("elfsign");
156
157	if (argc < MIN_ARGS) {
158		es_error(gettext("invalid number of arguments"));
159		usage();
160		return (EXIT_INVALID_ARG);
161	}
162
163	scmd = argv[1];
164	cmd_info.cert = NULL;
165	cmd_info.elfobj = NULL;
166	cmd_info.elfcnt = 0;
167	cmd_info.es_action = ES_GET;
168	cmd_info.ess = NULL;
169	cmd_info.extracnt = 0;
170	cmd_info.field = FLD_UNKNOWN;
171	cmd_info.internal_req = '\0';
172	cmd_info.pinpath = NULL;
173	cmd_info.privpath = NULL;
174	cmd_info.token_label = NULL;
175	cmd_info.verbose = B_FALSE;
176
177	if (strcmp(scmd, SIGN) == 0) {
178		cmd_info.cmd = ES_SIGN;
179		opts = SIGN_OPTS;
180		cryptodebug("cmd=sign opts=%s", opts);
181		action = do_sign;
182		cmd_info.es_action = ES_UPDATE_RSA_SHA1;
183	} else if (strcmp(scmd, VERIFY) == 0) {
184		cmd_info.cmd = ES_VERIFY;
185		opts = VERIFY_OPTS;
186		cryptodebug("cmd=verify opts=%s", opts);
187		action = do_verify;
188	} else if (strcmp(scmd, REQUEST) == 0) {
189		cmd_info.cmd = ES_REQUEST;
190		opts = REQUEST_OPTS;
191		cryptodebug("cmd=request opts=%s", opts);
192		action = do_cert_request;
193	} else if (strcmp(scmd, LIST) == 0) {
194		cmd_info.cmd = ES_LIST;
195		opts = LIST_OPTS;
196		cryptodebug("cmd=list opts=%s", opts);
197		action = do_list;
198	} else {
199		es_error(gettext("Unknown sub-command: %s"),
200		    scmd);
201		usage();
202		return (EXIT_INVALID_ARG);
203	}
204
205	/*
206	 * Note:  There is no need to check that optarg isn't NULL
207	 *	  because getopt does that for us.
208	 */
209	while (!errflag && (c = getopt(argc - 1, argv + 1, opts)) != EOF) {
210		if (strchr("ceFihkPTr", c) != NULL)
211			cryptodebug("c=%c, '%s'", c, optarg);
212		else
213			cryptodebug("c=%c", c);
214
215		switch (c) {
216		case 'c':
217			cmd_info.cert = optarg;
218			break;
219		case 'e':
220			cmd_info.elfcnt++;
221			cmd_info.elfobj = (char **)realloc(cmd_info.elfobj,
222			    sizeof (char *) * cmd_info.elfcnt);
223			if (cmd_info.elfobj == NULL) {
224				es_error(gettext(
225				    "Too many elf objects specified."));
226				return (EXIT_INVALID_ARG);
227			}
228			cmd_info.elfobj[cmd_info.elfcnt - 1] = optarg;
229			break;
230		case 'f':
231			{
232				struct field_s	*fp;
233				cmd_info.field = FLD_UNKNOWN;
234				for (fp = fields; fp->name != NULL; fp++) {
235					if (strcasecmp(optarg, fp->name) == 0) {
236						cmd_info.field = fp->field;
237						break;
238					}
239				}
240				if (cmd_info.field == FLD_UNKNOWN) {
241					cryptodebug("Invalid field option");
242					errflag++;
243				}
244			}
245			break;
246		case 'F':
247			if (strcasecmp(optarg, ES_FMT_RSA_MD5_SHA1) == 0)
248				cmd_info.es_action = ES_UPDATE_RSA_MD5_SHA1;
249			else if (strcasecmp(optarg, ES_FMT_RSA_SHA1) == 0)
250				cmd_info.es_action = ES_UPDATE_RSA_SHA1;
251			else {
252				cryptodebug("Invalid format option");
253				errflag++;
254			}
255			break;
256		case 'i':	 /* Undocumented internal Sun use only */
257			cmd_info.internal_req = *optarg;
258			break;
259		case 'k':
260			cmd_info.privpath = optarg;
261			if (cmd_info.token_label != NULL ||
262			    cmd_info.pinpath != NULL)
263				errflag++;
264			break;
265		case 'P':
266			cmd_info.pinpath = optarg;
267			if (cmd_info.privpath != NULL)
268				errflag++;
269			break;
270		case 'r':
271			cmd_info.cert = optarg;
272			break;
273		case 'T':
274			cmd_info.token_label = optarg;
275			if (cmd_info.privpath != NULL)
276				errflag++;
277			break;
278		case 'v':
279			cmd_info.verbose = B_TRUE;
280			break;
281		default:
282			errflag++;
283		}
284	}
285
286	optind++;	/* we skipped over subcommand */
287	cmd_info.extracnt = argc - optind;
288
289	if (cmd_info.extracnt != 0 &&
290	    cmd_info.cmd != ES_SIGN && cmd_info.cmd != ES_VERIFY) {
291		cryptodebug("Extra arguments, optind=%d, argc=%d",
292		    optind, argc);
293		errflag++;
294	}
295
296	switch (cmd_info.cmd) {
297	case ES_VERIFY:
298		if (cmd_info.elfcnt + argc - optind == 0) {
299			cryptodebug("Missing elfobj");
300			errflag++;
301		}
302		break;
303
304	case ES_SIGN:
305		if (((cmd_info.privpath == NULL) &&
306		    (cmd_info.token_label == NULL)) ||
307		    (cmd_info.cert == NULL) ||
308		    (cmd_info.elfcnt + argc - optind == 0)) {
309			cryptodebug("Missing privpath|token_label/cert/elfobj");
310			errflag++;
311		}
312		break;
313
314	case ES_REQUEST:
315		if (((cmd_info.privpath == NULL) &&
316		    (cmd_info.token_label == NULL)) ||
317		    (cmd_info.cert == NULL)) {
318			cryptodebug("Missing privpath|token_label/certreq");
319			errflag++;
320		}
321		break;
322	case ES_LIST:
323		if ((cmd_info.cert != NULL) == (cmd_info.elfcnt > 0)) {
324			cryptodebug("Neither or both of cert/elfobj");
325			errflag++;
326		}
327		break;
328	}
329
330	if (errflag) {
331		usage();
332		return (EXIT_INVALID_ARG);
333	}
334
335	switch (cmd_info.cmd) {
336	case ES_REQUEST:
337	case ES_LIST:
338		ret = action(NULL);
339		break;
340	default:
341		{
342		int i;
343		ret_t	iret;
344
345		ret = EXIT_OKAY;
346		iret = EXIT_OKAY;
347		for (i = 0; i < cmd_info.elfcnt &&
348		    (ret == EXIT_OKAY || cmd_info.cmd != ES_SIGN); i++) {
349			iret = action(cmd_info.elfobj[i]);
350			if (iret > ret)
351				ret = iret;
352		}
353		for (i = optind; i < argc &&
354		    (ret == EXIT_OKAY || cmd_info.cmd != ES_SIGN); i++) {
355			iret = action(argv[i]);
356			if (iret > ret)
357				ret = iret;
358		}
359		break;
360		}
361	}
362
363	if (cmd_info.elfobj != NULL)
364		free(cmd_info.elfobj);
365
366	return (ret);
367}
368
369
370static void
371usage(void)
372{
373/* BEGIN CSTYLED */
374	(void) fprintf(stderr, gettext(
375 "usage:\n"
376 "\telfsign sign [-v] [-e <elf_object>] -c <certificate_file>\n"
377 "\t\t[-F <format>] -k <private_key_file> [elf_object]..."
378 "\n"
379 "\telfsign sign [-v] [-e <elf_object>] -c <certificate_file>\n"
380 "\t\t[-F <format>] -T <token_label> [-P <pin_file>] [elf_object]..."
381 "\n\n"
382 "\telfsign verify [-v] [-c <certificate_file>] [-e <elf_object>]\n"
383 "\t\t[elf_object]..."
384 "\n\n"
385 "\telfsign request -r <certificate_request_file> -k <private_key_file>"
386 "\n"
387 "\telfsign request -r <certificate_request_file> -T <token_label>"
388 "\n\n"
389 "\telfsign list -f field -c <certificate_file>"
390 "\n"
391 "\telfsign list -f field -e <elf_object>"
392 "\n"));
393/* END CSTYLED */
394}
395
396static ret_t
397getelfobj(char *elfpath)
398{
399	ELFsign_status_t estatus;
400	ret_t	ret = EXIT_SIGN_FAILED;
401
402	estatus = elfsign_begin(elfpath, cmd_info.es_action, &(cmd_info.ess));
403	switch (estatus) {
404	case ELFSIGN_SUCCESS:
405		ret = EXIT_OKAY;
406		break;
407	case ELFSIGN_INVALID_ELFOBJ:
408		es_error(gettext(
409		    "Unable to open %s as an ELF object."),
410		    elfpath);
411		ret = EXIT_CANT_OPEN_ELF_OBJECT;
412		break;
413	default:
414		es_error(gettext("unexpected failure: %d"), estatus);
415		if (cmd_info.cmd == ES_SIGN) {
416			ret = EXIT_SIGN_FAILED;
417		} else if (cmd_info.cmd == ES_VERIFY) {
418			ret = EXIT_VERIFY_FAILED;
419		}
420	}
421
422	return (ret);
423}
424
425static ret_t
426setcertpath(void)
427{
428	ELFsign_status_t estatus;
429	ret_t	ret = EXIT_SIGN_FAILED;
430
431	if (cmd_info.cert == NULL)
432		return (EXIT_OKAY);
433	estatus = elfsign_setcertpath(cmd_info.ess, cmd_info.cert);
434	switch (estatus) {
435	case ELFSIGN_SUCCESS:
436		ret = EXIT_OKAY;
437		break;
438	case ELFSIGN_INVALID_CERTPATH:
439		if (cmd_info.cert != NULL) {
440			es_error(gettext("Unable to open %s as a certificate."),
441			    cmd_info.cert);
442		}
443		ret = EXIT_BAD_CERT;
444		break;
445	default:
446		es_error(gettext("unusable certificate: %s"), cmd_info.cert);
447		if (cmd_info.cmd == ES_SIGN) {
448			ret = EXIT_SIGN_FAILED;
449		} else if (cmd_info.cmd == ES_VERIFY) {
450			ret = EXIT_VERIFY_FAILED;
451		}
452	}
453
454	return (ret);
455}
456
457/*
458 * getpin - return pointer to token PIN in static storage
459 */
460static char *
461getpin(void)
462{
463	static char	pinbuf[PASS_MAX + 1];
464	char	*pp;
465	FILE	*pinfile;
466
467	if (cmd_info.pinpath == NULL)
468		return (getpassphrase(
469		    gettext("Enter PIN for PKCS#11 token: ")));
470	if ((pinfile = fopen(cmd_info.pinpath, "r")) == NULL) {
471		es_error(gettext("failed to open %s."),
472		    cmd_info.pinpath);
473		return (NULL);
474	}
475
476	pp = fgets(pinbuf, sizeof (pinbuf), pinfile);
477	(void) fclose(pinfile);
478	if (pp == NULL) {
479		es_error(gettext("failed to read PIN from %s."),
480		    cmd_info.pinpath);
481		return (NULL);
482	}
483	pp = &pinbuf[strlen(pinbuf) - 1];
484	if (*pp == '\n')
485		*pp = '\0';
486	return (pinbuf);
487}
488
489/*
490 * Add the .SUNW_signature sections for the ELF signature
491 */
492static ret_t
493do_sign(char *object)
494{
495	ret_t 	ret;
496	ELFsign_status_t	elfstat;
497	struct filesignatures	*fssp = NULL;
498	size_t fs_len;
499	uchar_t sig[SIG_MAX_LENGTH];
500	size_t	sig_len = SIG_MAX_LENGTH;
501	uchar_t	hash[SIG_MAX_LENGTH];
502	size_t	hash_len = SIG_MAX_LENGTH;
503	ELFCert_t	cert = NULL;
504	char	*dn;
505	size_t	dn_len;
506
507	cryptodebug("do_sign");
508	if ((ret = getelfobj(object)) != EXIT_OKAY)
509		return (ret);
510
511	if (cmd_info.token_label &&
512	    !elfcertlib_settoken(cmd_info.ess, cmd_info.token_label)) {
513		es_error(gettext("Unable to access token: %s"),
514		    cmd_info.token_label);
515		ret = EXIT_SIGN_FAILED;
516		goto cleanup;
517	}
518
519	if ((ret = setcertpath()) != EXIT_OKAY)
520		goto cleanup;
521
522	if (!elfcertlib_getcert(cmd_info.ess, cmd_info.cert, NULL, &cert,
523	    cmd_info.es_action)) {
524		es_error(gettext("Unable to load certificate: %s"),
525		    cmd_info.cert);
526		ret = EXIT_BAD_CERT;
527		goto cleanup;
528	}
529
530	if (cmd_info.privpath != NULL) {
531		if (!elfcertlib_loadprivatekey(cmd_info.ess, cert,
532		    cmd_info.privpath)) {
533			es_error(gettext("Unable to load private key: %s"),
534			    cmd_info.privpath);
535			ret = EXIT_BAD_PRIVATEKEY;
536			goto cleanup;
537		}
538	} else {
539		char *pin = getpin();
540		if (pin == NULL) {
541			es_error(gettext("Unable to get PIN"));
542			ret = EXIT_BAD_PRIVATEKEY;
543			goto cleanup;
544		}
545		if (!elfcertlib_loadtokenkey(cmd_info.ess, cert,
546		    cmd_info.token_label, pin)) {
547			es_error(gettext("Unable to access private key "
548			    "in token %s"), cmd_info.token_label);
549			ret = EXIT_BAD_PRIVATEKEY;
550			goto cleanup;
551		}
552	}
553
554	/*
555	 * Get the DN from the certificate.
556	 */
557	if ((dn = elfcertlib_getdn(cert)) == NULL) {
558		es_error(gettext("Unable to find DN in certificate %s"),
559		    cmd_info.cert);
560		ret = EXIT_SIGN_FAILED;
561		goto cleanup;
562	}
563	dn_len = strlen(dn);
564	cryptodebug("DN = %s", dn);
565
566	elfstat = elfsign_signatures(cmd_info.ess, &fssp, &fs_len, ES_GET);
567	if (elfstat != ELFSIGN_SUCCESS) {
568		if (elfstat != ELFSIGN_NOTSIGNED) {
569			es_error(gettext("Unable to retrieve existing "
570			    "signature block in %s"), object);
571			ret = EXIT_SIGN_FAILED;
572			goto cleanup;
573		}
574		fssp = NULL;
575		/*
576		 * force creation and naming of signature section
577		 * so the hash doesn't change
578		 */
579		if (elfsign_signatures(cmd_info.ess, &fssp, &fs_len,
580		    cmd_info.es_action) != ELFSIGN_SUCCESS) {
581			es_error(gettext("Unable to insert "
582			    "signature block into %s"), object);
583			ret = EXIT_SIGN_FAILED;
584			goto cleanup;
585		}
586	}
587
588	bzero(hash, sizeof (hash));
589	if (elfsign_hash(cmd_info.ess, hash, &hash_len) != ELFSIGN_SUCCESS) {
590		es_error(gettext("Unable to calculate hash of ELF object %s"),
591		    object);
592		ret = EXIT_SIGN_FAILED;
593		goto cleanup;
594	}
595
596	bzero(sig, sizeof (sig));
597	if (!elfcertlib_sign(cmd_info.ess, cert,
598	    hash, hash_len, sig, &sig_len)) {
599		es_error(gettext("Unable to sign %s using key from %s"),
600		    object, cmd_info.privpath ?
601		    cmd_info.privpath : cmd_info.token_label);
602		ret = EXIT_SIGN_FAILED;
603		goto cleanup;
604	}
605
606	{ /* DEBUG START */
607		const int sigstr_len = sizeof (char) * sig_len * 2 + 1;
608		char *sigstr = malloc(sigstr_len);
609
610		tohexstr(sig, sig_len, sigstr, sigstr_len);
611		cryptodebug("sig value is: %s", sigstr);
612		free(sigstr);
613	} /* DEBUG END */
614
615	fssp = elfsign_insert_dso(cmd_info.ess, fssp,
616	    dn, dn_len, sig, sig_len, NULL, 0);
617	if (fssp == NULL) {
618		es_error(gettext("Unable to prepare signature for %s"),
619		    object);
620		ret = EXIT_SIGN_FAILED;
621		goto cleanup;
622	}
623	if (elfsign_signatures(cmd_info.ess, &fssp, &fs_len,
624	    cmd_info.es_action) != ELFSIGN_SUCCESS) {
625		es_error(gettext("Unable to update %s: with signature"),
626		    object);
627		ret = EXIT_SIGN_FAILED;
628		goto cleanup;
629	}
630	if (cmd_info.verbose || (cmd_info.elfcnt + cmd_info.extracnt) > 1) {
631		(void) fprintf(stdout,
632		    gettext("elfsign: %s signed successfully.\n"),
633		    object);
634	}
635	if (cmd_info.verbose) {
636		struct ELFsign_sig_info *esip;
637
638		if (elfsign_sig_info(fssp, &esip)) {
639			sig_info_print(esip);
640			elfsign_sig_info_free(esip);
641		}
642	}
643
644	ret = EXIT_OKAY;
645
646cleanup:
647	free(fssp);
648	bzero(sig, sig_len);
649	bzero(hash, hash_len);
650
651	if (cert != NULL)
652		elfcertlib_releasecert(cmd_info.ess, cert);
653	if (cmd_info.ess != NULL)
654		elfsign_end(cmd_info.ess);
655
656	return (ret);
657}
658
659/*
660 * Verify the signature of the object
661 * This subcommand is intended to be used by developers during their build
662 * processes.  Therefore we can not assume that the certificate is in
663 * /etc/crypto/certs so we must use the path we got from the commandline.
664 */
665static ret_t
666do_verify(char *object)
667{
668	ELFsign_status_t res;
669	struct ELFsign_sig_info	*esip;
670	ret_t	retval;
671
672	cryptodebug("do_verify");
673	if ((retval = getelfobj(object)) != EXIT_OKAY)
674		return (retval);
675
676	if ((retval = setcertpath()) != EXIT_OKAY) {
677		elfsign_end(cmd_info.ess);
678		return (retval);
679	}
680
681	res = elfsign_verify_signature(cmd_info.ess, &esip);
682	switch (res) {
683	case ELFSIGN_SUCCESS:
684		(void) fprintf(stdout,
685		    gettext("elfsign: verification of %s passed.\n"),
686		    object);
687		if (cmd_info.verbose)
688			sig_info_print(esip);
689		retval = EXIT_OKAY;
690		break;
691	case ELFSIGN_FAILED:
692	case ELFSIGN_INVALID_CERTPATH:
693		es_error(gettext("verification of %s failed."),
694		    object);
695		if (cmd_info.verbose)
696			sig_info_print(esip);
697		retval = EXIT_VERIFY_FAILED;
698		break;
699	case ELFSIGN_NOTSIGNED:
700		es_error(gettext("no signature found in %s."),
701		    object);
702		retval = EXIT_VERIFY_FAILED_UNSIGNED;
703		break;
704	default:
705		es_error(gettext("unexpected failure attempting verification "
706		    "of %s."), object);
707		retval = EXIT_VERIFY_FAILED_UNSIGNED;
708		break;
709	}
710
711	if (esip != NULL)
712		elfsign_sig_info_free(esip);
713	if (cmd_info.ess != NULL)
714		elfsign_end(cmd_info.ess);
715	return (retval);
716}
717
718#define	SET_VALUE(f, s) \
719	kmfrv = f; \
720	if (kmfrv != KMF_OK) { \
721		char *e = NULL; \
722		(void) kmf_get_kmf_error_str(kmfrv, &e); \
723		cryptoerror(LOG_STDERR, \
724			gettext("Failed to %s: %s\n"), \
725			s, (e ? e : "unknown error")); \
726		if (e) free(e); \
727		goto cleanup; \
728	}
729
730static KMF_RETURN
731create_csr(char *dn)
732{
733	KMF_RETURN kmfrv = KMF_OK;
734	KMF_HANDLE_T kmfhandle = NULL;
735	KMF_KEY_HANDLE pubk, prik;
736	KMF_X509_NAME csrSubject;
737	KMF_CSR_DATA csr;
738	KMF_ALGORITHM_INDEX sigAlg = KMF_ALGID_MD5WithRSA;
739	KMF_DATA signedCsr = { NULL, 0 };
740	char *err;
741	KMF_ATTRIBUTE	attrlist[16];
742	KMF_ENCODE_FORMAT	format;
743	KMF_KEYSTORE_TYPE	kstype;
744	KMF_KEY_ALG	keytype;
745	uint32_t	keylength;
746	KMF_CREDENTIAL	cred;
747	char	*pin = NULL;
748	int	numattr;
749
750	if ((kmfrv = kmf_initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
751		(void) kmf_get_kmf_error_str(kmfrv, &err);
752		cryptoerror(LOG_STDERR,
753		    gettext("Error initializing KMF: %s\n"),
754		    (err ? err : "unknown error"));
755		if (err)
756			free(err);
757		return (kmfrv);
758	}
759	(void) memset(&csr, 0, sizeof (csr));
760	(void) memset(&csrSubject, 0, sizeof (csrSubject));
761
762	if (cmd_info.privpath != NULL) {
763		kstype = KMF_KEYSTORE_OPENSSL;
764		format = KMF_FORMAT_ASN1;
765	} else {
766		boolean_t	readonly;
767		/* args checking verified (cmd_info.token_label != NULL) */
768
769		/* Get a PIN to store the private key in the token */
770		pin = getpin();
771
772		if (pin == NULL) {
773			(void) kmf_finalize(kmfhandle);
774			return (KMF_ERR_AUTH_FAILED);
775		}
776
777		kstype = KMF_KEYSTORE_PK11TOKEN;
778		readonly = B_FALSE;
779
780		numattr = 0;
781		kmf_set_attr_at_index(attrlist, numattr++,
782		    KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype));
783		kmf_set_attr_at_index(attrlist, numattr++,
784		    KMF_TOKEN_LABEL_ATTR, cmd_info.token_label,
785		    strlen(cmd_info.token_label));
786		kmf_set_attr_at_index(attrlist, numattr++,
787		    KMF_READONLY_ATTR, &readonly, sizeof (readonly));
788		kmfrv = kmf_configure_keystore(kmfhandle, numattr, attrlist);
789		if (kmfrv != KMF_OK) {
790			goto cleanup;
791		}
792	}
793
794	/* Create the RSA keypair */
795	keytype = KMF_RSA;
796	keylength = ES_DEFAULT_KEYSIZE;
797	(void) memset(&prik, 0, sizeof (prik));
798	(void) memset(&pubk, 0, sizeof (pubk));
799
800	numattr = 0;
801	kmf_set_attr_at_index(attrlist, numattr++,
802	    KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype));
803	kmf_set_attr_at_index(attrlist, numattr++,
804	    KMF_KEYALG_ATTR, &keytype, sizeof (keytype));
805	kmf_set_attr_at_index(attrlist, numattr++,
806	    KMF_KEYLENGTH_ATTR, &keylength, sizeof (keylength));
807	if (pin != NULL) {
808		cred.cred = pin;
809		cred.credlen = strlen(pin);
810		kmf_set_attr_at_index(attrlist, numattr++,
811		    KMF_CREDENTIAL_ATTR, &cred, sizeof (KMF_CREDENTIAL));
812	}
813	kmf_set_attr_at_index(attrlist, numattr++,
814	    KMF_PRIVKEY_HANDLE_ATTR, &prik, sizeof (KMF_KEY_HANDLE));
815	kmf_set_attr_at_index(attrlist, numattr++,
816	    KMF_PUBKEY_HANDLE_ATTR, &pubk, sizeof (KMF_KEY_HANDLE));
817	if (kstype == KMF_KEYSTORE_OPENSSL) {
818		kmf_set_attr_at_index(attrlist, numattr++,
819		    KMF_KEY_FILENAME_ATTR, cmd_info.privpath,
820		    strlen(cmd_info.privpath));
821		kmf_set_attr_at_index(attrlist, numattr++,
822		    KMF_ENCODE_FORMAT_ATTR, &format, sizeof (format));
823	}
824
825	kmfrv = kmf_create_keypair(kmfhandle, numattr, attrlist);
826	if (kmfrv != KMF_OK) {
827		(void) kmf_get_kmf_error_str(kmfrv, &err);
828		cryptoerror(LOG_STDERR,
829		    gettext("Create RSA keypair failed: %s"),
830		    (err ? err : "unknown error"));
831		free(err);
832		goto cleanup;
833	}
834
835	kmfrv = kmf_dn_parser(dn, &csrSubject);
836	if (kmfrv != KMF_OK) {
837		(void) kmf_get_kmf_error_str(kmfrv, &err);
838		cryptoerror(LOG_STDERR,
839		    gettext("Error parsing subject name: %s\n"),
840		    (err ? err : "unknown error"));
841		free(err);
842		goto cleanup;
843	}
844
845	SET_VALUE(kmf_set_csr_pubkey(kmfhandle, &pubk, &csr), "keypair");
846
847	SET_VALUE(kmf_set_csr_version(&csr, 2), "version number");
848
849	SET_VALUE(kmf_set_csr_subject(&csr, &csrSubject), "subject name");
850
851	SET_VALUE(kmf_set_csr_sig_alg(&csr, sigAlg), "SignatureAlgorithm");
852
853	if ((kmfrv = kmf_sign_csr(kmfhandle, &csr, &prik, &signedCsr)) ==
854	    KMF_OK) {
855		kmfrv = kmf_create_csr_file(&signedCsr, KMF_FORMAT_PEM,
856		    cmd_info.cert);
857	}
858
859cleanup:
860	(void) kmf_free_kmf_key(kmfhandle, &prik);
861	(void) kmf_free_data(&signedCsr);
862	(void) kmf_free_signed_csr(&csr);
863	(void) kmf_finalize(kmfhandle);
864
865	return (kmfrv);
866}
867
868
869#define	CN_MAX_LENGTH	64	/* Verisign implementation limit */
870/*
871 * Generate a certificate request into the file named cmd_info.cert
872 */
873/*ARGSUSED*/
874static ret_t
875do_cert_request(char *object)
876{
877	const char	 PartnerDNFMT[] =
878	    "CN=%s, "
879	    "OU=Class B, "
880	    "OU=Solaris Cryptographic Framework, "
881	    "OU=Partner Object Signing, "
882	    "O=Sun Microsystems Inc";
883	const char	 SunCDNFMT[] =
884	    "CN=%s, "
885	    "OU=Class B, "
886	    "OU=Solaris Cryptographic Framework, "
887	    "OU=Corporate Object Signing, "
888	    "O=Sun Microsystems Inc";
889	const char	 SunSDNFMT[] =
890	    "CN=%s, "
891	    "OU=Class B, "
892	    "OU=Solaris Signed Execution, "
893	    "OU=Corporate Object Signing, "
894	    "O=Sun Microsystems Inc";
895	const char	 *dnfmt = NULL;
896	char	cn[CN_MAX_LENGTH + 1];
897	char	*dn = NULL;
898	size_t	dn_len;
899	KMF_RETURN   kmfret;
900	cryptodebug("do_cert_request");
901
902	/*
903	 * Get the DN prefix from the user
904	 */
905	switch (cmd_info.internal_req) {
906	case 'c':
907		dnfmt = SunCDNFMT;
908		(void) fprintf(stdout, gettext(
909		    "Enter Sun Microsystems, Inc. Release name.\n"
910		    "This will be the prefix of the Certificate DN: "));
911		break;
912	case 's':
913		dnfmt = SunSDNFMT;
914		(void) fprintf(stdout, gettext(
915		    "Enter Sun Microsystems, Inc. Release name.\n"
916		    "This will be the prefix of the Certificate DN: "));
917		break;
918	default:
919		dnfmt = PartnerDNFMT;
920		(void) fprintf(stdout, gettext(
921		    "Enter Company Name / Stock Symbol"
922		    " or some other globally unique identifier.\n"
923		    "This will be the prefix of the Certificate DN: "));
924		break;
925	}
926
927	(void) fgets(cn, sizeof (cn), stdin);
928	if ((cn == NULL) || (cn[0] == '\n')) {
929		es_error(gettext("you must specify a Certificate DN prefix"));
930		return (EXIT_INVALID_ARG);
931	}
932
933	if (cn[strlen(cn) - 1] == '\n') {
934		cn[strlen(cn) - 1] = '\0';	/* chop trailing \n */
935	} else {
936		es_error(gettext("You must specify a Certificate DN prefix "
937		    "of no more than %d characters"), CN_MAX_LENGTH);
938		return (EXIT_INVALID_ARG);
939	}
940
941	/* Update DN string */
942	dn_len = strlen(cn) + strlen(dnfmt);
943	dn = malloc(dn_len + 1);
944	(void) snprintf(dn, dn_len, dnfmt, cn);
945
946	cryptodebug("Generating Certificate request for DN: %s", dn);
947	kmfret = create_csr(dn);
948	free(dn);
949	if (kmfret == KMF_OK)
950		return (EXIT_OKAY);
951	else
952		return (EXIT_CSR_FAILED);
953}
954
955static void
956str_print(char *s)
957{
958	if (s == NULL)
959		return;
960	(void) fprintf(stdout, "%s\n", s);
961}
962
963/*ARGSUSED*/
964static ret_t
965do_list(char *object)
966{
967	ret_t	retval;
968
969	if (cmd_info.elfcnt > 0) {
970		ELFsign_status_t	elfstat;
971		struct filesignatures	*fssp = NULL;
972		size_t fs_len;
973		struct ELFsign_sig_info	*esip;
974
975		if ((retval = getelfobj(cmd_info.elfobj[0])) != EXIT_OKAY)
976			return (retval);
977		elfstat = elfsign_signatures(cmd_info.ess,
978		    &fssp, &fs_len, ES_GET);
979		if (elfstat == ELFSIGN_SUCCESS) {
980			retval = EXIT_OKAY;
981			if (elfsign_sig_info(fssp, &esip)) {
982				switch (cmd_info.field) {
983				case FLD_FORMAT:
984					str_print(esip->esi_format);
985					break;
986				case FLD_SIGNER:
987					str_print(esip->esi_signer);
988					break;
989				case FLD_TIME:
990					if (esip->esi_time == 0)
991						retval = EXIT_INVALID_ARG;
992					else
993						str_print(time_str(
994						    esip->esi_time));
995					break;
996				default:
997					retval = EXIT_INVALID_ARG;
998				}
999				elfsign_sig_info_free(esip);
1000			}
1001			free(fssp);
1002		} else
1003			retval = EXIT_VERIFY_FAILED_UNSIGNED;
1004		elfsign_end(cmd_info.ess);
1005	} else {
1006		ELFCert_t	cert;
1007		/*
1008		 * Initialize the ESS record here even though we are not
1009		 * actually opening any ELF files.
1010		 */
1011		if (elfsign_begin(NULL, ES_GET, &(cmd_info.ess)) !=
1012		    ELFSIGN_SUCCESS)
1013			return (EXIT_MEMORY_ERROR);
1014
1015		if (elfcertlib_getcert(cmd_info.ess, cmd_info.cert, NULL,
1016		    &cert, cmd_info.es_action)) {
1017			retval = EXIT_OKAY;
1018			switch (cmd_info.field) {
1019			case FLD_SUBJECT:
1020				str_print(elfcertlib_getdn(cert));
1021				break;
1022			case FLD_ISSUER:
1023				str_print(elfcertlib_getissuer(cert));
1024				break;
1025			default:
1026				retval = EXIT_INVALID_ARG;
1027			}
1028			elfcertlib_releasecert(cmd_info.ess, cert);
1029		} else
1030			retval = EXIT_BAD_CERT;
1031		elfsign_end(cmd_info.ess);
1032	}
1033
1034	return (retval);
1035}
1036
1037static void
1038es_error(const char *fmt, ...)
1039{
1040	char msgbuf[BUFSIZ];
1041	va_list	args;
1042
1043	va_start(args, fmt);
1044	(void) vsnprintf(msgbuf, sizeof (msgbuf), fmt, args);
1045	va_end(args);
1046	(void) fflush(stdout);
1047	cryptoerror(LOG_STDERR, "%s", msgbuf);
1048	(void) fflush(stderr);
1049}
1050
1051static char *
1052time_str(time_t t)
1053{
1054	static char	buf[80];
1055	char		*bufp;
1056
1057	bufp = buf;
1058	if (strftime(buf, sizeof (buf), NULL, localtime(&t)) == 0)
1059		bufp = ctime(&t);
1060	return (bufp);
1061}
1062
1063static void
1064sig_info_print(struct ELFsign_sig_info *esip)
1065{
1066	if (esip == NULL)
1067		return;
1068	(void) fprintf(stdout, gettext("format: %s.\n"), esip->esi_format);
1069	(void) fprintf(stdout, gettext("signer: %s.\n"), esip->esi_signer);
1070	if (esip->esi_time == 0)
1071		return;
1072	(void) fprintf(stdout, gettext("signed on: %s.\n"),
1073	    time_str(esip->esi_time));
1074}
1075