Helper for --passphrase-fd [was: Re: Problem with Gnu-PG]

Bernd Jendrissek berndj@prism.co.za
Thu Mar 1 11:26:07 2001


Matthias Urlichs smurf@noris.de wrote:

> For a pseudo-safe way to do this, I would use a shell script somewhat
> like this one:
>
> #!/bin/sh
> echo -n "Passphrase: "; stty -echo; read pp; stty echo; echo ""
>
> for i in "$@" ; do ## each recipient
> echo "$i ..."
> echo "$pp" | gpg --passphrase-fd 0 --batch --whatever-other-arguments-"$i"
> done
>
> .. except that this is insecure, as the passphrase might end up in the
> swap file.
Here's my solution, I hope I've done it right! Please tell if I haven't. Needs a bit of assembly, very little. This is for i386: /* * sec_cat.c - secure passphrase copier * * Unfortunately, sec_cat causes other pty-writers to get stuck waiting for * a semaphore to go up. Solved. This was a bug in Linux 2.4.0-testN * (*** MY OWN KERNEL BUG ***) ;) */ #include <stdio.h> #include <sys/fcntl.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <termios.h> #include <unistd.h> char pass_buf[256]; static char char_translation[256]; static char char_class(const char *buf) { int c; /* * This has to be in assembler to guarantee that the password never * leaves its buffer; if even single chars of the password were * allowed onto the stack (due to a register spill, for instance), * over time the entire password may be paged out to disk under * unfavourable conditions. Crackers create these conditions. * * The operating system kernel should guarantee that process * registers are never paged. Linux works this way. */ __asm__("movzbl (%1),%0\n" "movzbl (%2,%0),%0\n" : "=r" (c) : "r" (buf), "r" (char_translation)); return ((char) c); } int main() { struct termios term, rawterm; unsigned long pts_num, pty_locked; int ptm, i, m, n; int got_pass; int locked = 1; int input = 0; ptm = open("/dev/ptmx", O_WRONLY); if (ptm == -1) { fprintf(stderr, "Cannot open /dev/ptmx\n"); return 1; } if (ioctl(ptm, TIOCGPTN, &pts_num)) { fprintf(stderr, "Cannot get slave tty number\n"); return 1; } pty_locked = 0; if (ioctl(ptm, TIOCSPTLCK, &pty_locked)) { fprintf(stderr, "Cannot unlock pty slave\n"); return 1; } if (mlock(pass_buf, sizeof (pass_buf))) { fprintf(stderr, "Warning: cannot lock password buffer\n"); locked = 0; } for (i = 0; i < 256; i++) { char_translation[i] = 0; } /* char_translation[0177] = 0177; */ char_translation[0012] = 0012; char_translation[0015] = 0015; input = open("/dev/tty", O_RDWR); if (input == -1) { fprintf(stderr, "Cannot open tty\n"); return 1; } write(input, "Enter password: ", 16); if (tcgetattr(input, &term)) { fprintf(stderr, "Cannot get terminal attributes\n"); return 1; } cfmakeraw(&rawterm); if (tcsetattr(input, TCSADRAIN, &rawterm)) { fprintf(stderr, "Cannot set raw terminal attributes\n"); return 1; } m = 0; got_pass = 0; while(got_pass == 0 && m < sizeof (pass_buf) - 1) { n = read(input, pass_buf + m, sizeof (pass_buf) - m); if (n == -1) { fprintf(stderr, "Cannot read from terminal\n"); return 1; } for (i = 0; i < n && got_pass == 0; i++) { /* Must *NOT* use pass_buf[m+i] directly */ switch (char_class(pass_buf + m+i)) { case 0177: /* Do allow erasure of password */ case 0012: case 0015: m++; got_pass = 1; write(input, "\r\n", 2); break; default: m++; write(input, "*", 1); } } } n = m; if (tcsetattr(input, TCSADRAIN, &term)) { fprintf(stderr, "Cannot restore terminal attributes\n"); return 1; } close(input); fprintf(stdout, "%d\n", pts_num); /* Become a daemon */ /* fork: make sure we are not a process group leader */ switch (fork()) { case -1: /* error */ fprintf(stderr, "Cannot fork\n"); return 1; case 0: /* child */ break; default: /* parent */ _exit(0); } /* Close all fd's we know */ fflush(stdout); close(0); close(1); close(2); /* This is it! */ if (setsid() == -1) { return 1; } while (1) { int retval; for (m = 0; m < n; m += retval) { retval = write(ptm, pass_buf + m, n - m); if (retval == -1) { return 2; } } } /* Really we should never be here */ if (locked) { munlock(pass_buf, sizeof (pass_buf)); /* Assume success */ } return 0; }