New patches here: The variable key size patch has been touched up a bit but is functionally identical. The external program patch has been reworked. The external program is now specified like this: mount -o loop,encryption=aes-cbc-128,keygen=hashprog;sha256 \ ~sluskyb/testloop /mnt/test The looped file and the encryption method are passed as additional arguments, so this would run "hashprog sha256 ~sluskyb/testloop aes-cbc-128" and read the encryption key from its stdout. There is no equivalent option in losetup, but then hashprog sha256 |losetup -e aes-cbc-128 -p 0 /dev/loop/11 \ ~sluskyb/testloop works just fine. I've cut out xgetpass() entirely. Now that hashing is done outside losetup, there's no sense in reading any more than LO_KEY_SIZE bytes. So if we're given an fd or an external program then we do a plain old read(2), otherwise a plain old getpass(3). QCF? -- Ben Slusky | "You think that's air you're sluskyb@xxxxxxxxxxxxxx | breathing now?" sluskyb@xxxxxxxxxx | -Morpheus PGP keyID ADA44B3B
--- lomount.c-fixed-key-size 2003-07-16 19:56:53.000000000 -0400 +++ lomount.c 2003-08-07 13:34:22.000000000 -0400 @@ -24,12 +24,15 @@ #include <ctype.h> #include <fcntl.h> #include <errno.h> +#include <limits.h> #include <stdlib.h> #include <unistd.h> +#include <sys/types.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/mman.h> #include <sys/sysmacros.h> +#include <regex.h> #include "loop.h" #include "lomount.h" @@ -249,7 +252,7 @@ set_loop(const char *device, const char *file, int offset, const char *encryption, int pfd, int *loopro) { struct loop_info64 loopinfo64; - int fd, ffd, mode; + int fd, ffd, mode, keysize = 0; char *pass; mode = (*loopro ? O_RDONLY : O_RDWR); @@ -275,9 +278,38 @@ if (digits_only(encryption)) { loopinfo64.lo_encrypt_type = atoi(encryption); } else { + regex_t keysize_re; + regmatch_t keysize_rm; + int rerror; + char rerror_buf[LINE_MAX+1], + *encryption_dup = xstrdup(encryption); + loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI; + + if ((rerror = regcomp(&keysize_re, + "-[[:digit:]]+$", + REG_EXTENDED)) != 0) { + fprintf(stderr, _("regcomp: ")); + regerror(rerror, &keysize_re, rerror_buf, + LINE_MAX); + fprintf(stderr, "%s\n", rerror_buf); + } else { + if (regexec(&keysize_re, encryption_dup, + 1, &keysize_rm, 0) == 0) { + keysize = atoi(encryption_dup + + keysize_rm.rm_so + 1); + /* convert #bits to #bytes */ + keysize /= 8; + /* cut the keysize out now */ + encryption_dup[keysize_rm.rm_so] = '\0'; + } + + regfree(&keysize_re); + } + snprintf(loopinfo64.lo_crypt_name, LO_NAME_SIZE, - "%s", encryption); + "%s", encryption_dup); + free(encryption_dup); } } @@ -307,9 +339,14 @@ strlen(loopinfo64.lo_encrypt_key); break; default: + if (keysize == 0) { + fprintf(stderr, _("You must specify a key size (in bits) for use with CryptoAPI encryption.\n")); + return -1; + } + /* FIXME we should be checking keysize against the min/max in /proc/crypto */ pass = xgetpass(pfd, _("Password: ")); xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE); - loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE; + loopinfo64.lo_encrypt_key_size = keysize; } if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
--- lomount.c-no-reader-prog 2003-08-07 13:48:35.000000000 -0400 +++ lomount.c 2003-08-07 16:32:26.000000000 -0400 @@ -33,6 +33,7 @@ #include <sys/mman.h> #include <sys/sysmacros.h> #include <regex.h> +#include <signal.h> #include "loop.h" #include "lomount.h" @@ -201,45 +202,6 @@ return 0; } -/* - * A function to read the passphrase either from the terminal or from - * an open file descriptor. - */ -static char * -xgetpass(int pfd, const char *prompt) { - char *pass; - int buflen, i; - - if (pfd < 0) /* terminal */ - return getpass(prompt); - - pass = NULL; - buflen = 0; - for (i=0; ; i++) { - if (i >= buflen-1) { - /* we're running out of space in the buffer. - * Make it bigger: */ - char *tmppass = pass; - buflen += 128; - pass = realloc(tmppass, buflen); - if (pass == NULL) { - /* realloc failed. Stop reading. */ - error("Out of memory while reading passphrase"); - pass = tmppass; /* the old buffer hasn't changed */ - break; - } - } - if (read(pfd, pass+i, 1) != 1 || pass[i] == '\n') - break; - } - if (pass == NULL) - return ""; - else { - pass[i] = 0; - return pass; - } -} - static int digits_only(const char *s) { while (*s) @@ -344,8 +306,26 @@ return -1; } /* FIXME we should be checking keysize against the min/max in /proc/crypto */ - pass = xgetpass(pfd, _("Password: ")); - xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE); + + if (pfd == -1) { + pass = getpass(_("Password: ")); + xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE); + } else { + /* If we're reading from an extenral program, * + * odds are good that a SIGCHLD will interrupt * + * this read(), and ruin our whole day. So we * + * must block it. */ + sigset_t ss, oss; + sigemptyset(&ss); + sigaddset(&ss, SIGCHLD); + sigprocmask(SIG_BLOCK, &ss, &oss); + if (read(pfd, loopinfo64.lo_encrypt_key, + LO_KEY_SIZE) == -1) { + perror("read"); + fprintf(stderr, _("Error reading encryption key, exiting\n")); + } + sigprocmask(SIG_SETMASK, &oss, NULL); + } loopinfo64.lo_encrypt_key_size = keysize; } --- mount.8-no-reader-prog 2003-08-04 00:26:04.000000000 -0400 +++ mount.8 2003-08-07 17:02:39.000000000 -0400 @@ -1696,6 +1696,11 @@ .BR loop ", " offset " and " encryption , that are really options to .BR losetup (8). +You can also use the +.BR keygen +option to have mount call an external program from, which it will read the +encryption key. Arguments to this program can be given, separated by semicolons. + If no explicit loop device is mentioned (but just an option `\fB\-o loop\fP' is given), then .B mount --- mount.c-no-reader-prog 2003-07-15 17:38:48.000000000 -0400 +++ mount.c 2003-08-07 16:43:12.000000000 -0400 @@ -195,7 +195,7 @@ }; static char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_encryption, - *opt_speed; + *opt_keygen, *opt_speed; static struct string_opt_map { char *tag; @@ -206,6 +206,7 @@ { "vfs=", 1, &opt_vfstype }, { "offset=", 0, &opt_offset }, { "encryption=", 0, &opt_encryption }, + { "keygen=", 0, &opt_keygen }, { "speed=", 0, &opt_speed }, { NULL, 0, NULL } }; @@ -586,7 +587,7 @@ *type = opt_vfstype; } - *loop = ((*flags & MS_LOOP) || *loopdev || opt_offset || opt_encryption); + *loop = ((*flags & MS_LOOP) || *loopdev || opt_offset || opt_encryption || opt_keygen); *loopfile = *spec; if (*loop) { @@ -596,6 +597,11 @@ printf(_("mount: skipping the setup of a loop device\n")); } else { int loopro = (*flags & MS_RDONLY); + /* Extra args to the keygen program. Right now there are 2: * + * - the looped file * + * - the encryption type used */ + char *keygen_args[] = {*loopfile, opt_encryption}; + const int _n_keygen_args = 2; if (!*loopdev || !**loopdev) *loopdev = find_unused_loop_device(); @@ -604,6 +610,8 @@ if (verbose) printf(_("mount: going to use the loop device %s\n"), *loopdev); offset = opt_offset ? strtoul(opt_offset, NULL, 0) : 0; + if (opt_keygen) + pfd = use_keygen_prog(opt_keygen, keygen_args, _n_keygen_args); if (set_loop(*loopdev, *loopfile, offset, opt_encryption, pfd, &loopro)) { if (verbose) --- sundries.c-no-reader-prog 2003-08-04 00:24:47.000000000 -0400 +++ sundries.c 2003-08-07 16:53:40.000000000 -0400 @@ -12,6 +12,8 @@ #include <stdio.h> #include <string.h> #include <mntent.h> /* for MNTTYPE_SWAP */ +#include <sys/types.h> +#include <sys/wait.h> #include "fstab.h" #include "sundries.h" #include "realpath.h" @@ -285,3 +287,101 @@ free(canonical); return xstrdup(path); } + +static volatile pid_t keygen_pid = -1; + +/* Handle a SIGCHLD -- wait for the child process */ +static void +child_handler (int i) { + int status; + if (keygen_pid != -1 && wait(&status) == keygen_pid) { + keygen_pid = -1; + if (WEXITSTATUS(status) != 0) + exit(WEXITSTATUS(status)); + } +} + +/* Make sure we clean up after the child */ +static void +child_cleanup (void) { + /* "Clean up" means wait. So we let child_handler do all the work */ + while (keygen_pid != -1) + sleep(1); +} + +/* Split a string into pieces, using delim as the delimiter. * + * Returns the number of pieces. */ +static int +split_args (char *args[], const char *str, const char *delim, int nargs) { + int i=0; + char *s = xstrdup(str); + args[0] = strtok(s, delim); + if (args[0] == NULL) + return 0; + + while (++i < nargs) { + if ((args[i] = strtok(NULL, delim)) == NULL) + break; + } + + return i; +} + +#define KEYGEN_MAX_ARGS 64 /* more than anyone will need, right? */ + +/* Call an external program to give us the encryption key for an * + * encrypted device. We split the string s into a command and args on * + * semicolons ('cuz you can't put spaces in the fs_mntopts field), then * + * add some specific args (eg. the looped file or device, the * + * encryption method used; check the caller to see the actual list). * + * Returns a file descriptor from which we read the key. */ +int +use_keygen_prog (const char *s, const char *addl_args[], int naddl_args) { + int fd[2]; + + struct sigaction sa = { + .sa_handler = child_handler, + .sa_flags = SA_NOCLDSTOP + }; + + if (pipe(fd) == -1) { + perror("pipe"); + exit(EX_SYSERR); + } + if (sigaction(SIGCHLD, &sa, NULL) == -1) { + perror("sigaction"); + exit(EX_SYSERR); + } + if ((keygen_pid = fork()) == -1) { + perror("fork"); + exit(EX_SYSERR); + } else if (keygen_pid) { /* parent */ + atexit(child_cleanup); + close(fd[1]); + return fd[0]; + } else { /* child */ + char *args[KEYGEN_MAX_ARGS+1]; + int i, n = split_args(args, s, ";", KEYGEN_MAX_ARGS - naddl_args); + if (!n) { + fprintf(stderr, _("Invalid keygen program, exiting\n")); + exit(EX_USAGE); + } + for(i=0; i < naddl_args; i++) + args[n+i] = addl_args[i]; + args[n+naddl_args] = NULL; + + close(fd[0]); + if (dup2(fd[1], STDOUT_FILENO) == -1) { + perror("dup2"); + exit(EX_SYSERR); + } + setuid(getuid()); /* set euid to ruid */ + if (execvp(args[0], args) == -1) { + perror(args[0]); + exit(EX_USAGE); + } + } + + return 0; /* so gcc will shut up */ +} + --- sundries.h-no-reader-prog 2002-10-31 20:00:50.000000000 -0500 +++ sundries.h 2003-08-07 17:07:52.000000000 -0400 @@ -25,6 +25,7 @@ void error (const char *fmt, ...); int matching_type (const char *type, const char *types); int matching_opts (const char *options, const char *test_opts); +int use_keygen_prog (const char *s, const char *addl_args[], int naddl_args); void *xmalloc (size_t size); char *xstrdup (const char *s); char *xstrndup (const char *s, int n);