xref: /titanic_50/usr/src/cmd/lofiadm/main.c (revision 7d82f0f819f2fde1c321b8ac4ff15e494c5eb4b1)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51d8ed0f0Svikram  * Common Development and Distribution License (the "License").
61d8ed0f0Svikram  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2293239addSjohnlev  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
231d8ed0f0Svikram  * Use is subject to license terms.
243d7072f8Seschrock  */
253d7072f8Seschrock 
263d7072f8Seschrock /*
277c478bd9Sstevel@tonic-gate  * lofiadm - administer lofi(7d). Very simple, add and remove file<->device
287c478bd9Sstevel@tonic-gate  * associations, and display status. All the ioctls are private between
297c478bd9Sstevel@tonic-gate  * lofi and lofiadm, and so are very simple - device information is
307c478bd9Sstevel@tonic-gate  * communicated via a minor number.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/param.h>
357c478bd9Sstevel@tonic-gate #include <sys/lofi.h>
367c478bd9Sstevel@tonic-gate #include <sys/stat.h>
3787117650Saalok #include <netinet/in.h>
387c478bd9Sstevel@tonic-gate #include <stdio.h>
397c478bd9Sstevel@tonic-gate #include <fcntl.h>
407c478bd9Sstevel@tonic-gate #include <locale.h>
417c478bd9Sstevel@tonic-gate #include <string.h>
4287117650Saalok #include <strings.h>
437c478bd9Sstevel@tonic-gate #include <errno.h>
447c478bd9Sstevel@tonic-gate #include <stdlib.h>
457c478bd9Sstevel@tonic-gate #include <unistd.h>
467c478bd9Sstevel@tonic-gate #include <stropts.h>
471d8ed0f0Svikram #include <libdevinfo.h>
4887117650Saalok #include <libgen.h>
4987117650Saalok #include <ctype.h>
50579df0adSaalok #include <dlfcn.h>
51*7d82f0f8SDina K Nimeh #include <limits.h>
52*7d82f0f8SDina K Nimeh #include <security/cryptoki.h>
53*7d82f0f8SDina K Nimeh #include <cryptoutil.h>
54*7d82f0f8SDina K Nimeh #include <sys/crypto/ioctl.h>
55*7d82f0f8SDina K Nimeh #include <sys/crypto/ioctladmin.h>
567c478bd9Sstevel@tonic-gate #include "utils.h"
577c478bd9Sstevel@tonic-gate 
58*7d82f0f8SDina K Nimeh /* Only need the IV len #defines out of these files, nothing else. */
59*7d82f0f8SDina K Nimeh #include <aes/aes_impl.h>
60*7d82f0f8SDina K Nimeh #include <des/des_impl.h>
61*7d82f0f8SDina K Nimeh #include <blowfish/blowfish_impl.h>
62*7d82f0f8SDina K Nimeh 
637c478bd9Sstevel@tonic-gate static const char USAGE[] =
64*7d82f0f8SDina K Nimeh 	"Usage: %s -a file [ device ] "
65*7d82f0f8SDina K Nimeh 	" [-c aes-128-cbc|aes-192-cbc|aes-256-cbc|des3-cbc|blowfish-cbc]"
66*7d82f0f8SDina K Nimeh 	" [-e] [-k keyfile] [-T [token]:[manuf]:[serial]:key]\n"
677c478bd9Sstevel@tonic-gate 	"       %s -d file | device\n"
6887117650Saalok 	"       %s -C [algorithm] [-s segment_size] file\n"
6987117650Saalok 	"       %s -U file\n"
70f9153c6bSDina K Nimeh 	"       %s [ file | device ]\n";
717c478bd9Sstevel@tonic-gate 
72*7d82f0f8SDina K Nimeh typedef struct token_spec {
73*7d82f0f8SDina K Nimeh 	char	*name;
74*7d82f0f8SDina K Nimeh 	char	*mfr;
75*7d82f0f8SDina K Nimeh 	char	*serno;
76*7d82f0f8SDina K Nimeh 	char	*key;
77*7d82f0f8SDina K Nimeh } token_spec_t;
78*7d82f0f8SDina K Nimeh 
79*7d82f0f8SDina K Nimeh typedef struct mech_alias {
80*7d82f0f8SDina K Nimeh 	char	*alias;
81*7d82f0f8SDina K Nimeh 	CK_MECHANISM_TYPE type;
82*7d82f0f8SDina K Nimeh 	char	*name;		/* for ioctl */
83*7d82f0f8SDina K Nimeh 	char	*iv_name;	/* for ioctl */
84*7d82f0f8SDina K Nimeh 	size_t	iv_len;		/* for ioctl */
85*7d82f0f8SDina K Nimeh 	iv_method_t iv_type;	/* for ioctl */
86*7d82f0f8SDina K Nimeh 	size_t	min_keysize;	/* in bytes */
87*7d82f0f8SDina K Nimeh 	size_t	max_keysize;	/* in bytes */
88*7d82f0f8SDina K Nimeh 	token_spec_t *token;
89*7d82f0f8SDina K Nimeh 	CK_SLOT_ID slot;
90*7d82f0f8SDina K Nimeh } mech_alias_t;
91*7d82f0f8SDina K Nimeh 
92*7d82f0f8SDina K Nimeh static mech_alias_t mech_aliases[] = {
93*7d82f0f8SDina K Nimeh 	/* Preferred one should always be listed first. */
94*7d82f0f8SDina K Nimeh 	{ "aes-256-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
95*7d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
96*7d82f0f8SDina K Nimeh 	{ "aes-192-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
97*7d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
98*7d82f0f8SDina K Nimeh 	{ "aes-128-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
99*7d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
100*7d82f0f8SDina K Nimeh 	{ "des3-cbc", CKM_DES3_CBC, "CKM_DES3_CBC", "CKM_DES3_ECB", DES_IV_LEN,
101*7d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID)-1 },
102*7d82f0f8SDina K Nimeh 	{ "blowfish-cbc", CKM_BLOWFISH_CBC, "CKM_BLOWFISH_CBC",
103*7d82f0f8SDina K Nimeh 	    "CKM_BLOWFISH_ECB", BLOWFISH_IV_LEN, IVM_ENC_BLKNO, ULONG_MAX,
104*7d82f0f8SDina K Nimeh 	    0L, NULL, (CK_SLOT_ID)-1 }
105*7d82f0f8SDina K Nimeh 	/*
106*7d82f0f8SDina K Nimeh 	 * A cipher without an iv requirement would look like this:
107*7d82f0f8SDina K Nimeh 	 * { "aes-xex", CKM_AES_XEX, "CKM_AES_XEX", NULL, 0,
108*7d82f0f8SDina K Nimeh 	 *    IVM_NONE, ULONG_MAX, 0L, NULL, (CK_SLOT_ID)-1 }
109*7d82f0f8SDina K Nimeh 	 */
110*7d82f0f8SDina K Nimeh };
111*7d82f0f8SDina K Nimeh 
112*7d82f0f8SDina K Nimeh int	mech_aliases_count = (sizeof (mech_aliases) / sizeof (mech_alias_t));
113*7d82f0f8SDina K Nimeh 
114*7d82f0f8SDina K Nimeh /* Preferred cipher, if one isn't specified on command line. */
115*7d82f0f8SDina K Nimeh #define	DEFAULT_CIPHER	(&mech_aliases[0])
116*7d82f0f8SDina K Nimeh 
117*7d82f0f8SDina K Nimeh #define	DEFAULT_CIPHER_NUM	64	/* guess # kernel ciphers available */
118*7d82f0f8SDina K Nimeh #define	DEFAULT_MECHINFO_NUM	16	/* guess # kernel mechs available */
119*7d82f0f8SDina K Nimeh #define	MIN_PASSLEN		8	/* min acceptable passphrase size */
1207c478bd9Sstevel@tonic-gate 
12187117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
12287117650Saalok 	size_t *destlen, int level);
12387117650Saalok 
12487117650Saalok lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
12587117650Saalok 	{NULL,  gzip_compress,  6,	"gzip"}, /* default */
12687117650Saalok 	{NULL,	gzip_compress,	6,	"gzip-6"},
12787117650Saalok 	{NULL,	gzip_compress,	9, 	"gzip-9"}
12887117650Saalok };
12987117650Saalok 
130*7d82f0f8SDina K Nimeh /* For displaying lofi mappings */
13187117650Saalok #define	FORMAT 			"%-20s     %-30s	%s\n"
132*7d82f0f8SDina K Nimeh 
13387117650Saalok #define	COMPRESS_ALGORITHM	"gzip"
13487117650Saalok #define	COMPRESS_THRESHOLD	2048
13587117650Saalok #define	SEGSIZE			131072
13687117650Saalok #define	BLOCK_SIZE		512
13787117650Saalok #define	KILOBYTE		1024
13887117650Saalok #define	MEGABYTE		(KILOBYTE * KILOBYTE)
13987117650Saalok #define	GIGABYTE		(KILOBYTE * MEGABYTE)
140579df0adSaalok #define	LIBZ			"libz.so"
141579df0adSaalok 
142579df0adSaalok static int (*compress2p)(void *, ulong_t *, void *, size_t, int) = NULL;
14387117650Saalok 
14487117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
14587117650Saalok 	size_t *dstlen, int level)
14687117650Saalok {
147579df0adSaalok 	void *libz_hdl = NULL;
14887117650Saalok 
149579df0adSaalok 	/*
150579df0adSaalok 	 * The first time we are called, attempt to dlopen()
151579df0adSaalok 	 * libz.so and get a pointer to the compress2() function
152579df0adSaalok 	 */
153579df0adSaalok 	if (compress2p == NULL) {
154579df0adSaalok 		if ((libz_hdl = openlib(LIBZ)) == NULL)
155579df0adSaalok 			die(gettext("could not find %s. "
156579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
157579df0adSaalok 
158579df0adSaalok 		if ((compress2p =
159579df0adSaalok 		    (int (*)(void *, ulong_t *, void *, size_t, int))
160579df0adSaalok 		    dlsym(libz_hdl, "compress2")) == NULL) {
161579df0adSaalok 			closelib();
162579df0adSaalok 			die(gettext("could not find the correct %s. "
163579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
164579df0adSaalok 		}
165579df0adSaalok 	}
166579df0adSaalok 
167579df0adSaalok 	if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0)
168579df0adSaalok 		return (-1);
16987117650Saalok 	return (0);
17087117650Saalok }
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate static void
173*7d82f0f8SDina K Nimeh usage(const char *pname)
1747c478bd9Sstevel@tonic-gate {
175*7d82f0f8SDina K Nimeh 	(void) fprintf(stderr, gettext(USAGE), pname, pname, pname,
176*7d82f0f8SDina K Nimeh 	    pname, pname);
1777c478bd9Sstevel@tonic-gate 	exit(E_USAGE);
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate /*
1817c478bd9Sstevel@tonic-gate  * Translate a lofi device name to a minor number. We might be asked
1827c478bd9Sstevel@tonic-gate  * to do this when there is no association (such as when the user specifies
1837c478bd9Sstevel@tonic-gate  * a particular device), so we can only look at the string.
1847c478bd9Sstevel@tonic-gate  */
1857c478bd9Sstevel@tonic-gate static int
1867c478bd9Sstevel@tonic-gate name_to_minor(const char *devicename)
1877c478bd9Sstevel@tonic-gate {
1887c478bd9Sstevel@tonic-gate 	int	minor;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
1917c478bd9Sstevel@tonic-gate 		return (minor);
1927c478bd9Sstevel@tonic-gate 	}
1937c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
1947c478bd9Sstevel@tonic-gate 		return (minor);
1957c478bd9Sstevel@tonic-gate 	}
1967c478bd9Sstevel@tonic-gate 	return (0);
1977c478bd9Sstevel@tonic-gate }
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate /*
2007c478bd9Sstevel@tonic-gate  * This might be the first time we've used this minor number. If so,
2017c478bd9Sstevel@tonic-gate  * it might also be that the /dev links are in the process of being created
2027c478bd9Sstevel@tonic-gate  * by devfsadmd (or that they'll be created "soon"). We cannot return
2037c478bd9Sstevel@tonic-gate  * until they're there or the invoker of lofiadm might try to use them
2047c478bd9Sstevel@tonic-gate  * and not find them. This can happen if a shell script is running on
2057c478bd9Sstevel@tonic-gate  * an MP.
2067c478bd9Sstevel@tonic-gate  */
2077c478bd9Sstevel@tonic-gate static int sleeptime = 2;	/* number of seconds to sleep between stat's */
2087c478bd9Sstevel@tonic-gate static int maxsleep = 120;	/* maximum number of seconds to sleep */
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate static void
2117c478bd9Sstevel@tonic-gate wait_until_dev_complete(int minor)
2127c478bd9Sstevel@tonic-gate {
2137c478bd9Sstevel@tonic-gate 	struct stat64 buf;
2147c478bd9Sstevel@tonic-gate 	int	cursleep;
21587117650Saalok 	char	blkpath[MAXPATHLEN];
21687117650Saalok 	char	charpath[MAXPATHLEN];
2171d8ed0f0Svikram 	di_devlink_handle_t hdl;
2181d8ed0f0Svikram 
2197c478bd9Sstevel@tonic-gate 	(void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
2207c478bd9Sstevel@tonic-gate 	    LOFI_BLOCK_NAME, minor);
2217c478bd9Sstevel@tonic-gate 	(void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
2227c478bd9Sstevel@tonic-gate 	    LOFI_CHAR_NAME, minor);
2231d8ed0f0Svikram 
2241d8ed0f0Svikram 	/* Check if links already present */
2251d8ed0f0Svikram 	if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
2261d8ed0f0Svikram 		return;
2271d8ed0f0Svikram 
2281d8ed0f0Svikram 	/* First use di_devlink_init() */
2291d8ed0f0Svikram 	if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
2301d8ed0f0Svikram 		(void) di_devlink_fini(&hdl);
2311d8ed0f0Svikram 		goto out;
2321d8ed0f0Svikram 	}
2331d8ed0f0Svikram 
2341d8ed0f0Svikram 	/*
2351d8ed0f0Svikram 	 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
2361d8ed0f0Svikram 	 * only fail if the caller is non-root. In that case, wait for
2371d8ed0f0Svikram 	 * link creation via sysevents.
2381d8ed0f0Svikram 	 */
239*7d82f0f8SDina K Nimeh 	for (cursleep = 0; cursleep < maxsleep; cursleep += sleeptime) {
240*7d82f0f8SDina K Nimeh 		if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
2417c478bd9Sstevel@tonic-gate 			return;
242*7d82f0f8SDina K Nimeh 		(void) sleep(sleeptime);
2437c478bd9Sstevel@tonic-gate 	}
2441d8ed0f0Svikram 
2457c478bd9Sstevel@tonic-gate 	/* one last try */
2461d8ed0f0Svikram out:
2477c478bd9Sstevel@tonic-gate 	if (stat64(blkpath, &buf) == -1) {
2487c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), blkpath);
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 	if (stat64(charpath, &buf) == -1) {
2517c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), charpath);
2527c478bd9Sstevel@tonic-gate 	}
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate /*
256*7d82f0f8SDina K Nimeh  * Map the file and return the minor number the driver picked for the file
257*7d82f0f8SDina K Nimeh  * DO NOT use this function if the filename is actually the device name.
258*7d82f0f8SDina K Nimeh  */
259*7d82f0f8SDina K Nimeh static int
260*7d82f0f8SDina K Nimeh lofi_map_file(int lfd, struct lofi_ioctl li, const char *filename)
261*7d82f0f8SDina K Nimeh {
262*7d82f0f8SDina K Nimeh 	int	minor;
263*7d82f0f8SDina K Nimeh 
264*7d82f0f8SDina K Nimeh 	li.li_minor = 0;
265*7d82f0f8SDina K Nimeh 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
266*7d82f0f8SDina K Nimeh 	minor = ioctl(lfd, LOFI_MAP_FILE, &li);
267*7d82f0f8SDina K Nimeh 	if (minor == -1) {
268*7d82f0f8SDina K Nimeh 		if (errno == ENOTSUP)
269*7d82f0f8SDina K Nimeh 			warn(gettext("encrypting compressed files is "
270*7d82f0f8SDina K Nimeh 			    "unsupported"));
271*7d82f0f8SDina K Nimeh 		die(gettext("could not map file %s"), filename);
272*7d82f0f8SDina K Nimeh 	}
273*7d82f0f8SDina K Nimeh 	wait_until_dev_complete(minor);
274*7d82f0f8SDina K Nimeh 	return (minor);
275*7d82f0f8SDina K Nimeh }
276*7d82f0f8SDina K Nimeh 
277*7d82f0f8SDina K Nimeh /*
2787c478bd9Sstevel@tonic-gate  * Add a device association. If devicename is NULL, let the driver
2797c478bd9Sstevel@tonic-gate  * pick a device.
2807c478bd9Sstevel@tonic-gate  */
2817c478bd9Sstevel@tonic-gate static void
28287117650Saalok add_mapping(int lfd, const char *devicename, const char *filename,
283*7d82f0f8SDina K Nimeh     mech_alias_t *cipher, const char *rkey, size_t rksz)
2847c478bd9Sstevel@tonic-gate {
2857c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
286*7d82f0f8SDina K Nimeh 
287*7d82f0f8SDina K Nimeh 	li.li_crypto_enabled = B_FALSE;
288*7d82f0f8SDina K Nimeh 	if (cipher != NULL) {
289*7d82f0f8SDina K Nimeh 		/* set up encryption for mapped file */
290*7d82f0f8SDina K Nimeh 		li.li_crypto_enabled = B_TRUE;
291*7d82f0f8SDina K Nimeh 		(void) strlcpy(li.li_cipher, cipher->name,
292*7d82f0f8SDina K Nimeh 		    sizeof (li.li_cipher));
293*7d82f0f8SDina K Nimeh 		if (rksz > sizeof (li.li_key)) {
294*7d82f0f8SDina K Nimeh 			die(gettext("key too large"));
295*7d82f0f8SDina K Nimeh 		}
296*7d82f0f8SDina K Nimeh 		bcopy(rkey, li.li_key, rksz);
297*7d82f0f8SDina K Nimeh 		li.li_key_len = rksz << 3;	/* convert to bits */
298*7d82f0f8SDina K Nimeh 
299*7d82f0f8SDina K Nimeh 		li.li_iv_type = cipher->iv_type;
300*7d82f0f8SDina K Nimeh 		li.li_iv_len = cipher->iv_len;	/* 0 when no iv needed */
301*7d82f0f8SDina K Nimeh 		switch (cipher->iv_type) {
302*7d82f0f8SDina K Nimeh 		case IVM_ENC_BLKNO:
303*7d82f0f8SDina K Nimeh 			(void) strlcpy(li.li_iv_cipher, cipher->iv_name,
304*7d82f0f8SDina K Nimeh 			    sizeof (li.li_iv_cipher));
305*7d82f0f8SDina K Nimeh 			break;
306*7d82f0f8SDina K Nimeh 		case IVM_NONE:
307*7d82f0f8SDina K Nimeh 			/* FALLTHROUGH */
308*7d82f0f8SDina K Nimeh 		default:
309*7d82f0f8SDina K Nimeh 			break;
310*7d82f0f8SDina K Nimeh 		}
311*7d82f0f8SDina K Nimeh 	}
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
314*7d82f0f8SDina K Nimeh 		int	minor;
31587117650Saalok 
316*7d82f0f8SDina K Nimeh 		/* pick one via the driver */
317*7d82f0f8SDina K Nimeh 		minor = lofi_map_file(lfd, li, filename);
318*7d82f0f8SDina K Nimeh 		/* if mapping succeeds, print the one picked */
319*7d82f0f8SDina K Nimeh 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor);
3207c478bd9Sstevel@tonic-gate 		return;
3217c478bd9Sstevel@tonic-gate 	}
322*7d82f0f8SDina K Nimeh 
3237c478bd9Sstevel@tonic-gate 	/* use device we were given */
324*7d82f0f8SDina K Nimeh 	li.li_minor = name_to_minor(devicename);
325*7d82f0f8SDina K Nimeh 	if (li.li_minor == 0) {
3267c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
3277c478bd9Sstevel@tonic-gate 	}
32887117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
329*7d82f0f8SDina K Nimeh 
330*7d82f0f8SDina K Nimeh 	/* if device is already in use li.li_minor won't change */
3317c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
332*7d82f0f8SDina K Nimeh 		if (errno == ENOTSUP)
333*7d82f0f8SDina K Nimeh 			warn(gettext("encrypting compressed files is "
334*7d82f0f8SDina K Nimeh 			    "unsupported"));
3357c478bd9Sstevel@tonic-gate 		die(gettext("could not map file %s to %s"), filename,
3367c478bd9Sstevel@tonic-gate 		    devicename);
3377c478bd9Sstevel@tonic-gate 	}
338*7d82f0f8SDina K Nimeh 	wait_until_dev_complete(li.li_minor);
3397c478bd9Sstevel@tonic-gate }
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate /*
3427c478bd9Sstevel@tonic-gate  * Remove an association. Delete by device name if non-NULL, or by
3437c478bd9Sstevel@tonic-gate  * filename otherwise.
3447c478bd9Sstevel@tonic-gate  */
3457c478bd9Sstevel@tonic-gate static void
3463d7072f8Seschrock delete_mapping(int lfd, const char *devicename, const char *filename,
3473d7072f8Seschrock     boolean_t force)
3487c478bd9Sstevel@tonic-gate {
3497c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
3507c478bd9Sstevel@tonic-gate 
3513d7072f8Seschrock 	li.li_force = force;
35293239addSjohnlev 	li.li_cleanup = B_FALSE;
35393239addSjohnlev 
3547c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
3557c478bd9Sstevel@tonic-gate 		/* delete by filename */
35687117650Saalok 		(void) strlcpy(li.li_filename, filename,
35787117650Saalok 		    sizeof (li.li_filename));
3587c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
3597c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
3607c478bd9Sstevel@tonic-gate 			die(gettext("could not unmap file %s"), filename);
3617c478bd9Sstevel@tonic-gate 		}
3627c478bd9Sstevel@tonic-gate 		return;
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 
365f9153c6bSDina K Nimeh 	/* delete by device */
3667c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
3677c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
3687c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
3697c478bd9Sstevel@tonic-gate 	}
3707c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
3717c478bd9Sstevel@tonic-gate 		die(gettext("could not unmap device %s"), devicename);
3727c478bd9Sstevel@tonic-gate 	}
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate 
375*7d82f0f8SDina K Nimeh /*
376*7d82f0f8SDina K Nimeh  * Show filename given devicename, or devicename given filename.
377*7d82f0f8SDina K Nimeh  */
3787c478bd9Sstevel@tonic-gate static void
3797c478bd9Sstevel@tonic-gate print_one_mapping(int lfd, const char *devicename, const char *filename)
3807c478bd9Sstevel@tonic-gate {
3817c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
3847c478bd9Sstevel@tonic-gate 		/* given filename, print devicename */
3857c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
38687117650Saalok 		(void) strlcpy(li.li_filename, filename,
38787117650Saalok 		    sizeof (li.li_filename));
3887c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
3897c478bd9Sstevel@tonic-gate 			die(gettext("could not find device for %s"), filename);
3907c478bd9Sstevel@tonic-gate 		}
3917c478bd9Sstevel@tonic-gate 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
3927c478bd9Sstevel@tonic-gate 		return;
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	/* given devicename, print filename */
3967c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
3977c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
3987c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
3997c478bd9Sstevel@tonic-gate 	}
4007c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
4017c478bd9Sstevel@tonic-gate 		die(gettext("could not find filename for %s"), devicename);
4027c478bd9Sstevel@tonic-gate 	}
4037c478bd9Sstevel@tonic-gate 	(void) printf("%s\n", li.li_filename);
4047c478bd9Sstevel@tonic-gate }
4057c478bd9Sstevel@tonic-gate 
40687117650Saalok /*
407*7d82f0f8SDina K Nimeh  * Print the list of all the mappings, including a header.
408*7d82f0f8SDina K Nimeh  */
409*7d82f0f8SDina K Nimeh static void
410*7d82f0f8SDina K Nimeh print_mappings(int fd)
411*7d82f0f8SDina K Nimeh {
412*7d82f0f8SDina K Nimeh 	struct lofi_ioctl li;
413*7d82f0f8SDina K Nimeh 	int	minor;
414*7d82f0f8SDina K Nimeh 	int	maxminor;
415*7d82f0f8SDina K Nimeh 	char	path[MAXPATHLEN];
416*7d82f0f8SDina K Nimeh 	char	options[MAXPATHLEN];
417*7d82f0f8SDina K Nimeh 
418*7d82f0f8SDina K Nimeh 	li.li_minor = 0;
419*7d82f0f8SDina K Nimeh 	if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
420*7d82f0f8SDina K Nimeh 		die("ioctl");
421*7d82f0f8SDina K Nimeh 	}
422*7d82f0f8SDina K Nimeh 	maxminor = li.li_minor;
423*7d82f0f8SDina K Nimeh 
424*7d82f0f8SDina K Nimeh 	(void) printf(FORMAT, gettext("Block Device"), gettext("File"),
425*7d82f0f8SDina K Nimeh 	    gettext("Options"));
426*7d82f0f8SDina K Nimeh 	for (minor = 1; minor <= maxminor; minor++) {
427*7d82f0f8SDina K Nimeh 		li.li_minor = minor;
428*7d82f0f8SDina K Nimeh 		if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
429*7d82f0f8SDina K Nimeh 			if (errno == ENXIO)
430*7d82f0f8SDina K Nimeh 				continue;
431*7d82f0f8SDina K Nimeh 			warn("ioctl");
432*7d82f0f8SDina K Nimeh 			break;
433*7d82f0f8SDina K Nimeh 		}
434*7d82f0f8SDina K Nimeh 		(void) snprintf(path, sizeof (path), "/dev/%s/%d",
435*7d82f0f8SDina K Nimeh 		    LOFI_BLOCK_NAME, minor);
436*7d82f0f8SDina K Nimeh 		/*
437*7d82f0f8SDina K Nimeh 		 * Encrypted lofi and compressed lofi are mutually exclusive.
438*7d82f0f8SDina K Nimeh 		 */
439*7d82f0f8SDina K Nimeh 		if (li.li_crypto_enabled)
440*7d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options),
441*7d82f0f8SDina K Nimeh 			    gettext("Encrypted"));
442*7d82f0f8SDina K Nimeh 		else if (li.li_algorithm[0] != '\0')
443*7d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options),
444*7d82f0f8SDina K Nimeh 			    gettext("Compressed(%s)"), li.li_algorithm);
445*7d82f0f8SDina K Nimeh 		else
446*7d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options), "-");
447*7d82f0f8SDina K Nimeh 
448*7d82f0f8SDina K Nimeh 		(void) printf(FORMAT, path, li.li_filename, options);
449*7d82f0f8SDina K Nimeh 	}
450*7d82f0f8SDina K Nimeh }
451*7d82f0f8SDina K Nimeh 
452*7d82f0f8SDina K Nimeh /*
453*7d82f0f8SDina K Nimeh  * Verify the cipher selected by user.
454*7d82f0f8SDina K Nimeh  */
455*7d82f0f8SDina K Nimeh static mech_alias_t *
456*7d82f0f8SDina K Nimeh ciph2mech(const char *alias)
457*7d82f0f8SDina K Nimeh {
458*7d82f0f8SDina K Nimeh 	int	i;
459*7d82f0f8SDina K Nimeh 
460*7d82f0f8SDina K Nimeh 	for (i = 0; i < mech_aliases_count; i++) {
461*7d82f0f8SDina K Nimeh 		if (strcasecmp(alias, mech_aliases[i].alias) == 0)
462*7d82f0f8SDina K Nimeh 			return (&mech_aliases[i]);
463*7d82f0f8SDina K Nimeh 	}
464*7d82f0f8SDina K Nimeh 	return (NULL);
465*7d82f0f8SDina K Nimeh }
466*7d82f0f8SDina K Nimeh 
467*7d82f0f8SDina K Nimeh /*
468*7d82f0f8SDina K Nimeh  * Verify user selected cipher is also available in kernel.
469*7d82f0f8SDina K Nimeh  *
470*7d82f0f8SDina K Nimeh  * While traversing kernel list of mechs, if the cipher is supported in the
471*7d82f0f8SDina K Nimeh  * kernel for both encryption and decryption, it also picks up the min/max
472*7d82f0f8SDina K Nimeh  * key size.
473*7d82f0f8SDina K Nimeh  */
474*7d82f0f8SDina K Nimeh static boolean_t
475*7d82f0f8SDina K Nimeh kernel_cipher_check(mech_alias_t *cipher)
476*7d82f0f8SDina K Nimeh {
477*7d82f0f8SDina K Nimeh 	boolean_t ciph_ok = B_FALSE;
478*7d82f0f8SDina K Nimeh 	boolean_t iv_ok = B_FALSE;
479*7d82f0f8SDina K Nimeh 	int	i;
480*7d82f0f8SDina K Nimeh 	int	count;
481*7d82f0f8SDina K Nimeh 	crypto_get_mechanism_list_t *kciphers = NULL;
482*7d82f0f8SDina K Nimeh 	crypto_get_all_mechanism_info_t *kinfo = NULL;
483*7d82f0f8SDina K Nimeh 	int	fd = -1;
484*7d82f0f8SDina K Nimeh 	size_t	keymin;
485*7d82f0f8SDina K Nimeh 	size_t	keymax;
486*7d82f0f8SDina K Nimeh 
487*7d82f0f8SDina K Nimeh 	/* if cipher doesn't need iv generating mech, bypass that check now */
488*7d82f0f8SDina K Nimeh 	if (cipher->iv_name == NULL)
489*7d82f0f8SDina K Nimeh 		iv_ok = B_TRUE;
490*7d82f0f8SDina K Nimeh 
491*7d82f0f8SDina K Nimeh 	/* allocate some space for the list of kernel ciphers */
492*7d82f0f8SDina K Nimeh 	count = DEFAULT_CIPHER_NUM;
493*7d82f0f8SDina K Nimeh 	kciphers = malloc(sizeof (crypto_get_mechanism_list_t) +
494*7d82f0f8SDina K Nimeh 	    sizeof (crypto_mech_name_t) * (count - 1));
495*7d82f0f8SDina K Nimeh 	if (kciphers == NULL)
496*7d82f0f8SDina K Nimeh 		die(gettext("failed to allocate memory for list of "
497*7d82f0f8SDina K Nimeh 		    "kernel mechanisms"));
498*7d82f0f8SDina K Nimeh 	kciphers->ml_count = count;
499*7d82f0f8SDina K Nimeh 
500*7d82f0f8SDina K Nimeh 	/* query crypto device to get list of kernel ciphers */
501*7d82f0f8SDina K Nimeh 	if ((fd = open("/dev/crypto", O_RDWR)) == -1) {
502*7d82f0f8SDina K Nimeh 		warn(gettext("failed to open %s"), "/dev/crypto");
503*7d82f0f8SDina K Nimeh 		goto kcc_out;
504*7d82f0f8SDina K Nimeh 	}
505*7d82f0f8SDina K Nimeh 
506*7d82f0f8SDina K Nimeh 	if (ioctl(fd, CRYPTO_GET_MECHANISM_LIST, kciphers) == -1) {
507*7d82f0f8SDina K Nimeh 		warn(gettext("CRYPTO_GET_MECHANISM_LIST ioctl failed"));
508*7d82f0f8SDina K Nimeh 		goto kcc_out;
509*7d82f0f8SDina K Nimeh 	}
510*7d82f0f8SDina K Nimeh 
511*7d82f0f8SDina K Nimeh 	if (kciphers->ml_return_value == CRYPTO_BUFFER_TOO_SMALL) {
512*7d82f0f8SDina K Nimeh 		count = kciphers->ml_count;
513*7d82f0f8SDina K Nimeh 		free(kciphers);
514*7d82f0f8SDina K Nimeh 		kciphers = malloc(sizeof (crypto_get_mechanism_list_t) +
515*7d82f0f8SDina K Nimeh 		    sizeof (crypto_mech_name_t) * (count - 1));
516*7d82f0f8SDina K Nimeh 		if (kciphers == NULL) {
517*7d82f0f8SDina K Nimeh 			warn(gettext("failed to allocate memory for list of "
518*7d82f0f8SDina K Nimeh 			    "kernel mechanisms"));
519*7d82f0f8SDina K Nimeh 			goto kcc_out;
520*7d82f0f8SDina K Nimeh 		}
521*7d82f0f8SDina K Nimeh 		kciphers->ml_count = count;
522*7d82f0f8SDina K Nimeh 
523*7d82f0f8SDina K Nimeh 		if (ioctl(fd, CRYPTO_GET_MECHANISM_LIST, kciphers) == -1) {
524*7d82f0f8SDina K Nimeh 			warn(gettext("CRYPTO_GET_MECHANISM_LIST ioctl failed"));
525*7d82f0f8SDina K Nimeh 			goto kcc_out;
526*7d82f0f8SDina K Nimeh 		}
527*7d82f0f8SDina K Nimeh 	}
528*7d82f0f8SDina K Nimeh 
529*7d82f0f8SDina K Nimeh 	if (kciphers->ml_return_value != CRYPTO_SUCCESS) {
530*7d82f0f8SDina K Nimeh 		warn(gettext(
531*7d82f0f8SDina K Nimeh 		    "CRYPTO_GET_MECHANISM_LIST ioctl return value = %d\n"),
532*7d82f0f8SDina K Nimeh 		    kciphers->ml_return_value);
533*7d82f0f8SDina K Nimeh 		goto kcc_out;
534*7d82f0f8SDina K Nimeh 	}
535*7d82f0f8SDina K Nimeh 
536*7d82f0f8SDina K Nimeh 	/*
537*7d82f0f8SDina K Nimeh 	 * scan list of kernel ciphers looking for the selected one and if
538*7d82f0f8SDina K Nimeh 	 * it needs an iv generated using another cipher, also look for that
539*7d82f0f8SDina K Nimeh 	 * additional cipher to be used for generating the iv
540*7d82f0f8SDina K Nimeh 	 */
541*7d82f0f8SDina K Nimeh 	count = kciphers->ml_count;
542*7d82f0f8SDina K Nimeh 	for (i = 0; i < count && !(ciph_ok && iv_ok); i++) {
543*7d82f0f8SDina K Nimeh 		if (!ciph_ok &&
544*7d82f0f8SDina K Nimeh 		    strcasecmp(cipher->name, kciphers->ml_list[i]) == 0)
545*7d82f0f8SDina K Nimeh 			ciph_ok = B_TRUE;
546*7d82f0f8SDina K Nimeh 		if (!iv_ok &&
547*7d82f0f8SDina K Nimeh 		    strcasecmp(cipher->iv_name, kciphers->ml_list[i]) == 0)
548*7d82f0f8SDina K Nimeh 			iv_ok = B_TRUE;
549*7d82f0f8SDina K Nimeh 	}
550*7d82f0f8SDina K Nimeh 	free(kciphers);
551*7d82f0f8SDina K Nimeh 	kciphers = NULL;
552*7d82f0f8SDina K Nimeh 
553*7d82f0f8SDina K Nimeh 	if (!ciph_ok)
554*7d82f0f8SDina K Nimeh 		warn(gettext("%s mechanism not supported in kernel\n"),
555*7d82f0f8SDina K Nimeh 		    cipher->name);
556*7d82f0f8SDina K Nimeh 	if (!iv_ok)
557*7d82f0f8SDina K Nimeh 		warn(gettext("%s mechanism not supported in kernel\n"),
558*7d82f0f8SDina K Nimeh 		    cipher->iv_name);
559*7d82f0f8SDina K Nimeh 
560*7d82f0f8SDina K Nimeh 	if (ciph_ok) {
561*7d82f0f8SDina K Nimeh 		/* Get the details about the user selected cipher */
562*7d82f0f8SDina K Nimeh 		count = DEFAULT_MECHINFO_NUM;
563*7d82f0f8SDina K Nimeh 		kinfo = malloc(sizeof (crypto_get_all_mechanism_info_t) +
564*7d82f0f8SDina K Nimeh 		    sizeof (crypto_mechanism_info_t) * (count - 1));
565*7d82f0f8SDina K Nimeh 		if (kinfo == NULL) {
566*7d82f0f8SDina K Nimeh 			warn(gettext("failed to allocate memory for "
567*7d82f0f8SDina K Nimeh 			    "kernel mechanism info"));
568*7d82f0f8SDina K Nimeh 			goto kcc_out;
569*7d82f0f8SDina K Nimeh 		}
570*7d82f0f8SDina K Nimeh 		kinfo->mi_count = count;
571*7d82f0f8SDina K Nimeh 		(void) strlcpy(kinfo->mi_mechanism_name, cipher->name,
572*7d82f0f8SDina K Nimeh 		    CRYPTO_MAX_MECH_NAME);
573*7d82f0f8SDina K Nimeh 
574*7d82f0f8SDina K Nimeh 		if (ioctl(fd, CRYPTO_GET_ALL_MECHANISM_INFO, kinfo) == -1) {
575*7d82f0f8SDina K Nimeh 			warn(gettext(
576*7d82f0f8SDina K Nimeh 			    "CRYPTO_GET_ALL_MECHANISM_INFO ioctl failed"));
577*7d82f0f8SDina K Nimeh 			goto kcc_out;
578*7d82f0f8SDina K Nimeh 		}
579*7d82f0f8SDina K Nimeh 
580*7d82f0f8SDina K Nimeh 		if (kinfo->mi_return_value == CRYPTO_BUFFER_TOO_SMALL) {
581*7d82f0f8SDina K Nimeh 			count = kinfo->mi_count;
582*7d82f0f8SDina K Nimeh 			free(kinfo);
583*7d82f0f8SDina K Nimeh 			kinfo = malloc(
584*7d82f0f8SDina K Nimeh 			    sizeof (crypto_get_all_mechanism_info_t) +
585*7d82f0f8SDina K Nimeh 			    sizeof (crypto_mechanism_info_t) * (count - 1));
586*7d82f0f8SDina K Nimeh 			if (kinfo == NULL) {
587*7d82f0f8SDina K Nimeh 				warn(gettext("failed to allocate memory for "
588*7d82f0f8SDina K Nimeh 				    "kernel mechanism info"));
589*7d82f0f8SDina K Nimeh 				goto kcc_out;
590*7d82f0f8SDina K Nimeh 			}
591*7d82f0f8SDina K Nimeh 			kinfo->mi_count = count;
592*7d82f0f8SDina K Nimeh 			(void) strlcpy(kinfo->mi_mechanism_name, cipher->name,
593*7d82f0f8SDina K Nimeh 			    CRYPTO_MAX_MECH_NAME);
594*7d82f0f8SDina K Nimeh 
595*7d82f0f8SDina K Nimeh 			if (ioctl(fd, CRYPTO_GET_ALL_MECHANISM_INFO, kinfo) ==
596*7d82f0f8SDina K Nimeh 			    -1) {
597*7d82f0f8SDina K Nimeh 				warn(gettext("CRYPTO_GET_ALL_MECHANISM_INFO "
598*7d82f0f8SDina K Nimeh 				    "ioctl failed"));
599*7d82f0f8SDina K Nimeh 				goto kcc_out;
600*7d82f0f8SDina K Nimeh 			}
601*7d82f0f8SDina K Nimeh 		}
602*7d82f0f8SDina K Nimeh 
603*7d82f0f8SDina K Nimeh 		if (kinfo->mi_return_value != CRYPTO_SUCCESS) {
604*7d82f0f8SDina K Nimeh 			warn(gettext("CRYPTO_GET_ALL_MECHANISM_INFO ioctl "
605*7d82f0f8SDina K Nimeh 			    "return value = %d\n"), kinfo->mi_return_value);
606*7d82f0f8SDina K Nimeh 			goto kcc_out;
607*7d82f0f8SDina K Nimeh 		}
608*7d82f0f8SDina K Nimeh 
609*7d82f0f8SDina K Nimeh 		/* Set key min and max size */
610*7d82f0f8SDina K Nimeh 		count = kinfo->mi_count;
611*7d82f0f8SDina K Nimeh 		i = 0;
612*7d82f0f8SDina K Nimeh 		if (i < count) {
613*7d82f0f8SDina K Nimeh 			keymin = kinfo->mi_list[i].mi_min_key_size;
614*7d82f0f8SDina K Nimeh 			keymax = kinfo->mi_list[i].mi_max_key_size;
615*7d82f0f8SDina K Nimeh 			if (kinfo->mi_list[i].mi_keysize_unit &
616*7d82f0f8SDina K Nimeh 			    CRYPTO_KEYSIZE_UNIT_IN_BITS) {
617*7d82f0f8SDina K Nimeh 				keymin = CRYPTO_BITS2BYTES(keymin);
618*7d82f0f8SDina K Nimeh 				keymax = CRYPTO_BITS2BYTES(keymax);
619*7d82f0f8SDina K Nimeh 
620*7d82f0f8SDina K Nimeh 			}
621*7d82f0f8SDina K Nimeh 			cipher->min_keysize = keymin;
622*7d82f0f8SDina K Nimeh 			cipher->max_keysize = keymax;
623*7d82f0f8SDina K Nimeh 		}
624*7d82f0f8SDina K Nimeh 		free(kinfo);
625*7d82f0f8SDina K Nimeh 		kinfo = NULL;
626*7d82f0f8SDina K Nimeh 
627*7d82f0f8SDina K Nimeh 		if (i == count) {
628*7d82f0f8SDina K Nimeh 			(void) close(fd);
629*7d82f0f8SDina K Nimeh 			die(gettext(
630*7d82f0f8SDina K Nimeh 			    "failed to find usable %s kernel mechanism, "
631*7d82f0f8SDina K Nimeh 			    "use \"cryptoadm list -m\" to find available "
632*7d82f0f8SDina K Nimeh 			    "mechanisms\n"),
633*7d82f0f8SDina K Nimeh 			    cipher->name);
634*7d82f0f8SDina K Nimeh 		}
635*7d82f0f8SDina K Nimeh 	}
636*7d82f0f8SDina K Nimeh 
637*7d82f0f8SDina K Nimeh 	/* Note: key min/max, unit size, usage for iv cipher are not checked. */
638*7d82f0f8SDina K Nimeh 
639*7d82f0f8SDina K Nimeh 	return (ciph_ok && iv_ok);
640*7d82f0f8SDina K Nimeh 
641*7d82f0f8SDina K Nimeh kcc_out:
642*7d82f0f8SDina K Nimeh 	if (kinfo != NULL)
643*7d82f0f8SDina K Nimeh 		free(kinfo);
644*7d82f0f8SDina K Nimeh 	if (kciphers != NULL)
645*7d82f0f8SDina K Nimeh 		free(kciphers);
646*7d82f0f8SDina K Nimeh 	if (fd != -1)
647*7d82f0f8SDina K Nimeh 		(void) close(fd);
648*7d82f0f8SDina K Nimeh 	return (B_FALSE);
649*7d82f0f8SDina K Nimeh }
650*7d82f0f8SDina K Nimeh 
651*7d82f0f8SDina K Nimeh /*
652*7d82f0f8SDina K Nimeh  * Break up token spec into its components (non-destructive)
653*7d82f0f8SDina K Nimeh  */
654*7d82f0f8SDina K Nimeh static token_spec_t *
655*7d82f0f8SDina K Nimeh parsetoken(char *spec)
656*7d82f0f8SDina K Nimeh {
657*7d82f0f8SDina K Nimeh #define	FLD_NAME	0
658*7d82f0f8SDina K Nimeh #define	FLD_MANUF	1
659*7d82f0f8SDina K Nimeh #define	FLD_SERIAL	2
660*7d82f0f8SDina K Nimeh #define	FLD_LABEL	3
661*7d82f0f8SDina K Nimeh #define	NFIELDS		4
662*7d82f0f8SDina K Nimeh #define	nullfield(i)	((field[(i)+1] - field[(i)]) <= 1)
663*7d82f0f8SDina K Nimeh #define	copyfield(fld, i)	\
664*7d82f0f8SDina K Nimeh 		{							\
665*7d82f0f8SDina K Nimeh 			int	n;					\
666*7d82f0f8SDina K Nimeh 			(fld) = NULL;					\
667*7d82f0f8SDina K Nimeh 			if ((n = (field[(i)+1] - field[(i)])) > 1) {	\
668*7d82f0f8SDina K Nimeh 				if (((fld) = malloc(n)) != NULL) {	\
669*7d82f0f8SDina K Nimeh 					(void) strncpy((fld), field[(i)], n); \
670*7d82f0f8SDina K Nimeh 					((fld))[n - 1] = '\0';		\
671*7d82f0f8SDina K Nimeh 				}					\
672*7d82f0f8SDina K Nimeh 			}						\
673*7d82f0f8SDina K Nimeh 		}
674*7d82f0f8SDina K Nimeh 
675*7d82f0f8SDina K Nimeh 	int	i;
676*7d82f0f8SDina K Nimeh 	char	*field[NFIELDS + 1];	/* +1 to catch extra delimiters */
677*7d82f0f8SDina K Nimeh 	token_spec_t *ti = NULL;
678*7d82f0f8SDina K Nimeh 
679*7d82f0f8SDina K Nimeh 	if (spec == NULL)
680*7d82f0f8SDina K Nimeh 		return (NULL);
681*7d82f0f8SDina K Nimeh 
682*7d82f0f8SDina K Nimeh 	/*
683*7d82f0f8SDina K Nimeh 	 * Correct format is "[name]:[manuf]:[serial]:key". Can't use
684*7d82f0f8SDina K Nimeh 	 * strtok because it treats ":::key" and "key:::" and "key" all
685*7d82f0f8SDina K Nimeh 	 * as the same thing, and we can't have the :s compressed away.
686*7d82f0f8SDina K Nimeh 	 */
687*7d82f0f8SDina K Nimeh 	field[0] = spec;
688*7d82f0f8SDina K Nimeh 	for (i = 1; i < NFIELDS + 1; i++) {
689*7d82f0f8SDina K Nimeh 		field[i] = strchr(field[i-1], ':');
690*7d82f0f8SDina K Nimeh 		if (field[i] == NULL)
691*7d82f0f8SDina K Nimeh 			break;
692*7d82f0f8SDina K Nimeh 		field[i]++;
693*7d82f0f8SDina K Nimeh 	}
694*7d82f0f8SDina K Nimeh 	if (i < NFIELDS)		/* not enough fields */
695*7d82f0f8SDina K Nimeh 		return (NULL);
696*7d82f0f8SDina K Nimeh 	if (field[NFIELDS] != NULL)	/* too many fields */
697*7d82f0f8SDina K Nimeh 		return (NULL);
698*7d82f0f8SDina K Nimeh 	field[NFIELDS] = strchr(field[NFIELDS-1], '\0') + 1;
699*7d82f0f8SDina K Nimeh 
700*7d82f0f8SDina K Nimeh 	/* key label can't be empty */
701*7d82f0f8SDina K Nimeh 	if (nullfield(FLD_LABEL))
702*7d82f0f8SDina K Nimeh 		return (NULL);
703*7d82f0f8SDina K Nimeh 
704*7d82f0f8SDina K Nimeh 	ti = malloc(sizeof (token_spec_t));
705*7d82f0f8SDina K Nimeh 	if (ti == NULL)
706*7d82f0f8SDina K Nimeh 		return (NULL);
707*7d82f0f8SDina K Nimeh 
708*7d82f0f8SDina K Nimeh 	copyfield(ti->name, FLD_NAME);
709*7d82f0f8SDina K Nimeh 	copyfield(ti->mfr, FLD_MANUF);
710*7d82f0f8SDina K Nimeh 	copyfield(ti->serno, FLD_SERIAL);
711*7d82f0f8SDina K Nimeh 	copyfield(ti->key, FLD_LABEL);
712*7d82f0f8SDina K Nimeh 
713*7d82f0f8SDina K Nimeh 	/*
714*7d82f0f8SDina K Nimeh 	 * If token specified and it only contains a key label, then
715*7d82f0f8SDina K Nimeh 	 * search all tokens for the key, otherwise only those with
716*7d82f0f8SDina K Nimeh 	 * matching name, mfr, and serno are used.
717*7d82f0f8SDina K Nimeh 	 */
718*7d82f0f8SDina K Nimeh 	/*
719*7d82f0f8SDina K Nimeh 	 * That's how we'd like it to be, however, if only the key label
720*7d82f0f8SDina K Nimeh 	 * is specified, default to using softtoken.  It's easier.
721*7d82f0f8SDina K Nimeh 	 */
722*7d82f0f8SDina K Nimeh 	if (ti->name == NULL && ti->mfr == NULL && ti->serno == NULL)
723*7d82f0f8SDina K Nimeh 		ti->name = strdup(pkcs11_default_token());
724*7d82f0f8SDina K Nimeh 	return (ti);
725*7d82f0f8SDina K Nimeh }
726*7d82f0f8SDina K Nimeh 
727*7d82f0f8SDina K Nimeh /*
728*7d82f0f8SDina K Nimeh  * PBE the passphrase into a raw key
729*7d82f0f8SDina K Nimeh  */
730*7d82f0f8SDina K Nimeh static void
731*7d82f0f8SDina K Nimeh getkeyfromuser(mech_alias_t *cipher, char **raw_key, size_t *raw_key_sz)
732*7d82f0f8SDina K Nimeh {
733*7d82f0f8SDina K Nimeh 	CK_SESSION_HANDLE sess;
734*7d82f0f8SDina K Nimeh 	CK_RV	rv;
735*7d82f0f8SDina K Nimeh 	char	*pass = NULL;
736*7d82f0f8SDina K Nimeh 	size_t	passlen = 0;
737*7d82f0f8SDina K Nimeh 	void	*salt = NULL;	/* don't use NULL, see note on salt below */
738*7d82f0f8SDina K Nimeh 	size_t	saltlen = 0;
739*7d82f0f8SDina K Nimeh 	CK_KEY_TYPE ktype;
740*7d82f0f8SDina K Nimeh 	void	*kvalue;
741*7d82f0f8SDina K Nimeh 	size_t	klen;
742*7d82f0f8SDina K Nimeh 
743*7d82f0f8SDina K Nimeh 	/* did init_crypto find a slot that supports this cipher? */
744*7d82f0f8SDina K Nimeh 	if (cipher->slot == (CK_SLOT_ID)-1 || cipher->max_keysize == 0) {
745*7d82f0f8SDina K Nimeh 		rv = CKR_MECHANISM_INVALID;
746*7d82f0f8SDina K Nimeh 		goto cleanup;
747*7d82f0f8SDina K Nimeh 	}
748*7d82f0f8SDina K Nimeh 
749*7d82f0f8SDina K Nimeh 	rv = pkcs11_mech2keytype(cipher->type, &ktype);
750*7d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
751*7d82f0f8SDina K Nimeh 		goto cleanup;
752*7d82f0f8SDina K Nimeh 
753*7d82f0f8SDina K Nimeh 	/*
754*7d82f0f8SDina K Nimeh 	 * use the passphrase to generate a PBE PKCS#5 secret key and
755*7d82f0f8SDina K Nimeh 	 * retrieve the raw key data to eventually pass it to the kernel;
756*7d82f0f8SDina K Nimeh 	 */
757*7d82f0f8SDina K Nimeh 	rv = C_OpenSession(cipher->slot, CKF_SERIAL_SESSION, NULL, NULL, &sess);
758*7d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
759*7d82f0f8SDina K Nimeh 		goto cleanup;
760*7d82f0f8SDina K Nimeh 
761*7d82f0f8SDina K Nimeh 	/* get user passphrase with 8 byte minimum */
762*7d82f0f8SDina K Nimeh 	if (pkcs11_get_pass(NULL, &pass, &passlen, MIN_PASSLEN, B_TRUE) < 0) {
763*7d82f0f8SDina K Nimeh 		die(gettext("passphrases do not match\n"));
764*7d82f0f8SDina K Nimeh 	}
765*7d82f0f8SDina K Nimeh 
766*7d82f0f8SDina K Nimeh 	/*
767*7d82f0f8SDina K Nimeh 	 * salt should not be NULL, or else pkcs11_PasswdToKey() will
768*7d82f0f8SDina K Nimeh 	 * complain about CKR_MECHANISM_PARAM_INVALID; the following is
769*7d82f0f8SDina K Nimeh 	 * to make up for not having a salt until a proper one is used
770*7d82f0f8SDina K Nimeh 	 */
771*7d82f0f8SDina K Nimeh 	salt = pass;
772*7d82f0f8SDina K Nimeh 	saltlen = passlen;
773*7d82f0f8SDina K Nimeh 
774*7d82f0f8SDina K Nimeh 	klen = cipher->max_keysize;
775*7d82f0f8SDina K Nimeh 	rv = pkcs11_PasswdToKey(sess, pass, passlen, salt, saltlen, ktype,
776*7d82f0f8SDina K Nimeh 	    cipher->max_keysize, &kvalue, &klen);
777*7d82f0f8SDina K Nimeh 
778*7d82f0f8SDina K Nimeh 	(void) C_CloseSession(sess);
779*7d82f0f8SDina K Nimeh 
780*7d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
781*7d82f0f8SDina K Nimeh 		goto cleanup;
782*7d82f0f8SDina K Nimeh 	}
783*7d82f0f8SDina K Nimeh 
784*7d82f0f8SDina K Nimeh 	/* assert(klen == cipher->max_keysize); */
785*7d82f0f8SDina K Nimeh 	*raw_key_sz = klen;
786*7d82f0f8SDina K Nimeh 	*raw_key = (char *)kvalue;
787*7d82f0f8SDina K Nimeh 	return;
788*7d82f0f8SDina K Nimeh 
789*7d82f0f8SDina K Nimeh cleanup:
790*7d82f0f8SDina K Nimeh 	die(gettext("failed to generate %s key from passphrase: %s"),
791*7d82f0f8SDina K Nimeh 	    cipher->alias, pkcs11_strerror(rv));
792*7d82f0f8SDina K Nimeh }
793*7d82f0f8SDina K Nimeh 
794*7d82f0f8SDina K Nimeh /*
795*7d82f0f8SDina K Nimeh  * Read raw key from file; also handles ephemeral keys.
796*7d82f0f8SDina K Nimeh  */
797*7d82f0f8SDina K Nimeh void
798*7d82f0f8SDina K Nimeh getkeyfromfile(const char *pathname, mech_alias_t *cipher, char **key,
799*7d82f0f8SDina K Nimeh     size_t *ksz)
800*7d82f0f8SDina K Nimeh {
801*7d82f0f8SDina K Nimeh 	int	fd;
802*7d82f0f8SDina K Nimeh 	struct stat sbuf;
803*7d82f0f8SDina K Nimeh 	boolean_t notplain = B_FALSE;
804*7d82f0f8SDina K Nimeh 	ssize_t	cursz;
805*7d82f0f8SDina K Nimeh 	ssize_t	nread;
806*7d82f0f8SDina K Nimeh 
807*7d82f0f8SDina K Nimeh 	/* ephemeral keys are just random data */
808*7d82f0f8SDina K Nimeh 	if (pathname == NULL) {
809*7d82f0f8SDina K Nimeh 		*ksz = cipher->max_keysize;
810*7d82f0f8SDina K Nimeh 		*key = malloc(*ksz);
811*7d82f0f8SDina K Nimeh 		if (*key == NULL)
812*7d82f0f8SDina K Nimeh 			die(gettext("failed to allocate memory for"
813*7d82f0f8SDina K Nimeh 			    " ephemeral key"));
814*7d82f0f8SDina K Nimeh 		if (pkcs11_random_data(*key, *ksz) < 0) {
815*7d82f0f8SDina K Nimeh 			free(*key);
816*7d82f0f8SDina K Nimeh 			die(gettext("failed to get enough random data"));
817*7d82f0f8SDina K Nimeh 		}
818*7d82f0f8SDina K Nimeh 		return;
819*7d82f0f8SDina K Nimeh 	}
820*7d82f0f8SDina K Nimeh 
821*7d82f0f8SDina K Nimeh 	/*
822*7d82f0f8SDina K Nimeh 	 * If the remaining section of code didn't also check for secure keyfile
823*7d82f0f8SDina K Nimeh 	 * permissions and whether the key is within cipher min and max lengths,
824*7d82f0f8SDina K Nimeh 	 * (or, if those things moved out of this block), we could have had:
825*7d82f0f8SDina K Nimeh 	 *	if (pkcs11_read_data(pathname, key, ksz) < 0)
826*7d82f0f8SDina K Nimeh 	 *		handle_error();
827*7d82f0f8SDina K Nimeh 	 */
828*7d82f0f8SDina K Nimeh 
829*7d82f0f8SDina K Nimeh 	if ((fd = open(pathname, O_RDONLY, 0)) == -1)
830*7d82f0f8SDina K Nimeh 		die(gettext("open of keyfile (%s) failed"), pathname);
831*7d82f0f8SDina K Nimeh 
832*7d82f0f8SDina K Nimeh 	if (fstat(fd, &sbuf) == -1)
833*7d82f0f8SDina K Nimeh 		die(gettext("fstat of keyfile (%s) failed"), pathname);
834*7d82f0f8SDina K Nimeh 
835*7d82f0f8SDina K Nimeh 	if (S_ISREG(sbuf.st_mode)) {
836*7d82f0f8SDina K Nimeh 		if ((sbuf.st_mode & (S_IWGRP | S_IWOTH)) != 0)
837*7d82f0f8SDina K Nimeh 			die(gettext("insecure permissions on keyfile %s\n"),
838*7d82f0f8SDina K Nimeh 			    pathname);
839*7d82f0f8SDina K Nimeh 
840*7d82f0f8SDina K Nimeh 		*ksz = sbuf.st_size;
841*7d82f0f8SDina K Nimeh 		if (*ksz < cipher->min_keysize || cipher->max_keysize < *ksz) {
842*7d82f0f8SDina K Nimeh 			warn(gettext("%s: invalid keysize: %d\n"),
843*7d82f0f8SDina K Nimeh 			    pathname, (int)*ksz);
844*7d82f0f8SDina K Nimeh 			die(gettext("\t%d <= keysize <= %d\n"),
845*7d82f0f8SDina K Nimeh 			    cipher->min_keysize, cipher->max_keysize);
846*7d82f0f8SDina K Nimeh 		}
847*7d82f0f8SDina K Nimeh 	} else {
848*7d82f0f8SDina K Nimeh 		*ksz = cipher->max_keysize;
849*7d82f0f8SDina K Nimeh 		notplain = B_TRUE;
850*7d82f0f8SDina K Nimeh 	}
851*7d82f0f8SDina K Nimeh 
852*7d82f0f8SDina K Nimeh 	*key = malloc(*ksz);
853*7d82f0f8SDina K Nimeh 	if (*key == NULL)
854*7d82f0f8SDina K Nimeh 		die(gettext("failed to allocate memory for key from file"));
855*7d82f0f8SDina K Nimeh 
856*7d82f0f8SDina K Nimeh 	for (cursz = 0, nread = 0; cursz < *ksz; cursz += nread) {
857*7d82f0f8SDina K Nimeh 		nread = read(fd, *key, *ksz);
858*7d82f0f8SDina K Nimeh 		if (nread > 0)
859*7d82f0f8SDina K Nimeh 			continue;
860*7d82f0f8SDina K Nimeh 		/*
861*7d82f0f8SDina K Nimeh 		 * nread == 0.  If it's not a regular file we were trying to
862*7d82f0f8SDina K Nimeh 		 * get the maximum keysize of data possible for this cipher.
863*7d82f0f8SDina K Nimeh 		 * But if we've got at least the minimum keysize of data,
864*7d82f0f8SDina K Nimeh 		 * round down to the nearest keysize unit and call it good.
865*7d82f0f8SDina K Nimeh 		 * If we haven't met the minimum keysize, that's an error.
866*7d82f0f8SDina K Nimeh 		 * If it's a regular file, nread = 0 is also an error.
867*7d82f0f8SDina K Nimeh 		 */
868*7d82f0f8SDina K Nimeh 		if (nread == 0 && notplain && cursz >= cipher->min_keysize) {
869*7d82f0f8SDina K Nimeh 			*ksz = (cursz / cipher->min_keysize) *
870*7d82f0f8SDina K Nimeh 			    cipher->min_keysize;
871*7d82f0f8SDina K Nimeh 			break;
872*7d82f0f8SDina K Nimeh 		}
873*7d82f0f8SDina K Nimeh 		die(gettext("%s: can't read all keybytes"), pathname);
874*7d82f0f8SDina K Nimeh 	}
875*7d82f0f8SDina K Nimeh 	(void) close(fd);
876*7d82f0f8SDina K Nimeh }
877*7d82f0f8SDina K Nimeh 
878*7d82f0f8SDina K Nimeh /*
879*7d82f0f8SDina K Nimeh  * Read the raw key from token, or from a file that was wrapped with a
880*7d82f0f8SDina K Nimeh  * key from token
881*7d82f0f8SDina K Nimeh  */
882*7d82f0f8SDina K Nimeh void
883*7d82f0f8SDina K Nimeh getkeyfromtoken(CK_SESSION_HANDLE sess,
884*7d82f0f8SDina K Nimeh     token_spec_t *token, const char *keyfile, mech_alias_t *cipher,
885*7d82f0f8SDina K Nimeh     char **raw_key, size_t *raw_key_sz)
886*7d82f0f8SDina K Nimeh {
887*7d82f0f8SDina K Nimeh 	CK_RV	rv = CKR_OK;
888*7d82f0f8SDina K Nimeh 	CK_BBOOL trueval = B_TRUE;
889*7d82f0f8SDina K Nimeh 	CK_OBJECT_CLASS kclass;		/* secret key or RSA private key */
890*7d82f0f8SDina K Nimeh 	CK_KEY_TYPE ktype;		/* from selected cipher or CKK_RSA */
891*7d82f0f8SDina K Nimeh 	CK_KEY_TYPE raw_ktype;		/* from selected cipher */
892*7d82f0f8SDina K Nimeh 	CK_ATTRIBUTE	key_tmpl[] = {
893*7d82f0f8SDina K Nimeh 		{ CKA_CLASS, NULL, 0 },	/* re-used for token key and unwrap */
894*7d82f0f8SDina K Nimeh 		{ CKA_KEY_TYPE, NULL, 0 },	/* ditto */
895*7d82f0f8SDina K Nimeh 		{ CKA_LABEL, NULL, 0 },
896*7d82f0f8SDina K Nimeh 		{ CKA_TOKEN, NULL, 0 },
897*7d82f0f8SDina K Nimeh 		{ CKA_PRIVATE, NULL, 0 }
898*7d82f0f8SDina K Nimeh 	    };
899*7d82f0f8SDina K Nimeh 	CK_ULONG attrs = sizeof (key_tmpl) / sizeof (CK_ATTRIBUTE);
900*7d82f0f8SDina K Nimeh 	int	i;
901*7d82f0f8SDina K Nimeh 	char	*pass = NULL;
902*7d82f0f8SDina K Nimeh 	size_t	passlen = 0;
903*7d82f0f8SDina K Nimeh 	CK_OBJECT_HANDLE obj, rawobj;
904*7d82f0f8SDina K Nimeh 	CK_ULONG num_objs = 1;		/* just want to find 1 token key */
905*7d82f0f8SDina K Nimeh 	CK_MECHANISM unwrap = { CKM_RSA_PKCS, NULL, 0 };
906*7d82f0f8SDina K Nimeh 	char	*rkey;
907*7d82f0f8SDina K Nimeh 	size_t	rksz;
908*7d82f0f8SDina K Nimeh 
909*7d82f0f8SDina K Nimeh 	if (token == NULL || token->key == NULL)
910*7d82f0f8SDina K Nimeh 		return;
911*7d82f0f8SDina K Nimeh 
912*7d82f0f8SDina K Nimeh 	/* did init_crypto find a slot that supports this cipher? */
913*7d82f0f8SDina K Nimeh 	if (cipher->slot == (CK_SLOT_ID)-1 || cipher->max_keysize == 0) {
914*7d82f0f8SDina K Nimeh 		die(gettext("failed to find any cryptographic provider, "
915*7d82f0f8SDina K Nimeh 		    "use \"cryptoadm list -p\" to find providers: %s\n"),
916*7d82f0f8SDina K Nimeh 		    pkcs11_strerror(CKR_MECHANISM_INVALID));
917*7d82f0f8SDina K Nimeh 	}
918*7d82f0f8SDina K Nimeh 
919*7d82f0f8SDina K Nimeh 	if (pkcs11_get_pass(token->name, &pass, &passlen, 0, B_FALSE) < 0)
920*7d82f0f8SDina K Nimeh 		die(gettext("unable to get passphrase"));
921*7d82f0f8SDina K Nimeh 
922*7d82f0f8SDina K Nimeh 	/* use passphrase to login to token */
923*7d82f0f8SDina K Nimeh 	if (pass != NULL && passlen > 0) {
924*7d82f0f8SDina K Nimeh 		rv = C_Login(sess, CKU_USER, (CK_UTF8CHAR_PTR)pass, passlen);
925*7d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
926*7d82f0f8SDina K Nimeh 			die(gettext("cannot login to the token %s: %s\n"),
927*7d82f0f8SDina K Nimeh 			    token->name, pkcs11_strerror(rv));
928*7d82f0f8SDina K Nimeh 		}
929*7d82f0f8SDina K Nimeh 	}
930*7d82f0f8SDina K Nimeh 
931*7d82f0f8SDina K Nimeh 	rv = pkcs11_mech2keytype(cipher->type, &raw_ktype);
932*7d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
933*7d82f0f8SDina K Nimeh 		die(gettext("failed to get key type for cipher %s: %s\n"),
934*7d82f0f8SDina K Nimeh 		    cipher->name, pkcs11_strerror(rv));
935*7d82f0f8SDina K Nimeh 	}
936*7d82f0f8SDina K Nimeh 
937*7d82f0f8SDina K Nimeh 	/*
938*7d82f0f8SDina K Nimeh 	 * If no keyfile was given, then the token key is secret key to
939*7d82f0f8SDina K Nimeh 	 * be used for encryption/decryption.  Otherwise, the keyfile
940*7d82f0f8SDina K Nimeh 	 * contains a wrapped secret key, and the token is actually the
941*7d82f0f8SDina K Nimeh 	 * unwrapping RSA private key.
942*7d82f0f8SDina K Nimeh 	 */
943*7d82f0f8SDina K Nimeh 	if (keyfile == NULL) {
944*7d82f0f8SDina K Nimeh 		kclass = CKO_SECRET_KEY;
945*7d82f0f8SDina K Nimeh 		ktype = raw_ktype;
946*7d82f0f8SDina K Nimeh 	} else {
947*7d82f0f8SDina K Nimeh 		kclass = CKO_PRIVATE_KEY;
948*7d82f0f8SDina K Nimeh 		ktype = CKK_RSA;
949*7d82f0f8SDina K Nimeh 	}
950*7d82f0f8SDina K Nimeh 
951*7d82f0f8SDina K Nimeh 	/* Find the key in the token first */
952*7d82f0f8SDina K Nimeh 	for (i = 0; i < attrs; i++) {
953*7d82f0f8SDina K Nimeh 		switch (key_tmpl[i].type) {
954*7d82f0f8SDina K Nimeh 		case CKA_CLASS:
955*7d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &kclass;
956*7d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (kclass);
957*7d82f0f8SDina K Nimeh 			break;
958*7d82f0f8SDina K Nimeh 		case CKA_KEY_TYPE:
959*7d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &ktype;
960*7d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (ktype);
961*7d82f0f8SDina K Nimeh 			break;
962*7d82f0f8SDina K Nimeh 		case CKA_LABEL:
963*7d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = token->key;
964*7d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = strlen(token->key);
965*7d82f0f8SDina K Nimeh 			break;
966*7d82f0f8SDina K Nimeh 		case CKA_TOKEN:
967*7d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &trueval;
968*7d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (trueval);
969*7d82f0f8SDina K Nimeh 			break;
970*7d82f0f8SDina K Nimeh 		case CKA_PRIVATE:
971*7d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &trueval;
972*7d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (trueval);
973*7d82f0f8SDina K Nimeh 			break;
974*7d82f0f8SDina K Nimeh 		default:
975*7d82f0f8SDina K Nimeh 			break;
976*7d82f0f8SDina K Nimeh 		}
977*7d82f0f8SDina K Nimeh 	}
978*7d82f0f8SDina K Nimeh 	rv = C_FindObjectsInit(sess, key_tmpl, attrs);
979*7d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
980*7d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s: %s\n"), token->key,
981*7d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
982*7d82f0f8SDina K Nimeh 	rv = C_FindObjects(sess, &obj, 1, &num_objs);
983*7d82f0f8SDina K Nimeh 	(void) C_FindObjectsFinal(sess);
984*7d82f0f8SDina K Nimeh 
985*7d82f0f8SDina K Nimeh 	if (num_objs == 0) {
986*7d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s\n"), token->key);
987*7d82f0f8SDina K Nimeh 	} else if (rv != CKR_OK) {
988*7d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s: %s\n"), token->key,
989*7d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
990*7d82f0f8SDina K Nimeh 	}
991*7d82f0f8SDina K Nimeh 
992*7d82f0f8SDina K Nimeh 	/*
993*7d82f0f8SDina K Nimeh 	 * No keyfile means when token key is found, convert it to raw key,
994*7d82f0f8SDina K Nimeh 	 * and done.  Otherwise still need do an unwrap to create yet another
995*7d82f0f8SDina K Nimeh 	 * obj and that needs to be converted to raw key before we're done.
996*7d82f0f8SDina K Nimeh 	 */
997*7d82f0f8SDina K Nimeh 	if (keyfile == NULL) {
998*7d82f0f8SDina K Nimeh 		/* obj contains raw key, extract it */
999*7d82f0f8SDina K Nimeh 		rv = pkcs11_ObjectToKey(sess, obj, (void **)&rkey, &rksz,
1000*7d82f0f8SDina K Nimeh 		    B_FALSE);
1001*7d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
1002*7d82f0f8SDina K Nimeh 			die(gettext("failed to get key value for %s"
1003*7d82f0f8SDina K Nimeh 			    " from token %s, %s\n"), token->key,
1004*7d82f0f8SDina K Nimeh 			    token->name, pkcs11_strerror(rv));
1005*7d82f0f8SDina K Nimeh 		}
1006*7d82f0f8SDina K Nimeh 	} else {
1007*7d82f0f8SDina K Nimeh 		getkeyfromfile(keyfile, cipher, &rkey, &rksz);
1008*7d82f0f8SDina K Nimeh 
1009*7d82f0f8SDina K Nimeh 		/*
1010*7d82f0f8SDina K Nimeh 		 * Got the wrapping RSA obj and the wrapped key from file.
1011*7d82f0f8SDina K Nimeh 		 * Unwrap the key from file with RSA obj to get rawkey obj.
1012*7d82f0f8SDina K Nimeh 		 */
1013*7d82f0f8SDina K Nimeh 
1014*7d82f0f8SDina K Nimeh 		/* re-use the first two attributes of key_tmpl */
1015*7d82f0f8SDina K Nimeh 		kclass = CKO_SECRET_KEY;
1016*7d82f0f8SDina K Nimeh 		ktype = raw_ktype;
1017*7d82f0f8SDina K Nimeh 
1018*7d82f0f8SDina K Nimeh 		rv = C_UnwrapKey(sess, &unwrap, obj, (CK_BYTE_PTR)rkey,
1019*7d82f0f8SDina K Nimeh 		    rksz, key_tmpl, 2, &rawobj);
1020*7d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
1021*7d82f0f8SDina K Nimeh 			die(gettext("failed to unwrap key in keyfile %s,"
1022*7d82f0f8SDina K Nimeh 			    " %s\n"), keyfile, pkcs11_strerror(rv));
1023*7d82f0f8SDina K Nimeh 		}
1024*7d82f0f8SDina K Nimeh 		/* rawobj contains raw key, extract it */
1025*7d82f0f8SDina K Nimeh 		rv = pkcs11_ObjectToKey(sess, rawobj, (void **)&rkey, &rksz,
1026*7d82f0f8SDina K Nimeh 		    B_TRUE);
1027*7d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
1028*7d82f0f8SDina K Nimeh 			die(gettext("failed to get unwrapped key value for"
1029*7d82f0f8SDina K Nimeh 			    " key in keyfile %s, %s\n"), keyfile,
1030*7d82f0f8SDina K Nimeh 			    pkcs11_strerror(rv));
1031*7d82f0f8SDina K Nimeh 		}
1032*7d82f0f8SDina K Nimeh 	}
1033*7d82f0f8SDina K Nimeh 
1034*7d82f0f8SDina K Nimeh 	/* validate raw key size */
1035*7d82f0f8SDina K Nimeh 	if (rksz < cipher->min_keysize || cipher->max_keysize < rksz) {
1036*7d82f0f8SDina K Nimeh 		warn(gettext("%s: invalid keysize: %d\n"), keyfile, (int)rksz);
1037*7d82f0f8SDina K Nimeh 		die(gettext("\t%d <= keysize <= %d\n"), cipher->min_keysize,
1038*7d82f0f8SDina K Nimeh 		    cipher->max_keysize);
1039*7d82f0f8SDina K Nimeh 	}
1040*7d82f0f8SDina K Nimeh 
1041*7d82f0f8SDina K Nimeh 	*raw_key_sz = rksz;
1042*7d82f0f8SDina K Nimeh 	*raw_key = (char *)rkey;
1043*7d82f0f8SDina K Nimeh }
1044*7d82f0f8SDina K Nimeh 
1045*7d82f0f8SDina K Nimeh /*
1046*7d82f0f8SDina K Nimeh  * Set up cipher key limits and verify PKCS#11 can be done
1047*7d82f0f8SDina K Nimeh  * match_token_cipher is the function pointer used by
1048*7d82f0f8SDina K Nimeh  * pkcs11_GetCriteriaSession() init_crypto.
1049*7d82f0f8SDina K Nimeh  */
1050*7d82f0f8SDina K Nimeh boolean_t
1051*7d82f0f8SDina K Nimeh match_token_cipher(CK_SLOT_ID slot_id, void *args, CK_RV *rv)
1052*7d82f0f8SDina K Nimeh {
1053*7d82f0f8SDina K Nimeh 	token_spec_t *token;
1054*7d82f0f8SDina K Nimeh 	mech_alias_t *cipher;
1055*7d82f0f8SDina K Nimeh 	CK_TOKEN_INFO tokinfo;
1056*7d82f0f8SDina K Nimeh 	CK_MECHANISM_INFO mechinfo;
1057*7d82f0f8SDina K Nimeh 	boolean_t token_match;
1058*7d82f0f8SDina K Nimeh 
1059*7d82f0f8SDina K Nimeh 	/*
1060*7d82f0f8SDina K Nimeh 	 * While traversing slot list, pick up the following info per slot:
1061*7d82f0f8SDina K Nimeh 	 * - if token specified, whether it matches this slot's token info
1062*7d82f0f8SDina K Nimeh 	 * - if the slot supports the PKCS#5 PBKD2 cipher
1063*7d82f0f8SDina K Nimeh 	 *
1064*7d82f0f8SDina K Nimeh 	 * If the user said on the command line
1065*7d82f0f8SDina K Nimeh 	 *	-T tok:mfr:ser:lab -k keyfile
1066*7d82f0f8SDina K Nimeh 	 *	-c cipher -T tok:mfr:ser:lab -k keyfile
1067*7d82f0f8SDina K Nimeh 	 * the given cipher or the default cipher apply to keyfile,
1068*7d82f0f8SDina K Nimeh 	 * If the user said instead
1069*7d82f0f8SDina K Nimeh 	 *	-T tok:mfr:ser:lab
1070*7d82f0f8SDina K Nimeh 	 *	-c cipher -T tok:mfr:ser:lab
1071*7d82f0f8SDina K Nimeh 	 * the key named "lab" may or may not agree with the given
1072*7d82f0f8SDina K Nimeh 	 * cipher or the default cipher.  In those cases, cipher will
1073*7d82f0f8SDina K Nimeh 	 * be overridden with the actual cipher type of the key "lab".
1074*7d82f0f8SDina K Nimeh 	 */
1075*7d82f0f8SDina K Nimeh 	*rv = CKR_FUNCTION_FAILED;
1076*7d82f0f8SDina K Nimeh 
1077*7d82f0f8SDina K Nimeh 	if (args == NULL) {
1078*7d82f0f8SDina K Nimeh 		return (B_FALSE);
1079*7d82f0f8SDina K Nimeh 	}
1080*7d82f0f8SDina K Nimeh 
1081*7d82f0f8SDina K Nimeh 	cipher = (mech_alias_t *)args;
1082*7d82f0f8SDina K Nimeh 	token = cipher->token;
1083*7d82f0f8SDina K Nimeh 
1084*7d82f0f8SDina K Nimeh 	if (C_GetMechanismInfo(slot_id, cipher->type, &mechinfo) != CKR_OK) {
1085*7d82f0f8SDina K Nimeh 		return (B_FALSE);
1086*7d82f0f8SDina K Nimeh 	}
1087*7d82f0f8SDina K Nimeh 
1088*7d82f0f8SDina K Nimeh 	if (token == NULL) {
1089*7d82f0f8SDina K Nimeh 		if (C_GetMechanismInfo(slot_id, CKM_PKCS5_PBKD2, &mechinfo) !=
1090*7d82f0f8SDina K Nimeh 		    CKR_OK) {
1091*7d82f0f8SDina K Nimeh 			return (B_FALSE);
1092*7d82f0f8SDina K Nimeh 		}
1093*7d82f0f8SDina K Nimeh 		goto foundit;
1094*7d82f0f8SDina K Nimeh 	}
1095*7d82f0f8SDina K Nimeh 
1096*7d82f0f8SDina K Nimeh 	/* does the token match the token spec? */
1097*7d82f0f8SDina K Nimeh 	if (token->key == NULL || (C_GetTokenInfo(slot_id, &tokinfo) != CKR_OK))
1098*7d82f0f8SDina K Nimeh 		return (B_FALSE);
1099*7d82f0f8SDina K Nimeh 
1100*7d82f0f8SDina K Nimeh 	token_match = B_TRUE;
1101*7d82f0f8SDina K Nimeh 
1102*7d82f0f8SDina K Nimeh 	if (token->name != NULL && (token->name)[0] != '\0' &&
1103*7d82f0f8SDina K Nimeh 	    strncmp((char *)token->name, (char *)tokinfo.label,
1104*7d82f0f8SDina K Nimeh 	    TOKEN_LABEL_SIZE) != 0)
1105*7d82f0f8SDina K Nimeh 		token_match = B_FALSE;
1106*7d82f0f8SDina K Nimeh 	if (token->mfr != NULL && (token->mfr)[0] != '\0' &&
1107*7d82f0f8SDina K Nimeh 	    strncmp((char *)token->mfr, (char *)tokinfo.manufacturerID,
1108*7d82f0f8SDina K Nimeh 	    TOKEN_MANUFACTURER_SIZE) != 0)
1109*7d82f0f8SDina K Nimeh 		token_match = B_FALSE;
1110*7d82f0f8SDina K Nimeh 	if (token->serno != NULL && (token->serno)[0] != '\0' &&
1111*7d82f0f8SDina K Nimeh 	    strncmp((char *)token->serno, (char *)tokinfo.serialNumber,
1112*7d82f0f8SDina K Nimeh 	    TOKEN_SERIAL_SIZE) != 0)
1113*7d82f0f8SDina K Nimeh 		token_match = B_FALSE;
1114*7d82f0f8SDina K Nimeh 
1115*7d82f0f8SDina K Nimeh 	if (!token_match)
1116*7d82f0f8SDina K Nimeh 		return (B_FALSE);
1117*7d82f0f8SDina K Nimeh 
1118*7d82f0f8SDina K Nimeh foundit:
1119*7d82f0f8SDina K Nimeh 	cipher->slot = slot_id;
1120*7d82f0f8SDina K Nimeh 	return (B_TRUE);
1121*7d82f0f8SDina K Nimeh }
1122*7d82f0f8SDina K Nimeh 
1123*7d82f0f8SDina K Nimeh /*
1124*7d82f0f8SDina K Nimeh  * Clean up crypto loose ends
1125*7d82f0f8SDina K Nimeh  */
1126*7d82f0f8SDina K Nimeh static void
1127*7d82f0f8SDina K Nimeh end_crypto(CK_SESSION_HANDLE sess)
1128*7d82f0f8SDina K Nimeh {
1129*7d82f0f8SDina K Nimeh 	(void) C_CloseSession(sess);
1130*7d82f0f8SDina K Nimeh 	(void) C_Finalize(NULL);
1131*7d82f0f8SDina K Nimeh }
1132*7d82f0f8SDina K Nimeh 
1133*7d82f0f8SDina K Nimeh /*
1134*7d82f0f8SDina K Nimeh  * Set up crypto, opening session on slot that matches token and cipher
1135*7d82f0f8SDina K Nimeh  */
1136*7d82f0f8SDina K Nimeh static void
1137*7d82f0f8SDina K Nimeh init_crypto(token_spec_t *token, mech_alias_t *cipher,
1138*7d82f0f8SDina K Nimeh     CK_SESSION_HANDLE_PTR sess)
1139*7d82f0f8SDina K Nimeh {
1140*7d82f0f8SDina K Nimeh 	CK_RV	rv;
1141*7d82f0f8SDina K Nimeh 
1142*7d82f0f8SDina K Nimeh 	cipher->token = token;
1143*7d82f0f8SDina K Nimeh 
1144*7d82f0f8SDina K Nimeh 	/* Turn off Metaslot so that we can see actual tokens */
1145*7d82f0f8SDina K Nimeh 	if (setenv("METASLOT_ENABLED", "false", 1) < 0) {
1146*7d82f0f8SDina K Nimeh 		die(gettext("could not disable Metaslot"));
1147*7d82f0f8SDina K Nimeh 	}
1148*7d82f0f8SDina K Nimeh 
1149*7d82f0f8SDina K Nimeh 	rv = pkcs11_GetCriteriaSession(match_token_cipher, (void *)cipher,
1150*7d82f0f8SDina K Nimeh 	    sess);
1151*7d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
1152*7d82f0f8SDina K Nimeh 		end_crypto(*sess);
1153*7d82f0f8SDina K Nimeh 		if (rv == CKR_HOST_MEMORY) {
1154*7d82f0f8SDina K Nimeh 			die("malloc");
1155*7d82f0f8SDina K Nimeh 		}
1156*7d82f0f8SDina K Nimeh 		die(gettext("failed to find any cryptographic provider, "
1157*7d82f0f8SDina K Nimeh 		    "use \"cryptoadm list -p\" to find providers: %s\n"),
1158*7d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
1159*7d82f0f8SDina K Nimeh 	}
1160*7d82f0f8SDina K Nimeh }
1161*7d82f0f8SDina K Nimeh 
1162*7d82f0f8SDina K Nimeh /*
116387117650Saalok  * Uncompress a file.
116487117650Saalok  *
116587117650Saalok  * First map the file in to establish a device
116687117650Saalok  * association, then read from it. On-the-fly
116787117650Saalok  * decompression will automatically uncompress
116887117650Saalok  * the file if it's compressed
116987117650Saalok  *
117087117650Saalok  * If the file is mapped and a device association
117187117650Saalok  * has been established, disallow uncompressing
117287117650Saalok  * the file until it is unmapped.
117387117650Saalok  */
117487117650Saalok static void
117587117650Saalok lofi_uncompress(int lfd, const char *filename)
11767c478bd9Sstevel@tonic-gate {
117787117650Saalok 	struct lofi_ioctl li;
117887117650Saalok 	char buf[MAXBSIZE];
117987117650Saalok 	char devicename[32];
118087117650Saalok 	char tmpfilename[MAXPATHLEN];
1181*7d82f0f8SDina K Nimeh 	char *x;
118287117650Saalok 	char *dir = NULL;
118387117650Saalok 	char *file = NULL;
118487117650Saalok 	int minor = 0;
118587117650Saalok 	struct stat64 statbuf;
118687117650Saalok 	int compfd = -1;
118787117650Saalok 	int uncompfd = -1;
118887117650Saalok 	ssize_t rbytes;
118987117650Saalok 
119087117650Saalok 	/*
119187117650Saalok 	 * Disallow uncompressing the file if it is
119287117650Saalok 	 * already mapped.
119387117650Saalok 	 */
119487117650Saalok 	li.li_minor = 0;
119587117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
119687117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1)
119787117650Saalok 		die(gettext("%s must be unmapped before uncompressing"),
119887117650Saalok 		    filename);
119987117650Saalok 
120087117650Saalok 	/* Zero length files don't need to be uncompressed */
120187117650Saalok 	if (stat64(filename, &statbuf) == -1)
120287117650Saalok 		die(gettext("stat: %s"), filename);
120387117650Saalok 	if (statbuf.st_size == 0)
120487117650Saalok 		return;
120587117650Saalok 
1206*7d82f0f8SDina K Nimeh 	minor = lofi_map_file(lfd, li, filename);
120787117650Saalok 	(void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d",
120887117650Saalok 	    LOFI_BLOCK_NAME, minor);
120987117650Saalok 
121087117650Saalok 	/* If the file isn't compressed, we just return */
121187117650Saalok 	if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) ||
1212a423e759Saalok 	    (li.li_algorithm[0] == '\0')) {
121387117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1214a423e759Saalok 		die("%s is not compressed\n", filename);
121587117650Saalok 	}
121687117650Saalok 
121787117650Saalok 	if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) {
121887117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
121987117650Saalok 		die(gettext("open: %s"), filename);
122087117650Saalok 	}
122187117650Saalok 	/* Create a temp file in the same directory */
1222*7d82f0f8SDina K Nimeh 	x = strdup(filename);
1223*7d82f0f8SDina K Nimeh 	dir = strdup(dirname(x));
1224*7d82f0f8SDina K Nimeh 	free(x);
1225*7d82f0f8SDina K Nimeh 	x = strdup(filename);
1226*7d82f0f8SDina K Nimeh 	file = strdup(basename(x));
1227*7d82f0f8SDina K Nimeh 	free(x);
122887117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
122987117650Saalok 	    "%s/.%sXXXXXX", dir, file);
1230*7d82f0f8SDina K Nimeh 	free(dir);
1231*7d82f0f8SDina K Nimeh 	free(file);
123287117650Saalok 
123387117650Saalok 	if ((uncompfd = mkstemp64(tmpfilename)) == -1) {
123487117650Saalok 		(void) close(compfd);
123587117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1236a423e759Saalok 		die("%s could not be uncompressed\n", filename);
123787117650Saalok 	}
123887117650Saalok 
123987117650Saalok 	/*
124087117650Saalok 	 * Set the mode bits and the owner of this temporary
124187117650Saalok 	 * file to be that of the original uncompressed file
124287117650Saalok 	 */
124387117650Saalok 	(void) fchmod(uncompfd, statbuf.st_mode);
124487117650Saalok 
124587117650Saalok 	if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) {
124687117650Saalok 		(void) close(compfd);
124787117650Saalok 		(void) close(uncompfd);
124887117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1249a423e759Saalok 		die("%s could not be uncompressed\n", filename);
125087117650Saalok 	}
125187117650Saalok 
125287117650Saalok 	/* Now read from the device in MAXBSIZE-sized chunks */
125387117650Saalok 	for (;;) {
125487117650Saalok 		rbytes = read(compfd, buf, sizeof (buf));
125587117650Saalok 
125687117650Saalok 		if (rbytes <= 0)
125787117650Saalok 			break;
125887117650Saalok 
125987117650Saalok 		if (write(uncompfd, buf, rbytes) != rbytes) {
126087117650Saalok 			rbytes = -1;
126187117650Saalok 			break;
126287117650Saalok 		}
126387117650Saalok 	}
126487117650Saalok 
126587117650Saalok 	(void) close(compfd);
126687117650Saalok 	(void) close(uncompfd);
126787117650Saalok 
126887117650Saalok 	/* Delete the mapping */
126987117650Saalok 	delete_mapping(lfd, devicename, filename, B_TRUE);
127087117650Saalok 
127187117650Saalok 	/*
127287117650Saalok 	 * If an error occured while reading or writing, rbytes will
127387117650Saalok 	 * be negative
127487117650Saalok 	 */
127587117650Saalok 	if (rbytes < 0) {
127687117650Saalok 		(void) unlink(tmpfilename);
127787117650Saalok 		die(gettext("could not read from %s"), filename);
127887117650Saalok 	}
127987117650Saalok 
128087117650Saalok 	/* Rename the temp file to the actual file */
128187117650Saalok 	if (rename(tmpfilename, filename) == -1)
128287117650Saalok 		(void) unlink(tmpfilename);
128387117650Saalok }
128487117650Saalok 
128587117650Saalok /*
128687117650Saalok  * Compress a file
128787117650Saalok  */
128887117650Saalok static void
12899525893dSaalok lofi_compress(int *lfd, const char *filename, int compress_index,
129087117650Saalok     uint32_t segsize)
129187117650Saalok {
129287117650Saalok 	struct lofi_ioctl lic;
129387117650Saalok 	lofi_compress_info_t *li;
12949525893dSaalok 	struct flock lock;
129587117650Saalok 	char tmpfilename[MAXPATHLEN];
129687117650Saalok 	char comp_filename[MAXPATHLEN];
129787117650Saalok 	char algorithm[MAXALGLEN];
1298*7d82f0f8SDina K Nimeh 	char *x;
129987117650Saalok 	char *dir = NULL, *file = NULL;
130087117650Saalok 	uchar_t *uncompressed_seg = NULL;
130187117650Saalok 	uchar_t *compressed_seg = NULL;
130287117650Saalok 	uint32_t compressed_segsize;
130387117650Saalok 	uint32_t len_compressed, count;
130487117650Saalok 	uint32_t index_entries, index_sz;
130587117650Saalok 	uint64_t *index = NULL;
130687117650Saalok 	uint64_t offset;
130787117650Saalok 	size_t real_segsize;
130887117650Saalok 	struct stat64 statbuf;
130987117650Saalok 	int compfd = -1, uncompfd = -1;
131087117650Saalok 	int tfd = -1;
131187117650Saalok 	ssize_t rbytes, wbytes, lastread;
131287117650Saalok 	int i, type;
131387117650Saalok 
131487117650Saalok 	/*
131587117650Saalok 	 * Disallow compressing the file if it is
131687117650Saalok 	 * already mapped
131787117650Saalok 	 */
131887117650Saalok 	lic.li_minor = 0;
131987117650Saalok 	(void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
13209525893dSaalok 	if (ioctl(*lfd, LOFI_GET_MINOR, &lic) != -1)
132187117650Saalok 		die(gettext("%s must be unmapped before compressing"),
132287117650Saalok 		    filename);
132387117650Saalok 
13249525893dSaalok 	/*
13259525893dSaalok 	 * Close the control device so other operations
13269525893dSaalok 	 * can use it
13279525893dSaalok 	 */
13289525893dSaalok 	(void) close(*lfd);
13299525893dSaalok 	*lfd = -1;
13309525893dSaalok 
133187117650Saalok 	li = &lofi_compress_table[compress_index];
133287117650Saalok 
133387117650Saalok 	/*
133487117650Saalok 	 * The size of the buffer to hold compressed data must
133587117650Saalok 	 * be slightly larger than the compressed segment size.
133687117650Saalok 	 *
133787117650Saalok 	 * The compress functions use part of the buffer as
133887117650Saalok 	 * scratch space to do calculations.
133987117650Saalok 	 * Ref: http://www.zlib.net/manual.html#compress2
134087117650Saalok 	 */
134187117650Saalok 	compressed_segsize = segsize + (segsize >> 6);
134287117650Saalok 	compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
134387117650Saalok 	uncompressed_seg = (uchar_t *)malloc(segsize);
134487117650Saalok 
134587117650Saalok 	if (compressed_seg == NULL || uncompressed_seg == NULL)
134687117650Saalok 		die(gettext("No memory"));
134787117650Saalok 
13489525893dSaalok 	if ((uncompfd = open64(filename, O_RDWR|O_LARGEFILE, 0)) == -1)
134987117650Saalok 		die(gettext("open: %s"), filename);
135087117650Saalok 
13519525893dSaalok 	lock.l_type = F_WRLCK;
13529525893dSaalok 	lock.l_whence = SEEK_SET;
13539525893dSaalok 	lock.l_start = 0;
13549525893dSaalok 	lock.l_len = 0;
13559525893dSaalok 
13569525893dSaalok 	/*
13579525893dSaalok 	 * Use an advisory lock to ensure that only a
13589525893dSaalok 	 * single lofiadm process compresses a given
13599525893dSaalok 	 * file at any given time
13609525893dSaalok 	 *
13619525893dSaalok 	 * A close on the file descriptor automatically
13629525893dSaalok 	 * closes all lock state on the file
13639525893dSaalok 	 */
13649525893dSaalok 	if (fcntl(uncompfd, F_SETLKW, &lock) == -1)
13659525893dSaalok 		die(gettext("fcntl: %s"), filename);
13669525893dSaalok 
136787117650Saalok 	if (fstat64(uncompfd, &statbuf) == -1) {
136887117650Saalok 		(void) close(uncompfd);
136987117650Saalok 		die(gettext("fstat: %s"), filename);
137087117650Saalok 	}
137187117650Saalok 
137287117650Saalok 	/* Zero length files don't need to be compressed */
137387117650Saalok 	if (statbuf.st_size == 0) {
137487117650Saalok 		(void) close(uncompfd);
137587117650Saalok 		return;
137687117650Saalok 	}
137787117650Saalok 
137887117650Saalok 	/*
137987117650Saalok 	 * Create temporary files in the same directory that
138087117650Saalok 	 * will hold the intermediate data
138187117650Saalok 	 */
1382*7d82f0f8SDina K Nimeh 	x = strdup(filename);
1383*7d82f0f8SDina K Nimeh 	dir = strdup(dirname(x));
1384*7d82f0f8SDina K Nimeh 	free(x);
1385*7d82f0f8SDina K Nimeh 	x = strdup(filename);
1386*7d82f0f8SDina K Nimeh 	file = strdup(basename(x));
1387*7d82f0f8SDina K Nimeh 	free(x);
138887117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
138987117650Saalok 	    "%s/.%sXXXXXX", dir, file);
139087117650Saalok 	(void) snprintf(comp_filename, sizeof (comp_filename),
139187117650Saalok 	    "%s/.%sXXXXXX", dir, file);
1392*7d82f0f8SDina K Nimeh 	free(dir);
1393*7d82f0f8SDina K Nimeh 	free(file);
139487117650Saalok 
139587117650Saalok 	if ((tfd = mkstemp64(tmpfilename)) == -1)
139687117650Saalok 		goto cleanup;
139787117650Saalok 
139887117650Saalok 	if ((compfd = mkstemp64(comp_filename)) == -1)
139987117650Saalok 		goto cleanup;
140087117650Saalok 
140187117650Saalok 	/*
140287117650Saalok 	 * Set the mode bits and owner of the compressed
140387117650Saalok 	 * file to be that of the original uncompressed file
140487117650Saalok 	 */
140587117650Saalok 	(void) fchmod(compfd, statbuf.st_mode);
140687117650Saalok 
140787117650Saalok 	if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
140887117650Saalok 		goto cleanup;
140987117650Saalok 
141087117650Saalok 	/*
141187117650Saalok 	 * Calculate the number of index entries required.
141287117650Saalok 	 * index entries are stored as an array. adding
141387117650Saalok 	 * a '2' here accounts for the fact that the last
141487117650Saalok 	 * segment may not be a multiple of the segment size
141587117650Saalok 	 */
141687117650Saalok 	index_sz = (statbuf.st_size / segsize) + 2;
141787117650Saalok 	index = malloc(sizeof (*index) * index_sz);
141887117650Saalok 
141987117650Saalok 	if (index == NULL)
142087117650Saalok 		goto cleanup;
142187117650Saalok 
142287117650Saalok 	offset = 0;
142387117650Saalok 	lastread = segsize;
142487117650Saalok 	count = 0;
142587117650Saalok 
142687117650Saalok 	/*
142787117650Saalok 	 * Now read from the uncompressed file in 'segsize'
142887117650Saalok 	 * sized chunks, compress what was read in and
142987117650Saalok 	 * write it out to a temporary file
143087117650Saalok 	 */
143187117650Saalok 	for (;;) {
143287117650Saalok 		rbytes = read(uncompfd, uncompressed_seg, segsize);
143387117650Saalok 
143487117650Saalok 		if (rbytes <= 0)
143587117650Saalok 			break;
143687117650Saalok 
143787117650Saalok 		if (lastread < segsize)
143887117650Saalok 			goto cleanup;
143987117650Saalok 
144087117650Saalok 		/*
144187117650Saalok 		 * Account for the first byte that
144287117650Saalok 		 * indicates whether a segment is
144387117650Saalok 		 * compressed or not
144487117650Saalok 		 */
144587117650Saalok 		real_segsize = segsize - 1;
144687117650Saalok 		(void) li->l_compress(uncompressed_seg, rbytes,
144787117650Saalok 		    compressed_seg + SEGHDR, &real_segsize, li->l_level);
144887117650Saalok 
144987117650Saalok 		/*
145087117650Saalok 		 * If the length of the compressed data is more
145187117650Saalok 		 * than a threshold then there isn't any benefit
145287117650Saalok 		 * to be had from compressing this segment - leave
145387117650Saalok 		 * it uncompressed.
145487117650Saalok 		 *
145587117650Saalok 		 * NB. In case an error occurs during compression (above)
145687117650Saalok 		 * the 'real_segsize' isn't changed. The logic below
145787117650Saalok 		 * ensures that that segment is left uncompressed.
145887117650Saalok 		 */
145987117650Saalok 		len_compressed = real_segsize;
146087117650Saalok 		if (real_segsize > segsize - COMPRESS_THRESHOLD) {
146187117650Saalok 			(void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
146287117650Saalok 			    rbytes);
146387117650Saalok 			type = UNCOMPRESSED;
146487117650Saalok 			len_compressed = rbytes;
146587117650Saalok 		} else {
146687117650Saalok 			type = COMPRESSED;
146787117650Saalok 		}
146887117650Saalok 
146987117650Saalok 		/*
147087117650Saalok 		 * Set the first byte or the SEGHDR to
147187117650Saalok 		 * indicate if it's compressed or not
147287117650Saalok 		 */
147387117650Saalok 		*compressed_seg = type;
147487117650Saalok 		wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
147587117650Saalok 		if (wbytes != (len_compressed + SEGHDR)) {
147687117650Saalok 			rbytes = -1;
147787117650Saalok 			break;
147887117650Saalok 		}
147987117650Saalok 
148087117650Saalok 		index[count] = BE_64(offset);
148187117650Saalok 		offset += wbytes;
148287117650Saalok 		lastread = rbytes;
148387117650Saalok 		count++;
148487117650Saalok 	}
148587117650Saalok 
148687117650Saalok 	(void) close(uncompfd);
148787117650Saalok 
148887117650Saalok 	if (rbytes < 0)
148987117650Saalok 		goto cleanup;
149087117650Saalok 	/*
149187117650Saalok 	 * The last index entry is a sentinel entry. It does not point to
149287117650Saalok 	 * an actual compressed segment but helps in computing the size of
149387117650Saalok 	 * the compressed segment. The size of each compressed segment is
149487117650Saalok 	 * computed by subtracting the current index value from the next
149587117650Saalok 	 * one (the compressed blocks are stored sequentially)
149687117650Saalok 	 */
149787117650Saalok 	index[count++] = BE_64(offset);
149887117650Saalok 
149987117650Saalok 	/*
150087117650Saalok 	 * Now write the compressed data along with the
150187117650Saalok 	 * header information to this file which will
150287117650Saalok 	 * later be renamed to the original uncompressed
150387117650Saalok 	 * file name
150487117650Saalok 	 *
150587117650Saalok 	 * The header is as follows -
150687117650Saalok 	 *
150787117650Saalok 	 * Signature (name of the compression algorithm)
150887117650Saalok 	 * Compression segment size (a multiple of 512)
150987117650Saalok 	 * Number of index entries
151087117650Saalok 	 * Size of the last block
151187117650Saalok 	 * The array containing the index entries
151287117650Saalok 	 *
151387117650Saalok 	 * the header is always stored in network byte
151487117650Saalok 	 * order
151587117650Saalok 	 */
151687117650Saalok 	(void) bzero(algorithm, sizeof (algorithm));
151787117650Saalok 	(void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
151887117650Saalok 	if (write(compfd, algorithm, sizeof (algorithm))
151987117650Saalok 	    != sizeof (algorithm))
152087117650Saalok 		goto cleanup;
152187117650Saalok 
152287117650Saalok 	segsize = htonl(segsize);
152387117650Saalok 	if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
152487117650Saalok 		goto cleanup;
152587117650Saalok 
152687117650Saalok 	index_entries = htonl(count);
152787117650Saalok 	if (write(compfd, &index_entries, sizeof (index_entries)) !=
152887117650Saalok 	    sizeof (index_entries))
152987117650Saalok 		goto cleanup;
153087117650Saalok 
153187117650Saalok 	lastread = htonl(lastread);
153287117650Saalok 	if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
153387117650Saalok 		goto cleanup;
153487117650Saalok 
153587117650Saalok 	for (i = 0; i < count; i++) {
153687117650Saalok 		if (write(compfd, index + i, sizeof (*index)) !=
153787117650Saalok 		    sizeof (*index))
153887117650Saalok 			goto cleanup;
153987117650Saalok 	}
154087117650Saalok 
154187117650Saalok 	/* Header is written, now write the compressed data */
154287117650Saalok 	if (lseek(tfd, 0, SEEK_SET) != 0)
154387117650Saalok 		goto cleanup;
154487117650Saalok 
154587117650Saalok 	rbytes = wbytes = 0;
154687117650Saalok 
154787117650Saalok 	for (;;) {
154887117650Saalok 		rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
154987117650Saalok 
155087117650Saalok 		if (rbytes <= 0)
155187117650Saalok 			break;
155287117650Saalok 
155387117650Saalok 		if (write(compfd, compressed_seg, rbytes) != rbytes)
155487117650Saalok 			goto cleanup;
155587117650Saalok 	}
155687117650Saalok 
155787117650Saalok 	if (fstat64(compfd, &statbuf) == -1)
155887117650Saalok 		goto cleanup;
155987117650Saalok 
156087117650Saalok 	/*
156187117650Saalok 	 * Round up the compressed file size to be a multiple of
156287117650Saalok 	 * DEV_BSIZE. lofi(7D) likes it that way.
156387117650Saalok 	 */
156487117650Saalok 	if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
156587117650Saalok 
156687117650Saalok 		offset = DEV_BSIZE - offset;
156787117650Saalok 
156887117650Saalok 		for (i = 0; i < offset; i++)
156987117650Saalok 			uncompressed_seg[i] = '\0';
157087117650Saalok 		if (write(compfd, uncompressed_seg, offset) != offset)
157187117650Saalok 			goto cleanup;
157287117650Saalok 	}
157387117650Saalok 	(void) close(compfd);
157487117650Saalok 	(void) close(tfd);
157587117650Saalok 	(void) unlink(tmpfilename);
157687117650Saalok cleanup:
157787117650Saalok 	if (rbytes < 0) {
157887117650Saalok 		if (tfd != -1)
157987117650Saalok 			(void) unlink(tmpfilename);
158087117650Saalok 		if (compfd != -1)
158187117650Saalok 			(void) unlink(comp_filename);
158287117650Saalok 		die(gettext("error compressing file %s"), filename);
158387117650Saalok 	} else {
158487117650Saalok 		/* Rename the compressed file to the actual file */
158587117650Saalok 		if (rename(comp_filename, filename) == -1) {
158687117650Saalok 			(void) unlink(comp_filename);
158787117650Saalok 			die(gettext("error compressing file %s"), filename);
158887117650Saalok 		}
158987117650Saalok 	}
159087117650Saalok 	if (compressed_seg != NULL)
159187117650Saalok 		free(compressed_seg);
159287117650Saalok 	if (uncompressed_seg != NULL)
159387117650Saalok 		free(uncompressed_seg);
159487117650Saalok 	if (index != NULL)
159587117650Saalok 		free(index);
159687117650Saalok 	if (compfd != -1)
159787117650Saalok 		(void) close(compfd);
159887117650Saalok 	if (uncompfd != -1)
159987117650Saalok 		(void) close(uncompfd);
160087117650Saalok 	if (tfd != -1)
160187117650Saalok 		(void) close(tfd);
160287117650Saalok }
160387117650Saalok 
160487117650Saalok static int
160587117650Saalok lofi_compress_select(const char *algname)
160687117650Saalok {
160787117650Saalok 	int i;
160887117650Saalok 
160987117650Saalok 	for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
161087117650Saalok 		if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
161187117650Saalok 			return (i);
161287117650Saalok 	}
161387117650Saalok 	return (-1);
161487117650Saalok }
161587117650Saalok 
161687117650Saalok static void
161787117650Saalok check_algorithm_validity(const char *algname, int *compress_index)
161887117650Saalok {
161987117650Saalok 	*compress_index = lofi_compress_select(algname);
162087117650Saalok 	if (*compress_index < 0)
162187117650Saalok 		die(gettext("invalid algorithm name: %s\n"), algname);
162287117650Saalok }
162387117650Saalok 
162487117650Saalok static void
162587117650Saalok check_file_validity(const char *filename)
162687117650Saalok {
16277c478bd9Sstevel@tonic-gate 	struct stat64 buf;
162887117650Saalok 	int 	error;
1629*7d82f0f8SDina K Nimeh 	int	fd;
16307c478bd9Sstevel@tonic-gate 
16317c478bd9Sstevel@tonic-gate 	fd = open64(filename, O_RDONLY);
16327c478bd9Sstevel@tonic-gate 	if (fd == -1) {
16337c478bd9Sstevel@tonic-gate 		die(gettext("open: %s"), filename);
16347c478bd9Sstevel@tonic-gate 	}
16357c478bd9Sstevel@tonic-gate 	error = fstat64(fd, &buf);
16367c478bd9Sstevel@tonic-gate 	if (error == -1) {
16377c478bd9Sstevel@tonic-gate 		die(gettext("fstat: %s"), filename);
16387c478bd9Sstevel@tonic-gate 	} else if (!S_ISLOFIABLE(buf.st_mode)) {
16397c478bd9Sstevel@tonic-gate 		die(gettext("%s is not a regular file, "
16407c478bd9Sstevel@tonic-gate 		    "block, or character device\n"),
16417c478bd9Sstevel@tonic-gate 		    filename);
16427c478bd9Sstevel@tonic-gate 	} else if ((buf.st_size % DEV_BSIZE) != 0) {
1643f9153c6bSDina K Nimeh 		die(gettext("size of %s is not a multiple of %d\n"),
16447c478bd9Sstevel@tonic-gate 		    filename, DEV_BSIZE);
16457c478bd9Sstevel@tonic-gate 	}
16467c478bd9Sstevel@tonic-gate 	(void) close(fd);
164787117650Saalok 
164887117650Saalok 	if (name_to_minor(filename) != 0) {
1649f9153c6bSDina K Nimeh 		die(gettext("cannot use %s on itself\n"), LOFI_DRIVER_NAME);
16507c478bd9Sstevel@tonic-gate 	}
165187117650Saalok }
165287117650Saalok 
165387117650Saalok static uint32_t
165487117650Saalok convert_to_num(const char *str)
165587117650Saalok {
165687117650Saalok 	int len;
165787117650Saalok 	uint32_t segsize, mult = 1;
165887117650Saalok 
165987117650Saalok 	len = strlen(str);
166087117650Saalok 	if (len && isalpha(str[len - 1])) {
166187117650Saalok 		switch (str[len - 1]) {
166287117650Saalok 		case 'k':
166387117650Saalok 		case 'K':
166487117650Saalok 			mult = KILOBYTE;
166587117650Saalok 			break;
166687117650Saalok 		case 'b':
166787117650Saalok 		case 'B':
166887117650Saalok 			mult = BLOCK_SIZE;
166987117650Saalok 			break;
167087117650Saalok 		case 'm':
167187117650Saalok 		case 'M':
167287117650Saalok 			mult = MEGABYTE;
167387117650Saalok 			break;
167487117650Saalok 		case 'g':
167587117650Saalok 		case 'G':
167687117650Saalok 			mult = GIGABYTE;
167787117650Saalok 			break;
167887117650Saalok 		default:
167987117650Saalok 			die(gettext("invalid segment size %s\n"), str);
168087117650Saalok 		}
168187117650Saalok 	}
168287117650Saalok 
168387117650Saalok 	segsize = atol(str);
168487117650Saalok 	segsize *= mult;
168587117650Saalok 
168687117650Saalok 	return (segsize);
168787117650Saalok }
168887117650Saalok 
168987117650Saalok int
169087117650Saalok main(int argc, char *argv[])
169187117650Saalok {
169287117650Saalok 	int	lfd;
169387117650Saalok 	int	c;
169487117650Saalok 	const char *devicename = NULL;
169587117650Saalok 	const char *filename = NULL;
169687117650Saalok 	const char *algname = COMPRESS_ALGORITHM;
169787117650Saalok 	int	openflag;
169887117650Saalok 	int	minor;
169987117650Saalok 	int 	compress_index;
170087117650Saalok 	uint32_t segsize = SEGSIZE;
170187117650Saalok 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
170287117650Saalok 	boolean_t force = B_FALSE;
1703*7d82f0f8SDina K Nimeh 	const char *pname;
1704*7d82f0f8SDina K Nimeh 	boolean_t errflag = B_FALSE;
1705*7d82f0f8SDina K Nimeh 	boolean_t addflag = B_FALSE;
1706*7d82f0f8SDina K Nimeh 	boolean_t deleteflag = B_FALSE;
1707*7d82f0f8SDina K Nimeh 	boolean_t ephflag = B_FALSE;
1708*7d82f0f8SDina K Nimeh 	boolean_t compressflag = B_FALSE;
1709*7d82f0f8SDina K Nimeh 	boolean_t uncompressflag = B_FALSE;
1710*7d82f0f8SDina K Nimeh 	/* the next two work together for -c, -k, -T, -e options only */
1711*7d82f0f8SDina K Nimeh 	boolean_t need_crypto = B_FALSE;	/* if any -c, -k, -T, -e */
1712*7d82f0f8SDina K Nimeh 	boolean_t cipher_only = B_TRUE;		/* if -c only */
1713*7d82f0f8SDina K Nimeh 	const char *keyfile = NULL;
1714*7d82f0f8SDina K Nimeh 	mech_alias_t *cipher = NULL;
1715*7d82f0f8SDina K Nimeh 	token_spec_t *token = NULL;
1716*7d82f0f8SDina K Nimeh 	char	*rkey = NULL;
1717*7d82f0f8SDina K Nimeh 	size_t	rksz = 0;
1718f9153c6bSDina K Nimeh 	char realfilename[MAXPATHLEN];
171987117650Saalok 
172087117650Saalok 	pname = getpname(argv[0]);
172187117650Saalok 
172287117650Saalok 	(void) setlocale(LC_ALL, "");
172387117650Saalok 	(void) textdomain(TEXT_DOMAIN);
172487117650Saalok 
1725*7d82f0f8SDina K Nimeh 	while ((c = getopt(argc, argv, "a:c:Cd:efk:o:s:T:U")) != EOF) {
172687117650Saalok 		switch (c) {
172787117650Saalok 		case 'a':
1728*7d82f0f8SDina K Nimeh 			addflag = B_TRUE;
1729f9153c6bSDina K Nimeh 			if ((filename = realpath(optarg, realfilename)) == NULL)
1730f9153c6bSDina K Nimeh 				die("%s", optarg);
17317c478bd9Sstevel@tonic-gate 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
17327c478bd9Sstevel@tonic-gate 				/* optional device */
17337c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
17347c478bd9Sstevel@tonic-gate 				optind++;
17357c478bd9Sstevel@tonic-gate 			}
17367c478bd9Sstevel@tonic-gate 			break;
173787117650Saalok 		case 'C':
1738*7d82f0f8SDina K Nimeh 			compressflag = B_TRUE;
1739*7d82f0f8SDina K Nimeh 			if (((argc - optind) > 1) && (*argv[optind] != '-')) {
1740*7d82f0f8SDina K Nimeh 				/* optional algorithm */
1741*7d82f0f8SDina K Nimeh 				algname = argv[optind];
174287117650Saalok 				optind++;
174387117650Saalok 			}
174487117650Saalok 			check_algorithm_validity(algname, &compress_index);
174587117650Saalok 			break;
1746*7d82f0f8SDina K Nimeh 		case 'c':
1747*7d82f0f8SDina K Nimeh 			/* is the chosen cipher allowed? */
1748*7d82f0f8SDina K Nimeh 			if ((cipher = ciph2mech(optarg)) == NULL) {
1749*7d82f0f8SDina K Nimeh 				errflag = B_TRUE;
1750*7d82f0f8SDina K Nimeh 				warn(gettext("cipher %s not allowed\n"),
1751*7d82f0f8SDina K Nimeh 				    optarg);
1752*7d82f0f8SDina K Nimeh 			}
1753*7d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
1754*7d82f0f8SDina K Nimeh 			/* cipher_only is already set */
1755*7d82f0f8SDina K Nimeh 			break;
17567c478bd9Sstevel@tonic-gate 		case 'd':
1757*7d82f0f8SDina K Nimeh 			deleteflag = B_TRUE;
17587c478bd9Sstevel@tonic-gate 			minor = name_to_minor(optarg);
17597c478bd9Sstevel@tonic-gate 			if (minor != 0)
17607c478bd9Sstevel@tonic-gate 				devicename = optarg;
1761f9153c6bSDina K Nimeh 			else {
1762f9153c6bSDina K Nimeh 				if ((filename = realpath(optarg,
1763f9153c6bSDina K Nimeh 				    realfilename)) == NULL)
1764f9153c6bSDina K Nimeh 					die("%s", optarg);
1765f9153c6bSDina K Nimeh 			}
17667c478bd9Sstevel@tonic-gate 			break;
1767*7d82f0f8SDina K Nimeh 		case 'e':
1768*7d82f0f8SDina K Nimeh 			ephflag = B_TRUE;
1769*7d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
1770*7d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
1771*7d82f0f8SDina K Nimeh 			break;
17723d7072f8Seschrock 		case 'f':
17733d7072f8Seschrock 			force = B_TRUE;
17743d7072f8Seschrock 			break;
1775*7d82f0f8SDina K Nimeh 		case 'k':
1776*7d82f0f8SDina K Nimeh 			keyfile = optarg;
1777*7d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
1778*7d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
1779*7d82f0f8SDina K Nimeh 			break;
178087117650Saalok 		case 's':
178187117650Saalok 			segsize = convert_to_num(optarg);
178287117650Saalok 			if (segsize == 0 || segsize % DEV_BSIZE)
178387117650Saalok 				die(gettext("segment size %s is invalid "
178487117650Saalok 				    "or not a multiple of minimum block "
178587117650Saalok 				    "size %ld\n"), optarg, DEV_BSIZE);
1786*7d82f0f8SDina K Nimeh 			break;
1787*7d82f0f8SDina K Nimeh 		case 'T':
1788*7d82f0f8SDina K Nimeh 			if ((token = parsetoken(optarg)) == NULL) {
1789*7d82f0f8SDina K Nimeh 				errflag = B_TRUE;
1790*7d82f0f8SDina K Nimeh 				warn(
1791*7d82f0f8SDina K Nimeh 				    gettext("invalid token key specifier %s\n"),
1792*7d82f0f8SDina K Nimeh 				    optarg);
1793*7d82f0f8SDina K Nimeh 			}
1794*7d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
1795*7d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
179687117650Saalok 			break;
179787117650Saalok 		case 'U':
1798*7d82f0f8SDina K Nimeh 			uncompressflag = B_TRUE;
179987117650Saalok 			break;
18007c478bd9Sstevel@tonic-gate 		case '?':
18017c478bd9Sstevel@tonic-gate 		default:
1802*7d82f0f8SDina K Nimeh 			errflag = B_TRUE;
18037c478bd9Sstevel@tonic-gate 			break;
18047c478bd9Sstevel@tonic-gate 		}
18057c478bd9Sstevel@tonic-gate 	}
1806*7d82f0f8SDina K Nimeh 
1807*7d82f0f8SDina K Nimeh 	/* Check for mutually exclusive combinations of options */
180887117650Saalok 	if (errflag ||
180987117650Saalok 	    (addflag && deleteflag) ||
1810*7d82f0f8SDina K Nimeh 	    (!addflag && need_crypto) ||
181187117650Saalok 	    ((compressflag || uncompressflag) && (addflag || deleteflag)))
1812*7d82f0f8SDina K Nimeh 		usage(pname);
1813*7d82f0f8SDina K Nimeh 
1814*7d82f0f8SDina K Nimeh 	/* ephemeral key, and key from either file or token are incompatible */
1815*7d82f0f8SDina K Nimeh 	if (ephflag && (keyfile != NULL || token != NULL)) {
1816*7d82f0f8SDina K Nimeh 		die(gettext("ephemeral key cannot be used with keyfile"
1817*7d82f0f8SDina K Nimeh 		    " or token key\n"));
1818*7d82f0f8SDina K Nimeh 	}
1819*7d82f0f8SDina K Nimeh 
1820*7d82f0f8SDina K Nimeh 	/*
1821*7d82f0f8SDina K Nimeh 	 * "-c" but no "-k", "-T", "-e", or "-T -k" means derive key from
1822*7d82f0f8SDina K Nimeh 	 * command line passphrase
1823*7d82f0f8SDina K Nimeh 	 */
18247c478bd9Sstevel@tonic-gate 
18257c478bd9Sstevel@tonic-gate 	switch (argc - optind) {
18267c478bd9Sstevel@tonic-gate 	case 0: /* no more args */
1827*7d82f0f8SDina K Nimeh 		if (compressflag || uncompressflag)	/* needs filename */
1828*7d82f0f8SDina K Nimeh 			usage(pname);
18297c478bd9Sstevel@tonic-gate 		break;
1830*7d82f0f8SDina K Nimeh 	case 1:
18317c478bd9Sstevel@tonic-gate 		if (addflag || deleteflag)
1832*7d82f0f8SDina K Nimeh 			usage(pname);
1833*7d82f0f8SDina K Nimeh 		/* one arg means compress/uncompress the file ... */
1834*7d82f0f8SDina K Nimeh 		if (compressflag || uncompressflag) {
1835*7d82f0f8SDina K Nimeh 			if ((filename = realpath(argv[optind],
1836*7d82f0f8SDina K Nimeh 			    realfilename)) == NULL)
1837*7d82f0f8SDina K Nimeh 				die("%s", argv[optind]);
1838*7d82f0f8SDina K Nimeh 		/* ... or without options means print the association */
1839*7d82f0f8SDina K Nimeh 		} else {
18407c478bd9Sstevel@tonic-gate 			minor = name_to_minor(argv[optind]);
18417c478bd9Sstevel@tonic-gate 			if (minor != 0)
18427c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
1843f9153c6bSDina K Nimeh 			else {
1844f9153c6bSDina K Nimeh 				if ((filename = realpath(argv[optind],
1845f9153c6bSDina K Nimeh 				    realfilename)) == NULL)
1846f9153c6bSDina K Nimeh 					die("%s", argv[optind]);
1847f9153c6bSDina K Nimeh 			}
1848*7d82f0f8SDina K Nimeh 		}
18497c478bd9Sstevel@tonic-gate 		break;
18507c478bd9Sstevel@tonic-gate 	default:
1851*7d82f0f8SDina K Nimeh 		usage(pname);
18527c478bd9Sstevel@tonic-gate 		break;
18537c478bd9Sstevel@tonic-gate 	}
18547c478bd9Sstevel@tonic-gate 
1855*7d82f0f8SDina K Nimeh 	if (addflag || compressflag || uncompressflag)
1856*7d82f0f8SDina K Nimeh 		check_file_validity(filename);
1857*7d82f0f8SDina K Nimeh 
18587c478bd9Sstevel@tonic-gate 	if (filename && !valid_abspath(filename))
18597c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
18607c478bd9Sstevel@tonic-gate 
18617c478bd9Sstevel@tonic-gate 	/*
18627c478bd9Sstevel@tonic-gate 	 * Here, we know the arguments are correct, the filename is an
18637c478bd9Sstevel@tonic-gate 	 * absolute path, it exists and is a regular file. We don't yet
18647c478bd9Sstevel@tonic-gate 	 * know that the device name is ok or not.
18657c478bd9Sstevel@tonic-gate 	 */
1866*7d82f0f8SDina K Nimeh 
18677c478bd9Sstevel@tonic-gate 	openflag = O_EXCL;
186887117650Saalok 	if (addflag || deleteflag || compressflag || uncompressflag)
18697c478bd9Sstevel@tonic-gate 		openflag |= O_RDWR;
18707c478bd9Sstevel@tonic-gate 	else
18717c478bd9Sstevel@tonic-gate 		openflag |= O_RDONLY;
18727c478bd9Sstevel@tonic-gate 	lfd = open(lofictl, openflag);
18737c478bd9Sstevel@tonic-gate 	if (lfd == -1) {
18747c478bd9Sstevel@tonic-gate 		if ((errno == EPERM) || (errno == EACCES)) {
1875f9153c6bSDina K Nimeh 			die(gettext("you do not have permission to perform "
1876f9153c6bSDina K Nimeh 			    "that operation.\n"));
18777c478bd9Sstevel@tonic-gate 		} else {
1878f9153c6bSDina K Nimeh 			die(gettext("open: %s"), lofictl);
18797c478bd9Sstevel@tonic-gate 		}
18807c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
18817c478bd9Sstevel@tonic-gate 	}
1882*7d82f0f8SDina K Nimeh 
1883*7d82f0f8SDina K Nimeh 	/*
1884*7d82f0f8SDina K Nimeh 	 * No passphrase is needed for ephemeral key, or when key is
1885*7d82f0f8SDina K Nimeh 	 * in a file and not wrapped by another key from a token.
1886*7d82f0f8SDina K Nimeh 	 * However, a passphrase is needed in these cases:
1887*7d82f0f8SDina K Nimeh 	 * 1. cipher with no ephemeral key, key file, or token,
1888*7d82f0f8SDina K Nimeh 	 *    in which case the passphrase is used to build the key
1889*7d82f0f8SDina K Nimeh 	 * 2. token with an optional cipher or optional key file,
1890*7d82f0f8SDina K Nimeh 	 *    in which case the passphrase unlocks the token
1891*7d82f0f8SDina K Nimeh 	 * If only the cipher is specified, reconfirm the passphrase
1892*7d82f0f8SDina K Nimeh 	 * to ensure the user hasn't mis-entered it.  Otherwise, the
1893*7d82f0f8SDina K Nimeh 	 * token will enforce the token passphrase.
1894*7d82f0f8SDina K Nimeh 	 */
1895*7d82f0f8SDina K Nimeh 	if (need_crypto) {
1896*7d82f0f8SDina K Nimeh 		CK_SESSION_HANDLE	sess;
1897*7d82f0f8SDina K Nimeh 
1898*7d82f0f8SDina K Nimeh 		/* pick a cipher if none specified */
1899*7d82f0f8SDina K Nimeh 		if (cipher == NULL)
1900*7d82f0f8SDina K Nimeh 			cipher = DEFAULT_CIPHER;
1901*7d82f0f8SDina K Nimeh 
1902*7d82f0f8SDina K Nimeh 		if (!kernel_cipher_check(cipher))
1903*7d82f0f8SDina K Nimeh 			die(gettext(
1904*7d82f0f8SDina K Nimeh 			    "use \"cryptoadm list -m\" to find available "
1905*7d82f0f8SDina K Nimeh 			    "mechanisms\n"));
1906*7d82f0f8SDina K Nimeh 
1907*7d82f0f8SDina K Nimeh 		init_crypto(token, cipher, &sess);
1908*7d82f0f8SDina K Nimeh 
1909*7d82f0f8SDina K Nimeh 		if (cipher_only) {
1910*7d82f0f8SDina K Nimeh 			getkeyfromuser(cipher, &rkey, &rksz);
1911*7d82f0f8SDina K Nimeh 		} else if (token != NULL) {
1912*7d82f0f8SDina K Nimeh 			getkeyfromtoken(sess, token, keyfile, cipher,
1913*7d82f0f8SDina K Nimeh 			    &rkey, &rksz);
1914*7d82f0f8SDina K Nimeh 		} else {
1915*7d82f0f8SDina K Nimeh 			/* this also handles ephemeral keys */
1916*7d82f0f8SDina K Nimeh 			getkeyfromfile(keyfile, cipher, &rkey, &rksz);
1917*7d82f0f8SDina K Nimeh 		}
1918*7d82f0f8SDina K Nimeh 
1919*7d82f0f8SDina K Nimeh 		end_crypto(sess);
1920*7d82f0f8SDina K Nimeh 	}
1921*7d82f0f8SDina K Nimeh 
1922*7d82f0f8SDina K Nimeh 	/*
1923*7d82f0f8SDina K Nimeh 	 * Now to the real work.
1924*7d82f0f8SDina K Nimeh 	 */
19257c478bd9Sstevel@tonic-gate 	if (addflag)
1926*7d82f0f8SDina K Nimeh 		add_mapping(lfd, devicename, filename, cipher, rkey, rksz);
192787117650Saalok 	else if (compressflag)
19289525893dSaalok 		lofi_compress(&lfd, filename, compress_index, segsize);
192987117650Saalok 	else if (uncompressflag)
193087117650Saalok 		lofi_uncompress(lfd, filename);
19317c478bd9Sstevel@tonic-gate 	else if (deleteflag)
19323d7072f8Seschrock 		delete_mapping(lfd, devicename, filename, force);
19337c478bd9Sstevel@tonic-gate 	else if (filename || devicename)
19347c478bd9Sstevel@tonic-gate 		print_one_mapping(lfd, devicename, filename);
19357c478bd9Sstevel@tonic-gate 	else
19367c478bd9Sstevel@tonic-gate 		print_mappings(lfd);
1937579df0adSaalok 
19389525893dSaalok 	if (lfd != -1)
19397c478bd9Sstevel@tonic-gate 		(void) close(lfd);
1940579df0adSaalok 	closelib();
19417c478bd9Sstevel@tonic-gate 	return (E_SUCCESS);
19427c478bd9Sstevel@tonic-gate }
1943