# HG changeset patch # User Guido Berhoerster # Date 1427893077 -7200 # Node ID 97097b4b6bfb53e1ad7cffd32cfe0bd4b78f6cae # Parent e1309515d111733e471f600eba4370f461a7e2e6 Add pwsdump utility The pwsdum utility can dump PasswordSafe database files to a plaintext format and convert this format back into a PasswordSafe database. diff -r e1309515d111 -r 97097b4b6bfb Makefile --- a/Makefile Wed Mar 25 17:10:23 2015 +0100 +++ b/Makefile Wed Apr 01 14:57:57 2015 +0200 @@ -99,38 +99,73 @@ HAVE_ARC4RANDOM ?= 0 HAVE_ENDIAN_H ?= 1 HAVE_SYS_ENDIAN_H ?= 0 + HAVE_ERR_H ?= 1 HAVE_GETENTROPY ?= 0 + HAVE_GETLINE ?= 0 + HAVE_SETPROGNAME ?= 0 + HAVE_READPASSPHRASE_H ?= 0 HAVE_SYS_TREE_H ?= 0 + HAVE_VIS_H ?= 0 else ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly),) HAVE_ARC4RANDOM ?= 1 HAVE_ENDIAN_H ?= 0 HAVE_SYS_ENDIAN_H ?= 1 + HAVE_ERR_H ?= 1 HAVE_GETENTROPY ?= 0 + HAVE_GETLINE ?= 1 + HAVE_SETPROGNAME ?= 1 + HAVE_READPASSPHRASE_H ?= 1 HAVE_SYS_TREE_H ?= 1 + HAVE_VIS_H ?= 1 else ifeq ($(OS_NAME),NetBSD) HAVE_ARC4RANDOM ?= 1 HAVE_ENDIAN_H ?= 0 HAVE_SYS_ENDIAN_H ?= 1 + HAVE_ERR_H ?= 1 HAVE_GETENTROPY ?= 0 + HAVE_GETLINE ?= 1 + HAVE_SETPROGNAME ?= 1 + HAVE_READPASSPHRASE_H ?= 0 HAVE_SYS_TREE_H ?= 1 + HAVE_VIS_H ?= 1 else ifeq ($(OS_NAME),OpenBSD) HAVE_ARC4RANDOM ?= 1 HAVE_ENDIAN_H ?= 0 HAVE_SYS_ENDIAN_H ?= 1 + HAVE_ERR_H ?= 1 HAVE_GETENTROPY ?= 1 + HAVE_GETLINE ?= 1 + HAVE_SETPROGNAME ?= 1 + HAVE_READPASSPHRASE_H ?= 1 HAVE_SYS_TREE_H ?= 1 + HAVE_VIS_H ?= 1 else ifeq ($(OS_NAME),SunOS) HAVE_ARC4RANDOM ?= 0 HAVE_ENDIAN_H ?= 0 HAVE_SYS_ENDIAN_H ?= 0 HAVE_GETENTROPY ?= 0 + ifeq ($(OS_RELEASE),5.10) + HAVE_ERR_H ?= 0 + HAVE_GETLINE ?= 0 + else + HAVE_ERR_H ?= 1 + HAVE_GETLINE ?= 1 + endif + HAVE_SETPROGNAME ?= 0 + HAVE_READPASSPHRASE_H ?= 0 HAVE_SYS_TREE_H ?= 0 + HAVE_VIS_H ?= 0 else HAVE_ARC4RANDOM ?= 0 HAVE_ENDIAN_H ?= 0 HAVE_SYS_ENDIAN_H ?= 0 + HAVE_ERR_H ?= 0 HAVE_GETENTROPY ?= 0 + HAVE_GETLINE ?= 0 + HAVE_SETPROGNAME ?= 0 + HAVE_READPASSPHRASE_H ?= 0 HAVE_SYS_TREE_H ?= 0 + HAVE_VIS_H ?= 0 endif LIBPWS_OBJS = pws.o \ @@ -141,7 +176,10 @@ LIBPWS_LIB = $(PACKAGE).a LIBPWS_LIB_MEMBERS = $(LIBPWS_OBJS:%.o=$(LIBPWS_LIB)(%.o)) -OBJS = $(LIBPWS_OBJS) +PWSDUMP_BIN = pwsdump +PWSDUMP_OBJS = pwsdump.o + +OBJS = $(LIBPWS_OBJS) $(PWSDUMP_OBJS) LIBPWS_MANPAGES = libpws.3 @@ -212,7 +250,7 @@ .PHONY: all clean clobber dist install -all: $(LIBPWS_LIB) $(MANPAGES) +all: $(PWSDUMP_BIN) $(LIBPWS_LIB) $(MANPAGES) doc: $(MANPAGES) $(XHTML_DOCUMENTATION) @@ -226,15 +264,41 @@ XCPPFLAGS += -DHAVE_SYS_ENDIAN_H else LIBPWS_OBJS += compat/endian.o + PWSDUMP_OBJS += compat/endian.o +endif +ifeq ($(HAVE_ERR_H),1) + XCPPFLAGS += -DHAVE_ERR_H +else + PWSDUMP_OBJS += compat/err.o endif ifeq ($(HAVE_GETENTROPY),1) XCPPFLAGS += -DHAVE_GETENTROPY else LIBPWS_OBJS += compat/getentropy.o endif +ifeq ($(HAVE_GETLINE),1) + XCPPFLAGS += -DHAVE_GETLINE +else + PWSDUMP_OBJS += compat/getline.o +endif +ifeq ($(HAVE_READPASSPHRASE_H),1) + XCPPFLAGS += -DHAVE_READPASSPHRASE_H +else + PWSDUMP_OBJS += compat/readpassphrase.o +endif +ifeq ($(HAVE_SETPROGNAME),1) + XCPPFLAGS += -DHAVE_SETPROGNAME +else + PWSDUMP_OBJS += compat/setprogname.o +endif ifeq ($(HAVE_SYS_TREE_H),1) XCPPFLAGS += -DHAVE_SYS_TREE_H endif +ifeq ($(HAVE_VIS_H),1) + XCPPFLAGS += -DHAVE_VIS_H +else + PWSDUMP_OBJS += compat/vis.o compat/unvis.o +endif ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly OpenBSD),) XCPPFLAGS += -I/usr/local/include XLDFLAGS += -L/usr/local/lib @@ -258,6 +322,8 @@ $(eval $(call generate-manpage-rule,$(PWS3_FILE_CREATE_MANPAGES))) +$(PWSDUMP_BIN): $(PWSDUMP_OBJS) $(LIBPWS_LIB) -lnettle + %.o: %.c $(MAKEDEPEND.c) $< | $(SED) -f deps.sed >$*.d $(COMPILE.c) -o $@ $< @@ -297,7 +363,7 @@ done clean: - rm -f $(LIBPWS_LIB) $(LIBPWS_OBJS) $(MANPAGES) \ + rm -f $(LIBPWS_LIB) $(PWSDUMP_BIN) $(OBJS) $(MANPAGES) \ $(XHTML_DOCUMENTATION) clobber: clean diff -r e1309515d111 -r 97097b4b6bfb compat.h --- a/compat.h Wed Mar 25 17:10:23 2015 +0100 +++ b/compat.h Wed Apr 01 14:57:57 2015 +0200 @@ -36,12 +36,32 @@ #include "compat/endian.h" #endif /* !defined(HAVE_ENDIAN_H) && !defined(HAVE_SYS_ENDIAN_H) */ +#ifndef HAVE_ERR_H +#include "compat/err.h" +#endif /* !HAVE_ERR_H */ + #ifndef HAVE_GETENTROPY #include "compat/getentropy.h" #endif /* !HAVE_GETENTROPY */ +#ifndef HAVE_GETLINE +#include "compat/getline.h" +#endif /* !HAVE_GETLINE */ + +#ifndef HAVE_READPASSPHRASE_H +#include "compat/readpassphrase.h" +#endif /* !HAVE_READPASSPHRASE_H */ + +#ifndef HAVE_SETPROGNAME +#include "compat/setprogname.h" +#endif /* !HAVE_SETPROGNAME */ + #ifndef HAVE_SYS_TREE_H #include "compat/tree.h" #endif /* !HAVE_SYS_TREE_H */ +#ifndef HAVE_VIS_H +#include "compat/vis.h" +#endif /* !HAVE_VIS_H */ + #endif /* COMPAT_H */ diff -r e1309515d111 -r 97097b4b6bfb compat/err.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/err.c Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "pws-compat.h" + +void +vwarn(const char *fmt, va_list args) +{ + int old_errno = errno; + + fprintf(stderr, "%s: ", getprogname()); + if (fmt != NULL) { + vfprintf(stderr, fmt, args); + fprintf(stderr, ": "); + } + fprintf(stderr, "%s\n", strerror(old_errno)); + errno = old_errno; +} + +void +vwarnx(const char *fmt, va_list args) +{ + fprintf(stderr, "%s: ", getprogname()); + if (fmt != NULL) { + vfprintf(stderr, fmt, args); + } + fputc('\n', stderr); +} + +void +verr(int eval, const char *fmt, va_list args) +{ + vwarn(fmt, args); + + exit(eval); +} + +void +verrx(int eval, const char *fmt, va_list args) +{ + vwarnx(fmt, args); + + exit(eval); +} + +void +err(int eval, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vwarn(fmt, args); + va_end(args); + + exit(eval); +} + +void +errx(int eval, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vwarnx(fmt, args); + va_end(args); + + exit(eval); +} + +void +warn(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vwarn(fmt, args); + va_end(args); +} + +void +warnx(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vwarnx(fmt, args); + va_end(args); +} diff -r e1309515d111 -r 97097b4b6bfb compat/err.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/err.h Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ERR_H +#define ERR_H + +#include + +void err(int, const char *, ...); +void errx(int, const char *, ...); +void warn(const char *, ...); +void warnx(const char *, ...); +void verr(int, const char *, va_list); +void verrx(int, const char *, va_list); +void vwarn(const char *, va_list); +void vwarnx(const char *, va_list); + +#endif /* ERR_H */ diff -r e1309515d111 -r 97097b4b6bfb compat/getline.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/getline.c Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "pws-compat.h" + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +ssize_t +getdelim(char **linep, size_t *sizep, int delim, FILE *fp) +{ + ssize_t len; + char *line = *linep; + size_t size = *sizep; + size_t pos = 0; + char *line_new; + int c; + + if (*linep == NULL) { + size = 0; + } + + flockfile(fp); + + for (;;) { + if (size - pos < 2) { + size = MIN((size_t)SSIZE_MAX + 1, + (size < BUFSIZ) ? BUFSIZ : size * 1.5); + if (size - pos < 2) { + len = -1; + errno = EOVERFLOW; + break; + } + line_new = realloc(line, size); + if (line_new == NULL) { + len = -1; + break; + } + *linep = line = line_new; + *sizep = size; + } + c = fgetc(fp); + if (c == EOF) { + len = -1; + break; + } + line[pos] = c; + pos++; + len = pos; + if (c == delim) { + break; + } + } + + if (*linep != NULL) { + (*linep)[pos] = '\0'; + } + + funlockfile(fp); + + return (len); +} + +ssize_t +getline(char **linep, size_t *sizep, FILE *fp) +{ + return (getdelim(linep, sizep, '\n', fp)); +} diff -r e1309515d111 -r 97097b4b6bfb compat/getline.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/getline.h Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef GETLINE_H +#define GETLINE_H + +#include +#include + +ssize_t getline(char **, size_t *, FILE *); +ssize_t getdelim(char **, size_t *, int, FILE *); + +#endif /* !GETLINE_H */ diff -r e1309515d111 -r 97097b4b6bfb compat/pws-compat.h --- a/compat/pws-compat.h Wed Mar 25 17:10:23 2015 +0100 +++ b/compat/pws-compat.h Wed Apr 01 14:57:57 2015 +0200 @@ -35,8 +35,44 @@ #define htobe32 pws_compat_htobe32 #endif /* !defined(HAVE_ENDIAN_H) && !defined(HAVE_SYS_ENDIAN_H) */ +#ifndef HAVE_ERR_H +#define err pws_compat_err +#define errx pws_compat_errx +#define warn pws_compat_warn +#define warnx pws_compat_warnx +#define verr pws_compat_verr +#define verrx pws_compat_verrx +#define vwarn pws_compat_vwarn +#define vwarnx pws_compat_vwarnx +#endif /* !HAVE_ERR_H */ + #ifndef HAVE_GETENTROPY #define getentropy pws_compat_getentropy #endif /* !HAVE_GETENTROPY */ +#ifndef HAVE_GETLINE +#define getline pws_compat_getline +#define getdelim pws_compat_getdelim +#endif /* !HAVE_GETLINE */ + +#ifndef HAVE_READPASSPHRASE_H +#define readpassphrase pws_compat_readpassphrase +#endif /* !HAVE_READPASSPHRASE_H */ + +#ifndef HAVE_SETPROGNAME +#define setprogname pws_compat_setprogname +#define getprogname pws_compat_getprogname +#endif /* !HAVE_SETPROGNAME */ + +#ifndef HAVE_VIS_H +#define vis pws_compat_vis +#define strvis pws_compat_strvis +#define stravis pws_compat_stravis +#define strnvis pws_compat_strnvis +#define strvisx pws_compat_strvisx +#define strunvis pws_compat_strunvis +#define unvis pws_compat_unvis +#define strnunvis pws_compat_strnunvis +#endif /* !HAVE_VIS_H */ + #endif /* PWS_COMPAT_H */ diff -r e1309515d111 -r 97097b4b6bfb compat/readpassphrase.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/readpassphrase.c Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,211 @@ +/* $OpenBSD: readpassphrase.c,v 1.24 2013/11/24 23:51:29 deraadt Exp $ */ + +/* + * Copyright (c) 2000-2002, 2007, 2010 + * Todd C. Miller + * + * 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 +#include +#include +#ifdef HAVE_PATHS_H +#include +#endif /* HAVE_PATHS_H */ +#include +#include +#include +#include +#include + +#include "pws-compat.h" +#include "readpassphrase.h" + +#ifndef TCSASOFT +#define TCSASOFT 0 +#endif /* !TCSASOFT */ + +#ifndef _NSIG +#ifdef NSIG +#define _NSIG NSIG +#else /* NSIG */ +#define _NSIG 128 +#endif /* NSIG */ +#endif /* !_NSIG */ + +#ifndef _PATH_TTY +#define _PATH_TTY "/dev/tty" +#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 /* VSTATUS */ + (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &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, TCSAFLUSH|TCSASOFT, &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); +} + +#if 0 +char * +getpass(const char *prompt) +{ + static char buf[_PASSWORD_LEN + 1]; + + return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); +} +#endif + +static void handler(int s) +{ + + signo[s] = 1; +} diff -r e1309515d111 -r 97097b4b6bfb compat/readpassphrase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/readpassphrase.h Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,38 @@ +/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ + +/* + * Copyright (c) 2000, 2002 Todd C. Miller + * + * 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. + */ + +#ifndef _READPASSPHRASE_H_ +#define _READPASSPHRASE_H_ + +#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 */ + +#include + +char * readpassphrase(const char *, char *, size_t, int); + +#endif /* !_READPASSPHRASE_H_ */ diff -r e1309515d111 -r 97097b4b6bfb compat/setprogname.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/setprogname.c Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "pws-compat.h" + +static const char *progname = ""; + +void +setprogname(const char *name) +{ + const char *p; + + p = strrchr(name, '/'); + progname = (p != NULL) ? p + 1 : name; +} + +const char * +getprogname(void) +{ + return (progname); +} diff -r e1309515d111 -r 97097b4b6bfb compat/setprogname.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/setprogname.h Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SETPROGNAME_H +#define SETPROGNAME_H + +void setprogname(const char *); +const char * getprogname(void); + +#endif /* !SETPROGNAME_H */ diff -r e1309515d111 -r 97097b4b6bfb compat/unvis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/unvis.c Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,290 @@ +/* $OpenBSD: unvis.c,v 1.17 2015/09/13 11:32:51 guenther Exp $ */ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include "pws-compat.h" +#include "vis.h" + +/* + * decode driven by state machine + */ +#define S_GROUND 0 /* haven't seen escape char */ +#define S_START 1 /* start decoding special sequence */ +#define S_META 2 /* metachar started (M) */ +#define S_META1 3 /* metachar more, regular char (-) */ +#define S_CTRL 4 /* control char started (^) */ +#define S_OCTAL2 5 /* octal digit 2 */ +#define S_OCTAL3 6 /* octal digit 3 */ + +#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') + +typedef unsigned int u_int; +typedef unsigned char u_char; + +/* + * unvis - decode characters previously encoded by vis + */ +int +unvis(char *cp, char c, int *astate, int flag) +{ + + if (flag & UNVIS_END) { + if (*astate == S_OCTAL2 || *astate == S_OCTAL3) { + *astate = S_GROUND; + return (UNVIS_VALID); + } + return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); + } + + switch (*astate) { + + case S_GROUND: + *cp = 0; + if (c == '\\') { + *astate = S_START; + return (0); + } + *cp = c; + return (UNVIS_VALID); + + case S_START: + switch(c) { + case '-': + *cp = 0; + *astate = S_GROUND; + return (0); + case '\\': + case '"': + *cp = c; + *astate = S_GROUND; + return (UNVIS_VALID); + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + *cp = (c - '0'); + *astate = S_OCTAL2; + return (0); + case 'M': + *cp = (char) 0200; + *astate = S_META; + return (0); + case '^': + *astate = S_CTRL; + return (0); + case 'n': + *cp = '\n'; + *astate = S_GROUND; + return (UNVIS_VALID); + case 'r': + *cp = '\r'; + *astate = S_GROUND; + return (UNVIS_VALID); + case 'b': + *cp = '\b'; + *astate = S_GROUND; + return (UNVIS_VALID); + case 'a': + *cp = '\007'; + *astate = S_GROUND; + return (UNVIS_VALID); + case 'v': + *cp = '\v'; + *astate = S_GROUND; + return (UNVIS_VALID); + case 't': + *cp = '\t'; + *astate = S_GROUND; + return (UNVIS_VALID); + case 'f': + *cp = '\f'; + *astate = S_GROUND; + return (UNVIS_VALID); + case 's': + *cp = ' '; + *astate = S_GROUND; + return (UNVIS_VALID); + case 'E': + *cp = '\033'; + *astate = S_GROUND; + return (UNVIS_VALID); + case '\n': + /* + * hidden newline + */ + *astate = S_GROUND; + return (UNVIS_NOCHAR); + case '$': + /* + * hidden marker + */ + *astate = S_GROUND; + return (UNVIS_NOCHAR); + } + *astate = S_GROUND; + return (UNVIS_SYNBAD); + + case S_META: + if (c == '-') + *astate = S_META1; + else if (c == '^') + *astate = S_CTRL; + else { + *astate = S_GROUND; + return (UNVIS_SYNBAD); + } + return (0); + + case S_META1: + *astate = S_GROUND; + *cp |= c; + return (UNVIS_VALID); + + case S_CTRL: + if (c == '?') + *cp |= 0177; + else + *cp |= c & 037; + *astate = S_GROUND; + return (UNVIS_VALID); + + case S_OCTAL2: /* second possible octal digit */ + if (isoctal(c)) { + /* + * yes - and maybe a third + */ + *cp = (*cp << 3) + (c - '0'); + *astate = S_OCTAL3; + return (0); + } + /* + * no - done with current sequence, push back passed char + */ + *astate = S_GROUND; + return (UNVIS_VALIDPUSH); + + case S_OCTAL3: /* third possible octal digit */ + *astate = S_GROUND; + if (isoctal(c)) { + *cp = (*cp << 3) + (c - '0'); + return (UNVIS_VALID); + } + /* + * we were done, push back passed char + */ + return (UNVIS_VALIDPUSH); + + default: + /* + * decoder in unknown state - (probably uninitialized) + */ + *astate = S_GROUND; + return (UNVIS_SYNBAD); + } +} + +/* + * strunvis - decode src into dst + * + * Number of chars decoded into dst is returned, -1 on error. + * Dst is null terminated. + */ + +int +strunvis(char *dst, const char *src) +{ + char c; + char *start = dst; + int state = 0; + + while ((c = *src++)) { + again: + switch (unvis(dst, c, &state, 0)) { + case UNVIS_VALID: + dst++; + break; + case UNVIS_VALIDPUSH: + dst++; + goto again; + case 0: + case UNVIS_NOCHAR: + break; + default: + *dst = '\0'; + return (-1); + } + } + if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) + dst++; + *dst = '\0'; + return (dst - start); +} + +ssize_t +strnunvis(char *dst, const char *src, size_t sz) +{ + char c, p; + char *start = dst, *end = dst + sz - 1; + int state = 0; + + if (sz > 0) + *end = '\0'; + while ((c = *src++)) { + again: + switch (unvis(&p, c, &state, 0)) { + case UNVIS_VALID: + if (dst < end) + *dst = p; + dst++; + break; + case UNVIS_VALIDPUSH: + if (dst < end) + *dst = p; + dst++; + goto again; + case 0: + case UNVIS_NOCHAR: + break; + default: + if (dst <= end) + *dst = '\0'; + return (-1); + } + } + if (unvis(&p, c, &state, UNVIS_END) == UNVIS_VALID) { + if (dst < end) + *dst = p; + dst++; + } + if (dst <= end) + *dst = '\0'; + return (dst - start); +} + diff -r e1309515d111 -r 97097b4b6bfb compat/vis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/vis.c Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,246 @@ +/* $OpenBSD: vis.c,v 1.25 2015/09/13 11:32:51 guenther Exp $ */ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "pws-compat.h" +#include "vis.h" + +#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') +#define isvisible(c,flag) \ + (((c) == '\\' || (flag & VIS_ALL) == 0) && \ + (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ + (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ + (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ + ((flag & VIS_SP) == 0 && (c) == ' ') || \ + ((flag & VIS_TAB) == 0 && (c) == '\t') || \ + ((flag & VIS_NL) == 0 && (c) == '\n') || \ + ((flag & VIS_SAFE) && ((c) == '\b' || \ + (c) == '\007' || (c) == '\r' || \ + isgraph((u_char)(c)))))) + +typedef unsigned int u_int; +typedef unsigned char u_char; + +/* + * vis - visually encode characters + */ +char * +vis(char *dst, int c, int flag, int nextc) +{ + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) + *dst++ = '\\'; + *dst++ = c; + *dst = '\0'; + return (dst); + } + + if (flag & VIS_CSTYLE) { + switch(c) { + case '\n': + *dst++ = '\\'; + *dst++ = 'n'; + goto done; + case '\r': + *dst++ = '\\'; + *dst++ = 'r'; + goto done; + case '\b': + *dst++ = '\\'; + *dst++ = 'b'; + goto done; + case '\a': + *dst++ = '\\'; + *dst++ = 'a'; + goto done; + case '\v': + *dst++ = '\\'; + *dst++ = 'v'; + goto done; + case '\t': + *dst++ = '\\'; + *dst++ = 't'; + goto done; + case '\f': + *dst++ = '\\'; + *dst++ = 'f'; + goto done; + case ' ': + *dst++ = '\\'; + *dst++ = 's'; + goto done; + case '\0': + *dst++ = '\\'; + *dst++ = '0'; + if (isoctal(nextc)) { + *dst++ = '0'; + *dst++ = '0'; + } + goto done; + } + } + if (((c & 0177) == ' ') || (flag & VIS_OCTAL) || + ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) { + *dst++ = '\\'; + *dst++ = ((u_char)c >> 6 & 07) + '0'; + *dst++ = ((u_char)c >> 3 & 07) + '0'; + *dst++ = ((u_char)c & 07) + '0'; + goto done; + } + if ((flag & VIS_NOSLASH) == 0) + *dst++ = '\\'; + if (c & 0200) { + c &= 0177; + *dst++ = 'M'; + } + if (iscntrl((u_char)c)) { + *dst++ = '^'; + if (c == 0177) + *dst++ = '?'; + else + *dst++ = c + '@'; + } else { + *dst++ = '-'; + *dst++ = c; + } +done: + *dst = '\0'; + return (dst); +} + +/* + * strvis, strnvis, strvisx - visually encode characters from src into dst + * + * Dst must be 4 times the size of src to account for possible + * expansion. The length of dst, not including the trailing NULL, + * is returned. + * + * Strnvis will write no more than siz-1 bytes (and will NULL terminate). + * The number of bytes needed to fully encode the string is returned. + * + * Strvisx encodes exactly len bytes from src into dst. + * This is useful for encoding a block of data. + */ +int +strvis(char *dst, const char *src, int flag) +{ + char c; + char *start; + + for (start = dst; (c = *src);) + dst = vis(dst, c, flag, *++src); + *dst = '\0'; + return (dst - start); +} + +int +strnvis(char *dst, const char *src, size_t siz, int flag) +{ + char *start, *end; + char tbuf[5]; + int c, i; + + i = 0; + for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) { + /* need space for the extra '\\' */ + if (dst + 1 >= end) { + i = 2; + break; + } + *dst++ = '\\'; + } + i = 1; + *dst++ = c; + src++; + } else { + i = vis(tbuf, c, flag, *++src) - tbuf; + if (dst + i <= end) { + memcpy(dst, tbuf, i); + dst += i; + } else { + src--; + break; + } + } + } + if (siz > 0) + *dst = '\0'; + if (dst + i > end) { + /* adjust return value for truncation */ + while ((c = *src)) + dst += vis(tbuf, c, flag, *++src) - tbuf; + } + return (dst - start); +} + +int +stravis(char **outp, const char *src, int flag) +{ + char *buf; + int len, serrno; + + buf = malloc(4 * strlen(src) + 1); + if (buf == NULL) + return -1; + len = strvis(buf, src, flag); + serrno = errno; + *outp = realloc(buf, len + 1); + if (*outp == NULL) { + *outp = buf; + errno = serrno; + } + return (len); +} + +int +strvisx(char *dst, const char *src, size_t len, int flag) +{ + char c; + char *start; + + for (start = dst; len > 1; len--) { + c = *src; + dst = vis(dst, c, flag, *++src); + } + if (len) + dst = vis(dst, *src, flag, '\0'); + *dst = '\0'; + return (dst - start); +} diff -r e1309515d111 -r 97097b4b6bfb compat/vis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/vis.h Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,87 @@ +/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ +/* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vis.h 5.9 (Berkeley) 4/3/91 + */ + +#ifndef _VIS_H_ +#define _VIS_H_ + +#include + +/* + * to select alternate encoding format + */ +#define VIS_OCTAL 0x01 /* use octal \ddd format */ +#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ + +/* + * to alter set of characters encoded (default is to encode all + * non-graphic except space, tab, and newline). + */ +#define VIS_SP 0x04 /* also encode space */ +#define VIS_TAB 0x08 /* also encode tab */ +#define VIS_NL 0x10 /* also encode newline */ +#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) +#define VIS_SAFE 0x20 /* only encode "unsafe" characters */ +#define VIS_DQ 0x200 /* backslash-escape double quotes */ +#define VIS_ALL 0x400 /* encode all characters */ + +/* + * other + */ +#define VIS_NOSLASH 0x40 /* inhibit printing '\' */ +#define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ + +/* + * unvis return codes + */ +#define UNVIS_VALID 1 /* character valid */ +#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ +#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ +#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ +#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ + +/* + * unvis flags + */ +#define UNVIS_END 1 /* no more characters */ + +char *vis(char *, int, int, int); +int strvis(char *, const char *, int); +int stravis(char **, const char *, int); +int strnvis(char *, const char *, size_t, int); +int strvisx(char *, const char *, size_t, int); +int strunvis(char *, const char *); +int unvis(char *, char, int *, int); +ssize_t strnunvis(char *, const char *, size_t); + +#endif /* !_VIS_H_ */ diff -r e1309515d111 -r 97097b4b6bfb pwsdump.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pwsdump.c Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,1118 @@ +/* + * Copyright (C) 2015 Guido Berhoerster + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "compat.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_READPASSPHRASE_H +#include +#endif /* HAVE_READPASSPHRASE_H */ +#ifdef HAVE_VIS_H +#include +#endif /* HAVE_VIS_H */ +#ifdef HAVE_SYS_ENDIAN_H +#include +#else +#ifdef HAVE_ENDIAN_H +#include +#endif /* HAVE_ENDIAN_H */ +#endif /* HAVE_SYS_ENDIAN_H */ +#ifdef HAVE_ERR_H +#include +#endif /* HAVE_ERR_H */ +#include + +#define EXIT_USAGE 2 +#define TIME_FORMAT "%Y-%m-%dT%TZ" +#define TIME_SIZE (4 + 1 + 2 + 1 + 2 + 1 + 8 + 1 + 1) + +enum { + FORMAT_PWS3, + FORMAT_DUMP +}; + +enum { + INITIAL, + IN_HEADER, + IN_RECORD +}; + +static void +header_field_dump_write(struct pws3_field *field, FILE *fp) { + size_t i; + const char *text; + char *vis_text; + const unsigned char *bytes; + time_t time; + struct tm *tm; + char time_buf[TIME_SIZE]; + size_t len; + + fprintf(fp, "%02x:", pws3_field_get_type(field)); + switch (pws3_field_get_data_type(field)) { + case PWS_DATA_TYPE_UUID: + bytes = pws3_field_get_uuid(field); + for (i = 0; i < 16; i++) { + fprintf(fp, "%02x", bytes[i]); + } + fprintf(fp, "\n"); + break; + case PWS_DATA_TYPE_TEXT: + text = pws3_field_get_text(field); + text = (text != NULL) ? text : ""; + vis_text = malloc(4 * strlen(text) + 1); + if (vis_text == NULL) { + err(1, NULL); + } + strvis(vis_text, text, VIS_TAB | VIS_NL | VIS_CSTYLE); + fprintf(fp, "%s\n", vis_text); + free(vis_text); + break; + case PWS_DATA_TYPE_TIME: + time = pws3_field_get_time(field); + tm = gmtime(&time); + strftime(time_buf, sizeof (time_buf), TIME_FORMAT, tm); + fprintf(fp, "%s\n", time_buf); + break; + case PWS_DATA_TYPE_UINT8: + fprintf(fp, "%02" PRIx8 "\n", + pws3_field_get_uint8(field)); + break; + case PWS_DATA_TYPE_UINT16: + fprintf(fp, "%04" PRIx16 "\n", + pws3_field_get_uint16(field)); + break; + case PWS_DATA_TYPE_UINT32: + fprintf(fp, "%08" PRIx32 "\n", + pws3_field_get_uint32(field)); + break; + case PWS_DATA_TYPE_BYTES: + pws3_field_get_bytes(field, &bytes, &len); + for (i = 0; i < len; i++) { + fprintf(fp, "%s%02x", (i > 0) ? " " : "", bytes[i]); + } + fprintf(fp, "\n"); + } +} + +static void +record_field_dump_write(struct pws3_field *field, FILE *fp) { + uint8_t field_type; + size_t i; + const char *text; + char *vis_text; + const unsigned char *bytes; + time_t time; + struct tm *tm; + char time_buf[TIME_SIZE]; + size_t len; + + field_type = pws3_field_get_type(field); + fprintf(fp, "%02x:", field_type); + switch (pws3_field_get_data_type(field)) { + case PWS_DATA_TYPE_UUID: + bytes = pws3_field_get_uuid(field); + for (i = 0; i < 16; i++) { + fprintf(fp, "%02x", bytes[i]); + } + fprintf(fp, "\n"); + break; + case PWS_DATA_TYPE_TEXT: + text = pws3_field_get_text(field); + text = (text != NULL) ? text : ""; + + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + vis_text = malloc(4 * strlen(text) + 1); + if (vis_text == NULL) { + err(1, NULL); + } + } else { + vis_text = malloc(4 * strlen(text) + 1); + if (vis_text == NULL) { + err(1, NULL); + } + } + strvis(vis_text, text, VIS_TAB | VIS_NL | VIS_CSTYLE); + fprintf(fp, "%s\n", vis_text); + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + free(vis_text); + } else { + free(vis_text); + } + break; + case PWS_DATA_TYPE_TIME: + time = pws3_field_get_time(field); + tm = gmtime(&time); + strftime(time_buf, sizeof (time_buf), TIME_FORMAT, tm); + fprintf(fp, "%s\n", time_buf); + break; + case PWS_DATA_TYPE_UINT8: + fprintf(fp, "%02" PRIx8 "\n", + pws3_field_get_uint8(field)); + break; + case PWS_DATA_TYPE_UINT16: + fprintf(fp, "%04" PRIx16 "\n", + pws3_field_get_uint16(field)); + break; + case PWS_DATA_TYPE_UINT32: + fprintf(fp, "%08" PRIx32 "\n", + pws3_field_get_uint32(field)); + break; + case PWS_DATA_TYPE_BYTES: + pws3_field_get_bytes(field, &bytes, &len); + for (i = 0; i < len; i++) { + fprintf(fp, "%s%02x", (i > 0) ? " " : "", bytes[i]); + } + fprintf(fp, "\n"); + } +} + +static int +parse_hex_byte(const char *str, uint8_t *u8p) +{ + size_t i; + uint8_t value; + uint8_t u8 = 0; + + if (!(isascii(str[0]) && isxdigit(str[0])) || + !(isascii(str[1]) && isxdigit(str[1]))) { + return (-1); + } + + for (i = 0; i < 2; i++) { + if (str[i] >= '0' && str[i] <= '9') { + value = (str[i] - '0'); + } else if (str[i] >= 'A' && str[i] <= 'F') { + value = (10 + (str[i] - 'A')); + } else { + value = (10 + (str[i] - 'a')); + } + u8 += value << ((2 - 1 - i) * 4); + } + + *u8p = u8; + + return (0); +} + +static struct pws3_field * +header_field_dump_parse(const char *line) +{ + const char *p = line; + uint8_t field_type; + struct pws3_field *field = NULL; + size_t len; + size_t i; + unsigned char uuid[32]; + char *text = NULL; + struct tm tm; + uint8_t u8; + uint16_t u16; + uint32_t u32; + unsigned char *bytes = NULL; + + if (strlen(line) < 3) { + goto err; + } + + if (parse_hex_byte(p, &field_type) != 0) { + goto err; + } + p += 2; + field = pws3_field_create(1, field_type); + if (field == NULL) { + err(1, NULL); + } + + if (*p++ != ':') { + goto err; + } + + len = strlen(p); + + switch (pws3_field_get_data_type(field)) { + case PWS_DATA_TYPE_UUID: + for (i = 0; i < 16; i++) { + if (parse_hex_byte(p, &uuid[i]) != 0) { + goto err; + } + p += 2; + + while (*p == ' ') { p++; } + } + + if (*p != '\0') { + goto err; + } + + if (pws3_field_set_uuid(field, uuid) != 0) { + goto err; + } + break; + case PWS_DATA_TYPE_TEXT: + if (len > PWS3_MAX_FIELD_SIZE) { + goto err; + } + text = malloc(len + 1); + if (text == NULL) { + err(1, NULL); + } + if (strunvis(text, p) == -1) { + goto err; + } + if (pws3_field_set_text(field, text) != 0) { + goto err; + } + break; + case PWS_DATA_TYPE_TIME: + p = strptime(p, TIME_FORMAT, &tm); + if ((p == NULL) || (*p != '\0')) { + goto err; + } + tm.tm_isdst = -1; + pws3_field_set_time(field, mktime(&tm)); + break; + case PWS_DATA_TYPE_UINT8: + if (len != 2) { + goto err; + } + if (parse_hex_byte(p, &u8) != 0) { + goto err; + } + pws3_field_set_uint8(field, u8); + break; + case PWS_DATA_TYPE_UINT16: + if (len != 4) { + goto err; + } + for (i = 0; i < 2; i++) { + if (parse_hex_byte(p, &((unsigned char *)&u16)[i]) != + 0) { + goto err; + } + p += 2; + } + pws3_field_set_uint16(field, be16toh(u16)); + break; + case PWS_DATA_TYPE_UINT32: + if (len != 8) { + goto err; + } + for (i = 0; i < 4; i++) { + if (parse_hex_byte(p, + &((unsigned char *)&u32)[i]) != 0) { + goto err; + } + p += 2; + } + pws3_field_set_uint16(field, be32toh(u32)); + break; + case PWS_DATA_TYPE_BYTES: + bytes = malloc(len / 2); + if (bytes == NULL) { + err(1, NULL); + } + for (i = 0; (*p != '\0') && (i < PWS3_MAX_FIELD_SIZE); i++) { + if (parse_hex_byte(p, &bytes[i]) != 0) { + goto err; + } + p += 2; + + while (*p == ' ') { p++; } + } + + if (*p != '\0') { + goto err; + } + + if (pws3_field_set_bytes(field, bytes, i) != 0) { + goto err; + } + } + + free(bytes); + free(text); + + return (field); +err: + free(bytes); + free(text); + pws3_field_destroy(field); + + return (NULL); +} + +static struct pws3_field * +record_field_dump_parse(const char *line) +{ + const char *p = line; + uint8_t field_type = 0xff; + struct pws3_field *field = NULL; + size_t len; + size_t i; + unsigned char uuid[32]; + char *text = NULL; + struct tm tm; + uint8_t u8; + uint16_t u16; + uint32_t u32; + unsigned char *bytes = NULL; + + if (strlen(line) < 3) { + goto err; + } + + if (parse_hex_byte(p, &field_type) != 0) { + goto err; + } + p += 2; + field = pws3_field_create(0, field_type); + if (field == NULL) { + err(1, NULL); + } + + if (*p++ != ':') { + goto err; + } + + len = strlen(p); + + switch (pws3_field_get_data_type(field)) { + case PWS_DATA_TYPE_UUID: + for (i = 0; i < 16; i++) { + if (parse_hex_byte(p, &uuid[i]) != 0) { + goto err; + } + p += 2; + + while (*p == ' ') { p++; } + } + + if (*p != '\0') { + goto err; + } + + if (pws3_field_set_uuid(field, uuid) != 0) { + goto err; + } + break; + case PWS_DATA_TYPE_TEXT: + if (((field_type == PWS3_RECORD_FIELD_PASSWORD) && + (len > PWS3_MAX_PASSWORD_LEN)) || + (len > PWS3_MAX_FIELD_SIZE)) { + goto err; + } + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + text = malloc(len + 1); + if (text == NULL) { + err(1, NULL); + } + } else { + text = malloc(len + 1); + } + if (strunvis(text, p) == -1) { + goto err; + } + if (pws3_field_set_text(field, text) != 0) { + goto err; + } + break; + case PWS_DATA_TYPE_TIME: + p = strptime(p, TIME_FORMAT, &tm); + if ((p == NULL) || (*p != '\0')) { + goto err; + } + tm.tm_isdst = -1; + pws3_field_set_time(field, mktime(&tm)); + break; + case PWS_DATA_TYPE_UINT8: + if (len != 2) { + goto err; + } + if (parse_hex_byte(p, &u8) != 0) { + goto err; + } + pws3_field_set_uint8(field, u8); + break; + case PWS_DATA_TYPE_UINT16: + if (len != 4) { + goto err; + } + for (i = 0; i < 2; i++) { + if (parse_hex_byte(p, &((unsigned char *)&u16)[i]) != + 0) { + goto err; + } + p += 2; + } + pws3_field_set_uint16(field, be16toh(u16)); + break; + case PWS_DATA_TYPE_UINT32: + if (len != 8) { + goto err; + } + for (i = 0; i < 4; i++) { + if (parse_hex_byte(p, &((unsigned char *)&u32)[i]) != + 0) { + goto err; + } + p += 2; + } + pws3_field_set_uint16(field, be32toh(u32)); + break; + case PWS_DATA_TYPE_BYTES: + bytes = malloc(len / 2); + if (bytes == NULL) { + err(1, NULL); + } + for (i = 0; (*p != '\0') && (i < PWS3_MAX_FIELD_SIZE); i++) { + if (parse_hex_byte(p, &bytes[i]) != 0) { + goto err; + } + p += 2; + + while (*p == ' ') { p++; } + } + + if (*p != '\0') { + goto err; + } + + if (pws3_field_set_bytes(field, bytes, i) != 0) { + goto err; + } + } + + free(bytes); + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + free(text); + } else { + free(text); + } + + return (field); +err: + free(bytes); + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + free(text); + } else { + free(text); + } + pws3_field_destroy(field); + + return (NULL); +} + +static int +dump_read(struct pws3_file *file, FILE *fp) +{ + int retval = -1; + ssize_t line_len; + char *line = NULL; + size_t line_size = 0; + size_t line_no = 0; + int state = INITIAL; + struct pws3_field *header_field = NULL; + struct pws3_record *record = NULL; + struct pws3_field *record_field = NULL; + struct pws3_field *field_uuid; + + errno = 0; + while ((line_len = getline(&line, &line_size, fp)) != -1) { + line_no++; + + /* skip empty lines and comments */ + if (line_len <= 1 || (line[0] == '#')) { + continue; + } + + /* remove trailing newline */ + if (line[line_len - 1] == '\n') { + line[line_len - 1] = '\0'; + } + + switch (state) { + case INITIAL: + if (strcasecmp(line, "HEADER") == 0) { + state = IN_HEADER; + } else { + warnx("syntax error in line %zu", line_no); + goto out; + } + break; + case IN_HEADER: + if (strncasecmp(line, "RECORD", + strlen("RECORD")) == 0) { + state = IN_RECORD; + } else { + header_field = header_field_dump_parse(line); + if (header_field == NULL) { + warnx("syntax error in line %zu", + line_no); + goto out; + } + pws3_file_set_header_field(file, header_field); + header_field = NULL; + } + break; + case IN_RECORD: + if (strncasecmp(line, "RECORD", + strlen("RECORD")) == 0) { + if (record == NULL) { + warnx("syntax error in line %zu", + line_no); + goto out; + } + + /* check for mandatory UUID field */ + if (((field_uuid = pws3_record_get_field(record, + PWS3_RECORD_FIELD_UUID)) == NULL) || + (pws3_field_get_uuid(field_uuid) == + NULL)) { + warnx("record ending on line %zu is " + "missing UUID field", line_no); + goto out; + } + pws3_file_insert_record(file, record); + record = NULL; + } else { + if (record == NULL) { + record = pws3_record_create(); + if (record == NULL) { + err(1, NULL); + } + } + + record_field = record_field_dump_parse(line); + if (record_field == NULL) { + warnx("syntax error in line %zu", + line_no); + goto out; + } + pws3_record_set_field(record, record_field); + record_field = NULL; + } + } + errno = 0; + } + if (errno != 0) { + warn("failed to read from input file"); + goto out; + } + if (record != NULL) { + /* check for mandatory UUID field */ + if (((field_uuid = pws3_record_get_field(record, + PWS3_RECORD_FIELD_UUID)) == NULL) || + (pws3_field_get_uuid(field_uuid) == NULL)) { + warnx("record ending on line %zu is missing UUID " + "field", line_no); + goto out; + } + pws3_file_insert_record(file, record); + record = NULL; + } + + retval = 0; + +out: + pws3_field_destroy(header_field); + pws3_field_destroy(record_field); + pws3_record_destroy(record); + free(line); + + return (retval); +} + +static int +dump_write(struct pws3_file *file, FILE *fp) +{ + size_t i; + struct pws3_field *header_field; + struct pws3_record *record; + size_t n; + struct pws3_field *record_field; + + if (fprintf(fp, "# Passwordsafe v3 database dump\nHeader\n") < 0) { + warn("failed to write to output file"); + return (-1); + } + for (i = 0x00; i < 0xff; i++) { + header_field = pws3_file_get_header_field(file, i); + if (header_field != NULL) { + header_field_dump_write(header_field, fp); + } + + while ((i == PWS3_HEADER_FIELD_EMPTY_GROUPS) && + (header_field != NULL) && + (header_field = pws3_file_next_empty_group(file, + header_field)) != NULL) { + header_field_dump_write(header_field, fp); + } + } + + for (record = pws3_file_first_record(file), n = 1; record != NULL; + record = pws3_file_next_record(file, record), n++) { + if (fprintf(fp, "Record %zu\n", n) < 0) { + warn("failed to write to output file"); + return (-1); + } + + for (i = 0x00; i <= 0xff; i++) { + record_field = pws3_record_get_field(record, i); + if (record_field != NULL) { + record_field_dump_write(record_field, fp); + } + } + } + + return (0); +} + +void +usage(void) +{ + fprintf(stderr, "usage: pwsdump [-f pws3 | dump] [-o filename] " + "[-p password_file] [-P password_fd] [-t pws3 | dump] " + "input_file\n" + " pwsdump -f dump [-o filename] [-t pws3 | dump]\n"); +} + +int +main(int argc, char *argv[]) +{ + int status = EXIT_FAILURE; + int c; + int errflag = 0; + int from_format = FORMAT_PWS3; + int to_format = FORMAT_DUMP; + const char *in_filename = NULL; + const char *out_filename = NULL; + const char *new_password_filename = NULL; + const char *password_filename = NULL; + int fd_value = -1; + char *p; + int fd_new_password = -1; + FILE * fp_new_password = NULL; + int fd_password = -1; + FILE * fp_password = NULL; + struct pws3_file *file = NULL; + FILE *fp_in = stdin; + char *password = NULL; + ssize_t password_len; + size_t password_size = PWS3_MAX_PASSWORD_LEN + 1; + char *new_password = NULL; + ssize_t new_password_len; + size_t new_password_size = PWS3_MAX_PASSWORD_LEN + 1; + char *confirm_password = NULL; + FILE *fp_out = stdout; + struct stat statbuf_in; + struct stat statbuf_out; + int need_tmpfile = 0; + int fd_tmp = -1; + char *out_filename_tmp = NULL; + char *out_dir = NULL; + char *tmp_filename = NULL; + int len; + mode_t old_mode; + + setprogname(argv[0]); + + if (pws_init() != 0) { + goto out; + } + + /* timestamps are processed as UTC */ + if (setenv("TZ", "", 1) != 0) { + goto out; + } + tzset(); + + while (!errflag && ((c = getopt(argc, argv, "f:n:N:o:p:P:t:")) != -1)) { + switch (c) { + case 'f': + if (strcmp(optarg, "pws3") == 0) { + from_format = FORMAT_PWS3; + } else if (strcmp(optarg, "dump") == 0) { + from_format = FORMAT_DUMP; + } else { + errflag = 1; + } + break; + case 'n': + fd_new_password = -1; + new_password_filename = optarg; + break; + case 'N': + new_password_filename = NULL; + errno = 0; + fd_value = strtol(optarg, &p, 10); + if ((errno == 0) && (p != optarg) && (*p == '\0') && + (fd_value >= 0) && (fd_value < INT_MAX)) { + fd_new_password = (int)fd_value; + } else { + errflag = 1; + } + break; + case 'o': + out_filename = optarg; + break; + case 'p': + fd_password = -1; + password_filename = optarg; + break; + case 'P': + password_filename = NULL; + errno = 0; + fd_value = strtol(optarg, &p, 10); + if ((errno == 0) && (p != optarg) && (*p == '\0') && + (fd_value >= 0) && + (fd_value < INT_MAX)) { + fd_password = (int)fd_value; + } else { + errflag = 1; + } + break; + case 't': + if (strcmp(optarg, "pws3") == 0) { + to_format = FORMAT_PWS3; + } else if (strcmp(optarg, "dump") == 0) { + to_format = FORMAT_DUMP; + } else { + errflag = 1; + } + break; + default: + errflag = 1; + } + } + + if (errflag || ((from_format == FORMAT_PWS3) && (argc != optind + 1)) || + (argc > optind + 1)) { + usage(); + status = EXIT_USAGE; + goto out; + } + + if (optind == argc - 1) { + in_filename = argv[optind]; + } + + if (fd_password != -1) { + fp_password = fdopen(fd_password, "r"); + if (fp_password == NULL) { + warn("invalid password fd %d", fd_password); + goto out; + } + fd_password = -1; + } else if (password_filename != NULL) { + fp_password = fopen(password_filename, "r"); + if (fp_password == NULL) { + warn("could not open password file"); + goto out; + } + } + + if (fd_new_password != -1) { + fp_new_password = fdopen(fd_new_password, "r"); + if (fp_new_password == NULL) { + warn("invalid password fd %d", fd_new_password); + goto out; + } + fd_new_password = -1; + } else if (new_password_filename != NULL) { + fp_new_password = fopen(new_password_filename, "r"); + if (fp_new_password == NULL) { + warn("could not open password file"); + goto out; + } + } + + if (in_filename != NULL) { + fp_in = fopen(in_filename, "r"); + if (fp_in == NULL) { + warn("could not open input file"); + goto out; + } + } + + if (out_filename != NULL) { + if (in_filename != NULL) { + if (fstat(fileno(fp_in), &statbuf_in) == -1) { + warn("could not stat input file"); + status = EXIT_FAILURE; + goto out; + } + if (stat(out_filename, &statbuf_out) == -1) { + if (errno != ENOENT) { + warn("could not stat output file"); + status = 1; + goto out; + } + } else if ((statbuf_in.st_ino == statbuf_out.st_ino) && + (statbuf_in.st_dev == statbuf_out.st_dev)) { + need_tmpfile = 1; + } + } + + if (need_tmpfile) { + out_filename_tmp = strdup(out_filename); + if (out_filename_tmp == NULL) { + err(1, NULL); + } + out_dir = dirname(out_filename_tmp); + len = snprintf(NULL, 0, "%s/pwsdumpXXXXXX", out_dir); + if (len < 0) { + warn(NULL); + goto out; + } + tmp_filename = malloc((size_t)len + 1); + if (tmp_filename == NULL) { + err(1, NULL); + } + if (snprintf(tmp_filename, (size_t)len + 1, + "%s/pwsdumpXXXXXX", out_dir) != len) { + warn(NULL); + goto out; + } + old_mode = umask(077); + fd_tmp = mkstemp(tmp_filename); + umask(old_mode); + if (fd_tmp == -1) { + warn("could not create temporary file"); + goto out; + } + fp_out = fdopen(fd_tmp, "w"); + if (fp_out == NULL) { + warn("could not open temporary file"); + goto out; + } + fd_tmp = -1; + } else { + old_mode = umask(077); + fp_out = fopen(out_filename, "w"); + umask(old_mode); + if (fp_out == NULL) { + warn("could not open output file"); + goto out; + } + } + } + + file = pws3_file_create(); + if (file == NULL) { + err(1, NULL); + } + + if (from_format == FORMAT_PWS3) { + password = malloc(password_size); + if (password == NULL) { + err(1, NULL); + } + if (fp_password != NULL) { + errno = 0; + if (getline(&password, &password_size, + fp_password) == -1) { + if (errno != 0) { + warn("failed to read password"); + } else { + warnx("failed to read password"); + } + goto out; + } + password_len = strlen(password); + /* strip trailing newline */ + if ((password_len > 0) && + (password[password_len - 1] == '\n')) { + password[password_len - 1] = '\0'; + password_len--; + } + if (password_len == 0) { + warnx("invalid password"); + goto out; + } else if (password_len > PWS3_MAX_PASSWORD_LEN) { + warnx("password too long"); + goto out; + } + } else { + if (readpassphrase("Enter password: ", password, + password_size, RPP_ECHO_OFF | + RPP_REQUIRE_TTY) == NULL) { + err(1, NULL); + } + password_len = strlen(password); + } + if (password_len == 0) { + warnx("invalid password"); + goto out; + } + + if (pws3_file_read_stream(file, password, fp_in) != 0) { + warnx("%s", pws3_file_get_error_message(file)); + goto out; + } + } else { + if (dump_read(file, fp_in) != 0) { + goto out; + } + } + + if (to_format == FORMAT_PWS3) { + new_password = malloc(new_password_size); + if (new_password == NULL) { + err(1, NULL); + } + if (fp_new_password != NULL) { + errno = 0; + if (getline(&new_password, &new_password_size, + fp_new_password) == -1) { + if (errno != 0) { + warn("failed to read password"); + } else { + warnx("failed to read password"); + } + goto out; + } + new_password_len = strlen(new_password); + /* strip trailing newline */ + if ((new_password_len > 0) && + (new_password[new_password_len - 1] == '\n')) { + new_password[new_password_len - 1] = '\0'; + new_password_len--; + } + if (new_password_len == 0) { + warnx("invalid password"); + goto out; + } else if (new_password_len > PWS3_MAX_PASSWORD_LEN) { + warnx("password too long"); + goto out; + } + } else { + if (readpassphrase("Enter new password: ", new_password, + PWS3_MAX_PASSWORD_LEN + 1, RPP_ECHO_OFF | + RPP_REQUIRE_TTY) == NULL) { + err(1, NULL); + } + if (strlen(new_password) == 0) { + warnx("invalid password"); + goto out; + } + + confirm_password = malloc(PWS3_MAX_PASSWORD_LEN + 1); + if (confirm_password == NULL) { + err(1, NULL); + } + if (readpassphrase("Confirm new password: ", + confirm_password, PWS3_MAX_PASSWORD_LEN + 1, + RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { + err(1, NULL); + } + if (strcmp(new_password, confirm_password) != 0) { + warnx("password mismatch"); + goto out; + } + } + + if (pws3_file_write_stream(file, new_password, 10000, fp_out) != + 0) { + goto out; + } + } else { + if (dump_write(file, fp_out) != 0) { + goto out; + } + if (fflush(fp_out) != 0) { + warn("failed to flush output file"); + goto out; + } + } + + status = EXIT_SUCCESS; + +out: + if (fd_new_password != -1) { + close(fd_new_password); + } + + if (fp_new_password != NULL) { + fclose(fp_new_password); + } + + if (fd_password != -1) { + close(fd_password); + } + + if (fp_password != NULL) { + fclose(fp_password); + } + + if ((fp_in != NULL) && (fp_in != stdin)) { + fclose(fp_in); + } + + if (fd_tmp != -1) { + close(fd_tmp); + } + + if ((fp_out != NULL) && (fp_out != stdout)) { + fclose(fp_out); + if (status == EXIT_SUCCESS) { + if (need_tmpfile) { + if (rename(tmp_filename, out_filename) == -1) { + warn("could not create output file"); + status = EXIT_FAILURE; + unlink(tmp_filename); + } + } + } else { + if (need_tmpfile) { + unlink(tmp_filename); + } else { + unlink(out_filename); + } + } + } + + pws3_file_destroy(file); + free(out_filename_tmp); + free(tmp_filename); + free(confirm_password); + free(new_password); + free(password); + + pws_finalize(); + + exit(status); +}