2 * Copyright (c) 2009-2011 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Alex Hornung <ahornung@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 #include <libcryptsetup.h>
44 #include <tcplay_api.h>
48 #define _iswhitespace(X) ((((X) == ' ') || ((X) == '\t'))?1:0)
50 #define CRYPTDISKS_START 1
51 #define CRYPTDISKS_STOP 2
57 const char *keyfiles[256];
60 unsigned long long timeout;
63 static void syntax_error(const char *, ...) __printflike(1, 2);
65 static int line_no = 1;
67 static int iswhitespace(char c)
69 return _iswhitespace(c);
72 static int iscomma(char c)
77 static int yesDialog(char *msg __unused)
82 static void cmdLineLog(int level __unused, char *msg)
87 static struct interface_callbacks cmd_icb = {
88 .yesDialog = yesDialog,
93 syntax_error(const char *fmt, ...)
99 vsnprintf(buf, sizeof(buf), fmt, ap);
101 errx(1, "crypttab: syntax error on line %d: %s\n", line_no, buf);
106 entry_check_num_args(char **tokens, int num)
110 for (i = 0; tokens[i] != NULL; i++)
114 syntax_error("at least %d tokens were expected but only %d "
115 "were found", num, i);
122 line_tokenize(char *buffer, int (*is_sep)(char), char comment_char, char **tokens)
127 i = strlen(buffer) + 1;
130 /* Skip leading white-space */
131 while ((_iswhitespace(buffer[c])) && (c < i)) c++;
134 * If this line effectively (after indentation) begins with the comment
135 * character, we ignore the rest of the line.
137 if (buffer[c] == comment_char)
140 tokens[0] = &buffer[c];
141 for (n = 1; c < i; c++) {
142 if (buffer[c] == '"') {
145 if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
147 syntax_error("stray opening quote not "
148 "at beginning of token");
152 tokens[n-1] = &buffer[c+1];
155 if ((c < i-1) && (!is_sep(buffer[c+1]))) {
157 syntax_error("stray closing quote not "
171 if (is_sep(buffer[c])) {
173 while ((_iswhitespace(buffer[c])) && (c < i)) c++;
174 tokens[n++] = &buffer[c--];
188 parse_crypt_options(struct generic_opts *go, char *option)
190 char *parameter, *endptr;
193 unsigned long long ullval;
197 parameter = strchr(option, '=');
198 noparam = (parameter == NULL);
205 if (strcmp(option, "tries") == 0) {
207 syntax_error("The option 'tries' needs a parameter");
210 lval = strtol(parameter, &endptr, 10);
212 syntax_error("The option 'tries' expects an integer "
213 "parameter, not '%s'", parameter);
216 go->ntries = (int)lval;
217 } else if (strcmp(option, "timeout") == 0) {
219 syntax_error("The option 'timeout' needs a parameter");
222 ullval = strtoull(parameter, &endptr, 10);
224 syntax_error("The option 'timeout' expects an integer "
225 "parameter, not '%s'", parameter);
228 go->timeout = ullval;
229 } else if (strcmp(option, "keyscript") == 0) {
231 syntax_error("The option 'keyscript' needs a parameter");
234 /* Allocate safe key memory */
235 buf = alloc_safe_mem(8192);
237 err(1, "Could not allocate safe memory");
240 fd = popen(parameter, "r");
242 syntax_error("The 'keyscript' file could not be run");
245 if ((fread(buf, 1, sizeof(buf), fd)) == 0)
246 syntax_error("The 'keyscript' program failed");
250 /* Get rid of trailing new-line */
251 if ((endptr = strrchr(buf, '\n')) != NULL)
254 go->passphrase = buf;
255 } else if (strcmp(option, "none") == 0) {
256 /* Valid option, does nothing */
258 syntax_error("Unknown option: %s", option);
266 generic_opts_to_luks(struct crypt_options *co, struct generic_opts *go)
268 if (go->nkeyfiles > 1)
269 fprintf(stderr, "crypttab: Warning: LUKS only supports one "
270 "keyfile; on line %d\n", line_no);
273 co->tries = go->ntries;
274 co->name = go->map_name;
275 co->device = go->device;
276 co->key_file = (go->nkeyfiles == 1) ? go->keyfiles[0] : NULL;
277 co->passphrase = go->passphrase;
278 co->timeout = go->timeout;
282 generic_opts_to_tcplay(struct tc_api_opts *tco, struct generic_opts *go)
284 /* Make sure keyfile array is NULL-terminated */
285 go->keyfiles[go->nkeyfiles] = NULL;
287 tco->tc_interactive_prompt = (go->passphrase != NULL) ? 0 : 1;
288 tco->tc_password_retries = go->ntries;
289 tco->tc_map_name = go->map_name;
290 tco->tc_device = go->device;
291 tco->tc_keyfiles = go->keyfiles;
292 tco->tc_passphrase = go->passphrase;
293 tco->tc_prompt_timeout = go->timeout;
297 entry_parser(char **tokens, char **options, int type)
299 struct crypt_options co;
300 struct tc_api_opts tco;
301 struct generic_opts go;
302 int r, i, error, isluks;
304 if (entry_check_num_args(tokens, 2) != 0)
307 bzero(&go, sizeof(go));
308 bzero(&co, sizeof(co));
309 bzero(&tco, sizeof(tco));
313 go.map_name = tokens[0];
314 go.device = tokens[1];
316 /* (Try to) parse extra options */
317 for (i = 0; options[i] != NULL; i++)
318 parse_crypt_options(&go, options[i]);
320 if ((tokens[2] != NULL) && (strcmp(tokens[2], "none") != 0)) {
321 /* We got a keyfile */
322 go.keyfiles[go.nkeyfiles++] = tokens[2];
325 generic_opts_to_luks(&co, &go);
326 generic_opts_to_tcplay(&tco, &go);
329 * Check whether the device is a LUKS-formatted device; otherwise
330 * we assume its a TrueCrypt volume.
332 isluks = !crypt_isLuks(&co);
335 if ((error = tc_api_init(0)) != 0) {
336 fprintf(stderr, "crypttab: line %d: tc_api could not "
337 "be initialized\n", line_no);
342 if (type == CRYPTDISKS_STOP) {
344 /* Check if the device is active */
345 r = crypt_query_device(&co);
347 /* If r > 0, then the device is active */
351 /* Actually close the device */
352 crypt_remove_device(&co);
354 /* Assume tcplay volume */
355 tc_api_unmap_volume(&tco);
357 } else if (type == CRYPTDISKS_START) {
358 /* Open the device */
360 if ((error = crypt_luksOpen(&co)) != 0) {
361 fprintf(stderr, "crypttab: line %d: device %s "
362 "could not be mapped / opened\n",
363 line_no, tco.tc_device);
367 /* Assume tcplay volume */
368 if ((error = tc_api_map_volume(&tco)) != 0) {
369 fprintf(stderr, "crypttab: line %d: device %s "
370 "could not be mapped / opened: %s\n",
371 line_no, tco.tc_device,
372 tc_api_get_error_msg());
386 process_line(FILE* fd, int type)
394 while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
395 buffer[i++] = (char)c;
396 if (i == (sizeof(buffer) -1))
401 if (feof(fd) || ferror(fd))
405 n = line_tokenize(buffer, &iswhitespace, '#', tokens);
408 * If there are not enough arguments for any function or it is
409 * a line full of whitespaces, we just return here. Or if a
410 * quote wasn't closed.
412 if ((n < 2) || (tokens[0][0] == '\0'))
416 * If there are at least 4 tokens, one of them (the last) is a list
421 i = line_tokenize(tokens[3], &iscomma, '#', options);
423 syntax_error("Invalid expression in options token");
427 entry_parser(tokens, options, type);
434 main(int argc, char *argv[])
437 int ch, start = 0, stop = 0;
439 while ((ch = getopt(argc, argv, "01")) != -1) {
455 atexit(check_and_purge_safe_mem);
457 if ((start && stop) || (!start && !stop))
458 errx(1, "please specify exactly one of -0 and -1");
460 fd = fopen("/etc/crypttab", "r");
465 while (process_line(fd, (start) ? CRYPTDISKS_START : CRYPTDISKS_STOP) == 0)