commit 7ac8303c17f9aa1e28c1baedf67e579f4b91f91f
Author: Naveen Narayanan zerous <zerous@nocebo.space>
Date: Fri, 29 Dec 2017 19:57:44 +0300
initial commit
Diffstat:
A | LICENSE | | | 13 | +++++++++++++ |
A | Makefile | | | 33 | +++++++++++++++++++++++++++++++++ |
A | README.md | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | config.mk | | | 10 | ++++++++++ |
A | log.c | | | 154 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | pass.1 | | | 0 | |
A | pass.c | | | 345 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | pass.h | | | 14 | ++++++++++++++ |
A | readpassphrase.c | | | 187 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | readpassphrase.h | | | 31 | +++++++++++++++++++++++++++++++ |
10 files changed, 850 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,13 @@
+© 2017-2019 Naveen Narayanan <zerous@nocebo.space>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,33 @@
+VERSION = 0.1
+
+include config.mk
+
+OBJ = pass.o \
+ log.o \
+ readpassphrase.o
+
+BIN = pass
+
+all: ${BIN}
+
+${BIN}: ${OBJ}
+ ${CC} ${LDFLAGS} -o $@ ${OBJ} ${LIBS}
+
+${OBJ}: pass.h
+
+install: all
+ mkdir -p ${DESTDIR}${PREFIX}/bin
+ cp -f ${BIN} ${DESTDIR}${PREFIX}/bin
+ chmod 755 ${DESTDIR}${PREFIX}/bin/${BIN}
+ mkdir -p ${DESTDIR}${MANPREFIX}/man1
+ cp -f ${BIN}.1 ${DESTDIR}${MANPREFIX}/man1
+ chmod 644 ${DESTDIR}${MANPREFIX}/man1/${BIN}.1
+
+uninstall:
+ rm -f ${DESTDIR}${PREFIX}/bin/${BIN} \
+ ${DESTDIR}${MANPREFIX}/man1/${BIN}.1
+
+clean:
+ rm -f ${OBJ} pass pass.core pass.o
+
+.PHONY: all clean install uninstall
diff --git a/README.md b/README.md
@@ -0,0 +1,63 @@
+# pass
+
+A stripped down version of the standard unix password manager "pass" in C.
+
+# Dependencies
+
+gpg2
+libgpgme
+
+# Installation
+
+Edit config.mk to match your setup (pass is installed into
+/usr/local/ namespace by default).
+
+Post-configuration:
+
+$ make
+$ doas make install
+
+# Documentation:
+
+* With pass, each password lives inside of a gpg encrypted file whose filename is
+ the title of the website or resource that requires the password.
+* These encrypted files may be dealt with like any other file & manipulated
+ using standard command line file management utilities.
+* Passwords live inside the respective files in ~/.password-store.
+* pass provides some commands for adding, removing and retrieving passwords.
+* Editing passwords is possible by overwriting them.
+
+# Commands
+
+init:
+ initialize new password storage and use a gpg-uid for encryption.
+
+insert:
+ insert a new password into the password store.
+
+rm:
+ remove password from the password store.
+
+show:
+ "show" is just a place-holder. Invoking pass with the filename will
+ decrypt it and print the password to stdout.
+
+help:
+ Show usage message.
+
+ver:
+ Show version information
+
+# Credits
+
+Jason at zx2c4 dot com <https://www.passwordstore.org/>
+
+# Extras
+
+pkg_install tree pwgen
+
+You may use tree to view the structure of files and directories in
+~/.password-store.
+You can pipeline the output of pwgen to pass; creating complex passwords.
+ pwgen -1 | pass insert Email/userid@domain.com
+Note: pass will read input until newline.
diff --git a/config.mk b/config.mk
@@ -0,0 +1,10 @@
+PREFIX = /usr/local
+MANPREFIX = ${PREFIX}/man
+
+CFLAGS ?=-Os
+CFLAGS +=-pedantic -Wall -Wextra -g
+CFLAGS +=-I/usr/include -I/usr/local/include
+CFLAGS +=-DVERSION=\"${VERSION}\"
+LDFLAGS +=-L/usr/local/lib
+
+LIBS +=-lgpgme -lassuan -lgpg-error
diff --git a/log.c b/log.c
@@ -0,0 +1,154 @@
+#include <errno.h>
+#include <gpgme.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pass.h"
+
+int debug;
+
+static char *progname;
+
+static void
+vlog(char *msg, va_list ap)
+{
+ char file[PATH_MAX];
+ const char *home;
+ FILE *fp;
+ struct stat sb;
+
+ if (debug) {
+ if (!(home = getenv("HOME"))) {
+ fprintf(stderr, "$HOME not set, cannot determine password-store location");
+ exit(1);
+ }
+ snprintf(file, sizeof(file), "%s/.pass.log", home);
+
+ if (!stat(file,&sb))
+ remove(file);
+ if((fp = fopen(file, "ab"))) {
+ fprintf(fp, "%s: ", progname);
+ vfprintf(fp, msg, ap);
+ fputc('\n', fp);
+ } else {
+ fprintf(stderr, "fopen %s: %s", file, strerror(errno));
+ }
+ } else {
+ fprintf(stderr, "%s: ", progname);
+ vfprintf(stderr, msg, ap);
+ fputc('\n', stderr);
+ }
+}
+
+void
+loginit(char *prog)
+{
+ progname = prog;
+}
+
+void
+logdbg(char *msg, ...)
+{
+ char buf[512];
+ va_list ap;
+
+ va_start(ap, msg);
+ snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
+ vlog(buf, ap);
+ va_end(ap);
+}
+
+void
+logdbgx(char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ vlog(msg, ap);
+ va_end(ap);
+}
+
+void
+logwarn(char *msg, ...)
+{
+ char buf[512];
+ va_list ap;
+
+ va_start(ap, msg);
+ snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
+ vlog(buf, ap);
+ va_end(ap);
+}
+
+void
+logwarnx(char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ vlog(msg, ap);
+ va_end(ap);
+}
+
+void
+loggpg(gpgme_error_t gerr, char *msg, ...)
+{
+ char buf[512];
+ va_list ap;
+
+ va_start(ap, msg);
+ snprintf(buf, sizeof(buf), "%s: %s", msg, gpgme_strerror(gerr));
+ vlog(msg, ap);
+ va_end(ap);
+}
+
+void
+loggpgx(char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ vlog(msg, ap);
+ va_end(ap);
+}
+
+void
+fatalgpg(gpgme_error_t gerr, char *msg, ...)
+{
+ char buf[512];
+ va_list ap;
+
+ va_start(ap, msg);
+ snprintf(buf, sizeof(buf), "%s: %s", msg, gpgme_strerror(gerr));
+ vlog(buf, ap);
+ va_end(ap);
+ exit(1);
+}
+
+void
+fatal(char *msg, ...)
+{
+ char buf[512];
+ va_list ap;
+
+ va_start(ap, msg);
+ snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
+ vlog(buf, ap);
+ va_end(ap);
+ exit(1);
+}
+
+void
+fatalx(char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ vlog(msg, ap);
+ va_end(ap);
+ exit(1);
+}
diff --git a/pass.1 b/pass.1
diff --git a/pass.c b/pass.c
@@ -0,0 +1,345 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <fcntl.h>
+#include <gpgme.h>
+#include <getopt.h>
+#include <limits.h>
+#include <libgen.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "pass.h"
+#include "readpassphrase.h"
+
+#define MAX_BUFSIZ 512
+static char file[PATH_MAX];
+const char *home;
+
+void
+initgpgme(void)
+{
+ const char *reqlibver = "1.6.0";
+ const char *reqgpgver = "2.1.0";
+
+ setlocale(LC_ALL, "en_US.UTF-8");
+ gpgme_set_locale(NULL, LC_ALL, "en_US.UTF-8");
+ gpgme_set_global_flag("require-gnupg", reqgpgver);
+ gpgme_check_version(reqlibver);
+}
+
+void
+gethomedir(void)
+{
+ if (!(home = getenv("HOME")))
+ fatalx("$HOME not set, cannot determine password-store location");
+}
+
+
+void
+delete(char *item)
+{
+ char *l;
+ int r;
+
+ gethomedir();
+ snprintf(file, sizeof(file), "%s/.password-store/%s.gpg", home, item);
+ printf("Are you sure you would like to delete %s? [y/N] ", item);
+ if ((r = getchar()) == 'N' || r == 'n')
+ exit(0);
+ else if ((r == 'y' || r == 'Y') && !remove(file)) {
+ printf("removed '%s'\n", file);
+ if ((l = strrchr(file, '/')))
+ *l = '\0';
+ rmdir(file);
+ }
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "usage:\tpass init gpg-id\n"
+ "\tpass insert pass-name\n"
+ "\tpass pass-name\n"
+ "\tpass rm pass-name\n"
+ );
+ exit(1);
+}
+
+void
+decrypt(char *buf)
+{
+ gpgme_data_t in, out;
+ gpgme_error_t gpgerr;
+ gpgme_ctx_t ctx;
+ gpgme_protocol_t proto;
+ int a, ret;
+
+ initgpgme();
+ proto = GPGME_PROTOCOL_OpenPGP;
+
+ gpgerr = gpgme_new(&ctx);
+ if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
+ fatalgpg(gpgerr, "Error: gpgme_new: %s");
+ gpgerr = gpgme_set_protocol(ctx, proto);
+ if (gpgme_err_code(gpgerr) == GPG_ERR_INV_VALUE)
+ fatalgpg(gpgerr, "Error: gpgme_set_protocol");
+ gpgerr = gpgme_data_new_from_file(&in, file, 1);
+ if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
+ fatalgpg(gpgerr, "Error: gpgme_data_new_from_file");
+ gpgerr = gpgme_data_new(&out);
+ if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
+ fatalgpg(gpgerr, "Error: gpgme_data_new");
+ gpgerr = gpgme_op_decrypt(ctx, in, out);
+ if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
+ fatalgpg(gpgerr, "Error: gpgme_op_decrypt");
+ ret = gpgme_data_seek(out, 0, SEEK_SET);
+ if (ret)
+ fatalx("gpgme_data_seek");
+ if ((a = gpgme_data_read(out, buf, 100)) > 0)
+ buf[a] = '\0';
+ gpgme_data_release(in);
+ gpgme_data_release(out);
+ gpgme_release(ctx);
+}
+
+
+void
+printpass(char *item)
+{
+ char buf[MAX_BUFSIZ];
+ int fin;
+
+ gethomedir();
+ snprintf(file, sizeof(file), "%s/.password-store/%s.gpg", home, item);
+ /* Check if file exists */
+ fin = open(file, O_RDONLY);
+ if (fin == -1)
+ fatal("%s is not in password store.", file);
+ decrypt(buf);
+ printf("%s\n", buf);
+ close(fin);
+}
+
+void
+getuserid(char *u, int usize)
+{
+ char file[PATH_MAX];
+ FILE *fp;
+ int i;
+
+ snprintf(file, sizeof(file), "%s/.password-store/.gpg-id", home);
+ fp = fopen(file, "r");
+ if (!fp)
+ fatal("fopen: %s", file);
+ while ((i = fgetc(fp)) != EOF && usize--)
+ *u++ = i;
+ *u = '\0';
+}
+
+void
+mkdirp(const char *tp)
+{
+ char *i, t[PATH_MAX];
+ int r;
+ mode_t mode = S_IRWXU;
+
+ memcpy(t, file, strlen(file) + 1);
+ while ((i = strchr(tp, '/'))) {
+ *i = '\0';
+ snprintf(file, sizeof(file), "%s/%s", t, tp);
+ printf("mkdir %s\n", file);
+ if ((r = mkdir(file, mode)) == -1) {
+ if (errno != EEXIST)
+ fatal("mkdir");
+ }
+ tp = i + 1;
+ }
+}
+
+void
+encrypt()
+{
+ gpgme_data_t in, out;
+ gpgme_error_t gpgerr;
+ gpgme_ctx_t ctx;
+ gpgme_key_t key;
+ gpgme_key_t keys[2];
+ gpgme_protocol_t proto;
+ char uid[MAX_BUFSIZ], t[PATH_MAX];
+ FILE *fin, *fout;
+
+ proto = GPGME_PROTOCOL_OpenPGP;
+ key = NULL;
+
+ initgpgme();
+ getuserid(uid, MAX_BUFSIZ);
+ gpgerr = gpgme_new(&ctx);
+ if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
+ fatalgpg(gpgerr, "gpme_new");
+ gpgerr = gpgme_set_protocol(ctx, proto);
+ if (gpgme_err_code(gpgerr) == GPG_ERR_INV_VALUE)
+ fatalgpg(gpgerr, "gpgme_set_protocol");
+ gpgme_set_armor(ctx, 1);
+ if (gpgme_op_keylist_start(ctx, uid, 0) != GPG_ERR_INV_VALUE)
+ while (!(gpgerr = gpgme_op_keylist_next(ctx, &key))) {
+ if (key->can_encrypt)
+ break;
+ }
+ if (gpgme_err_code(gpgerr) == GPG_ERR_EOF)
+ fatalgpg(gpgerr, "can not find key");
+ keys[0] = key;
+ keys[1] = NULL;
+ fin = fopen(file, "r");
+ memcpy(t, file, strlen(file) + 1);
+ snprintf(file, sizeof(file), "%s.gpg", t);
+ fout = fopen(file, "w");
+ gpgerr = gpgme_data_new_from_stream(&in, fin);
+ if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
+ fatalgpg(gpgerr, "gpgme_data_new_from_stream");
+ gpgerr = gpgme_data_new_from_stream(&out, fout);
+ gpgme_data_set_encoding(out, GPGME_DATA_ENCODING_ARMOR);
+ if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
+ fatalgpg(gpgerr, "gpgme_data_new_from_stream");
+ gpgerr = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
+ if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
+ fatalgpg(gpgerr, "gpgme_op_encrypt");
+ if (remove(t))
+ fprintf(stderr, "remove failed\n");
+ gpgme_key_release(key);
+ gpgme_data_release(in);
+ gpgme_data_release(out);
+ gpgme_release(ctx);
+}
+
+void
+insert(char *item)
+{
+ char *filename, t[PATH_MAX];
+ FILE *fp;
+ char pass[MAX_BUFSIZ];
+ int c, fd;
+
+ c = 'y';
+ fd = fileno(stdin);
+
+ gethomedir();
+ snprintf(file, sizeof(file), "%s/.password-store", home);
+ filename = basename(item);
+ mkdirp(item);
+ memcpy(t, file, strlen(file) + 1);
+ snprintf(file, sizeof(file), "%s/%s", t, filename);
+ snprintf(t, sizeof(t), "%s.gpg", file);
+ if ((fp = fopen(t, "r"))) {
+ if (isatty(fd)) {
+ printf("An entry already exists for %s. Overwrite it? [y/N] ", filename);
+ if ((c = getchar()) == 'N' || c == 'n') {
+ fclose(fp);
+ logdbgx("Don't overwrite");
+ exit(1);
+ }
+ } else
+ /* Assuming user knows what he/she is doing */
+ printf("Overwriting %s\n", filename);
+ }
+ if (c != 'Y' && c != 'y')
+ exit(1);
+ if (!(fp = fopen(file, "w+b")))
+ fatal("fopen: %s", file);
+ if (isatty(fd)) {
+ readpassphrase("Enter password: ", pass, MAX_BUFSIZ, RPP_ECHO_OFF);
+ memcpy(t, pass, strlen(pass) + 1);
+ readpassphrase("Retype password: ", pass, MAX_BUFSIZ, RPP_ECHO_OFF);
+ if (!strcmp(pass, t))
+ {
+ int i = 0;
+ while(t[i] != '\0') {
+ if (fputc(t[i], fp) == EOF)
+ fatal("fputc: %s", file);
+ i++;
+ }
+ } else
+ fatalx("Passwords don't match.");
+ } else {
+ int c;
+ while ((c = getchar()) != EOF && c != '\n')
+ fputc(c, fp);
+ }
+ fclose(fp);
+ encrypt();
+}
+
+int
+isinit(void)
+{
+ int fp;
+ struct stat sb;
+
+ gethomedir();
+ snprintf(file, sizeof(file), "%s/.password-store", home);
+ if ((fp = open(file, O_RDONLY)) != -1 &&
+ (!fstat(fp, &sb)) &&
+ (S_ISDIR(sb.st_mode))) {
+ close(fp);
+ return 1;
+ }
+ return 0;
+}
+
+void
+initpass(char *pgpid)
+{
+ FILE *gpg;
+ mode_t mode = S_IRWXU;
+
+ if (!pgpid) {
+ usage();
+ }
+ if (!isinit())
+ {
+ if (mkdir(file, mode))
+ fatal("mkdir");
+ snprintf(file, sizeof(file), "%s/.password-store/.gpg-id", home);
+ if (!(gpg = fopen(file, "a")))
+ fatal("fopen");
+ if (fputs(pgpid, gpg) == EOF)
+ fatal("fputs");
+ printf("Password store initialized for %s\n", pgpid);
+ fclose(gpg);
+ } else
+ printf("Password store initialized for %s\n", pgpid);
+}
+
+int
+main(int argc, char** argv)
+{
+ char **s;
+ int i;
+
+ debug = 0;
+
+ for (s = argv, i = 0; i < argc; i++) {
+ if (!strcmp("-d", *s++))
+ debug = 1;
+ }
+ if (argc > 1)
+ {
+ argv++;
+ loginit("pass");
+ if (!strcmp("init", *argv))
+ initpass(*(argv + 1));
+ else if (!strcmp("insert", *argv))
+ insert(*(argv + 1));
+ else if (!strcmp("rm", *argv))
+ delete(*(argv + 1));
+ else
+ printpass(*argv);
+ } else
+ usage();
+ return 0;
+}
diff --git a/pass.h b/pass.h
@@ -0,0 +1,14 @@
+/* log.c */
+extern int debug;
+
+/* log.c */
+void loginit(char *);
+void logdbg(char *, ...);
+void logdbgx(char *, ...);
+void logwarn(char *, ...);
+void logwarnx(char *, ...);
+void fatal(char *, ...);
+void fatalx(char *, ...);
+void loggpg(gpgme_error_t, char *, ...);
+void loggpgx(char *, ...);
+void fatalgpg(gpgme_error_t, char *, ...);
diff --git a/readpassphrase.c b/readpassphrase.c
@@ -0,0 +1,187 @@
+/* $OpenBSD: readpassphrase.c,v 1.25 2015/09/14 10:45:27 guenther Exp $ */
+
+/*
+ * Copyright (c) 2000-2002, 2007, 2010
+ * Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "readpassphrase.h"
+
+#ifdef TCSASOFT
+#define _T_FLUSH (TCSAFLUSH|TCSASOFT)
+#else
+#define _T_FLUSH (TCSAFLUSH)
+#endif
+
+static volatile sig_atomic_t signo[_NSIG];
+
+static void handler(int);
+
+char *
+readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
+{
+ ssize_t nr;
+ int input, output, save_errno, i, need_restart;
+ char ch, *p, *end;
+ struct termios term, oterm;
+ struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
+ struct sigaction savetstp, savettin, savettou, savepipe;
+
+ /* I suppose we could alloc on demand in this case (XXX). */
+ if (bufsiz == 0) {
+ errno = EINVAL;
+ return(NULL);
+ }
+
+restart:
+ for (i = 0; i < _NSIG; i++)
+ signo[i] = 0;
+ nr = -1;
+ save_errno = 0;
+ need_restart = 0;
+ /*
+ * Read and write to /dev/tty if available. If not, read from
+ * stdin and write to stderr unless a tty is required.
+ */
+ if ((flags & RPP_STDIN) ||
+ (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
+ if (flags & RPP_REQUIRE_TTY) {
+ errno = ENOTTY;
+ return(NULL);
+ }
+ input = STDIN_FILENO;
+ output = STDERR_FILENO;
+ }
+
+ /*
+ * Turn off echo if possible.
+ * If we are using a tty but are not the foreground pgrp this will
+ * generate SIGTTOU, so do it *before* installing the signal handlers.
+ */
+ if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
+ memcpy(&term, &oterm, sizeof(term));
+ if (!(flags & RPP_ECHO_ON))
+ term.c_lflag &= ~(ECHO | ECHONL);
+#ifdef VSTATUS
+ if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
+ term.c_cc[VSTATUS] = _POSIX_VDISABLE;
+#endif
+ (void)tcsetattr(input, _T_FLUSH, &term);
+ } else {
+ memset(&term, 0, sizeof(term));
+ term.c_lflag |= ECHO;
+ memset(&oterm, 0, sizeof(oterm));
+ oterm.c_lflag |= ECHO;
+ }
+
+ /*
+ * Catch signals that would otherwise cause the user to end
+ * up with echo turned off in the shell. Don't worry about
+ * things like SIGXCPU and SIGVTALRM for now.
+ */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0; /* don't restart system calls */
+ sa.sa_handler = handler;
+ (void)sigaction(SIGALRM, &sa, &savealrm);
+ (void)sigaction(SIGHUP, &sa, &savehup);
+ (void)sigaction(SIGINT, &sa, &saveint);
+ (void)sigaction(SIGPIPE, &sa, &savepipe);
+ (void)sigaction(SIGQUIT, &sa, &savequit);
+ (void)sigaction(SIGTERM, &sa, &saveterm);
+ (void)sigaction(SIGTSTP, &sa, &savetstp);
+ (void)sigaction(SIGTTIN, &sa, &savettin);
+ (void)sigaction(SIGTTOU, &sa, &savettou);
+
+ if (!(flags & RPP_STDIN))
+ (void)write(output, prompt, strlen(prompt));
+ end = buf + bufsiz - 1;
+ p = buf;
+ while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
+ if (p < end) {
+ if ((flags & RPP_SEVENBIT))
+ ch &= 0x7f;
+ if (isalpha((unsigned char)ch)) {
+ if ((flags & RPP_FORCELOWER))
+ ch = (char)tolower((unsigned char)ch);
+ if ((flags & RPP_FORCEUPPER))
+ ch = (char)toupper((unsigned char)ch);
+ }
+ *p++ = ch;
+ }
+ }
+ *p = '\0';
+ save_errno = errno;
+ if (!(term.c_lflag & ECHO))
+ (void)write(output, "\n", 1);
+
+ /* Restore old terminal settings and signals. */
+ if (memcmp(&term, &oterm, sizeof(term)) != 0) {
+ while (tcsetattr(input, _T_FLUSH, &oterm) == -1 &&
+ errno == EINTR && !signo[SIGTTOU])
+ continue;
+ }
+ (void)sigaction(SIGALRM, &savealrm, NULL);
+ (void)sigaction(SIGHUP, &savehup, NULL);
+ (void)sigaction(SIGINT, &saveint, NULL);
+ (void)sigaction(SIGQUIT, &savequit, NULL);
+ (void)sigaction(SIGPIPE, &savepipe, NULL);
+ (void)sigaction(SIGTERM, &saveterm, NULL);
+ (void)sigaction(SIGTSTP, &savetstp, NULL);
+ (void)sigaction(SIGTTIN, &savettin, NULL);
+ (void)sigaction(SIGTTOU, &savettou, NULL);
+ if (input != STDIN_FILENO)
+ (void)close(input);
+
+ /*
+ * If we were interrupted by a signal, resend it to ourselves
+ * now that we have restored the signal handlers.
+ */
+ for (i = 0; i < _NSIG; i++) {
+ if (signo[i]) {
+ kill(getpid(), i);
+ switch (i) {
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ need_restart = 1;
+ }
+ }
+ }
+ if (need_restart)
+ goto restart;
+
+ if (save_errno)
+ errno = save_errno;
+ return(nr == -1 ? NULL : buf);
+}
+
+static void handler(int s)
+{
+ signo[s] = 1;
+}
diff --git a/readpassphrase.h b/readpassphrase.h
@@ -0,0 +1,31 @@
+/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */
+
+/*
+ * Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */
+#define RPP_ECHO_ON 0x01 /* Leave echo on. */
+#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */
+#define RPP_FORCELOWER 0x04 /* Force input to lower case. */
+#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */
+#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */
+#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */
+
+char *readpassphrase(const char *, char *, size_t, int);