xref: /dflybsd-src/sbin/cryptdisks/cryptdisks.c (revision 7a8ad07e4feda5d9f79caa4ef40f29e9a683e154)
1bda92397SAlex Hornung /*
2*7a8ad07eSAlex Hornung  * Copyright (c) 2009-2011 The DragonFly Project.  All rights reserved.
3bda92397SAlex Hornung  *
4bda92397SAlex Hornung  * This code is derived from software contributed to The DragonFly Project
5bda92397SAlex Hornung  * by Alex Hornung <ahornung@gmail.com>
6bda92397SAlex Hornung  *
7bda92397SAlex Hornung  * Redistribution and use in source and binary forms, with or without
8bda92397SAlex Hornung  * modification, are permitted provided that the following conditions
9bda92397SAlex Hornung  * are met:
10bda92397SAlex Hornung  *
11bda92397SAlex Hornung  * 1. Redistributions of source code must retain the above copyright
12bda92397SAlex Hornung  *    notice, this list of conditions and the following disclaimer.
13bda92397SAlex Hornung  * 2. Redistributions in binary form must reproduce the above copyright
14bda92397SAlex Hornung  *    notice, this list of conditions and the following disclaimer in
15bda92397SAlex Hornung  *    the documentation and/or other materials provided with the
16bda92397SAlex Hornung  *    distribution.
17bda92397SAlex Hornung  * 3. Neither the name of The DragonFly Project nor the names of its
18bda92397SAlex Hornung  *    contributors may be used to endorse or promote products derived
19bda92397SAlex Hornung  *    from this software without specific, prior written permission.
20bda92397SAlex Hornung  *
21bda92397SAlex Hornung  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22bda92397SAlex Hornung  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23bda92397SAlex Hornung  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24bda92397SAlex Hornung  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25bda92397SAlex Hornung  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26bda92397SAlex Hornung  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27bda92397SAlex Hornung  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28bda92397SAlex Hornung  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29bda92397SAlex Hornung  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30bda92397SAlex Hornung  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31bda92397SAlex Hornung  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32bda92397SAlex Hornung  * SUCH DAMAGE.
33bda92397SAlex Hornung  */
34bda92397SAlex Hornung 
35bda92397SAlex Hornung #include <stdio.h>
36bda92397SAlex Hornung #include <stdarg.h>
37bda92397SAlex Hornung #include <string.h>
385e1ed6baSAlex Hornung #include <stdlib.h>
39bda92397SAlex Hornung #include <unistd.h>
40bda92397SAlex Hornung #include <errno.h>
41bda92397SAlex Hornung #include <err.h>
42bda92397SAlex Hornung 
43bda92397SAlex Hornung #include <libcryptsetup.h>
44*7a8ad07eSAlex Hornung #include <tcplay_api.h>
45bda92397SAlex Hornung 
465e1ed6baSAlex Hornung #include "safe_mem.h"
475e1ed6baSAlex Hornung 
485e1ed6baSAlex Hornung #define _iswhitespace(X)	((((X) == ' ') || ((X) == '\t'))?1:0)
49bda92397SAlex Hornung 
50bda92397SAlex Hornung #define CRYPTDISKS_START	1
51bda92397SAlex Hornung #define CRYPTDISKS_STOP		2
52bda92397SAlex Hornung 
53*7a8ad07eSAlex Hornung struct generic_opts {
54*7a8ad07eSAlex Hornung 	char		*device;
55*7a8ad07eSAlex Hornung 	char		*map_name;
56*7a8ad07eSAlex Hornung 	char		*passphrase;
57*7a8ad07eSAlex Hornung 	const char	*keyfiles[256];
58*7a8ad07eSAlex Hornung 	int		nkeyfiles;
59*7a8ad07eSAlex Hornung 	int		ntries;
60*7a8ad07eSAlex Hornung 	unsigned long long	timeout;
61*7a8ad07eSAlex Hornung };
62*7a8ad07eSAlex Hornung 
63b58f1e66SSascha Wildner static void syntax_error(const char *, ...) __printflike(1, 2);
64b58f1e66SSascha Wildner 
65bda92397SAlex Hornung static int line_no = 1;
66bda92397SAlex Hornung 
675e1ed6baSAlex Hornung static int iswhitespace(char c)
685e1ed6baSAlex Hornung {
695e1ed6baSAlex Hornung 	return _iswhitespace(c);
705e1ed6baSAlex Hornung }
715e1ed6baSAlex Hornung 
725e1ed6baSAlex Hornung static int iscomma(char c)
735e1ed6baSAlex Hornung {
745e1ed6baSAlex Hornung 	return (c == ',');
755e1ed6baSAlex Hornung }
765e1ed6baSAlex Hornung 
77bda92397SAlex Hornung static int yesDialog(char *msg __unused)
78bda92397SAlex Hornung {
79bda92397SAlex Hornung 	return 1;
80bda92397SAlex Hornung }
81bda92397SAlex Hornung 
82bda92397SAlex Hornung static void cmdLineLog(int level __unused, char *msg)
83bda92397SAlex Hornung {
84bda92397SAlex Hornung 	printf("%s", msg);
85bda92397SAlex Hornung }
86bda92397SAlex Hornung 
87bda92397SAlex Hornung static struct interface_callbacks cmd_icb = {
88bda92397SAlex Hornung 	.yesDialog = yesDialog,
89bda92397SAlex Hornung 	.log = cmdLineLog,
90bda92397SAlex Hornung };
91bda92397SAlex Hornung 
92bda92397SAlex Hornung static void
93bda92397SAlex Hornung syntax_error(const char *fmt, ...)
94bda92397SAlex Hornung {
95bda92397SAlex Hornung 	char buf[1024];
96bda92397SAlex Hornung 	va_list ap;
97bda92397SAlex Hornung 
98bda92397SAlex Hornung 	va_start(ap, fmt);
99bda92397SAlex Hornung 	vsnprintf(buf, sizeof(buf), fmt, ap);
100bda92397SAlex Hornung 	va_end(ap);
101bda92397SAlex Hornung 	errx(1, "crypttab: syntax error on line %d: %s\n", line_no, buf);
102bda92397SAlex Hornung }
103bda92397SAlex Hornung 
104bda92397SAlex Hornung 
105bda92397SAlex Hornung static int
106bda92397SAlex Hornung entry_check_num_args(char **tokens, int num)
107bda92397SAlex Hornung {
108bda92397SAlex Hornung 	int i;
109bda92397SAlex Hornung 
110bda92397SAlex Hornung 	for (i = 0; tokens[i] != NULL; i++)
111bda92397SAlex Hornung 		;
112bda92397SAlex Hornung 
113bda92397SAlex Hornung 	if (i < num) {
1145e1ed6baSAlex Hornung 		syntax_error("at least %d tokens were expected but only %d "
1155e1ed6baSAlex Hornung 		    "were found", num, i);
116bda92397SAlex Hornung 		return 1;
117bda92397SAlex Hornung 	}
118bda92397SAlex Hornung 	return 0;
119bda92397SAlex Hornung }
120bda92397SAlex Hornung 
121bda92397SAlex Hornung static int
1225e1ed6baSAlex Hornung line_tokenize(char *buffer, int (*is_sep)(char), char comment_char, char **tokens)
1235e1ed6baSAlex Hornung {
1245e1ed6baSAlex Hornung 	int c, n, i;
1255e1ed6baSAlex Hornung 	int quote = 0;
1265e1ed6baSAlex Hornung 
1275e1ed6baSAlex Hornung 	i = strlen(buffer) + 1;
1285e1ed6baSAlex Hornung 	c = 0;
1295e1ed6baSAlex Hornung 
1305e1ed6baSAlex Hornung 	/* Skip leading white-space */
1315e1ed6baSAlex Hornung 	while ((_iswhitespace(buffer[c])) && (c < i)) c++;
1325e1ed6baSAlex Hornung 
1335e1ed6baSAlex Hornung 	/*
1345e1ed6baSAlex Hornung 	 * If this line effectively (after indentation) begins with the comment
1355e1ed6baSAlex Hornung 	 * character, we ignore the rest of the line.
1365e1ed6baSAlex Hornung 	 */
1375e1ed6baSAlex Hornung 	if (buffer[c] == comment_char)
1385e1ed6baSAlex Hornung 		return 0;
1395e1ed6baSAlex Hornung 
1405e1ed6baSAlex Hornung 	tokens[0] = &buffer[c];
1415e1ed6baSAlex Hornung 	for (n = 1; c < i; c++) {
1425e1ed6baSAlex Hornung 		if (buffer[c] == '"') {
1435e1ed6baSAlex Hornung 			quote = !quote;
1445e1ed6baSAlex Hornung 			if (quote) {
1455e1ed6baSAlex Hornung 				if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
1465e1ed6baSAlex Hornung #if 0
1475e1ed6baSAlex Hornung 					syntax_error("stray opening quote not "
1485e1ed6baSAlex Hornung 					    "at beginning of token");
1495e1ed6baSAlex Hornung 					/* NOTREACHED */
1505e1ed6baSAlex Hornung #endif
1515e1ed6baSAlex Hornung 				} else {
1525e1ed6baSAlex Hornung 					tokens[n-1] = &buffer[c+1];
1535e1ed6baSAlex Hornung 				}
1545e1ed6baSAlex Hornung 			} else {
1555e1ed6baSAlex Hornung 				if ((c < i-1) && (!is_sep(buffer[c+1]))) {
1565e1ed6baSAlex Hornung #if 0
1575e1ed6baSAlex Hornung 					syntax_error("stray closing quote not "
1585e1ed6baSAlex Hornung 					    "at end of token");
1595e1ed6baSAlex Hornung 					/* NOTREACHED */
1605e1ed6baSAlex Hornung #endif
1615e1ed6baSAlex Hornung 				} else {
1625e1ed6baSAlex Hornung 					buffer[c] = '\0';
1635e1ed6baSAlex Hornung 				}
1645e1ed6baSAlex Hornung 			}
1655e1ed6baSAlex Hornung 		}
1665e1ed6baSAlex Hornung 
1675e1ed6baSAlex Hornung 		if (quote) {
1685e1ed6baSAlex Hornung 			continue;
1695e1ed6baSAlex Hornung 		}
1705e1ed6baSAlex Hornung 
1715e1ed6baSAlex Hornung 		if (is_sep(buffer[c])) {
1725e1ed6baSAlex Hornung 			buffer[c++] = '\0';
1735e1ed6baSAlex Hornung 			while ((_iswhitespace(buffer[c])) && (c < i)) c++;
1745e1ed6baSAlex Hornung 			tokens[n++] = &buffer[c--];
1755e1ed6baSAlex Hornung 		}
1765e1ed6baSAlex Hornung 	}
1775e1ed6baSAlex Hornung 	tokens[n] = NULL;
1785e1ed6baSAlex Hornung 
1795e1ed6baSAlex Hornung 	if (quote) {
1805e1ed6baSAlex Hornung 		tokens[0] = NULL;
1815e1ed6baSAlex Hornung 		return 0;
1825e1ed6baSAlex Hornung 	}
1835e1ed6baSAlex Hornung 
1845e1ed6baSAlex Hornung 	return n;
1855e1ed6baSAlex Hornung }
1865e1ed6baSAlex Hornung 
1875e1ed6baSAlex Hornung static int
188*7a8ad07eSAlex Hornung parse_crypt_options(struct generic_opts *go, char *option)
1895e1ed6baSAlex Hornung {
1905e1ed6baSAlex Hornung 	char	*parameter, *endptr;
1915e1ed6baSAlex Hornung 	char	*buf;
1925e1ed6baSAlex Hornung 	long	lval;
1935e1ed6baSAlex Hornung 	unsigned long long ullval;
1945e1ed6baSAlex Hornung 	int	noparam = 0;
1955e1ed6baSAlex Hornung 	FILE	*fd;
1965e1ed6baSAlex Hornung 
1975e1ed6baSAlex Hornung 	parameter = strchr(option, '=');
1985e1ed6baSAlex Hornung 	noparam = (parameter == NULL);
1995e1ed6baSAlex Hornung 	if (!noparam)
2005e1ed6baSAlex Hornung 	{
2015e1ed6baSAlex Hornung 		*parameter = '\0';
2025e1ed6baSAlex Hornung 		++parameter;
2035e1ed6baSAlex Hornung 	}
2045e1ed6baSAlex Hornung 
2055e1ed6baSAlex Hornung 	if (strcmp(option, "tries") == 0) {
2065e1ed6baSAlex Hornung 		if (noparam)
2075e1ed6baSAlex Hornung 			syntax_error("The option 'tries' needs a parameter");
2085e1ed6baSAlex Hornung 			/* NOTREACHED */
2095e1ed6baSAlex Hornung 
2105e1ed6baSAlex Hornung 		lval = strtol(parameter, &endptr, 10);
2115e1ed6baSAlex Hornung 		if (*endptr != '\0')
2125e1ed6baSAlex Hornung 			syntax_error("The option 'tries' expects an integer "
2135e1ed6baSAlex Hornung 			    "parameter, not '%s'", parameter);
2145e1ed6baSAlex Hornung 			/* NOTREACHED */
2155e1ed6baSAlex Hornung 
216*7a8ad07eSAlex Hornung 		go->ntries = (int)lval;
2175e1ed6baSAlex Hornung 	} else if (strcmp(option, "timeout") == 0) {
2185e1ed6baSAlex Hornung 		if (noparam)
2195e1ed6baSAlex Hornung 			syntax_error("The option 'timeout' needs a parameter");
2205e1ed6baSAlex Hornung 			/* NOTREACHED */
2215e1ed6baSAlex Hornung 
2225e1ed6baSAlex Hornung 		ullval = strtoull(parameter, &endptr, 10);
2235e1ed6baSAlex Hornung 		if (*endptr != '\0')
2245e1ed6baSAlex Hornung 			syntax_error("The option 'timeout' expects an integer "
2255e1ed6baSAlex Hornung 			    "parameter, not '%s'", parameter);
2265e1ed6baSAlex Hornung 			/* NOTREACHED */
2275e1ed6baSAlex Hornung 
228*7a8ad07eSAlex Hornung 		go->timeout = ullval;
2295e1ed6baSAlex Hornung 	} else if (strcmp(option, "keyscript") == 0) {
2305e1ed6baSAlex Hornung 		if (noparam)
2315e1ed6baSAlex Hornung 			syntax_error("The option 'keyscript' needs a parameter");
2325e1ed6baSAlex Hornung 			/* NOTREACHED */
2335e1ed6baSAlex Hornung 
2345e1ed6baSAlex Hornung 		/* Allocate safe key memory */
2355e1ed6baSAlex Hornung 		buf = alloc_safe_mem(8192);
23644053572SAlex Hornung 		if (buf == NULL)
23744053572SAlex Hornung 			err(1, "Could not allocate safe memory");
23844053572SAlex Hornung 			/* NOTREACHED */
2395e1ed6baSAlex Hornung 
2405e1ed6baSAlex Hornung 		fd = popen(parameter, "r");
24144053572SAlex Hornung 		if (fd == NULL)
24244053572SAlex Hornung 			syntax_error("The 'keyscript' file could not be run");
24344053572SAlex Hornung 			/* NOTREACHED */
24444053572SAlex Hornung 
2455e1ed6baSAlex Hornung 		if ((fread(buf, 1, sizeof(buf), fd)) == 0)
2465e1ed6baSAlex Hornung 			syntax_error("The 'keyscript' program failed");
2475e1ed6baSAlex Hornung 			/* NOTREACHED */
2485e1ed6baSAlex Hornung 		pclose(fd);
2495e1ed6baSAlex Hornung 
2505e1ed6baSAlex Hornung 		/* Get rid of trailing new-line */
2515e1ed6baSAlex Hornung 		if ((endptr = strrchr(buf, '\n')) != NULL)
2525e1ed6baSAlex Hornung 			*endptr = '\0';
2535e1ed6baSAlex Hornung 
254*7a8ad07eSAlex Hornung 		go->passphrase = buf;
2555e1ed6baSAlex Hornung 	} else if (strcmp(option, "none") == 0) {
2565e1ed6baSAlex Hornung 		/* Valid option, does nothing */
2575e1ed6baSAlex Hornung 	} else {
2585e1ed6baSAlex Hornung 		syntax_error("Unknown option: %s", option);
2595e1ed6baSAlex Hornung 		/* NOTREACHED */
2605e1ed6baSAlex Hornung 	}
2615e1ed6baSAlex Hornung 
2625e1ed6baSAlex Hornung 	return 0;
2635e1ed6baSAlex Hornung }
2645e1ed6baSAlex Hornung 
265*7a8ad07eSAlex Hornung static void
266*7a8ad07eSAlex Hornung generic_opts_to_luks(struct crypt_options *co, struct generic_opts *go)
267*7a8ad07eSAlex Hornung {
268*7a8ad07eSAlex Hornung 	if (go->nkeyfiles > 1)
269*7a8ad07eSAlex Hornung 		fprintf(stderr, "crypttab: Warning: LUKS only supports one "
270*7a8ad07eSAlex Hornung 		    "keyfile; on line %d\n", line_no);
271*7a8ad07eSAlex Hornung 
272*7a8ad07eSAlex Hornung 	co->icb = &cmd_icb;
273*7a8ad07eSAlex Hornung 	co->tries = go->ntries;
274*7a8ad07eSAlex Hornung 	co->name = go->map_name;
275*7a8ad07eSAlex Hornung 	co->device = go->device;
276*7a8ad07eSAlex Hornung 	co->key_file = (go->nkeyfiles == 1) ? go->keyfiles[0] : NULL;
277*7a8ad07eSAlex Hornung 	co->passphrase = go->passphrase;
278*7a8ad07eSAlex Hornung 	co->timeout = go->timeout;
279*7a8ad07eSAlex Hornung }
280*7a8ad07eSAlex Hornung 
281*7a8ad07eSAlex Hornung static void
282*7a8ad07eSAlex Hornung generic_opts_to_tcplay(struct tc_api_opts *tco, struct generic_opts *go)
283*7a8ad07eSAlex Hornung {
284*7a8ad07eSAlex Hornung 	/* Make sure keyfile array is NULL-terminated */
285*7a8ad07eSAlex Hornung 	go->keyfiles[go->nkeyfiles] = NULL;
286*7a8ad07eSAlex Hornung 
287*7a8ad07eSAlex Hornung 	tco->tc_interactive_prompt = (go->passphrase != NULL) ? 0 : 1;
288*7a8ad07eSAlex Hornung 	tco->tc_password_retries = go->ntries;
289*7a8ad07eSAlex Hornung 	tco->tc_map_name = go->map_name;
290*7a8ad07eSAlex Hornung 	tco->tc_device = go->device;
291*7a8ad07eSAlex Hornung 	tco->tc_keyfiles = go->keyfiles;
292*7a8ad07eSAlex Hornung 	tco->tc_passphrase = go->passphrase;
293*7a8ad07eSAlex Hornung 	tco->tc_prompt_timeout = go->timeout;
294*7a8ad07eSAlex Hornung }
295*7a8ad07eSAlex Hornung 
2965e1ed6baSAlex Hornung static int
2975e1ed6baSAlex Hornung entry_parser(char **tokens, char **options, int type)
298bda92397SAlex Hornung {
299bda92397SAlex Hornung 	struct crypt_options co;
300*7a8ad07eSAlex Hornung 	struct tc_api_opts tco;
301*7a8ad07eSAlex Hornung 	struct generic_opts go;
302*7a8ad07eSAlex Hornung 	int r, i, error, isluks;
303bda92397SAlex Hornung 
304bda92397SAlex Hornung 	if (entry_check_num_args(tokens, 2) != 0)
305bda92397SAlex Hornung 		return 1;
306bda92397SAlex Hornung 
307*7a8ad07eSAlex Hornung 	bzero(&go, sizeof(go));
308bda92397SAlex Hornung 	bzero(&co, sizeof(co));
309*7a8ad07eSAlex Hornung 	bzero(&tco, sizeof(tco));
310bda92397SAlex Hornung 
311*7a8ad07eSAlex Hornung 
312*7a8ad07eSAlex Hornung 	go.ntries = 3;
313*7a8ad07eSAlex Hornung 	go.map_name = tokens[0];
314*7a8ad07eSAlex Hornung 	go.device = tokens[1];
315bda92397SAlex Hornung 
3165e1ed6baSAlex Hornung 	/* (Try to) parse extra options */
3175e1ed6baSAlex Hornung 	for (i = 0; options[i] != NULL; i++)
318*7a8ad07eSAlex Hornung 		parse_crypt_options(&go, options[i]);
3195e1ed6baSAlex Hornung 
320*7a8ad07eSAlex Hornung 	if ((tokens[2] != NULL) && (strcmp(tokens[2], "none") != 0)) {
321*7a8ad07eSAlex Hornung 		/* We got a keyfile */
322*7a8ad07eSAlex Hornung 		go.keyfiles[go.nkeyfiles++] = tokens[2];
323*7a8ad07eSAlex Hornung 	}
324*7a8ad07eSAlex Hornung 
325*7a8ad07eSAlex Hornung 	generic_opts_to_luks(&co, &go);
326*7a8ad07eSAlex Hornung 	generic_opts_to_tcplay(&tco, &go);
327*7a8ad07eSAlex Hornung 
328*7a8ad07eSAlex Hornung 	/*
329*7a8ad07eSAlex Hornung 	 * Check whether the device is a LUKS-formatted device; otherwise
330*7a8ad07eSAlex Hornung 	 * we assume its a TrueCrypt volume.
331*7a8ad07eSAlex Hornung 	 */
332*7a8ad07eSAlex Hornung 	isluks = !crypt_isLuks(&co);
333*7a8ad07eSAlex Hornung 
334*7a8ad07eSAlex Hornung 	if (!isluks) {
335*7a8ad07eSAlex Hornung 		if ((error = tc_api_init(0)) != 0) {
336*7a8ad07eSAlex Hornung 			fprintf(stderr, "crypttab: line %d: tc_api could not "
337*7a8ad07eSAlex Hornung 			    "be initialized\n", line_no);
338bda92397SAlex Hornung 			return 1;
339bda92397SAlex Hornung 		}
340*7a8ad07eSAlex Hornung 	}
341bda92397SAlex Hornung 
342bda92397SAlex Hornung 	if (type == CRYPTDISKS_STOP) {
343*7a8ad07eSAlex Hornung 		if (isluks) {
344bda92397SAlex Hornung 			/* Check if the device is active */
345bda92397SAlex Hornung 			r = crypt_query_device(&co);
346bda92397SAlex Hornung 
347bda92397SAlex Hornung 			/* If r > 0, then the device is active */
348bda92397SAlex Hornung 			if (r <= 0)
349bda92397SAlex Hornung 				return 0;
350bda92397SAlex Hornung 
351bda92397SAlex Hornung 			/* Actually close the device */
352bda92397SAlex Hornung 			crypt_remove_device(&co);
353*7a8ad07eSAlex Hornung 		} else {
354*7a8ad07eSAlex Hornung 			/* Assume tcplay volume */
355*7a8ad07eSAlex Hornung 			tc_api_unmap_volume(&tco);
356*7a8ad07eSAlex Hornung 		}
357bda92397SAlex Hornung 	} else if (type == CRYPTDISKS_START) {
358*7a8ad07eSAlex Hornung 		/* Open the device */
359*7a8ad07eSAlex Hornung 		if (isluks) {
360*7a8ad07eSAlex Hornung 			if ((error = crypt_luksOpen(&co)) != 0) {
361*7a8ad07eSAlex Hornung 				fprintf(stderr, "crypttab: line %d: device %s "
362*7a8ad07eSAlex Hornung 				    "could not be mapped / opened\n",
363*7a8ad07eSAlex Hornung 				    line_no, tco.tc_device);
364*7a8ad07eSAlex Hornung 				return 1;
365*7a8ad07eSAlex Hornung 			}
366*7a8ad07eSAlex Hornung 		} else {
367*7a8ad07eSAlex Hornung 			/* Assume tcplay volume */
368*7a8ad07eSAlex Hornung 			if ((error = tc_api_map_volume(&tco)) != 0) {
369*7a8ad07eSAlex Hornung 				fprintf(stderr, "crypttab: line %d: device %s "
370*7a8ad07eSAlex Hornung 				    "could not be mapped / opened: %s\n",
371*7a8ad07eSAlex Hornung 				    line_no, tco.tc_device,
372*7a8ad07eSAlex Hornung 				    tc_api_get_error_msg());
373*7a8ad07eSAlex Hornung 				tc_api_uninit();
374*7a8ad07eSAlex Hornung 				return 1;
375*7a8ad07eSAlex Hornung 			}
376*7a8ad07eSAlex Hornung 		}
377bda92397SAlex Hornung 	}
378bda92397SAlex Hornung 
379*7a8ad07eSAlex Hornung 	if (!isluks)
380*7a8ad07eSAlex Hornung 		tc_api_uninit();
381bda92397SAlex Hornung 
382bda92397SAlex Hornung 	return 0;
383bda92397SAlex Hornung }
384bda92397SAlex Hornung 
385bda92397SAlex Hornung static int
386bda92397SAlex Hornung process_line(FILE* fd, int type)
387bda92397SAlex Hornung {
388bda92397SAlex Hornung 	char buffer[4096];
389bda92397SAlex Hornung 	char *tokens[256];
3905e1ed6baSAlex Hornung 	char *options[256];
391bda92397SAlex Hornung 	int c, n, i = 0;
392bda92397SAlex Hornung 	int ret = 0;
393bda92397SAlex Hornung 
394bda92397SAlex Hornung 	while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
395bda92397SAlex Hornung 		buffer[i++] = (char)c;
396bda92397SAlex Hornung 		if (i == (sizeof(buffer) -1))
397bda92397SAlex Hornung 			break;
398bda92397SAlex Hornung 	}
399bda92397SAlex Hornung 	buffer[i] = '\0';
400bda92397SAlex Hornung 
401bda92397SAlex Hornung 	if (feof(fd) || ferror(fd))
402bda92397SAlex Hornung 		ret = 1;
403bda92397SAlex Hornung 
404bda92397SAlex Hornung 
4055e1ed6baSAlex Hornung 	n = line_tokenize(buffer, &iswhitespace, '#', tokens);
406bda92397SAlex Hornung 
407bda92397SAlex Hornung 	/*
408bda92397SAlex Hornung 	 * If there are not enough arguments for any function or it is
409bda92397SAlex Hornung 	 * a line full of whitespaces, we just return here. Or if a
410bda92397SAlex Hornung 	 * quote wasn't closed.
411bda92397SAlex Hornung 	 */
4125e1ed6baSAlex Hornung 	if ((n < 2) || (tokens[0][0] == '\0'))
413bda92397SAlex Hornung 		return ret;
414bda92397SAlex Hornung 
4155e1ed6baSAlex Hornung 	/*
4165e1ed6baSAlex Hornung 	 * If there are at least 4 tokens, one of them (the last) is a list
4175e1ed6baSAlex Hornung 	 * of options.
4185e1ed6baSAlex Hornung 	 */
4195e1ed6baSAlex Hornung 	if (n >= 4)
4205e1ed6baSAlex Hornung 	{
4215e1ed6baSAlex Hornung 		i = line_tokenize(tokens[3], &iscomma, '#', options);
4225e1ed6baSAlex Hornung 		if (i == 0)
4235e1ed6baSAlex Hornung 			syntax_error("Invalid expression in options token");
4245e1ed6baSAlex Hornung 			/* NOTREACHED */
4255e1ed6baSAlex Hornung 	}
4265e1ed6baSAlex Hornung 
4275e1ed6baSAlex Hornung 	entry_parser(tokens, options, type);
428bda92397SAlex Hornung 
429bda92397SAlex Hornung 	return ret;
430bda92397SAlex Hornung }
431bda92397SAlex Hornung 
432bda92397SAlex Hornung 
433bda92397SAlex Hornung int
434bda92397SAlex Hornung main(int argc, char *argv[])
435bda92397SAlex Hornung {
436bda92397SAlex Hornung 	FILE *fd;
437bda92397SAlex Hornung 	int ch, start = 0, stop = 0;
438bda92397SAlex Hornung 
439bda92397SAlex Hornung 	while ((ch = getopt(argc, argv, "01")) != -1) {
440bda92397SAlex Hornung 		switch (ch) {
441bda92397SAlex Hornung 		case '1':
442bda92397SAlex Hornung 			start = 1;
443bda92397SAlex Hornung 			break;
444bda92397SAlex Hornung 		case '0':
445bda92397SAlex Hornung 			stop = 1;
446bda92397SAlex Hornung 			break;
447bda92397SAlex Hornung 		default:
448bda92397SAlex Hornung 			break;
449bda92397SAlex Hornung 		}
450bda92397SAlex Hornung 	}
451bda92397SAlex Hornung 
452bda92397SAlex Hornung 	argc -= optind;
453bda92397SAlex Hornung 	argv += optind;
454bda92397SAlex Hornung 
4555e1ed6baSAlex Hornung 	atexit(check_and_purge_safe_mem);
4565e1ed6baSAlex Hornung 
457bda92397SAlex Hornung 	if ((start && stop) || (!start && !stop))
458bda92397SAlex Hornung 		errx(1, "please specify exactly one of -0 and -1");
459bda92397SAlex Hornung 
460bda92397SAlex Hornung 	fd = fopen("/etc/crypttab", "r");
461bda92397SAlex Hornung 	if (fd == NULL)
462bda92397SAlex Hornung 		err(1, "fopen");
463bda92397SAlex Hornung 		/* NOTREACHED */
464bda92397SAlex Hornung 
465bda92397SAlex Hornung 	while (process_line(fd, (start) ? CRYPTDISKS_START : CRYPTDISKS_STOP) == 0)
466bda92397SAlex Hornung 		++line_no;
467bda92397SAlex Hornung 
468bda92397SAlex Hornung 	fclose(fd);
469bda92397SAlex Hornung 	return 0;
470bda92397SAlex Hornung }
4715e1ed6baSAlex Hornung 
472