xref: /titanic_51/usr/src/cmd/lofiadm/main.c (revision a29e56d91db891741f1af9f6bbd3e3c3cac5f19b)
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.
276e096e64SGary Mills  * Copyright (c) 2014 Gary Mills
284549a1bdSAndrey Sokolov  * Copyright (c) 2016 Andrey Sokolov
293d7072f8Seschrock  */
303d7072f8Seschrock 
313d7072f8Seschrock /*
327c478bd9Sstevel@tonic-gate  * lofiadm - administer lofi(7d). Very simple, add and remove file<->device
337c478bd9Sstevel@tonic-gate  * associations, and display status. All the ioctls are private between
347c478bd9Sstevel@tonic-gate  * lofi and lofiadm, and so are very simple - device information is
357c478bd9Sstevel@tonic-gate  * communicated via a minor number.
367c478bd9Sstevel@tonic-gate  */
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include <sys/types.h>
397c478bd9Sstevel@tonic-gate #include <sys/param.h>
407c478bd9Sstevel@tonic-gate #include <sys/lofi.h>
417c478bd9Sstevel@tonic-gate #include <sys/stat.h>
424058a205Sjrgn.keil@googlemail.com #include <sys/sysmacros.h>
4387117650Saalok #include <netinet/in.h>
447c478bd9Sstevel@tonic-gate #include <stdio.h>
457c478bd9Sstevel@tonic-gate #include <fcntl.h>
467c478bd9Sstevel@tonic-gate #include <locale.h>
477c478bd9Sstevel@tonic-gate #include <string.h>
4887117650Saalok #include <strings.h>
497c478bd9Sstevel@tonic-gate #include <errno.h>
507c478bd9Sstevel@tonic-gate #include <stdlib.h>
517c478bd9Sstevel@tonic-gate #include <unistd.h>
527c478bd9Sstevel@tonic-gate #include <stropts.h>
531d8ed0f0Svikram #include <libdevinfo.h>
5487117650Saalok #include <libgen.h>
5587117650Saalok #include <ctype.h>
56579df0adSaalok #include <dlfcn.h>
577d82f0f8SDina K Nimeh #include <limits.h>
587d82f0f8SDina K Nimeh #include <security/cryptoki.h>
597d82f0f8SDina K Nimeh #include <cryptoutil.h>
607d82f0f8SDina K Nimeh #include <sys/crypto/ioctl.h>
617d82f0f8SDina K Nimeh #include <sys/crypto/ioctladmin.h>
62*a29e56d9SToomas Soome #include <sys/cmlb.h>
63*a29e56d9SToomas Soome #include <sys/mkdev.h>
647c478bd9Sstevel@tonic-gate #include "utils.h"
65b1efbcd6SAlok Aggarwal #include <LzmaEnc.h>
667c478bd9Sstevel@tonic-gate 
677d82f0f8SDina K Nimeh /* Only need the IV len #defines out of these files, nothing else. */
687d82f0f8SDina K Nimeh #include <aes/aes_impl.h>
697d82f0f8SDina K Nimeh #include <des/des_impl.h>
707d82f0f8SDina K Nimeh #include <blowfish/blowfish_impl.h>
717d82f0f8SDina K Nimeh 
727c478bd9Sstevel@tonic-gate static const char USAGE[] =
73*a29e56d9SToomas Soome 	"Usage: %s [-r] [-l] -a file [ device ]\n"
74795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -a file [device]\n"
75795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -k raw_key_file -a file [device]\n"
76795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -T [token]:[manuf]:[serial]:key "
77795f5a90SMohamed A. Khalfella 	"-a file [device]\n"
78795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -T [token]:[manuf]:[serial]:key "
79795f5a90SMohamed A. Khalfella 	"-k wrapped_key_file -a file [device]\n"
80795f5a90SMohamed A. Khalfella 	"       %s [-r] -c crypto_algorithm -e -a file [device]\n"
817c478bd9Sstevel@tonic-gate 	"       %s -d file | device\n"
82b1efbcd6SAlok Aggarwal 	"       %s -C [gzip|gzip-6|gzip-9|lzma] [-s segment_size] file\n"
8387117650Saalok 	"       %s -U file\n"
84f9153c6bSDina K Nimeh 	"       %s [ file | device ]\n";
857c478bd9Sstevel@tonic-gate 
867d82f0f8SDina K Nimeh typedef struct token_spec {
877d82f0f8SDina K Nimeh 	char	*name;
887d82f0f8SDina K Nimeh 	char	*mfr;
897d82f0f8SDina K Nimeh 	char	*serno;
907d82f0f8SDina K Nimeh 	char	*key;
917d82f0f8SDina K Nimeh } token_spec_t;
927d82f0f8SDina K Nimeh 
937d82f0f8SDina K Nimeh typedef struct mech_alias {
947d82f0f8SDina K Nimeh 	char	*alias;
957d82f0f8SDina K Nimeh 	CK_MECHANISM_TYPE type;
967d82f0f8SDina K Nimeh 	char	*name;		/* for ioctl */
977d82f0f8SDina K Nimeh 	char	*iv_name;	/* for ioctl */
987d82f0f8SDina K Nimeh 	size_t	iv_len;		/* for ioctl */
997d82f0f8SDina K Nimeh 	iv_method_t iv_type;	/* for ioctl */
1007d82f0f8SDina K Nimeh 	size_t	min_keysize;	/* in bytes */
1017d82f0f8SDina K Nimeh 	size_t	max_keysize;	/* in bytes */
1027d82f0f8SDina K Nimeh 	token_spec_t *token;
1037d82f0f8SDina K Nimeh 	CK_SLOT_ID slot;
1047d82f0f8SDina K Nimeh } mech_alias_t;
1057d82f0f8SDina K Nimeh 
1067d82f0f8SDina K Nimeh static mech_alias_t mech_aliases[] = {
1077d82f0f8SDina K Nimeh 	/* Preferred one should always be listed first. */
1087d82f0f8SDina K Nimeh 	{ "aes-256-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
1097d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
1107d82f0f8SDina K Nimeh 	{ "aes-192-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
1117d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
1127d82f0f8SDina K Nimeh 	{ "aes-128-cbc", CKM_AES_CBC, "CKM_AES_CBC", "CKM_AES_ECB", AES_IV_LEN,
1137d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID) -1 },
1147d82f0f8SDina K Nimeh 	{ "des3-cbc", CKM_DES3_CBC, "CKM_DES3_CBC", "CKM_DES3_ECB", DES_IV_LEN,
1157d82f0f8SDina K Nimeh 	    IVM_ENC_BLKNO, ULONG_MAX, 0L, NULL, (CK_SLOT_ID)-1 },
1167d82f0f8SDina K Nimeh 	{ "blowfish-cbc", CKM_BLOWFISH_CBC, "CKM_BLOWFISH_CBC",
1177d82f0f8SDina K Nimeh 	    "CKM_BLOWFISH_ECB", BLOWFISH_IV_LEN, IVM_ENC_BLKNO, ULONG_MAX,
1187d82f0f8SDina K Nimeh 	    0L, NULL, (CK_SLOT_ID)-1 }
1197d82f0f8SDina K Nimeh 	/*
1207d82f0f8SDina K Nimeh 	 * A cipher without an iv requirement would look like this:
1217d82f0f8SDina K Nimeh 	 * { "aes-xex", CKM_AES_XEX, "CKM_AES_XEX", NULL, 0,
1227d82f0f8SDina K Nimeh 	 *    IVM_NONE, ULONG_MAX, 0L, NULL, (CK_SLOT_ID)-1 }
1237d82f0f8SDina K Nimeh 	 */
1247d82f0f8SDina K Nimeh };
1257d82f0f8SDina K Nimeh 
1267d82f0f8SDina K Nimeh int	mech_aliases_count = (sizeof (mech_aliases) / sizeof (mech_alias_t));
1277d82f0f8SDina K Nimeh 
1287d82f0f8SDina K Nimeh /* Preferred cipher, if one isn't specified on command line. */
1297d82f0f8SDina K Nimeh #define	DEFAULT_CIPHER	(&mech_aliases[0])
1307d82f0f8SDina K Nimeh 
1317d82f0f8SDina K Nimeh #define	DEFAULT_CIPHER_NUM	64	/* guess # kernel ciphers available */
1327d82f0f8SDina K Nimeh #define	DEFAULT_MECHINFO_NUM	16	/* guess # kernel mechs available */
1337d82f0f8SDina K Nimeh #define	MIN_PASSLEN		8	/* min acceptable passphrase size */
1347c478bd9Sstevel@tonic-gate 
13587117650Saalok static int gzip_compress(void *src, size_t srclen, void *dst,
13687117650Saalok 	size_t *destlen, int level);
137b1efbcd6SAlok Aggarwal static int lzma_compress(void *src, size_t srclen, void *dst,
138b1efbcd6SAlok Aggarwal 	size_t *destlen, int level);
13987117650Saalok 
14087117650Saalok lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
14187117650Saalok 	{NULL,  		gzip_compress,  6,	"gzip"}, /* default */
14287117650Saalok 	{NULL,			gzip_compress,	6,	"gzip-6"},
143b1efbcd6SAlok Aggarwal 	{NULL,			gzip_compress,	9, 	"gzip-9"},
144b1efbcd6SAlok Aggarwal 	{NULL,  		lzma_compress, 	0, 	"lzma"}
14587117650Saalok };
14687117650Saalok 
1477d82f0f8SDina K Nimeh /* For displaying lofi mappings */
14887117650Saalok #define	FORMAT 			"%-20s     %-30s	%s\n"
1497d82f0f8SDina K Nimeh 
15087117650Saalok #define	COMPRESS_ALGORITHM	"gzip"
15187117650Saalok #define	COMPRESS_THRESHOLD	2048
15287117650Saalok #define	SEGSIZE			131072
15387117650Saalok #define	BLOCK_SIZE		512
15487117650Saalok #define	KILOBYTE		1024
15587117650Saalok #define	MEGABYTE		(KILOBYTE * KILOBYTE)
15687117650Saalok #define	GIGABYTE		(KILOBYTE * MEGABYTE)
1576e096e64SGary Mills #define	LIBZ			"libz.so.1"
158579df0adSaalok 
159b1efbcd6SAlok Aggarwal static void
160b1efbcd6SAlok Aggarwal usage(const char *pname)
16187117650Saalok {
162b1efbcd6SAlok Aggarwal 	(void) fprintf(stderr, gettext(USAGE), pname, pname, pname,
163795f5a90SMohamed A. Khalfella 	    pname, pname, pname, pname, pname, pname, pname);
164b1efbcd6SAlok Aggarwal 	exit(E_USAGE);
165b1efbcd6SAlok Aggarwal }
166b1efbcd6SAlok Aggarwal 
167b1efbcd6SAlok Aggarwal static int
168b1efbcd6SAlok Aggarwal gzip_compress(void *src, size_t srclen, void *dst, size_t *dstlen, int level)
169b1efbcd6SAlok Aggarwal {
170b1efbcd6SAlok Aggarwal 	static int (*compress2p)(void *, ulong_t *, void *, size_t, int) = NULL;
171579df0adSaalok 	void *libz_hdl = NULL;
17287117650Saalok 
173579df0adSaalok 	/*
174579df0adSaalok 	 * The first time we are called, attempt to dlopen()
1756e096e64SGary Mills 	 * libz.so.1 and get a pointer to the compress2() function
176579df0adSaalok 	 */
177579df0adSaalok 	if (compress2p == NULL) {
178579df0adSaalok 		if ((libz_hdl = openlib(LIBZ)) == NULL)
179579df0adSaalok 			die(gettext("could not find %s. "
180579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
181579df0adSaalok 
182579df0adSaalok 		if ((compress2p =
183579df0adSaalok 		    (int (*)(void *, ulong_t *, void *, size_t, int))
184579df0adSaalok 		    dlsym(libz_hdl, "compress2")) == NULL) {
185579df0adSaalok 			closelib();
186579df0adSaalok 			die(gettext("could not find the correct %s. "
187579df0adSaalok 			    "gzip compression unavailable\n"), LIBZ);
188579df0adSaalok 		}
189579df0adSaalok 	}
190579df0adSaalok 
191579df0adSaalok 	if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0)
192579df0adSaalok 		return (-1);
19387117650Saalok 	return (0);
19487117650Saalok }
1957c478bd9Sstevel@tonic-gate 
196b1efbcd6SAlok Aggarwal /*ARGSUSED*/
1977c478bd9Sstevel@tonic-gate static void
198b1efbcd6SAlok Aggarwal *SzAlloc(void *p, size_t size)
1997c478bd9Sstevel@tonic-gate {
200b1efbcd6SAlok Aggarwal 	return (malloc(size));
201b1efbcd6SAlok Aggarwal }
202b1efbcd6SAlok Aggarwal 
203b1efbcd6SAlok Aggarwal /*ARGSUSED*/
204b1efbcd6SAlok Aggarwal static void
205b1efbcd6SAlok Aggarwal SzFree(void *p, void *address, size_t size)
206b1efbcd6SAlok Aggarwal {
207b1efbcd6SAlok Aggarwal 	free(address);
208b1efbcd6SAlok Aggarwal }
209b1efbcd6SAlok Aggarwal 
210b1efbcd6SAlok Aggarwal static ISzAlloc g_Alloc = {
211b1efbcd6SAlok Aggarwal 	SzAlloc,
212b1efbcd6SAlok Aggarwal 	SzFree
213b1efbcd6SAlok Aggarwal };
214b1efbcd6SAlok Aggarwal 
215b1efbcd6SAlok Aggarwal #define	LZMA_UNCOMPRESSED_SIZE	8
216b1efbcd6SAlok Aggarwal #define	LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMPRESSED_SIZE)
217b1efbcd6SAlok Aggarwal 
218b1efbcd6SAlok Aggarwal /*ARGSUSED*/
219b1efbcd6SAlok Aggarwal static int
220b1efbcd6SAlok Aggarwal lzma_compress(void *src, size_t srclen, void *dst,
221b1efbcd6SAlok Aggarwal     size_t *dstlen, int level)
222b1efbcd6SAlok Aggarwal {
223b1efbcd6SAlok Aggarwal 	CLzmaEncProps props;
224b1efbcd6SAlok Aggarwal 	size_t outsize2;
225b1efbcd6SAlok Aggarwal 	size_t outsizeprocessed;
226b1efbcd6SAlok Aggarwal 	size_t outpropssize = LZMA_PROPS_SIZE;
227b1efbcd6SAlok Aggarwal 	uint64_t t = 0;
228b1efbcd6SAlok Aggarwal 	SRes res;
229b1efbcd6SAlok Aggarwal 	Byte *dstp;
230b1efbcd6SAlok Aggarwal 	int i;
231b1efbcd6SAlok Aggarwal 
232b1efbcd6SAlok Aggarwal 	outsize2 = *dstlen;
233b1efbcd6SAlok Aggarwal 
234b1efbcd6SAlok Aggarwal 	LzmaEncProps_Init(&props);
235b1efbcd6SAlok Aggarwal 
236b1efbcd6SAlok Aggarwal 	/*
237b1efbcd6SAlok Aggarwal 	 * The LZMA compressed file format is as follows -
238b1efbcd6SAlok Aggarwal 	 *
239b1efbcd6SAlok Aggarwal 	 * Offset Size(bytes) Description
240b1efbcd6SAlok Aggarwal 	 * 0		1	LZMA properties (lc, lp, lp (encoded))
241b1efbcd6SAlok Aggarwal 	 * 1		4	Dictionary size (little endian)
242b1efbcd6SAlok Aggarwal 	 * 5		8	Uncompressed size (little endian)
243b1efbcd6SAlok Aggarwal 	 * 13			Compressed data
244b1efbcd6SAlok Aggarwal 	 */
245b1efbcd6SAlok Aggarwal 
246b1efbcd6SAlok Aggarwal 	/* set the dictionary size to be 8MB */
247b1efbcd6SAlok Aggarwal 	props.dictSize = 1 << 23;
248b1efbcd6SAlok Aggarwal 
249b1efbcd6SAlok Aggarwal 	if (*dstlen < LZMA_HEADER_SIZE)
250b1efbcd6SAlok Aggarwal 		return (SZ_ERROR_OUTPUT_EOF);
251b1efbcd6SAlok Aggarwal 
252b1efbcd6SAlok Aggarwal 	dstp = (Byte *)dst;
253b1efbcd6SAlok Aggarwal 	t = srclen;
254b1efbcd6SAlok Aggarwal 	/*
255b1efbcd6SAlok Aggarwal 	 * Set the uncompressed size in the LZMA header
256b1efbcd6SAlok Aggarwal 	 * The LZMA properties (specified in 'props')
257b1efbcd6SAlok Aggarwal 	 * will be set by the call to LzmaEncode()
258b1efbcd6SAlok Aggarwal 	 */
259b1efbcd6SAlok Aggarwal 	for (i = 0; i < LZMA_UNCOMPRESSED_SIZE; i++, t >>= 8) {
260b1efbcd6SAlok Aggarwal 		dstp[LZMA_PROPS_SIZE + i] = (Byte)t;
261b1efbcd6SAlok Aggarwal 	}
262b1efbcd6SAlok Aggarwal 
263b1efbcd6SAlok Aggarwal 	outsizeprocessed = outsize2 - LZMA_HEADER_SIZE;
264b1efbcd6SAlok Aggarwal 	res = LzmaEncode(dstp + LZMA_HEADER_SIZE, &outsizeprocessed,
265b1efbcd6SAlok Aggarwal 	    src, srclen, &props, dstp, &outpropssize, 0, NULL,
266b1efbcd6SAlok Aggarwal 	    &g_Alloc, &g_Alloc);
267b1efbcd6SAlok Aggarwal 
268b1efbcd6SAlok Aggarwal 	if (res != 0)
269b1efbcd6SAlok Aggarwal 		return (-1);
270b1efbcd6SAlok Aggarwal 
271b1efbcd6SAlok Aggarwal 	*dstlen = outsizeprocessed + LZMA_HEADER_SIZE;
272b1efbcd6SAlok Aggarwal 	return (0);
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate /*
2767c478bd9Sstevel@tonic-gate  * Translate a lofi device name to a minor number. We might be asked
2777c478bd9Sstevel@tonic-gate  * to do this when there is no association (such as when the user specifies
2787c478bd9Sstevel@tonic-gate  * a particular device), so we can only look at the string.
2797c478bd9Sstevel@tonic-gate  */
2807c478bd9Sstevel@tonic-gate static int
2817c478bd9Sstevel@tonic-gate name_to_minor(const char *devicename)
2827c478bd9Sstevel@tonic-gate {
283*a29e56d9SToomas Soome 	struct stat st;
2847c478bd9Sstevel@tonic-gate 
285*a29e56d9SToomas Soome 	/*
286*a29e56d9SToomas Soome 	 * If devicename does not exist, then devicename contains
287*a29e56d9SToomas Soome 	 * the name of the device to be created.
288*a29e56d9SToomas Soome 	 * Note we only allow non-labeled devices here.
289*a29e56d9SToomas Soome 	 */
290*a29e56d9SToomas Soome 	if (stat(devicename, &st)) {
291*a29e56d9SToomas Soome 		int minor, rv;
292*a29e56d9SToomas Soome 
293*a29e56d9SToomas Soome 		rv = sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor);
294*a29e56d9SToomas Soome 		if (rv == 1)
2957c478bd9Sstevel@tonic-gate 			return (minor);
296*a29e56d9SToomas Soome 		rv = sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor);
297*a29e56d9SToomas Soome 		if (rv == 1)
2987c478bd9Sstevel@tonic-gate 			return (minor);
299*a29e56d9SToomas Soome 
300*a29e56d9SToomas Soome 		return (0);
3017c478bd9Sstevel@tonic-gate 	}
302*a29e56d9SToomas Soome 
303*a29e56d9SToomas Soome 	if (st.st_mode & S_IFCHR || st.st_mode & S_IFBLK) {
304*a29e56d9SToomas Soome 		return (LOFI_MINOR2ID(minor(st.st_rdev)));
305*a29e56d9SToomas Soome 	}
306*a29e56d9SToomas Soome 
3077c478bd9Sstevel@tonic-gate 	return (0);
3087c478bd9Sstevel@tonic-gate }
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate /*
3117c478bd9Sstevel@tonic-gate  * This might be the first time we've used this minor number. If so,
3127c478bd9Sstevel@tonic-gate  * it might also be that the /dev links are in the process of being created
3137c478bd9Sstevel@tonic-gate  * by devfsadmd (or that they'll be created "soon"). We cannot return
3147c478bd9Sstevel@tonic-gate  * until they're there or the invoker of lofiadm might try to use them
3157c478bd9Sstevel@tonic-gate  * and not find them. This can happen if a shell script is running on
3167c478bd9Sstevel@tonic-gate  * an MP.
3177c478bd9Sstevel@tonic-gate  */
3187c478bd9Sstevel@tonic-gate static int sleeptime = 2;	/* number of seconds to sleep between stat's */
3197c478bd9Sstevel@tonic-gate static int maxsleep = 120;	/* maximum number of seconds to sleep */
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate static void
322*a29e56d9SToomas Soome make_blkdevname(struct lofi_ioctl *li, char *path, size_t len)
323*a29e56d9SToomas Soome {
324*a29e56d9SToomas Soome 	char *r1, *r2;
325*a29e56d9SToomas Soome 	size_t l1;
326*a29e56d9SToomas Soome 
327*a29e56d9SToomas Soome 	if (li->li_devpath[0] == '\0') {
328*a29e56d9SToomas Soome 		if (li->li_labeled)
329*a29e56d9SToomas Soome 			(void) strlcpy(path, "unknown", len);
330*a29e56d9SToomas Soome 		else
331*a29e56d9SToomas Soome 			(void) snprintf(path, len,
332*a29e56d9SToomas Soome 			    "/dev/" LOFI_BLOCK_NAME "/%d", li->li_id);
333*a29e56d9SToomas Soome 		return;
334*a29e56d9SToomas Soome 	}
335*a29e56d9SToomas Soome 	(void) strlcpy(path, li->li_devpath, len);
336*a29e56d9SToomas Soome 	r1 = strchr(path, 'r');
337*a29e56d9SToomas Soome 	l1 = r1 - path;
338*a29e56d9SToomas Soome 	r2 = strchr(li->li_devpath, 'r');
339*a29e56d9SToomas Soome 	(void) strlcpy(r1, r2+1, len - l1);
340*a29e56d9SToomas Soome 
341*a29e56d9SToomas Soome 	if (li->li_labeled) {
342*a29e56d9SToomas Soome 		(void) strlcat(path, "p0", len);
343*a29e56d9SToomas Soome 	}
344*a29e56d9SToomas Soome }
345*a29e56d9SToomas Soome 
346*a29e56d9SToomas Soome static void
347*a29e56d9SToomas Soome wait_until_dev_complete(struct lofi_ioctl *li)
3487c478bd9Sstevel@tonic-gate {
3497c478bd9Sstevel@tonic-gate 	struct stat64 buf;
3507c478bd9Sstevel@tonic-gate 	int	cursleep;
35187117650Saalok 	char	blkpath[MAXPATHLEN];
35287117650Saalok 	char	charpath[MAXPATHLEN];
3531d8ed0f0Svikram 	di_devlink_handle_t hdl;
3541d8ed0f0Svikram 
355*a29e56d9SToomas Soome 	make_blkdevname(li, blkpath, sizeof (blkpath));
356*a29e56d9SToomas Soome 	(void) strlcpy(charpath, li->li_devpath, sizeof (charpath));
357*a29e56d9SToomas Soome 
358*a29e56d9SToomas Soome 	if (li->li_labeled) {
359*a29e56d9SToomas Soome 		(void) strlcat(charpath, "p0", sizeof (charpath));
360*a29e56d9SToomas Soome 	}
3611d8ed0f0Svikram 
3621d8ed0f0Svikram 	/* Check if links already present */
3631d8ed0f0Svikram 	if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
3641d8ed0f0Svikram 		return;
3651d8ed0f0Svikram 
3661d8ed0f0Svikram 	/* First use di_devlink_init() */
3671d8ed0f0Svikram 	if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
3681d8ed0f0Svikram 		(void) di_devlink_fini(&hdl);
3691d8ed0f0Svikram 		goto out;
3701d8ed0f0Svikram 	}
3711d8ed0f0Svikram 
3721d8ed0f0Svikram 	/*
3731d8ed0f0Svikram 	 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
3741d8ed0f0Svikram 	 * only fail if the caller is non-root. In that case, wait for
3751d8ed0f0Svikram 	 * link creation via sysevents.
3761d8ed0f0Svikram 	 */
3777d82f0f8SDina K Nimeh 	for (cursleep = 0; cursleep < maxsleep; cursleep += sleeptime) {
3787d82f0f8SDina K Nimeh 		if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
3797c478bd9Sstevel@tonic-gate 			return;
3807d82f0f8SDina K Nimeh 		(void) sleep(sleeptime);
3817c478bd9Sstevel@tonic-gate 	}
3821d8ed0f0Svikram 
3837c478bd9Sstevel@tonic-gate 	/* one last try */
3841d8ed0f0Svikram out:
3857c478bd9Sstevel@tonic-gate 	if (stat64(blkpath, &buf) == -1) {
3867c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), blkpath);
3877c478bd9Sstevel@tonic-gate 	}
3887c478bd9Sstevel@tonic-gate 	if (stat64(charpath, &buf) == -1) {
3897c478bd9Sstevel@tonic-gate 		die(gettext("%s was not created"), charpath);
3907c478bd9Sstevel@tonic-gate 	}
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate /*
3947d82f0f8SDina K Nimeh  * Map the file and return the minor number the driver picked for the file
3957d82f0f8SDina K Nimeh  * DO NOT use this function if the filename is actually the device name.
3967d82f0f8SDina K Nimeh  */
3977d82f0f8SDina K Nimeh static int
398*a29e56d9SToomas Soome lofi_map_file(int lfd, struct lofi_ioctl *li, const char *filename)
3997d82f0f8SDina K Nimeh {
4007d82f0f8SDina K Nimeh 	int	minor;
4017d82f0f8SDina K Nimeh 
402*a29e56d9SToomas Soome 	li->li_id = 0;
403*a29e56d9SToomas Soome 	(void) strlcpy(li->li_filename, filename, sizeof (li->li_filename));
404*a29e56d9SToomas Soome 	minor = ioctl(lfd, LOFI_MAP_FILE, li);
4057d82f0f8SDina K Nimeh 	if (minor == -1) {
4067d82f0f8SDina K Nimeh 		if (errno == ENOTSUP)
4077d82f0f8SDina K Nimeh 			warn(gettext("encrypting compressed files is "
4087d82f0f8SDina K Nimeh 			    "unsupported"));
4097d82f0f8SDina K Nimeh 		die(gettext("could not map file %s"), filename);
4107d82f0f8SDina K Nimeh 	}
411*a29e56d9SToomas Soome 	wait_until_dev_complete(li);
4127d82f0f8SDina K Nimeh 	return (minor);
4137d82f0f8SDina K Nimeh }
4147d82f0f8SDina K Nimeh 
4157d82f0f8SDina K Nimeh /*
4167c478bd9Sstevel@tonic-gate  * Add a device association. If devicename is NULL, let the driver
4177c478bd9Sstevel@tonic-gate  * pick a device.
4187c478bd9Sstevel@tonic-gate  */
4197c478bd9Sstevel@tonic-gate static void
42087117650Saalok add_mapping(int lfd, const char *devicename, const char *filename,
421*a29e56d9SToomas Soome     mech_alias_t *cipher, const char *rkey, size_t rksz, boolean_t rdonly,
422*a29e56d9SToomas Soome     boolean_t label)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
4257d82f0f8SDina K Nimeh 
426*a29e56d9SToomas Soome 	bzero(&li, sizeof (li));
427cd69fabeSAlexander Eremin 	li.li_readonly = rdonly;
428*a29e56d9SToomas Soome 	li.li_labeled = label;
429cd69fabeSAlexander Eremin 
4307d82f0f8SDina K Nimeh 	li.li_crypto_enabled = B_FALSE;
4317d82f0f8SDina K Nimeh 	if (cipher != NULL) {
4327d82f0f8SDina K Nimeh 		/* set up encryption for mapped file */
4337d82f0f8SDina K Nimeh 		li.li_crypto_enabled = B_TRUE;
4347d82f0f8SDina K Nimeh 		(void) strlcpy(li.li_cipher, cipher->name,
4357d82f0f8SDina K Nimeh 		    sizeof (li.li_cipher));
4367d82f0f8SDina K Nimeh 		if (rksz > sizeof (li.li_key)) {
4377d82f0f8SDina K Nimeh 			die(gettext("key too large"));
4387d82f0f8SDina K Nimeh 		}
4397d82f0f8SDina K Nimeh 		bcopy(rkey, li.li_key, rksz);
4407d82f0f8SDina K Nimeh 		li.li_key_len = rksz << 3;	/* convert to bits */
4417d82f0f8SDina K Nimeh 
4427d82f0f8SDina K Nimeh 		li.li_iv_type = cipher->iv_type;
4437d82f0f8SDina K Nimeh 		li.li_iv_len = cipher->iv_len;	/* 0 when no iv needed */
4447d82f0f8SDina K Nimeh 		switch (cipher->iv_type) {
4457d82f0f8SDina K Nimeh 		case IVM_ENC_BLKNO:
4467d82f0f8SDina K Nimeh 			(void) strlcpy(li.li_iv_cipher, cipher->iv_name,
4477d82f0f8SDina K Nimeh 			    sizeof (li.li_iv_cipher));
4487d82f0f8SDina K Nimeh 			break;
4497d82f0f8SDina K Nimeh 		case IVM_NONE:
4507d82f0f8SDina K Nimeh 			/* FALLTHROUGH */
4517d82f0f8SDina K Nimeh 		default:
4527d82f0f8SDina K Nimeh 			break;
4537d82f0f8SDina K Nimeh 		}
4547d82f0f8SDina K Nimeh 	}
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
4577d82f0f8SDina K Nimeh 		int	minor;
458*a29e56d9SToomas Soome 		char	path[MAXPATHLEN];
45987117650Saalok 
4607d82f0f8SDina K Nimeh 		/* pick one via the driver */
461*a29e56d9SToomas Soome 		minor = lofi_map_file(lfd, &li, filename);
462*a29e56d9SToomas Soome 		if (minor > 0) {
463*a29e56d9SToomas Soome 			make_blkdevname(&li, path, sizeof (path));
464*a29e56d9SToomas Soome 
4657d82f0f8SDina K Nimeh 			/* if mapping succeeds, print the one picked */
466*a29e56d9SToomas Soome 			(void) printf("%s\n", path);
467*a29e56d9SToomas Soome 		}
4687c478bd9Sstevel@tonic-gate 		return;
4697c478bd9Sstevel@tonic-gate 	}
4707d82f0f8SDina K Nimeh 
4717c478bd9Sstevel@tonic-gate 	/* use device we were given */
472*a29e56d9SToomas Soome 	li.li_id = name_to_minor(devicename);
473*a29e56d9SToomas Soome 	if (li.li_id == 0) {
4747c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
4757c478bd9Sstevel@tonic-gate 	}
47687117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
4777d82f0f8SDina K Nimeh 
4787d82f0f8SDina K Nimeh 	/* if device is already in use li.li_minor won't change */
4797c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
4807d82f0f8SDina K Nimeh 		if (errno == ENOTSUP)
4817d82f0f8SDina K Nimeh 			warn(gettext("encrypting compressed files is "
4827d82f0f8SDina K Nimeh 			    "unsupported"));
4837c478bd9Sstevel@tonic-gate 		die(gettext("could not map file %s to %s"), filename,
4847c478bd9Sstevel@tonic-gate 		    devicename);
4857c478bd9Sstevel@tonic-gate 	}
486*a29e56d9SToomas Soome 	wait_until_dev_complete(&li);
4877c478bd9Sstevel@tonic-gate }
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate /*
4907c478bd9Sstevel@tonic-gate  * Remove an association. Delete by device name if non-NULL, or by
4917c478bd9Sstevel@tonic-gate  * filename otherwise.
4927c478bd9Sstevel@tonic-gate  */
4937c478bd9Sstevel@tonic-gate static void
4943d7072f8Seschrock delete_mapping(int lfd, const char *devicename, const char *filename,
4953d7072f8Seschrock     boolean_t force)
4967c478bd9Sstevel@tonic-gate {
4977c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
4987c478bd9Sstevel@tonic-gate 
4993d7072f8Seschrock 	li.li_force = force;
50093239addSjohnlev 	li.li_cleanup = B_FALSE;
50193239addSjohnlev 
5027c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
5037c478bd9Sstevel@tonic-gate 		/* delete by filename */
50487117650Saalok 		(void) strlcpy(li.li_filename, filename,
50587117650Saalok 		    sizeof (li.li_filename));
506*a29e56d9SToomas Soome 		li.li_id = 0;
5077c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
5087c478bd9Sstevel@tonic-gate 			die(gettext("could not unmap file %s"), filename);
5097c478bd9Sstevel@tonic-gate 		}
5107c478bd9Sstevel@tonic-gate 		return;
5117c478bd9Sstevel@tonic-gate 	}
5127c478bd9Sstevel@tonic-gate 
513f9153c6bSDina K Nimeh 	/* delete by device */
514*a29e56d9SToomas Soome 	li.li_id = name_to_minor(devicename);
515*a29e56d9SToomas Soome 	if (li.li_id == 0) {
5167c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
5177c478bd9Sstevel@tonic-gate 	}
5187c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
5197c478bd9Sstevel@tonic-gate 		die(gettext("could not unmap device %s"), devicename);
5207c478bd9Sstevel@tonic-gate 	}
5217c478bd9Sstevel@tonic-gate }
5227c478bd9Sstevel@tonic-gate 
5237d82f0f8SDina K Nimeh /*
5247d82f0f8SDina K Nimeh  * Show filename given devicename, or devicename given filename.
5257d82f0f8SDina K Nimeh  */
5267c478bd9Sstevel@tonic-gate static void
5277c478bd9Sstevel@tonic-gate print_one_mapping(int lfd, const char *devicename, const char *filename)
5287c478bd9Sstevel@tonic-gate {
5297c478bd9Sstevel@tonic-gate 	struct lofi_ioctl li;
530*a29e56d9SToomas Soome 	char blkpath[MAXPATHLEN];
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	if (devicename == NULL) {
5337c478bd9Sstevel@tonic-gate 		/* given filename, print devicename */
534*a29e56d9SToomas Soome 		li.li_id = 0;
53587117650Saalok 		(void) strlcpy(li.li_filename, filename,
53687117650Saalok 		    sizeof (li.li_filename));
5377c478bd9Sstevel@tonic-gate 		if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
5387c478bd9Sstevel@tonic-gate 			die(gettext("could not find device for %s"), filename);
5397c478bd9Sstevel@tonic-gate 		}
540*a29e56d9SToomas Soome 		make_blkdevname(&li, blkpath, sizeof (blkpath));
541*a29e56d9SToomas Soome 		(void) printf("%s\n", blkpath);
5427c478bd9Sstevel@tonic-gate 		return;
5437c478bd9Sstevel@tonic-gate 	}
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	/* given devicename, print filename */
546*a29e56d9SToomas Soome 	li.li_id = name_to_minor(devicename);
547*a29e56d9SToomas Soome 	if (li.li_id == 0) {
5487c478bd9Sstevel@tonic-gate 		die(gettext("malformed device name %s\n"), devicename);
5497c478bd9Sstevel@tonic-gate 	}
5507c478bd9Sstevel@tonic-gate 	if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
5517c478bd9Sstevel@tonic-gate 		die(gettext("could not find filename for %s"), devicename);
5527c478bd9Sstevel@tonic-gate 	}
5537c478bd9Sstevel@tonic-gate 	(void) printf("%s\n", li.li_filename);
5547c478bd9Sstevel@tonic-gate }
5557c478bd9Sstevel@tonic-gate 
55687117650Saalok /*
5577d82f0f8SDina K Nimeh  * Print the list of all the mappings, including a header.
5587d82f0f8SDina K Nimeh  */
5597d82f0f8SDina K Nimeh static void
5607d82f0f8SDina K Nimeh print_mappings(int fd)
5617d82f0f8SDina K Nimeh {
5627d82f0f8SDina K Nimeh 	struct lofi_ioctl li;
5637d82f0f8SDina K Nimeh 	int	minor;
5647d82f0f8SDina K Nimeh 	int	maxminor;
5657d82f0f8SDina K Nimeh 	char	path[MAXPATHLEN];
566cd69fabeSAlexander Eremin 	char	options[MAXPATHLEN] = { 0 };
5677d82f0f8SDina K Nimeh 
568*a29e56d9SToomas Soome 	li.li_id = 0;
5697d82f0f8SDina K Nimeh 	if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
5707d82f0f8SDina K Nimeh 		die("ioctl");
5717d82f0f8SDina K Nimeh 	}
572*a29e56d9SToomas Soome 	maxminor = li.li_id;
5737d82f0f8SDina K Nimeh 
5747d82f0f8SDina K Nimeh 	(void) printf(FORMAT, gettext("Block Device"), gettext("File"),
5757d82f0f8SDina K Nimeh 	    gettext("Options"));
5767d82f0f8SDina K Nimeh 	for (minor = 1; minor <= maxminor; minor++) {
577*a29e56d9SToomas Soome 		li.li_id = minor;
5787d82f0f8SDina K Nimeh 		if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
5797d82f0f8SDina K Nimeh 			if (errno == ENXIO)
5807d82f0f8SDina K Nimeh 				continue;
5817d82f0f8SDina K Nimeh 			warn("ioctl");
5827d82f0f8SDina K Nimeh 			break;
5837d82f0f8SDina K Nimeh 		}
584*a29e56d9SToomas Soome 		make_blkdevname(&li, path, sizeof (path));
585cd69fabeSAlexander Eremin 
586cd69fabeSAlexander Eremin 		options[0] = '\0';
587cd69fabeSAlexander Eremin 
5887d82f0f8SDina K Nimeh 		/*
5897d82f0f8SDina K Nimeh 		 * Encrypted lofi and compressed lofi are mutually exclusive.
5907d82f0f8SDina K Nimeh 		 */
5917d82f0f8SDina K Nimeh 		if (li.li_crypto_enabled)
5927d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options),
5937d82f0f8SDina K Nimeh 			    gettext("Encrypted"));
5947d82f0f8SDina K Nimeh 		else if (li.li_algorithm[0] != '\0')
5957d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options),
5967d82f0f8SDina K Nimeh 			    gettext("Compressed(%s)"), li.li_algorithm);
597cd69fabeSAlexander Eremin 		if (li.li_readonly) {
598cd69fabeSAlexander Eremin 			if (strlen(options) != 0) {
599*a29e56d9SToomas Soome 				(void) strlcat(options, ",Readonly",
600cd69fabeSAlexander Eremin 				    sizeof (options));
601cd69fabeSAlexander Eremin 			} else {
602cd69fabeSAlexander Eremin 				(void) snprintf(options, sizeof (options),
603cd69fabeSAlexander Eremin 				    gettext("Readonly"));
604cd69fabeSAlexander Eremin 			}
605cd69fabeSAlexander Eremin 		}
606*a29e56d9SToomas Soome 		if (li.li_labeled) {
607*a29e56d9SToomas Soome 			if (strlen(options) != 0) {
608*a29e56d9SToomas Soome 				(void) strlcat(options, ",Labeled",
609*a29e56d9SToomas Soome 				    sizeof (options));
610*a29e56d9SToomas Soome 			} else {
611*a29e56d9SToomas Soome 				(void) snprintf(options, sizeof (options),
612*a29e56d9SToomas Soome 				    gettext("Labeled"));
613*a29e56d9SToomas Soome 			}
614*a29e56d9SToomas Soome 		}
615cd69fabeSAlexander Eremin 		if (strlen(options) == 0)
6167d82f0f8SDina K Nimeh 			(void) snprintf(options, sizeof (options), "-");
6177d82f0f8SDina K Nimeh 
6187d82f0f8SDina K Nimeh 		(void) printf(FORMAT, path, li.li_filename, options);
6197d82f0f8SDina K Nimeh 	}
6207d82f0f8SDina K Nimeh }
6217d82f0f8SDina K Nimeh 
6227d82f0f8SDina K Nimeh /*
6237d82f0f8SDina K Nimeh  * Verify the cipher selected by user.
6247d82f0f8SDina K Nimeh  */
6257d82f0f8SDina K Nimeh static mech_alias_t *
6267d82f0f8SDina K Nimeh ciph2mech(const char *alias)
6277d82f0f8SDina K Nimeh {
6287d82f0f8SDina K Nimeh 	int	i;
6297d82f0f8SDina K Nimeh 
6307d82f0f8SDina K Nimeh 	for (i = 0; i < mech_aliases_count; i++) {
6317d82f0f8SDina K Nimeh 		if (strcasecmp(alias, mech_aliases[i].alias) == 0)
6327d82f0f8SDina K Nimeh 			return (&mech_aliases[i]);
6337d82f0f8SDina K Nimeh 	}
6347d82f0f8SDina K Nimeh 	return (NULL);
6357d82f0f8SDina K Nimeh }
6367d82f0f8SDina K Nimeh 
6377d82f0f8SDina K Nimeh /*
6387d82f0f8SDina K Nimeh  * Verify user selected cipher is also available in kernel.
6397d82f0f8SDina K Nimeh  *
6407d82f0f8SDina K Nimeh  * While traversing kernel list of mechs, if the cipher is supported in the
6417d82f0f8SDina K Nimeh  * kernel for both encryption and decryption, it also picks up the min/max
6427d82f0f8SDina K Nimeh  * key size.
6437d82f0f8SDina K Nimeh  */
6447d82f0f8SDina K Nimeh static boolean_t
6457d82f0f8SDina K Nimeh kernel_cipher_check(mech_alias_t *cipher)
6467d82f0f8SDina K Nimeh {
6477d82f0f8SDina K Nimeh 	boolean_t ciph_ok = B_FALSE;
6487d82f0f8SDina K Nimeh 	boolean_t iv_ok = B_FALSE;
6497d82f0f8SDina K Nimeh 	int	i;
6507d82f0f8SDina K Nimeh 	int	count;
6517d82f0f8SDina K Nimeh 	crypto_get_mechanism_list_t *kciphers = NULL;
6527d82f0f8SDina K Nimeh 	crypto_get_all_mechanism_info_t *kinfo = NULL;
6537d82f0f8SDina K Nimeh 	int	fd = -1;
6547d82f0f8SDina K Nimeh 	size_t	keymin;
6557d82f0f8SDina K Nimeh 	size_t	keymax;
6567d82f0f8SDina K Nimeh 
6577d82f0f8SDina K Nimeh 	/* if cipher doesn't need iv generating mech, bypass that check now */
6587d82f0f8SDina K Nimeh 	if (cipher->iv_name == NULL)
6597d82f0f8SDina K Nimeh 		iv_ok = B_TRUE;
6607d82f0f8SDina K Nimeh 
6617d82f0f8SDina K Nimeh 	/* allocate some space for the list of kernel ciphers */
6627d82f0f8SDina K Nimeh 	count = DEFAULT_CIPHER_NUM;
6637d82f0f8SDina K Nimeh 	kciphers = malloc(sizeof (crypto_get_mechanism_list_t) +
6647d82f0f8SDina K Nimeh 	    sizeof (crypto_mech_name_t) * (count - 1));
6657d82f0f8SDina K Nimeh 	if (kciphers == NULL)
6667d82f0f8SDina K Nimeh 		die(gettext("failed to allocate memory for list of "
6677d82f0f8SDina K Nimeh 		    "kernel mechanisms"));
6687d82f0f8SDina K Nimeh 	kciphers->ml_count = count;
6697d82f0f8SDina K Nimeh 
6707d82f0f8SDina K Nimeh 	/* query crypto device to get list of kernel ciphers */
6717d82f0f8SDina K Nimeh 	if ((fd = open("/dev/crypto", O_RDWR)) == -1) {
6727d82f0f8SDina K Nimeh 		warn(gettext("failed to open %s"), "/dev/crypto");
6737d82f0f8SDina K Nimeh 		goto kcc_out;
6747d82f0f8SDina K Nimeh 	}
6757d82f0f8SDina K Nimeh 
6767d82f0f8SDina K Nimeh 	if (ioctl(fd, CRYPTO_GET_MECHANISM_LIST, kciphers) == -1) {
6777d82f0f8SDina K Nimeh 		warn(gettext("CRYPTO_GET_MECHANISM_LIST ioctl failed"));
6787d82f0f8SDina K Nimeh 		goto kcc_out;
6797d82f0f8SDina K Nimeh 	}
6807d82f0f8SDina K Nimeh 
6817d82f0f8SDina K Nimeh 	if (kciphers->ml_return_value == CRYPTO_BUFFER_TOO_SMALL) {
6827d82f0f8SDina K Nimeh 		count = kciphers->ml_count;
6837d82f0f8SDina K Nimeh 		free(kciphers);
6847d82f0f8SDina K Nimeh 		kciphers = malloc(sizeof (crypto_get_mechanism_list_t) +
6857d82f0f8SDina K Nimeh 		    sizeof (crypto_mech_name_t) * (count - 1));
6867d82f0f8SDina K Nimeh 		if (kciphers == NULL) {
6877d82f0f8SDina K Nimeh 			warn(gettext("failed to allocate memory for list of "
6887d82f0f8SDina K Nimeh 			    "kernel mechanisms"));
6897d82f0f8SDina K Nimeh 			goto kcc_out;
6907d82f0f8SDina K Nimeh 		}
6917d82f0f8SDina K Nimeh 		kciphers->ml_count = count;
6927d82f0f8SDina K Nimeh 
6937d82f0f8SDina K Nimeh 		if (ioctl(fd, CRYPTO_GET_MECHANISM_LIST, kciphers) == -1) {
6947d82f0f8SDina K Nimeh 			warn(gettext("CRYPTO_GET_MECHANISM_LIST ioctl failed"));
6957d82f0f8SDina K Nimeh 			goto kcc_out;
6967d82f0f8SDina K Nimeh 		}
6977d82f0f8SDina K Nimeh 	}
6987d82f0f8SDina K Nimeh 
6997d82f0f8SDina K Nimeh 	if (kciphers->ml_return_value != CRYPTO_SUCCESS) {
7007d82f0f8SDina K Nimeh 		warn(gettext(
7017d82f0f8SDina K Nimeh 		    "CRYPTO_GET_MECHANISM_LIST ioctl return value = %d\n"),
7027d82f0f8SDina K Nimeh 		    kciphers->ml_return_value);
7037d82f0f8SDina K Nimeh 		goto kcc_out;
7047d82f0f8SDina K Nimeh 	}
7057d82f0f8SDina K Nimeh 
7067d82f0f8SDina K Nimeh 	/*
7077d82f0f8SDina K Nimeh 	 * scan list of kernel ciphers looking for the selected one and if
7087d82f0f8SDina K Nimeh 	 * it needs an iv generated using another cipher, also look for that
7097d82f0f8SDina K Nimeh 	 * additional cipher to be used for generating the iv
7107d82f0f8SDina K Nimeh 	 */
7117d82f0f8SDina K Nimeh 	count = kciphers->ml_count;
7127d82f0f8SDina K Nimeh 	for (i = 0; i < count && !(ciph_ok && iv_ok); i++) {
7137d82f0f8SDina K Nimeh 		if (!ciph_ok &&
7147d82f0f8SDina K Nimeh 		    strcasecmp(cipher->name, kciphers->ml_list[i]) == 0)
7157d82f0f8SDina K Nimeh 			ciph_ok = B_TRUE;
7167d82f0f8SDina K Nimeh 		if (!iv_ok &&
7177d82f0f8SDina K Nimeh 		    strcasecmp(cipher->iv_name, kciphers->ml_list[i]) == 0)
7187d82f0f8SDina K Nimeh 			iv_ok = B_TRUE;
7197d82f0f8SDina K Nimeh 	}
7207d82f0f8SDina K Nimeh 	free(kciphers);
7217d82f0f8SDina K Nimeh 	kciphers = NULL;
7227d82f0f8SDina K Nimeh 
7237d82f0f8SDina K Nimeh 	if (!ciph_ok)
7247d82f0f8SDina K Nimeh 		warn(gettext("%s mechanism not supported in kernel\n"),
7257d82f0f8SDina K Nimeh 		    cipher->name);
7267d82f0f8SDina K Nimeh 	if (!iv_ok)
7277d82f0f8SDina K Nimeh 		warn(gettext("%s mechanism not supported in kernel\n"),
7287d82f0f8SDina K Nimeh 		    cipher->iv_name);
7297d82f0f8SDina K Nimeh 
7307d82f0f8SDina K Nimeh 	if (ciph_ok) {
7317d82f0f8SDina K Nimeh 		/* Get the details about the user selected cipher */
7327d82f0f8SDina K Nimeh 		count = DEFAULT_MECHINFO_NUM;
7337d82f0f8SDina K Nimeh 		kinfo = malloc(sizeof (crypto_get_all_mechanism_info_t) +
7347d82f0f8SDina K Nimeh 		    sizeof (crypto_mechanism_info_t) * (count - 1));
7357d82f0f8SDina K Nimeh 		if (kinfo == NULL) {
7367d82f0f8SDina K Nimeh 			warn(gettext("failed to allocate memory for "
7377d82f0f8SDina K Nimeh 			    "kernel mechanism info"));
7387d82f0f8SDina K Nimeh 			goto kcc_out;
7397d82f0f8SDina K Nimeh 		}
7407d82f0f8SDina K Nimeh 		kinfo->mi_count = count;
7417d82f0f8SDina K Nimeh 		(void) strlcpy(kinfo->mi_mechanism_name, cipher->name,
7427d82f0f8SDina K Nimeh 		    CRYPTO_MAX_MECH_NAME);
7437d82f0f8SDina K Nimeh 
7447d82f0f8SDina K Nimeh 		if (ioctl(fd, CRYPTO_GET_ALL_MECHANISM_INFO, kinfo) == -1) {
7457d82f0f8SDina K Nimeh 			warn(gettext(
7467d82f0f8SDina K Nimeh 			    "CRYPTO_GET_ALL_MECHANISM_INFO ioctl failed"));
7477d82f0f8SDina K Nimeh 			goto kcc_out;
7487d82f0f8SDina K Nimeh 		}
7497d82f0f8SDina K Nimeh 
7507d82f0f8SDina K Nimeh 		if (kinfo->mi_return_value == CRYPTO_BUFFER_TOO_SMALL) {
7517d82f0f8SDina K Nimeh 			count = kinfo->mi_count;
7527d82f0f8SDina K Nimeh 			free(kinfo);
7537d82f0f8SDina K Nimeh 			kinfo = malloc(
7547d82f0f8SDina K Nimeh 			    sizeof (crypto_get_all_mechanism_info_t) +
7557d82f0f8SDina K Nimeh 			    sizeof (crypto_mechanism_info_t) * (count - 1));
7567d82f0f8SDina K Nimeh 			if (kinfo == NULL) {
7577d82f0f8SDina K Nimeh 				warn(gettext("failed to allocate memory for "
7587d82f0f8SDina K Nimeh 				    "kernel mechanism info"));
7597d82f0f8SDina K Nimeh 				goto kcc_out;
7607d82f0f8SDina K Nimeh 			}
7617d82f0f8SDina K Nimeh 			kinfo->mi_count = count;
7627d82f0f8SDina K Nimeh 			(void) strlcpy(kinfo->mi_mechanism_name, cipher->name,
7637d82f0f8SDina K Nimeh 			    CRYPTO_MAX_MECH_NAME);
7647d82f0f8SDina K Nimeh 
7657d82f0f8SDina K Nimeh 			if (ioctl(fd, CRYPTO_GET_ALL_MECHANISM_INFO, kinfo) ==
7667d82f0f8SDina K Nimeh 			    -1) {
7677d82f0f8SDina K Nimeh 				warn(gettext("CRYPTO_GET_ALL_MECHANISM_INFO "
7687d82f0f8SDina K Nimeh 				    "ioctl failed"));
7697d82f0f8SDina K Nimeh 				goto kcc_out;
7707d82f0f8SDina K Nimeh 			}
7717d82f0f8SDina K Nimeh 		}
7727d82f0f8SDina K Nimeh 
7737d82f0f8SDina K Nimeh 		if (kinfo->mi_return_value != CRYPTO_SUCCESS) {
7747d82f0f8SDina K Nimeh 			warn(gettext("CRYPTO_GET_ALL_MECHANISM_INFO ioctl "
7757d82f0f8SDina K Nimeh 			    "return value = %d\n"), kinfo->mi_return_value);
7767d82f0f8SDina K Nimeh 			goto kcc_out;
7777d82f0f8SDina K Nimeh 		}
7787d82f0f8SDina K Nimeh 
7797d82f0f8SDina K Nimeh 		/* Set key min and max size */
7807d82f0f8SDina K Nimeh 		count = kinfo->mi_count;
7817d82f0f8SDina K Nimeh 		i = 0;
7827d82f0f8SDina K Nimeh 		if (i < count) {
7837d82f0f8SDina K Nimeh 			keymin = kinfo->mi_list[i].mi_min_key_size;
7847d82f0f8SDina K Nimeh 			keymax = kinfo->mi_list[i].mi_max_key_size;
7857d82f0f8SDina K Nimeh 			if (kinfo->mi_list[i].mi_keysize_unit &
7867d82f0f8SDina K Nimeh 			    CRYPTO_KEYSIZE_UNIT_IN_BITS) {
7877d82f0f8SDina K Nimeh 				keymin = CRYPTO_BITS2BYTES(keymin);
7887d82f0f8SDina K Nimeh 				keymax = CRYPTO_BITS2BYTES(keymax);
7897d82f0f8SDina K Nimeh 
7907d82f0f8SDina K Nimeh 			}
7917d82f0f8SDina K Nimeh 			cipher->min_keysize = keymin;
7927d82f0f8SDina K Nimeh 			cipher->max_keysize = keymax;
7937d82f0f8SDina K Nimeh 		}
7947d82f0f8SDina K Nimeh 		free(kinfo);
7957d82f0f8SDina K Nimeh 		kinfo = NULL;
7967d82f0f8SDina K Nimeh 
7977d82f0f8SDina K Nimeh 		if (i == count) {
7987d82f0f8SDina K Nimeh 			(void) close(fd);
7997d82f0f8SDina K Nimeh 			die(gettext(
8007d82f0f8SDina K Nimeh 			    "failed to find usable %s kernel mechanism, "
8017d82f0f8SDina K Nimeh 			    "use \"cryptoadm list -m\" to find available "
8027d82f0f8SDina K Nimeh 			    "mechanisms\n"),
8037d82f0f8SDina K Nimeh 			    cipher->name);
8047d82f0f8SDina K Nimeh 		}
8057d82f0f8SDina K Nimeh 	}
8067d82f0f8SDina K Nimeh 
8077d82f0f8SDina K Nimeh 	/* Note: key min/max, unit size, usage for iv cipher are not checked. */
8087d82f0f8SDina K Nimeh 
8097d82f0f8SDina K Nimeh 	return (ciph_ok && iv_ok);
8107d82f0f8SDina K Nimeh 
8117d82f0f8SDina K Nimeh kcc_out:
8127d82f0f8SDina K Nimeh 	if (kinfo != NULL)
8137d82f0f8SDina K Nimeh 		free(kinfo);
8147d82f0f8SDina K Nimeh 	if (kciphers != NULL)
8157d82f0f8SDina K Nimeh 		free(kciphers);
8167d82f0f8SDina K Nimeh 	if (fd != -1)
8177d82f0f8SDina K Nimeh 		(void) close(fd);
8187d82f0f8SDina K Nimeh 	return (B_FALSE);
8197d82f0f8SDina K Nimeh }
8207d82f0f8SDina K Nimeh 
8217d82f0f8SDina K Nimeh /*
8227d82f0f8SDina K Nimeh  * Break up token spec into its components (non-destructive)
8237d82f0f8SDina K Nimeh  */
8247d82f0f8SDina K Nimeh static token_spec_t *
8257d82f0f8SDina K Nimeh parsetoken(char *spec)
8267d82f0f8SDina K Nimeh {
8277d82f0f8SDina K Nimeh #define	FLD_NAME	0
8287d82f0f8SDina K Nimeh #define	FLD_MANUF	1
8297d82f0f8SDina K Nimeh #define	FLD_SERIAL	2
8307d82f0f8SDina K Nimeh #define	FLD_LABEL	3
8317d82f0f8SDina K Nimeh #define	NFIELDS		4
8327d82f0f8SDina K Nimeh #define	nullfield(i)	((field[(i)+1] - field[(i)]) <= 1)
8337d82f0f8SDina K Nimeh #define	copyfield(fld, i)	\
8347d82f0f8SDina K Nimeh 		{							\
8357d82f0f8SDina K Nimeh 			int	n;					\
8367d82f0f8SDina K Nimeh 			(fld) = NULL;					\
8377d82f0f8SDina K Nimeh 			if ((n = (field[(i)+1] - field[(i)])) > 1) {	\
8387d82f0f8SDina K Nimeh 				if (((fld) = malloc(n)) != NULL) {	\
8397d82f0f8SDina K Nimeh 					(void) strncpy((fld), field[(i)], n); \
8407d82f0f8SDina K Nimeh 					((fld))[n - 1] = '\0';		\
8417d82f0f8SDina K Nimeh 				}					\
8427d82f0f8SDina K Nimeh 			}						\
8437d82f0f8SDina K Nimeh 		}
8447d82f0f8SDina K Nimeh 
8457d82f0f8SDina K Nimeh 	int	i;
8467d82f0f8SDina K Nimeh 	char	*field[NFIELDS + 1];	/* +1 to catch extra delimiters */
8477d82f0f8SDina K Nimeh 	token_spec_t *ti = NULL;
8487d82f0f8SDina K Nimeh 
8497d82f0f8SDina K Nimeh 	if (spec == NULL)
8507d82f0f8SDina K Nimeh 		return (NULL);
8517d82f0f8SDina K Nimeh 
8527d82f0f8SDina K Nimeh 	/*
8537d82f0f8SDina K Nimeh 	 * Correct format is "[name]:[manuf]:[serial]:key". Can't use
8547d82f0f8SDina K Nimeh 	 * strtok because it treats ":::key" and "key:::" and "key" all
8557d82f0f8SDina K Nimeh 	 * as the same thing, and we can't have the :s compressed away.
8567d82f0f8SDina K Nimeh 	 */
8577d82f0f8SDina K Nimeh 	field[0] = spec;
8587d82f0f8SDina K Nimeh 	for (i = 1; i < NFIELDS + 1; i++) {
8597d82f0f8SDina K Nimeh 		field[i] = strchr(field[i-1], ':');
8607d82f0f8SDina K Nimeh 		if (field[i] == NULL)
8617d82f0f8SDina K Nimeh 			break;
8627d82f0f8SDina K Nimeh 		field[i]++;
8637d82f0f8SDina K Nimeh 	}
8647d82f0f8SDina K Nimeh 	if (i < NFIELDS)		/* not enough fields */
8657d82f0f8SDina K Nimeh 		return (NULL);
8667d82f0f8SDina K Nimeh 	if (field[NFIELDS] != NULL)	/* too many fields */
8677d82f0f8SDina K Nimeh 		return (NULL);
8687d82f0f8SDina K Nimeh 	field[NFIELDS] = strchr(field[NFIELDS-1], '\0') + 1;
8697d82f0f8SDina K Nimeh 
8707d82f0f8SDina K Nimeh 	/* key label can't be empty */
8717d82f0f8SDina K Nimeh 	if (nullfield(FLD_LABEL))
8727d82f0f8SDina K Nimeh 		return (NULL);
8737d82f0f8SDina K Nimeh 
8747d82f0f8SDina K Nimeh 	ti = malloc(sizeof (token_spec_t));
8757d82f0f8SDina K Nimeh 	if (ti == NULL)
8767d82f0f8SDina K Nimeh 		return (NULL);
8777d82f0f8SDina K Nimeh 
8787d82f0f8SDina K Nimeh 	copyfield(ti->name, FLD_NAME);
8797d82f0f8SDina K Nimeh 	copyfield(ti->mfr, FLD_MANUF);
8807d82f0f8SDina K Nimeh 	copyfield(ti->serno, FLD_SERIAL);
8817d82f0f8SDina K Nimeh 	copyfield(ti->key, FLD_LABEL);
8827d82f0f8SDina K Nimeh 
8837d82f0f8SDina K Nimeh 	/*
8847d82f0f8SDina K Nimeh 	 * If token specified and it only contains a key label, then
8857d82f0f8SDina K Nimeh 	 * search all tokens for the key, otherwise only those with
8867d82f0f8SDina K Nimeh 	 * matching name, mfr, and serno are used.
8877d82f0f8SDina K Nimeh 	 */
8887d82f0f8SDina K Nimeh 	/*
8897d82f0f8SDina K Nimeh 	 * That's how we'd like it to be, however, if only the key label
8907d82f0f8SDina K Nimeh 	 * is specified, default to using softtoken.  It's easier.
8917d82f0f8SDina K Nimeh 	 */
8927d82f0f8SDina K Nimeh 	if (ti->name == NULL && ti->mfr == NULL && ti->serno == NULL)
8937d82f0f8SDina K Nimeh 		ti->name = strdup(pkcs11_default_token());
8947d82f0f8SDina K Nimeh 	return (ti);
8957d82f0f8SDina K Nimeh }
8967d82f0f8SDina K Nimeh 
8977d82f0f8SDina K Nimeh /*
8987d82f0f8SDina K Nimeh  * PBE the passphrase into a raw key
8997d82f0f8SDina K Nimeh  */
9007d82f0f8SDina K Nimeh static void
9017d82f0f8SDina K Nimeh getkeyfromuser(mech_alias_t *cipher, char **raw_key, size_t *raw_key_sz)
9027d82f0f8SDina K Nimeh {
9037d82f0f8SDina K Nimeh 	CK_SESSION_HANDLE sess;
9047d82f0f8SDina K Nimeh 	CK_RV	rv;
9057d82f0f8SDina K Nimeh 	char	*pass = NULL;
9067d82f0f8SDina K Nimeh 	size_t	passlen = 0;
9077d82f0f8SDina K Nimeh 	void	*salt = NULL;	/* don't use NULL, see note on salt below */
9087d82f0f8SDina K Nimeh 	size_t	saltlen = 0;
9097d82f0f8SDina K Nimeh 	CK_KEY_TYPE ktype;
9107d82f0f8SDina K Nimeh 	void	*kvalue;
9117d82f0f8SDina K Nimeh 	size_t	klen;
9127d82f0f8SDina K Nimeh 
9137d82f0f8SDina K Nimeh 	/* did init_crypto find a slot that supports this cipher? */
9147d82f0f8SDina K Nimeh 	if (cipher->slot == (CK_SLOT_ID)-1 || cipher->max_keysize == 0) {
9157d82f0f8SDina K Nimeh 		rv = CKR_MECHANISM_INVALID;
9167d82f0f8SDina K Nimeh 		goto cleanup;
9177d82f0f8SDina K Nimeh 	}
9187d82f0f8SDina K Nimeh 
9197d82f0f8SDina K Nimeh 	rv = pkcs11_mech2keytype(cipher->type, &ktype);
9207d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
9217d82f0f8SDina K Nimeh 		goto cleanup;
9227d82f0f8SDina K Nimeh 
9237d82f0f8SDina K Nimeh 	/*
9247d82f0f8SDina K Nimeh 	 * use the passphrase to generate a PBE PKCS#5 secret key and
9257d82f0f8SDina K Nimeh 	 * retrieve the raw key data to eventually pass it to the kernel;
9267d82f0f8SDina K Nimeh 	 */
9277d82f0f8SDina K Nimeh 	rv = C_OpenSession(cipher->slot, CKF_SERIAL_SESSION, NULL, NULL, &sess);
9287d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
9297d82f0f8SDina K Nimeh 		goto cleanup;
9307d82f0f8SDina K Nimeh 
9317d82f0f8SDina K Nimeh 	/* get user passphrase with 8 byte minimum */
9327d82f0f8SDina K Nimeh 	if (pkcs11_get_pass(NULL, &pass, &passlen, MIN_PASSLEN, B_TRUE) < 0) {
9337d82f0f8SDina K Nimeh 		die(gettext("passphrases do not match\n"));
9347d82f0f8SDina K Nimeh 	}
9357d82f0f8SDina K Nimeh 
9367d82f0f8SDina K Nimeh 	/*
9377d82f0f8SDina K Nimeh 	 * salt should not be NULL, or else pkcs11_PasswdToKey() will
9387d82f0f8SDina K Nimeh 	 * complain about CKR_MECHANISM_PARAM_INVALID; the following is
9397d82f0f8SDina K Nimeh 	 * to make up for not having a salt until a proper one is used
9407d82f0f8SDina K Nimeh 	 */
9417d82f0f8SDina K Nimeh 	salt = pass;
9427d82f0f8SDina K Nimeh 	saltlen = passlen;
9437d82f0f8SDina K Nimeh 
9447d82f0f8SDina K Nimeh 	klen = cipher->max_keysize;
9457d82f0f8SDina K Nimeh 	rv = pkcs11_PasswdToKey(sess, pass, passlen, salt, saltlen, ktype,
9467d82f0f8SDina K Nimeh 	    cipher->max_keysize, &kvalue, &klen);
9477d82f0f8SDina K Nimeh 
9487d82f0f8SDina K Nimeh 	(void) C_CloseSession(sess);
9497d82f0f8SDina K Nimeh 
9507d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
9517d82f0f8SDina K Nimeh 		goto cleanup;
9527d82f0f8SDina K Nimeh 	}
9537d82f0f8SDina K Nimeh 
9547d82f0f8SDina K Nimeh 	/* assert(klen == cipher->max_keysize); */
9557d82f0f8SDina K Nimeh 	*raw_key_sz = klen;
9567d82f0f8SDina K Nimeh 	*raw_key = (char *)kvalue;
9577d82f0f8SDina K Nimeh 	return;
9587d82f0f8SDina K Nimeh 
9597d82f0f8SDina K Nimeh cleanup:
9607d82f0f8SDina K Nimeh 	die(gettext("failed to generate %s key from passphrase: %s"),
9617d82f0f8SDina K Nimeh 	    cipher->alias, pkcs11_strerror(rv));
9627d82f0f8SDina K Nimeh }
9637d82f0f8SDina K Nimeh 
9647d82f0f8SDina K Nimeh /*
9657d82f0f8SDina K Nimeh  * Read raw key from file; also handles ephemeral keys.
9667d82f0f8SDina K Nimeh  */
9677d82f0f8SDina K Nimeh void
9687d82f0f8SDina K Nimeh getkeyfromfile(const char *pathname, mech_alias_t *cipher, char **key,
9697d82f0f8SDina K Nimeh     size_t *ksz)
9707d82f0f8SDina K Nimeh {
9717d82f0f8SDina K Nimeh 	int	fd;
9727d82f0f8SDina K Nimeh 	struct stat sbuf;
9737d82f0f8SDina K Nimeh 	boolean_t notplain = B_FALSE;
9747d82f0f8SDina K Nimeh 	ssize_t	cursz;
9757d82f0f8SDina K Nimeh 	ssize_t	nread;
9767d82f0f8SDina K Nimeh 
9777d82f0f8SDina K Nimeh 	/* ephemeral keys are just random data */
9787d82f0f8SDina K Nimeh 	if (pathname == NULL) {
9797d82f0f8SDina K Nimeh 		*ksz = cipher->max_keysize;
9807d82f0f8SDina K Nimeh 		*key = malloc(*ksz);
9817d82f0f8SDina K Nimeh 		if (*key == NULL)
9827d82f0f8SDina K Nimeh 			die(gettext("failed to allocate memory for"
9837d82f0f8SDina K Nimeh 			    " ephemeral key"));
9847b79d846SDina K Nimeh 		if (pkcs11_get_urandom(*key, *ksz) < 0) {
9857d82f0f8SDina K Nimeh 			free(*key);
9867d82f0f8SDina K Nimeh 			die(gettext("failed to get enough random data"));
9877d82f0f8SDina K Nimeh 		}
9887d82f0f8SDina K Nimeh 		return;
9897d82f0f8SDina K Nimeh 	}
9907d82f0f8SDina K Nimeh 
9917d82f0f8SDina K Nimeh 	/*
9927d82f0f8SDina K Nimeh 	 * If the remaining section of code didn't also check for secure keyfile
9937d82f0f8SDina K Nimeh 	 * permissions and whether the key is within cipher min and max lengths,
9947d82f0f8SDina K Nimeh 	 * (or, if those things moved out of this block), we could have had:
9957d82f0f8SDina K Nimeh 	 *	if (pkcs11_read_data(pathname, key, ksz) < 0)
9967d82f0f8SDina K Nimeh 	 *		handle_error();
9977d82f0f8SDina K Nimeh 	 */
9987d82f0f8SDina K Nimeh 
9997d82f0f8SDina K Nimeh 	if ((fd = open(pathname, O_RDONLY, 0)) == -1)
10007d82f0f8SDina K Nimeh 		die(gettext("open of keyfile (%s) failed"), pathname);
10017d82f0f8SDina K Nimeh 
10027d82f0f8SDina K Nimeh 	if (fstat(fd, &sbuf) == -1)
10037d82f0f8SDina K Nimeh 		die(gettext("fstat of keyfile (%s) failed"), pathname);
10047d82f0f8SDina K Nimeh 
10057d82f0f8SDina K Nimeh 	if (S_ISREG(sbuf.st_mode)) {
10067d82f0f8SDina K Nimeh 		if ((sbuf.st_mode & (S_IWGRP | S_IWOTH)) != 0)
10077d82f0f8SDina K Nimeh 			die(gettext("insecure permissions on keyfile %s\n"),
10087d82f0f8SDina K Nimeh 			    pathname);
10097d82f0f8SDina K Nimeh 
10107d82f0f8SDina K Nimeh 		*ksz = sbuf.st_size;
10117d82f0f8SDina K Nimeh 		if (*ksz < cipher->min_keysize || cipher->max_keysize < *ksz) {
10127d82f0f8SDina K Nimeh 			warn(gettext("%s: invalid keysize: %d\n"),
10137d82f0f8SDina K Nimeh 			    pathname, (int)*ksz);
10147d82f0f8SDina K Nimeh 			die(gettext("\t%d <= keysize <= %d\n"),
10157d82f0f8SDina K Nimeh 			    cipher->min_keysize, cipher->max_keysize);
10167d82f0f8SDina K Nimeh 		}
10177d82f0f8SDina K Nimeh 	} else {
10187d82f0f8SDina K Nimeh 		*ksz = cipher->max_keysize;
10197d82f0f8SDina K Nimeh 		notplain = B_TRUE;
10207d82f0f8SDina K Nimeh 	}
10217d82f0f8SDina K Nimeh 
10227d82f0f8SDina K Nimeh 	*key = malloc(*ksz);
10237d82f0f8SDina K Nimeh 	if (*key == NULL)
10247d82f0f8SDina K Nimeh 		die(gettext("failed to allocate memory for key from file"));
10257d82f0f8SDina K Nimeh 
10267d82f0f8SDina K Nimeh 	for (cursz = 0, nread = 0; cursz < *ksz; cursz += nread) {
10277d82f0f8SDina K Nimeh 		nread = read(fd, *key, *ksz);
10287d82f0f8SDina K Nimeh 		if (nread > 0)
10297d82f0f8SDina K Nimeh 			continue;
10307d82f0f8SDina K Nimeh 		/*
10317d82f0f8SDina K Nimeh 		 * nread == 0.  If it's not a regular file we were trying to
10327d82f0f8SDina K Nimeh 		 * get the maximum keysize of data possible for this cipher.
10337d82f0f8SDina K Nimeh 		 * But if we've got at least the minimum keysize of data,
10347d82f0f8SDina K Nimeh 		 * round down to the nearest keysize unit and call it good.
10357d82f0f8SDina K Nimeh 		 * If we haven't met the minimum keysize, that's an error.
10367d82f0f8SDina K Nimeh 		 * If it's a regular file, nread = 0 is also an error.
10377d82f0f8SDina K Nimeh 		 */
10387d82f0f8SDina K Nimeh 		if (nread == 0 && notplain && cursz >= cipher->min_keysize) {
10397d82f0f8SDina K Nimeh 			*ksz = (cursz / cipher->min_keysize) *
10407d82f0f8SDina K Nimeh 			    cipher->min_keysize;
10417d82f0f8SDina K Nimeh 			break;
10427d82f0f8SDina K Nimeh 		}
10437d82f0f8SDina K Nimeh 		die(gettext("%s: can't read all keybytes"), pathname);
10447d82f0f8SDina K Nimeh 	}
10457d82f0f8SDina K Nimeh 	(void) close(fd);
10467d82f0f8SDina K Nimeh }
10477d82f0f8SDina K Nimeh 
10487d82f0f8SDina K Nimeh /*
10497d82f0f8SDina K Nimeh  * Read the raw key from token, or from a file that was wrapped with a
10507d82f0f8SDina K Nimeh  * key from token
10517d82f0f8SDina K Nimeh  */
10527d82f0f8SDina K Nimeh void
10537d82f0f8SDina K Nimeh getkeyfromtoken(CK_SESSION_HANDLE sess,
10547d82f0f8SDina K Nimeh     token_spec_t *token, const char *keyfile, mech_alias_t *cipher,
10557d82f0f8SDina K Nimeh     char **raw_key, size_t *raw_key_sz)
10567d82f0f8SDina K Nimeh {
10577d82f0f8SDina K Nimeh 	CK_RV	rv = CKR_OK;
10587d82f0f8SDina K Nimeh 	CK_BBOOL trueval = B_TRUE;
10597d82f0f8SDina K Nimeh 	CK_OBJECT_CLASS kclass;		/* secret key or RSA private key */
10607d82f0f8SDina K Nimeh 	CK_KEY_TYPE ktype;		/* from selected cipher or CKK_RSA */
10617d82f0f8SDina K Nimeh 	CK_KEY_TYPE raw_ktype;		/* from selected cipher */
10627d82f0f8SDina K Nimeh 	CK_ATTRIBUTE	key_tmpl[] = {
10637d82f0f8SDina K Nimeh 		{ CKA_CLASS, NULL, 0 },	/* re-used for token key and unwrap */
10647d82f0f8SDina K Nimeh 		{ CKA_KEY_TYPE, NULL, 0 },	/* ditto */
10657d82f0f8SDina K Nimeh 		{ CKA_LABEL, NULL, 0 },
10667d82f0f8SDina K Nimeh 		{ CKA_TOKEN, NULL, 0 },
10677d82f0f8SDina K Nimeh 		{ CKA_PRIVATE, NULL, 0 }
10687d82f0f8SDina K Nimeh 	    };
10697d82f0f8SDina K Nimeh 	CK_ULONG attrs = sizeof (key_tmpl) / sizeof (CK_ATTRIBUTE);
10707d82f0f8SDina K Nimeh 	int	i;
10717d82f0f8SDina K Nimeh 	char	*pass = NULL;
10727d82f0f8SDina K Nimeh 	size_t	passlen = 0;
10737d82f0f8SDina K Nimeh 	CK_OBJECT_HANDLE obj, rawobj;
10747d82f0f8SDina K Nimeh 	CK_ULONG num_objs = 1;		/* just want to find 1 token key */
10757d82f0f8SDina K Nimeh 	CK_MECHANISM unwrap = { CKM_RSA_PKCS, NULL, 0 };
10767d82f0f8SDina K Nimeh 	char	*rkey;
10777d82f0f8SDina K Nimeh 	size_t	rksz;
10787d82f0f8SDina K Nimeh 
10797d82f0f8SDina K Nimeh 	if (token == NULL || token->key == NULL)
10807d82f0f8SDina K Nimeh 		return;
10817d82f0f8SDina K Nimeh 
10827d82f0f8SDina K Nimeh 	/* did init_crypto find a slot that supports this cipher? */
10837d82f0f8SDina K Nimeh 	if (cipher->slot == (CK_SLOT_ID)-1 || cipher->max_keysize == 0) {
10847d82f0f8SDina K Nimeh 		die(gettext("failed to find any cryptographic provider, "
10857d82f0f8SDina K Nimeh 		    "use \"cryptoadm list -p\" to find providers: %s\n"),
10867d82f0f8SDina K Nimeh 		    pkcs11_strerror(CKR_MECHANISM_INVALID));
10877d82f0f8SDina K Nimeh 	}
10887d82f0f8SDina K Nimeh 
10897d82f0f8SDina K Nimeh 	if (pkcs11_get_pass(token->name, &pass, &passlen, 0, B_FALSE) < 0)
10907d82f0f8SDina K Nimeh 		die(gettext("unable to get passphrase"));
10917d82f0f8SDina K Nimeh 
10927d82f0f8SDina K Nimeh 	/* use passphrase to login to token */
10937d82f0f8SDina K Nimeh 	if (pass != NULL && passlen > 0) {
10947d82f0f8SDina K Nimeh 		rv = C_Login(sess, CKU_USER, (CK_UTF8CHAR_PTR)pass, passlen);
10957d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
10967d82f0f8SDina K Nimeh 			die(gettext("cannot login to the token %s: %s\n"),
10977d82f0f8SDina K Nimeh 			    token->name, pkcs11_strerror(rv));
10987d82f0f8SDina K Nimeh 		}
10997d82f0f8SDina K Nimeh 	}
11007d82f0f8SDina K Nimeh 
11017d82f0f8SDina K Nimeh 	rv = pkcs11_mech2keytype(cipher->type, &raw_ktype);
11027d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
11037d82f0f8SDina K Nimeh 		die(gettext("failed to get key type for cipher %s: %s\n"),
11047d82f0f8SDina K Nimeh 		    cipher->name, pkcs11_strerror(rv));
11057d82f0f8SDina K Nimeh 	}
11067d82f0f8SDina K Nimeh 
11077d82f0f8SDina K Nimeh 	/*
11087d82f0f8SDina K Nimeh 	 * If no keyfile was given, then the token key is secret key to
11097d82f0f8SDina K Nimeh 	 * be used for encryption/decryption.  Otherwise, the keyfile
11107d82f0f8SDina K Nimeh 	 * contains a wrapped secret key, and the token is actually the
11117d82f0f8SDina K Nimeh 	 * unwrapping RSA private key.
11127d82f0f8SDina K Nimeh 	 */
11137d82f0f8SDina K Nimeh 	if (keyfile == NULL) {
11147d82f0f8SDina K Nimeh 		kclass = CKO_SECRET_KEY;
11157d82f0f8SDina K Nimeh 		ktype = raw_ktype;
11167d82f0f8SDina K Nimeh 	} else {
11177d82f0f8SDina K Nimeh 		kclass = CKO_PRIVATE_KEY;
11187d82f0f8SDina K Nimeh 		ktype = CKK_RSA;
11197d82f0f8SDina K Nimeh 	}
11207d82f0f8SDina K Nimeh 
11217d82f0f8SDina K Nimeh 	/* Find the key in the token first */
11227d82f0f8SDina K Nimeh 	for (i = 0; i < attrs; i++) {
11237d82f0f8SDina K Nimeh 		switch (key_tmpl[i].type) {
11247d82f0f8SDina K Nimeh 		case CKA_CLASS:
11257d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &kclass;
11267d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (kclass);
11277d82f0f8SDina K Nimeh 			break;
11287d82f0f8SDina K Nimeh 		case CKA_KEY_TYPE:
11297d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &ktype;
11307d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (ktype);
11317d82f0f8SDina K Nimeh 			break;
11327d82f0f8SDina K Nimeh 		case CKA_LABEL:
11337d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = token->key;
11347d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = strlen(token->key);
11357d82f0f8SDina K Nimeh 			break;
11367d82f0f8SDina K Nimeh 		case CKA_TOKEN:
11377d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &trueval;
11387d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (trueval);
11397d82f0f8SDina K Nimeh 			break;
11407d82f0f8SDina K Nimeh 		case CKA_PRIVATE:
11417d82f0f8SDina K Nimeh 			key_tmpl[i].pValue = &trueval;
11427d82f0f8SDina K Nimeh 			key_tmpl[i].ulValueLen = sizeof (trueval);
11437d82f0f8SDina K Nimeh 			break;
11447d82f0f8SDina K Nimeh 		default:
11457d82f0f8SDina K Nimeh 			break;
11467d82f0f8SDina K Nimeh 		}
11477d82f0f8SDina K Nimeh 	}
11487d82f0f8SDina K Nimeh 	rv = C_FindObjectsInit(sess, key_tmpl, attrs);
11497d82f0f8SDina K Nimeh 	if (rv != CKR_OK)
11507d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s: %s\n"), token->key,
11517d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
11527d82f0f8SDina K Nimeh 	rv = C_FindObjects(sess, &obj, 1, &num_objs);
11537d82f0f8SDina K Nimeh 	(void) C_FindObjectsFinal(sess);
11547d82f0f8SDina K Nimeh 
11557d82f0f8SDina K Nimeh 	if (num_objs == 0) {
11567d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s\n"), token->key);
11577d82f0f8SDina K Nimeh 	} else if (rv != CKR_OK) {
11587d82f0f8SDina K Nimeh 		die(gettext("cannot find key %s: %s\n"), token->key,
11597d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
11607d82f0f8SDina K Nimeh 	}
11617d82f0f8SDina K Nimeh 
11627d82f0f8SDina K Nimeh 	/*
11637d82f0f8SDina K Nimeh 	 * No keyfile means when token key is found, convert it to raw key,
11647d82f0f8SDina K Nimeh 	 * and done.  Otherwise still need do an unwrap to create yet another
11657d82f0f8SDina K Nimeh 	 * obj and that needs to be converted to raw key before we're done.
11667d82f0f8SDina K Nimeh 	 */
11677d82f0f8SDina K Nimeh 	if (keyfile == NULL) {
11687d82f0f8SDina K Nimeh 		/* obj contains raw key, extract it */
11697d82f0f8SDina K Nimeh 		rv = pkcs11_ObjectToKey(sess, obj, (void **)&rkey, &rksz,
11707d82f0f8SDina K Nimeh 		    B_FALSE);
11717d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
11727d82f0f8SDina K Nimeh 			die(gettext("failed to get key value for %s"
11737d82f0f8SDina K Nimeh 			    " from token %s, %s\n"), token->key,
11747d82f0f8SDina K Nimeh 			    token->name, pkcs11_strerror(rv));
11757d82f0f8SDina K Nimeh 		}
11767d82f0f8SDina K Nimeh 	} else {
11777d82f0f8SDina K Nimeh 		getkeyfromfile(keyfile, cipher, &rkey, &rksz);
11787d82f0f8SDina K Nimeh 
11797d82f0f8SDina K Nimeh 		/*
11807d82f0f8SDina K Nimeh 		 * Got the wrapping RSA obj and the wrapped key from file.
11817d82f0f8SDina K Nimeh 		 * Unwrap the key from file with RSA obj to get rawkey obj.
11827d82f0f8SDina K Nimeh 		 */
11837d82f0f8SDina K Nimeh 
11847d82f0f8SDina K Nimeh 		/* re-use the first two attributes of key_tmpl */
11857d82f0f8SDina K Nimeh 		kclass = CKO_SECRET_KEY;
11867d82f0f8SDina K Nimeh 		ktype = raw_ktype;
11877d82f0f8SDina K Nimeh 
11887d82f0f8SDina K Nimeh 		rv = C_UnwrapKey(sess, &unwrap, obj, (CK_BYTE_PTR)rkey,
11897d82f0f8SDina K Nimeh 		    rksz, key_tmpl, 2, &rawobj);
11907d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
11917d82f0f8SDina K Nimeh 			die(gettext("failed to unwrap key in keyfile %s,"
11927d82f0f8SDina K Nimeh 			    " %s\n"), keyfile, pkcs11_strerror(rv));
11937d82f0f8SDina K Nimeh 		}
11947d82f0f8SDina K Nimeh 		/* rawobj contains raw key, extract it */
11957d82f0f8SDina K Nimeh 		rv = pkcs11_ObjectToKey(sess, rawobj, (void **)&rkey, &rksz,
11967d82f0f8SDina K Nimeh 		    B_TRUE);
11977d82f0f8SDina K Nimeh 		if (rv != CKR_OK) {
11987d82f0f8SDina K Nimeh 			die(gettext("failed to get unwrapped key value for"
11997d82f0f8SDina K Nimeh 			    " key in keyfile %s, %s\n"), keyfile,
12007d82f0f8SDina K Nimeh 			    pkcs11_strerror(rv));
12017d82f0f8SDina K Nimeh 		}
12027d82f0f8SDina K Nimeh 	}
12037d82f0f8SDina K Nimeh 
12047d82f0f8SDina K Nimeh 	/* validate raw key size */
12057d82f0f8SDina K Nimeh 	if (rksz < cipher->min_keysize || cipher->max_keysize < rksz) {
12067d82f0f8SDina K Nimeh 		warn(gettext("%s: invalid keysize: %d\n"), keyfile, (int)rksz);
12077d82f0f8SDina K Nimeh 		die(gettext("\t%d <= keysize <= %d\n"), cipher->min_keysize,
12087d82f0f8SDina K Nimeh 		    cipher->max_keysize);
12097d82f0f8SDina K Nimeh 	}
12107d82f0f8SDina K Nimeh 
12117d82f0f8SDina K Nimeh 	*raw_key_sz = rksz;
12127d82f0f8SDina K Nimeh 	*raw_key = (char *)rkey;
12137d82f0f8SDina K Nimeh }
12147d82f0f8SDina K Nimeh 
12157d82f0f8SDina K Nimeh /*
12167d82f0f8SDina K Nimeh  * Set up cipher key limits and verify PKCS#11 can be done
12177d82f0f8SDina K Nimeh  * match_token_cipher is the function pointer used by
12187d82f0f8SDina K Nimeh  * pkcs11_GetCriteriaSession() init_crypto.
12197d82f0f8SDina K Nimeh  */
12207d82f0f8SDina K Nimeh boolean_t
12217d82f0f8SDina K Nimeh match_token_cipher(CK_SLOT_ID slot_id, void *args, CK_RV *rv)
12227d82f0f8SDina K Nimeh {
12237d82f0f8SDina K Nimeh 	token_spec_t *token;
12247d82f0f8SDina K Nimeh 	mech_alias_t *cipher;
12257d82f0f8SDina K Nimeh 	CK_TOKEN_INFO tokinfo;
12267d82f0f8SDina K Nimeh 	CK_MECHANISM_INFO mechinfo;
12277d82f0f8SDina K Nimeh 	boolean_t token_match;
12287d82f0f8SDina K Nimeh 
12297d82f0f8SDina K Nimeh 	/*
12307d82f0f8SDina K Nimeh 	 * While traversing slot list, pick up the following info per slot:
12317d82f0f8SDina K Nimeh 	 * - if token specified, whether it matches this slot's token info
12327d82f0f8SDina K Nimeh 	 * - if the slot supports the PKCS#5 PBKD2 cipher
12337d82f0f8SDina K Nimeh 	 *
12347d82f0f8SDina K Nimeh 	 * If the user said on the command line
12357d82f0f8SDina K Nimeh 	 *	-T tok:mfr:ser:lab -k keyfile
12367d82f0f8SDina K Nimeh 	 *	-c cipher -T tok:mfr:ser:lab -k keyfile
12377d82f0f8SDina K Nimeh 	 * the given cipher or the default cipher apply to keyfile,
12387d82f0f8SDina K Nimeh 	 * If the user said instead
12397d82f0f8SDina K Nimeh 	 *	-T tok:mfr:ser:lab
12407d82f0f8SDina K Nimeh 	 *	-c cipher -T tok:mfr:ser:lab
12417d82f0f8SDina K Nimeh 	 * the key named "lab" may or may not agree with the given
12427d82f0f8SDina K Nimeh 	 * cipher or the default cipher.  In those cases, cipher will
12437d82f0f8SDina K Nimeh 	 * be overridden with the actual cipher type of the key "lab".
12447d82f0f8SDina K Nimeh 	 */
12457d82f0f8SDina K Nimeh 	*rv = CKR_FUNCTION_FAILED;
12467d82f0f8SDina K Nimeh 
12477d82f0f8SDina K Nimeh 	if (args == NULL) {
12487d82f0f8SDina K Nimeh 		return (B_FALSE);
12497d82f0f8SDina K Nimeh 	}
12507d82f0f8SDina K Nimeh 
12517d82f0f8SDina K Nimeh 	cipher = (mech_alias_t *)args;
12527d82f0f8SDina K Nimeh 	token = cipher->token;
12537d82f0f8SDina K Nimeh 
12547d82f0f8SDina K Nimeh 	if (C_GetMechanismInfo(slot_id, cipher->type, &mechinfo) != CKR_OK) {
12557d82f0f8SDina K Nimeh 		return (B_FALSE);
12567d82f0f8SDina K Nimeh 	}
12577d82f0f8SDina K Nimeh 
12587d82f0f8SDina K Nimeh 	if (token == NULL) {
12597d82f0f8SDina K Nimeh 		if (C_GetMechanismInfo(slot_id, CKM_PKCS5_PBKD2, &mechinfo) !=
12607d82f0f8SDina K Nimeh 		    CKR_OK) {
12617d82f0f8SDina K Nimeh 			return (B_FALSE);
12627d82f0f8SDina K Nimeh 		}
12637d82f0f8SDina K Nimeh 		goto foundit;
12647d82f0f8SDina K Nimeh 	}
12657d82f0f8SDina K Nimeh 
12667d82f0f8SDina K Nimeh 	/* does the token match the token spec? */
12677d82f0f8SDina K Nimeh 	if (token->key == NULL || (C_GetTokenInfo(slot_id, &tokinfo) != CKR_OK))
12687d82f0f8SDina K Nimeh 		return (B_FALSE);
12697d82f0f8SDina K Nimeh 
12707d82f0f8SDina K Nimeh 	token_match = B_TRUE;
12717d82f0f8SDina K Nimeh 
12727d82f0f8SDina K Nimeh 	if (token->name != NULL && (token->name)[0] != '\0' &&
12737d82f0f8SDina K Nimeh 	    strncmp((char *)token->name, (char *)tokinfo.label,
12747d82f0f8SDina K Nimeh 	    TOKEN_LABEL_SIZE) != 0)
12757d82f0f8SDina K Nimeh 		token_match = B_FALSE;
12767d82f0f8SDina K Nimeh 	if (token->mfr != NULL && (token->mfr)[0] != '\0' &&
12777d82f0f8SDina K Nimeh 	    strncmp((char *)token->mfr, (char *)tokinfo.manufacturerID,
12787d82f0f8SDina K Nimeh 	    TOKEN_MANUFACTURER_SIZE) != 0)
12797d82f0f8SDina K Nimeh 		token_match = B_FALSE;
12807d82f0f8SDina K Nimeh 	if (token->serno != NULL && (token->serno)[0] != '\0' &&
12817d82f0f8SDina K Nimeh 	    strncmp((char *)token->serno, (char *)tokinfo.serialNumber,
12827d82f0f8SDina K Nimeh 	    TOKEN_SERIAL_SIZE) != 0)
12837d82f0f8SDina K Nimeh 		token_match = B_FALSE;
12847d82f0f8SDina K Nimeh 
12857d82f0f8SDina K Nimeh 	if (!token_match)
12867d82f0f8SDina K Nimeh 		return (B_FALSE);
12877d82f0f8SDina K Nimeh 
12887d82f0f8SDina K Nimeh foundit:
12897d82f0f8SDina K Nimeh 	cipher->slot = slot_id;
12907d82f0f8SDina K Nimeh 	return (B_TRUE);
12917d82f0f8SDina K Nimeh }
12927d82f0f8SDina K Nimeh 
12937d82f0f8SDina K Nimeh /*
12947d82f0f8SDina K Nimeh  * Clean up crypto loose ends
12957d82f0f8SDina K Nimeh  */
12967d82f0f8SDina K Nimeh static void
12977d82f0f8SDina K Nimeh end_crypto(CK_SESSION_HANDLE sess)
12987d82f0f8SDina K Nimeh {
12997d82f0f8SDina K Nimeh 	(void) C_CloseSession(sess);
13007d82f0f8SDina K Nimeh 	(void) C_Finalize(NULL);
13017d82f0f8SDina K Nimeh }
13027d82f0f8SDina K Nimeh 
13037d82f0f8SDina K Nimeh /*
13047d82f0f8SDina K Nimeh  * Set up crypto, opening session on slot that matches token and cipher
13057d82f0f8SDina K Nimeh  */
13067d82f0f8SDina K Nimeh static void
13077d82f0f8SDina K Nimeh init_crypto(token_spec_t *token, mech_alias_t *cipher,
13087d82f0f8SDina K Nimeh     CK_SESSION_HANDLE_PTR sess)
13097d82f0f8SDina K Nimeh {
13107d82f0f8SDina K Nimeh 	CK_RV	rv;
13117d82f0f8SDina K Nimeh 
13127d82f0f8SDina K Nimeh 	cipher->token = token;
13137d82f0f8SDina K Nimeh 
13147d82f0f8SDina K Nimeh 	/* Turn off Metaslot so that we can see actual tokens */
13157d82f0f8SDina K Nimeh 	if (setenv("METASLOT_ENABLED", "false", 1) < 0) {
13167d82f0f8SDina K Nimeh 		die(gettext("could not disable Metaslot"));
13177d82f0f8SDina K Nimeh 	}
13187d82f0f8SDina K Nimeh 
13197d82f0f8SDina K Nimeh 	rv = pkcs11_GetCriteriaSession(match_token_cipher, (void *)cipher,
13207d82f0f8SDina K Nimeh 	    sess);
13217d82f0f8SDina K Nimeh 	if (rv != CKR_OK) {
13227d82f0f8SDina K Nimeh 		end_crypto(*sess);
13237d82f0f8SDina K Nimeh 		if (rv == CKR_HOST_MEMORY) {
13247d82f0f8SDina K Nimeh 			die("malloc");
13257d82f0f8SDina K Nimeh 		}
13267d82f0f8SDina K Nimeh 		die(gettext("failed to find any cryptographic provider, "
13277d82f0f8SDina K Nimeh 		    "use \"cryptoadm list -p\" to find providers: %s\n"),
13287d82f0f8SDina K Nimeh 		    pkcs11_strerror(rv));
13297d82f0f8SDina K Nimeh 	}
13307d82f0f8SDina K Nimeh }
13317d82f0f8SDina K Nimeh 
13327d82f0f8SDina K Nimeh /*
133387117650Saalok  * Uncompress a file.
133487117650Saalok  *
133587117650Saalok  * First map the file in to establish a device
133687117650Saalok  * association, then read from it. On-the-fly
133787117650Saalok  * decompression will automatically uncompress
133887117650Saalok  * the file if it's compressed
133987117650Saalok  *
134087117650Saalok  * If the file is mapped and a device association
134187117650Saalok  * has been established, disallow uncompressing
134287117650Saalok  * the file until it is unmapped.
134387117650Saalok  */
134487117650Saalok static void
134587117650Saalok lofi_uncompress(int lfd, const char *filename)
13467c478bd9Sstevel@tonic-gate {
134787117650Saalok 	struct lofi_ioctl li;
134887117650Saalok 	char buf[MAXBSIZE];
134987117650Saalok 	char devicename[32];
135087117650Saalok 	char tmpfilename[MAXPATHLEN];
13517d82f0f8SDina K Nimeh 	char *x;
135287117650Saalok 	char *dir = NULL;
135387117650Saalok 	char *file = NULL;
135487117650Saalok 	int minor = 0;
135587117650Saalok 	struct stat64 statbuf;
135687117650Saalok 	int compfd = -1;
135787117650Saalok 	int uncompfd = -1;
135887117650Saalok 	ssize_t rbytes;
135987117650Saalok 
136087117650Saalok 	/*
136187117650Saalok 	 * Disallow uncompressing the file if it is
136287117650Saalok 	 * already mapped.
136387117650Saalok 	 */
1364789dced6SJerry Jelinek 	li.li_crypto_enabled = B_FALSE;
1365*a29e56d9SToomas Soome 	li.li_id = 0;
136687117650Saalok 	(void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
136787117650Saalok 	if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1)
136887117650Saalok 		die(gettext("%s must be unmapped before uncompressing"),
136987117650Saalok 		    filename);
137087117650Saalok 
137187117650Saalok 	/* Zero length files don't need to be uncompressed */
137287117650Saalok 	if (stat64(filename, &statbuf) == -1)
137387117650Saalok 		die(gettext("stat: %s"), filename);
137487117650Saalok 	if (statbuf.st_size == 0)
137587117650Saalok 		return;
137687117650Saalok 
1377*a29e56d9SToomas Soome 	minor = lofi_map_file(lfd, &li, filename);
137887117650Saalok 	(void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d",
137987117650Saalok 	    LOFI_BLOCK_NAME, minor);
138087117650Saalok 
138187117650Saalok 	/* If the file isn't compressed, we just return */
138287117650Saalok 	if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) ||
1383a423e759Saalok 	    (li.li_algorithm[0] == '\0')) {
138487117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1385a423e759Saalok 		die("%s is not compressed\n", filename);
138687117650Saalok 	}
138787117650Saalok 
138887117650Saalok 	if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) {
138987117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
139087117650Saalok 		die(gettext("open: %s"), filename);
139187117650Saalok 	}
139287117650Saalok 	/* Create a temp file in the same directory */
13937d82f0f8SDina K Nimeh 	x = strdup(filename);
13947d82f0f8SDina K Nimeh 	dir = strdup(dirname(x));
13957d82f0f8SDina K Nimeh 	free(x);
13967d82f0f8SDina K Nimeh 	x = strdup(filename);
13977d82f0f8SDina K Nimeh 	file = strdup(basename(x));
13987d82f0f8SDina K Nimeh 	free(x);
139987117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
140087117650Saalok 	    "%s/.%sXXXXXX", dir, file);
14017d82f0f8SDina K Nimeh 	free(dir);
14027d82f0f8SDina K Nimeh 	free(file);
140387117650Saalok 
140487117650Saalok 	if ((uncompfd = mkstemp64(tmpfilename)) == -1) {
140587117650Saalok 		(void) close(compfd);
140687117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1407a423e759Saalok 		die("%s could not be uncompressed\n", filename);
140887117650Saalok 	}
140987117650Saalok 
141087117650Saalok 	/*
141187117650Saalok 	 * Set the mode bits and the owner of this temporary
141287117650Saalok 	 * file to be that of the original uncompressed file
141387117650Saalok 	 */
141487117650Saalok 	(void) fchmod(uncompfd, statbuf.st_mode);
141587117650Saalok 
141687117650Saalok 	if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) {
141787117650Saalok 		(void) close(compfd);
141887117650Saalok 		(void) close(uncompfd);
141987117650Saalok 		delete_mapping(lfd, devicename, filename, B_TRUE);
1420a423e759Saalok 		die("%s could not be uncompressed\n", filename);
142187117650Saalok 	}
142287117650Saalok 
142387117650Saalok 	/* Now read from the device in MAXBSIZE-sized chunks */
142487117650Saalok 	for (;;) {
142587117650Saalok 		rbytes = read(compfd, buf, sizeof (buf));
142687117650Saalok 
142787117650Saalok 		if (rbytes <= 0)
142887117650Saalok 			break;
142987117650Saalok 
143087117650Saalok 		if (write(uncompfd, buf, rbytes) != rbytes) {
143187117650Saalok 			rbytes = -1;
143287117650Saalok 			break;
143387117650Saalok 		}
143487117650Saalok 	}
143587117650Saalok 
143687117650Saalok 	(void) close(compfd);
143787117650Saalok 	(void) close(uncompfd);
143887117650Saalok 
143987117650Saalok 	/* Delete the mapping */
144087117650Saalok 	delete_mapping(lfd, devicename, filename, B_TRUE);
144187117650Saalok 
144287117650Saalok 	/*
144387117650Saalok 	 * If an error occured while reading or writing, rbytes will
144487117650Saalok 	 * be negative
144587117650Saalok 	 */
144687117650Saalok 	if (rbytes < 0) {
144787117650Saalok 		(void) unlink(tmpfilename);
144887117650Saalok 		die(gettext("could not read from %s"), filename);
144987117650Saalok 	}
145087117650Saalok 
145187117650Saalok 	/* Rename the temp file to the actual file */
145287117650Saalok 	if (rename(tmpfilename, filename) == -1)
145387117650Saalok 		(void) unlink(tmpfilename);
145487117650Saalok }
145587117650Saalok 
145687117650Saalok /*
145787117650Saalok  * Compress a file
145887117650Saalok  */
145987117650Saalok static void
14609525893dSaalok lofi_compress(int *lfd, const char *filename, int compress_index,
146187117650Saalok     uint32_t segsize)
146287117650Saalok {
146387117650Saalok 	struct lofi_ioctl lic;
146487117650Saalok 	lofi_compress_info_t *li;
14659525893dSaalok 	struct flock lock;
146687117650Saalok 	char tmpfilename[MAXPATHLEN];
146787117650Saalok 	char comp_filename[MAXPATHLEN];
146887117650Saalok 	char algorithm[MAXALGLEN];
14697d82f0f8SDina K Nimeh 	char *x;
147087117650Saalok 	char *dir = NULL, *file = NULL;
147187117650Saalok 	uchar_t *uncompressed_seg = NULL;
147287117650Saalok 	uchar_t *compressed_seg = NULL;
147387117650Saalok 	uint32_t compressed_segsize;
147487117650Saalok 	uint32_t len_compressed, count;
147587117650Saalok 	uint32_t index_entries, index_sz;
147687117650Saalok 	uint64_t *index = NULL;
147787117650Saalok 	uint64_t offset;
147887117650Saalok 	size_t real_segsize;
147987117650Saalok 	struct stat64 statbuf;
148087117650Saalok 	int compfd = -1, uncompfd = -1;
148187117650Saalok 	int tfd = -1;
148287117650Saalok 	ssize_t rbytes, wbytes, lastread;
148387117650Saalok 	int i, type;
148487117650Saalok 
148587117650Saalok 	/*
148687117650Saalok 	 * Disallow compressing the file if it is
148787117650Saalok 	 * already mapped
148887117650Saalok 	 */
1489*a29e56d9SToomas Soome 	lic.li_id = 0;
149087117650Saalok 	(void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
14919525893dSaalok 	if (ioctl(*lfd, LOFI_GET_MINOR, &lic) != -1)
149287117650Saalok 		die(gettext("%s must be unmapped before compressing"),
149387117650Saalok 		    filename);
149487117650Saalok 
14959525893dSaalok 	/*
14969525893dSaalok 	 * Close the control device so other operations
14979525893dSaalok 	 * can use it
14989525893dSaalok 	 */
14999525893dSaalok 	(void) close(*lfd);
15009525893dSaalok 	*lfd = -1;
15019525893dSaalok 
150287117650Saalok 	li = &lofi_compress_table[compress_index];
150387117650Saalok 
150487117650Saalok 	/*
150587117650Saalok 	 * The size of the buffer to hold compressed data must
150687117650Saalok 	 * be slightly larger than the compressed segment size.
150787117650Saalok 	 *
150887117650Saalok 	 * The compress functions use part of the buffer as
150987117650Saalok 	 * scratch space to do calculations.
151087117650Saalok 	 * Ref: http://www.zlib.net/manual.html#compress2
151187117650Saalok 	 */
151287117650Saalok 	compressed_segsize = segsize + (segsize >> 6);
151387117650Saalok 	compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
151487117650Saalok 	uncompressed_seg = (uchar_t *)malloc(segsize);
151587117650Saalok 
151687117650Saalok 	if (compressed_seg == NULL || uncompressed_seg == NULL)
151787117650Saalok 		die(gettext("No memory"));
151887117650Saalok 
15199525893dSaalok 	if ((uncompfd = open64(filename, O_RDWR|O_LARGEFILE, 0)) == -1)
152087117650Saalok 		die(gettext("open: %s"), filename);
152187117650Saalok 
15229525893dSaalok 	lock.l_type = F_WRLCK;
15239525893dSaalok 	lock.l_whence = SEEK_SET;
15249525893dSaalok 	lock.l_start = 0;
15259525893dSaalok 	lock.l_len = 0;
15269525893dSaalok 
15279525893dSaalok 	/*
15289525893dSaalok 	 * Use an advisory lock to ensure that only a
15299525893dSaalok 	 * single lofiadm process compresses a given
15309525893dSaalok 	 * file at any given time
15319525893dSaalok 	 *
15329525893dSaalok 	 * A close on the file descriptor automatically
15339525893dSaalok 	 * closes all lock state on the file
15349525893dSaalok 	 */
15359525893dSaalok 	if (fcntl(uncompfd, F_SETLKW, &lock) == -1)
15369525893dSaalok 		die(gettext("fcntl: %s"), filename);
15379525893dSaalok 
153887117650Saalok 	if (fstat64(uncompfd, &statbuf) == -1) {
153987117650Saalok 		(void) close(uncompfd);
154087117650Saalok 		die(gettext("fstat: %s"), filename);
154187117650Saalok 	}
154287117650Saalok 
154387117650Saalok 	/* Zero length files don't need to be compressed */
154487117650Saalok 	if (statbuf.st_size == 0) {
154587117650Saalok 		(void) close(uncompfd);
154687117650Saalok 		return;
154787117650Saalok 	}
154887117650Saalok 
154987117650Saalok 	/*
155087117650Saalok 	 * Create temporary files in the same directory that
155187117650Saalok 	 * will hold the intermediate data
155287117650Saalok 	 */
15537d82f0f8SDina K Nimeh 	x = strdup(filename);
15547d82f0f8SDina K Nimeh 	dir = strdup(dirname(x));
15557d82f0f8SDina K Nimeh 	free(x);
15567d82f0f8SDina K Nimeh 	x = strdup(filename);
15577d82f0f8SDina K Nimeh 	file = strdup(basename(x));
15587d82f0f8SDina K Nimeh 	free(x);
155987117650Saalok 	(void) snprintf(tmpfilename, sizeof (tmpfilename),
156087117650Saalok 	    "%s/.%sXXXXXX", dir, file);
156187117650Saalok 	(void) snprintf(comp_filename, sizeof (comp_filename),
156287117650Saalok 	    "%s/.%sXXXXXX", dir, file);
15637d82f0f8SDina K Nimeh 	free(dir);
15647d82f0f8SDina K Nimeh 	free(file);
156587117650Saalok 
156687117650Saalok 	if ((tfd = mkstemp64(tmpfilename)) == -1)
156787117650Saalok 		goto cleanup;
156887117650Saalok 
156987117650Saalok 	if ((compfd = mkstemp64(comp_filename)) == -1)
157087117650Saalok 		goto cleanup;
157187117650Saalok 
157287117650Saalok 	/*
157387117650Saalok 	 * Set the mode bits and owner of the compressed
157487117650Saalok 	 * file to be that of the original uncompressed file
157587117650Saalok 	 */
157687117650Saalok 	(void) fchmod(compfd, statbuf.st_mode);
157787117650Saalok 
157887117650Saalok 	if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
157987117650Saalok 		goto cleanup;
158087117650Saalok 
158187117650Saalok 	/*
158287117650Saalok 	 * Calculate the number of index entries required.
158387117650Saalok 	 * index entries are stored as an array. adding
158487117650Saalok 	 * a '2' here accounts for the fact that the last
158587117650Saalok 	 * segment may not be a multiple of the segment size
158687117650Saalok 	 */
158787117650Saalok 	index_sz = (statbuf.st_size / segsize) + 2;
158887117650Saalok 	index = malloc(sizeof (*index) * index_sz);
158987117650Saalok 
159087117650Saalok 	if (index == NULL)
159187117650Saalok 		goto cleanup;
159287117650Saalok 
159387117650Saalok 	offset = 0;
159487117650Saalok 	lastread = segsize;
159587117650Saalok 	count = 0;
159687117650Saalok 
159787117650Saalok 	/*
159887117650Saalok 	 * Now read from the uncompressed file in 'segsize'
159987117650Saalok 	 * sized chunks, compress what was read in and
160087117650Saalok 	 * write it out to a temporary file
160187117650Saalok 	 */
160287117650Saalok 	for (;;) {
160387117650Saalok 		rbytes = read(uncompfd, uncompressed_seg, segsize);
160487117650Saalok 
160587117650Saalok 		if (rbytes <= 0)
160687117650Saalok 			break;
160787117650Saalok 
160887117650Saalok 		if (lastread < segsize)
160987117650Saalok 			goto cleanup;
161087117650Saalok 
161187117650Saalok 		/*
161287117650Saalok 		 * Account for the first byte that
161387117650Saalok 		 * indicates whether a segment is
161487117650Saalok 		 * compressed or not
161587117650Saalok 		 */
161687117650Saalok 		real_segsize = segsize - 1;
161787117650Saalok 		(void) li->l_compress(uncompressed_seg, rbytes,
161887117650Saalok 		    compressed_seg + SEGHDR, &real_segsize, li->l_level);
161987117650Saalok 
162087117650Saalok 		/*
162187117650Saalok 		 * If the length of the compressed data is more
162287117650Saalok 		 * than a threshold then there isn't any benefit
162387117650Saalok 		 * to be had from compressing this segment - leave
162487117650Saalok 		 * it uncompressed.
162587117650Saalok 		 *
162687117650Saalok 		 * NB. In case an error occurs during compression (above)
162787117650Saalok 		 * the 'real_segsize' isn't changed. The logic below
162887117650Saalok 		 * ensures that that segment is left uncompressed.
162987117650Saalok 		 */
163087117650Saalok 		len_compressed = real_segsize;
16314058a205Sjrgn.keil@googlemail.com 		if (segsize <= COMPRESS_THRESHOLD ||
16324058a205Sjrgn.keil@googlemail.com 		    real_segsize > (segsize - COMPRESS_THRESHOLD)) {
163387117650Saalok 			(void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
163487117650Saalok 			    rbytes);
163587117650Saalok 			type = UNCOMPRESSED;
163687117650Saalok 			len_compressed = rbytes;
163787117650Saalok 		} else {
163887117650Saalok 			type = COMPRESSED;
163987117650Saalok 		}
164087117650Saalok 
164187117650Saalok 		/*
164287117650Saalok 		 * Set the first byte or the SEGHDR to
164387117650Saalok 		 * indicate if it's compressed or not
164487117650Saalok 		 */
164587117650Saalok 		*compressed_seg = type;
164687117650Saalok 		wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
164787117650Saalok 		if (wbytes != (len_compressed + SEGHDR)) {
164887117650Saalok 			rbytes = -1;
164987117650Saalok 			break;
165087117650Saalok 		}
165187117650Saalok 
165287117650Saalok 		index[count] = BE_64(offset);
165387117650Saalok 		offset += wbytes;
165487117650Saalok 		lastread = rbytes;
165587117650Saalok 		count++;
165687117650Saalok 	}
165787117650Saalok 
165887117650Saalok 	(void) close(uncompfd);
165987117650Saalok 
166087117650Saalok 	if (rbytes < 0)
166187117650Saalok 		goto cleanup;
166287117650Saalok 	/*
166387117650Saalok 	 * The last index entry is a sentinel entry. It does not point to
166487117650Saalok 	 * an actual compressed segment but helps in computing the size of
166587117650Saalok 	 * the compressed segment. The size of each compressed segment is
166687117650Saalok 	 * computed by subtracting the current index value from the next
166787117650Saalok 	 * one (the compressed blocks are stored sequentially)
166887117650Saalok 	 */
166987117650Saalok 	index[count++] = BE_64(offset);
167087117650Saalok 
167187117650Saalok 	/*
167287117650Saalok 	 * Now write the compressed data along with the
167387117650Saalok 	 * header information to this file which will
167487117650Saalok 	 * later be renamed to the original uncompressed
167587117650Saalok 	 * file name
167687117650Saalok 	 *
167787117650Saalok 	 * The header is as follows -
167887117650Saalok 	 *
167987117650Saalok 	 * Signature (name of the compression algorithm)
168087117650Saalok 	 * Compression segment size (a multiple of 512)
168187117650Saalok 	 * Number of index entries
168287117650Saalok 	 * Size of the last block
168387117650Saalok 	 * The array containing the index entries
168487117650Saalok 	 *
168587117650Saalok 	 * the header is always stored in network byte
168687117650Saalok 	 * order
168787117650Saalok 	 */
168887117650Saalok 	(void) bzero(algorithm, sizeof (algorithm));
168987117650Saalok 	(void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
169087117650Saalok 	if (write(compfd, algorithm, sizeof (algorithm))
169187117650Saalok 	    != sizeof (algorithm))
169287117650Saalok 		goto cleanup;
169387117650Saalok 
169487117650Saalok 	segsize = htonl(segsize);
169587117650Saalok 	if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
169687117650Saalok 		goto cleanup;
169787117650Saalok 
169887117650Saalok 	index_entries = htonl(count);
169987117650Saalok 	if (write(compfd, &index_entries, sizeof (index_entries)) !=
170087117650Saalok 	    sizeof (index_entries))
170187117650Saalok 		goto cleanup;
170287117650Saalok 
170387117650Saalok 	lastread = htonl(lastread);
170487117650Saalok 	if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
170587117650Saalok 		goto cleanup;
170687117650Saalok 
170787117650Saalok 	for (i = 0; i < count; i++) {
170887117650Saalok 		if (write(compfd, index + i, sizeof (*index)) !=
170987117650Saalok 		    sizeof (*index))
171087117650Saalok 			goto cleanup;
171187117650Saalok 	}
171287117650Saalok 
171387117650Saalok 	/* Header is written, now write the compressed data */
171487117650Saalok 	if (lseek(tfd, 0, SEEK_SET) != 0)
171587117650Saalok 		goto cleanup;
171687117650Saalok 
171787117650Saalok 	rbytes = wbytes = 0;
171887117650Saalok 
171987117650Saalok 	for (;;) {
172087117650Saalok 		rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
172187117650Saalok 
172287117650Saalok 		if (rbytes <= 0)
172387117650Saalok 			break;
172487117650Saalok 
172587117650Saalok 		if (write(compfd, compressed_seg, rbytes) != rbytes)
172687117650Saalok 			goto cleanup;
172787117650Saalok 	}
172887117650Saalok 
172987117650Saalok 	if (fstat64(compfd, &statbuf) == -1)
173087117650Saalok 		goto cleanup;
173187117650Saalok 
173287117650Saalok 	/*
173387117650Saalok 	 * Round up the compressed file size to be a multiple of
173487117650Saalok 	 * DEV_BSIZE. lofi(7D) likes it that way.
173587117650Saalok 	 */
173687117650Saalok 	if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
173787117650Saalok 
173887117650Saalok 		offset = DEV_BSIZE - offset;
173987117650Saalok 
174087117650Saalok 		for (i = 0; i < offset; i++)
174187117650Saalok 			uncompressed_seg[i] = '\0';
174287117650Saalok 		if (write(compfd, uncompressed_seg, offset) != offset)
174387117650Saalok 			goto cleanup;
174487117650Saalok 	}
174587117650Saalok 	(void) close(compfd);
174687117650Saalok 	(void) close(tfd);
174787117650Saalok 	(void) unlink(tmpfilename);
174887117650Saalok cleanup:
174987117650Saalok 	if (rbytes < 0) {
175087117650Saalok 		if (tfd != -1)
175187117650Saalok 			(void) unlink(tmpfilename);
175287117650Saalok 		if (compfd != -1)
175387117650Saalok 			(void) unlink(comp_filename);
175487117650Saalok 		die(gettext("error compressing file %s"), filename);
175587117650Saalok 	} else {
175687117650Saalok 		/* Rename the compressed file to the actual file */
175787117650Saalok 		if (rename(comp_filename, filename) == -1) {
175887117650Saalok 			(void) unlink(comp_filename);
175987117650Saalok 			die(gettext("error compressing file %s"), filename);
176087117650Saalok 		}
176187117650Saalok 	}
176287117650Saalok 	if (compressed_seg != NULL)
176387117650Saalok 		free(compressed_seg);
176487117650Saalok 	if (uncompressed_seg != NULL)
176587117650Saalok 		free(uncompressed_seg);
176687117650Saalok 	if (index != NULL)
176787117650Saalok 		free(index);
176887117650Saalok 	if (compfd != -1)
176987117650Saalok 		(void) close(compfd);
177087117650Saalok 	if (uncompfd != -1)
177187117650Saalok 		(void) close(uncompfd);
177287117650Saalok 	if (tfd != -1)
177387117650Saalok 		(void) close(tfd);
177487117650Saalok }
177587117650Saalok 
177687117650Saalok static int
177787117650Saalok lofi_compress_select(const char *algname)
177887117650Saalok {
177987117650Saalok 	int i;
178087117650Saalok 
178187117650Saalok 	for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
178287117650Saalok 		if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
178387117650Saalok 			return (i);
178487117650Saalok 	}
178587117650Saalok 	return (-1);
178687117650Saalok }
178787117650Saalok 
178887117650Saalok static void
178987117650Saalok check_algorithm_validity(const char *algname, int *compress_index)
179087117650Saalok {
179187117650Saalok 	*compress_index = lofi_compress_select(algname);
179287117650Saalok 	if (*compress_index < 0)
179387117650Saalok 		die(gettext("invalid algorithm name: %s\n"), algname);
179487117650Saalok }
179587117650Saalok 
179687117650Saalok static void
179787117650Saalok check_file_validity(const char *filename)
179887117650Saalok {
17997c478bd9Sstevel@tonic-gate 	struct stat64 buf;
180087117650Saalok 	int 	error;
18017d82f0f8SDina K Nimeh 	int	fd;
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 	fd = open64(filename, O_RDONLY);
18047c478bd9Sstevel@tonic-gate 	if (fd == -1) {
18057c478bd9Sstevel@tonic-gate 		die(gettext("open: %s"), filename);
18067c478bd9Sstevel@tonic-gate 	}
18077c478bd9Sstevel@tonic-gate 	error = fstat64(fd, &buf);
18087c478bd9Sstevel@tonic-gate 	if (error == -1) {
18097c478bd9Sstevel@tonic-gate 		die(gettext("fstat: %s"), filename);
18107c478bd9Sstevel@tonic-gate 	} else if (!S_ISLOFIABLE(buf.st_mode)) {
18117c478bd9Sstevel@tonic-gate 		die(gettext("%s is not a regular file, "
18127c478bd9Sstevel@tonic-gate 		    "block, or character device\n"),
18137c478bd9Sstevel@tonic-gate 		    filename);
18147c478bd9Sstevel@tonic-gate 	} else if ((buf.st_size % DEV_BSIZE) != 0) {
1815f9153c6bSDina K Nimeh 		die(gettext("size of %s is not a multiple of %d\n"),
18167c478bd9Sstevel@tonic-gate 		    filename, DEV_BSIZE);
18177c478bd9Sstevel@tonic-gate 	}
18187c478bd9Sstevel@tonic-gate 	(void) close(fd);
181987117650Saalok 
182087117650Saalok 	if (name_to_minor(filename) != 0) {
1821f9153c6bSDina K Nimeh 		die(gettext("cannot use %s on itself\n"), LOFI_DRIVER_NAME);
18227c478bd9Sstevel@tonic-gate 	}
182387117650Saalok }
182487117650Saalok 
182587117650Saalok static uint32_t
182687117650Saalok convert_to_num(const char *str)
182787117650Saalok {
182887117650Saalok 	int len;
182987117650Saalok 	uint32_t segsize, mult = 1;
183087117650Saalok 
183187117650Saalok 	len = strlen(str);
183287117650Saalok 	if (len && isalpha(str[len - 1])) {
183387117650Saalok 		switch (str[len - 1]) {
183487117650Saalok 		case 'k':
183587117650Saalok 		case 'K':
183687117650Saalok 			mult = KILOBYTE;
183787117650Saalok 			break;
183887117650Saalok 		case 'b':
183987117650Saalok 		case 'B':
184087117650Saalok 			mult = BLOCK_SIZE;
184187117650Saalok 			break;
184287117650Saalok 		case 'm':
184387117650Saalok 		case 'M':
184487117650Saalok 			mult = MEGABYTE;
184587117650Saalok 			break;
184687117650Saalok 		case 'g':
184787117650Saalok 		case 'G':
184887117650Saalok 			mult = GIGABYTE;
184987117650Saalok 			break;
185087117650Saalok 		default:
185187117650Saalok 			die(gettext("invalid segment size %s\n"), str);
185287117650Saalok 		}
185387117650Saalok 	}
185487117650Saalok 
185587117650Saalok 	segsize = atol(str);
185687117650Saalok 	segsize *= mult;
185787117650Saalok 
185887117650Saalok 	return (segsize);
185987117650Saalok }
186087117650Saalok 
186187117650Saalok int
186287117650Saalok main(int argc, char *argv[])
186387117650Saalok {
186487117650Saalok 	int	lfd;
186587117650Saalok 	int	c;
186687117650Saalok 	const char *devicename = NULL;
186787117650Saalok 	const char *filename = NULL;
186887117650Saalok 	const char *algname = COMPRESS_ALGORITHM;
186987117650Saalok 	int	openflag;
187087117650Saalok 	int	minor;
187187117650Saalok 	int	compress_index;
187287117650Saalok 	uint32_t segsize = SEGSIZE;
187387117650Saalok 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
187487117650Saalok 	boolean_t force = B_FALSE;
18757d82f0f8SDina K Nimeh 	const char *pname;
18767d82f0f8SDina K Nimeh 	boolean_t errflag = B_FALSE;
18777d82f0f8SDina K Nimeh 	boolean_t addflag = B_FALSE;
1878*a29e56d9SToomas Soome 	boolean_t labelflag = B_FALSE;
1879cd69fabeSAlexander Eremin 	boolean_t rdflag = B_FALSE;
18807d82f0f8SDina K Nimeh 	boolean_t deleteflag = B_FALSE;
18817d82f0f8SDina K Nimeh 	boolean_t ephflag = B_FALSE;
18827d82f0f8SDina K Nimeh 	boolean_t compressflag = B_FALSE;
18837d82f0f8SDina K Nimeh 	boolean_t uncompressflag = B_FALSE;
18847d82f0f8SDina K Nimeh 	/* the next two work together for -c, -k, -T, -e options only */
18857d82f0f8SDina K Nimeh 	boolean_t need_crypto = B_FALSE;	/* if any -c, -k, -T, -e */
18867d82f0f8SDina K Nimeh 	boolean_t cipher_only = B_TRUE;		/* if -c only */
18877d82f0f8SDina K Nimeh 	const char *keyfile = NULL;
18887d82f0f8SDina K Nimeh 	mech_alias_t *cipher = NULL;
18897d82f0f8SDina K Nimeh 	token_spec_t *token = NULL;
18907d82f0f8SDina K Nimeh 	char	*rkey = NULL;
18917d82f0f8SDina K Nimeh 	size_t	rksz = 0;
1892f9153c6bSDina K Nimeh 	char realfilename[MAXPATHLEN];
189387117650Saalok 
189487117650Saalok 	pname = getpname(argv[0]);
189587117650Saalok 
189687117650Saalok 	(void) setlocale(LC_ALL, "");
189787117650Saalok 	(void) textdomain(TEXT_DOMAIN);
189887117650Saalok 
1899*a29e56d9SToomas Soome 	while ((c = getopt(argc, argv, "a:b:c:Cd:efk:lrRs:T:U")) != EOF) {
190087117650Saalok 		switch (c) {
190187117650Saalok 		case 'a':
19027d82f0f8SDina K Nimeh 			addflag = B_TRUE;
1903f9153c6bSDina K Nimeh 			if ((filename = realpath(optarg, realfilename)) == NULL)
1904f9153c6bSDina K Nimeh 				die("%s", optarg);
19057c478bd9Sstevel@tonic-gate 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
19067c478bd9Sstevel@tonic-gate 				/* optional device */
19077c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
19087c478bd9Sstevel@tonic-gate 				optind++;
19097c478bd9Sstevel@tonic-gate 			}
19107c478bd9Sstevel@tonic-gate 			break;
191187117650Saalok 		case 'C':
19127d82f0f8SDina K Nimeh 			compressflag = B_TRUE;
19137d82f0f8SDina K Nimeh 			if (((argc - optind) > 1) && (*argv[optind] != '-')) {
19147d82f0f8SDina K Nimeh 				/* optional algorithm */
19157d82f0f8SDina K Nimeh 				algname = argv[optind];
191687117650Saalok 				optind++;
191787117650Saalok 			}
191887117650Saalok 			check_algorithm_validity(algname, &compress_index);
191987117650Saalok 			break;
19207d82f0f8SDina K Nimeh 		case 'c':
19217d82f0f8SDina K Nimeh 			/* is the chosen cipher allowed? */
19227d82f0f8SDina K Nimeh 			if ((cipher = ciph2mech(optarg)) == NULL) {
19237d82f0f8SDina K Nimeh 				errflag = B_TRUE;
19247d82f0f8SDina K Nimeh 				warn(gettext("cipher %s not allowed\n"),
19257d82f0f8SDina K Nimeh 				    optarg);
19267d82f0f8SDina K Nimeh 			}
19277d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
19287d82f0f8SDina K Nimeh 			/* cipher_only is already set */
19297d82f0f8SDina K Nimeh 			break;
19307c478bd9Sstevel@tonic-gate 		case 'd':
19317d82f0f8SDina K Nimeh 			deleteflag = B_TRUE;
19327c478bd9Sstevel@tonic-gate 			minor = name_to_minor(optarg);
19337c478bd9Sstevel@tonic-gate 			if (minor != 0)
19347c478bd9Sstevel@tonic-gate 				devicename = optarg;
1935f9153c6bSDina K Nimeh 			else {
1936f9153c6bSDina K Nimeh 				if ((filename = realpath(optarg,
1937f9153c6bSDina K Nimeh 				    realfilename)) == NULL)
1938f9153c6bSDina K Nimeh 					die("%s", optarg);
1939f9153c6bSDina K Nimeh 			}
19407c478bd9Sstevel@tonic-gate 			break;
19417d82f0f8SDina K Nimeh 		case 'e':
19427d82f0f8SDina K Nimeh 			ephflag = B_TRUE;
19437d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
19447d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
19457d82f0f8SDina K Nimeh 			break;
19463d7072f8Seschrock 		case 'f':
19473d7072f8Seschrock 			force = B_TRUE;
19483d7072f8Seschrock 			break;
19497d82f0f8SDina K Nimeh 		case 'k':
19507d82f0f8SDina K Nimeh 			keyfile = optarg;
19517d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
19527d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
19537d82f0f8SDina K Nimeh 			break;
1954*a29e56d9SToomas Soome 		case 'l':
1955*a29e56d9SToomas Soome 			labelflag = B_TRUE;
1956*a29e56d9SToomas Soome 			break;
1957cd69fabeSAlexander Eremin 		case 'r':
1958cd69fabeSAlexander Eremin 			rdflag = B_TRUE;
1959cd69fabeSAlexander Eremin 			break;
196087117650Saalok 		case 's':
196187117650Saalok 			segsize = convert_to_num(optarg);
19624058a205Sjrgn.keil@googlemail.com 			if (segsize < DEV_BSIZE || !ISP2(segsize))
196387117650Saalok 				die(gettext("segment size %s is invalid "
196487117650Saalok 				    "or not a multiple of minimum block "
196587117650Saalok 				    "size %ld\n"), optarg, DEV_BSIZE);
19667d82f0f8SDina K Nimeh 			break;
19677d82f0f8SDina K Nimeh 		case 'T':
19687d82f0f8SDina K Nimeh 			if ((token = parsetoken(optarg)) == NULL) {
19697d82f0f8SDina K Nimeh 				errflag = B_TRUE;
19707d82f0f8SDina K Nimeh 				warn(
19717d82f0f8SDina K Nimeh 				    gettext("invalid token key specifier %s\n"),
19727d82f0f8SDina K Nimeh 				    optarg);
19737d82f0f8SDina K Nimeh 			}
19747d82f0f8SDina K Nimeh 			need_crypto = B_TRUE;
19757d82f0f8SDina K Nimeh 			cipher_only = B_FALSE;	/* need to unset cipher_only */
197687117650Saalok 			break;
197787117650Saalok 		case 'U':
19787d82f0f8SDina K Nimeh 			uncompressflag = B_TRUE;
197987117650Saalok 			break;
19807c478bd9Sstevel@tonic-gate 		case '?':
19817c478bd9Sstevel@tonic-gate 		default:
19827d82f0f8SDina K Nimeh 			errflag = B_TRUE;
19837c478bd9Sstevel@tonic-gate 			break;
19847c478bd9Sstevel@tonic-gate 		}
19857c478bd9Sstevel@tonic-gate 	}
19867d82f0f8SDina K Nimeh 
19877d82f0f8SDina K Nimeh 	/* Check for mutually exclusive combinations of options */
198887117650Saalok 	if (errflag ||
198987117650Saalok 	    (addflag && deleteflag) ||
1990*a29e56d9SToomas Soome 	    (labelflag && !addflag) ||
1991cd69fabeSAlexander Eremin 	    (rdflag && !addflag) ||
19927d82f0f8SDina K Nimeh 	    (!addflag && need_crypto) ||
1993*a29e56d9SToomas Soome 	    (need_crypto && labelflag) ||
1994*a29e56d9SToomas Soome 	    ((compressflag || uncompressflag) &&
1995*a29e56d9SToomas Soome 	    (labelflag || addflag || deleteflag)))
19967d82f0f8SDina K Nimeh 		usage(pname);
19977d82f0f8SDina K Nimeh 
19987d82f0f8SDina K Nimeh 	/* ephemeral key, and key from either file or token are incompatible */
19997d82f0f8SDina K Nimeh 	if (ephflag && (keyfile != NULL || token != NULL)) {
20007d82f0f8SDina K Nimeh 		die(gettext("ephemeral key cannot be used with keyfile"
20017d82f0f8SDina K Nimeh 		    " or token key\n"));
20027d82f0f8SDina K Nimeh 	}
20037d82f0f8SDina K Nimeh 
20047d82f0f8SDina K Nimeh 	/*
20057d82f0f8SDina K Nimeh 	 * "-c" but no "-k", "-T", "-e", or "-T -k" means derive key from
20067d82f0f8SDina K Nimeh 	 * command line passphrase
20077d82f0f8SDina K Nimeh 	 */
20087c478bd9Sstevel@tonic-gate 
20097c478bd9Sstevel@tonic-gate 	switch (argc - optind) {
20107c478bd9Sstevel@tonic-gate 	case 0: /* no more args */
20117d82f0f8SDina K Nimeh 		if (compressflag || uncompressflag)	/* needs filename */
20127d82f0f8SDina K Nimeh 			usage(pname);
20137c478bd9Sstevel@tonic-gate 		break;
20147d82f0f8SDina K Nimeh 	case 1:
20157c478bd9Sstevel@tonic-gate 		if (addflag || deleteflag)
20167d82f0f8SDina K Nimeh 			usage(pname);
20177d82f0f8SDina K Nimeh 		/* one arg means compress/uncompress the file ... */
20187d82f0f8SDina K Nimeh 		if (compressflag || uncompressflag) {
20197d82f0f8SDina K Nimeh 			if ((filename = realpath(argv[optind],
20207d82f0f8SDina K Nimeh 			    realfilename)) == NULL)
20217d82f0f8SDina K Nimeh 				die("%s", argv[optind]);
20227d82f0f8SDina K Nimeh 		/* ... or without options means print the association */
20237d82f0f8SDina K Nimeh 		} else {
20247c478bd9Sstevel@tonic-gate 			minor = name_to_minor(argv[optind]);
20257c478bd9Sstevel@tonic-gate 			if (minor != 0)
20267c478bd9Sstevel@tonic-gate 				devicename = argv[optind];
2027f9153c6bSDina K Nimeh 			else {
2028f9153c6bSDina K Nimeh 				if ((filename = realpath(argv[optind],
2029f9153c6bSDina K Nimeh 				    realfilename)) == NULL)
2030f9153c6bSDina K Nimeh 					die("%s", argv[optind]);
2031f9153c6bSDina K Nimeh 			}
20327d82f0f8SDina K Nimeh 		}
20337c478bd9Sstevel@tonic-gate 		break;
20347c478bd9Sstevel@tonic-gate 	default:
20357d82f0f8SDina K Nimeh 		usage(pname);
20367c478bd9Sstevel@tonic-gate 		break;
20377c478bd9Sstevel@tonic-gate 	}
20387c478bd9Sstevel@tonic-gate 
20397d82f0f8SDina K Nimeh 	if (addflag || compressflag || uncompressflag)
20407d82f0f8SDina K Nimeh 		check_file_validity(filename);
20417d82f0f8SDina K Nimeh 
20427c478bd9Sstevel@tonic-gate 	if (filename && !valid_abspath(filename))
20437c478bd9Sstevel@tonic-gate 		exit(E_ERROR);
20447c478bd9Sstevel@tonic-gate 
20457c478bd9Sstevel@tonic-gate 	/*
20467c478bd9Sstevel@tonic-gate 	 * Here, we know the arguments are correct, the filename is an
20477c478bd9Sstevel@tonic-gate 	 * absolute path, it exists and is a regular file. We don't yet
20487c478bd9Sstevel@tonic-gate 	 * know that the device name is ok or not.
20497c478bd9Sstevel@tonic-gate 	 */
20507d82f0f8SDina K Nimeh 
20517c478bd9Sstevel@tonic-gate 	openflag = O_EXCL;
205287117650Saalok 	if (addflag || deleteflag || compressflag || uncompressflag)
20537c478bd9Sstevel@tonic-gate 		openflag |= O_RDWR;
20547c478bd9Sstevel@tonic-gate 	else
20557c478bd9Sstevel@tonic-gate 		openflag |= O_RDONLY;
20567c478bd9Sstevel@tonic-gate 	lfd = open(lofictl, openflag);
20577c478bd9Sstevel@tonic-gate 	if (lfd == -1) {
20587c478bd9Sstevel@tonic-gate 		if ((errno == EPERM) || (errno == EACCES)) {
2059f9153c6bSDina K Nimeh 			die(gettext("you do not have permission to perform "
2060f9153c6bSDina K Nimeh 			    "that operation.\n"));
20617c478bd9Sstevel@tonic-gate 		} else {
2062f9153c6bSDina K Nimeh 			die(gettext("open: %s"), lofictl);
20637c478bd9Sstevel@tonic-gate 		}
20647c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
20657c478bd9Sstevel@tonic-gate 	}
20667d82f0f8SDina K Nimeh 
20677d82f0f8SDina K Nimeh 	/*
20687d82f0f8SDina K Nimeh 	 * No passphrase is needed for ephemeral key, or when key is
20697d82f0f8SDina K Nimeh 	 * in a file and not wrapped by another key from a token.
20707d82f0f8SDina K Nimeh 	 * However, a passphrase is needed in these cases:
20717d82f0f8SDina K Nimeh 	 * 1. cipher with no ephemeral key, key file, or token,
20727d82f0f8SDina K Nimeh 	 *    in which case the passphrase is used to build the key
20737d82f0f8SDina K Nimeh 	 * 2. token with an optional cipher or optional key file,
20747d82f0f8SDina K Nimeh 	 *    in which case the passphrase unlocks the token
20757d82f0f8SDina K Nimeh 	 * If only the cipher is specified, reconfirm the passphrase
20767d82f0f8SDina K Nimeh 	 * to ensure the user hasn't mis-entered it.  Otherwise, the
20777d82f0f8SDina K Nimeh 	 * token will enforce the token passphrase.
20787d82f0f8SDina K Nimeh 	 */
20797d82f0f8SDina K Nimeh 	if (need_crypto) {
20807d82f0f8SDina K Nimeh 		CK_SESSION_HANDLE	sess;
20817d82f0f8SDina K Nimeh 
20827d82f0f8SDina K Nimeh 		/* pick a cipher if none specified */
20837d82f0f8SDina K Nimeh 		if (cipher == NULL)
20847d82f0f8SDina K Nimeh 			cipher = DEFAULT_CIPHER;
20857d82f0f8SDina K Nimeh 
20867d82f0f8SDina K Nimeh 		if (!kernel_cipher_check(cipher))
20877d82f0f8SDina K Nimeh 			die(gettext(
20887d82f0f8SDina K Nimeh 			    "use \"cryptoadm list -m\" to find available "
20897d82f0f8SDina K Nimeh 			    "mechanisms\n"));
20907d82f0f8SDina K Nimeh 
20917d82f0f8SDina K Nimeh 		init_crypto(token, cipher, &sess);
20927d82f0f8SDina K Nimeh 
20937d82f0f8SDina K Nimeh 		if (cipher_only) {
20947d82f0f8SDina K Nimeh 			getkeyfromuser(cipher, &rkey, &rksz);
20957d82f0f8SDina K Nimeh 		} else if (token != NULL) {
20967d82f0f8SDina K Nimeh 			getkeyfromtoken(sess, token, keyfile, cipher,
20977d82f0f8SDina K Nimeh 			    &rkey, &rksz);
20987d82f0f8SDina K Nimeh 		} else {
20997d82f0f8SDina K Nimeh 			/* this also handles ephemeral keys */
21007d82f0f8SDina K Nimeh 			getkeyfromfile(keyfile, cipher, &rkey, &rksz);
21017d82f0f8SDina K Nimeh 		}
21027d82f0f8SDina K Nimeh 
21037d82f0f8SDina K Nimeh 		end_crypto(sess);
21047d82f0f8SDina K Nimeh 	}
21057d82f0f8SDina K Nimeh 
21067d82f0f8SDina K Nimeh 	/*
21077d82f0f8SDina K Nimeh 	 * Now to the real work.
21087d82f0f8SDina K Nimeh 	 */
21097c478bd9Sstevel@tonic-gate 	if (addflag)
2110cd69fabeSAlexander Eremin 		add_mapping(lfd, devicename, filename, cipher, rkey, rksz,
2111*a29e56d9SToomas Soome 		    rdflag, labelflag);
211287117650Saalok 	else if (compressflag)
21139525893dSaalok 		lofi_compress(&lfd, filename, compress_index, segsize);
211487117650Saalok 	else if (uncompressflag)
211587117650Saalok 		lofi_uncompress(lfd, filename);
21167c478bd9Sstevel@tonic-gate 	else if (deleteflag)
21173d7072f8Seschrock 		delete_mapping(lfd, devicename, filename, force);
21187c478bd9Sstevel@tonic-gate 	else if (filename || devicename)
21197c478bd9Sstevel@tonic-gate 		print_one_mapping(lfd, devicename, filename);
21207c478bd9Sstevel@tonic-gate 	else
21217c478bd9Sstevel@tonic-gate 		print_mappings(lfd);
2122579df0adSaalok 
21239525893dSaalok 	if (lfd != -1)
21247c478bd9Sstevel@tonic-gate 		(void) close(lfd);
2125579df0adSaalok 	closelib();
21267c478bd9Sstevel@tonic-gate 	return (E_SUCCESS);
21277c478bd9Sstevel@tonic-gate }
2128