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

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


Matthias Urlichs smurf at 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;
}



More information about the Gnupg-devel mailing list