pass

simple password manager
git clone git://git.2f30.org/pass.git
Log | Files | Refs | LICENSE

commit 7ac8303c17f9aa1e28c1baedf67e579f4b91f91f
Author: Naveen Narayanan zerous <zerous@nocebo.space>
Date:   Fri, 29 Dec 2017 19:57:44 +0300

initial commit

Diffstat:
LICENSE | 13+++++++++++++
Makefile | 33+++++++++++++++++++++++++++++++++
README.md | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
config.mk | 10++++++++++
log.c | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
pass.1 | 0
pass.c | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
pass.h | 14++++++++++++++
readpassphrase.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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);