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) {
230 size_t keymem_len
= 8192;
233 syntax_error("The option 'keyscript' needs a parameter");
236 /* Allocate safe key memory */
237 buf
= alloc_safe_mem(keymem_len
);
239 err(1, "Could not allocate safe memory");
242 fd
= popen(parameter
, "r");
244 syntax_error("The 'keyscript' file could not be run");
247 if ((fread(buf
, 1, keymem_len
, fd
)) == 0)
248 syntax_error("The 'keyscript' program failed");
252 /* Get rid of trailing new-line */
253 if ((endptr
= strrchr(buf
, '\n')) != NULL
)
256 go
->passphrase
= buf
;
257 } else if (strcmp(option
, "none") == 0) {
258 /* Valid option, does nothing */
260 syntax_error("Unknown option: %s", option
);
268 generic_opts_to_luks(struct crypt_options
*co
, struct generic_opts
*go
)
270 if (go
->nkeyfiles
> 1)
271 fprintf(stderr
, "crypttab: Warning: LUKS only supports one "
272 "keyfile; on line %d\n", line_no
);
275 co
->tries
= go
->ntries
;
276 co
->name
= go
->map_name
;
277 co
->device
= go
->device
;
278 co
->key_file
= (go
->nkeyfiles
== 1) ? go
->keyfiles
[0] : NULL
;
279 co
->passphrase
= go
->passphrase
;
280 co
->timeout
= go
->timeout
;
284 entry_parser(char **tokens
, char **options
, int type
)
286 struct crypt_options co
;
287 tc_api_task tcplay_task
;
288 struct generic_opts go
;
289 int r
, i
, error
, isluks
;
291 if (entry_check_num_args(tokens
, 2) != 0)
294 bzero(&go
, sizeof(go
));
295 bzero(&co
, sizeof(co
));
299 go
.map_name
= tokens
[0];
300 go
.device
= tokens
[1];
302 /* (Try to) parse extra options */
303 for (i
= 0; options
[i
] != NULL
; i
++)
304 parse_crypt_options(&go
, options
[i
]);
306 if ((tokens
[2] != NULL
) && (strcmp(tokens
[2], "none") != 0)) {
307 /* We got a keyfile */
308 go
.keyfiles
[go
.nkeyfiles
++] = tokens
[2];
311 generic_opts_to_luks(&co
, &go
);
314 * Check whether the device is a LUKS-formatted device; otherwise
315 * we assume its a TrueCrypt volume.
317 isluks
= !crypt_isLuks(&co
);
320 if ((error
= tc_api_init(0)) != 0) {
321 fprintf(stderr
, "crypttab: line %d: tc_api could not "
322 "be initialized\n", line_no
);
327 if (type
== CRYPTDISKS_STOP
) {
329 /* Check if the device is active */
330 r
= crypt_query_device(&co
);
332 /* If r > 0, then the device is active */
336 /* Actually close the device */
337 crypt_remove_device(&co
);
339 /* Assume tcplay volume */
340 if ((tcplay_task
= tc_api_task_init("unmap")) == NULL
) {
341 fprintf(stderr
, "tc_api_task_init failed.\n");
344 if ((error
= tc_api_task_set(tcplay_task
, "dev", go
.device
))) {
345 fprintf(stderr
, "tc_api_task_set dev failed\n");
348 if ((error
= tc_api_task_set(tcplay_task
, "map_name",
350 fprintf(stderr
, "tc_api_task_set map_name failed\n");
353 if ((error
= tc_api_task_do(tcplay_task
))) {
354 fprintf(stderr
, "crypttab: line %d: device %s "
355 "could not be unmapped: %s\n",
357 tc_api_task_get_error(tcplay_task
));
360 if ((error
= tc_api_task_uninit(tcplay_task
))) {
361 fprintf(stderr
, "tc_api_task_uninit failed\n");
366 } else if (type
== CRYPTDISKS_START
) {
367 /* Open the device */
369 if ((error
= crypt_luksOpen(&co
)) != 0) {
370 fprintf(stderr
, "crypttab: line %d: device %s "
371 "could not be mapped/opened\n",
376 if ((tcplay_task
= tc_api_task_init("map")) == NULL
) {
377 fprintf(stderr
, "tc_api_task_init failed.\n");
380 if ((error
= tc_api_task_set(tcplay_task
, "dev", go
.device
))) {
381 fprintf(stderr
, "tc_api_task_set dev failed\n");
385 if ((error
= tc_api_task_set(tcplay_task
, "map_name",
387 fprintf(stderr
, "tc_api_task_set map_name failed\n");
390 if ((error
= tc_api_task_set(tcplay_task
, "interactive",
391 (go
.passphrase
!= NULL
) ? 0 : 1))) {
392 fprintf(stderr
, "tc_api_task_set map_name failed\n");
395 if ((error
= tc_api_task_set(tcplay_task
, "retries",
397 fprintf(stderr
, "tc_api_task_set map_name failed\n");
400 if ((error
= tc_api_task_set(tcplay_task
, "timeout",
402 fprintf(stderr
, "tc_api_task_set map_name failed\n");
406 if (go
.passphrase
!= NULL
) {
407 if ((error
= tc_api_task_set(tcplay_task
, "passphrase",
409 fprintf(stderr
, "tc_api_task_set map_name failed\n");
414 for (i
= 0; i
< go
.nkeyfiles
; i
++) {
415 if ((error
= tc_api_task_set(tcplay_task
, "keyfiles",
417 fprintf(stderr
, "tc_api_task_set keyfile failed\n");
421 if ((error
= tc_api_task_do(tcplay_task
))) {
422 fprintf(stderr
, "crypttab: line %d: device %s "
423 "could not be mapped/opened: %s\n",
425 tc_api_task_get_error(tcplay_task
));
428 if ((error
= tc_api_task_uninit(tcplay_task
))) {
429 fprintf(stderr
, "tc_api_task_uninit failed\n");
446 process_line(FILE* fd
, int type
)
454 while (((c
= fgetc(fd
)) != EOF
) && (c
!= '\n')) {
455 buffer
[i
++] = (char)c
;
456 if (i
== (sizeof(buffer
) -1))
461 if (feof(fd
) || ferror(fd
))
465 n
= line_tokenize(buffer
, &iswhitespace
, '#', tokens
);
468 * If there are not enough arguments for any function or it is
469 * a line full of whitespaces, we just return here. Or if a
470 * quote wasn't closed.
472 if ((n
< 2) || (tokens
[0][0] == '\0'))
476 * If there are at least 4 tokens, one of them (the last) is a list
481 i
= line_tokenize(tokens
[3], &iscomma
, '#', options
);
483 syntax_error("Invalid expression in options token");
487 entry_parser(tokens
, options
, type
);
494 main(int argc
, char *argv
[])
497 int ch
, start
= 0, stop
= 0;
499 while ((ch
= getopt(argc
, argv
, "01")) != -1) {
515 atexit(check_and_purge_safe_mem
);
517 if ((start
&& stop
) || (!start
&& !stop
))
518 errx(1, "please specify exactly one of -0 and -1");
520 fd
= fopen("/etc/crypttab", "r");
525 while (process_line(fd
, (start
) ? CRYPTDISKS_START
: CRYPTDISKS_STOP
) == 0)