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 392