Merge branch 'vendor/LESS'
[dragonfly.git] / sbin / cryptdisks / cryptdisks.c
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <ahornung@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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
16  *    distribution.
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.
20  *
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
32  * SUCH DAMAGE.
33  */
34
35 #include <stdio.h>
36 #include <stdarg.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <err.h>
42
43 #include <libcryptsetup.h>
44
45 #include "safe_mem.h"
46
47 #define _iswhitespace(X)        ((((X) == ' ') || ((X) == '\t'))?1:0)
48
49 #define CRYPTDISKS_START        1
50 #define CRYPTDISKS_STOP         2
51
52 static void syntax_error(const char *, ...) __printflike(1, 2);
53
54 static int line_no = 1;
55
56 static int iswhitespace(char c)
57 {
58         return _iswhitespace(c);
59 }
60
61 static int iscomma(char c)
62 {
63         return (c == ',');
64 }
65
66 static int yesDialog(char *msg __unused)
67 {
68         return 1;
69 }
70
71 static void cmdLineLog(int level __unused, char *msg)
72 {
73         printf("%s", msg);
74 }
75
76 static struct interface_callbacks cmd_icb = {
77         .yesDialog = yesDialog,
78         .log = cmdLineLog,
79 };
80
81 static void
82 syntax_error(const char *fmt, ...)
83 {
84         char buf[1024];
85         va_list ap;
86
87         va_start(ap, fmt);
88         vsnprintf(buf, sizeof(buf), fmt, ap);
89         va_end(ap);
90         errx(1, "crypttab: syntax error on line %d: %s\n", line_no, buf);
91 }
92
93
94 static int
95 entry_check_num_args(char **tokens, int num)
96 {
97         int i;
98
99         for (i = 0; tokens[i] != NULL; i++)
100                 ;
101
102         if (i < num) {
103                 syntax_error("at least %d tokens were expected but only %d "
104                     "were found", num, i);
105                 return 1;
106         }
107         return 0;
108 }
109
110 static int
111 line_tokenize(char *buffer, int (*is_sep)(char), char comment_char, char **tokens)
112 {
113         int c, n, i;
114         int quote = 0;
115
116         i = strlen(buffer) + 1;
117         c = 0;
118
119         /* Skip leading white-space */
120         while ((_iswhitespace(buffer[c])) && (c < i)) c++;
121
122         /*
123          * If this line effectively (after indentation) begins with the comment
124          * character, we ignore the rest of the line.
125          */
126         if (buffer[c] == comment_char)
127                 return 0;
128
129         tokens[0] = &buffer[c];
130         for (n = 1; c < i; c++) {
131                 if (buffer[c] == '"') {
132                         quote = !quote;
133                         if (quote) {
134                                 if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
135 #if 0
136                                         syntax_error("stray opening quote not "
137                                             "at beginning of token");
138                                         /* NOTREACHED */
139 #endif
140                                 } else {
141                                         tokens[n-1] = &buffer[c+1];
142                                 }
143                         } else {
144                                 if ((c < i-1) && (!is_sep(buffer[c+1]))) {
145 #if 0
146                                         syntax_error("stray closing quote not "
147                                             "at end of token");
148                                         /* NOTREACHED */
149 #endif
150                                 } else {
151                                         buffer[c] = '\0';
152                                 }
153                         }
154                 }
155
156                 if (quote) {
157                         continue;
158                 }
159
160                 if (is_sep(buffer[c])) {
161                         buffer[c++] = '\0';
162                         while ((_iswhitespace(buffer[c])) && (c < i)) c++;
163                         tokens[n++] = &buffer[c--];
164                 }
165         }
166         tokens[n] = NULL;
167
168         if (quote) {
169                 tokens[0] = NULL;
170                 return 0;
171         }
172
173         return n;
174 }
175
176 static int
177 parse_crypt_options(struct crypt_options *co, char *option)
178 {
179         char    *parameter, *endptr;
180         char    *buf;
181         long    lval;
182         unsigned long long ullval;
183         int     noparam = 0;
184         FILE    *fd;
185
186         parameter = strchr(option, '=');
187         noparam = (parameter == NULL);
188         if (!noparam)
189         {
190                 *parameter = '\0';
191                 ++parameter;
192         }
193
194         if (strcmp(option, "tries") == 0) {
195                 if (noparam)
196                         syntax_error("The option 'tries' needs a parameter");
197                         /* NOTREACHED */
198
199                 lval = strtol(parameter, &endptr, 10);
200                 if (*endptr != '\0')
201                         syntax_error("The option 'tries' expects an integer "
202                             "parameter, not '%s'", parameter);
203                         /* NOTREACHED */
204
205                 co->tries = (int)lval;
206         } else if (strcmp(option, "timeout") == 0) {
207                 if (noparam)
208                         syntax_error("The option 'timeout' needs a parameter");
209                         /* NOTREACHED */
210
211                 ullval = strtoull(parameter, &endptr, 10);
212                 if (*endptr != '\0')
213                         syntax_error("The option 'timeout' expects an integer "
214                             "parameter, not '%s'", parameter);
215                         /* NOTREACHED */
216
217                 co->timeout = ullval;
218         } else if (strcmp(option, "keyscript") == 0) {
219                 if (noparam)
220                         syntax_error("The option 'keyscript' needs a parameter");
221                         /* NOTREACHED */
222
223                 /* Allocate safe key memory */
224                 buf = alloc_safe_mem(8192);
225                 if (buf == NULL)
226                         err(1, "Could not allocate safe memory");
227                         /* NOTREACHED */
228
229                 fd = popen(parameter, "r");
230                 if (fd == NULL)
231                         syntax_error("The 'keyscript' file could not be run");
232                         /* NOTREACHED */
233
234                 if ((fread(buf, 1, sizeof(buf), fd)) == 0)
235                         syntax_error("The 'keyscript' program failed");
236                         /* NOTREACHED */
237                 pclose(fd);
238
239                 /* Get rid of trailing new-line */
240                 if ((endptr = strrchr(buf, '\n')) != NULL)
241                         *endptr = '\0';
242
243                 co->passphrase = buf;
244         } else if (strcmp(option, "none") == 0) {
245                 /* Valid option, does nothing */
246         } else {
247                 syntax_error("Unknown option: %s", option);
248                 /* NOTREACHED */
249         }
250
251         return 0;
252 }
253
254 static int
255 entry_parser(char **tokens, char **options, int type)
256 {
257         struct crypt_options co;
258         int r, i, error;
259
260         if (entry_check_num_args(tokens, 2) != 0)
261                 return 1;
262
263         bzero(&co, sizeof(co));
264
265         co.icb = &cmd_icb;
266         co.tries = 3;
267         co.name = tokens[0];
268         co.device = tokens[1];
269
270         /* (Try to) parse extra options */
271         for (i = 0; options[i] != NULL; i++)
272                 parse_crypt_options(&co, options[i]);
273
274         /* Verify that the device is indeed a LUKS-formatted device */
275         error = crypt_isLuks(&co);
276         if (error) {
277                 printf("crypttab: line %d: device %s is not a luks device\n",
278                     line_no, co.device);
279                 return 1;
280         }
281
282         if (type == CRYPTDISKS_STOP) {
283                 /* Check if the device is active */
284                 r = crypt_query_device(&co);
285
286                 /* If r > 0, then the device is active */
287                 if (r <= 0)
288                         return 0;
289
290                 /* Actually close the device */
291                 crypt_remove_device(&co);
292         } else if (type == CRYPTDISKS_START) {
293                 if ((tokens[2] != NULL) && (strcmp(tokens[2], "none") != 0)) {
294                         /* We got a keyfile */
295                         co.key_file = tokens[2];
296                 }
297
298                 /* Open the device */
299                 crypt_luksOpen(&co);
300         }
301
302         return 0;
303 }
304
305 static int
306 process_line(FILE* fd, int type)
307 {
308         char buffer[4096];
309         char *tokens[256];
310         char *options[256];
311         int c, n, i = 0;
312         int ret = 0;
313
314         while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
315                 buffer[i++] = (char)c;
316                 if (i == (sizeof(buffer) -1))
317                         break;
318         }
319         buffer[i] = '\0';
320
321         if (feof(fd) || ferror(fd))
322                 ret = 1;
323
324
325         n = line_tokenize(buffer, &iswhitespace, '#', tokens);
326
327         /*
328          * If there are not enough arguments for any function or it is
329          * a line full of whitespaces, we just return here. Or if a
330          * quote wasn't closed.
331          */
332         if ((n < 2) || (tokens[0][0] == '\0'))
333                 return ret;
334
335         /*
336          * If there are at least 4 tokens, one of them (the last) is a list
337          * of options.
338          */
339         if (n >= 4)
340         {
341                 i = line_tokenize(tokens[3], &iscomma, '#', options);
342                 if (i == 0)
343                         syntax_error("Invalid expression in options token");
344                         /* NOTREACHED */
345         }
346
347         entry_parser(tokens, options, type);
348
349         return ret;
350 }
351
352
353 int
354 main(int argc, char *argv[])
355 {
356         FILE *fd;
357         int ch, start = 0, stop = 0;
358
359         while ((ch = getopt(argc, argv, "01")) != -1) {
360                 switch (ch) {
361                 case '1':
362                         start = 1;
363                         break;
364                 case '0':
365                         stop = 1;
366                         break;
367                 default:
368                         break;
369                 }
370         }
371
372         argc -= optind;
373         argv += optind;
374
375         atexit(check_and_purge_safe_mem);
376
377         if ((start && stop) || (!start && !stop))
378                 errx(1, "please specify exactly one of -0 and -1");
379
380         fd = fopen("/etc/crypttab", "r");
381         if (fd == NULL)
382                 err(1, "fopen");
383                 /* NOTREACHED */
384
385         while (process_line(fd, (start) ? CRYPTDISKS_START : CRYPTDISKS_STOP) == 0)
386                 ++line_no;
387
388         fclose(fd);
389         return 0;
390 }
391