xref: /dflybsd-src/sbin/cryptdisks/cryptdisks.c (revision e129008b7b1355c83a8628e8d4dc9c6a588fe9fb)
1bda92397SAlex Hornung /*
27a8ad07eSAlex 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>
447a8ad07eSAlex 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 
537a8ad07eSAlex Hornung struct generic_opts {
547a8ad07eSAlex Hornung 	char		*device;
557a8ad07eSAlex Hornung 	char		*map_name;
567a8ad07eSAlex Hornung 	char		*passphrase;
577a8ad07eSAlex Hornung 	const char	*keyfiles[256];
587a8ad07eSAlex Hornung 	int		nkeyfiles;
597a8ad07eSAlex Hornung 	int		ntries;
607a8ad07eSAlex Hornung 	unsigned long long	timeout;
617a8ad07eSAlex Hornung };
627a8ad07eSAlex Hornung 
63b58f1e66SSascha Wildner static void syntax_error(const char *, ...) __printflike(1, 2);
64b58f1e66SSascha Wildner 
65bda92397SAlex Hornung static int line_no = 1;
66bda92397SAlex Hornung 
iswhitespace(char c)675e1ed6baSAlex Hornung static int iswhitespace(char c)
685e1ed6baSAlex Hornung {
695e1ed6baSAlex Hornung 	return _iswhitespace(c);
705e1ed6baSAlex Hornung }
715e1ed6baSAlex Hornung 
iscomma(char c)725e1ed6baSAlex Hornung static int iscomma(char c)
735e1ed6baSAlex Hornung {
745e1ed6baSAlex Hornung 	return (c == ',');
755e1ed6baSAlex Hornung }
765e1ed6baSAlex Hornung 
yesDialog(char * msg __unused)77bda92397SAlex Hornung static int yesDialog(char *msg __unused)
78bda92397SAlex Hornung {
79bda92397SAlex Hornung 	return 1;
80bda92397SAlex Hornung }
81bda92397SAlex Hornung 
cmdLineLog(int level __unused,char * msg)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
syntax_error(const char * fmt,...)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
entry_check_num_args(char ** tokens,int num)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
line_tokenize(char * buffer,int (* is_sep)(char),char comment_char,char ** tokens)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
parse_crypt_options(struct generic_opts * go,char * option)1887a8ad07eSAlex 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 
2167a8ad07eSAlex 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 
2287a8ad07eSAlex Hornung 		go->timeout = ullval;
2295e1ed6baSAlex Hornung 	} else if (strcmp(option, "keyscript") == 0) {
2301c482147SSascha Wildner 		size_t keymem_len = 8192;
2311c482147SSascha Wildner 
2325e1ed6baSAlex Hornung 		if (noparam)
2335e1ed6baSAlex Hornung 			syntax_error("The option 'keyscript' needs a parameter");
2345e1ed6baSAlex Hornung 			/* NOTREACHED */
2355e1ed6baSAlex Hornung 
2365e1ed6baSAlex Hornung 		/* Allocate safe key memory */
2371c482147SSascha Wildner 		buf = alloc_safe_mem(keymem_len);
23844053572SAlex Hornung 		if (buf == NULL)
23944053572SAlex Hornung 			err(1, "Could not allocate safe memory");
24044053572SAlex Hornung 			/* NOTREACHED */
2415e1ed6baSAlex Hornung 
2425e1ed6baSAlex Hornung 		fd = popen(parameter, "r");
24344053572SAlex Hornung 		if (fd == NULL)
24444053572SAlex Hornung 			syntax_error("The 'keyscript' file could not be run");
24544053572SAlex Hornung 			/* NOTREACHED */
24644053572SAlex Hornung 
2471c482147SSascha Wildner 		if ((fread(buf, 1, keymem_len, fd)) == 0)
2485e1ed6baSAlex Hornung 			syntax_error("The 'keyscript' program failed");
2495e1ed6baSAlex Hornung 			/* NOTREACHED */
2505e1ed6baSAlex Hornung 		pclose(fd);
2515e1ed6baSAlex Hornung 
2525e1ed6baSAlex Hornung 		/* Get rid of trailing new-line */
2535e1ed6baSAlex Hornung 		if ((endptr = strrchr(buf, '\n')) != NULL)
2545e1ed6baSAlex Hornung 			*endptr = '\0';
2555e1ed6baSAlex Hornung 
2567a8ad07eSAlex Hornung 		go->passphrase = buf;
2575e1ed6baSAlex Hornung 	} else if (strcmp(option, "none") == 0) {
2585e1ed6baSAlex Hornung 		/* Valid option, does nothing */
2595e1ed6baSAlex Hornung 	} else {
2605e1ed6baSAlex Hornung 		syntax_error("Unknown option: %s", option);
2615e1ed6baSAlex Hornung 		/* NOTREACHED */
2625e1ed6baSAlex Hornung 	}
2635e1ed6baSAlex Hornung 
2645e1ed6baSAlex Hornung 	return 0;
2655e1ed6baSAlex Hornung }
2665e1ed6baSAlex Hornung 
2677a8ad07eSAlex Hornung static void
generic_opts_to_luks(struct crypt_options * co,struct generic_opts * go)2687a8ad07eSAlex Hornung generic_opts_to_luks(struct crypt_options *co, struct generic_opts *go)
2697a8ad07eSAlex Hornung {
2707a8ad07eSAlex Hornung 	if (go->nkeyfiles > 1)
2717a8ad07eSAlex Hornung 		fprintf(stderr, "crypttab: Warning: LUKS only supports one "
2727a8ad07eSAlex Hornung 		    "keyfile; on line %d\n", line_no);
2737a8ad07eSAlex Hornung 
2747a8ad07eSAlex Hornung 	co->icb = &cmd_icb;
2757a8ad07eSAlex Hornung 	co->tries = go->ntries;
2767a8ad07eSAlex Hornung 	co->name = go->map_name;
2777a8ad07eSAlex Hornung 	co->device = go->device;
2787a8ad07eSAlex Hornung 	co->key_file = (go->nkeyfiles == 1) ? go->keyfiles[0] : NULL;
2797a8ad07eSAlex Hornung 	co->passphrase = go->passphrase;
2807a8ad07eSAlex Hornung 	co->timeout = go->timeout;
2817a8ad07eSAlex Hornung }
2827a8ad07eSAlex Hornung 
2835e1ed6baSAlex Hornung static int
entry_parser(char ** tokens,char ** options,int type)2845e1ed6baSAlex Hornung entry_parser(char **tokens, char **options, int type)
285bda92397SAlex Hornung {
286bda92397SAlex Hornung 	struct crypt_options co;
287*653318caSAlex Hornung 	tc_api_task tcplay_task;
2887a8ad07eSAlex Hornung 	struct generic_opts go;
2897a8ad07eSAlex Hornung 	int r, i, error, isluks;
290bda92397SAlex Hornung 
291bda92397SAlex Hornung 	if (entry_check_num_args(tokens, 2) != 0)
292bda92397SAlex Hornung 		return 1;
293bda92397SAlex Hornung 
2947a8ad07eSAlex Hornung 	bzero(&go, sizeof(go));
295bda92397SAlex Hornung 	bzero(&co, sizeof(co));
296bda92397SAlex Hornung 
2977a8ad07eSAlex Hornung 
2987a8ad07eSAlex Hornung 	go.ntries = 3;
2997a8ad07eSAlex Hornung 	go.map_name = tokens[0];
3007a8ad07eSAlex Hornung 	go.device = tokens[1];
301bda92397SAlex Hornung 
3025e1ed6baSAlex Hornung 	/* (Try to) parse extra options */
3035e1ed6baSAlex Hornung 	for (i = 0; options[i] != NULL; i++)
3047a8ad07eSAlex Hornung 		parse_crypt_options(&go, options[i]);
3055e1ed6baSAlex Hornung 
3067a8ad07eSAlex Hornung 	if ((tokens[2] != NULL) && (strcmp(tokens[2], "none") != 0)) {
3077a8ad07eSAlex Hornung 		/* We got a keyfile */
3087a8ad07eSAlex Hornung 		go.keyfiles[go.nkeyfiles++] = tokens[2];
3097a8ad07eSAlex Hornung 	}
3107a8ad07eSAlex Hornung 
3117a8ad07eSAlex Hornung 	generic_opts_to_luks(&co, &go);
3127a8ad07eSAlex Hornung 
3137a8ad07eSAlex Hornung 	/*
3147a8ad07eSAlex Hornung 	 * Check whether the device is a LUKS-formatted device; otherwise
3157a8ad07eSAlex Hornung 	 * we assume its a TrueCrypt volume.
3167a8ad07eSAlex Hornung 	 */
3177a8ad07eSAlex Hornung 	isluks = !crypt_isLuks(&co);
3187a8ad07eSAlex Hornung 
3197a8ad07eSAlex Hornung 	if (!isluks) {
3207a8ad07eSAlex Hornung 		if ((error = tc_api_init(0)) != 0) {
3217a8ad07eSAlex Hornung 			fprintf(stderr, "crypttab: line %d: tc_api could not "
3227a8ad07eSAlex Hornung 			    "be initialized\n", line_no);
323bda92397SAlex Hornung 			return 1;
324bda92397SAlex Hornung 		}
3257a8ad07eSAlex Hornung 	}
326bda92397SAlex Hornung 
327bda92397SAlex Hornung 	if (type == CRYPTDISKS_STOP) {
3287a8ad07eSAlex Hornung 		if (isluks) {
329bda92397SAlex Hornung 			/* Check if the device is active */
330bda92397SAlex Hornung 			r = crypt_query_device(&co);
331bda92397SAlex Hornung 
332bda92397SAlex Hornung 			/* If r > 0, then the device is active */
333bda92397SAlex Hornung 			if (r <= 0)
334bda92397SAlex Hornung 				return 0;
335bda92397SAlex Hornung 
336bda92397SAlex Hornung 			/* Actually close the device */
337bda92397SAlex Hornung 			crypt_remove_device(&co);
3387a8ad07eSAlex Hornung 		} else {
3397a8ad07eSAlex Hornung 			/* Assume tcplay volume */
340*653318caSAlex Hornung 			if ((tcplay_task = tc_api_task_init("unmap")) == NULL) {
341*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_init failed.\n");
342*653318caSAlex Hornung 				goto tcplay_err;
343*653318caSAlex Hornung 			}
344*653318caSAlex Hornung 			if ((error = tc_api_task_set(tcplay_task, "dev", go.device))) {
345*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_set dev failed\n");
346*653318caSAlex Hornung 				goto tcplay_err;
347*653318caSAlex Hornung 			}
348*653318caSAlex Hornung 			if ((error = tc_api_task_set(tcplay_task, "map_name",
349*653318caSAlex Hornung 			    go.map_name))) {
350*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_set map_name failed\n");
351*653318caSAlex Hornung 				goto tcplay_err;
352*653318caSAlex Hornung 			}
353*653318caSAlex Hornung 			if ((error = tc_api_task_do(tcplay_task))) {
354*653318caSAlex Hornung 				fprintf(stderr, "crypttab: line %d: device %s "
355*653318caSAlex Hornung 				    "could not be unmapped: %s\n",
356*653318caSAlex Hornung 				    line_no, go.device,
357*653318caSAlex Hornung 				    tc_api_task_get_error(tcplay_task));
358*653318caSAlex Hornung 				goto tcplay_err;
359*653318caSAlex Hornung 			}
360*653318caSAlex Hornung 			if ((error = tc_api_task_uninit(tcplay_task))) {
361*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_uninit failed\n");
362*653318caSAlex Hornung 				goto tcplay_err;
363*653318caSAlex Hornung 			}
364*653318caSAlex Hornung 
3657a8ad07eSAlex Hornung 		}
366bda92397SAlex Hornung 	} else if (type == CRYPTDISKS_START) {
3677a8ad07eSAlex Hornung 		/* Open the device */
3687a8ad07eSAlex Hornung 		if (isluks) {
3697a8ad07eSAlex Hornung 			if ((error = crypt_luksOpen(&co)) != 0) {
3707a8ad07eSAlex Hornung 				fprintf(stderr, "crypttab: line %d: device %s "
3717a8ad07eSAlex Hornung 				    "could not be mapped/opened\n",
372*653318caSAlex Hornung 				    line_no, co.device);
3737a8ad07eSAlex Hornung 				return 1;
3747a8ad07eSAlex Hornung 			}
3757a8ad07eSAlex Hornung 		} else {
376*653318caSAlex Hornung 			if ((tcplay_task = tc_api_task_init("map")) == NULL) {
377*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_init failed.\n");
378*653318caSAlex Hornung 				goto tcplay_err;
379*653318caSAlex Hornung 			}
380*653318caSAlex Hornung 			if ((error = tc_api_task_set(tcplay_task, "dev", go.device))) {
381*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_set dev failed\n");
382*653318caSAlex Hornung 				goto tcplay_err;
383*653318caSAlex Hornung 			}
384*653318caSAlex Hornung 			if ((error = tc_api_task_set(tcplay_task, "map_name",
385*653318caSAlex Hornung 			    go.map_name))) {
386*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_set map_name failed\n");
387*653318caSAlex Hornung 				goto tcplay_err;
388*653318caSAlex Hornung 			}
389*653318caSAlex Hornung 			if ((error = tc_api_task_set(tcplay_task, "interactive",
390*653318caSAlex Hornung 			    (go.passphrase != NULL) ? 0 : 1))) {
391*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_set map_name failed\n");
392*653318caSAlex Hornung 				goto tcplay_err;
393*653318caSAlex Hornung 			}
394*653318caSAlex Hornung 			if ((error = tc_api_task_set(tcplay_task, "retries",
395*653318caSAlex Hornung 			    go.ntries))) {
396*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_set map_name failed\n");
397*653318caSAlex Hornung 				goto tcplay_err;
398*653318caSAlex Hornung 			}
399*653318caSAlex Hornung 			if ((error = tc_api_task_set(tcplay_task, "timeout",
400*653318caSAlex Hornung 			    go.timeout))) {
401*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_set map_name failed\n");
402*653318caSAlex Hornung 				goto tcplay_err;
403*653318caSAlex Hornung 			}
404*653318caSAlex Hornung 
405*653318caSAlex Hornung 			if (go.passphrase != NULL) {
406*653318caSAlex Hornung 				if ((error = tc_api_task_set(tcplay_task, "passphrase",
407*653318caSAlex Hornung 				    go.passphrase))) {
408*653318caSAlex Hornung 					fprintf(stderr, "tc_api_task_set map_name failed\n");
409*653318caSAlex Hornung 					goto tcplay_err;
410*653318caSAlex Hornung 				}
411*653318caSAlex Hornung 			}
412*653318caSAlex Hornung 
413*653318caSAlex Hornung 			for (i = 0; i < go.nkeyfiles; i++) {
414*653318caSAlex Hornung 				if ((error = tc_api_task_set(tcplay_task, "keyfiles",
415*653318caSAlex Hornung 				    go.keyfiles[i]))) {
416*653318caSAlex Hornung 					fprintf(stderr, "tc_api_task_set keyfile failed\n");
417*653318caSAlex Hornung 					goto tcplay_err;
418*653318caSAlex Hornung 				}
419*653318caSAlex Hornung 			}
420*653318caSAlex Hornung 			if ((error = tc_api_task_do(tcplay_task))) {
4217a8ad07eSAlex Hornung 				fprintf(stderr, "crypttab: line %d: device %s "
4227a8ad07eSAlex Hornung 				    "could not be mapped/opened: %s\n",
423*653318caSAlex Hornung 				    line_no, go.device,
424*653318caSAlex Hornung 				    tc_api_task_get_error(tcplay_task));
425*653318caSAlex Hornung 				goto tcplay_err;
426*653318caSAlex Hornung 			}
427*653318caSAlex Hornung 			if ((error = tc_api_task_uninit(tcplay_task))) {
428*653318caSAlex Hornung 				fprintf(stderr, "tc_api_task_uninit failed\n");
429*653318caSAlex Hornung 				goto tcplay_err;
4307a8ad07eSAlex Hornung 			}
4317a8ad07eSAlex Hornung 		}
432bda92397SAlex Hornung 	}
433bda92397SAlex Hornung 
4347a8ad07eSAlex Hornung 	if (!isluks)
4357a8ad07eSAlex Hornung 		tc_api_uninit();
436bda92397SAlex Hornung 
437bda92397SAlex Hornung 	return 0;
438*653318caSAlex Hornung 
439*653318caSAlex Hornung tcplay_err:
440*653318caSAlex Hornung 	tc_api_uninit();
441*653318caSAlex Hornung 	return 1;
442bda92397SAlex Hornung }
443bda92397SAlex Hornung 
444bda92397SAlex Hornung static int
process_line(FILE * fd,int type)445bda92397SAlex Hornung process_line(FILE* fd, int type)
446bda92397SAlex Hornung {
447bda92397SAlex Hornung 	char buffer[4096];
448bda92397SAlex Hornung 	char *tokens[256];
4495e1ed6baSAlex Hornung 	char *options[256];
450bda92397SAlex Hornung 	int c, n, i = 0;
451bda92397SAlex Hornung 	int ret = 0;
452bda92397SAlex Hornung 
453bda92397SAlex Hornung 	while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
454bda92397SAlex Hornung 		buffer[i++] = (char)c;
455bda92397SAlex Hornung 		if (i == (sizeof(buffer) -1))
456bda92397SAlex Hornung 			break;
457bda92397SAlex Hornung 	}
458bda92397SAlex Hornung 	buffer[i] = '\0';
459bda92397SAlex Hornung 
460bda92397SAlex Hornung 	if (feof(fd) || ferror(fd))
461bda92397SAlex Hornung 		ret = 1;
462bda92397SAlex Hornung 
463bda92397SAlex Hornung 
4645e1ed6baSAlex Hornung 	n = line_tokenize(buffer, &iswhitespace, '#', tokens);
465bda92397SAlex Hornung 
466bda92397SAlex Hornung 	/*
467bda92397SAlex Hornung 	 * If there are not enough arguments for any function or it is
468bda92397SAlex Hornung 	 * a line full of whitespaces, we just return here. Or if a
469bda92397SAlex Hornung 	 * quote wasn't closed.
470bda92397SAlex Hornung 	 */
4715e1ed6baSAlex Hornung 	if ((n < 2) || (tokens[0][0] == '\0'))
472bda92397SAlex Hornung 		return ret;
473bda92397SAlex Hornung 
4745e1ed6baSAlex Hornung 	/*
4755e1ed6baSAlex Hornung 	 * If there are at least 4 tokens, one of them (the last) is a list
4765e1ed6baSAlex Hornung 	 * of options.
4775e1ed6baSAlex Hornung 	 */
4785e1ed6baSAlex Hornung 	if (n >= 4)
4795e1ed6baSAlex Hornung 	{
4805e1ed6baSAlex Hornung 		i = line_tokenize(tokens[3], &iscomma, '#', options);
4815e1ed6baSAlex Hornung 		if (i == 0)
4825e1ed6baSAlex Hornung 			syntax_error("Invalid expression in options token");
4835e1ed6baSAlex Hornung 			/* NOTREACHED */
4845e1ed6baSAlex Hornung 	}
4855e1ed6baSAlex Hornung 
4865e1ed6baSAlex Hornung 	entry_parser(tokens, options, type);
487bda92397SAlex Hornung 
488bda92397SAlex Hornung 	return ret;
489bda92397SAlex Hornung }
490bda92397SAlex Hornung 
491bda92397SAlex Hornung 
492bda92397SAlex Hornung int
main(int argc,char * argv[])493bda92397SAlex Hornung main(int argc, char *argv[])
494bda92397SAlex Hornung {
495bda92397SAlex Hornung 	FILE *fd;
496bda92397SAlex Hornung 	int ch, start = 0, stop = 0;
497bda92397SAlex Hornung 
498bda92397SAlex Hornung 	while ((ch = getopt(argc, argv, "01")) != -1) {
499bda92397SAlex Hornung 		switch (ch) {
500bda92397SAlex Hornung 		case '1':
501bda92397SAlex Hornung 			start = 1;
502bda92397SAlex Hornung 			break;
503bda92397SAlex Hornung 		case '0':
504bda92397SAlex Hornung 			stop = 1;
505bda92397SAlex Hornung 			break;
506bda92397SAlex Hornung 		default:
507bda92397SAlex Hornung 			break;
508bda92397SAlex Hornung 		}
509bda92397SAlex Hornung 	}
510bda92397SAlex Hornung 
511bda92397SAlex Hornung 	argc -= optind;
512bda92397SAlex Hornung 	argv += optind;
513bda92397SAlex Hornung 
5145e1ed6baSAlex Hornung 	atexit(check_and_purge_safe_mem);
5155e1ed6baSAlex Hornung 
516bda92397SAlex Hornung 	if ((start && stop) || (!start && !stop))
517bda92397SAlex Hornung 		errx(1, "please specify exactly one of -0 and -1");
518bda92397SAlex Hornung 
519bda92397SAlex Hornung 	fd = fopen("/etc/crypttab", "r");
520bda92397SAlex Hornung 	if (fd == NULL)
521bda92397SAlex Hornung 		err(1, "fopen");
522bda92397SAlex Hornung 		/* NOTREACHED */
523bda92397SAlex Hornung 
524bda92397SAlex Hornung 	while (process_line(fd, (start) ? CRYPTDISKS_START : CRYPTDISKS_STOP) == 0)
525bda92397SAlex Hornung 		++line_no;
526bda92397SAlex Hornung 
527bda92397SAlex Hornung 	fclose(fd);
528bda92397SAlex Hornung 	return 0;
529bda92397SAlex Hornung }
5305e1ed6baSAlex Hornung 
531