xref: /titanic_52/usr/src/cmd/lofiadm/main.c (revision 6e096e64ab50f0e0567b99c4306fe9f3dead76e6)
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 /*
22b1efbcd6SAlok Aggarwal  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
231d8ed0f0Svikram  * Use is subject to license terms.
24789dced6SJerry Jelinek  * Copyright 2012 Joyent, Inc.  All rights reserved.
25cd69fabeSAlexander Eremin  *
26cd69fabeSAlexander Eremin  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
27*6e096e64SGary Mills  * Copyright (c) 2014 Gary Mills
283d7072f8Seschrock  */
293d7072f8Seschrock 
303d7072f8Seschrock /*
317c478bd9Sstevel@tonic-gate  * lofiadm - administer lofi(7d). Very simple, add and remove file<->device
327c478bd9Sstevel@tonic-gate  * associations, and display status. All the ioctls are private between
337c478bd9Sstevel@tonic-gate  * lofi and lofiadm, and so are very simple - device information is
347c478bd9Sstevel@tonic-gate  * communicated via a minor number.
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <sys/types.h>
387c478bd9Sstevel@tonic-gate #include <sys/param.h>
397c478bd9Sstevel@tonic-gate #include <sys/lofi.h>
407c478bd9Sstevel@tonic-gate #include <sys/stat.h>
414058a205Sjrgn.keil@googlemail.com #include <sys/sysmacros.h>
4287117650Saalok #include <netinet/in.h>
437c478bd9Sstevel@tonic-gate #include <stdio.h>
447c478bd9Sstevel@tonic-gate #include <fcntl.h>
457c478bd9Sstevel@tonic-gate #include <locale.h>
467c478bd9Sstevel@tonic-gate #include <string.h>
4787117650Saalok #include <strings.h>
487c478bd9Sstevel@tonic-gate #include <errno.h>
497c478bd9Sstevel@tonic-gate #include <stdlib.h>
507c478bd9Sstevel@tonic-gate #include <unistd.h>
517c478bd9Sstevel@tonic-gate #include <stropts.h>
521d8ed0f0Svikram #include <libdevinfo.h>
5387117650Saalok #include <libgen.h>
5487117650Saalok #include <ctype.h>
55579df0adSaalok #include <dlfcn.h>
567d82f0f8SDina K Nimeh #include <limits.h>
577d82f0f8SDina K Nimeh #include <security/cryptoki.h>
587d82f0f8SDina K Nimeh #include <cryptoutil.h>
597d82f0f8SDina K Nimeh #include <sys/crypto/ioctl.h>
607d82f0f8SDina K Nimeh #include <sys/crypto/ioctladmin.h>
617c478bd9Sstevel@tonic-gate #include "utils.h"
62b1efbcd6SAlok Aggarwal #include <LzmaEnc.h>
637c478bd9Sstevel@tonic-gate 
647d82f0f8SDina K Nimeh /* Only need the IV len #defines out of these files, nothing else. */
657d82f0f8SDina K Nimeh #include <aes/aes_impl.h>
667d82f0f8SDina K Nimeh #include <des/des_impl.h>
677d82f0f8SDina K Nimeh #include <blowfish/blowfish_impl.h>
687d82f0f8SDina K Nimeh 
697c478bd9Sstevel@tonic-gate static const char USAGE[] =
70795f5a90SMohamed A. Khalfella 	"Usage: %s [-r] -a file [ device ]\n"
71795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -a file [device]\n"
72795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -k raw_key_file -a file [device]\n"
73795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -T [token]:[manuf]:[serial]:key "
74795f5a90SMohamed A. Khalfella 	"-a file [device]\n"
75795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -T [token]:[manuf]:[serial]:key "
76795f5a90SMohamed A. Khalfella 	"-k wrapped_key_file -a file [device]\n"
77795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -e -a file [device]\n"
787c478bd9Sstevel@tonic-gate 	"       %s -d file | device\n"
79b1efbcd6SAlok Aggarwal 	"       %s -C [gzip|gzip-6|gzip-9|lzma] [-s segment_size] file\n"
8087117650Saalok 	"       %s -U file\n"
81f9153c6bSDina K Nimeh 	"       %s [ file | device ]\n";
827c478bd9Sstevel@tonic-gate 
837d82f0f8SDina K Nimeh typedef struct token_spec {
847d82f0f8SDina K Nimeh 	char	*name;
857d82f0f8SDina K Nimeh 	char	*mfr;
867d82f0f8SDina K Nimeh 	char	*serno;
877d82f0f8SDina K Nimeh 	char	*key;
887d82f0f8SDina K Nimeh } token_spec_t;
897d82f0f8SDina K Nimeh 
907d82f0f8SDina K Nimeh typedef struct mech_alias {
917d82f0f8SDina K Nimeh 	char	*alias;
927d82f0f8SDina K Nimeh 	CK_MECHANISM_TYPE type;
937d82f0f8SDina K Nimeh 	char	*name;		/* for ioctl */
947d82f0f8SDina K Nimeh 	char	*iv_name;	/* for ioctl */
957d82f0f8SDina K Nimeh 	size_t	iv_len;		/* for ioctl */
967d82f0f8SDina K Nimeh 	iv_method_t iv_type;	/* for ioctl */
977d82f0f8SDina K Nimeh 	size_t	min_keysize;	/* in bytes */
987d82f0f8SDina K Nimeh 	size_t	max_keysize;	/* in bytes */
997d82f0f8SDina K Nimeh 	token_spec_t *token;
1007d82f0f8SDina K Nimeh 	CK_SLOT_ID slot;
1017d82f0f8SDina K Nimeh } mech_alias_t;
1027d82f0f8SDina K Nimeh 
1037d82f0f8SDina K Nimeh static mech_alias_t mech_aliases[] = {
1047d82f0f8SDina K Nimeh 	/* Preferred one should always be listed first. */
1057d82f0f8SDina K Nimeh 	{ "aes-256-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
1067d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
1077d82f0f8SDina K Nimeh 	{ "aes-192-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
1087d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
1097d82f0f8SDina K Nimeh 	{ "aes-128-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
1107d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
1117d82f0f8SDina K Nimeh 	{ "des3-cbc", CKM_DES3_CBC, "CKM_DES3_CBC", "CKM_DES3_ECB", DES_IV_LEN,
1127d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID)-1 },
1137d82f0f8SDina K Nimeh 	{ "blowfish-cbc", CKM_BLOWFISH_CBC, "CKM_BLOWFISH_CBC",
1147d82f0f8SDina K Nimeh 	    "CKM_BLOWFISH_ECB", BLOWFISH_IV_LEN, IVM_ENC_BLKNO, ULONG_MAX,
1157d82f0f8SDina K Nimeh 	    0L, NULL, (CK_SLOT_ID)-1 }
1167d82f0f8SDina K Nimeh 	/*
1177d82f0f8SDina K Nimeh 	 * A cipher without an iv requirement would look like this:
1187d82f0f8SDina K Nimeh 	 * { "aes-xex", CKM_AES_XEX, "CKM_AES_XEX", NULL, 0,
1197d82f0f8SDina K Nimeh 	 *    IVM_NONE, ULONG_MAX, 0L, NULL, (CK_SLOT_ID)-1 }
1207d82f0f8SDina K Nimeh 	 */
1217d82f0f8SDina K Nimeh };
1227d82f0f8SDina K Nimeh 
1237d82f0f8SDina K Nimeh int	mech_aliases_count = (sizeof (mech_aliases) / sizeof (mech_alias_t));
1247d82f0f8SDina K Nimeh 
1257d82f0f8SDina K Nimeh /* Preferred cipher, if one isn't specified on command line. */
1267d82f0f8SDina K Nimeh #define	DEFAULT_CIPHER	(&mech_aliases[0])
1277d82f0f8SDina K Nimeh 
1287d82f0f8SDina K Nimeh #define	DEFAULT_CIPHER_NUM	64	/* guess # kernel ciphers available */
1297d82f0f8SDina K Nimeh #define	DEFAULT_MECHINFO_NUM	16	/* guess # kernel mechs available */
1307d82f0f8SDina K Nimeh #define	MIN_PASSLEN		8	/* min acceptable passphrase size */
1317c478bd9Sstevel@tonic-gate 
13287117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
13387117650Saalok 	size_t *destlen, int level);
134b1efbcd6SAlok Aggarwal static int lzma_compress(void *src, size_t srclen, void *dst,
135b1efbcd6SAlok Aggarwal 	size_t *destlen, int level);
13687117650Saalok 
13787117650Saalok lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
13887117650Saalok 	{NULL,  		gzip_compress,  6,	"gzip"}, /* default */
13987117650Saalok 	{NULL,			gzip_compress,	6,	"gzip-6"},
140b1efbcd6SAlok Aggarwal 	{NULL,			gzip_compress,	9, 	"gzip-9"},
141b1efbcd6SAlok Aggarwal 	{NULL,  		lzma_compress, 	0, 	"lzma"}
14287117650Saalok };
14387117650Saalok 
1447d82f0f8SDina K Nimeh /* For displaying lofi mappings */
14587117650Saalok #define	FORMAT 			"%-20s     %-30s	%s\n"
1467d82f0f8SDina K Nimeh 
14787117650Saalok #define	COMPRESS_ALGORITHM	"gzip"
14887117650Saalok #define	COMPRESS_THRESHOLD	2048
14987117650Saalok #define	SEGSIZE			131072
15087117650Saalok #define	BLOCK_SIZE		512
15187117650Saalok #define	KILOBYTE		1024
15287117650Saalok #define	MEGABYTE		(KILOBYTE * KILOBYTE)
15387117650Saalok #define	GIGABYTE		(KILOBYTE * MEGABYTE)
154*6e096e64SGary Mills #define	LIBZ			"libz.so.1"
155579df0adSaalok 
156b1efbcd6SAlok Aggarwal static void
157b1efbcd6SAlok Aggarwal usage(const char *pname)
15887117650Saalok {
159b1efbcd6SAlok Aggarwal 	(void) fprintf(stderr, gettext(USAGE), pname, pname, pname,
160795f5a90SMohamed A. Khalfella 	    pname, pname, pname, pname, pname, pname, pname);
161b1efbcd6SAlok Aggarwal 	exit(E_USAGE);
162b1efbcd6SAlok Aggarwal }
163b1efbcd6SAlok Aggarwal 
164b1efbcd6SAlok Aggarwal static int
165b1efbcd6SAlok Aggarwal gzip_compress(void *src, size_t srclen, void *dst, size_t *dstlen, int level)
166b1efbcd6SAlok Aggarwal {
167b1efbcd6SAlok Aggarwal 	static int (*compress2p)(void *, ulong_t *, void *, size_t, int) = NULL;
168579df0adSaalok 	void *libz_hdl = NULL;
16987117650Saalok 
170579df0adSaalok 	/*
171579df0adSaalok 	 * The first time we are called, attempt to dlopen()
172*6e096e64SGary Mills 	 * libz.so.1 and get a pointer to the compress2() function
173579df0adSaalok 	 */
174579df0adSaalok 	if (compress2p == NULL) {
175579df0adSaalok 		if ((libz_hdl = openlib(LIBZ)) == NULL)
176579df0adSaalok 			die(gettext("could not find %s. "
177579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
178579df0adSaalok 
179579df0adSaalok 		if ((compress2p =
180579df0adSaalok 		    (int (*)(void *, ulong_t *, void *, size_t, int))
181579df0adSaalok 		    dlsym(libz_hdl, "compress2")) == NULL) {
182579df0adSaalok 			closelib();
183579df0adSaalok 			die(gettext("could not find the correct %s. "
184579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
185579df0adSaalok 		}
186579df0adSaalok 	}
187579df0adSaalok 
188579df0adSaalok 	if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0)
189579df0adSaalok 		return (-1);
19087117650Saalok 	return (0);
19187117650Saalok }
1927c478bd9Sstevel@tonic-gate 
193b1efbcd6SAlok Aggarwal /*ARGSUSED*/
1947c478bd9Sstevel@tonic-gate static void
195b1efbcd6SAlok Aggarwal *SzAlloc(void *p, size_t size)
1967c478bd9Sstevel@tonic-gate {
197b1efbcd6SAlok Aggarwal 	return (malloc(size));
198b1efbcd6SAlok Aggarwal }
199b1efbcd6SAlok Aggarwal 
200b1efbcd6SAlok Aggarwal /*ARGSUSED*/
201b1efbcd6SAlok Aggarwal static void
202b1efbcd6SAlok Aggarwal SzFree(void *p, void *address, size_t size)
203b1efbcd6SAlok Aggarwal {
204b1efbcd6SAlok Aggarwal 	free(address);
205b1efbcd6SAlok Aggarwal }
206b1efbcd6SAlok Aggarwal 
207b1efbcd6SAlok Aggarwal static ISzAlloc g_Alloc = {
208b1efbcd6SAlok Aggarwal 	SzAlloc,
209b1efbcd6SAlok Aggarwal 	SzFree
210b1efbcd6SAlok Aggarwal };
211b1efbcd6SAlok Aggarwal 
212b1efbcd6SAlok Aggarwal #define	LZMA_UNCOMPRESSED_SIZE	8
213b1efbcd6SAlok Aggarwal #define	LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMPRESSED_SIZE)
214b1efbcd6SAlok Aggarwal 
215b1efbcd6SAlok Aggarwal /*ARGSUSED*/
216b1efbcd6SAlok Aggarwal static int
217b1efbcd6SAlok Aggarwal lzma_compress(void *src, size_t srclen, void *dst,
218b1efbcd6SAlok Aggarwal 	size_t *dstlen, int level)
219b1efbcd6SAlok Aggarwal {
220b1efbcd6SAlok Aggarwal 	CLzmaEncProps props;
221b1efbcd6SAlok Aggarwal 	size_t outsize2;
222b1efbcd6SAlok Aggarwal 	size_t outsizeprocessed;
223b1efbcd6SAlok Aggarwal 	size_t outpropssize = LZMA_PROPS_SIZE;
224b1efbcd6SAlok Aggarwal 	uint64_t t = 0;
225b1efbcd6SAlok Aggarwal 	SRes res;
226b1efbcd6SAlok Aggarwal 	Byte *dstp;
227b1efbcd6SAlok Aggarwal 	int i;
228b1efbcd6SAlok Aggarwal 
229b1efbcd6SAlok Aggarwal 	outsize2 = *dstlen;
230b1efbcd6SAlok Aggarwal 
231b1efbcd6SAlok Aggarwal 	LzmaEncProps_Init(&props);
232b1efbcd6SAlok Aggarwal 
233b1efbcd6SAlok Aggarwal 	/*
234b1efbcd6SAlok Aggarwal 	 * The LZMA compressed file format is as follows -
235b1efbcd6SAlok Aggarwal 	 *
236b1efbcd6SAlok Aggarwal 	 * Offset Size(bytes) Description
237b1efbcd6SAlok Aggarwal 	 * 0		1	LZMA properties (lc, lp, lp (encoded))
238b1efbcd6SAlok Aggarwal 	 * 1		4	Dictionary size (little endian)
239b1efbcd6SAlok Aggarwal 	 * 5		8	Uncompressed size (little endian)
240b1efbcd6SAlok Aggarwal 	 * 13			Compressed data
241b1efbcd6SAlok Aggarwal 	 */
242b1efbcd6SAlok Aggarwal 
243b1efbcd6SAlok Aggarwal 	/* set the dictionary size to be 8MB */
244b1efbcd6SAlok Aggarwal 	props.dictSize = 1 << 23;
245b1efbcd6SAlok Aggarwal 
246b1efbcd6SAlok Aggarwal 	if (*dstlen < LZMA_HEADER_SIZE)
247b1efbcd6SAlok Aggarwal 		return (SZ_ERROR_OUTPUT_EOF);
248b1efbcd6SAlok Aggarwal 
249b1efbcd6SAlok Aggarwal 	dstp = (Byte *)dst;
250b1efbcd6SAlok Aggarwal 	t = srclen;
251b1efbcd6SAlok Aggarwal 	/*
252b1efbcd6SAlok Aggarwal 	 * Set the uncompressed size in the LZMA header
253b1efbcd6SAlok Aggarwal 	 * The LZMA properties (specified in 'props')
254b1efbcd6SAlok Aggarwal 	 * will be set by the call to LzmaEncode()
255b1efbcd6SAlok Aggarwal 	 */
256b1efbcd6SAlok Aggarwal 	for (i = 0; i < LZMA_UNCOMPRESSED_SIZE; i++, t >>= 8) {
257b1efbcd6SAlok Aggarwal 		dstp[LZMA_PROPS_SIZE + i] = (Byte)t;
258b1efbcd6SAlok Aggarwal 	}
259b1efbcd6SAlok Aggarwal 
260b1efbcd6SAlok Aggarwal 	outsizeprocessed = outsize2 - LZMA_HEADER_SIZE;
261b1efbcd6SAlok Aggarwal 	res = LzmaEncode(dstp + LZMA_HEADER_SIZE, &outsizeprocessed,
262b1efbcd6SAlok Aggarwal 	    src, srclen, &props, dstp, &outpropssize, 0, NULL,
263b1efbcd6SAlok Aggarwal 	    &g_Alloc, &g_Alloc);
264b1efbcd6SAlok Aggarwal 
265b1efbcd6SAlok Aggarwal 	if (res != 0)
266b1efbcd6SAlok Aggarwal 		return (-1);
267b1efbcd6SAlok Aggarwal 
268b1efbcd6SAlok Aggarwal 	*dstlen = outsizeprocessed + LZMA_HEADER_SIZE;
269b1efbcd6SAlok Aggarwal 	return (0);
2707c478bd9Sstevel@tonic-gate }
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate /*
2737c478bd9Sstevel@tonic-gate  * Translate a lofi device name to a minor number. We might be asked
2747c478bd9Sstevel@tonic-gate  * to do this when there is no association (such as when the user specifies
2757c478bd9Sstevel@tonic-gate  * a particular device), so we can only look at the string.
2767c478bd9Sstevel@tonic-gate  */
2777c478bd9Sstevel@tonic-gate static int
2787c478bd9Sstevel@tonic-gate name_to_minor(const char *devicename)
2797c478bd9Sstevel@tonic-gate {
2807c478bd9Sstevel@tonic-gate 	int	minor;
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
2837c478bd9Sstevel@tonic-gate 		return (minor);
2847c478bd9Sstevel@tonic-gate 	}
2857c478bd9Sstevel@tonic-gate 	if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
2867c478bd9Sstevel@tonic-gate 		return (minor);
2877c478bd9Sstevel@tonic-gate 	}
2887c478bd9Sstevel@tonic-gate 	return (0);
2897c478bd9Sstevel@tonic-gate }
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate  * This might be the first time we've used this minor number. If so,
2937c478bd9Sstevel@tonic-gate  * it might also be that the /dev links are in the process of being created
2947c478bd9Sstevel@tonic-gate  * by devfsadmd (or that they'll be created "soon"). We cannot return
2957c478bd9Sstevel@tonic-gate  * until they're there or the invoker of lofiadm might try to use them
2967c478bd9Sstevel@tonic-gate  * and not find them. This can happen if a shell script is running on
2977c478bd9Sstevel@tonic-gate  * an MP.
2987c478bd9Sstevel@tonic-gate  */
2997c478bd9Sstevel@tonic-gate static int sleeptime = 2;	/* number of seconds to sleep between stat's */
3007c478bd9Sstevel@tonic-gate static int maxsleep = 120;	/* maximum number of seconds to sleep */
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate static void
3037c478bd9Sstevel@tonic-gate wait_until_dev_complete(int minor)
3047c478bd9Sstevel@tonic-gate {
3057c478bd9Sstevel@tonic-gate 	struct stat64 buf;
3067c478bd9Sstevel@tonic-gate 	int	cursleep;
30787117650Saalok 	char	blkpath[MAXPATHLEN];
30887117650Saalok 	char	charpath[MAXPATHLEN];
3091d8ed0f0Svikram 	di_devlink_handle_t hdl;
3101d8ed0f0Svikram 
3117c478bd9Sstevel@tonic-gate 	(void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
3127c478bd9Sstevel@tonic-gate 	    LOFI_BLOCK_NAME, minor);
3137c478bd9Sstevel@tonic-gate 	(void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
3147c478bd9Sstevel@tonic-gate 	    LOFI_CHAR_NAME, minor);
3151d8ed0f0Svikram 
3161d8ed0f0Svikram 	/* Check if links already present */
3171d8ed0f0Svikram 	if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
3181d8ed0f0Svikram 		return;
3191d8ed0f0Svikram 
3201d8ed0f0Svikram 	/* First use di_devlink_init() */
3211d8ed0f0Svikram 	if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
3221d8ed0f0Svikram 		(void) di_devlink_fini(&hdl);
3231d8ed0f0Svikram 		goto out;
3241d8ed0f0Svikram 	}
3251d8ed0f0Svikram 
3261d8ed0f0Svikram 	/*
3271d8ed0f0Svikram 	 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
3281d8ed0f0Svikram 	 * only fail if the caller is non-root. In that case, wait for
3291d8ed0f0Svikram 	 * link creation via sysevents.
3301d8ed0f0Svikram 	 */
3317d82f0f8SDina K Nimeh 	for (cursleep = 0; cursleep < maxsleep; cursleep += sleeptime) {
3327d82f0f8SDina K Nimeh 		if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
3337c478bd9Sstevel@tonic-gate 			return;
3347d82f0f8SDina K Nimeh 		(void) sleep(sleeptime);
3357c478bd9Sstevel@tonic-gate 	}
3361d8ed0f0Svikram 
3377c478bd9Sstevel@tonic-gate 	/* one last try */
3381d8ed0f0Svikram out:
3397c478bd9Sstevel@tonic-gate 	if (stat64(blkpath, &buf) == -1) {
3407c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), blkpath);
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 	if (stat64(charpath, &buf) == -1) {
3437c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), charpath);
3447c478bd9Sstevel@tonic-gate 	}
3457c478bd9Sstevel@tonic-gate }
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate /*
3487d82f0f8SDina K Nimeh  * Map the file and return the minor number the driver picked for the file
3497d82f0f8SDina K Nimeh  * DO NOT use this function if the filename is actually the device name.
3507d82f0f8SDina K Nimeh  */
3517d82f0f8SDina K Nimeh static int
3527d82f0f8SDina K Nimeh lofi_map_file(int lfd, struct lofi_ioctl li, const char *filename)
3537d82f0f8SDina K Nimeh {
3547d82f0f8SDina K Nimeh 	int	minor;
3557d82f0f8SDina K Nimeh 
3567d82f0f8SDina K Nimeh 	li.li_minor = 0;
3577d82f0f8SDina K Nimeh 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
3587d82f0f8SDina K Nimeh 	minor = ioctl(lfd, LOFI_MAP_FILE, &li);
3597d82f0f8SDina K Nimeh 	if (minor == -1) {
3607d82f0f8SDina K Nimeh 		if (errno == ENOTSUP)
3617d82f0f8SDina K Nimeh 			warn(gettext("encrypting compressed files is "
3627d82f0f8SDina K Nimeh 			    "unsupported"));
3637d82f0f8SDina K Nimeh 		die(gettext("could not map file %s"), filename);
3647d82f0f8SDina K Nimeh 	}
3657d82f0f8SDina K Nimeh 	wait_until_dev_complete(minor);
3667d82f0f8SDina K Nimeh 	return (minor);
3677d82f0f8SDina K Nimeh }
3687d82f0f8SDina K Nimeh 
3697d82f0f8SDina K Nimeh /*
3707c478bd9Sstevel@tonic-gate  * Add a device association. If devicename is NULL, let the driver
3717c478bd9Sstevel@tonic-gate  * pick a device.
3727c478bd9Sstevel@tonic-gate  */
3737c478bd9Sstevel@tonic-gate static void
37487117650Saalok add_mapping(int lfd, const char *devicename, const char *filename,
375cd69fabeSAlexander Eremin     mech_alias_t *cipher, const char *rkey, size_t rksz, boolean_t rdonly)
3767c478bd9Sstevel@tonic-gate {
3777c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
3787d82f0f8SDina K Nimeh 
379cd69fabeSAlexander Eremin 	li.li_readonly = rdonly;
380cd69fabeSAlexander Eremin 
3817d82f0f8SDina K Nimeh 	li.li_crypto_enabled = B_FALSE;
3827d82f0f8SDina K Nimeh 	if (cipher != NULL) {
3837d82f0f8SDina K Nimeh 		/* set up encryption for mapped file */
3847d82f0f8SDina K Nimeh 		li.li_crypto_enabled = B_TRUE;
3857d82f0f8SDina K Nimeh 		(void) strlcpy(li.li_cipher, cipher->name,
3867d82f0f8SDina K Nimeh 		    sizeof (li.li_cipher));
3877d82f0f8SDina K Nimeh 		if (rksz > sizeof (li.li_key)) {
3887d82f0f8SDina K Nimeh 			die(gettext("key too large"));
3897d82f0f8SDina K Nimeh 		}
3907d82f0f8SDina K Nimeh 		bcopy(rkey, li.li_key, rksz);
3917d82f0f8SDina K Nimeh 		li.li_key_len = rksz << 3;	/* convert to bits */
3927d82f0f8SDina K Nimeh 
3937d82f0f8SDina K Nimeh 		li.li_iv_type = cipher->iv_type;
3947d82f0f8SDina K Nimeh 		li.li_iv_len = cipher->iv_len;	/* 0 when no iv needed */
3957d82f0f8SDina K Nimeh 		switch (cipher->iv_type) {
3967d82f0f8SDina K Nimeh 		case IVM_ENC_BLKNO:
3977d82f0f8SDina K Nimeh 			(void) strlcpy(li.li_iv_cipher, cipher->iv_name,
3987d82f0f8SDina K Nimeh 			    sizeof (li.li_iv_cipher));
3997d82f0f8SDina K Nimeh 			break;
4007d82f0f8SDina K Nimeh 		case IVM_NONE:
4017d82f0f8SDina K Nimeh 			/* FALLTHROUGH */
4027d82f0f8SDina K Nimeh 		default:
4037d82f0f8SDina K Nimeh 			break;
4047d82f0f8SDina K Nimeh 		}
4057d82f0f8SDina K Nimeh 	}
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
4087d82f0f8SDina K Nimeh 		int	minor;
40987117650Saalok 
4107d82f0f8SDina K Nimeh 		/* pick one via the driver */
4117d82f0f8SDina K Nimeh 		minor = lofi_map_file(lfd, li, filename);
4127d82f0f8SDina K Nimeh 		/* if mapping succeeds, print the one picked */
4137d82f0f8SDina K Nimeh 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor);
4147c478bd9Sstevel@tonic-gate 		return;
4157c478bd9Sstevel@tonic-gate 	}
4167d82f0f8SDina K Nimeh 
4177c478bd9Sstevel@tonic-gate 	/* use device we were given */
4187d82f0f8SDina K Nimeh 	li.li_minor = name_to_minor(devicename);
4197d82f0f8SDina K Nimeh 	if (li.li_minor == 0) {
4207c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
4217c478bd9Sstevel@tonic-gate 	}
42287117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
4237d82f0f8SDina K Nimeh 
4247d82f0f8SDina K Nimeh 	/* if device is already in use li.li_minor won't change */
4257c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
4267d82f0f8SDina K Nimeh 		if (errno == ENOTSUP)
4277d82f0f8SDina K Nimeh 			warn(gettext("encrypting compressed files is "
4287d82f0f8SDina K Nimeh 			    "unsupported"));
4297c478bd9Sstevel@tonic-gate 		die(gettext("could not map file %s to %s"), filename,
4307c478bd9Sstevel@tonic-gate 		    devicename);
4317c478bd9Sstevel@tonic-gate 	}
4327d82f0f8SDina K Nimeh 	wait_until_dev_complete(li.li_minor);
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate /*
4367c478bd9Sstevel@tonic-gate  * Remove an association. Delete by device name if non-NULL, or by
4377c478bd9Sstevel@tonic-gate  * filename otherwise.
4387c478bd9Sstevel@tonic-gate  */
4397c478bd9Sstevel@tonic-gate static void
4403d7072f8Seschrock delete_mapping(int lfd, const char *devicename, const char *filename,
4413d7072f8Seschrock     boolean_t force)
4427c478bd9Sstevel@tonic-gate {
4437c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
4447c478bd9Sstevel@tonic-gate 
4453d7072f8Seschrock 	li.li_force = force;
44693239addSjohnlev 	li.li_cleanup = B_FALSE;
44793239addSjohnlev 
4487c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
4497c478bd9Sstevel@tonic-gate 		/* delete by filename */
45087117650Saalok 		(void) strlcpy(li.li_filename, filename,
45187117650Saalok 		    sizeof (li.li_filename));
4527c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
4537c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
4547c478bd9Sstevel@tonic-gate 			die(gettext("could not unmap file %s"), filename);
4557c478bd9Sstevel@tonic-gate 		}
4567c478bd9Sstevel@tonic-gate 		return;
4577c478bd9Sstevel@tonic-gate 	}
4587c478bd9Sstevel@tonic-gate 
459f9153c6bSDina K Nimeh 	/* delete by device */
4607c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
4617c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
4627c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
4637c478bd9Sstevel@tonic-gate 	}
4647c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
4657c478bd9Sstevel@tonic-gate 		die(gettext("could not unmap device %s"), devicename);
4667c478bd9Sstevel@tonic-gate 	}
4677c478bd9Sstevel@tonic-gate }
4687c478bd9Sstevel@tonic-gate 
4697d82f0f8SDina K Nimeh /*
4707d82f0f8SDina K Nimeh  * Show filename given devicename, or devicename given filename.
4717d82f0f8SDina K Nimeh  */
4727c478bd9Sstevel@tonic-gate static void
4737c478bd9Sstevel@tonic-gate print_one_mapping(int lfd, const char *devicename, const char *filename)
4747c478bd9Sstevel@tonic-gate {
4757c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
4787c478bd9Sstevel@tonic-gate 		/* given filename, print devicename */
4797c478bd9Sstevel@tonic-gate 		li.li_minor = 0;
48087117650Saalok 		(void) strlcpy(li.li_filename, filename,
48187117650Saalok 		    sizeof (li.li_filename));
4827c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
4837c478bd9Sstevel@tonic-gate 			die(gettext("could not find device for %s"), filename);
4847c478bd9Sstevel@tonic-gate 		}
4857c478bd9Sstevel@tonic-gate 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
4867c478bd9Sstevel@tonic-gate 		return;
4877c478bd9Sstevel@tonic-gate 	}
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	/* given devicename, print filename */
4907c478bd9Sstevel@tonic-gate 	li.li_minor = name_to_minor(devicename);
4917c478bd9Sstevel@tonic-gate 	if (li.li_minor == 0) {
4927c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
4937c478bd9Sstevel@tonic-gate 	}
4947c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
4957c478bd9Sstevel@tonic-gate 		die(gettext("could not find filename for %s"), devicename);
4967c478bd9Sstevel@tonic-gate 	}
4977c478bd9Sstevel@tonic-gate 	(void) printf("%s\n", li.li_filename);
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate 
50087117650Saalok /*
5017d82f0f8SDina K Nimeh  * Print the list of all the mappings, including a header.
5027d82f0f8SDina K Nimeh  */
5037d82f0f8SDina K Nimeh static void
5047d82f0f8SDina K Nimeh print_mappings(int fd)
5057d82f0f8SDina K Nimeh {
5067d82f0f8SDina K Nimeh 	struct lofi_ioctl li;
5077d82f0f8SDina K Nimeh 	int	minor;
5087d82f0f8SDina K Nimeh 	int	maxminor;
5097d82f0f8SDina K Nimeh 	char	path[MAXPATHLEN];
510cd69fabeSAlexander Eremin 	char	options[MAXPATHLEN] = { 0 };
5117d82f0f8SDina K Nimeh 
5127d82f0f8SDina K Nimeh 	li.li_minor = 0;
5137d82f0f8SDina K Nimeh 	if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
5147d82f0f8SDina K Nimeh 		die("ioctl");
5157d82f0f8SDina K Nimeh 	}
5167d82f0f8SDina K Nimeh 	maxminor = li.li_minor;
5177d82f0f8SDina K Nimeh 
5187d82f0f8SDina K Nimeh 	(void) printf(FORMAT, gettext("Block Device"), gettext("File"),
5197d82f0f8SDina K Nimeh 	    gettext("Options"));
5207d82f0f8SDina K Nimeh 	for (minor = 1; minor <= maxminor; minor++) {
5217d82f0f8SDina K Nimeh 		li.li_minor = minor;
5227d82f0f8SDina K Nimeh 		if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
5237d82f0f8SDina K Nimeh 			if (errno == ENXIO)
5247d82f0f8SDina K Nimeh 				continue;
5257d82f0f8SDina K Nimeh 			warn("ioctl");
5267d82f0f8SDina K Nimeh 			break;
5277d82f0f8SDina K Nimeh 		}
5287d82f0f8SDina K Nimeh 		(void) snprintf(path, sizeof (path), "/dev/%s/%d",
5297d82f0f8SDina K Nimeh 		    LOFI_BLOCK_NAME, minor);
530cd69fabeSAlexander Eremin 
531cd69fabeSAlexander Eremin 		options[0] = '\0';
532cd69fabeSAlexander Eremin 
5337d82f0f8SDina K Nimeh 		/*
5347d82f0f8SDina K Nimeh 		 * Encrypted lofi and compressed lofi are mutually exclusive.
5357d82f0f8SDina K Nimeh 		 */
5367d82f0f8SDina K Nimeh 		if (li.li_crypto_enabled)
5377d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options),
5387d82f0f8SDina K Nimeh 			    gettext("Encrypted"));
5397d82f0f8SDina K Nimeh 		else if (li.li_algorithm[0] != '\0')
5407d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options),
5417d82f0f8SDina K Nimeh 			    gettext("Compressed(%s)"), li.li_algorithm);
542cd69fabeSAlexander Eremin 		if (li.li_readonly) {
543cd69fabeSAlexander Eremin 			if (strlen(options) != 0) {
544cd69fabeSAlexander Eremin 				(void) strlcat(options, ",", sizeof (options));
545cd69fabeSAlexander Eremin 				(void) strlcat(options, "Readonly",
546cd69fabeSAlexander Eremin 				    sizeof (options));
547cd69fabeSAlexander Eremin 			} else {
548cd69fabeSAlexander Eremin 				(void) snprintf(options, sizeof (options),
549cd69fabeSAlexander Eremin 				    gettext("Readonly"));
550cd69fabeSAlexander Eremin 			}
551cd69fabeSAlexander Eremin 		}
552cd69fabeSAlexander Eremin 		if (strlen(options) == 0)
5537d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options), "-");
5547d82f0f8SDina K Nimeh 
5557d82f0f8SDina K Nimeh 		(void) printf(FORMAT, path, li.li_filename, options);
5567d82f0f8SDina K Nimeh 	}
5577d82f0f8SDina K Nimeh }
5587d82f0f8SDina K Nimeh 
5597d82f0f8SDina K Nimeh /*
5607d82f0f8SDina K Nimeh  * Verify the cipher selected by user.
5617d82f0f8SDina K Nimeh  */
5627d82f0f8SDina K Nimeh static mech_alias_t *
5637d82f0f8SDina K Nimeh ciph2mech(const char *alias)
5647d82f0f8SDina K Nimeh {
5657d82f0f8SDina K Nimeh 	int	i;
5667d82f0f8SDina K Nimeh 
5677d82f0f8SDina K Nimeh 	for (i = 0; i < mech_aliases_count; i++) {
5687d82f0f8SDina K Nimeh 		if (strcasecmp(alias, mech_aliases[i].alias) == 0)
5697d82f0f8SDina K Nimeh 			return (&mech_aliases[i]);
5707d82f0f8SDina K Nimeh 	}
5717d82f0f8SDina K Nimeh 	return (NULL);
5727d82f0f8SDina K Nimeh }
5737d82f0f8SDina K Nimeh 
5747d82f0f8SDina K Nimeh /*
5757d82f0f8SDina K Nimeh  * Verify user selected cipher is also available in kernel.
5767d82f0f8SDina K Nimeh  *
5777d82f0f8SDina K Nimeh  * While traversing kernel list of mechs, if the cipher is supported in the
5787d82f0f8SDina K Nimeh  * kernel for both encryption and decryption, it also picks up the min/max
5797d82f0f8SDina K Nimeh  * key size.
5807d82f0f8SDina K Nimeh  */
5817d82f0f8SDina K Nimeh static boolean_t
5827d82f0f8SDina K Nimeh kernel_cipher_check(mech_alias_t *cipher)
5837d82f0f8SDina K Nimeh {
5847d82f0f8SDina K Nimeh 	boolean_t ciph_ok = B_FALSE;
5857d82f0f8SDina K Nimeh 	boolean_t iv_ok = B_FALSE;
5867d82f0f8SDina K Nimeh 	int	i;
5877d82f0f8SDina K Nimeh 	int	count;
5887d82f0f8SDina K Nimeh 	crypto_get_mechanism_list_t *kciphers = NULL;
5897d82f0f8SDina K Nimeh 	crypto_get_all_mechanism_info_t *kinfo = NULL;
5907d82f0f8SDina K Nimeh 	int	fd = -1;
5917d82f0f8SDina K Nimeh 	size_t	keymin;
5927d82f0f8SDina K Nimeh 	size_t	keymax;
5937d82f0f8SDina K Nimeh 
5947d82f0f8SDina K Nimeh 	/* if cipher doesn't need iv generating mech, bypass that check now */
5957d82f0f8SDina K Nimeh 	if (cipher->iv_name == NULL)
5967d82f0f8SDina K Nimeh 		iv_ok = B_TRUE;
5977d82f0f8SDina K Nimeh 
5987d82f0f8SDina K Nimeh 	/* allocate some space for the list of kernel ciphers */
5997d82f0f8SDina K Nimeh 	count = DEFAULT_CIPHER_NUM;
6007d82f0f8SDina K Nimeh 	kciphers = malloc(sizeof (crypto_get_mechanism_list_t) +
6017d82f0f8SDina K Nimeh 	    sizeof (crypto_mech_name_t) * (count - 1));
6027d82f0f8SDina K Nimeh 	if (kciphers == NULL)
6037d82f0f8SDina K Nimeh 		die(gettext("failed to allocate memory for list of "
6047d82f0f8SDina K Nimeh 		    "kernel mechanisms"));
6057d82f0f8SDina K Nimeh 	kciphers->ml_count = count;
6067d82f0f8SDina K Nimeh 
6077d82f0f8SDina K Nimeh 	/* query crypto device to get list of kernel ciphers */
6087d82f0f8SDina K Nimeh 	if ((fd = open("/dev/crypto", O_RDWR)) == -1) {
6097d82f0f8SDina K Nimeh 		warn(gettext("failed to open %s"), "/dev/crypto");
6107d82f0f8SDina K Nimeh 		goto kcc_out;
6117d82f0f8SDina K Nimeh 	}
6127d82f0f8SDina K Nimeh 
6137d82f0f8SDina K Nimeh 	if (ioctl(fd, CRYPTO_GET_MECHANISM_LIST, kciphers) == -1) {
6147d82f0f8SDina K Nimeh 		warn(gettext("CRYPTO_GET_MECHANISM_LIST ioctl failed"));
6157d82f0f8SDina K Nimeh 		goto kcc_out;
6167d82f0f8SDina K Nimeh 	}
6177d82f0f8SDina K Nimeh 
6187d82f0f8SDina K Nimeh 	if (kciphers->ml_return_value == CRYPTO_BUFFER_TOO_SMALL) {
6197d82f0f8SDina K Nimeh 		count = kciphers->ml_count;
6207d82f0f8SDina K Nimeh 		free(kciphers);
6217d82f0f8SDina K Nimeh 		kciphers = malloc(sizeof (crypto_get_mechanism_list_t) +
6227d82f0f8SDina K Nimeh 		    sizeof (crypto_mech_name_t) * (count - 1));
6237d82f0f8SDina K Nimeh 		if (kciphers == NULL) {
6247d82f0f8SDina K Nimeh 			warn(gettext("failed to allocate memory for list of "
6257d82f0f8SDina K Nimeh 			    "kernel mechanisms"));
6267d82f0f8SDina K Nimeh 			goto kcc_out;
6277d82f0f8SDina K Nimeh 		}
6287d82f0f8SDina K Nimeh 		kciphers->ml_count = count;
6297d82f0f8SDina K Nimeh 
6307d82f0f8SDina K Nimeh 		if (ioctl(fd, CRYPTO_GET_MECHANISM_LIST, kciphers) == -1) {
6317d82f0f8SDina K Nimeh 			warn(gettext("CRYPTO_GET_MECHANISM_LIST ioctl failed"));
6327d82f0f8SDina K Nimeh 			goto kcc_out;
6337d82f0f8SDina K Nimeh 		}
6347d82f0f8SDina K Nimeh 	}
6357d82f0f8SDina K Nimeh 
6367d82f0f8SDina K Nimeh 	if (kciphers->ml_return_value != CRYPTO_SUCCESS) {
6377d82f0f8SDina K Nimeh 		warn(gettext(
6387d82f0f8SDina K Nimeh 		    "CRYPTO_GET_MECHANISM_LIST ioctl return value = %d\n"),
6397d82f0f8SDina K Nimeh 		    kciphers->ml_return_value);
6407d82f0f8SDina K Nimeh 		goto kcc_out;
6417d82f0f8SDina K Nimeh 	}
6427d82f0f8SDina K Nimeh 
6437d82f0f8SDina K Nimeh 	/*
6447d82f0f8SDina K Nimeh 	 * scan list of kernel ciphers looking for the selected one and if
6457d82f0f8SDina K Nimeh 	 * it needs an iv generated using another cipher, also look for that
6467d82f0f8SDina K Nimeh 	 * additional cipher to be used for generating the iv
6477d82f0f8SDina K Nimeh 	 */
6487d82f0f8SDina K Nimeh 	count = kciphers->ml_count;
6497d82f0f8SDina K Nimeh 	for (i = 0; i < count && !(ciph_ok && iv_ok); i++) {
6507d82f0f8SDina K Nimeh 		if (!ciph_ok &&
6517d82f0f8SDina K Nimeh 		    strcasecmp(cipher->name, kciphers->ml_list[i]) == 0)
6527d82f0f8SDina K Nimeh 			ciph_ok = B_TRUE;
6537d82f0f8SDina K Nimeh 		if (!iv_ok &&
6547d82f0f8SDina K Nimeh 		    strcasecmp(cipher->iv_name, kciphers->ml_list[i]) == 0)
6557d82f0f8SDina K Nimeh 			iv_ok = B_TRUE;
6567d82f0f8SDina K Nimeh 	}
6577d82f0f8SDina K Nimeh 	free(kciphers);
6587d82f0f8SDina K Nimeh 	kciphers = NULL;
6597d82f0f8SDina K Nimeh 
6607d82f0f8SDina K Nimeh 	if (!ciph_ok)
6617d82f0f8SDina K Nimeh 		warn(gettext("%s mechanism not supported in kernel\n"),
6627d82f0f8SDina K Nimeh 		    cipher->name);
6637d82f0f8SDina K Nimeh 	if (!iv_ok)
6647d82f0f8SDina K Nimeh 		warn(gettext("%s mechanism not supported in kernel\n"),
6657d82f0f8SDina K Nimeh 		    cipher->iv_name);
6667d82f0f8SDina K Nimeh 
6677d82f0f8SDina K Nimeh 	if (ciph_ok) {
6687d82f0f8SDina K Nimeh 		/* Get the details about the user selected cipher */
6697d82f0f8SDina K Nimeh 		count = DEFAULT_MECHINFO_NUM;
6707d82f0f8SDina K Nimeh 		kinfo = malloc(sizeof (crypto_get_all_mechanism_info_t) +
6717d82f0f8SDina K Nimeh 		    sizeof (crypto_mechanism_info_t) * (count - 1));
6727d82f0f8SDina K Nimeh 		if (kinfo == NULL) {
6737d82f0f8SDina K Nimeh 			warn(gettext("failed to allocate memory for "
6747d82f0f8SDina K Nimeh 			    "kernel mechanism info"));
6757d82f0f8SDina K Nimeh 			goto kcc_out;
6767d82f0f8SDina K Nimeh 		}
6777d82f0f8SDina K Nimeh 		kinfo->mi_count = count;
6787d82f0f8SDina K Nimeh 		(void) strlcpy(kinfo->mi_mechanism_name, cipher->name,
6797d82f0f8SDina K Nimeh 		    CRYPTO_MAX_MECH_NAME);
6807d82f0f8SDina K Nimeh 
6817d82f0f8SDina K Nimeh 		if (ioctl(fd, CRYPTO_GET_ALL_MECHANISM_INFO, kinfo) == -1) {
6827d82f0f8SDina K Nimeh 			warn(gettext(
6837d82f0f8SDina K Nimeh 			    "CRYPTO_GET_ALL_MECHANISM_INFO ioctl failed"));
6847d82f0f8SDina K Nimeh 			goto kcc_out;
6857d82f0f8SDina K Nimeh 		}
6867d82f0f8SDina K Nimeh 
6877d82f0f8SDina K Nimeh 		if (kinfo->mi_return_value == CRYPTO_BUFFER_TOO_SMALL) {
6887d82f0f8SDina K Nimeh 			count = kinfo->mi_count;
6897d82f0f8SDina K Nimeh 			free(kinfo);
6907d82f0f8SDina K Nimeh 			kinfo = malloc(
6917d82f0f8SDina K Nimeh 			    sizeof (crypto_get_all_mechanism_info_t) +
6927d82f0f8SDina K Nimeh 			    sizeof (crypto_mechanism_info_t) * (count - 1));
6937d82f0f8SDina K Nimeh 			if (kinfo == NULL) {
6947d82f0f8SDina K Nimeh 				warn(gettext("failed to allocate memory for "
6957d82f0f8SDina K Nimeh 				    "kernel mechanism info"));
6967d82f0f8SDina K Nimeh 				goto kcc_out;
6977d82f0f8SDina K Nimeh 			}
6987d82f0f8SDina K Nimeh 			kinfo->mi_count = count;
6997d82f0f8SDina K Nimeh 			(void) strlcpy(kinfo->mi_mechanism_name, cipher->name,
7007d82f0f8SDina K Nimeh 			    CRYPTO_MAX_MECH_NAME);
7017d82f0f8SDina K Nimeh 
7027d82f0f8SDina K Nimeh 			if (ioctl(fd, CRYPTO_GET_ALL_MECHANISM_INFO, kinfo) ==
7037d82f0f8SDina K Nimeh 			    -1) {
7047d82f0f8SDina K Nimeh 				warn(gettext("CRYPTO_GET_ALL_MECHANISM_INFO "
7057d82f0f8SDina K Nimeh 				    "ioctl failed"));
7067d82f0f8SDina K Nimeh 				goto kcc_out;
7077d82f0f8SDina K Nimeh 			}
7087d82f0f8SDina K Nimeh 		}
7097d82f0f8SDina K Nimeh 
7107d82f0f8SDina K Nimeh 		if (kinfo->mi_return_value != CRYPTO_SUCCESS) {
7117d82f0f8SDina K Nimeh 			warn(gettext("CRYPTO_GET_ALL_MECHANISM_INFO ioctl "
7127d82f0f8SDina K Nimeh 			    "return value = %d\n"), kinfo->mi_return_value);
7137d82f0f8SDina K Nimeh 			goto kcc_out;
7147d82f0f8SDina K Nimeh 		}
7157d82f0f8SDina K Nimeh 
7167d82f0f8SDina K Nimeh 		/* Set key min and max size */
7177d82f0f8SDina K Nimeh 		count = kinfo->mi_count;
7187d82f0f8SDina K Nimeh 		i = 0;
7197d82f0f8SDina K Nimeh 		if (i < count) {
7207d82f0f8SDina K Nimeh 			keymin = kinfo->mi_list[i].mi_min_key_size;
7217d82f0f8SDina K Nimeh 			keymax = kinfo->mi_list[i].mi_max_key_size;
7227d82f0f8SDina K Nimeh 			if (kinfo->mi_list[i].mi_keysize_unit &
7237d82f0f8SDina K Nimeh 			    CRYPTO_KEYSIZE_UNIT_IN_BITS) {
7247d82f0f8SDina K Nimeh 				keymin = CRYPTO_BITS2BYTES(keymin);
7257d82f0f8SDina K Nimeh 				keymax = CRYPTO_BITS2BYTES(keymax);
7267d82f0f8SDina K Nimeh 
7277d82f0f8SDina K Nimeh 			}
7287d82f0f8SDina K Nimeh 			cipher->min_keysize = keymin;
7297d82f0f8SDina K Nimeh 			cipher->max_keysize = keymax;
7307d82f0f8SDina K Nimeh 		}
7317d82f0f8SDina K Nimeh 		free(kinfo);
7327d82f0f8SDina K Nimeh 		kinfo = NULL;
7337d82f0f8SDina K Nimeh 
7347d82f0f8SDina K Nimeh 		if (i == count) {
7357d82f0f8SDina K Nimeh 			(void) close(fd);
7367d82f0f8SDina K Nimeh 			die(gettext(
7377d82f0f8SDina K Nimeh 			    "failed to find usable %s kernel mechanism, "
7387d82f0f8SDina K Nimeh 			    "use \"cryptoadm list -m\" to find available "
7397d82f0f8SDina K Nimeh 			    "mechanisms\n"),
7407d82f0f8SDina K Nimeh 			    cipher->name);
7417d82f0f8SDina K Nimeh 		}
7427d82f0f8SDina K Nimeh 	}
7437d82f0f8SDina K Nimeh 
7447d82f0f8SDina K Nimeh 	/* Note: key min/max, unit size, usage for iv cipher are not checked. */
7457d82f0f8SDina K Nimeh 
7467d82f0f8SDina K Nimeh 	return (ciph_ok && iv_ok);
7477d82f0f8SDina K Nimeh 
7487d82f0f8SDina K Nimeh kcc_out:
7497d82f0f8SDina K Nimeh 	if (kinfo != NULL)
7507d82f0f8SDina K Nimeh 		free(kinfo);
7517d82f0f8SDina K Nimeh 	if (kciphers != NULL)
7527d82f0f8SDina K Nimeh 		free(kciphers);
7537d82f0f8SDina K Nimeh 	if (fd != -1)
7547d82f0f8SDina K Nimeh 		(void) close(fd);
7557d82f0f8SDina K Nimeh 	return (B_FALSE);
7567d82f0f8SDina K Nimeh }
7577d82f0f8SDina K Nimeh 
7587d82f0f8SDina K Nimeh /*
7597d82f0f8SDina K Nimeh  * Break up token spec into its components (non-destructive)
7607d82f0f8SDina K Nimeh  */
7617d82f0f8SDina K Nimeh static token_spec_t *
7627d82f0f8SDina K Nimeh parsetoken(char *spec)
7637d82f0f8SDina K Nimeh {
7647d82f0f8SDina K Nimeh #define	FLD_NAME	0
7657d82f0f8SDina K Nimeh #define	FLD_MANUF	1
7667d82f0f8SDina K Nimeh #define	FLD_SERIAL	2
7677d82f0f8SDina K Nimeh #define	FLD_LABEL	3
7687d82f0f8SDina K Nimeh #define	NFIELDS		4
7697d82f0f8SDina K Nimeh #define	nullfield(i)	((field[(i)+1] - field[(i)]) <= 1)
7707d82f0f8SDina K Nimeh #define	copyfield(fld, i)	\
7717d82f0f8SDina K Nimeh 		{							\
7727d82f0f8SDina K Nimeh 			int	n;					\
7737d82f0f8SDina K Nimeh 			(fld) = NULL;					\
7747d82f0f8SDina K Nimeh 			if ((n = (field[(i)+1] - field[(i)])) > 1) {	\
7757d82f0f8SDina K Nimeh 				if (((fld) = malloc(n)) != NULL) {	\
7767d82f0f8SDina K Nimeh 					(void) strncpy((fld), field[(i)], n); \
7777d82f0f8SDina K Nimeh 					((fld))[n - 1] = '\0';		\
7787d82f0f8SDina K Nimeh 				}					\
7797d82f0f8SDina K Nimeh 			}						\
7807d82f0f8SDina K Nimeh 		}
7817d82f0f8SDina K Nimeh 
7827d82f0f8SDina K Nimeh 	int	i;
7837d82f0f8SDina K Nimeh 	char	*field[NFIELDS + 1];	/* +1 to catch extra delimiters */
7847d82f0f8SDina K Nimeh 	token_spec_t *ti = NULL;
7857d82f0f8SDina K Nimeh 
7867d82f0f8SDina K Nimeh 	if (spec == NULL)
7877d82f0f8SDina K Nimeh 		return (NULL);
7887d82f0f8SDina K Nimeh 
7897d82f0f8SDina K Nimeh 	/*
7907d82f0f8SDina K Nimeh 	 * Correct format is "[name]:[manuf]:[serial]:key". Can't use
7917d82f0f8SDina K Nimeh 	 * strtok because it treats ":::key" and "key:::" and "key" all
7927d82f0f8SDina K Nimeh 	 * as the same thing, and we can't have the :s compressed away.
7937d82f0f8SDina K Nimeh 	 */
7947d82f0f8SDina K Nimeh 	field[0] = spec;
7957d82f0f8SDina K Nimeh 	for (i = 1; i < NFIELDS + 1; i++) {
7967d82f0f8SDina K Nimeh 		field[i] = strchr(field[i-1], ':');
7977d82f0f8SDina K Nimeh 		if (field[i] == NULL)
7987d82f0f8SDina K Nimeh 			break;
7997d82f0f8SDina K Nimeh 		field[i]++;
8007d82f0f8SDina K Nimeh 	}
8017d82f0f8SDina K Nimeh 	if (i < NFIELDS)		/* not enough fields */
8027d82f0f8SDina K Nimeh 		return (NULL);
8037d82f0f8SDina K Nimeh 	if (field[NFIELDS] != NULL)	/* too many fields */
8047d82f0f8SDina K Nimeh 		return (NULL);
8057d82f0f8SDina K Nimeh 	field[NFIELDS] = strchr(field[NFIELDS-1], '\0') + 1;
8067d82f0f8SDina K Nimeh 
8077d82f0f8SDina K Nimeh 	/* key label can't be empty */
8087d82f0f8SDina K Nimeh 	if (nullfield(FLD_LABEL))
8097d82f0f8SDina K Nimeh 		return (NULL);
8107d82f0f8SDina K Nimeh 
8117d82f0f8SDina K Nimeh 	ti = malloc(sizeof (token_spec_t));
8127d82f0f8SDina K Nimeh 	if (ti == NULL)
8137d82f0f8SDina K Nimeh 		return (NULL);
8147d82f0f8SDina K Nimeh 
8157d82f0f8SDina K Nimeh 	copyfield(ti->name, FLD_NAME);
8167d82f0f8SDina K Nimeh 	copyfield(ti->mfr, FLD_MANUF);
8177d82f0f8SDina K Nimeh 	copyfield(ti->serno, FLD_SERIAL);
8187d82f0f8SDina K Nimeh 	copyfield(ti->key, FLD_LABEL);
8197d82f0f8SDina K Nimeh 
8207d82f0f8SDina K Nimeh 	/*
8217d82f0f8SDina K Nimeh 	 * If token specified and it only contains a key label, then
8227d82f0f8SDina K Nimeh 	 * search all tokens for the key, otherwise only those with
8237d82f0f8SDina K Nimeh 	 * matching name, mfr, and serno are used.
8247d82f0f8SDina K Nimeh 	 */
8257d82f0f8SDina K Nimeh 	/*
8267d82f0f8SDina K Nimeh 	 * That's how we'd like it to be, however, if only the key label
8277d82f0f8SDina K Nimeh 	 * is specified, default to using softtoken.  It's easier.
8287d82f0f8SDina K Nimeh 	 */
8297d82f0f8SDina K Nimeh 	if (ti->name == NULL && ti->mfr == NULL && ti->serno == NULL)
8307d82f0f8SDina K Nimeh 		ti->name = strdup(pkcs11_default_token());
8317d82f0f8SDina K Nimeh 	return (ti);
8327d82f0f8SDina K Nimeh }
8337d82f0f8SDina K Nimeh 
8347d82f0f8SDina K Nimeh /*
8357d82f0f8SDina K Nimeh  * PBE the passphrase into a raw key
8367d82f0f8SDina K Nimeh  */
8377d82f0f8SDina K Nimeh static void
8387d82f0f8SDina K Nimeh getkeyfromuser(mech_alias_t *cipher, char **raw_key, size_t *raw_key_sz)
8397d82f0f8SDina K Nimeh {
8407d82f0f8SDina K Nimeh 	CK_SESSION_HANDLE sess;
8417d82f0f8SDina K Nimeh 	CK_RV	rv;
8427d82f0f8SDina K Nimeh 	char	*pass = NULL;
8437d82f0f8SDina K Nimeh 	size_t	passlen = 0;
8447d82f0f8SDina K Nimeh 	void	*salt = NULL;	/* don't use NULL, see note on salt below */
8457d82f0f8SDina K Nimeh 	size_t	saltlen = 0;
8467d82f0f8SDina K Nimeh 	CK_KEY_TYPE ktype;
8477d82f0f8SDina K Nimeh 	void	*kvalue;
8487d82f0f8SDina K Nimeh 	size_t	klen;
8497d82f0f8SDina K Nimeh 
8507d82f0f8SDina K Nimeh 	/* did init_crypto find a slot that supports this cipher? */
8517d82f0f8SDina K Nimeh 	if (cipher->slot == (CK_SLOT_ID)-1 || cipher->max_keysize == 0) {
8527d82f0f8SDina K Nimeh 		rv = CKR_MECHANISM_INVALID;
8537d82f0f8SDina K Nimeh 		goto cleanup;
8547d82f0f8SDina K Nimeh 	}
8557d82f0f8SDina K Nimeh 
8567d82f0f8SDina K Nimeh 	rv = pkcs11_mech2keytype(cipher->type, &ktype);
8577d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
8587d82f0f8SDina K Nimeh 		goto cleanup;
8597d82f0f8SDina K Nimeh 
8607d82f0f8SDina K Nimeh 	/*
8617d82f0f8SDina K Nimeh 	 * use the passphrase to generate a PBE PKCS#5 secret key and
8627d82f0f8SDina K Nimeh 	 * retrieve the raw key data to eventually pass it to the kernel;
8637d82f0f8SDina K Nimeh 	 */
8647d82f0f8SDina K Nimeh 	rv = C_OpenSession(cipher->slot, CKF_SERIAL_SESSION, NULL, NULL, &sess);
8657d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
8667d82f0f8SDina K Nimeh 		goto cleanup;
8677d82f0f8SDina K Nimeh 
8687d82f0f8SDina K Nimeh 	/* get user passphrase with 8 byte minimum */
8697d82f0f8SDina K Nimeh 	if (pkcs11_get_pass(NULL, &pass, &passlen, MIN_PASSLEN, B_TRUE) < 0) {
8707d82f0f8SDina K Nimeh 		die(gettext("passphrases do not match\n"));
8717d82f0f8SDina K Nimeh 	}
8727d82f0f8SDina K Nimeh 
8737d82f0f8SDina K Nimeh 	/*
8747d82f0f8SDina K Nimeh 	 * salt should not be NULL, or else pkcs11_PasswdToKey() will
8757d82f0f8SDina K Nimeh 	 * complain about CKR_MECHANISM_PARAM_INVALID; the following is
8767d82f0f8SDina K Nimeh 	 * to make up for not having a salt until a proper one is used
8777d82f0f8SDina K Nimeh 	 */
8787d82f0f8SDina K Nimeh 	salt = pass;
8797d82f0f8SDina K Nimeh 	saltlen = passlen;
8807d82f0f8SDina K Nimeh 
8817d82f0f8SDina K Nimeh 	klen = cipher->max_keysize;
8827d82f0f8SDina K Nimeh 	rv = pkcs11_PasswdToKey(sess, pass, passlen, salt, saltlen, ktype,
8837d82f0f8SDina K Nimeh 	    cipher->max_keysize, &kvalue, &klen);
8847d82f0f8SDina K Nimeh 
8857d82f0f8SDina K Nimeh 	(void) C_CloseSession(sess);
8867d82f0f8SDina K Nimeh 
8877d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
8887d82f0f8SDina K Nimeh 		goto cleanup;
8897d82f0f8SDina K Nimeh 	}
8907d82f0f8SDina K Nimeh 
8917d82f0f8SDina K Nimeh 	/* assert(klen == cipher->max_keysize); */
8927d82f0f8SDina K Nimeh 	*raw_key_sz = klen;
8937d82f0f8SDina K Nimeh 	*raw_key = (char *)kvalue;
8947d82f0f8SDina K Nimeh 	return;
8957d82f0f8SDina K Nimeh 
8967d82f0f8SDina K Nimeh cleanup:
8977d82f0f8SDina K Nimeh 	die(gettext("failed to generate %s key from passphrase: %s"),
8987d82f0f8SDina K Nimeh 	    cipher->alias, pkcs11_strerror(rv));
8997d82f0f8SDina K Nimeh }
9007d82f0f8SDina K Nimeh 
9017d82f0f8SDina K Nimeh /*
9027d82f0f8SDina K Nimeh  * Read raw key from file; also handles ephemeral keys.
9037d82f0f8SDina K Nimeh  */
9047d82f0f8SDina K Nimeh void
9057d82f0f8SDina K Nimeh getkeyfromfile(const char *pathname, mech_alias_t *cipher, char **key,
9067d82f0f8SDina K Nimeh     size_t *ksz)
9077d82f0f8SDina K Nimeh {
9087d82f0f8SDina K Nimeh 	int	fd;
9097d82f0f8SDina K Nimeh 	struct stat sbuf;
9107d82f0f8SDina K Nimeh 	boolean_t notplain = B_FALSE;
9117d82f0f8SDina K Nimeh 	ssize_t	cursz;
9127d82f0f8SDina K Nimeh 	ssize_t	nread;
9137d82f0f8SDina K Nimeh 
9147d82f0f8SDina K Nimeh 	/* ephemeral keys are just random data */
9157d82f0f8SDina K Nimeh 	if (pathname == NULL) {
9167d82f0f8SDina K Nimeh 		*ksz = cipher->max_keysize;
9177d82f0f8SDina K Nimeh 		*key = malloc(*ksz);
9187d82f0f8SDina K Nimeh 		if (*key == NULL)
9197d82f0f8SDina K Nimeh 			die(gettext("failed to allocate memory for"
9207d82f0f8SDina K Nimeh 			    " ephemeral key"));
9217b79d846SDina K Nimeh 		if (pkcs11_get_urandom(*key, *ksz) < 0) {
9227d82f0f8SDina K Nimeh 			free(*key);
9237d82f0f8SDina K Nimeh 			die(gettext("failed to get enough random data"));
9247d82f0f8SDina K Nimeh 		}
9257d82f0f8SDina K Nimeh 		return;
9267d82f0f8SDina K Nimeh 	}
9277d82f0f8SDina K Nimeh 
9287d82f0f8SDina K Nimeh 	/*
9297d82f0f8SDina K Nimeh 	 * If the remaining section of code didn't also check for secure keyfile
9307d82f0f8SDina K Nimeh 	 * permissions and whether the key is within cipher min and max lengths,
9317d82f0f8SDina K Nimeh 	 * (or, if those things moved out of this block), we could have had:
9327d82f0f8SDina K Nimeh 	 *	if (pkcs11_read_data(pathname, key, ksz) < 0)
9337d82f0f8SDina K Nimeh 	 *		handle_error();
9347d82f0f8SDina K Nimeh 	 */
9357d82f0f8SDina K Nimeh 
9367d82f0f8SDina K Nimeh 	if ((fd = open(pathname, O_RDONLY, 0)) == -1)
9377d82f0f8SDina K Nimeh 		die(gettext("open of keyfile (%s) failed"), pathname);
9387d82f0f8SDina K Nimeh 
9397d82f0f8SDina K Nimeh 	if (fstat(fd, &sbuf) == -1)
9407d82f0f8SDina K Nimeh 		die(gettext("fstat of keyfile (%s) failed"), pathname);
9417d82f0f8SDina K Nimeh 
9427d82f0f8SDina K Nimeh 	if (S_ISREG(sbuf.st_mode)) {
9437d82f0f8SDina K Nimeh 		if ((sbuf.st_mode & (S_IWGRP | S_IWOTH)) != 0)
9447d82f0f8SDina K Nimeh 			die(gettext("insecure permissions on keyfile %s\n"),
9457d82f0f8SDina K Nimeh 			    pathname);
9467d82f0f8SDina K Nimeh 
9477d82f0f8SDina K Nimeh 		*ksz = sbuf.st_size;
9487d82f0f8SDina K Nimeh 		if (*ksz < cipher->min_keysize || cipher->max_keysize < *ksz) {
9497d82f0f8SDina K Nimeh 			warn(gettext("%s: invalid keysize: %d\n"),
9507d82f0f8SDina K Nimeh 			    pathname, (int)*ksz);
9517d82f0f8SDina K Nimeh 			die(gettext("\t%d <= keysize <= %d\n"),
9527d82f0f8SDina K Nimeh 			    cipher->min_keysize, cipher->max_keysize);
9537d82f0f8SDina K Nimeh 		}
9547d82f0f8SDina K Nimeh 	} else {
9557d82f0f8SDina K Nimeh 		*ksz = cipher->max_keysize;
9567d82f0f8SDina K Nimeh 		notplain = B_TRUE;
9577d82f0f8SDina K Nimeh 	}
9587d82f0f8SDina K Nimeh 
9597d82f0f8SDina K Nimeh 	*key = malloc(*ksz);
9607d82f0f8SDina K Nimeh 	if (*key == NULL)
9617d82f0f8SDina K Nimeh 		die(gettext("failed to allocate memory for key from file"));
9627d82f0f8SDina K Nimeh 
9637d82f0f8SDina K Nimeh 	for (cursz = 0, nread = 0; cursz < *ksz; cursz += nread) {
9647d82f0f8SDina K Nimeh 		nread = read(fd, *key, *ksz);
9657d82f0f8SDina K Nimeh 		if (nread > 0)
9667d82f0f8SDina K Nimeh 			continue;
9677d82f0f8SDina K Nimeh 		/*
9687d82f0f8SDina K Nimeh 		 * nread == 0.  If it's not a regular file we were trying to
9697d82f0f8SDina K Nimeh 		 * get the maximum keysize of data possible for this cipher.
9707d82f0f8SDina K Nimeh 		 * But if we've got at least the minimum keysize of data,
9717d82f0f8SDina K Nimeh 		 * round down to the nearest keysize unit and call it good.
9727d82f0f8SDina K Nimeh 		 * If we haven't met the minimum keysize, that's an error.
9737d82f0f8SDina K Nimeh 		 * If it's a regular file, nread = 0 is also an error.
9747d82f0f8SDina K Nimeh 		 */
9757d82f0f8SDina K Nimeh 		if (nread == 0 && notplain && cursz >= cipher->min_keysize) {
9767d82f0f8SDina K Nimeh 			*ksz = (cursz / cipher->min_keysize) *
9777d82f0f8SDina K Nimeh 			    cipher->min_keysize;
9787d82f0f8SDina K Nimeh 			break;
9797d82f0f8SDina K Nimeh 		}
9807d82f0f8SDina K Nimeh 		die(gettext("%s: can't read all keybytes"), pathname);
9817d82f0f8SDina K Nimeh 	}
9827d82f0f8SDina K Nimeh 	(void) close(fd);
9837d82f0f8SDina K Nimeh }
9847d82f0f8SDina K Nimeh 
9857d82f0f8SDina K Nimeh /*
9867d82f0f8SDina K Nimeh  * Read the raw key from token, or from a file that was wrapped with a
9877d82f0f8SDina K Nimeh  * key from token
9887d82f0f8SDina K Nimeh  */
9897d82f0f8SDina K Nimeh void
9907d82f0f8SDina K Nimeh getkeyfromtoken(CK_SESSION_HANDLE sess,
9917d82f0f8SDina K Nimeh     token_spec_t *token, const char *keyfile, mech_alias_t *cipher,
9927d82f0f8SDina K Nimeh     char **raw_key, size_t *raw_key_sz)
9937d82f0f8SDina K Nimeh {
9947d82f0f8SDina K Nimeh 	CK_RV	rv = CKR_OK;
9957d82f0f8SDina K Nimeh 	CK_BBOOL trueval = B_TRUE;
9967d82f0f8SDina K Nimeh 	CK_OBJECT_CLASS kclass;		/* secret key or RSA private key */
9977d82f0f8SDina K Nimeh 	CK_KEY_TYPE ktype;		/* from selected cipher or CKK_RSA */
9987d82f0f8SDina K Nimeh 	CK_KEY_TYPE raw_ktype;		/* from selected cipher */
9997d82f0f8SDina K Nimeh 	CK_ATTRIBUTE	key_tmpl[] = {
10007d82f0f8SDina K Nimeh 		{ CKA_CLASS, NULL, 0 },	/* re-used for token key and unwrap */
10017d82f0f8SDina K Nimeh 		{ CKA_KEY_TYPE, NULL, 0 },	/* ditto */
10027d82f0f8SDina K Nimeh 		{ CKA_LABEL, NULL, 0 },
10037d82f0f8SDina K Nimeh 		{ CKA_TOKEN, NULL, 0 },
10047d82f0f8SDina K Nimeh 		{ CKA_PRIVATE, NULL, 0 }
10057d82f0f8SDina K Nimeh 	    };
10067d82f0f8SDina K Nimeh 	CK_ULONG attrs = sizeof (key_tmpl) / sizeof (CK_ATTRIBUTE);
10077d82f0f8SDina K Nimeh 	int	i;
10087d82f0f8SDina K Nimeh 	char	*pass = NULL;
10097d82f0f8SDina K Nimeh 	size_t	passlen = 0;
10107d82f0f8SDina K Nimeh 	CK_OBJECT_HANDLE obj, rawobj;
10117d82f0f8SDina K Nimeh 	CK_ULONG num_objs = 1;		/* just want to find 1 token key */
10127d82f0f8SDina K Nimeh 	CK_MECHANISM unwrap = { CKM_RSA_PKCS, NULL, 0 };
10137d82f0f8SDina K Nimeh 	char	*rkey;
10147d82f0f8SDina K Nimeh 	size_t	rksz;
10157d82f0f8SDina K Nimeh 
10167d82f0f8SDina K Nimeh 	if (token == NULL || token->key == NULL)
10177d82f0f8SDina K Nimeh 		return;
10187d82f0f8SDina K Nimeh 
10197d82f0f8SDina K Nimeh 	/* did init_crypto find a slot that supports this cipher? */
10207d82f0f8SDina K Nimeh 	if (cipher->slot == (CK_SLOT_ID)-1 || cipher->max_keysize == 0) {
10217d82f0f8SDina K Nimeh 		die(gettext("failed to find any cryptographic provider, "
10227d82f0f8SDina K Nimeh 		    "use \"cryptoadm list -p\" to find providers: %s\n"),
10237d82f0f8SDina K Nimeh 		    pkcs11_strerror(CKR_MECHANISM_INVALID));
10247d82f0f8SDina K Nimeh 	}
10257d82f0f8SDina K Nimeh 
10267d82f0f8SDina K Nimeh 	if (pkcs11_get_pass(token->name, &pass, &passlen, 0, B_FALSE) < 0)
10277d82f0f8SDina K Nimeh 		die(gettext("unable to get passphrase"));
10287d82f0f8SDina K Nimeh 
10297d82f0f8SDina K Nimeh 	/* use passphrase to login to token */
10307d82f0f8SDina K Nimeh 	if (pass != NULL && passlen > 0) {
10317d82f0f8SDina K Nimeh 		rv = C_Login(sess, CKU_USER, (CK_UTF8CHAR_PTR)pass, passlen);
10327d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
10337d82f0f8SDina K Nimeh 			die(gettext("cannot login to the token %s: %s\n"),
10347d82f0f8SDina K Nimeh 			    token->name, pkcs11_strerror(rv));
10357d82f0f8SDina K Nimeh 		}
10367d82f0f8SDina K Nimeh 	}
10377d82f0f8SDina K Nimeh 
10387d82f0f8SDina K Nimeh 	rv = pkcs11_mech2keytype(cipher->type, &raw_ktype);
10397d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
10407d82f0f8SDina K Nimeh 		die(gettext("failed to get key type for cipher %s: %s\n"),
10417d82f0f8SDina K Nimeh 		    cipher->name, pkcs11_strerror(rv));
10427d82f0f8SDina K Nimeh 	}
10437d82f0f8SDina K Nimeh 
10447d82f0f8SDina K Nimeh 	/*
10457d82f0f8SDina K Nimeh 	 * If no keyfile was given, then the token key is secret key to
10467d82f0f8SDina K Nimeh 	 * be used for encryption/decryption.  Otherwise, the keyfile
10477d82f0f8SDina K Nimeh 	 * contains a wrapped secret key, and the token is actually the
10487d82f0f8SDina K Nimeh 	 * unwrapping RSA private key.
10497d82f0f8SDina K Nimeh 	 */
10507d82f0f8SDina K Nimeh 	if (keyfile == NULL) {
10517d82f0f8SDina K Nimeh 		kclass = CKO_SECRET_KEY;
10527d82f0f8SDina K Nimeh 		ktype = raw_ktype;
10537d82f0f8SDina K Nimeh 	} else {
10547d82f0f8SDina K Nimeh 		kclass = CKO_PRIVATE_KEY;
10557d82f0f8SDina K Nimeh 		ktype = CKK_RSA;
10567d82f0f8SDina K Nimeh 	}
10577d82f0f8SDina K Nimeh 
10587d82f0f8SDina K Nimeh 	/* Find the key in the token first */
10597d82f0f8SDina K Nimeh 	for (i = 0; i < attrs; i++) {
10607d82f0f8SDina K Nimeh 		switch (key_tmpl[i].type) {
10617d82f0f8SDina K Nimeh 		case CKA_CLASS:
10627d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &kclass;
10637d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (kclass);
10647d82f0f8SDina K Nimeh 			break;
10657d82f0f8SDina K Nimeh 		case CKA_KEY_TYPE:
10667d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &ktype;
10677d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (ktype);
10687d82f0f8SDina K Nimeh 			break;
10697d82f0f8SDina K Nimeh 		case CKA_LABEL:
10707d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = token->key;
10717d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = strlen(token->key);
10727d82f0f8SDina K Nimeh 			break;
10737d82f0f8SDina K Nimeh 		case CKA_TOKEN:
10747d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &trueval;
10757d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (trueval);
10767d82f0f8SDina K Nimeh 			break;
10777d82f0f8SDina K Nimeh 		case CKA_PRIVATE:
10787d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &trueval;
10797d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (trueval);
10807d82f0f8SDina K Nimeh 			break;
10817d82f0f8SDina K Nimeh 		default:
10827d82f0f8SDina K Nimeh 			break;
10837d82f0f8SDina K Nimeh 		}
10847d82f0f8SDina K Nimeh 	}
10857d82f0f8SDina K Nimeh 	rv = C_FindObjectsInit(sess, key_tmpl, attrs);
10867d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
10877d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s: %s\n"), token->key,
10887d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
10897d82f0f8SDina K Nimeh 	rv = C_FindObjects(sess, &obj, 1, &num_objs);
10907d82f0f8SDina K Nimeh 	(void) C_FindObjectsFinal(sess);
10917d82f0f8SDina K Nimeh 
10927d82f0f8SDina K Nimeh 	if (num_objs == 0) {
10937d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s\n"), token->key);
10947d82f0f8SDina K Nimeh 	} else if (rv != CKR_OK) {
10957d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s: %s\n"), token->key,
10967d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
10977d82f0f8SDina K Nimeh 	}
10987d82f0f8SDina K Nimeh 
10997d82f0f8SDina K Nimeh 	/*
11007d82f0f8SDina K Nimeh 	 * No keyfile means when token key is found, convert it to raw key,
11017d82f0f8SDina K Nimeh 	 * and done.  Otherwise still need do an unwrap to create yet another
11027d82f0f8SDina K Nimeh 	 * obj and that needs to be converted to raw key before we're done.
11037d82f0f8SDina K Nimeh 	 */
11047d82f0f8SDina K Nimeh 	if (keyfile == NULL) {
11057d82f0f8SDina K Nimeh 		/* obj contains raw key, extract it */
11067d82f0f8SDina K Nimeh 		rv = pkcs11_ObjectToKey(sess, obj, (void **)&rkey, &rksz,
11077d82f0f8SDina K Nimeh 		    B_FALSE);
11087d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
11097d82f0f8SDina K Nimeh 			die(gettext("failed to get key value for %s"
11107d82f0f8SDina K Nimeh 			    " from token %s, %s\n"), token->key,
11117d82f0f8SDina K Nimeh 			    token->name, pkcs11_strerror(rv));
11127d82f0f8SDina K Nimeh 		}
11137d82f0f8SDina K Nimeh 	} else {
11147d82f0f8SDina K Nimeh 		getkeyfromfile(keyfile, cipher, &rkey, &rksz);
11157d82f0f8SDina K Nimeh 
11167d82f0f8SDina K Nimeh 		/*
11177d82f0f8SDina K Nimeh 		 * Got the wrapping RSA obj and the wrapped key from file.
11187d82f0f8SDina K Nimeh 		 * Unwrap the key from file with RSA obj to get rawkey obj.
11197d82f0f8SDina K Nimeh 		 */
11207d82f0f8SDina K Nimeh 
11217d82f0f8SDina K Nimeh 		/* re-use the first two attributes of key_tmpl */
11227d82f0f8SDina K Nimeh 		kclass = CKO_SECRET_KEY;
11237d82f0f8SDina K Nimeh 		ktype = raw_ktype;
11247d82f0f8SDina K Nimeh 
11257d82f0f8SDina K Nimeh 		rv = C_UnwrapKey(sess, &unwrap, obj, (CK_BYTE_PTR)rkey,
11267d82f0f8SDina K Nimeh 		    rksz, key_tmpl, 2, &rawobj);
11277d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
11287d82f0f8SDina K Nimeh 			die(gettext("failed to unwrap key in keyfile %s,"
11297d82f0f8SDina K Nimeh 			    " %s\n"), keyfile, pkcs11_strerror(rv));
11307d82f0f8SDina K Nimeh 		}
11317d82f0f8SDina K Nimeh 		/* rawobj contains raw key, extract it */
11327d82f0f8SDina K Nimeh 		rv = pkcs11_ObjectToKey(sess, rawobj, (void **)&rkey, &rksz,
11337d82f0f8SDina K Nimeh 		    B_TRUE);
11347d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
11357d82f0f8SDina K Nimeh 			die(gettext("failed to get unwrapped key value for"
11367d82f0f8SDina K Nimeh 			    " key in keyfile %s, %s\n"), keyfile,
11377d82f0f8SDina K Nimeh 			    pkcs11_strerror(rv));
11387d82f0f8SDina K Nimeh 		}
11397d82f0f8SDina K Nimeh 	}
11407d82f0f8SDina K Nimeh 
11417d82f0f8SDina K Nimeh 	/* validate raw key size */
11427d82f0f8SDina K Nimeh 	if (rksz < cipher->min_keysize || cipher->max_keysize < rksz) {
11437d82f0f8SDina K Nimeh 		warn(gettext("%s: invalid keysize: %d\n"), keyfile, (int)rksz);
11447d82f0f8SDina K Nimeh 		die(gettext("\t%d <= keysize <= %d\n"), cipher->min_keysize,
11457d82f0f8SDina K Nimeh 		    cipher->max_keysize);
11467d82f0f8SDina K Nimeh 	}
11477d82f0f8SDina K Nimeh 
11487d82f0f8SDina K Nimeh 	*raw_key_sz = rksz;
11497d82f0f8SDina K Nimeh 	*raw_key = (char *)rkey;
11507d82f0f8SDina K Nimeh }
11517d82f0f8SDina K Nimeh 
11527d82f0f8SDina K Nimeh /*
11537d82f0f8SDina K Nimeh  * Set up cipher key limits and verify PKCS#11 can be done
11547d82f0f8SDina K Nimeh  * match_token_cipher is the function pointer used by
11557d82f0f8SDina K Nimeh  * pkcs11_GetCriteriaSession() init_crypto.
11567d82f0f8SDina K Nimeh  */
11577d82f0f8SDina K Nimeh boolean_t
11587d82f0f8SDina K Nimeh match_token_cipher(CK_SLOT_ID slot_id, void *args, CK_RV *rv)
11597d82f0f8SDina K Nimeh {
11607d82f0f8SDina K Nimeh 	token_spec_t *token;
11617d82f0f8SDina K Nimeh 	mech_alias_t *cipher;
11627d82f0f8SDina K Nimeh 	CK_TOKEN_INFO tokinfo;
11637d82f0f8SDina K Nimeh 	CK_MECHANISM_INFO mechinfo;
11647d82f0f8SDina K Nimeh 	boolean_t token_match;
11657d82f0f8SDina K Nimeh 
11667d82f0f8SDina K Nimeh 	/*
11677d82f0f8SDina K Nimeh 	 * While traversing slot list, pick up the following info per slot:
11687d82f0f8SDina K Nimeh 	 * - if token specified, whether it matches this slot's token info
11697d82f0f8SDina K Nimeh 	 * - if the slot supports the PKCS#5 PBKD2 cipher
11707d82f0f8SDina K Nimeh 	 *
11717d82f0f8SDina K Nimeh 	 * If the user said on the command line
11727d82f0f8SDina K Nimeh 	 *	-T tok:mfr:ser:lab -k keyfile
11737d82f0f8SDina K Nimeh 	 *	-c cipher -T tok:mfr:ser:lab -k keyfile
11747d82f0f8SDina K Nimeh 	 * the given cipher or the default cipher apply to keyfile,
11757d82f0f8SDina K Nimeh 	 * If the user said instead
11767d82f0f8SDina K Nimeh 	 *	-T tok:mfr:ser:lab
11777d82f0f8SDina K Nimeh 	 *	-c cipher -T tok:mfr:ser:lab
11787d82f0f8SDina K Nimeh 	 * the key named "lab" may or may not agree with the given
11797d82f0f8SDina K Nimeh 	 * cipher or the default cipher.  In those cases, cipher will
11807d82f0f8SDina K Nimeh 	 * be overridden with the actual cipher type of the key "lab".
11817d82f0f8SDina K Nimeh 	 */
11827d82f0f8SDina K Nimeh 	*rv = CKR_FUNCTION_FAILED;
11837d82f0f8SDina K Nimeh 
11847d82f0f8SDina K Nimeh 	if (args == NULL) {
11857d82f0f8SDina K Nimeh 		return (B_FALSE);
11867d82f0f8SDina K Nimeh 	}
11877d82f0f8SDina K Nimeh 
11887d82f0f8SDina K Nimeh 	cipher = (mech_alias_t *)args;
11897d82f0f8SDina K Nimeh 	token = cipher->token;
11907d82f0f8SDina K Nimeh 
11917d82f0f8SDina K Nimeh 	if (C_GetMechanismInfo(slot_id, cipher->type, &mechinfo) != CKR_OK) {
11927d82f0f8SDina K Nimeh 		return (B_FALSE);
11937d82f0f8SDina K Nimeh 	}
11947d82f0f8SDina K Nimeh 
11957d82f0f8SDina K Nimeh 	if (token == NULL) {
11967d82f0f8SDina K Nimeh 		if (C_GetMechanismInfo(slot_id, CKM_PKCS5_PBKD2, &mechinfo) !=
11977d82f0f8SDina K Nimeh 		    CKR_OK) {
11987d82f0f8SDina K Nimeh 			return (B_FALSE);
11997d82f0f8SDina K Nimeh 		}
12007d82f0f8SDina K Nimeh 		goto foundit;
12017d82f0f8SDina K Nimeh 	}
12027d82f0f8SDina K Nimeh 
12037d82f0f8SDina K Nimeh 	/* does the token match the token spec? */
12047d82f0f8SDina K Nimeh 	if (token->key == NULL || (C_GetTokenInfo(slot_id, &tokinfo) != CKR_OK))
12057d82f0f8SDina K Nimeh 		return (B_FALSE);
12067d82f0f8SDina K Nimeh 
12077d82f0f8SDina K Nimeh 	token_match = B_TRUE;
12087d82f0f8SDina K Nimeh 
12097d82f0f8SDina K Nimeh 	if (token->name != NULL && (token->name)[0] != '\0' &&
12107d82f0f8SDina K Nimeh 	    strncmp((char *)token->name, (char *)tokinfo.label,
12117d82f0f8SDina K Nimeh 	    TOKEN_LABEL_SIZE) != 0)
12127d82f0f8SDina K Nimeh 		token_match = B_FALSE;
12137d82f0f8SDina K Nimeh 	if (token->mfr != NULL && (token->mfr)[0] != '\0' &&
12147d82f0f8SDina K Nimeh 	    strncmp((char *)token->mfr, (char *)tokinfo.manufacturerID,
12157d82f0f8SDina K Nimeh 	    TOKEN_MANUFACTURER_SIZE) != 0)
12167d82f0f8SDina K Nimeh 		token_match = B_FALSE;
12177d82f0f8SDina K Nimeh 	if (token->serno != NULL && (token->serno)[0] != '\0' &&
12187d82f0f8SDina K Nimeh 	    strncmp((char *)token->serno, (char *)tokinfo.serialNumber,
12197d82f0f8SDina K Nimeh 	    TOKEN_SERIAL_SIZE) != 0)
12207d82f0f8SDina K Nimeh 		token_match = B_FALSE;
12217d82f0f8SDina K Nimeh 
12227d82f0f8SDina K Nimeh 	if (!token_match)
12237d82f0f8SDina K Nimeh 		return (B_FALSE);
12247d82f0f8SDina K Nimeh 
12257d82f0f8SDina K Nimeh foundit:
12267d82f0f8SDina K Nimeh 	cipher->slot = slot_id;
12277d82f0f8SDina K Nimeh 	return (B_TRUE);
12287d82f0f8SDina K Nimeh }
12297d82f0f8SDina K Nimeh 
12307d82f0f8SDina K Nimeh /*
12317d82f0f8SDina K Nimeh  * Clean up crypto loose ends
12327d82f0f8SDina K Nimeh  */
12337d82f0f8SDina K Nimeh static void
12347d82f0f8SDina K Nimeh end_crypto(CK_SESSION_HANDLE sess)
12357d82f0f8SDina K Nimeh {
12367d82f0f8SDina K Nimeh 	(void) C_CloseSession(sess);
12377d82f0f8SDina K Nimeh 	(void) C_Finalize(NULL);
12387d82f0f8SDina K Nimeh }
12397d82f0f8SDina K Nimeh 
12407d82f0f8SDina K Nimeh /*
12417d82f0f8SDina K Nimeh  * Set up crypto, opening session on slot that matches token and cipher
12427d82f0f8SDina K Nimeh  */
12437d82f0f8SDina K Nimeh static void
12447d82f0f8SDina K Nimeh init_crypto(token_spec_t *token, mech_alias_t *cipher,
12457d82f0f8SDina K Nimeh     CK_SESSION_HANDLE_PTR sess)
12467d82f0f8SDina K Nimeh {
12477d82f0f8SDina K Nimeh 	CK_RV	rv;
12487d82f0f8SDina K Nimeh 
12497d82f0f8SDina K Nimeh 	cipher->token = token;
12507d82f0f8SDina K Nimeh 
12517d82f0f8SDina K Nimeh 	/* Turn off Metaslot so that we can see actual tokens */
12527d82f0f8SDina K Nimeh 	if (setenv("METASLOT_ENABLED", "false", 1) < 0) {
12537d82f0f8SDina K Nimeh 		die(gettext("could not disable Metaslot"));
12547d82f0f8SDina K Nimeh 	}
12557d82f0f8SDina K Nimeh 
12567d82f0f8SDina K Nimeh 	rv = pkcs11_GetCriteriaSession(match_token_cipher, (void *)cipher,
12577d82f0f8SDina K Nimeh 	    sess);
12587d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
12597d82f0f8SDina K Nimeh 		end_crypto(*sess);
12607d82f0f8SDina K Nimeh 		if (rv == CKR_HOST_MEMORY) {
12617d82f0f8SDina K Nimeh 			die("malloc");
12627d82f0f8SDina K Nimeh 		}
12637d82f0f8SDina K Nimeh 		die(gettext("failed to find any cryptographic provider, "
12647d82f0f8SDina K Nimeh 		    "use \"cryptoadm list -p\" to find providers: %s\n"),
12657d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
12667d82f0f8SDina K Nimeh 	}
12677d82f0f8SDina K Nimeh }
12687d82f0f8SDina K Nimeh 
12697d82f0f8SDina K Nimeh /*
127087117650Saalok  * Uncompress a file.
127187117650Saalok  *
127287117650Saalok  * First map the file in to establish a device
127387117650Saalok  * association, then read from it. On-the-fly
127487117650Saalok  * decompression will automatically uncompress
127587117650Saalok  * the file if it's compressed
127687117650Saalok  *
127787117650Saalok  * If the file is mapped and a device association
127887117650Saalok  * has been established, disallow uncompressing
127987117650Saalok  * the file until it is unmapped.
128087117650Saalok  */
128187117650Saalok static void
128287117650Saalok lofi_uncompress(int lfd, const char *filename)
12837c478bd9Sstevel@tonic-gate {
128487117650Saalok 	struct lofi_ioctl li;
128587117650Saalok 	char buf[MAXBSIZE];
128687117650Saalok 	char devicename[32];
128787117650Saalok 	char tmpfilename[MAXPATHLEN];
12887d82f0f8SDina K Nimeh 	char *x;
128987117650Saalok 	char *dir = NULL;
129087117650Saalok 	char *file = NULL;
129187117650Saalok 	int minor = 0;
129287117650Saalok 	struct stat64 statbuf;
129387117650Saalok 	int compfd = -1;
129487117650Saalok 	int uncompfd = -1;
129587117650Saalok 	ssize_t rbytes;
129687117650Saalok 
129787117650Saalok 	/*
129887117650Saalok 	 * Disallow uncompressing the file if it is
129987117650Saalok 	 * already mapped.
130087117650Saalok 	 */
1301789dced6SJerry Jelinek 	li.li_crypto_enabled = B_FALSE;
130287117650Saalok 	li.li_minor = 0;
130387117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
130487117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1)
130587117650Saalok 		die(gettext("%s must be unmapped before uncompressing"),
130687117650Saalok 		    filename);
130787117650Saalok 
130887117650Saalok 	/* Zero length files don't need to be uncompressed */
130987117650Saalok 	if (stat64(filename, &statbuf) == -1)
131087117650Saalok 		die(gettext("stat: %s"), filename);
131187117650Saalok 	if (statbuf.st_size == 0)
131287117650Saalok 		return;
131387117650Saalok 
13147d82f0f8SDina K Nimeh 	minor = lofi_map_file(lfd, li, filename);
131587117650Saalok 	(void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d",
131687117650Saalok 	    LOFI_BLOCK_NAME, minor);
131787117650Saalok 
131887117650Saalok 	/* If the file isn't compressed, we just return */
131987117650Saalok 	if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) ||
1320a423e759Saalok 	    (li.li_algorithm[0] == '\0')) {
132187117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1322a423e759Saalok 		die("%s is not compressed\n", filename);
132387117650Saalok 	}
132487117650Saalok 
132587117650Saalok 	if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) {
132687117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
132787117650Saalok 		die(gettext("open: %s"), filename);
132887117650Saalok 	}
132987117650Saalok 	/* Create a temp file in the same directory */
13307d82f0f8SDina K Nimeh 	x = strdup(filename);
13317d82f0f8SDina K Nimeh 	dir = strdup(dirname(x));
13327d82f0f8SDina K Nimeh 	free(x);
13337d82f0f8SDina K Nimeh 	x = strdup(filename);
13347d82f0f8SDina K Nimeh 	file = strdup(basename(x));
13357d82f0f8SDina K Nimeh 	free(x);
133687117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
133787117650Saalok 	    "%s/.%sXXXXXX", dir, file);
13387d82f0f8SDina K Nimeh 	free(dir);
13397d82f0f8SDina K Nimeh 	free(file);
134087117650Saalok 
134187117650Saalok 	if ((uncompfd = mkstemp64(tmpfilename)) == -1) {
134287117650Saalok 		(void) close(compfd);
134387117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1344a423e759Saalok 		die("%s could not be uncompressed\n", filename);
134587117650Saalok 	}
134687117650Saalok 
134787117650Saalok 	/*
134887117650Saalok 	 * Set the mode bits and the owner of this temporary
134987117650Saalok 	 * file to be that of the original uncompressed file
135087117650Saalok 	 */
135187117650Saalok 	(void) fchmod(uncompfd, statbuf.st_mode);
135287117650Saalok 
135387117650Saalok 	if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) {
135487117650Saalok 		(void) close(compfd);
135587117650Saalok 		(void) close(uncompfd);
135687117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1357a423e759Saalok 		die("%s could not be uncompressed\n", filename);
135887117650Saalok 	}
135987117650Saalok 
136087117650Saalok 	/* Now read from the device in MAXBSIZE-sized chunks */
136187117650Saalok 	for (;;) {
136287117650Saalok 		rbytes = read(compfd, buf, sizeof (buf));
136387117650Saalok 
136487117650Saalok 		if (rbytes <= 0)
136587117650Saalok 			break;
136687117650Saalok 
136787117650Saalok 		if (write(uncompfd, buf, rbytes) != rbytes) {
136887117650Saalok 			rbytes = -1;
136987117650Saalok 			break;
137087117650Saalok 		}
137187117650Saalok 	}
137287117650Saalok 
137387117650Saalok 	(void) close(compfd);
137487117650Saalok 	(void) close(uncompfd);
137587117650Saalok 
137687117650Saalok 	/* Delete the mapping */
137787117650Saalok 	delete_mapping(lfd, devicename, filename, B_TRUE);
137887117650Saalok 
137987117650Saalok 	/*
138087117650Saalok 	 * If an error occured while reading or writing, rbytes will
138187117650Saalok 	 * be negative
138287117650Saalok 	 */
138387117650Saalok 	if (rbytes < 0) {
138487117650Saalok 		(void) unlink(tmpfilename);
138587117650Saalok 		die(gettext("could not read from %s"), filename);
138687117650Saalok 	}
138787117650Saalok 
138887117650Saalok 	/* Rename the temp file to the actual file */
138987117650Saalok 	if (rename(tmpfilename, filename) == -1)
139087117650Saalok 		(void) unlink(tmpfilename);
139187117650Saalok }
139287117650Saalok 
139387117650Saalok /*
139487117650Saalok  * Compress a file
139587117650Saalok  */
139687117650Saalok static void
13979525893dSaalok lofi_compress(int *lfd, const char *filename, int compress_index,
139887117650Saalok     uint32_t segsize)
139987117650Saalok {
140087117650Saalok 	struct lofi_ioctl lic;
140187117650Saalok 	lofi_compress_info_t *li;
14029525893dSaalok 	struct flock lock;
140387117650Saalok 	char tmpfilename[MAXPATHLEN];
140487117650Saalok 	char comp_filename[MAXPATHLEN];
140587117650Saalok 	char algorithm[MAXALGLEN];
14067d82f0f8SDina K Nimeh 	char *x;
140787117650Saalok 	char *dir = NULL, *file = NULL;
140887117650Saalok 	uchar_t *uncompressed_seg = NULL;
140987117650Saalok 	uchar_t *compressed_seg = NULL;
141087117650Saalok 	uint32_t compressed_segsize;
141187117650Saalok 	uint32_t len_compressed, count;
141287117650Saalok 	uint32_t index_entries, index_sz;
141387117650Saalok 	uint64_t *index = NULL;
141487117650Saalok 	uint64_t offset;
141587117650Saalok 	size_t real_segsize;
141687117650Saalok 	struct stat64 statbuf;
141787117650Saalok 	int compfd = -1, uncompfd = -1;
141887117650Saalok 	int tfd = -1;
141987117650Saalok 	ssize_t rbytes, wbytes, lastread;
142087117650Saalok 	int i, type;
142187117650Saalok 
142287117650Saalok 	/*
142387117650Saalok 	 * Disallow compressing the file if it is
142487117650Saalok 	 * already mapped
142587117650Saalok 	 */
142687117650Saalok 	lic.li_minor = 0;
142787117650Saalok 	(void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
14289525893dSaalok 	if (ioctl(*lfd, LOFI_GET_MINOR, &lic) != -1)
142987117650Saalok 		die(gettext("%s must be unmapped before compressing"),
143087117650Saalok 		    filename);
143187117650Saalok 
14329525893dSaalok 	/*
14339525893dSaalok 	 * Close the control device so other operations
14349525893dSaalok 	 * can use it
14359525893dSaalok 	 */
14369525893dSaalok 	(void) close(*lfd);
14379525893dSaalok 	*lfd = -1;
14389525893dSaalok 
143987117650Saalok 	li = &lofi_compress_table[compress_index];
144087117650Saalok 
144187117650Saalok 	/*
144287117650Saalok 	 * The size of the buffer to hold compressed data must
144387117650Saalok 	 * be slightly larger than the compressed segment size.
144487117650Saalok 	 *
144587117650Saalok 	 * The compress functions use part of the buffer as
144687117650Saalok 	 * scratch space to do calculations.
144787117650Saalok 	 * Ref: http://www.zlib.net/manual.html#compress2
144887117650Saalok 	 */
144987117650Saalok 	compressed_segsize = segsize + (segsize >> 6);
145087117650Saalok 	compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
145187117650Saalok 	uncompressed_seg = (uchar_t *)malloc(segsize);
145287117650Saalok 
145387117650Saalok 	if (compressed_seg == NULL || uncompressed_seg == NULL)
145487117650Saalok 		die(gettext("No memory"));
145587117650Saalok 
14569525893dSaalok 	if ((uncompfd = open64(filename, O_RDWR|O_LARGEFILE, 0)) == -1)
145787117650Saalok 		die(gettext("open: %s"), filename);
145887117650Saalok 
14599525893dSaalok 	lock.l_type = F_WRLCK;
14609525893dSaalok 	lock.l_whence = SEEK_SET;
14619525893dSaalok 	lock.l_start = 0;
14629525893dSaalok 	lock.l_len = 0;
14639525893dSaalok 
14649525893dSaalok 	/*
14659525893dSaalok 	 * Use an advisory lock to ensure that only a
14669525893dSaalok 	 * single lofiadm process compresses a given
14679525893dSaalok 	 * file at any given time
14689525893dSaalok 	 *
14699525893dSaalok 	 * A close on the file descriptor automatically
14709525893dSaalok 	 * closes all lock state on the file
14719525893dSaalok 	 */
14729525893dSaalok 	if (fcntl(uncompfd, F_SETLKW, &lock) == -1)
14739525893dSaalok 		die(gettext("fcntl: %s"), filename);
14749525893dSaalok 
147587117650Saalok 	if (fstat64(uncompfd, &statbuf) == -1) {
147687117650Saalok 		(void) close(uncompfd);
147787117650Saalok 		die(gettext("fstat: %s"), filename);
147887117650Saalok 	}
147987117650Saalok 
148087117650Saalok 	/* Zero length files don't need to be compressed */
148187117650Saalok 	if (statbuf.st_size == 0) {
148287117650Saalok 		(void) close(uncompfd);
148387117650Saalok 		return;
148487117650Saalok 	}
148587117650Saalok 
148687117650Saalok 	/*
148787117650Saalok 	 * Create temporary files in the same directory that
148887117650Saalok 	 * will hold the intermediate data
148987117650Saalok 	 */
14907d82f0f8SDina K Nimeh 	x = strdup(filename);
14917d82f0f8SDina K Nimeh 	dir = strdup(dirname(x));
14927d82f0f8SDina K Nimeh 	free(x);
14937d82f0f8SDina K Nimeh 	x = strdup(filename);
14947d82f0f8SDina K Nimeh 	file = strdup(basename(x));
14957d82f0f8SDina K Nimeh 	free(x);
149687117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
149787117650Saalok 	    "%s/.%sXXXXXX", dir, file);
149887117650Saalok 	(void) snprintf(comp_filename, sizeof (comp_filename),
149987117650Saalok 	    "%s/.%sXXXXXX", dir, file);
15007d82f0f8SDina K Nimeh 	free(dir);
15017d82f0f8SDina K Nimeh 	free(file);
150287117650Saalok 
150387117650Saalok 	if ((tfd = mkstemp64(tmpfilename)) == -1)
150487117650Saalok 		goto cleanup;
150587117650Saalok 
150687117650Saalok 	if ((compfd = mkstemp64(comp_filename)) == -1)
150787117650Saalok 		goto cleanup;
150887117650Saalok 
150987117650Saalok 	/*
151087117650Saalok 	 * Set the mode bits and owner of the compressed
151187117650Saalok 	 * file to be that of the original uncompressed file
151287117650Saalok 	 */
151387117650Saalok 	(void) fchmod(compfd, statbuf.st_mode);
151487117650Saalok 
151587117650Saalok 	if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
151687117650Saalok 		goto cleanup;
151787117650Saalok 
151887117650Saalok 	/*
151987117650Saalok 	 * Calculate the number of index entries required.
152087117650Saalok 	 * index entries are stored as an array. adding
152187117650Saalok 	 * a '2' here accounts for the fact that the last
152287117650Saalok 	 * segment may not be a multiple of the segment size
152387117650Saalok 	 */
152487117650Saalok 	index_sz = (statbuf.st_size / segsize) + 2;
152587117650Saalok 	index = malloc(sizeof (*index) * index_sz);
152687117650Saalok 
152787117650Saalok 	if (index == NULL)
152887117650Saalok 		goto cleanup;
152987117650Saalok 
153087117650Saalok 	offset = 0;
153187117650Saalok 	lastread = segsize;
153287117650Saalok 	count = 0;
153387117650Saalok 
153487117650Saalok 	/*
153587117650Saalok 	 * Now read from the uncompressed file in 'segsize'
153687117650Saalok 	 * sized chunks, compress what was read in and
153787117650Saalok 	 * write it out to a temporary file
153887117650Saalok 	 */
153987117650Saalok 	for (;;) {
154087117650Saalok 		rbytes = read(uncompfd, uncompressed_seg, segsize);
154187117650Saalok 
154287117650Saalok 		if (rbytes <= 0)
154387117650Saalok 			break;
154487117650Saalok 
154587117650Saalok 		if (lastread < segsize)
154687117650Saalok 			goto cleanup;
154787117650Saalok 
154887117650Saalok 		/*
154987117650Saalok 		 * Account for the first byte that
155087117650Saalok 		 * indicates whether a segment is
155187117650Saalok 		 * compressed or not
155287117650Saalok 		 */
155387117650Saalok 		real_segsize = segsize - 1;
155487117650Saalok 		(void) li->l_compress(uncompressed_seg, rbytes,
155587117650Saalok 		    compressed_seg + SEGHDR, &real_segsize, li->l_level);
155687117650Saalok 
155787117650Saalok 		/*
155887117650Saalok 		 * If the length of the compressed data is more
155987117650Saalok 		 * than a threshold then there isn't any benefit
156087117650Saalok 		 * to be had from compressing this segment - leave
156187117650Saalok 		 * it uncompressed.
156287117650Saalok 		 *
156387117650Saalok 		 * NB. In case an error occurs during compression (above)
156487117650Saalok 		 * the 'real_segsize' isn't changed. The logic below
156587117650Saalok 		 * ensures that that segment is left uncompressed.
156687117650Saalok 		 */
156787117650Saalok 		len_compressed = real_segsize;
15684058a205Sjrgn.keil@googlemail.com 		if (segsize <= COMPRESS_THRESHOLD ||
15694058a205Sjrgn.keil@googlemail.com 		    real_segsize > (segsize - COMPRESS_THRESHOLD)) {
157087117650Saalok 			(void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
157187117650Saalok 			    rbytes);
157287117650Saalok 			type = UNCOMPRESSED;
157387117650Saalok 			len_compressed = rbytes;
157487117650Saalok 		} else {
157587117650Saalok 			type = COMPRESSED;
157687117650Saalok 		}
157787117650Saalok 
157887117650Saalok 		/*
157987117650Saalok 		 * Set the first byte or the SEGHDR to
158087117650Saalok 		 * indicate if it's compressed or not
158187117650Saalok 		 */
158287117650Saalok 		*compressed_seg = type;
158387117650Saalok 		wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
158487117650Saalok 		if (wbytes != (len_compressed + SEGHDR)) {
158587117650Saalok 			rbytes = -1;
158687117650Saalok 			break;
158787117650Saalok 		}
158887117650Saalok 
158987117650Saalok 		index[count] = BE_64(offset);
159087117650Saalok 		offset += wbytes;
159187117650Saalok 		lastread = rbytes;
159287117650Saalok 		count++;
159387117650Saalok 	}
159487117650Saalok 
159587117650Saalok 	(void) close(uncompfd);
159687117650Saalok 
159787117650Saalok 	if (rbytes < 0)
159887117650Saalok 		goto cleanup;
159987117650Saalok 	/*
160087117650Saalok 	 * The last index entry is a sentinel entry. It does not point to
160187117650Saalok 	 * an actual compressed segment but helps in computing the size of
160287117650Saalok 	 * the compressed segment. The size of each compressed segment is
160387117650Saalok 	 * computed by subtracting the current index value from the next
160487117650Saalok 	 * one (the compressed blocks are stored sequentially)
160587117650Saalok 	 */
160687117650Saalok 	index[count++] = BE_64(offset);
160787117650Saalok 
160887117650Saalok 	/*
160987117650Saalok 	 * Now write the compressed data along with the
161087117650Saalok 	 * header information to this file which will
161187117650Saalok 	 * later be renamed to the original uncompressed
161287117650Saalok 	 * file name
161387117650Saalok 	 *
161487117650Saalok 	 * The header is as follows -
161587117650Saalok 	 *
161687117650Saalok 	 * Signature (name of the compression algorithm)
161787117650Saalok 	 * Compression segment size (a multiple of 512)
161887117650Saalok 	 * Number of index entries
161987117650Saalok 	 * Size of the last block
162087117650Saalok 	 * The array containing the index entries
162187117650Saalok 	 *
162287117650Saalok 	 * the header is always stored in network byte
162387117650Saalok 	 * order
162487117650Saalok 	 */
162587117650Saalok 	(void) bzero(algorithm, sizeof (algorithm));
162687117650Saalok 	(void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
162787117650Saalok 	if (write(compfd, algorithm, sizeof (algorithm))
162887117650Saalok 	    != sizeof (algorithm))
162987117650Saalok 		goto cleanup;
163087117650Saalok 
163187117650Saalok 	segsize = htonl(segsize);
163287117650Saalok 	if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
163387117650Saalok 		goto cleanup;
163487117650Saalok 
163587117650Saalok 	index_entries = htonl(count);
163687117650Saalok 	if (write(compfd, &index_entries, sizeof (index_entries)) !=
163787117650Saalok 	    sizeof (index_entries))
163887117650Saalok 		goto cleanup;
163987117650Saalok 
164087117650Saalok 	lastread = htonl(lastread);
164187117650Saalok 	if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
164287117650Saalok 		goto cleanup;
164387117650Saalok 
164487117650Saalok 	for (i = 0; i < count; i++) {
164587117650Saalok 		if (write(compfd, index + i, sizeof (*index)) !=
164687117650Saalok 		    sizeof (*index))
164787117650Saalok 			goto cleanup;
164887117650Saalok 	}
164987117650Saalok 
165087117650Saalok 	/* Header is written, now write the compressed data */
165187117650Saalok 	if (lseek(tfd, 0, SEEK_SET) != 0)
165287117650Saalok 		goto cleanup;
165387117650Saalok 
165487117650Saalok 	rbytes = wbytes = 0;
165587117650Saalok 
165687117650Saalok 	for (;;) {
165787117650Saalok 		rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
165887117650Saalok 
165987117650Saalok 		if (rbytes <= 0)
166087117650Saalok 			break;
166187117650Saalok 
166287117650Saalok 		if (write(compfd, compressed_seg, rbytes) != rbytes)
166387117650Saalok 			goto cleanup;
166487117650Saalok 	}
166587117650Saalok 
166687117650Saalok 	if (fstat64(compfd, &statbuf) == -1)
166787117650Saalok 		goto cleanup;
166887117650Saalok 
166987117650Saalok 	/*
167087117650Saalok 	 * Round up the compressed file size to be a multiple of
167187117650Saalok 	 * DEV_BSIZE. lofi(7D) likes it that way.
167287117650Saalok 	 */
167387117650Saalok 	if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
167487117650Saalok 
167587117650Saalok 		offset = DEV_BSIZE - offset;
167687117650Saalok 
167787117650Saalok 		for (i = 0; i < offset; i++)
167887117650Saalok 			uncompressed_seg[i] = '\0';
167987117650Saalok 		if (write(compfd, uncompressed_seg, offset) != offset)
168087117650Saalok 			goto cleanup;
168187117650Saalok 	}
168287117650Saalok 	(void) close(compfd);
168387117650Saalok 	(void) close(tfd);
168487117650Saalok 	(void) unlink(tmpfilename);
168587117650Saalok cleanup:
168687117650Saalok 	if (rbytes < 0) {
168787117650Saalok 		if (tfd != -1)
168887117650Saalok 			(void) unlink(tmpfilename);
168987117650Saalok 		if (compfd != -1)
169087117650Saalok 			(void) unlink(comp_filename);
169187117650Saalok 		die(gettext("error compressing file %s"), filename);
169287117650Saalok 	} else {
169387117650Saalok 		/* Rename the compressed file to the actual file */
169487117650Saalok 		if (rename(comp_filename, filename) == -1) {
169587117650Saalok 			(void) unlink(comp_filename);
169687117650Saalok 			die(gettext("error compressing file %s"), filename);
169787117650Saalok 		}
169887117650Saalok 	}
169987117650Saalok 	if (compressed_seg != NULL)
170087117650Saalok 		free(compressed_seg);
170187117650Saalok 	if (uncompressed_seg != NULL)
170287117650Saalok 		free(uncompressed_seg);
170387117650Saalok 	if (index != NULL)
170487117650Saalok 		free(index);
170587117650Saalok 	if (compfd != -1)
170687117650Saalok 		(void) close(compfd);
170787117650Saalok 	if (uncompfd != -1)
170887117650Saalok 		(void) close(uncompfd);
170987117650Saalok 	if (tfd != -1)
171087117650Saalok 		(void) close(tfd);
171187117650Saalok }
171287117650Saalok 
171387117650Saalok static int
171487117650Saalok lofi_compress_select(const char *algname)
171587117650Saalok {
171687117650Saalok 	int i;
171787117650Saalok 
171887117650Saalok 	for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
171987117650Saalok 		if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
172087117650Saalok 			return (i);
172187117650Saalok 	}
172287117650Saalok 	return (-1);
172387117650Saalok }
172487117650Saalok 
172587117650Saalok static void
172687117650Saalok check_algorithm_validity(const char *algname, int *compress_index)
172787117650Saalok {
172887117650Saalok 	*compress_index = lofi_compress_select(algname);
172987117650Saalok 	if (*compress_index < 0)
173087117650Saalok 		die(gettext("invalid algorithm name: %s\n"), algname);
173187117650Saalok }
173287117650Saalok 
173387117650Saalok static void
173487117650Saalok check_file_validity(const char *filename)
173587117650Saalok {
17367c478bd9Sstevel@tonic-gate 	struct stat64 buf;
173787117650Saalok 	int 	error;
17387d82f0f8SDina K Nimeh 	int	fd;
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 	fd = open64(filename, O_RDONLY);
17417c478bd9Sstevel@tonic-gate 	if (fd == -1) {
17427c478bd9Sstevel@tonic-gate 		die(gettext("open: %s"), filename);
17437c478bd9Sstevel@tonic-gate 	}
17447c478bd9Sstevel@tonic-gate 	error = fstat64(fd, &buf);
17457c478bd9Sstevel@tonic-gate 	if (error == -1) {
17467c478bd9Sstevel@tonic-gate 		die(gettext("fstat: %s"), filename);
17477c478bd9Sstevel@tonic-gate 	} else if (!S_ISLOFIABLE(buf.st_mode)) {
17487c478bd9Sstevel@tonic-gate 		die(gettext("%s is not a regular file, "
17497c478bd9Sstevel@tonic-gate 		    "block, or character device\n"),
17507c478bd9Sstevel@tonic-gate 		    filename);
17517c478bd9Sstevel@tonic-gate 	} else if ((buf.st_size % DEV_BSIZE) != 0) {
1752f9153c6bSDina K Nimeh 		die(gettext("size of %s is not a multiple of %d\n"),
17537c478bd9Sstevel@tonic-gate 		    filename, DEV_BSIZE);
17547c478bd9Sstevel@tonic-gate 	}
17557c478bd9Sstevel@tonic-gate 	(void) close(fd);
175687117650Saalok 
175787117650Saalok 	if (name_to_minor(filename) != 0) {
1758f9153c6bSDina K Nimeh 		die(gettext("cannot use %s on itself\n"), LOFI_DRIVER_NAME);
17597c478bd9Sstevel@tonic-gate 	}
176087117650Saalok }
176187117650Saalok 
176287117650Saalok static uint32_t
176387117650Saalok convert_to_num(const char *str)
176487117650Saalok {
176587117650Saalok 	int len;
176687117650Saalok 	uint32_t segsize, mult = 1;
176787117650Saalok 
176887117650Saalok 	len = strlen(str);
176987117650Saalok 	if (len && isalpha(str[len - 1])) {
177087117650Saalok 		switch (str[len - 1]) {
177187117650Saalok 		case 'k':
177287117650Saalok 		case 'K':
177387117650Saalok 			mult = KILOBYTE;
177487117650Saalok 			break;
177587117650Saalok 		case 'b':
177687117650Saalok 		case 'B':
177787117650Saalok 			mult = BLOCK_SIZE;
177887117650Saalok 			break;
177987117650Saalok 		case 'm':
178087117650Saalok 		case 'M':
178187117650Saalok 			mult = MEGABYTE;
178287117650Saalok 			break;
178387117650Saalok 		case 'g':
178487117650Saalok 		case 'G':
178587117650Saalok 			mult = GIGABYTE;
178687117650Saalok 			break;
178787117650Saalok 		default:
178887117650Saalok 			die(gettext("invalid segment size %s\n"), str);
178987117650Saalok 		}
179087117650Saalok 	}
179187117650Saalok 
179287117650Saalok 	segsize = atol(str);
179387117650Saalok 	segsize *= mult;
179487117650Saalok 
179587117650Saalok 	return (segsize);
179687117650Saalok }
179787117650Saalok 
179887117650Saalok int
179987117650Saalok main(int argc, char *argv[])
180087117650Saalok {
180187117650Saalok 	int	lfd;
180287117650Saalok 	int	c;
180387117650Saalok 	const char *devicename = NULL;
180487117650Saalok 	const char *filename = NULL;
180587117650Saalok 	const char *algname = COMPRESS_ALGORITHM;
180687117650Saalok 	int	openflag;
180787117650Saalok 	int	minor;
180887117650Saalok 	int 	compress_index;
180987117650Saalok 	uint32_t segsize = SEGSIZE;
181087117650Saalok 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
181187117650Saalok 	boolean_t force = B_FALSE;
18127d82f0f8SDina K Nimeh 	const char *pname;
18137d82f0f8SDina K Nimeh 	boolean_t errflag = B_FALSE;
18147d82f0f8SDina K Nimeh 	boolean_t addflag = B_FALSE;
1815cd69fabeSAlexander Eremin 	boolean_t rdflag = B_FALSE;
18167d82f0f8SDina K Nimeh 	boolean_t deleteflag = B_FALSE;
18177d82f0f8SDina K Nimeh 	boolean_t ephflag = B_FALSE;
18187d82f0f8SDina K Nimeh 	boolean_t compressflag = B_FALSE;
18197d82f0f8SDina K Nimeh 	boolean_t uncompressflag = B_FALSE;
18207d82f0f8SDina K Nimeh 	/* the next two work together for -c, -k, -T, -e options only */
18217d82f0f8SDina K Nimeh 	boolean_t need_crypto = B_FALSE;	/* if any -c, -k, -T, -e */
18227d82f0f8SDina K Nimeh 	boolean_t cipher_only = B_TRUE;		/* if -c only */
18237d82f0f8SDina K Nimeh 	const char *keyfile = NULL;
18247d82f0f8SDina K Nimeh 	mech_alias_t *cipher = NULL;
18257d82f0f8SDina K Nimeh 	token_spec_t *token = NULL;
18267d82f0f8SDina K Nimeh 	char	*rkey = NULL;
18277d82f0f8SDina K Nimeh 	size_t	rksz = 0;
1828f9153c6bSDina K Nimeh 	char realfilename[MAXPATHLEN];
182987117650Saalok 
183087117650Saalok 	pname = getpname(argv[0]);
183187117650Saalok 
183287117650Saalok 	(void) setlocale(LC_ALL, "");
183387117650Saalok 	(void) textdomain(TEXT_DOMAIN);
183487117650Saalok 
1835cd69fabeSAlexander Eremin 	while ((c = getopt(argc, argv, "a:c:Cd:efk:o:rs:T:U")) != EOF) {
183687117650Saalok 		switch (c) {
183787117650Saalok 		case 'a':
18387d82f0f8SDina K Nimeh 			addflag = B_TRUE;
1839f9153c6bSDina K Nimeh 			if ((filename = realpath(optarg, realfilename)) == NULL)
1840f9153c6bSDina K Nimeh 				die("%s", optarg);
18417c478bd9Sstevel@tonic-gate 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
18427c478bd9Sstevel@tonic-gate 				/* optional device */
18437c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
18447c478bd9Sstevel@tonic-gate 				optind++;
18457c478bd9Sstevel@tonic-gate 			}
18467c478bd9Sstevel@tonic-gate 			break;
184787117650Saalok 		case 'C':
18487d82f0f8SDina K Nimeh 			compressflag = B_TRUE;
18497d82f0f8SDina K Nimeh 			if (((argc - optind) > 1) && (*argv[optind] != '-')) {
18507d82f0f8SDina K Nimeh 				/* optional algorithm */
18517d82f0f8SDina K Nimeh 				algname = argv[optind];
185287117650Saalok 				optind++;
185387117650Saalok 			}
185487117650Saalok 			check_algorithm_validity(algname, &compress_index);
185587117650Saalok 			break;
18567d82f0f8SDina K Nimeh 		case 'c':
18577d82f0f8SDina K Nimeh 			/* is the chosen cipher allowed? */
18587d82f0f8SDina K Nimeh 			if ((cipher = ciph2mech(optarg)) == NULL) {
18597d82f0f8SDina K Nimeh 				errflag = B_TRUE;
18607d82f0f8SDina K Nimeh 				warn(gettext("cipher %s not allowed\n"),
18617d82f0f8SDina K Nimeh 				    optarg);
18627d82f0f8SDina K Nimeh 			}
18637d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
18647d82f0f8SDina K Nimeh 			/* cipher_only is already set */
18657d82f0f8SDina K Nimeh 			break;
18667c478bd9Sstevel@tonic-gate 		case 'd':
18677d82f0f8SDina K Nimeh 			deleteflag = B_TRUE;
18687c478bd9Sstevel@tonic-gate 			minor = name_to_minor(optarg);
18697c478bd9Sstevel@tonic-gate 			if (minor != 0)
18707c478bd9Sstevel@tonic-gate 				devicename = optarg;
1871f9153c6bSDina K Nimeh 			else {
1872f9153c6bSDina K Nimeh 				if ((filename = realpath(optarg,
1873f9153c6bSDina K Nimeh 				    realfilename)) == NULL)
1874f9153c6bSDina K Nimeh 					die("%s", optarg);
1875f9153c6bSDina K Nimeh 			}
18767c478bd9Sstevel@tonic-gate 			break;
18777d82f0f8SDina K Nimeh 		case 'e':
18787d82f0f8SDina K Nimeh 			ephflag = B_TRUE;
18797d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
18807d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
18817d82f0f8SDina K Nimeh 			break;
18823d7072f8Seschrock 		case 'f':
18833d7072f8Seschrock 			force = B_TRUE;
18843d7072f8Seschrock 			break;
18857d82f0f8SDina K Nimeh 		case 'k':
18867d82f0f8SDina K Nimeh 			keyfile = optarg;
18877d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
18887d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
18897d82f0f8SDina K Nimeh 			break;
1890cd69fabeSAlexander Eremin 		case 'r':
1891cd69fabeSAlexander Eremin 			rdflag = B_TRUE;
1892cd69fabeSAlexander Eremin 			break;
189387117650Saalok 		case 's':
189487117650Saalok 			segsize = convert_to_num(optarg);
18954058a205Sjrgn.keil@googlemail.com 			if (segsize < DEV_BSIZE || !ISP2(segsize))
189687117650Saalok 				die(gettext("segment size %s is invalid "
189787117650Saalok 				    "or not a multiple of minimum block "
189887117650Saalok 				    "size %ld\n"), optarg, DEV_BSIZE);
18997d82f0f8SDina K Nimeh 			break;
19007d82f0f8SDina K Nimeh 		case 'T':
19017d82f0f8SDina K Nimeh 			if ((token = parsetoken(optarg)) == NULL) {
19027d82f0f8SDina K Nimeh 				errflag = B_TRUE;
19037d82f0f8SDina K Nimeh 				warn(
19047d82f0f8SDina K Nimeh 				    gettext("invalid token key specifier %s\n"),
19057d82f0f8SDina K Nimeh 				    optarg);
19067d82f0f8SDina K Nimeh 			}
19077d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
19087d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
190987117650Saalok 			break;
191087117650Saalok 		case 'U':
19117d82f0f8SDina K Nimeh 			uncompressflag = B_TRUE;
191287117650Saalok 			break;
19137c478bd9Sstevel@tonic-gate 		case '?':
19147c478bd9Sstevel@tonic-gate 		default:
19157d82f0f8SDina K Nimeh 			errflag = B_TRUE;
19167c478bd9Sstevel@tonic-gate 			break;
19177c478bd9Sstevel@tonic-gate 		}
19187c478bd9Sstevel@tonic-gate 	}
19197d82f0f8SDina K Nimeh 
19207d82f0f8SDina K Nimeh 	/* Check for mutually exclusive combinations of options */
192187117650Saalok 	if (errflag ||
192287117650Saalok 	    (addflag && deleteflag) ||
1923cd69fabeSAlexander Eremin 	    (rdflag && !addflag) ||
19247d82f0f8SDina K Nimeh 	    (!addflag && need_crypto) ||
192587117650Saalok 	    ((compressflag || uncompressflag) && (addflag || deleteflag)))
19267d82f0f8SDina K Nimeh 		usage(pname);
19277d82f0f8SDina K Nimeh 
19287d82f0f8SDina K Nimeh 	/* ephemeral key, and key from either file or token are incompatible */
19297d82f0f8SDina K Nimeh 	if (ephflag && (keyfile != NULL || token != NULL)) {
19307d82f0f8SDina K Nimeh 		die(gettext("ephemeral key cannot be used with keyfile"
19317d82f0f8SDina K Nimeh 		    " or token key\n"));
19327d82f0f8SDina K Nimeh 	}
19337d82f0f8SDina K Nimeh 
19347d82f0f8SDina K Nimeh 	/*
19357d82f0f8SDina K Nimeh 	 * "-c" but no "-k", "-T", "-e", or "-T -k" means derive key from
19367d82f0f8SDina K Nimeh 	 * command line passphrase
19377d82f0f8SDina K Nimeh 	 */
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 	switch (argc - optind) {
19407c478bd9Sstevel@tonic-gate 	case 0: /* no more args */
19417d82f0f8SDina K Nimeh 		if (compressflag || uncompressflag)	/* needs filename */
19427d82f0f8SDina K Nimeh 			usage(pname);
19437c478bd9Sstevel@tonic-gate 		break;
19447d82f0f8SDina K Nimeh 	case 1:
19457c478bd9Sstevel@tonic-gate 		if (addflag || deleteflag)
19467d82f0f8SDina K Nimeh 			usage(pname);
19477d82f0f8SDina K Nimeh 		/* one arg means compress/uncompress the file ... */
19487d82f0f8SDina K Nimeh 		if (compressflag || uncompressflag) {
19497d82f0f8SDina K Nimeh 			if ((filename = realpath(argv[optind],
19507d82f0f8SDina K Nimeh 			    realfilename)) == NULL)
19517d82f0f8SDina K Nimeh 				die("%s", argv[optind]);
19527d82f0f8SDina K Nimeh 		/* ... or without options means print the association */
19537d82f0f8SDina K Nimeh 		} else {
19547c478bd9Sstevel@tonic-gate 			minor = name_to_minor(argv[optind]);
19557c478bd9Sstevel@tonic-gate 			if (minor != 0)
19567c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
1957f9153c6bSDina K Nimeh 			else {
1958f9153c6bSDina K Nimeh 				if ((filename = realpath(argv[optind],
1959f9153c6bSDina K Nimeh 				    realfilename)) == NULL)
1960f9153c6bSDina K Nimeh 					die("%s", argv[optind]);
1961f9153c6bSDina K Nimeh 			}
19627d82f0f8SDina K Nimeh 		}
19637c478bd9Sstevel@tonic-gate 		break;
19647c478bd9Sstevel@tonic-gate 	default:
19657d82f0f8SDina K Nimeh 		usage(pname);
19667c478bd9Sstevel@tonic-gate 		break;
19677c478bd9Sstevel@tonic-gate 	}
19687c478bd9Sstevel@tonic-gate 
19697d82f0f8SDina K Nimeh 	if (addflag || compressflag || uncompressflag)
19707d82f0f8SDina K Nimeh 		check_file_validity(filename);
19717d82f0f8SDina K Nimeh 
19727c478bd9Sstevel@tonic-gate 	if (filename && !valid_abspath(filename))
19737c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate 	/*
19767c478bd9Sstevel@tonic-gate 	 * Here, we know the arguments are correct, the filename is an
19777c478bd9Sstevel@tonic-gate 	 * absolute path, it exists and is a regular file. We don't yet
19787c478bd9Sstevel@tonic-gate 	 * know that the device name is ok or not.
19797c478bd9Sstevel@tonic-gate 	 */
19807d82f0f8SDina K Nimeh 
19817c478bd9Sstevel@tonic-gate 	openflag = O_EXCL;
198287117650Saalok 	if (addflag || deleteflag || compressflag || uncompressflag)
19837c478bd9Sstevel@tonic-gate 		openflag |= O_RDWR;
19847c478bd9Sstevel@tonic-gate 	else
19857c478bd9Sstevel@tonic-gate 		openflag |= O_RDONLY;
19867c478bd9Sstevel@tonic-gate 	lfd = open(lofictl, openflag);
19877c478bd9Sstevel@tonic-gate 	if (lfd == -1) {
19887c478bd9Sstevel@tonic-gate 		if ((errno == EPERM) || (errno == EACCES)) {
1989f9153c6bSDina K Nimeh 			die(gettext("you do not have permission to perform "
1990f9153c6bSDina K Nimeh 			    "that operation.\n"));
19917c478bd9Sstevel@tonic-gate 		} else {
1992f9153c6bSDina K Nimeh 			die(gettext("open: %s"), lofictl);
19937c478bd9Sstevel@tonic-gate 		}
19947c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
19957c478bd9Sstevel@tonic-gate 	}
19967d82f0f8SDina K Nimeh 
19977d82f0f8SDina K Nimeh 	/*
19987d82f0f8SDina K Nimeh 	 * No passphrase is needed for ephemeral key, or when key is
19997d82f0f8SDina K Nimeh 	 * in a file and not wrapped by another key from a token.
20007d82f0f8SDina K Nimeh 	 * However, a passphrase is needed in these cases:
20017d82f0f8SDina K Nimeh 	 * 1. cipher with no ephemeral key, key file, or token,
20027d82f0f8SDina K Nimeh 	 *    in which case the passphrase is used to build the key
20037d82f0f8SDina K Nimeh 	 * 2. token with an optional cipher or optional key file,
20047d82f0f8SDina K Nimeh 	 *    in which case the passphrase unlocks the token
20057d82f0f8SDina K Nimeh 	 * If only the cipher is specified, reconfirm the passphrase
20067d82f0f8SDina K Nimeh 	 * to ensure the user hasn't mis-entered it.  Otherwise, the
20077d82f0f8SDina K Nimeh 	 * token will enforce the token passphrase.
20087d82f0f8SDina K Nimeh 	 */
20097d82f0f8SDina K Nimeh 	if (need_crypto) {
20107d82f0f8SDina K Nimeh 		CK_SESSION_HANDLE	sess;
20117d82f0f8SDina K Nimeh 
20127d82f0f8SDina K Nimeh 		/* pick a cipher if none specified */
20137d82f0f8SDina K Nimeh 		if (cipher == NULL)
20147d82f0f8SDina K Nimeh 			cipher = DEFAULT_CIPHER;
20157d82f0f8SDina K Nimeh 
20167d82f0f8SDina K Nimeh 		if (!kernel_cipher_check(cipher))
20177d82f0f8SDina K Nimeh 			die(gettext(
20187d82f0f8SDina K Nimeh 			    "use \"cryptoadm list -m\" to find available "
20197d82f0f8SDina K Nimeh 			    "mechanisms\n"));
20207d82f0f8SDina K Nimeh 
20217d82f0f8SDina K Nimeh 		init_crypto(token, cipher, &sess);
20227d82f0f8SDina K Nimeh 
20237d82f0f8SDina K Nimeh 		if (cipher_only) {
20247d82f0f8SDina K Nimeh 			getkeyfromuser(cipher, &rkey, &rksz);
20257d82f0f8SDina K Nimeh 		} else if (token != NULL) {
20267d82f0f8SDina K Nimeh 			getkeyfromtoken(sess, token, keyfile, cipher,
20277d82f0f8SDina K Nimeh 			    &rkey, &rksz);
20287d82f0f8SDina K Nimeh 		} else {
20297d82f0f8SDina K Nimeh 			/* this also handles ephemeral keys */
20307d82f0f8SDina K Nimeh 			getkeyfromfile(keyfile, cipher, &rkey, &rksz);
20317d82f0f8SDina K Nimeh 		}
20327d82f0f8SDina K Nimeh 
20337d82f0f8SDina K Nimeh 		end_crypto(sess);
20347d82f0f8SDina K Nimeh 	}
20357d82f0f8SDina K Nimeh 
20367d82f0f8SDina K Nimeh 	/*
20377d82f0f8SDina K Nimeh 	 * Now to the real work.
20387d82f0f8SDina K Nimeh 	 */
20397c478bd9Sstevel@tonic-gate 	if (addflag)
2040cd69fabeSAlexander Eremin 		add_mapping(lfd, devicename, filename, cipher, rkey, rksz,
2041cd69fabeSAlexander Eremin 		    rdflag);
204287117650Saalok 	else if (compressflag)
20439525893dSaalok 		lofi_compress(&lfd, filename, compress_index, segsize);
204487117650Saalok 	else if (uncompressflag)
204587117650Saalok 		lofi_uncompress(lfd, filename);
20467c478bd9Sstevel@tonic-gate 	else if (deleteflag)
20473d7072f8Seschrock 		delete_mapping(lfd, devicename, filename, force);
20487c478bd9Sstevel@tonic-gate 	else if (filename || devicename)
20497c478bd9Sstevel@tonic-gate 		print_one_mapping(lfd, devicename, filename);
20507c478bd9Sstevel@tonic-gate 	else
20517c478bd9Sstevel@tonic-gate 		print_mappings(lfd);
2052579df0adSaalok 
20539525893dSaalok 	if (lfd != -1)
20547c478bd9Sstevel@tonic-gate 		(void) close(lfd);
2055579df0adSaalok 	closelib();
20567c478bd9Sstevel@tonic-gate 	return (E_SUCCESS);
20577c478bd9Sstevel@tonic-gate }
2058