xref: /titanic_52/usr/src/cmd/lofiadm/main.c (revision b1efbcd6740f24e4bce347c64e48d9b74b472c67)
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 /*
22*b1efbcd6SAlok Aggarwal  * Copyright 2009 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>
517d82f0f8SDina K Nimeh #include <limits.h>
527d82f0f8SDina K Nimeh #include <security/cryptoki.h>
537d82f0f8SDina K Nimeh #include <cryptoutil.h>
547d82f0f8SDina K Nimeh #include <sys/crypto/ioctl.h>
557d82f0f8SDina K Nimeh #include <sys/crypto/ioctladmin.h>
567c478bd9Sstevel@tonic-gate #include "utils.h"
57*b1efbcd6SAlok Aggarwal #include <LzmaEnc.h>
587c478bd9Sstevel@tonic-gate 
597d82f0f8SDina K Nimeh /* Only need the IV len #defines out of these files, nothing else. */
607d82f0f8SDina K Nimeh #include <aes/aes_impl.h>
617d82f0f8SDina K Nimeh #include <des/des_impl.h>
627d82f0f8SDina K Nimeh #include <blowfish/blowfish_impl.h>
637d82f0f8SDina K Nimeh 
647c478bd9Sstevel@tonic-gate static const char USAGE[] =
657d82f0f8SDina K Nimeh 	"Usage: %s -a file [ device ] "
667d82f0f8SDina K Nimeh 	" [-c aes-128-cbc|aes-192-cbc|aes-256-cbc|des3-cbc|blowfish-cbc]"
677d82f0f8SDina K Nimeh 	" [-e] [-k keyfile] [-T [token]:[manuf]:[serial]:key]\n"
687c478bd9Sstevel@tonic-gate 	"       %s -d file | device\n"
69*b1efbcd6SAlok Aggarwal 	"       %s -C [gzip|gzip-6|gzip-9|lzma] [-s segment_size] file\n"
7087117650Saalok 	"       %s -U file\n"
71f9153c6bSDina K Nimeh 	"       %s [ file | device ]\n";
727c478bd9Sstevel@tonic-gate 
737d82f0f8SDina K Nimeh typedef struct token_spec {
747d82f0f8SDina K Nimeh 	char	*name;
757d82f0f8SDina K Nimeh 	char	*mfr;
767d82f0f8SDina K Nimeh 	char	*serno;
777d82f0f8SDina K Nimeh 	char	*key;
787d82f0f8SDina K Nimeh } token_spec_t;
797d82f0f8SDina K Nimeh 
807d82f0f8SDina K Nimeh typedef struct mech_alias {
817d82f0f8SDina K Nimeh 	char	*alias;
827d82f0f8SDina K Nimeh 	CK_MECHANISM_TYPE type;
837d82f0f8SDina K Nimeh 	char	*name;		/* for ioctl */
847d82f0f8SDina K Nimeh 	char	*iv_name;	/* for ioctl */
857d82f0f8SDina K Nimeh 	size_t	iv_len;		/* for ioctl */
867d82f0f8SDina K Nimeh 	iv_method_t iv_type;	/* for ioctl */
877d82f0f8SDina K Nimeh 	size_t	min_keysize;	/* in bytes */
887d82f0f8SDina K Nimeh 	size_t	max_keysize;	/* in bytes */
897d82f0f8SDina K Nimeh 	token_spec_t *token;
907d82f0f8SDina K Nimeh 	CK_SLOT_ID slot;
917d82f0f8SDina K Nimeh } mech_alias_t;
927d82f0f8SDina K Nimeh 
937d82f0f8SDina K Nimeh static mech_alias_t mech_aliases[] = {
947d82f0f8SDina K Nimeh 	/* Preferred one should always be listed first. */
957d82f0f8SDina K Nimeh 	{ "aes-256-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
967d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
977d82f0f8SDina K Nimeh 	{ "aes-192-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
987d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
997d82f0f8SDina K Nimeh 	{ "aes-128-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
1007d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
1017d82f0f8SDina K Nimeh 	{ "des3-cbc", CKM_DES3_CBC, "CKM_DES3_CBC", "CKM_DES3_ECB", DES_IV_LEN,
1027d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID)-1 },
1037d82f0f8SDina K Nimeh 	{ "blowfish-cbc", CKM_BLOWFISH_CBC, "CKM_BLOWFISH_CBC",
1047d82f0f8SDina K Nimeh 	    "CKM_BLOWFISH_ECB", BLOWFISH_IV_LEN, IVM_ENC_BLKNO, ULONG_MAX,
1057d82f0f8SDina K Nimeh 	    0L, NULL, (CK_SLOT_ID)-1 }
1067d82f0f8SDina K Nimeh 	/*
1077d82f0f8SDina K Nimeh 	 * A cipher without an iv requirement would look like this:
1087d82f0f8SDina K Nimeh 	 * { "aes-xex", CKM_AES_XEX, "CKM_AES_XEX", NULL, 0,
1097d82f0f8SDina K Nimeh 	 *    IVM_NONE, ULONG_MAX, 0L, NULL, (CK_SLOT_ID)-1 }
1107d82f0f8SDina K Nimeh 	 */
1117d82f0f8SDina K Nimeh };
1127d82f0f8SDina K Nimeh 
1137d82f0f8SDina K Nimeh int	mech_aliases_count = (sizeof (mech_aliases) / sizeof (mech_alias_t));
1147d82f0f8SDina K Nimeh 
1157d82f0f8SDina K Nimeh /* Preferred cipher, if one isn't specified on command line. */
1167d82f0f8SDina K Nimeh #define	DEFAULT_CIPHER	(&mech_aliases[0])
1177d82f0f8SDina K Nimeh 
1187d82f0f8SDina K Nimeh #define	DEFAULT_CIPHER_NUM	64	/* guess # kernel ciphers available */
1197d82f0f8SDina K Nimeh #define	DEFAULT_MECHINFO_NUM	16	/* guess # kernel mechs available */
1207d82f0f8SDina K Nimeh #define	MIN_PASSLEN		8	/* min acceptable passphrase size */
1217c478bd9Sstevel@tonic-gate 
12287117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
12387117650Saalok 	size_t *destlen, int level);
124*b1efbcd6SAlok Aggarwal static int lzma_compress(void *src, size_t srclen, void *dst,
125*b1efbcd6SAlok Aggarwal 	size_t *destlen, int level);
12687117650Saalok 
12787117650Saalok lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
12887117650Saalok 	{NULL,  		gzip_compress,  6,	"gzip"}, /* default */
12987117650Saalok 	{NULL,			gzip_compress,	6,	"gzip-6"},
130*b1efbcd6SAlok Aggarwal 	{NULL,			gzip_compress,	9, 	"gzip-9"},
131*b1efbcd6SAlok Aggarwal 	{NULL,  		lzma_compress, 	0, 	"lzma"}
13287117650Saalok };
13387117650Saalok 
1347d82f0f8SDina K Nimeh /* For displaying lofi mappings */
13587117650Saalok #define	FORMAT 			"%-20s     %-30s	%s\n"
1367d82f0f8SDina K Nimeh 
13787117650Saalok #define	COMPRESS_ALGORITHM	"gzip"
13887117650Saalok #define	COMPRESS_THRESHOLD	2048
13987117650Saalok #define	SEGSIZE			131072
14087117650Saalok #define	BLOCK_SIZE		512
14187117650Saalok #define	KILOBYTE		1024
14287117650Saalok #define	MEGABYTE		(KILOBYTE * KILOBYTE)
14387117650Saalok #define	GIGABYTE		(KILOBYTE * MEGABYTE)
144579df0adSaalok #define	LIBZ			"libz.so"
145579df0adSaalok 
146*b1efbcd6SAlok Aggarwal static void
147*b1efbcd6SAlok Aggarwal usage(const char *pname)
14887117650Saalok {
149*b1efbcd6SAlok Aggarwal 	(void) fprintf(stderr, gettext(USAGE), pname, pname, pname,
150*b1efbcd6SAlok Aggarwal 	    pname, pname);
151*b1efbcd6SAlok Aggarwal 	exit(E_USAGE);
152*b1efbcd6SAlok Aggarwal }
153*b1efbcd6SAlok Aggarwal 
154*b1efbcd6SAlok Aggarwal static int
155*b1efbcd6SAlok Aggarwal gzip_compress(void *src, size_t srclen, void *dst, size_t *dstlen, int level)
156*b1efbcd6SAlok Aggarwal {
157*b1efbcd6SAlok Aggarwal 	static int (*compress2p)(void *, ulong_t *, void *, size_t, int) = NULL;
158579df0adSaalok 	void *libz_hdl = NULL;
15987117650Saalok 
160579df0adSaalok 	/*
161579df0adSaalok 	 * The first time we are called, attempt to dlopen()
162579df0adSaalok 	 * libz.so and get a pointer to the compress2() function
163579df0adSaalok 	 */
164579df0adSaalok 	if (compress2p == NULL) {
165579df0adSaalok 		if ((libz_hdl = openlib(LIBZ)) == NULL)
166579df0adSaalok 			die(gettext("could not find %s. "
167579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
168579df0adSaalok 
169579df0adSaalok 		if ((compress2p =
170579df0adSaalok 		    (int (*)(void *, ulong_t *, void *, size_t, int))
171579df0adSaalok 		    dlsym(libz_hdl, "compress2")) == NULL) {
172579df0adSaalok 			closelib();
173579df0adSaalok 			die(gettext("could not find the correct %s. "
174579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
175579df0adSaalok 		}
176579df0adSaalok 	}
177579df0adSaalok 
178579df0adSaalok 	if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0)
179579df0adSaalok 		return (-1);
18087117650Saalok 	return (0);
18187117650Saalok }
1827c478bd9Sstevel@tonic-gate 
183*b1efbcd6SAlok Aggarwal /*ARGSUSED*/
1847c478bd9Sstevel@tonic-gate static void
185*b1efbcd6SAlok Aggarwal *SzAlloc(void *p, size_t size)
1867c478bd9Sstevel@tonic-gate {
187*b1efbcd6SAlok Aggarwal 	return (malloc(size));
188*b1efbcd6SAlok Aggarwal }
189*b1efbcd6SAlok Aggarwal 
190*b1efbcd6SAlok Aggarwal /*ARGSUSED*/
191*b1efbcd6SAlok Aggarwal static void
192*b1efbcd6SAlok Aggarwal SzFree(void *p, void *address, size_t size)
193*b1efbcd6SAlok Aggarwal {
194*b1efbcd6SAlok Aggarwal 	free(address);
195*b1efbcd6SAlok Aggarwal }
196*b1efbcd6SAlok Aggarwal 
197*b1efbcd6SAlok Aggarwal static ISzAlloc g_Alloc = {
198*b1efbcd6SAlok Aggarwal 	SzAlloc,
199*b1efbcd6SAlok Aggarwal 	SzFree
200*b1efbcd6SAlok Aggarwal };
201*b1efbcd6SAlok Aggarwal 
202*b1efbcd6SAlok Aggarwal #define	LZMA_UNCOMPRESSED_SIZE	8
203*b1efbcd6SAlok Aggarwal #define	LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMPRESSED_SIZE)
204*b1efbcd6SAlok Aggarwal 
205*b1efbcd6SAlok Aggarwal /*ARGSUSED*/
206*b1efbcd6SAlok Aggarwal static int
207*b1efbcd6SAlok Aggarwal lzma_compress(void *src, size_t srclen, void *dst,
208*b1efbcd6SAlok Aggarwal 	size_t *dstlen, int level)
209*b1efbcd6SAlok Aggarwal {
210*b1efbcd6SAlok Aggarwal 	CLzmaEncProps props;
211*b1efbcd6SAlok Aggarwal 	size_t outsize2;
212*b1efbcd6SAlok Aggarwal 	size_t outsizeprocessed;
213*b1efbcd6SAlok Aggarwal 	size_t outpropssize = LZMA_PROPS_SIZE;
214*b1efbcd6SAlok Aggarwal 	uint64_t t = 0;
215*b1efbcd6SAlok Aggarwal 	SRes res;
216*b1efbcd6SAlok Aggarwal 	Byte *dstp;
217*b1efbcd6SAlok Aggarwal 	int i;
218*b1efbcd6SAlok Aggarwal 
219*b1efbcd6SAlok Aggarwal 	outsize2 = *dstlen;
220*b1efbcd6SAlok Aggarwal 
221*b1efbcd6SAlok Aggarwal 	LzmaEncProps_Init(&props);
222*b1efbcd6SAlok Aggarwal 
223*b1efbcd6SAlok Aggarwal 	/*
224*b1efbcd6SAlok Aggarwal 	 * The LZMA compressed file format is as follows -
225*b1efbcd6SAlok Aggarwal 	 *
226*b1efbcd6SAlok Aggarwal 	 * Offset Size(bytes) Description
227*b1efbcd6SAlok Aggarwal 	 * 0		1	LZMA properties (lc, lp, lp (encoded))
228*b1efbcd6SAlok Aggarwal 	 * 1		4	Dictionary size (little endian)
229*b1efbcd6SAlok Aggarwal 	 * 5		8	Uncompressed size (little endian)
230*b1efbcd6SAlok Aggarwal 	 * 13			Compressed data
231*b1efbcd6SAlok Aggarwal 	 */
232*b1efbcd6SAlok Aggarwal 
233*b1efbcd6SAlok Aggarwal 	/* set the dictionary size to be 8MB */
234*b1efbcd6SAlok Aggarwal 	props.dictSize = 1 << 23;
235*b1efbcd6SAlok Aggarwal 
236*b1efbcd6SAlok Aggarwal 	if (*dstlen < LZMA_HEADER_SIZE)
237*b1efbcd6SAlok Aggarwal 		return (SZ_ERROR_OUTPUT_EOF);
238*b1efbcd6SAlok Aggarwal 
239*b1efbcd6SAlok Aggarwal 	dstp = (Byte *)dst;
240*b1efbcd6SAlok Aggarwal 	t = srclen;
241*b1efbcd6SAlok Aggarwal 	/*
242*b1efbcd6SAlok Aggarwal 	 * Set the uncompressed size in the LZMA header
243*b1efbcd6SAlok Aggarwal 	 * The LZMA properties (specified in 'props')
244*b1efbcd6SAlok Aggarwal 	 * will be set by the call to LzmaEncode()
245*b1efbcd6SAlok Aggarwal 	 */
246*b1efbcd6SAlok Aggarwal 	for (i = 0; i < LZMA_UNCOMPRESSED_SIZE; i++, t >>= 8) {
247*b1efbcd6SAlok Aggarwal 		dstp[LZMA_PROPS_SIZE + i] = (Byte)t;
248*b1efbcd6SAlok Aggarwal 	}
249*b1efbcd6SAlok Aggarwal 
250*b1efbcd6SAlok Aggarwal 	outsizeprocessed = outsize2 - LZMA_HEADER_SIZE;
251*b1efbcd6SAlok Aggarwal 	res = LzmaEncode(dstp + LZMA_HEADER_SIZE, &outsizeprocessed,
252*b1efbcd6SAlok Aggarwal 	    src, srclen, &props, dstp, &outpropssize, 0, NULL,
253*b1efbcd6SAlok Aggarwal 	    &g_Alloc, &g_Alloc);
254*b1efbcd6SAlok Aggarwal 
255*b1efbcd6SAlok Aggarwal 	if (res != 0)
256*b1efbcd6SAlok Aggarwal 		return (-1);
257*b1efbcd6SAlok Aggarwal 
258*b1efbcd6SAlok Aggarwal 	*dstlen = outsizeprocessed + LZMA_HEADER_SIZE;
259*b1efbcd6SAlok Aggarwal 	return (0);
2607c478bd9Sstevel@tonic-gate }
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate /*
2637c478bd9Sstevel@tonic-gate  * Translate a lofi device name to a minor number. We might be asked
2647c478bd9Sstevel@tonic-gate  * to do this when there is no association (such as when the user specifies
2657c478bd9Sstevel@tonic-gate  * a particular device), so we can only look at the string.
2667c478bd9Sstevel@tonic-gate  */
2677c478bd9Sstevel@tonic-gate static int
2687c478bd9Sstevel@tonic-gate name_to_minor(const char *devicename)
2697c478bd9Sstevel@tonic-gate {
2707c478bd9Sstevel@tonic-gate 	int	minor;
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
2737c478bd9Sstevel@tonic-gate 		return (minor);
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
2767c478bd9Sstevel@tonic-gate 		return (minor);
2777c478bd9Sstevel@tonic-gate 	}
2787c478bd9Sstevel@tonic-gate 	return (0);
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  * This might be the first time we've used this minor number. If so,
2837c478bd9Sstevel@tonic-gate  * it might also be that the /dev links are in the process of being created
2847c478bd9Sstevel@tonic-gate  * by devfsadmd (or that they'll be created "soon"). We cannot return
2857c478bd9Sstevel@tonic-gate  * until they're there or the invoker of lofiadm might try to use them
2867c478bd9Sstevel@tonic-gate  * and not find them. This can happen if a shell script is running on
2877c478bd9Sstevel@tonic-gate  * an MP.
2887c478bd9Sstevel@tonic-gate  */
2897c478bd9Sstevel@tonic-gate static int sleeptime = 2;	/* number of seconds to sleep between stat's */
2907c478bd9Sstevel@tonic-gate static int maxsleep = 120;	/* maximum number of seconds to sleep */
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate static void
2937c478bd9Sstevel@tonic-gate wait_until_dev_complete(int minor)
2947c478bd9Sstevel@tonic-gate {
2957c478bd9Sstevel@tonic-gate 	struct stat64 buf;
2967c478bd9Sstevel@tonic-gate 	int	cursleep;
29787117650Saalok 	char	blkpath[MAXPATHLEN];
29887117650Saalok 	char	charpath[MAXPATHLEN];
2991d8ed0f0Svikram 	di_devlink_handle_t hdl;
3001d8ed0f0Svikram 
3017c478bd9Sstevel@tonic-gate 	(void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
3027c478bd9Sstevel@tonic-gate 	    LOFI_BLOCK_NAME, minor);
3037c478bd9Sstevel@tonic-gate 	(void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
3047c478bd9Sstevel@tonic-gate 	    LOFI_CHAR_NAME, minor);
3051d8ed0f0Svikram 
3061d8ed0f0Svikram 	/* Check if links already present */
3071d8ed0f0Svikram 	if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
3081d8ed0f0Svikram 		return;
3091d8ed0f0Svikram 
3101d8ed0f0Svikram 	/* First use di_devlink_init() */
3111d8ed0f0Svikram 	if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
3121d8ed0f0Svikram 		(void) di_devlink_fini(&hdl);
3131d8ed0f0Svikram 		goto out;
3141d8ed0f0Svikram 	}
3151d8ed0f0Svikram 
3161d8ed0f0Svikram 	/*
3171d8ed0f0Svikram 	 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
3181d8ed0f0Svikram 	 * only fail if the caller is non-root. In that case, wait for
3191d8ed0f0Svikram 	 * link creation via sysevents.
3201d8ed0f0Svikram 	 */
3217d82f0f8SDina K Nimeh 	for (cursleep = 0; cursleep < maxsleep; cursleep += sleeptime) {
3227d82f0f8SDina K Nimeh 		if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
3237c478bd9Sstevel@tonic-gate 			return;
3247d82f0f8SDina K Nimeh 		(void) sleep(sleeptime);
3257c478bd9Sstevel@tonic-gate 	}
3261d8ed0f0Svikram 
3277c478bd9Sstevel@tonic-gate 	/* one last try */
3281d8ed0f0Svikram out:
3297c478bd9Sstevel@tonic-gate 	if (stat64(blkpath, &buf) == -1) {
3307c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), blkpath);
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 	if (stat64(charpath, &buf) == -1) {
3337c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), charpath);
3347c478bd9Sstevel@tonic-gate 	}
3357c478bd9Sstevel@tonic-gate }
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate /*
3387d82f0f8SDina K Nimeh  * Map the file and return the minor number the driver picked for the file
3397d82f0f8SDina K Nimeh  * DO NOT use this function if the filename is actually the device name.
3407d82f0f8SDina K Nimeh  */
3417d82f0f8SDina K Nimeh static int
3427d82f0f8SDina K Nimeh lofi_map_file(int lfd, struct lofi_ioctl li, const char *filename)
3437d82f0f8SDina K Nimeh {
3447d82f0f8SDina K Nimeh 	int	minor;
3457d82f0f8SDina K Nimeh 
3467d82f0f8SDina K Nimeh 	li.li_minor = 0;
3477d82f0f8SDina K Nimeh 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
3487d82f0f8SDina K Nimeh 	minor = ioctl(lfd, LOFI_MAP_FILE, &li);
3497d82f0f8SDina K Nimeh 	if (minor == -1) {
3507d82f0f8SDina K Nimeh 		if (errno == ENOTSUP)
3517d82f0f8SDina K Nimeh 			warn(gettext("encrypting compressed files is "
3527d82f0f8SDina K Nimeh 			    "unsupported"));
3537d82f0f8SDina K Nimeh 		die(gettext("could not map file %s"), filename);
3547d82f0f8SDina K Nimeh 	}
3557d82f0f8SDina K Nimeh 	wait_until_dev_complete(minor);
3567d82f0f8SDina K Nimeh 	return (minor);
3577d82f0f8SDina K Nimeh }
3587d82f0f8SDina K Nimeh 
3597d82f0f8SDina K Nimeh /*
3607c478bd9Sstevel@tonic-gate  * Add a device association. If devicename is NULL, let the driver
3617c478bd9Sstevel@tonic-gate  * pick a device.
3627c478bd9Sstevel@tonic-gate  */
3637c478bd9Sstevel@tonic-gate static void
36487117650Saalok add_mapping(int lfd, const char *devicename, const char *filename,
3657d82f0f8SDina K Nimeh     mech_alias_t *cipher, const char *rkey, size_t rksz)
3667c478bd9Sstevel@tonic-gate {
3677c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
3687d82f0f8SDina K Nimeh 
3697d82f0f8SDina K Nimeh 	li.li_crypto_enabled = B_FALSE;
3707d82f0f8SDina K Nimeh 	if (cipher != NULL) {
3717d82f0f8SDina K Nimeh 		/* set up encryption for mapped file */
3727d82f0f8SDina K Nimeh 		li.li_crypto_enabled = B_TRUE;
3737d82f0f8SDina K Nimeh 		(void) strlcpy(li.li_cipher, cipher->name,
3747d82f0f8SDina K Nimeh 		    sizeof (li.li_cipher));
3757d82f0f8SDina K Nimeh 		if (rksz > sizeof (li.li_key)) {
3767d82f0f8SDina K Nimeh 			die(gettext("key too large"));
3777d82f0f8SDina K Nimeh 		}
3787d82f0f8SDina K Nimeh 		bcopy(rkey, li.li_key, rksz);
3797d82f0f8SDina K Nimeh 		li.li_key_len = rksz << 3;	/* convert to bits */
3807d82f0f8SDina K Nimeh 
3817d82f0f8SDina K Nimeh 		li.li_iv_type = cipher->iv_type;
3827d82f0f8SDina K Nimeh 		li.li_iv_len = cipher->iv_len;	/* 0 when no iv needed */
3837d82f0f8SDina K Nimeh 		switch (cipher->iv_type) {
3847d82f0f8SDina K Nimeh 		case IVM_ENC_BLKNO:
3857d82f0f8SDina K Nimeh 			(void) strlcpy(li.li_iv_cipher, cipher->iv_name,
3867d82f0f8SDina K Nimeh 			    sizeof (li.li_iv_cipher));
3877d82f0f8SDina K Nimeh 			break;
3887d82f0f8SDina K Nimeh 		case IVM_NONE:
3897d82f0f8SDina K Nimeh 			/* FALLTHROUGH */
3907d82f0f8SDina K Nimeh 		default:
3917d82f0f8SDina K Nimeh 			break;
3927d82f0f8SDina K Nimeh 		}
3937d82f0f8SDina K Nimeh 	}
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
3967d82f0f8SDina K Nimeh 		int	minor;
39787117650Saalok 
3987d82f0f8SDina K Nimeh 		/* pick one via the driver */
3997d82f0f8SDina K Nimeh 		minor = lofi_map_file(lfd, li, filename);
4007d82f0f8SDina K Nimeh 		/* if mapping succeeds, print the one picked */
4017d82f0f8SDina K Nimeh 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor);
4027c478bd9Sstevel@tonic-gate 		return;
4037c478bd9Sstevel@tonic-gate 	}
4047d82f0f8SDina K Nimeh 
4057c478bd9Sstevel@tonic-gate 	/* use device we were given */
4067d82f0f8SDina K Nimeh 	li.li_minor = name_to_minor(devicename);
4077d82f0f8SDina K Nimeh 	if (li.li_minor == 0) {
4087c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
4097c478bd9Sstevel@tonic-gate 	}
41087117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
4117d82f0f8SDina K Nimeh 
4127d82f0f8SDina K Nimeh 	/* if device is already in use li.li_minor won't change */
4137c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
4147d82f0f8SDina K Nimeh 		if (errno == ENOTSUP)
4157d82f0f8SDina K Nimeh 			warn(gettext("encrypting compressed files is "
4167d82f0f8SDina K Nimeh 			    "unsupported"));
4177c478bd9Sstevel@tonic-gate 		die(gettext("could not map file %s to %s"), filename,
4187c478bd9Sstevel@tonic-gate 		    devicename);
4197c478bd9Sstevel@tonic-gate 	}
4207d82f0f8SDina K Nimeh 	wait_until_dev_complete(li.li_minor);
4217c478bd9Sstevel@tonic-gate }
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate /*
4247c478bd9Sstevel@tonic-gate  * Remove an association. Delete by device name if non-NULL, or by
4257c478bd9Sstevel@tonic-gate  * filename otherwise.
4267c478bd9Sstevel@tonic-gate  */
4277c478bd9Sstevel@tonic-gate static void
4283d7072f8Seschrock delete_mapping(int lfd, const char *devicename, const char *filename,
4293d7072f8Seschrock     boolean_t force)
4307c478bd9Sstevel@tonic-gate {
4317c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
4327c478bd9Sstevel@tonic-gate 
4333d7072f8Seschrock 	li.li_force = force;
43493239addSjohnlev 	li.li_cleanup = B_FALSE;
43593239addSjohnlev 
4367c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
4377c478bd9Sstevel@tonic-gate 		/* delete by filename */
43887117650Saalok 		(void) strlcpy(li.li_filename, filename,
43987117650Saalok 		    sizeof (li.li_filename));
4407c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
4417c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
4427c478bd9Sstevel@tonic-gate 			die(gettext("could not unmap file %s"), filename);
4437c478bd9Sstevel@tonic-gate 		}
4447c478bd9Sstevel@tonic-gate 		return;
4457c478bd9Sstevel@tonic-gate 	}
4467c478bd9Sstevel@tonic-gate 
447f9153c6bSDina K Nimeh 	/* delete by device */
4487c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
4497c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
4507c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
4517c478bd9Sstevel@tonic-gate 	}
4527c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
4537c478bd9Sstevel@tonic-gate 		die(gettext("could not unmap device %s"), devicename);
4547c478bd9Sstevel@tonic-gate 	}
4557c478bd9Sstevel@tonic-gate }
4567c478bd9Sstevel@tonic-gate 
4577d82f0f8SDina K Nimeh /*
4587d82f0f8SDina K Nimeh  * Show filename given devicename, or devicename given filename.
4597d82f0f8SDina K Nimeh  */
4607c478bd9Sstevel@tonic-gate static void
4617c478bd9Sstevel@tonic-gate print_one_mapping(int lfd, const char *devicename, const char *filename)
4627c478bd9Sstevel@tonic-gate {
4637c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
4667c478bd9Sstevel@tonic-gate 		/* given filename, print devicename */
4677c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
46887117650Saalok 		(void) strlcpy(li.li_filename, filename,
46987117650Saalok 		    sizeof (li.li_filename));
4707c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
4717c478bd9Sstevel@tonic-gate 			die(gettext("could not find device for %s"), filename);
4727c478bd9Sstevel@tonic-gate 		}
4737c478bd9Sstevel@tonic-gate 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
4747c478bd9Sstevel@tonic-gate 		return;
4757c478bd9Sstevel@tonic-gate 	}
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	/* given devicename, print filename */
4787c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
4797c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
4807c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
4817c478bd9Sstevel@tonic-gate 	}
4827c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
4837c478bd9Sstevel@tonic-gate 		die(gettext("could not find filename for %s"), devicename);
4847c478bd9Sstevel@tonic-gate 	}
4857c478bd9Sstevel@tonic-gate 	(void) printf("%s\n", li.li_filename);
4867c478bd9Sstevel@tonic-gate }
4877c478bd9Sstevel@tonic-gate 
48887117650Saalok /*
4897d82f0f8SDina K Nimeh  * Print the list of all the mappings, including a header.
4907d82f0f8SDina K Nimeh  */
4917d82f0f8SDina K Nimeh static void
4927d82f0f8SDina K Nimeh print_mappings(int fd)
4937d82f0f8SDina K Nimeh {
4947d82f0f8SDina K Nimeh 	struct lofi_ioctl li;
4957d82f0f8SDina K Nimeh 	int	minor;
4967d82f0f8SDina K Nimeh 	int	maxminor;
4977d82f0f8SDina K Nimeh 	char	path[MAXPATHLEN];
4987d82f0f8SDina K Nimeh 	char	options[MAXPATHLEN];
4997d82f0f8SDina K Nimeh 
5007d82f0f8SDina K Nimeh 	li.li_minor = 0;
5017d82f0f8SDina K Nimeh 	if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
5027d82f0f8SDina K Nimeh 		die("ioctl");
5037d82f0f8SDina K Nimeh 	}
5047d82f0f8SDina K Nimeh 	maxminor = li.li_minor;
5057d82f0f8SDina K Nimeh 
5067d82f0f8SDina K Nimeh 	(void) printf(FORMAT, gettext("Block Device"), gettext("File"),
5077d82f0f8SDina K Nimeh 	    gettext("Options"));
5087d82f0f8SDina K Nimeh 	for (minor = 1; minor <= maxminor; minor++) {
5097d82f0f8SDina K Nimeh 		li.li_minor = minor;
5107d82f0f8SDina K Nimeh 		if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
5117d82f0f8SDina K Nimeh 			if (errno == ENXIO)
5127d82f0f8SDina K Nimeh 				continue;
5137d82f0f8SDina K Nimeh 			warn("ioctl");
5147d82f0f8SDina K Nimeh 			break;
5157d82f0f8SDina K Nimeh 		}
5167d82f0f8SDina K Nimeh 		(void) snprintf(path, sizeof (path), "/dev/%s/%d",
5177d82f0f8SDina K Nimeh 		    LOFI_BLOCK_NAME, minor);
5187d82f0f8SDina K Nimeh 		/*
5197d82f0f8SDina K Nimeh 		 * Encrypted lofi and compressed lofi are mutually exclusive.
5207d82f0f8SDina K Nimeh 		 */
5217d82f0f8SDina K Nimeh 		if (li.li_crypto_enabled)
5227d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options),
5237d82f0f8SDina K Nimeh 			    gettext("Encrypted"));
5247d82f0f8SDina K Nimeh 		else if (li.li_algorithm[0] != '\0')
5257d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options),
5267d82f0f8SDina K Nimeh 			    gettext("Compressed(%s)"), li.li_algorithm);
5277d82f0f8SDina K Nimeh 		else
5287d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options), "-");
5297d82f0f8SDina K Nimeh 
5307d82f0f8SDina K Nimeh 		(void) printf(FORMAT, path, li.li_filename, options);
5317d82f0f8SDina K Nimeh 	}
5327d82f0f8SDina K Nimeh }
5337d82f0f8SDina K Nimeh 
5347d82f0f8SDina K Nimeh /*
5357d82f0f8SDina K Nimeh  * Verify the cipher selected by user.
5367d82f0f8SDina K Nimeh  */
5377d82f0f8SDina K Nimeh static mech_alias_t *
5387d82f0f8SDina K Nimeh ciph2mech(const char *alias)
5397d82f0f8SDina K Nimeh {
5407d82f0f8SDina K Nimeh 	int	i;
5417d82f0f8SDina K Nimeh 
5427d82f0f8SDina K Nimeh 	for (i = 0; i < mech_aliases_count; i++) {
5437d82f0f8SDina K Nimeh 		if (strcasecmp(alias, mech_aliases[i].alias) == 0)
5447d82f0f8SDina K Nimeh 			return (&mech_aliases[i]);
5457d82f0f8SDina K Nimeh 	}
5467d82f0f8SDina K Nimeh 	return (NULL);
5477d82f0f8SDina K Nimeh }
5487d82f0f8SDina K Nimeh 
5497d82f0f8SDina K Nimeh /*
5507d82f0f8SDina K Nimeh  * Verify user selected cipher is also available in kernel.
5517d82f0f8SDina K Nimeh  *
5527d82f0f8SDina K Nimeh  * While traversing kernel list of mechs, if the cipher is supported in the
5537d82f0f8SDina K Nimeh  * kernel for both encryption and decryption, it also picks up the min/max
5547d82f0f8SDina K Nimeh  * key size.
5557d82f0f8SDina K Nimeh  */
5567d82f0f8SDina K Nimeh static boolean_t
5577d82f0f8SDina K Nimeh kernel_cipher_check(mech_alias_t *cipher)
5587d82f0f8SDina K Nimeh {
5597d82f0f8SDina K Nimeh 	boolean_t ciph_ok = B_FALSE;
5607d82f0f8SDina K Nimeh 	boolean_t iv_ok = B_FALSE;
5617d82f0f8SDina K Nimeh 	int	i;
5627d82f0f8SDina K Nimeh 	int	count;
5637d82f0f8SDina K Nimeh 	crypto_get_mechanism_list_t *kciphers = NULL;
5647d82f0f8SDina K Nimeh 	crypto_get_all_mechanism_info_t *kinfo = NULL;
5657d82f0f8SDina K Nimeh 	int	fd = -1;
5667d82f0f8SDina K Nimeh 	size_t	keymin;
5677d82f0f8SDina K Nimeh 	size_t	keymax;
5687d82f0f8SDina K Nimeh 
5697d82f0f8SDina K Nimeh 	/* if cipher doesn't need iv generating mech, bypass that check now */
5707d82f0f8SDina K Nimeh 	if (cipher->iv_name == NULL)
5717d82f0f8SDina K Nimeh 		iv_ok = B_TRUE;
5727d82f0f8SDina K Nimeh 
5737d82f0f8SDina K Nimeh 	/* allocate some space for the list of kernel ciphers */
5747d82f0f8SDina K Nimeh 	count = DEFAULT_CIPHER_NUM;
5757d82f0f8SDina K Nimeh 	kciphers = malloc(sizeof (crypto_get_mechanism_list_t) +
5767d82f0f8SDina K Nimeh 	    sizeof (crypto_mech_name_t) * (count - 1));
5777d82f0f8SDina K Nimeh 	if (kciphers == NULL)
5787d82f0f8SDina K Nimeh 		die(gettext("failed to allocate memory for list of "
5797d82f0f8SDina K Nimeh 		    "kernel mechanisms"));
5807d82f0f8SDina K Nimeh 	kciphers->ml_count = count;
5817d82f0f8SDina K Nimeh 
5827d82f0f8SDina K Nimeh 	/* query crypto device to get list of kernel ciphers */
5837d82f0f8SDina K Nimeh 	if ((fd = open("/dev/crypto", O_RDWR)) == -1) {
5847d82f0f8SDina K Nimeh 		warn(gettext("failed to open %s"), "/dev/crypto");
5857d82f0f8SDina K Nimeh 		goto kcc_out;
5867d82f0f8SDina K Nimeh 	}
5877d82f0f8SDina K Nimeh 
5887d82f0f8SDina K Nimeh 	if (ioctl(fd, CRYPTO_GET_MECHANISM_LIST, kciphers) == -1) {
5897d82f0f8SDina K Nimeh 		warn(gettext("CRYPTO_GET_MECHANISM_LIST ioctl failed"));
5907d82f0f8SDina K Nimeh 		goto kcc_out;
5917d82f0f8SDina K Nimeh 	}
5927d82f0f8SDina K Nimeh 
5937d82f0f8SDina K Nimeh 	if (kciphers->ml_return_value == CRYPTO_BUFFER_TOO_SMALL) {
5947d82f0f8SDina K Nimeh 		count = kciphers->ml_count;
5957d82f0f8SDina K Nimeh 		free(kciphers);
5967d82f0f8SDina K Nimeh 		kciphers = malloc(sizeof (crypto_get_mechanism_list_t) +
5977d82f0f8SDina K Nimeh 		    sizeof (crypto_mech_name_t) * (count - 1));
5987d82f0f8SDina K Nimeh 		if (kciphers == NULL) {
5997d82f0f8SDina K Nimeh 			warn(gettext("failed to allocate memory for list of "
6007d82f0f8SDina K Nimeh 			    "kernel mechanisms"));
6017d82f0f8SDina K Nimeh 			goto kcc_out;
6027d82f0f8SDina K Nimeh 		}
6037d82f0f8SDina K Nimeh 		kciphers->ml_count = count;
6047d82f0f8SDina K Nimeh 
6057d82f0f8SDina K Nimeh 		if (ioctl(fd, CRYPTO_GET_MECHANISM_LIST, kciphers) == -1) {
6067d82f0f8SDina K Nimeh 			warn(gettext("CRYPTO_GET_MECHANISM_LIST ioctl failed"));
6077d82f0f8SDina K Nimeh 			goto kcc_out;
6087d82f0f8SDina K Nimeh 		}
6097d82f0f8SDina K Nimeh 	}
6107d82f0f8SDina K Nimeh 
6117d82f0f8SDina K Nimeh 	if (kciphers->ml_return_value != CRYPTO_SUCCESS) {
6127d82f0f8SDina K Nimeh 		warn(gettext(
6137d82f0f8SDina K Nimeh 		    "CRYPTO_GET_MECHANISM_LIST ioctl return value = %d\n"),
6147d82f0f8SDina K Nimeh 		    kciphers->ml_return_value);
6157d82f0f8SDina K Nimeh 		goto kcc_out;
6167d82f0f8SDina K Nimeh 	}
6177d82f0f8SDina K Nimeh 
6187d82f0f8SDina K Nimeh 	/*
6197d82f0f8SDina K Nimeh 	 * scan list of kernel ciphers looking for the selected one and if
6207d82f0f8SDina K Nimeh 	 * it needs an iv generated using another cipher, also look for that
6217d82f0f8SDina K Nimeh 	 * additional cipher to be used for generating the iv
6227d82f0f8SDina K Nimeh 	 */
6237d82f0f8SDina K Nimeh 	count = kciphers->ml_count;
6247d82f0f8SDina K Nimeh 	for (i = 0; i < count && !(ciph_ok && iv_ok); i++) {
6257d82f0f8SDina K Nimeh 		if (!ciph_ok &&
6267d82f0f8SDina K Nimeh 		    strcasecmp(cipher->name, kciphers->ml_list[i]) == 0)
6277d82f0f8SDina K Nimeh 			ciph_ok = B_TRUE;
6287d82f0f8SDina K Nimeh 		if (!iv_ok &&
6297d82f0f8SDina K Nimeh 		    strcasecmp(cipher->iv_name, kciphers->ml_list[i]) == 0)
6307d82f0f8SDina K Nimeh 			iv_ok = B_TRUE;
6317d82f0f8SDina K Nimeh 	}
6327d82f0f8SDina K Nimeh 	free(kciphers);
6337d82f0f8SDina K Nimeh 	kciphers = NULL;
6347d82f0f8SDina K Nimeh 
6357d82f0f8SDina K Nimeh 	if (!ciph_ok)
6367d82f0f8SDina K Nimeh 		warn(gettext("%s mechanism not supported in kernel\n"),
6377d82f0f8SDina K Nimeh 		    cipher->name);
6387d82f0f8SDina K Nimeh 	if (!iv_ok)
6397d82f0f8SDina K Nimeh 		warn(gettext("%s mechanism not supported in kernel\n"),
6407d82f0f8SDina K Nimeh 		    cipher->iv_name);
6417d82f0f8SDina K Nimeh 
6427d82f0f8SDina K Nimeh 	if (ciph_ok) {
6437d82f0f8SDina K Nimeh 		/* Get the details about the user selected cipher */
6447d82f0f8SDina K Nimeh 		count = DEFAULT_MECHINFO_NUM;
6457d82f0f8SDina K Nimeh 		kinfo = malloc(sizeof (crypto_get_all_mechanism_info_t) +
6467d82f0f8SDina K Nimeh 		    sizeof (crypto_mechanism_info_t) * (count - 1));
6477d82f0f8SDina K Nimeh 		if (kinfo == NULL) {
6487d82f0f8SDina K Nimeh 			warn(gettext("failed to allocate memory for "
6497d82f0f8SDina K Nimeh 			    "kernel mechanism info"));
6507d82f0f8SDina K Nimeh 			goto kcc_out;
6517d82f0f8SDina K Nimeh 		}
6527d82f0f8SDina K Nimeh 		kinfo->mi_count = count;
6537d82f0f8SDina K Nimeh 		(void) strlcpy(kinfo->mi_mechanism_name, cipher->name,
6547d82f0f8SDina K Nimeh 		    CRYPTO_MAX_MECH_NAME);
6557d82f0f8SDina K Nimeh 
6567d82f0f8SDina K Nimeh 		if (ioctl(fd, CRYPTO_GET_ALL_MECHANISM_INFO, kinfo) == -1) {
6577d82f0f8SDina K Nimeh 			warn(gettext(
6587d82f0f8SDina K Nimeh 			    "CRYPTO_GET_ALL_MECHANISM_INFO ioctl failed"));
6597d82f0f8SDina K Nimeh 			goto kcc_out;
6607d82f0f8SDina K Nimeh 		}
6617d82f0f8SDina K Nimeh 
6627d82f0f8SDina K Nimeh 		if (kinfo->mi_return_value == CRYPTO_BUFFER_TOO_SMALL) {
6637d82f0f8SDina K Nimeh 			count = kinfo->mi_count;
6647d82f0f8SDina K Nimeh 			free(kinfo);
6657d82f0f8SDina K Nimeh 			kinfo = malloc(
6667d82f0f8SDina K Nimeh 			    sizeof (crypto_get_all_mechanism_info_t) +
6677d82f0f8SDina K Nimeh 			    sizeof (crypto_mechanism_info_t) * (count - 1));
6687d82f0f8SDina K Nimeh 			if (kinfo == NULL) {
6697d82f0f8SDina K Nimeh 				warn(gettext("failed to allocate memory for "
6707d82f0f8SDina K Nimeh 				    "kernel mechanism info"));
6717d82f0f8SDina K Nimeh 				goto kcc_out;
6727d82f0f8SDina K Nimeh 			}
6737d82f0f8SDina K Nimeh 			kinfo->mi_count = count;
6747d82f0f8SDina K Nimeh 			(void) strlcpy(kinfo->mi_mechanism_name, cipher->name,
6757d82f0f8SDina K Nimeh 			    CRYPTO_MAX_MECH_NAME);
6767d82f0f8SDina K Nimeh 
6777d82f0f8SDina K Nimeh 			if (ioctl(fd, CRYPTO_GET_ALL_MECHANISM_INFO, kinfo) ==
6787d82f0f8SDina K Nimeh 			    -1) {
6797d82f0f8SDina K Nimeh 				warn(gettext("CRYPTO_GET_ALL_MECHANISM_INFO "
6807d82f0f8SDina K Nimeh 				    "ioctl failed"));
6817d82f0f8SDina K Nimeh 				goto kcc_out;
6827d82f0f8SDina K Nimeh 			}
6837d82f0f8SDina K Nimeh 		}
6847d82f0f8SDina K Nimeh 
6857d82f0f8SDina K Nimeh 		if (kinfo->mi_return_value != CRYPTO_SUCCESS) {
6867d82f0f8SDina K Nimeh 			warn(gettext("CRYPTO_GET_ALL_MECHANISM_INFO ioctl "
6877d82f0f8SDina K Nimeh 			    "return value = %d\n"), kinfo->mi_return_value);
6887d82f0f8SDina K Nimeh 			goto kcc_out;
6897d82f0f8SDina K Nimeh 		}
6907d82f0f8SDina K Nimeh 
6917d82f0f8SDina K Nimeh 		/* Set key min and max size */
6927d82f0f8SDina K Nimeh 		count = kinfo->mi_count;
6937d82f0f8SDina K Nimeh 		i = 0;
6947d82f0f8SDina K Nimeh 		if (i < count) {
6957d82f0f8SDina K Nimeh 			keymin = kinfo->mi_list[i].mi_min_key_size;
6967d82f0f8SDina K Nimeh 			keymax = kinfo->mi_list[i].mi_max_key_size;
6977d82f0f8SDina K Nimeh 			if (kinfo->mi_list[i].mi_keysize_unit &
6987d82f0f8SDina K Nimeh 			    CRYPTO_KEYSIZE_UNIT_IN_BITS) {
6997d82f0f8SDina K Nimeh 				keymin = CRYPTO_BITS2BYTES(keymin);
7007d82f0f8SDina K Nimeh 				keymax = CRYPTO_BITS2BYTES(keymax);
7017d82f0f8SDina K Nimeh 
7027d82f0f8SDina K Nimeh 			}
7037d82f0f8SDina K Nimeh 			cipher->min_keysize = keymin;
7047d82f0f8SDina K Nimeh 			cipher->max_keysize = keymax;
7057d82f0f8SDina K Nimeh 		}
7067d82f0f8SDina K Nimeh 		free(kinfo);
7077d82f0f8SDina K Nimeh 		kinfo = NULL;
7087d82f0f8SDina K Nimeh 
7097d82f0f8SDina K Nimeh 		if (i == count) {
7107d82f0f8SDina K Nimeh 			(void) close(fd);
7117d82f0f8SDina K Nimeh 			die(gettext(
7127d82f0f8SDina K Nimeh 			    "failed to find usable %s kernel mechanism, "
7137d82f0f8SDina K Nimeh 			    "use \"cryptoadm list -m\" to find available "
7147d82f0f8SDina K Nimeh 			    "mechanisms\n"),
7157d82f0f8SDina K Nimeh 			    cipher->name);
7167d82f0f8SDina K Nimeh 		}
7177d82f0f8SDina K Nimeh 	}
7187d82f0f8SDina K Nimeh 
7197d82f0f8SDina K Nimeh 	/* Note: key min/max, unit size, usage for iv cipher are not checked. */
7207d82f0f8SDina K Nimeh 
7217d82f0f8SDina K Nimeh 	return (ciph_ok && iv_ok);
7227d82f0f8SDina K Nimeh 
7237d82f0f8SDina K Nimeh kcc_out:
7247d82f0f8SDina K Nimeh 	if (kinfo != NULL)
7257d82f0f8SDina K Nimeh 		free(kinfo);
7267d82f0f8SDina K Nimeh 	if (kciphers != NULL)
7277d82f0f8SDina K Nimeh 		free(kciphers);
7287d82f0f8SDina K Nimeh 	if (fd != -1)
7297d82f0f8SDina K Nimeh 		(void) close(fd);
7307d82f0f8SDina K Nimeh 	return (B_FALSE);
7317d82f0f8SDina K Nimeh }
7327d82f0f8SDina K Nimeh 
7337d82f0f8SDina K Nimeh /*
7347d82f0f8SDina K Nimeh  * Break up token spec into its components (non-destructive)
7357d82f0f8SDina K Nimeh  */
7367d82f0f8SDina K Nimeh static token_spec_t *
7377d82f0f8SDina K Nimeh parsetoken(char *spec)
7387d82f0f8SDina K Nimeh {
7397d82f0f8SDina K Nimeh #define	FLD_NAME	0
7407d82f0f8SDina K Nimeh #define	FLD_MANUF	1
7417d82f0f8SDina K Nimeh #define	FLD_SERIAL	2
7427d82f0f8SDina K Nimeh #define	FLD_LABEL	3
7437d82f0f8SDina K Nimeh #define	NFIELDS		4
7447d82f0f8SDina K Nimeh #define	nullfield(i)	((field[(i)+1] - field[(i)]) <= 1)
7457d82f0f8SDina K Nimeh #define	copyfield(fld, i)	\
7467d82f0f8SDina K Nimeh 		{							\
7477d82f0f8SDina K Nimeh 			int	n;					\
7487d82f0f8SDina K Nimeh 			(fld) = NULL;					\
7497d82f0f8SDina K Nimeh 			if ((n = (field[(i)+1] - field[(i)])) > 1) {	\
7507d82f0f8SDina K Nimeh 				if (((fld) = malloc(n)) != NULL) {	\
7517d82f0f8SDina K Nimeh 					(void) strncpy((fld), field[(i)], n); \
7527d82f0f8SDina K Nimeh 					((fld))[n - 1] = '\0';		\
7537d82f0f8SDina K Nimeh 				}					\
7547d82f0f8SDina K Nimeh 			}						\
7557d82f0f8SDina K Nimeh 		}
7567d82f0f8SDina K Nimeh 
7577d82f0f8SDina K Nimeh 	int	i;
7587d82f0f8SDina K Nimeh 	char	*field[NFIELDS + 1];	/* +1 to catch extra delimiters */
7597d82f0f8SDina K Nimeh 	token_spec_t *ti = NULL;
7607d82f0f8SDina K Nimeh 
7617d82f0f8SDina K Nimeh 	if (spec == NULL)
7627d82f0f8SDina K Nimeh 		return (NULL);
7637d82f0f8SDina K Nimeh 
7647d82f0f8SDina K Nimeh 	/*
7657d82f0f8SDina K Nimeh 	 * Correct format is "[name]:[manuf]:[serial]:key". Can't use
7667d82f0f8SDina K Nimeh 	 * strtok because it treats ":::key" and "key:::" and "key" all
7677d82f0f8SDina K Nimeh 	 * as the same thing, and we can't have the :s compressed away.
7687d82f0f8SDina K Nimeh 	 */
7697d82f0f8SDina K Nimeh 	field[0] = spec;
7707d82f0f8SDina K Nimeh 	for (i = 1; i < NFIELDS + 1; i++) {
7717d82f0f8SDina K Nimeh 		field[i] = strchr(field[i-1], ':');
7727d82f0f8SDina K Nimeh 		if (field[i] == NULL)
7737d82f0f8SDina K Nimeh 			break;
7747d82f0f8SDina K Nimeh 		field[i]++;
7757d82f0f8SDina K Nimeh 	}
7767d82f0f8SDina K Nimeh 	if (i < NFIELDS)		/* not enough fields */
7777d82f0f8SDina K Nimeh 		return (NULL);
7787d82f0f8SDina K Nimeh 	if (field[NFIELDS] != NULL)	/* too many fields */
7797d82f0f8SDina K Nimeh 		return (NULL);
7807d82f0f8SDina K Nimeh 	field[NFIELDS] = strchr(field[NFIELDS-1], '\0') + 1;
7817d82f0f8SDina K Nimeh 
7827d82f0f8SDina K Nimeh 	/* key label can't be empty */
7837d82f0f8SDina K Nimeh 	if (nullfield(FLD_LABEL))
7847d82f0f8SDina K Nimeh 		return (NULL);
7857d82f0f8SDina K Nimeh 
7867d82f0f8SDina K Nimeh 	ti = malloc(sizeof (token_spec_t));
7877d82f0f8SDina K Nimeh 	if (ti == NULL)
7887d82f0f8SDina K Nimeh 		return (NULL);
7897d82f0f8SDina K Nimeh 
7907d82f0f8SDina K Nimeh 	copyfield(ti->name, FLD_NAME);
7917d82f0f8SDina K Nimeh 	copyfield(ti->mfr, FLD_MANUF);
7927d82f0f8SDina K Nimeh 	copyfield(ti->serno, FLD_SERIAL);
7937d82f0f8SDina K Nimeh 	copyfield(ti->key, FLD_LABEL);
7947d82f0f8SDina K Nimeh 
7957d82f0f8SDina K Nimeh 	/*
7967d82f0f8SDina K Nimeh 	 * If token specified and it only contains a key label, then
7977d82f0f8SDina K Nimeh 	 * search all tokens for the key, otherwise only those with
7987d82f0f8SDina K Nimeh 	 * matching name, mfr, and serno are used.
7997d82f0f8SDina K Nimeh 	 */
8007d82f0f8SDina K Nimeh 	/*
8017d82f0f8SDina K Nimeh 	 * That's how we'd like it to be, however, if only the key label
8027d82f0f8SDina K Nimeh 	 * is specified, default to using softtoken.  It's easier.
8037d82f0f8SDina K Nimeh 	 */
8047d82f0f8SDina K Nimeh 	if (ti->name == NULL && ti->mfr == NULL && ti->serno == NULL)
8057d82f0f8SDina K Nimeh 		ti->name = strdup(pkcs11_default_token());
8067d82f0f8SDina K Nimeh 	return (ti);
8077d82f0f8SDina K Nimeh }
8087d82f0f8SDina K Nimeh 
8097d82f0f8SDina K Nimeh /*
8107d82f0f8SDina K Nimeh  * PBE the passphrase into a raw key
8117d82f0f8SDina K Nimeh  */
8127d82f0f8SDina K Nimeh static void
8137d82f0f8SDina K Nimeh getkeyfromuser(mech_alias_t *cipher, char **raw_key, size_t *raw_key_sz)
8147d82f0f8SDina K Nimeh {
8157d82f0f8SDina K Nimeh 	CK_SESSION_HANDLE sess;
8167d82f0f8SDina K Nimeh 	CK_RV	rv;
8177d82f0f8SDina K Nimeh 	char	*pass = NULL;
8187d82f0f8SDina K Nimeh 	size_t	passlen = 0;
8197d82f0f8SDina K Nimeh 	void	*salt = NULL;	/* don't use NULL, see note on salt below */
8207d82f0f8SDina K Nimeh 	size_t	saltlen = 0;
8217d82f0f8SDina K Nimeh 	CK_KEY_TYPE ktype;
8227d82f0f8SDina K Nimeh 	void	*kvalue;
8237d82f0f8SDina K Nimeh 	size_t	klen;
8247d82f0f8SDina K Nimeh 
8257d82f0f8SDina K Nimeh 	/* did init_crypto find a slot that supports this cipher? */
8267d82f0f8SDina K Nimeh 	if (cipher->slot == (CK_SLOT_ID)-1 || cipher->max_keysize == 0) {
8277d82f0f8SDina K Nimeh 		rv = CKR_MECHANISM_INVALID;
8287d82f0f8SDina K Nimeh 		goto cleanup;
8297d82f0f8SDina K Nimeh 	}
8307d82f0f8SDina K Nimeh 
8317d82f0f8SDina K Nimeh 	rv = pkcs11_mech2keytype(cipher->type, &ktype);
8327d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
8337d82f0f8SDina K Nimeh 		goto cleanup;
8347d82f0f8SDina K Nimeh 
8357d82f0f8SDina K Nimeh 	/*
8367d82f0f8SDina K Nimeh 	 * use the passphrase to generate a PBE PKCS#5 secret key and
8377d82f0f8SDina K Nimeh 	 * retrieve the raw key data to eventually pass it to the kernel;
8387d82f0f8SDina K Nimeh 	 */
8397d82f0f8SDina K Nimeh 	rv = C_OpenSession(cipher->slot, CKF_SERIAL_SESSION, NULL, NULL, &sess);
8407d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
8417d82f0f8SDina K Nimeh 		goto cleanup;
8427d82f0f8SDina K Nimeh 
8437d82f0f8SDina K Nimeh 	/* get user passphrase with 8 byte minimum */
8447d82f0f8SDina K Nimeh 	if (pkcs11_get_pass(NULL, &pass, &passlen, MIN_PASSLEN, B_TRUE) < 0) {
8457d82f0f8SDina K Nimeh 		die(gettext("passphrases do not match\n"));
8467d82f0f8SDina K Nimeh 	}
8477d82f0f8SDina K Nimeh 
8487d82f0f8SDina K Nimeh 	/*
8497d82f0f8SDina K Nimeh 	 * salt should not be NULL, or else pkcs11_PasswdToKey() will
8507d82f0f8SDina K Nimeh 	 * complain about CKR_MECHANISM_PARAM_INVALID; the following is
8517d82f0f8SDina K Nimeh 	 * to make up for not having a salt until a proper one is used
8527d82f0f8SDina K Nimeh 	 */
8537d82f0f8SDina K Nimeh 	salt = pass;
8547d82f0f8SDina K Nimeh 	saltlen = passlen;
8557d82f0f8SDina K Nimeh 
8567d82f0f8SDina K Nimeh 	klen = cipher->max_keysize;
8577d82f0f8SDina K Nimeh 	rv = pkcs11_PasswdToKey(sess, pass, passlen, salt, saltlen, ktype,
8587d82f0f8SDina K Nimeh 	    cipher->max_keysize, &kvalue, &klen);
8597d82f0f8SDina K Nimeh 
8607d82f0f8SDina K Nimeh 	(void) C_CloseSession(sess);
8617d82f0f8SDina K Nimeh 
8627d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
8637d82f0f8SDina K Nimeh 		goto cleanup;
8647d82f0f8SDina K Nimeh 	}
8657d82f0f8SDina K Nimeh 
8667d82f0f8SDina K Nimeh 	/* assert(klen == cipher->max_keysize); */
8677d82f0f8SDina K Nimeh 	*raw_key_sz = klen;
8687d82f0f8SDina K Nimeh 	*raw_key = (char *)kvalue;
8697d82f0f8SDina K Nimeh 	return;
8707d82f0f8SDina K Nimeh 
8717d82f0f8SDina K Nimeh cleanup:
8727d82f0f8SDina K Nimeh 	die(gettext("failed to generate %s key from passphrase: %s"),
8737d82f0f8SDina K Nimeh 	    cipher->alias, pkcs11_strerror(rv));
8747d82f0f8SDina K Nimeh }
8757d82f0f8SDina K Nimeh 
8767d82f0f8SDina K Nimeh /*
8777d82f0f8SDina K Nimeh  * Read raw key from file; also handles ephemeral keys.
8787d82f0f8SDina K Nimeh  */
8797d82f0f8SDina K Nimeh void
8807d82f0f8SDina K Nimeh getkeyfromfile(const char *pathname, mech_alias_t *cipher, char **key,
8817d82f0f8SDina K Nimeh     size_t *ksz)
8827d82f0f8SDina K Nimeh {
8837d82f0f8SDina K Nimeh 	int	fd;
8847d82f0f8SDina K Nimeh 	struct stat sbuf;
8857d82f0f8SDina K Nimeh 	boolean_t notplain = B_FALSE;
8867d82f0f8SDina K Nimeh 	ssize_t	cursz;
8877d82f0f8SDina K Nimeh 	ssize_t	nread;
8887d82f0f8SDina K Nimeh 
8897d82f0f8SDina K Nimeh 	/* ephemeral keys are just random data */
8907d82f0f8SDina K Nimeh 	if (pathname == NULL) {
8917d82f0f8SDina K Nimeh 		*ksz = cipher->max_keysize;
8927d82f0f8SDina K Nimeh 		*key = malloc(*ksz);
8937d82f0f8SDina K Nimeh 		if (*key == NULL)
8947d82f0f8SDina K Nimeh 			die(gettext("failed to allocate memory for"
8957d82f0f8SDina K Nimeh 			    " ephemeral key"));
8967d82f0f8SDina K Nimeh 		if (pkcs11_random_data(*key, *ksz) < 0) {
8977d82f0f8SDina K Nimeh 			free(*key);
8987d82f0f8SDina K Nimeh 			die(gettext("failed to get enough random data"));
8997d82f0f8SDina K Nimeh 		}
9007d82f0f8SDina K Nimeh 		return;
9017d82f0f8SDina K Nimeh 	}
9027d82f0f8SDina K Nimeh 
9037d82f0f8SDina K Nimeh 	/*
9047d82f0f8SDina K Nimeh 	 * If the remaining section of code didn't also check for secure keyfile
9057d82f0f8SDina K Nimeh 	 * permissions and whether the key is within cipher min and max lengths,
9067d82f0f8SDina K Nimeh 	 * (or, if those things moved out of this block), we could have had:
9077d82f0f8SDina K Nimeh 	 *	if (pkcs11_read_data(pathname, key, ksz) < 0)
9087d82f0f8SDina K Nimeh 	 *		handle_error();
9097d82f0f8SDina K Nimeh 	 */
9107d82f0f8SDina K Nimeh 
9117d82f0f8SDina K Nimeh 	if ((fd = open(pathname, O_RDONLY, 0)) == -1)
9127d82f0f8SDina K Nimeh 		die(gettext("open of keyfile (%s) failed"), pathname);
9137d82f0f8SDina K Nimeh 
9147d82f0f8SDina K Nimeh 	if (fstat(fd, &sbuf) == -1)
9157d82f0f8SDina K Nimeh 		die(gettext("fstat of keyfile (%s) failed"), pathname);
9167d82f0f8SDina K Nimeh 
9177d82f0f8SDina K Nimeh 	if (S_ISREG(sbuf.st_mode)) {
9187d82f0f8SDina K Nimeh 		if ((sbuf.st_mode & (S_IWGRP | S_IWOTH)) != 0)
9197d82f0f8SDina K Nimeh 			die(gettext("insecure permissions on keyfile %s\n"),
9207d82f0f8SDina K Nimeh 			    pathname);
9217d82f0f8SDina K Nimeh 
9227d82f0f8SDina K Nimeh 		*ksz = sbuf.st_size;
9237d82f0f8SDina K Nimeh 		if (*ksz < cipher->min_keysize || cipher->max_keysize < *ksz) {
9247d82f0f8SDina K Nimeh 			warn(gettext("%s: invalid keysize: %d\n"),
9257d82f0f8SDina K Nimeh 			    pathname, (int)*ksz);
9267d82f0f8SDina K Nimeh 			die(gettext("\t%d <= keysize <= %d\n"),
9277d82f0f8SDina K Nimeh 			    cipher->min_keysize, cipher->max_keysize);
9287d82f0f8SDina K Nimeh 		}
9297d82f0f8SDina K Nimeh 	} else {
9307d82f0f8SDina K Nimeh 		*ksz = cipher->max_keysize;
9317d82f0f8SDina K Nimeh 		notplain = B_TRUE;
9327d82f0f8SDina K Nimeh 	}
9337d82f0f8SDina K Nimeh 
9347d82f0f8SDina K Nimeh 	*key = malloc(*ksz);
9357d82f0f8SDina K Nimeh 	if (*key == NULL)
9367d82f0f8SDina K Nimeh 		die(gettext("failed to allocate memory for key from file"));
9377d82f0f8SDina K Nimeh 
9387d82f0f8SDina K Nimeh 	for (cursz = 0, nread = 0; cursz < *ksz; cursz += nread) {
9397d82f0f8SDina K Nimeh 		nread = read(fd, *key, *ksz);
9407d82f0f8SDina K Nimeh 		if (nread > 0)
9417d82f0f8SDina K Nimeh 			continue;
9427d82f0f8SDina K Nimeh 		/*
9437d82f0f8SDina K Nimeh 		 * nread == 0.  If it's not a regular file we were trying to
9447d82f0f8SDina K Nimeh 		 * get the maximum keysize of data possible for this cipher.
9457d82f0f8SDina K Nimeh 		 * But if we've got at least the minimum keysize of data,
9467d82f0f8SDina K Nimeh 		 * round down to the nearest keysize unit and call it good.
9477d82f0f8SDina K Nimeh 		 * If we haven't met the minimum keysize, that's an error.
9487d82f0f8SDina K Nimeh 		 * If it's a regular file, nread = 0 is also an error.
9497d82f0f8SDina K Nimeh 		 */
9507d82f0f8SDina K Nimeh 		if (nread == 0 && notplain && cursz >= cipher->min_keysize) {
9517d82f0f8SDina K Nimeh 			*ksz = (cursz / cipher->min_keysize) *
9527d82f0f8SDina K Nimeh 			    cipher->min_keysize;
9537d82f0f8SDina K Nimeh 			break;
9547d82f0f8SDina K Nimeh 		}
9557d82f0f8SDina K Nimeh 		die(gettext("%s: can't read all keybytes"), pathname);
9567d82f0f8SDina K Nimeh 	}
9577d82f0f8SDina K Nimeh 	(void) close(fd);
9587d82f0f8SDina K Nimeh }
9597d82f0f8SDina K Nimeh 
9607d82f0f8SDina K Nimeh /*
9617d82f0f8SDina K Nimeh  * Read the raw key from token, or from a file that was wrapped with a
9627d82f0f8SDina K Nimeh  * key from token
9637d82f0f8SDina K Nimeh  */
9647d82f0f8SDina K Nimeh void
9657d82f0f8SDina K Nimeh getkeyfromtoken(CK_SESSION_HANDLE sess,
9667d82f0f8SDina K Nimeh     token_spec_t *token, const char *keyfile, mech_alias_t *cipher,
9677d82f0f8SDina K Nimeh     char **raw_key, size_t *raw_key_sz)
9687d82f0f8SDina K Nimeh {
9697d82f0f8SDina K Nimeh 	CK_RV	rv = CKR_OK;
9707d82f0f8SDina K Nimeh 	CK_BBOOL trueval = B_TRUE;
9717d82f0f8SDina K Nimeh 	CK_OBJECT_CLASS kclass;		/* secret key or RSA private key */
9727d82f0f8SDina K Nimeh 	CK_KEY_TYPE ktype;		/* from selected cipher or CKK_RSA */
9737d82f0f8SDina K Nimeh 	CK_KEY_TYPE raw_ktype;		/* from selected cipher */
9747d82f0f8SDina K Nimeh 	CK_ATTRIBUTE	key_tmpl[] = {
9757d82f0f8SDina K Nimeh 		{ CKA_CLASS, NULL, 0 },	/* re-used for token key and unwrap */
9767d82f0f8SDina K Nimeh 		{ CKA_KEY_TYPE, NULL, 0 },	/* ditto */
9777d82f0f8SDina K Nimeh 		{ CKA_LABEL, NULL, 0 },
9787d82f0f8SDina K Nimeh 		{ CKA_TOKEN, NULL, 0 },
9797d82f0f8SDina K Nimeh 		{ CKA_PRIVATE, NULL, 0 }
9807d82f0f8SDina K Nimeh 	    };
9817d82f0f8SDina K Nimeh 	CK_ULONG attrs = sizeof (key_tmpl) / sizeof (CK_ATTRIBUTE);
9827d82f0f8SDina K Nimeh 	int	i;
9837d82f0f8SDina K Nimeh 	char	*pass = NULL;
9847d82f0f8SDina K Nimeh 	size_t	passlen = 0;
9857d82f0f8SDina K Nimeh 	CK_OBJECT_HANDLE obj, rawobj;
9867d82f0f8SDina K Nimeh 	CK_ULONG num_objs = 1;		/* just want to find 1 token key */
9877d82f0f8SDina K Nimeh 	CK_MECHANISM unwrap = { CKM_RSA_PKCS, NULL, 0 };
9887d82f0f8SDina K Nimeh 	char	*rkey;
9897d82f0f8SDina K Nimeh 	size_t	rksz;
9907d82f0f8SDina K Nimeh 
9917d82f0f8SDina K Nimeh 	if (token == NULL || token->key == NULL)
9927d82f0f8SDina K Nimeh 		return;
9937d82f0f8SDina K Nimeh 
9947d82f0f8SDina K Nimeh 	/* did init_crypto find a slot that supports this cipher? */
9957d82f0f8SDina K Nimeh 	if (cipher->slot == (CK_SLOT_ID)-1 || cipher->max_keysize == 0) {
9967d82f0f8SDina K Nimeh 		die(gettext("failed to find any cryptographic provider, "
9977d82f0f8SDina K Nimeh 		    "use \"cryptoadm list -p\" to find providers: %s\n"),
9987d82f0f8SDina K Nimeh 		    pkcs11_strerror(CKR_MECHANISM_INVALID));
9997d82f0f8SDina K Nimeh 	}
10007d82f0f8SDina K Nimeh 
10017d82f0f8SDina K Nimeh 	if (pkcs11_get_pass(token->name, &pass, &passlen, 0, B_FALSE) < 0)
10027d82f0f8SDina K Nimeh 		die(gettext("unable to get passphrase"));
10037d82f0f8SDina K Nimeh 
10047d82f0f8SDina K Nimeh 	/* use passphrase to login to token */
10057d82f0f8SDina K Nimeh 	if (pass != NULL && passlen > 0) {
10067d82f0f8SDina K Nimeh 		rv = C_Login(sess, CKU_USER, (CK_UTF8CHAR_PTR)pass, passlen);
10077d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
10087d82f0f8SDina K Nimeh 			die(gettext("cannot login to the token %s: %s\n"),
10097d82f0f8SDina K Nimeh 			    token->name, pkcs11_strerror(rv));
10107d82f0f8SDina K Nimeh 		}
10117d82f0f8SDina K Nimeh 	}
10127d82f0f8SDina K Nimeh 
10137d82f0f8SDina K Nimeh 	rv = pkcs11_mech2keytype(cipher->type, &raw_ktype);
10147d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
10157d82f0f8SDina K Nimeh 		die(gettext("failed to get key type for cipher %s: %s\n"),
10167d82f0f8SDina K Nimeh 		    cipher->name, pkcs11_strerror(rv));
10177d82f0f8SDina K Nimeh 	}
10187d82f0f8SDina K Nimeh 
10197d82f0f8SDina K Nimeh 	/*
10207d82f0f8SDina K Nimeh 	 * If no keyfile was given, then the token key is secret key to
10217d82f0f8SDina K Nimeh 	 * be used for encryption/decryption.  Otherwise, the keyfile
10227d82f0f8SDina K Nimeh 	 * contains a wrapped secret key, and the token is actually the
10237d82f0f8SDina K Nimeh 	 * unwrapping RSA private key.
10247d82f0f8SDina K Nimeh 	 */
10257d82f0f8SDina K Nimeh 	if (keyfile == NULL) {
10267d82f0f8SDina K Nimeh 		kclass = CKO_SECRET_KEY;
10277d82f0f8SDina K Nimeh 		ktype = raw_ktype;
10287d82f0f8SDina K Nimeh 	} else {
10297d82f0f8SDina K Nimeh 		kclass = CKO_PRIVATE_KEY;
10307d82f0f8SDina K Nimeh 		ktype = CKK_RSA;
10317d82f0f8SDina K Nimeh 	}
10327d82f0f8SDina K Nimeh 
10337d82f0f8SDina K Nimeh 	/* Find the key in the token first */
10347d82f0f8SDina K Nimeh 	for (i = 0; i < attrs; i++) {
10357d82f0f8SDina K Nimeh 		switch (key_tmpl[i].type) {
10367d82f0f8SDina K Nimeh 		case CKA_CLASS:
10377d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &kclass;
10387d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (kclass);
10397d82f0f8SDina K Nimeh 			break;
10407d82f0f8SDina K Nimeh 		case CKA_KEY_TYPE:
10417d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &ktype;
10427d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (ktype);
10437d82f0f8SDina K Nimeh 			break;
10447d82f0f8SDina K Nimeh 		case CKA_LABEL:
10457d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = token->key;
10467d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = strlen(token->key);
10477d82f0f8SDina K Nimeh 			break;
10487d82f0f8SDina K Nimeh 		case CKA_TOKEN:
10497d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &trueval;
10507d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (trueval);
10517d82f0f8SDina K Nimeh 			break;
10527d82f0f8SDina K Nimeh 		case CKA_PRIVATE:
10537d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &trueval;
10547d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (trueval);
10557d82f0f8SDina K Nimeh 			break;
10567d82f0f8SDina K Nimeh 		default:
10577d82f0f8SDina K Nimeh 			break;
10587d82f0f8SDina K Nimeh 		}
10597d82f0f8SDina K Nimeh 	}
10607d82f0f8SDina K Nimeh 	rv = C_FindObjectsInit(sess, key_tmpl, attrs);
10617d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
10627d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s: %s\n"), token->key,
10637d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
10647d82f0f8SDina K Nimeh 	rv = C_FindObjects(sess, &obj, 1, &num_objs);
10657d82f0f8SDina K Nimeh 	(void) C_FindObjectsFinal(sess);
10667d82f0f8SDina K Nimeh 
10677d82f0f8SDina K Nimeh 	if (num_objs == 0) {
10687d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s\n"), token->key);
10697d82f0f8SDina K Nimeh 	} else if (rv != CKR_OK) {
10707d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s: %s\n"), token->key,
10717d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
10727d82f0f8SDina K Nimeh 	}
10737d82f0f8SDina K Nimeh 
10747d82f0f8SDina K Nimeh 	/*
10757d82f0f8SDina K Nimeh 	 * No keyfile means when token key is found, convert it to raw key,
10767d82f0f8SDina K Nimeh 	 * and done.  Otherwise still need do an unwrap to create yet another
10777d82f0f8SDina K Nimeh 	 * obj and that needs to be converted to raw key before we're done.
10787d82f0f8SDina K Nimeh 	 */
10797d82f0f8SDina K Nimeh 	if (keyfile == NULL) {
10807d82f0f8SDina K Nimeh 		/* obj contains raw key, extract it */
10817d82f0f8SDina K Nimeh 		rv = pkcs11_ObjectToKey(sess, obj, (void **)&rkey, &rksz,
10827d82f0f8SDina K Nimeh 		    B_FALSE);
10837d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
10847d82f0f8SDina K Nimeh 			die(gettext("failed to get key value for %s"
10857d82f0f8SDina K Nimeh 			    " from token %s, %s\n"), token->key,
10867d82f0f8SDina K Nimeh 			    token->name, pkcs11_strerror(rv));
10877d82f0f8SDina K Nimeh 		}
10887d82f0f8SDina K Nimeh 	} else {
10897d82f0f8SDina K Nimeh 		getkeyfromfile(keyfile, cipher, &rkey, &rksz);
10907d82f0f8SDina K Nimeh 
10917d82f0f8SDina K Nimeh 		/*
10927d82f0f8SDina K Nimeh 		 * Got the wrapping RSA obj and the wrapped key from file.
10937d82f0f8SDina K Nimeh 		 * Unwrap the key from file with RSA obj to get rawkey obj.
10947d82f0f8SDina K Nimeh 		 */
10957d82f0f8SDina K Nimeh 
10967d82f0f8SDina K Nimeh 		/* re-use the first two attributes of key_tmpl */
10977d82f0f8SDina K Nimeh 		kclass = CKO_SECRET_KEY;
10987d82f0f8SDina K Nimeh 		ktype = raw_ktype;
10997d82f0f8SDina K Nimeh 
11007d82f0f8SDina K Nimeh 		rv = C_UnwrapKey(sess, &unwrap, obj, (CK_BYTE_PTR)rkey,
11017d82f0f8SDina K Nimeh 		    rksz, key_tmpl, 2, &rawobj);
11027d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
11037d82f0f8SDina K Nimeh 			die(gettext("failed to unwrap key in keyfile %s,"
11047d82f0f8SDina K Nimeh 			    " %s\n"), keyfile, pkcs11_strerror(rv));
11057d82f0f8SDina K Nimeh 		}
11067d82f0f8SDina K Nimeh 		/* rawobj contains raw key, extract it */
11077d82f0f8SDina K Nimeh 		rv = pkcs11_ObjectToKey(sess, rawobj, (void **)&rkey, &rksz,
11087d82f0f8SDina K Nimeh 		    B_TRUE);
11097d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
11107d82f0f8SDina K Nimeh 			die(gettext("failed to get unwrapped key value for"
11117d82f0f8SDina K Nimeh 			    " key in keyfile %s, %s\n"), keyfile,
11127d82f0f8SDina K Nimeh 			    pkcs11_strerror(rv));
11137d82f0f8SDina K Nimeh 		}
11147d82f0f8SDina K Nimeh 	}
11157d82f0f8SDina K Nimeh 
11167d82f0f8SDina K Nimeh 	/* validate raw key size */
11177d82f0f8SDina K Nimeh 	if (rksz < cipher->min_keysize || cipher->max_keysize < rksz) {
11187d82f0f8SDina K Nimeh 		warn(gettext("%s: invalid keysize: %d\n"), keyfile, (int)rksz);
11197d82f0f8SDina K Nimeh 		die(gettext("\t%d <= keysize <= %d\n"), cipher->min_keysize,
11207d82f0f8SDina K Nimeh 		    cipher->max_keysize);
11217d82f0f8SDina K Nimeh 	}
11227d82f0f8SDina K Nimeh 
11237d82f0f8SDina K Nimeh 	*raw_key_sz = rksz;
11247d82f0f8SDina K Nimeh 	*raw_key = (char *)rkey;
11257d82f0f8SDina K Nimeh }
11267d82f0f8SDina K Nimeh 
11277d82f0f8SDina K Nimeh /*
11287d82f0f8SDina K Nimeh  * Set up cipher key limits and verify PKCS#11 can be done
11297d82f0f8SDina K Nimeh  * match_token_cipher is the function pointer used by
11307d82f0f8SDina K Nimeh  * pkcs11_GetCriteriaSession() init_crypto.
11317d82f0f8SDina K Nimeh  */
11327d82f0f8SDina K Nimeh boolean_t
11337d82f0f8SDina K Nimeh match_token_cipher(CK_SLOT_ID slot_id, void *args, CK_RV *rv)
11347d82f0f8SDina K Nimeh {
11357d82f0f8SDina K Nimeh 	token_spec_t *token;
11367d82f0f8SDina K Nimeh 	mech_alias_t *cipher;
11377d82f0f8SDina K Nimeh 	CK_TOKEN_INFO tokinfo;
11387d82f0f8SDina K Nimeh 	CK_MECHANISM_INFO mechinfo;
11397d82f0f8SDina K Nimeh 	boolean_t token_match;
11407d82f0f8SDina K Nimeh 
11417d82f0f8SDina K Nimeh 	/*
11427d82f0f8SDina K Nimeh 	 * While traversing slot list, pick up the following info per slot:
11437d82f0f8SDina K Nimeh 	 * - if token specified, whether it matches this slot's token info
11447d82f0f8SDina K Nimeh 	 * - if the slot supports the PKCS#5 PBKD2 cipher
11457d82f0f8SDina K Nimeh 	 *
11467d82f0f8SDina K Nimeh 	 * If the user said on the command line
11477d82f0f8SDina K Nimeh 	 *	-T tok:mfr:ser:lab -k keyfile
11487d82f0f8SDina K Nimeh 	 *	-c cipher -T tok:mfr:ser:lab -k keyfile
11497d82f0f8SDina K Nimeh 	 * the given cipher or the default cipher apply to keyfile,
11507d82f0f8SDina K Nimeh 	 * If the user said instead
11517d82f0f8SDina K Nimeh 	 *	-T tok:mfr:ser:lab
11527d82f0f8SDina K Nimeh 	 *	-c cipher -T tok:mfr:ser:lab
11537d82f0f8SDina K Nimeh 	 * the key named "lab" may or may not agree with the given
11547d82f0f8SDina K Nimeh 	 * cipher or the default cipher.  In those cases, cipher will
11557d82f0f8SDina K Nimeh 	 * be overridden with the actual cipher type of the key "lab".
11567d82f0f8SDina K Nimeh 	 */
11577d82f0f8SDina K Nimeh 	*rv = CKR_FUNCTION_FAILED;
11587d82f0f8SDina K Nimeh 
11597d82f0f8SDina K Nimeh 	if (args == NULL) {
11607d82f0f8SDina K Nimeh 		return (B_FALSE);
11617d82f0f8SDina K Nimeh 	}
11627d82f0f8SDina K Nimeh 
11637d82f0f8SDina K Nimeh 	cipher = (mech_alias_t *)args;
11647d82f0f8SDina K Nimeh 	token = cipher->token;
11657d82f0f8SDina K Nimeh 
11667d82f0f8SDina K Nimeh 	if (C_GetMechanismInfo(slot_id, cipher->type, &mechinfo) != CKR_OK) {
11677d82f0f8SDina K Nimeh 		return (B_FALSE);
11687d82f0f8SDina K Nimeh 	}
11697d82f0f8SDina K Nimeh 
11707d82f0f8SDina K Nimeh 	if (token == NULL) {
11717d82f0f8SDina K Nimeh 		if (C_GetMechanismInfo(slot_id, CKM_PKCS5_PBKD2, &mechinfo) !=
11727d82f0f8SDina K Nimeh 		    CKR_OK) {
11737d82f0f8SDina K Nimeh 			return (B_FALSE);
11747d82f0f8SDina K Nimeh 		}
11757d82f0f8SDina K Nimeh 		goto foundit;
11767d82f0f8SDina K Nimeh 	}
11777d82f0f8SDina K Nimeh 
11787d82f0f8SDina K Nimeh 	/* does the token match the token spec? */
11797d82f0f8SDina K Nimeh 	if (token->key == NULL || (C_GetTokenInfo(slot_id, &tokinfo) != CKR_OK))
11807d82f0f8SDina K Nimeh 		return (B_FALSE);
11817d82f0f8SDina K Nimeh 
11827d82f0f8SDina K Nimeh 	token_match = B_TRUE;
11837d82f0f8SDina K Nimeh 
11847d82f0f8SDina K Nimeh 	if (token->name != NULL && (token->name)[0] != '\0' &&
11857d82f0f8SDina K Nimeh 	    strncmp((char *)token->name, (char *)tokinfo.label,
11867d82f0f8SDina K Nimeh 	    TOKEN_LABEL_SIZE) != 0)
11877d82f0f8SDina K Nimeh 		token_match = B_FALSE;
11887d82f0f8SDina K Nimeh 	if (token->mfr != NULL && (token->mfr)[0] != '\0' &&
11897d82f0f8SDina K Nimeh 	    strncmp((char *)token->mfr, (char *)tokinfo.manufacturerID,
11907d82f0f8SDina K Nimeh 	    TOKEN_MANUFACTURER_SIZE) != 0)
11917d82f0f8SDina K Nimeh 		token_match = B_FALSE;
11927d82f0f8SDina K Nimeh 	if (token->serno != NULL && (token->serno)[0] != '\0' &&
11937d82f0f8SDina K Nimeh 	    strncmp((char *)token->serno, (char *)tokinfo.serialNumber,
11947d82f0f8SDina K Nimeh 	    TOKEN_SERIAL_SIZE) != 0)
11957d82f0f8SDina K Nimeh 		token_match = B_FALSE;
11967d82f0f8SDina K Nimeh 
11977d82f0f8SDina K Nimeh 	if (!token_match)
11987d82f0f8SDina K Nimeh 		return (B_FALSE);
11997d82f0f8SDina K Nimeh 
12007d82f0f8SDina K Nimeh foundit:
12017d82f0f8SDina K Nimeh 	cipher->slot = slot_id;
12027d82f0f8SDina K Nimeh 	return (B_TRUE);
12037d82f0f8SDina K Nimeh }
12047d82f0f8SDina K Nimeh 
12057d82f0f8SDina K Nimeh /*
12067d82f0f8SDina K Nimeh  * Clean up crypto loose ends
12077d82f0f8SDina K Nimeh  */
12087d82f0f8SDina K Nimeh static void
12097d82f0f8SDina K Nimeh end_crypto(CK_SESSION_HANDLE sess)
12107d82f0f8SDina K Nimeh {
12117d82f0f8SDina K Nimeh 	(void) C_CloseSession(sess);
12127d82f0f8SDina K Nimeh 	(void) C_Finalize(NULL);
12137d82f0f8SDina K Nimeh }
12147d82f0f8SDina K Nimeh 
12157d82f0f8SDina K Nimeh /*
12167d82f0f8SDina K Nimeh  * Set up crypto, opening session on slot that matches token and cipher
12177d82f0f8SDina K Nimeh  */
12187d82f0f8SDina K Nimeh static void
12197d82f0f8SDina K Nimeh init_crypto(token_spec_t *token, mech_alias_t *cipher,
12207d82f0f8SDina K Nimeh     CK_SESSION_HANDLE_PTR sess)
12217d82f0f8SDina K Nimeh {
12227d82f0f8SDina K Nimeh 	CK_RV	rv;
12237d82f0f8SDina K Nimeh 
12247d82f0f8SDina K Nimeh 	cipher->token = token;
12257d82f0f8SDina K Nimeh 
12267d82f0f8SDina K Nimeh 	/* Turn off Metaslot so that we can see actual tokens */
12277d82f0f8SDina K Nimeh 	if (setenv("METASLOT_ENABLED", "false", 1) < 0) {
12287d82f0f8SDina K Nimeh 		die(gettext("could not disable Metaslot"));
12297d82f0f8SDina K Nimeh 	}
12307d82f0f8SDina K Nimeh 
12317d82f0f8SDina K Nimeh 	rv = pkcs11_GetCriteriaSession(match_token_cipher, (void *)cipher,
12327d82f0f8SDina K Nimeh 	    sess);
12337d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
12347d82f0f8SDina K Nimeh 		end_crypto(*sess);
12357d82f0f8SDina K Nimeh 		if (rv == CKR_HOST_MEMORY) {
12367d82f0f8SDina K Nimeh 			die("malloc");
12377d82f0f8SDina K Nimeh 		}
12387d82f0f8SDina K Nimeh 		die(gettext("failed to find any cryptographic provider, "
12397d82f0f8SDina K Nimeh 		    "use \"cryptoadm list -p\" to find providers: %s\n"),
12407d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
12417d82f0f8SDina K Nimeh 	}
12427d82f0f8SDina K Nimeh }
12437d82f0f8SDina K Nimeh 
12447d82f0f8SDina K Nimeh /*
124587117650Saalok  * Uncompress a file.
124687117650Saalok  *
124787117650Saalok  * First map the file in to establish a device
124887117650Saalok  * association, then read from it. On-the-fly
124987117650Saalok  * decompression will automatically uncompress
125087117650Saalok  * the file if it's compressed
125187117650Saalok  *
125287117650Saalok  * If the file is mapped and a device association
125387117650Saalok  * has been established, disallow uncompressing
125487117650Saalok  * the file until it is unmapped.
125587117650Saalok  */
125687117650Saalok static void
125787117650Saalok lofi_uncompress(int lfd, const char *filename)
12587c478bd9Sstevel@tonic-gate {
125987117650Saalok 	struct lofi_ioctl li;
126087117650Saalok 	char buf[MAXBSIZE];
126187117650Saalok 	char devicename[32];
126287117650Saalok 	char tmpfilename[MAXPATHLEN];
12637d82f0f8SDina K Nimeh 	char *x;
126487117650Saalok 	char *dir = NULL;
126587117650Saalok 	char *file = NULL;
126687117650Saalok 	int minor = 0;
126787117650Saalok 	struct stat64 statbuf;
126887117650Saalok 	int compfd = -1;
126987117650Saalok 	int uncompfd = -1;
127087117650Saalok 	ssize_t rbytes;
127187117650Saalok 
127287117650Saalok 	/*
127387117650Saalok 	 * Disallow uncompressing the file if it is
127487117650Saalok 	 * already mapped.
127587117650Saalok 	 */
127687117650Saalok 	li.li_minor = 0;
127787117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
127887117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1)
127987117650Saalok 		die(gettext("%s must be unmapped before uncompressing"),
128087117650Saalok 		    filename);
128187117650Saalok 
128287117650Saalok 	/* Zero length files don't need to be uncompressed */
128387117650Saalok 	if (stat64(filename, &statbuf) == -1)
128487117650Saalok 		die(gettext("stat: %s"), filename);
128587117650Saalok 	if (statbuf.st_size == 0)
128687117650Saalok 		return;
128787117650Saalok 
12887d82f0f8SDina K Nimeh 	minor = lofi_map_file(lfd, li, filename);
128987117650Saalok 	(void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d",
129087117650Saalok 	    LOFI_BLOCK_NAME, minor);
129187117650Saalok 
129287117650Saalok 	/* If the file isn't compressed, we just return */
129387117650Saalok 	if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) ||
1294a423e759Saalok 	    (li.li_algorithm[0] == '\0')) {
129587117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1296a423e759Saalok 		die("%s is not compressed\n", filename);
129787117650Saalok 	}
129887117650Saalok 
129987117650Saalok 	if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) {
130087117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
130187117650Saalok 		die(gettext("open: %s"), filename);
130287117650Saalok 	}
130387117650Saalok 	/* Create a temp file in the same directory */
13047d82f0f8SDina K Nimeh 	x = strdup(filename);
13057d82f0f8SDina K Nimeh 	dir = strdup(dirname(x));
13067d82f0f8SDina K Nimeh 	free(x);
13077d82f0f8SDina K Nimeh 	x = strdup(filename);
13087d82f0f8SDina K Nimeh 	file = strdup(basename(x));
13097d82f0f8SDina K Nimeh 	free(x);
131087117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
131187117650Saalok 	    "%s/.%sXXXXXX", dir, file);
13127d82f0f8SDina K Nimeh 	free(dir);
13137d82f0f8SDina K Nimeh 	free(file);
131487117650Saalok 
131587117650Saalok 	if ((uncompfd = mkstemp64(tmpfilename)) == -1) {
131687117650Saalok 		(void) close(compfd);
131787117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1318a423e759Saalok 		die("%s could not be uncompressed\n", filename);
131987117650Saalok 	}
132087117650Saalok 
132187117650Saalok 	/*
132287117650Saalok 	 * Set the mode bits and the owner of this temporary
132387117650Saalok 	 * file to be that of the original uncompressed file
132487117650Saalok 	 */
132587117650Saalok 	(void) fchmod(uncompfd, statbuf.st_mode);
132687117650Saalok 
132787117650Saalok 	if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) {
132887117650Saalok 		(void) close(compfd);
132987117650Saalok 		(void) close(uncompfd);
133087117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1331a423e759Saalok 		die("%s could not be uncompressed\n", filename);
133287117650Saalok 	}
133387117650Saalok 
133487117650Saalok 	/* Now read from the device in MAXBSIZE-sized chunks */
133587117650Saalok 	for (;;) {
133687117650Saalok 		rbytes = read(compfd, buf, sizeof (buf));
133787117650Saalok 
133887117650Saalok 		if (rbytes <= 0)
133987117650Saalok 			break;
134087117650Saalok 
134187117650Saalok 		if (write(uncompfd, buf, rbytes) != rbytes) {
134287117650Saalok 			rbytes = -1;
134387117650Saalok 			break;
134487117650Saalok 		}
134587117650Saalok 	}
134687117650Saalok 
134787117650Saalok 	(void) close(compfd);
134887117650Saalok 	(void) close(uncompfd);
134987117650Saalok 
135087117650Saalok 	/* Delete the mapping */
135187117650Saalok 	delete_mapping(lfd, devicename, filename, B_TRUE);
135287117650Saalok 
135387117650Saalok 	/*
135487117650Saalok 	 * If an error occured while reading or writing, rbytes will
135587117650Saalok 	 * be negative
135687117650Saalok 	 */
135787117650Saalok 	if (rbytes < 0) {
135887117650Saalok 		(void) unlink(tmpfilename);
135987117650Saalok 		die(gettext("could not read from %s"), filename);
136087117650Saalok 	}
136187117650Saalok 
136287117650Saalok 	/* Rename the temp file to the actual file */
136387117650Saalok 	if (rename(tmpfilename, filename) == -1)
136487117650Saalok 		(void) unlink(tmpfilename);
136587117650Saalok }
136687117650Saalok 
136787117650Saalok /*
136887117650Saalok  * Compress a file
136987117650Saalok  */
137087117650Saalok static void
13719525893dSaalok lofi_compress(int *lfd, const char *filename, int compress_index,
137287117650Saalok     uint32_t segsize)
137387117650Saalok {
137487117650Saalok 	struct lofi_ioctl lic;
137587117650Saalok 	lofi_compress_info_t *li;
13769525893dSaalok 	struct flock lock;
137787117650Saalok 	char tmpfilename[MAXPATHLEN];
137887117650Saalok 	char comp_filename[MAXPATHLEN];
137987117650Saalok 	char algorithm[MAXALGLEN];
13807d82f0f8SDina K Nimeh 	char *x;
138187117650Saalok 	char *dir = NULL, *file = NULL;
138287117650Saalok 	uchar_t *uncompressed_seg = NULL;
138387117650Saalok 	uchar_t *compressed_seg = NULL;
138487117650Saalok 	uint32_t compressed_segsize;
138587117650Saalok 	uint32_t len_compressed, count;
138687117650Saalok 	uint32_t index_entries, index_sz;
138787117650Saalok 	uint64_t *index = NULL;
138887117650Saalok 	uint64_t offset;
138987117650Saalok 	size_t real_segsize;
139087117650Saalok 	struct stat64 statbuf;
139187117650Saalok 	int compfd = -1, uncompfd = -1;
139287117650Saalok 	int tfd = -1;
139387117650Saalok 	ssize_t rbytes, wbytes, lastread;
139487117650Saalok 	int i, type;
139587117650Saalok 
139687117650Saalok 	/*
139787117650Saalok 	 * Disallow compressing the file if it is
139887117650Saalok 	 * already mapped
139987117650Saalok 	 */
140087117650Saalok 	lic.li_minor = 0;
140187117650Saalok 	(void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
14029525893dSaalok 	if (ioctl(*lfd, LOFI_GET_MINOR, &lic) != -1)
140387117650Saalok 		die(gettext("%s must be unmapped before compressing"),
140487117650Saalok 		    filename);
140587117650Saalok 
14069525893dSaalok 	/*
14079525893dSaalok 	 * Close the control device so other operations
14089525893dSaalok 	 * can use it
14099525893dSaalok 	 */
14109525893dSaalok 	(void) close(*lfd);
14119525893dSaalok 	*lfd = -1;
14129525893dSaalok 
141387117650Saalok 	li = &lofi_compress_table[compress_index];
141487117650Saalok 
141587117650Saalok 	/*
141687117650Saalok 	 * The size of the buffer to hold compressed data must
141787117650Saalok 	 * be slightly larger than the compressed segment size.
141887117650Saalok 	 *
141987117650Saalok 	 * The compress functions use part of the buffer as
142087117650Saalok 	 * scratch space to do calculations.
142187117650Saalok 	 * Ref: http://www.zlib.net/manual.html#compress2
142287117650Saalok 	 */
142387117650Saalok 	compressed_segsize = segsize + (segsize >> 6);
142487117650Saalok 	compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
142587117650Saalok 	uncompressed_seg = (uchar_t *)malloc(segsize);
142687117650Saalok 
142787117650Saalok 	if (compressed_seg == NULL || uncompressed_seg == NULL)
142887117650Saalok 		die(gettext("No memory"));
142987117650Saalok 
14309525893dSaalok 	if ((uncompfd = open64(filename, O_RDWR|O_LARGEFILE, 0)) == -1)
143187117650Saalok 		die(gettext("open: %s"), filename);
143287117650Saalok 
14339525893dSaalok 	lock.l_type = F_WRLCK;
14349525893dSaalok 	lock.l_whence = SEEK_SET;
14359525893dSaalok 	lock.l_start = 0;
14369525893dSaalok 	lock.l_len = 0;
14379525893dSaalok 
14389525893dSaalok 	/*
14399525893dSaalok 	 * Use an advisory lock to ensure that only a
14409525893dSaalok 	 * single lofiadm process compresses a given
14419525893dSaalok 	 * file at any given time
14429525893dSaalok 	 *
14439525893dSaalok 	 * A close on the file descriptor automatically
14449525893dSaalok 	 * closes all lock state on the file
14459525893dSaalok 	 */
14469525893dSaalok 	if (fcntl(uncompfd, F_SETLKW, &lock) == -1)
14479525893dSaalok 		die(gettext("fcntl: %s"), filename);
14489525893dSaalok 
144987117650Saalok 	if (fstat64(uncompfd, &statbuf) == -1) {
145087117650Saalok 		(void) close(uncompfd);
145187117650Saalok 		die(gettext("fstat: %s"), filename);
145287117650Saalok 	}
145387117650Saalok 
145487117650Saalok 	/* Zero length files don't need to be compressed */
145587117650Saalok 	if (statbuf.st_size == 0) {
145687117650Saalok 		(void) close(uncompfd);
145787117650Saalok 		return;
145887117650Saalok 	}
145987117650Saalok 
146087117650Saalok 	/*
146187117650Saalok 	 * Create temporary files in the same directory that
146287117650Saalok 	 * will hold the intermediate data
146387117650Saalok 	 */
14647d82f0f8SDina K Nimeh 	x = strdup(filename);
14657d82f0f8SDina K Nimeh 	dir = strdup(dirname(x));
14667d82f0f8SDina K Nimeh 	free(x);
14677d82f0f8SDina K Nimeh 	x = strdup(filename);
14687d82f0f8SDina K Nimeh 	file = strdup(basename(x));
14697d82f0f8SDina K Nimeh 	free(x);
147087117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
147187117650Saalok 	    "%s/.%sXXXXXX", dir, file);
147287117650Saalok 	(void) snprintf(comp_filename, sizeof (comp_filename),
147387117650Saalok 	    "%s/.%sXXXXXX", dir, file);
14747d82f0f8SDina K Nimeh 	free(dir);
14757d82f0f8SDina K Nimeh 	free(file);
147687117650Saalok 
147787117650Saalok 	if ((tfd = mkstemp64(tmpfilename)) == -1)
147887117650Saalok 		goto cleanup;
147987117650Saalok 
148087117650Saalok 	if ((compfd = mkstemp64(comp_filename)) == -1)
148187117650Saalok 		goto cleanup;
148287117650Saalok 
148387117650Saalok 	/*
148487117650Saalok 	 * Set the mode bits and owner of the compressed
148587117650Saalok 	 * file to be that of the original uncompressed file
148687117650Saalok 	 */
148787117650Saalok 	(void) fchmod(compfd, statbuf.st_mode);
148887117650Saalok 
148987117650Saalok 	if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
149087117650Saalok 		goto cleanup;
149187117650Saalok 
149287117650Saalok 	/*
149387117650Saalok 	 * Calculate the number of index entries required.
149487117650Saalok 	 * index entries are stored as an array. adding
149587117650Saalok 	 * a '2' here accounts for the fact that the last
149687117650Saalok 	 * segment may not be a multiple of the segment size
149787117650Saalok 	 */
149887117650Saalok 	index_sz = (statbuf.st_size / segsize) + 2;
149987117650Saalok 	index = malloc(sizeof (*index) * index_sz);
150087117650Saalok 
150187117650Saalok 	if (index == NULL)
150287117650Saalok 		goto cleanup;
150387117650Saalok 
150487117650Saalok 	offset = 0;
150587117650Saalok 	lastread = segsize;
150687117650Saalok 	count = 0;
150787117650Saalok 
150887117650Saalok 	/*
150987117650Saalok 	 * Now read from the uncompressed file in 'segsize'
151087117650Saalok 	 * sized chunks, compress what was read in and
151187117650Saalok 	 * write it out to a temporary file
151287117650Saalok 	 */
151387117650Saalok 	for (;;) {
151487117650Saalok 		rbytes = read(uncompfd, uncompressed_seg, segsize);
151587117650Saalok 
151687117650Saalok 		if (rbytes <= 0)
151787117650Saalok 			break;
151887117650Saalok 
151987117650Saalok 		if (lastread < segsize)
152087117650Saalok 			goto cleanup;
152187117650Saalok 
152287117650Saalok 		/*
152387117650Saalok 		 * Account for the first byte that
152487117650Saalok 		 * indicates whether a segment is
152587117650Saalok 		 * compressed or not
152687117650Saalok 		 */
152787117650Saalok 		real_segsize = segsize - 1;
152887117650Saalok 		(void) li->l_compress(uncompressed_seg, rbytes,
152987117650Saalok 		    compressed_seg + SEGHDR, &real_segsize, li->l_level);
153087117650Saalok 
153187117650Saalok 		/*
153287117650Saalok 		 * If the length of the compressed data is more
153387117650Saalok 		 * than a threshold then there isn't any benefit
153487117650Saalok 		 * to be had from compressing this segment - leave
153587117650Saalok 		 * it uncompressed.
153687117650Saalok 		 *
153787117650Saalok 		 * NB. In case an error occurs during compression (above)
153887117650Saalok 		 * the 'real_segsize' isn't changed. The logic below
153987117650Saalok 		 * ensures that that segment is left uncompressed.
154087117650Saalok 		 */
154187117650Saalok 		len_compressed = real_segsize;
154287117650Saalok 		if (real_segsize > segsize - COMPRESS_THRESHOLD) {
154387117650Saalok 			(void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
154487117650Saalok 			    rbytes);
154587117650Saalok 			type = UNCOMPRESSED;
154687117650Saalok 			len_compressed = rbytes;
154787117650Saalok 		} else {
154887117650Saalok 			type = COMPRESSED;
154987117650Saalok 		}
155087117650Saalok 
155187117650Saalok 		/*
155287117650Saalok 		 * Set the first byte or the SEGHDR to
155387117650Saalok 		 * indicate if it's compressed or not
155487117650Saalok 		 */
155587117650Saalok 		*compressed_seg = type;
155687117650Saalok 		wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
155787117650Saalok 		if (wbytes != (len_compressed + SEGHDR)) {
155887117650Saalok 			rbytes = -1;
155987117650Saalok 			break;
156087117650Saalok 		}
156187117650Saalok 
156287117650Saalok 		index[count] = BE_64(offset);
156387117650Saalok 		offset += wbytes;
156487117650Saalok 		lastread = rbytes;
156587117650Saalok 		count++;
156687117650Saalok 	}
156787117650Saalok 
156887117650Saalok 	(void) close(uncompfd);
156987117650Saalok 
157087117650Saalok 	if (rbytes < 0)
157187117650Saalok 		goto cleanup;
157287117650Saalok 	/*
157387117650Saalok 	 * The last index entry is a sentinel entry. It does not point to
157487117650Saalok 	 * an actual compressed segment but helps in computing the size of
157587117650Saalok 	 * the compressed segment. The size of each compressed segment is
157687117650Saalok 	 * computed by subtracting the current index value from the next
157787117650Saalok 	 * one (the compressed blocks are stored sequentially)
157887117650Saalok 	 */
157987117650Saalok 	index[count++] = BE_64(offset);
158087117650Saalok 
158187117650Saalok 	/*
158287117650Saalok 	 * Now write the compressed data along with the
158387117650Saalok 	 * header information to this file which will
158487117650Saalok 	 * later be renamed to the original uncompressed
158587117650Saalok 	 * file name
158687117650Saalok 	 *
158787117650Saalok 	 * The header is as follows -
158887117650Saalok 	 *
158987117650Saalok 	 * Signature (name of the compression algorithm)
159087117650Saalok 	 * Compression segment size (a multiple of 512)
159187117650Saalok 	 * Number of index entries
159287117650Saalok 	 * Size of the last block
159387117650Saalok 	 * The array containing the index entries
159487117650Saalok 	 *
159587117650Saalok 	 * the header is always stored in network byte
159687117650Saalok 	 * order
159787117650Saalok 	 */
159887117650Saalok 	(void) bzero(algorithm, sizeof (algorithm));
159987117650Saalok 	(void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
160087117650Saalok 	if (write(compfd, algorithm, sizeof (algorithm))
160187117650Saalok 	    != sizeof (algorithm))
160287117650Saalok 		goto cleanup;
160387117650Saalok 
160487117650Saalok 	segsize = htonl(segsize);
160587117650Saalok 	if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
160687117650Saalok 		goto cleanup;
160787117650Saalok 
160887117650Saalok 	index_entries = htonl(count);
160987117650Saalok 	if (write(compfd, &index_entries, sizeof (index_entries)) !=
161087117650Saalok 	    sizeof (index_entries))
161187117650Saalok 		goto cleanup;
161287117650Saalok 
161387117650Saalok 	lastread = htonl(lastread);
161487117650Saalok 	if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
161587117650Saalok 		goto cleanup;
161687117650Saalok 
161787117650Saalok 	for (i = 0; i < count; i++) {
161887117650Saalok 		if (write(compfd, index + i, sizeof (*index)) !=
161987117650Saalok 		    sizeof (*index))
162087117650Saalok 			goto cleanup;
162187117650Saalok 	}
162287117650Saalok 
162387117650Saalok 	/* Header is written, now write the compressed data */
162487117650Saalok 	if (lseek(tfd, 0, SEEK_SET) != 0)
162587117650Saalok 		goto cleanup;
162687117650Saalok 
162787117650Saalok 	rbytes = wbytes = 0;
162887117650Saalok 
162987117650Saalok 	for (;;) {
163087117650Saalok 		rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
163187117650Saalok 
163287117650Saalok 		if (rbytes <= 0)
163387117650Saalok 			break;
163487117650Saalok 
163587117650Saalok 		if (write(compfd, compressed_seg, rbytes) != rbytes)
163687117650Saalok 			goto cleanup;
163787117650Saalok 	}
163887117650Saalok 
163987117650Saalok 	if (fstat64(compfd, &statbuf) == -1)
164087117650Saalok 		goto cleanup;
164187117650Saalok 
164287117650Saalok 	/*
164387117650Saalok 	 * Round up the compressed file size to be a multiple of
164487117650Saalok 	 * DEV_BSIZE. lofi(7D) likes it that way.
164587117650Saalok 	 */
164687117650Saalok 	if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
164787117650Saalok 
164887117650Saalok 		offset = DEV_BSIZE - offset;
164987117650Saalok 
165087117650Saalok 		for (i = 0; i < offset; i++)
165187117650Saalok 			uncompressed_seg[i] = '\0';
165287117650Saalok 		if (write(compfd, uncompressed_seg, offset) != offset)
165387117650Saalok 			goto cleanup;
165487117650Saalok 	}
165587117650Saalok 	(void) close(compfd);
165687117650Saalok 	(void) close(tfd);
165787117650Saalok 	(void) unlink(tmpfilename);
165887117650Saalok cleanup:
165987117650Saalok 	if (rbytes < 0) {
166087117650Saalok 		if (tfd != -1)
166187117650Saalok 			(void) unlink(tmpfilename);
166287117650Saalok 		if (compfd != -1)
166387117650Saalok 			(void) unlink(comp_filename);
166487117650Saalok 		die(gettext("error compressing file %s"), filename);
166587117650Saalok 	} else {
166687117650Saalok 		/* Rename the compressed file to the actual file */
166787117650Saalok 		if (rename(comp_filename, filename) == -1) {
166887117650Saalok 			(void) unlink(comp_filename);
166987117650Saalok 			die(gettext("error compressing file %s"), filename);
167087117650Saalok 		}
167187117650Saalok 	}
167287117650Saalok 	if (compressed_seg != NULL)
167387117650Saalok 		free(compressed_seg);
167487117650Saalok 	if (uncompressed_seg != NULL)
167587117650Saalok 		free(uncompressed_seg);
167687117650Saalok 	if (index != NULL)
167787117650Saalok 		free(index);
167887117650Saalok 	if (compfd != -1)
167987117650Saalok 		(void) close(compfd);
168087117650Saalok 	if (uncompfd != -1)
168187117650Saalok 		(void) close(uncompfd);
168287117650Saalok 	if (tfd != -1)
168387117650Saalok 		(void) close(tfd);
168487117650Saalok }
168587117650Saalok 
168687117650Saalok static int
168787117650Saalok lofi_compress_select(const char *algname)
168887117650Saalok {
168987117650Saalok 	int i;
169087117650Saalok 
169187117650Saalok 	for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
169287117650Saalok 		if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
169387117650Saalok 			return (i);
169487117650Saalok 	}
169587117650Saalok 	return (-1);
169687117650Saalok }
169787117650Saalok 
169887117650Saalok static void
169987117650Saalok check_algorithm_validity(const char *algname, int *compress_index)
170087117650Saalok {
170187117650Saalok 	*compress_index = lofi_compress_select(algname);
170287117650Saalok 	if (*compress_index < 0)
170387117650Saalok 		die(gettext("invalid algorithm name: %s\n"), algname);
170487117650Saalok }
170587117650Saalok 
170687117650Saalok static void
170787117650Saalok check_file_validity(const char *filename)
170887117650Saalok {
17097c478bd9Sstevel@tonic-gate 	struct stat64 buf;
171087117650Saalok 	int 	error;
17117d82f0f8SDina K Nimeh 	int	fd;
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate 	fd = open64(filename, O_RDONLY);
17147c478bd9Sstevel@tonic-gate 	if (fd == -1) {
17157c478bd9Sstevel@tonic-gate 		die(gettext("open: %s"), filename);
17167c478bd9Sstevel@tonic-gate 	}
17177c478bd9Sstevel@tonic-gate 	error = fstat64(fd, &buf);
17187c478bd9Sstevel@tonic-gate 	if (error == -1) {
17197c478bd9Sstevel@tonic-gate 		die(gettext("fstat: %s"), filename);
17207c478bd9Sstevel@tonic-gate 	} else if (!S_ISLOFIABLE(buf.st_mode)) {
17217c478bd9Sstevel@tonic-gate 		die(gettext("%s is not a regular file, "
17227c478bd9Sstevel@tonic-gate 		    "block, or character device\n"),
17237c478bd9Sstevel@tonic-gate 		    filename);
17247c478bd9Sstevel@tonic-gate 	} else if ((buf.st_size % DEV_BSIZE) != 0) {
1725f9153c6bSDina K Nimeh 		die(gettext("size of %s is not a multiple of %d\n"),
17267c478bd9Sstevel@tonic-gate 		    filename, DEV_BSIZE);
17277c478bd9Sstevel@tonic-gate 	}
17287c478bd9Sstevel@tonic-gate 	(void) close(fd);
172987117650Saalok 
173087117650Saalok 	if (name_to_minor(filename) != 0) {
1731f9153c6bSDina K Nimeh 		die(gettext("cannot use %s on itself\n"), LOFI_DRIVER_NAME);
17327c478bd9Sstevel@tonic-gate 	}
173387117650Saalok }
173487117650Saalok 
173587117650Saalok static uint32_t
173687117650Saalok convert_to_num(const char *str)
173787117650Saalok {
173887117650Saalok 	int len;
173987117650Saalok 	uint32_t segsize, mult = 1;
174087117650Saalok 
174187117650Saalok 	len = strlen(str);
174287117650Saalok 	if (len && isalpha(str[len - 1])) {
174387117650Saalok 		switch (str[len - 1]) {
174487117650Saalok 		case 'k':
174587117650Saalok 		case 'K':
174687117650Saalok 			mult = KILOBYTE;
174787117650Saalok 			break;
174887117650Saalok 		case 'b':
174987117650Saalok 		case 'B':
175087117650Saalok 			mult = BLOCK_SIZE;
175187117650Saalok 			break;
175287117650Saalok 		case 'm':
175387117650Saalok 		case 'M':
175487117650Saalok 			mult = MEGABYTE;
175587117650Saalok 			break;
175687117650Saalok 		case 'g':
175787117650Saalok 		case 'G':
175887117650Saalok 			mult = GIGABYTE;
175987117650Saalok 			break;
176087117650Saalok 		default:
176187117650Saalok 			die(gettext("invalid segment size %s\n"), str);
176287117650Saalok 		}
176387117650Saalok 	}
176487117650Saalok 
176587117650Saalok 	segsize = atol(str);
176687117650Saalok 	segsize *= mult;
176787117650Saalok 
176887117650Saalok 	return (segsize);
176987117650Saalok }
177087117650Saalok 
177187117650Saalok int
177287117650Saalok main(int argc, char *argv[])
177387117650Saalok {
177487117650Saalok 	int	lfd;
177587117650Saalok 	int	c;
177687117650Saalok 	const char *devicename = NULL;
177787117650Saalok 	const char *filename = NULL;
177887117650Saalok 	const char *algname = COMPRESS_ALGORITHM;
177987117650Saalok 	int	openflag;
178087117650Saalok 	int	minor;
178187117650Saalok 	int 	compress_index;
178287117650Saalok 	uint32_t segsize = SEGSIZE;
178387117650Saalok 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
178487117650Saalok 	boolean_t force = B_FALSE;
17857d82f0f8SDina K Nimeh 	const char *pname;
17867d82f0f8SDina K Nimeh 	boolean_t errflag = B_FALSE;
17877d82f0f8SDina K Nimeh 	boolean_t addflag = B_FALSE;
17887d82f0f8SDina K Nimeh 	boolean_t deleteflag = B_FALSE;
17897d82f0f8SDina K Nimeh 	boolean_t ephflag = B_FALSE;
17907d82f0f8SDina K Nimeh 	boolean_t compressflag = B_FALSE;
17917d82f0f8SDina K Nimeh 	boolean_t uncompressflag = B_FALSE;
17927d82f0f8SDina K Nimeh 	/* the next two work together for -c, -k, -T, -e options only */
17937d82f0f8SDina K Nimeh 	boolean_t need_crypto = B_FALSE;	/* if any -c, -k, -T, -e */
17947d82f0f8SDina K Nimeh 	boolean_t cipher_only = B_TRUE;		/* if -c only */
17957d82f0f8SDina K Nimeh 	const char *keyfile = NULL;
17967d82f0f8SDina K Nimeh 	mech_alias_t *cipher = NULL;
17977d82f0f8SDina K Nimeh 	token_spec_t *token = NULL;
17987d82f0f8SDina K Nimeh 	char	*rkey = NULL;
17997d82f0f8SDina K Nimeh 	size_t	rksz = 0;
1800f9153c6bSDina K Nimeh 	char realfilename[MAXPATHLEN];
180187117650Saalok 
180287117650Saalok 	pname = getpname(argv[0]);
180387117650Saalok 
180487117650Saalok 	(void) setlocale(LC_ALL, "");
180587117650Saalok 	(void) textdomain(TEXT_DOMAIN);
180687117650Saalok 
18077d82f0f8SDina K Nimeh 	while ((c = getopt(argc, argv, "a:c:Cd:efk:o:s:T:U")) != EOF) {
180887117650Saalok 		switch (c) {
180987117650Saalok 		case 'a':
18107d82f0f8SDina K Nimeh 			addflag = B_TRUE;
1811f9153c6bSDina K Nimeh 			if ((filename = realpath(optarg, realfilename)) == NULL)
1812f9153c6bSDina K Nimeh 				die("%s", optarg);
18137c478bd9Sstevel@tonic-gate 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
18147c478bd9Sstevel@tonic-gate 				/* optional device */
18157c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
18167c478bd9Sstevel@tonic-gate 				optind++;
18177c478bd9Sstevel@tonic-gate 			}
18187c478bd9Sstevel@tonic-gate 			break;
181987117650Saalok 		case 'C':
18207d82f0f8SDina K Nimeh 			compressflag = B_TRUE;
18217d82f0f8SDina K Nimeh 			if (((argc - optind) > 1) && (*argv[optind] != '-')) {
18227d82f0f8SDina K Nimeh 				/* optional algorithm */
18237d82f0f8SDina K Nimeh 				algname = argv[optind];
182487117650Saalok 				optind++;
182587117650Saalok 			}
182687117650Saalok 			check_algorithm_validity(algname, &compress_index);
182787117650Saalok 			break;
18287d82f0f8SDina K Nimeh 		case 'c':
18297d82f0f8SDina K Nimeh 			/* is the chosen cipher allowed? */
18307d82f0f8SDina K Nimeh 			if ((cipher = ciph2mech(optarg)) == NULL) {
18317d82f0f8SDina K Nimeh 				errflag = B_TRUE;
18327d82f0f8SDina K Nimeh 				warn(gettext("cipher %s not allowed\n"),
18337d82f0f8SDina K Nimeh 				    optarg);
18347d82f0f8SDina K Nimeh 			}
18357d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
18367d82f0f8SDina K Nimeh 			/* cipher_only is already set */
18377d82f0f8SDina K Nimeh 			break;
18387c478bd9Sstevel@tonic-gate 		case 'd':
18397d82f0f8SDina K Nimeh 			deleteflag = B_TRUE;
18407c478bd9Sstevel@tonic-gate 			minor = name_to_minor(optarg);
18417c478bd9Sstevel@tonic-gate 			if (minor != 0)
18427c478bd9Sstevel@tonic-gate 				devicename = optarg;
1843f9153c6bSDina K Nimeh 			else {
1844f9153c6bSDina K Nimeh 				if ((filename = realpath(optarg,
1845f9153c6bSDina K Nimeh 				    realfilename)) == NULL)
1846f9153c6bSDina K Nimeh 					die("%s", optarg);
1847f9153c6bSDina K Nimeh 			}
18487c478bd9Sstevel@tonic-gate 			break;
18497d82f0f8SDina K Nimeh 		case 'e':
18507d82f0f8SDina K Nimeh 			ephflag = B_TRUE;
18517d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
18527d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
18537d82f0f8SDina K Nimeh 			break;
18543d7072f8Seschrock 		case 'f':
18553d7072f8Seschrock 			force = B_TRUE;
18563d7072f8Seschrock 			break;
18577d82f0f8SDina K Nimeh 		case 'k':
18587d82f0f8SDina K Nimeh 			keyfile = optarg;
18597d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
18607d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
18617d82f0f8SDina K Nimeh 			break;
186287117650Saalok 		case 's':
186387117650Saalok 			segsize = convert_to_num(optarg);
186487117650Saalok 			if (segsize == 0 || segsize % DEV_BSIZE)
186587117650Saalok 				die(gettext("segment size %s is invalid "
186687117650Saalok 				    "or not a multiple of minimum block "
186787117650Saalok 				    "size %ld\n"), optarg, DEV_BSIZE);
18687d82f0f8SDina K Nimeh 			break;
18697d82f0f8SDina K Nimeh 		case 'T':
18707d82f0f8SDina K Nimeh 			if ((token = parsetoken(optarg)) == NULL) {
18717d82f0f8SDina K Nimeh 				errflag = B_TRUE;
18727d82f0f8SDina K Nimeh 				warn(
18737d82f0f8SDina K Nimeh 				    gettext("invalid token key specifier %s\n"),
18747d82f0f8SDina K Nimeh 				    optarg);
18757d82f0f8SDina K Nimeh 			}
18767d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
18777d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
187887117650Saalok 			break;
187987117650Saalok 		case 'U':
18807d82f0f8SDina K Nimeh 			uncompressflag = B_TRUE;
188187117650Saalok 			break;
18827c478bd9Sstevel@tonic-gate 		case '?':
18837c478bd9Sstevel@tonic-gate 		default:
18847d82f0f8SDina K Nimeh 			errflag = B_TRUE;
18857c478bd9Sstevel@tonic-gate 			break;
18867c478bd9Sstevel@tonic-gate 		}
18877c478bd9Sstevel@tonic-gate 	}
18887d82f0f8SDina K Nimeh 
18897d82f0f8SDina K Nimeh 	/* Check for mutually exclusive combinations of options */
189087117650Saalok 	if (errflag ||
189187117650Saalok 	    (addflag && deleteflag) ||
18927d82f0f8SDina K Nimeh 	    (!addflag && need_crypto) ||
189387117650Saalok 	    ((compressflag || uncompressflag) && (addflag || deleteflag)))
18947d82f0f8SDina K Nimeh 		usage(pname);
18957d82f0f8SDina K Nimeh 
18967d82f0f8SDina K Nimeh 	/* ephemeral key, and key from either file or token are incompatible */
18977d82f0f8SDina K Nimeh 	if (ephflag && (keyfile != NULL || token != NULL)) {
18987d82f0f8SDina K Nimeh 		die(gettext("ephemeral key cannot be used with keyfile"
18997d82f0f8SDina K Nimeh 		    " or token key\n"));
19007d82f0f8SDina K Nimeh 	}
19017d82f0f8SDina K Nimeh 
19027d82f0f8SDina K Nimeh 	/*
19037d82f0f8SDina K Nimeh 	 * "-c" but no "-k", "-T", "-e", or "-T -k" means derive key from
19047d82f0f8SDina K Nimeh 	 * command line passphrase
19057d82f0f8SDina K Nimeh 	 */
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	switch (argc - optind) {
19087c478bd9Sstevel@tonic-gate 	case 0: /* no more args */
19097d82f0f8SDina K Nimeh 		if (compressflag || uncompressflag)	/* needs filename */
19107d82f0f8SDina K Nimeh 			usage(pname);
19117c478bd9Sstevel@tonic-gate 		break;
19127d82f0f8SDina K Nimeh 	case 1:
19137c478bd9Sstevel@tonic-gate 		if (addflag || deleteflag)
19147d82f0f8SDina K Nimeh 			usage(pname);
19157d82f0f8SDina K Nimeh 		/* one arg means compress/uncompress the file ... */
19167d82f0f8SDina K Nimeh 		if (compressflag || uncompressflag) {
19177d82f0f8SDina K Nimeh 			if ((filename = realpath(argv[optind],
19187d82f0f8SDina K Nimeh 			    realfilename)) == NULL)
19197d82f0f8SDina K Nimeh 				die("%s", argv[optind]);
19207d82f0f8SDina K Nimeh 		/* ... or without options means print the association */
19217d82f0f8SDina K Nimeh 		} else {
19227c478bd9Sstevel@tonic-gate 			minor = name_to_minor(argv[optind]);
19237c478bd9Sstevel@tonic-gate 			if (minor != 0)
19247c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
1925f9153c6bSDina K Nimeh 			else {
1926f9153c6bSDina K Nimeh 				if ((filename = realpath(argv[optind],
1927f9153c6bSDina K Nimeh 				    realfilename)) == NULL)
1928f9153c6bSDina K Nimeh 					die("%s", argv[optind]);
1929f9153c6bSDina K Nimeh 			}
19307d82f0f8SDina K Nimeh 		}
19317c478bd9Sstevel@tonic-gate 		break;
19327c478bd9Sstevel@tonic-gate 	default:
19337d82f0f8SDina K Nimeh 		usage(pname);
19347c478bd9Sstevel@tonic-gate 		break;
19357c478bd9Sstevel@tonic-gate 	}
19367c478bd9Sstevel@tonic-gate 
19377d82f0f8SDina K Nimeh 	if (addflag || compressflag || uncompressflag)
19387d82f0f8SDina K Nimeh 		check_file_validity(filename);
19397d82f0f8SDina K Nimeh 
19407c478bd9Sstevel@tonic-gate 	if (filename && !valid_abspath(filename))
19417c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
19427c478bd9Sstevel@tonic-gate 
19437c478bd9Sstevel@tonic-gate 	/*
19447c478bd9Sstevel@tonic-gate 	 * Here, we know the arguments are correct, the filename is an
19457c478bd9Sstevel@tonic-gate 	 * absolute path, it exists and is a regular file. We don't yet
19467c478bd9Sstevel@tonic-gate 	 * know that the device name is ok or not.
19477c478bd9Sstevel@tonic-gate 	 */
19487d82f0f8SDina K Nimeh 
19497c478bd9Sstevel@tonic-gate 	openflag = O_EXCL;
195087117650Saalok 	if (addflag || deleteflag || compressflag || uncompressflag)
19517c478bd9Sstevel@tonic-gate 		openflag |= O_RDWR;
19527c478bd9Sstevel@tonic-gate 	else
19537c478bd9Sstevel@tonic-gate 		openflag |= O_RDONLY;
19547c478bd9Sstevel@tonic-gate 	lfd = open(lofictl, openflag);
19557c478bd9Sstevel@tonic-gate 	if (lfd == -1) {
19567c478bd9Sstevel@tonic-gate 		if ((errno == EPERM) || (errno == EACCES)) {
1957f9153c6bSDina K Nimeh 			die(gettext("you do not have permission to perform "
1958f9153c6bSDina K Nimeh 			    "that operation.\n"));
19597c478bd9Sstevel@tonic-gate 		} else {
1960f9153c6bSDina K Nimeh 			die(gettext("open: %s"), lofictl);
19617c478bd9Sstevel@tonic-gate 		}
19627c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
19637c478bd9Sstevel@tonic-gate 	}
19647d82f0f8SDina K Nimeh 
19657d82f0f8SDina K Nimeh 	/*
19667d82f0f8SDina K Nimeh 	 * No passphrase is needed for ephemeral key, or when key is
19677d82f0f8SDina K Nimeh 	 * in a file and not wrapped by another key from a token.
19687d82f0f8SDina K Nimeh 	 * However, a passphrase is needed in these cases:
19697d82f0f8SDina K Nimeh 	 * 1. cipher with no ephemeral key, key file, or token,
19707d82f0f8SDina K Nimeh 	 *    in which case the passphrase is used to build the key
19717d82f0f8SDina K Nimeh 	 * 2. token with an optional cipher or optional key file,
19727d82f0f8SDina K Nimeh 	 *    in which case the passphrase unlocks the token
19737d82f0f8SDina K Nimeh 	 * If only the cipher is specified, reconfirm the passphrase
19747d82f0f8SDina K Nimeh 	 * to ensure the user hasn't mis-entered it.  Otherwise, the
19757d82f0f8SDina K Nimeh 	 * token will enforce the token passphrase.
19767d82f0f8SDina K Nimeh 	 */
19777d82f0f8SDina K Nimeh 	if (need_crypto) {
19787d82f0f8SDina K Nimeh 		CK_SESSION_HANDLE	sess;
19797d82f0f8SDina K Nimeh 
19807d82f0f8SDina K Nimeh 		/* pick a cipher if none specified */
19817d82f0f8SDina K Nimeh 		if (cipher == NULL)
19827d82f0f8SDina K Nimeh 			cipher = DEFAULT_CIPHER;
19837d82f0f8SDina K Nimeh 
19847d82f0f8SDina K Nimeh 		if (!kernel_cipher_check(cipher))
19857d82f0f8SDina K Nimeh 			die(gettext(
19867d82f0f8SDina K Nimeh 			    "use \"cryptoadm list -m\" to find available "
19877d82f0f8SDina K Nimeh 			    "mechanisms\n"));
19887d82f0f8SDina K Nimeh 
19897d82f0f8SDina K Nimeh 		init_crypto(token, cipher, &sess);
19907d82f0f8SDina K Nimeh 
19917d82f0f8SDina K Nimeh 		if (cipher_only) {
19927d82f0f8SDina K Nimeh 			getkeyfromuser(cipher, &rkey, &rksz);
19937d82f0f8SDina K Nimeh 		} else if (token != NULL) {
19947d82f0f8SDina K Nimeh 			getkeyfromtoken(sess, token, keyfile, cipher,
19957d82f0f8SDina K Nimeh 			    &rkey, &rksz);
19967d82f0f8SDina K Nimeh 		} else {
19977d82f0f8SDina K Nimeh 			/* this also handles ephemeral keys */
19987d82f0f8SDina K Nimeh 			getkeyfromfile(keyfile, cipher, &rkey, &rksz);
19997d82f0f8SDina K Nimeh 		}
20007d82f0f8SDina K Nimeh 
20017d82f0f8SDina K Nimeh 		end_crypto(sess);
20027d82f0f8SDina K Nimeh 	}
20037d82f0f8SDina K Nimeh 
20047d82f0f8SDina K Nimeh 	/*
20057d82f0f8SDina K Nimeh 	 * Now to the real work.
20067d82f0f8SDina K Nimeh 	 */
20077c478bd9Sstevel@tonic-gate 	if (addflag)
20087d82f0f8SDina K Nimeh 		add_mapping(lfd, devicename, filename, cipher, rkey, rksz);
200987117650Saalok 	else if (compressflag)
20109525893dSaalok 		lofi_compress(&lfd, filename, compress_index, segsize);
201187117650Saalok 	else if (uncompressflag)
201287117650Saalok 		lofi_uncompress(lfd, filename);
20137c478bd9Sstevel@tonic-gate 	else if (deleteflag)
20143d7072f8Seschrock 		delete_mapping(lfd, devicename, filename, force);
20157c478bd9Sstevel@tonic-gate 	else if (filename || devicename)
20167c478bd9Sstevel@tonic-gate 		print_one_mapping(lfd, devicename, filename);
20177c478bd9Sstevel@tonic-gate 	else
20187c478bd9Sstevel@tonic-gate 		print_mappings(lfd);
2019579df0adSaalok 
20209525893dSaalok 	if (lfd != -1)
20217c478bd9Sstevel@tonic-gate 		(void) close(lfd);
2022579df0adSaalok 	closelib();
20237c478bd9Sstevel@tonic-gate 	return (E_SUCCESS);
20247c478bd9Sstevel@tonic-gate }
2025