Mercurial > projects > pwm
changeset 0:a7e41e1a79c8
Initial revision
author | Guido Berhoerster <guido+pwm@berhoerster.name> |
---|---|
date | Thu, 19 Jan 2017 22:39:51 +0100 |
parents | |
children | 55281f14dc9b |
files | Makefile cmd.c cmd.h compat.h compat/asprintf.c compat/asprintf.h compat/err.c compat/err.h compat/getentropy.c compat/getentropy.h compat/readpassphrase.c compat/readpassphrase.h compat/setprogname.c compat/setprogname.h compat/tree.h deps.sed pwfile.c pwfile.h pwm.c pwm.h tok.c tok.h util.c util.h |
diffstat | 24 files changed, 3973 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,167 @@ +# +# Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> +# +# 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. +# + +PACKAGE = pwm +VERSION = 1 +DISTNAME := $(PACKAGE)-$(VERSION) + +# gcc, clang, icc, Sun/Solaris Studio +CC := $(CC) -std=c99 +COMPILE.c = $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) $(TARGET_ARCH) -c +# gcc, clang, icc +MAKEDEPEND.c = $(CC) -MM $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) +# Sun/Solaris Studio +#MAKEDEPEND.c = $(CC) -xM1 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) +# X makedepend +#MAKEDEPEND.c = makedepend -f- -Y -- $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) -- +LINK.c = $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) $(LDFLAGS) $(XLDFLAGS) $(TARGET_ARCH) +LINK.o = $(CC) $(LDFLAGS) $(XLDFLAGS) $(TARGET_ARCH) +CP := cp +INSTALL := install +INSTALL.exec := $(INSTALL) -D -m 0755 +INSTALL.data := $(INSTALL) -D -m 0644 +INSTALL.link := $(CP) -f -P +PAX := pax +GZIP := gzip +SED := sed + +DESTDIR ?= +prefix ?= /usr/local +bindir ?= $(prefix)/bin + +OS_NAME := $(shell uname -s) +OS_RELEASE := $(shell uname -r) + +ifeq ($(OS_NAME),Linux) + HAVE_ASPRINTF ?= 1 + HAVE_ERR_H ?= 1 + HAVE_READPASSPHRASE_H ?= 0 + HAVE_SETPROGNAME ?= 0 + HAVE_SYS_TREE_H ?= 0 +else ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly),) + HAVE_ASPRINTF ?= 1 + HAVE_ERR_H ?= 1 + HAVE_READPASSPHRASE_H ?= 1 + HAVE_SETPROGNAME ?= 1 + HAVE_SYS_TREE_H ?= 1 +else ifeq ($(OS_NAME),NetBSD) + HAVE_ASPRINTF ?= 1 + HAVE_ERR_H ?= 1 + HAVE_READPASSPHRASE_H ?= 0 + HAVE_SYS_TREE_H ?= 1 + HAVE_SETPROGNAME ?= 1 +else ifeq ($(OS_NAME),OpenBSD) + HAVE_ASPRINTF ?= 1 + HAVE_ERR_H ?= 1 + HAVE_READPASSPHRASE_H ?= 1 + HAVE_SYS_TREE_H ?= 1 + HAVE_SETPROGNAME ?= 1 +else ifeq ($(OS_NAME),SunOS) + ifeq ($(OS_RELEASE),5.10) + HAVE_ASPRINTF ?= 0 + HAVE_ERR_H ?= 0 + else + HAVE_ASPRINTF ?= 1 + HAVE_ERR_H ?= 1 + endif + HAVE_READPASSPHRASE_H ?= 0 + HAVE_SYS_TREE_H ?= 0 + HAVE_SETPROGNAME ?= 0 +else + HAVE_ASPRINTF ?= 0 + HAVE_ERR_H ?= 0 + HAVE_READPASSPHRASE_H ?= 0 + HAVE_SETPROGNAME ?= 0 + HAVE_SYS_TREE_H ?= 0 +endif + +OBJS = cmd.o \ + pwfile.o \ + pwm.o \ + tok.o \ + util.o + +.DEFAULT_TARGET = all + +.PHONY: all clean clobber dist install + +all: $(PACKAGE) + +XCPPFLAGS = -DPACKAGE=\"$(PACKAGE)\" \ + -DVERSION=\"$(VERSION)\" +LDLIBS = -lpws -lnettle +ifeq ($(HAVE_ERR_H),1) + XCPPFLAGS += -DHAVE_ERR_H +else + OBJS += compat/err.o +endif +ifeq ($(HAVE_READPASSPHRASE_H),1) + XCPPFLAGS += -DHAVE_READPASSPHRASE_H +else + OBJS += compat/readpassphrase.o +endif +ifeq ($(HAVE_SETPROGNAME),1) + XCPPFLAGS += -DHAVE_SETPROGNAME +else + OBJS += compat/setprogname.o +endif +ifeq ($(HAVE_SYS_TREE_H),1) + XCPPFLAGS += -DHAVE_SYS_TREE_H +endif +ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly OpenBSD),) + XCPPFLAGS += -I/usr/local/include + XLDFLAGS += -L/usr/local/lib +else ifeq ($(OS_NAME),NetBSD) + XCPPFLAGS += -I/usr/pkg/include + XLDFLAGS += -L/usr/pkg/lib +endif +ifeq ($(findstring $(OS_NAME),FreeBSD DragonFly NetBSD OpenBSD),) + XCPPFLAGS += -D_XOPEN_SOURCE=600 +endif +ifeq ($(OS_NAME),SunOS) + XCPPFLAGS += -I/usr/xpg4/include -D__EXTENSIONS__ + XLDFLAGS += -L/usr/xpg4/lib -R/usr/xpg4/lib +endif + +$(PACKAGE): $(OBJS) + $(LINK.o) $^ $(LDLIBS) -o $@ + +%.o: %.c + $(MAKEDEPEND.c) $< | $(SED) -f deps.sed >$*.d + $(COMPILE.c) -o $@ $< + +install: + $(INSTALL.exec) $(PACKAGE) "$(DESTDIR)$(bindir)/$(PACKAGE)" + +clean: + rm -f $(PACKAGE) $(OBJS) + +clobber: clean + rm -f $(patsubst %.o,%.d,$(OBJS)) + +dist: clobber + $(PAX) -w -x ustar -s ',.*/\..*,,' -s ',./[^/]*\.tar\.gz,,' \ + -s ',^\.$$,,' -s ',\./,$(DISTNAME)/,' . | \ + $(GZIP) > $(DISTNAME).tar.gz + +-include $(patsubst %.o,%.d,$(OBJS))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmd.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,546 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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" + +#ifdef HAVE_ERR_H +#include <err.h> +#endif /* HAVE_ERR_H */ +#include <errno.h> +#include <limits.h> +#ifdef HAVE_READPASSPHRASE_H +#include <readpassphrase.h> +#endif /* READPASSPHRASE_H */ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "cmd.h" +#include "pwfile.h" +#include "util.h" + +enum field_type { + FIELD_UNKNOWN = -1, + FIELD_GROUP, + FIELD_TITLE, + FIELD_USERNAME, + FIELD_PASSWORD, + FIELD_NOTES, + FIELD_URL +}; + +static enum cmd_return cmd_list(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_create(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_modify(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_remove(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_show(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_pipe(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_creategroup(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_removegroup(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_changepassword(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_help(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_write(struct pwm_ctx *, int, char *[]); +static enum cmd_return cmd_quit(struct pwm_ctx *, int, char *[]); + +static const char *field_names[] = { + "group", + "title", + "username", + "password", + "notes", + "url" +}; + +static const char *field_labels[] = { + "Group: ", + "Title: ", + "Username: ", + "Password: ", + "Notes: ", + "URL: " +}; + +static struct cmd cmds[] = { + { "ls", "list", "list", "List entries", cmd_list }, + { "c", "create", "create field=value ...", "Create entry", cmd_create }, + { "m", "modify", "modify id field=value ...", "Modify entry", cmd_modify }, + { "rm", "remove", "remove id", "Delete entry", cmd_remove }, + { "s", "show", "show id field", "Show entry", cmd_show }, + { "p", "pipe", "pipe id field command", "Pipe entry to external command", + cmd_pipe }, + { "cg", "creategroup", "creategroup name", "Create empty group", + cmd_creategroup }, + { "rg", "removegroup", "removegroup name", "Delete empty group", + cmd_removegroup }, + { "ch", "changepassword", "changepassword", "Change password", + cmd_changepassword }, + { "h", "help", "help", "Show help text", cmd_help }, + { "w", "write", "write", "Write the database", cmd_write }, + { "q", "quit", "quit", "Quit", cmd_quit }, + { 0 } +}; + +static enum field_type +parse_field_name(const char *name) +{ + int i; + + for (i = 0; i < (int)COUNTOF(field_names); i++) { + if (strcmp(field_names[i], name) == 0) { + return (i); + } + } + + return (FIELD_UNKNOWN); +} + +static enum field_type +parse_field_assignment(char *arg, struct record *record) +{ + int i; + size_t field_name_len; + char *value; + + for (i = 0; i < (int)COUNTOF(field_names); i++) { + field_name_len = strlen(field_names[i]); + if ((strncmp(field_names[i], arg, field_name_len) == 0) && + (arg[field_name_len] == '=')){ + value = arg + field_name_len + 1; + if (*value == '\0') { + /* skip empty assignments */ + return (i); + } + switch (i) { + case FIELD_GROUP: + record->group = value; + break; + case FIELD_TITLE: + record->title = value; + break; + case FIELD_USERNAME: + record->username = value; + break; + case FIELD_PASSWORD: + record->password = value; + break; + case FIELD_NOTES: + record->notes = value; + break; + case FIELD_URL: + record->url = value; + break; + default: + break; + } + return (i); + } + } + + return (FIELD_UNKNOWN); +} + +static int +parse_id(const char *arg, unsigned int *idp) +{ + long x; + char *p; + + errno = 0; + x = strtol(arg, &p, 10); + if ((errno != 0) || (*arg == '\0') || (*p != '\0') || (x > UINT_MAX) || + (x <= 0)) { + return (-1); + } + *idp = (unsigned int)x; + + return (0); +} + +static enum cmd_return +cmd_list(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + union list_item **list; + size_t i; + + if (argc != 1) { + return (CMD_USAGE); + } + + list = pwfile_create_list(ctx); + for (i = 0; list[i] != NULL; i++) { + if (list[i]->any.type == ITEM_TYPE_GROUP) { + printf("[%s]\n", list[i]->group.group); + } else { + printf("%4u %s\n", list[i]->record.id, + (list[i]->record.title != NULL) ? + list[i]->record.title : ""); + } + } + pwfile_destroy_list(list); + + return (CMD_OK); +} + +static enum cmd_return +cmd_create(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + int i; + struct record record = { 0 }; + + if (argc < 2) { + return (CMD_USAGE); + } + + for (i = 1; i < argc; i++) { + if (parse_field_assignment(argv[i], &record) == FIELD_UNKNOWN) { + fprintf(stderr, "bad field assignment \"%s\"\n", + argv[i]); + return (CMD_ERR); + } + } + + pwfile_create_record(ctx, &record); + + return (CMD_OK); +} + +static enum cmd_return +cmd_modify(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + unsigned int id; + int i; + struct record record = { 0 }; + + if (argc < 2) { + return (CMD_USAGE); + } + + if (parse_id(argv[1], &id) != 0) { + fprintf(stderr, "invalid id %s\n", argv[1]); + return (CMD_ERR); + } + + for (i = 2; i < argc; i++) { + if (parse_field_assignment(argv[i], &record) == FIELD_UNKNOWN) { + fprintf(stderr, "bad field assignment \"%s\"\n", + argv[i]); + return (CMD_ERR); + } + } + + pwfile_modify_record(ctx, id, &record); + + return (CMD_OK); +} + +static enum cmd_return +cmd_remove(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + unsigned int id; + + if (argc != 2) { + return (CMD_USAGE); + } + + if (parse_id(argv[1], &id) != 0) { + fprintf(stderr, "invalid id %s\n", argv[1]); + return (CMD_ERR); + } + + if (pwfile_remove_record(ctx, id) != 0) { + fprintf(stderr, "failed to remove record %u\n", id); + return (CMD_ERR); + } + + return (CMD_OK); +} + +static int +print_field(const char *label, const char *value, int show_label, FILE *fp) +{ + fprintf(fp, "%s%s\n", show_label ? label : "", (value != NULL) ? + value : ""); + if (ferror(fp)) { + warn("fprintf"); + return (-1); + } + return (0); +} + +static void +print_record(struct record *record, int fields[], int show_labels, FILE *fp) +{ + if (fields[FIELD_TITLE]) { + if (print_field(field_labels[FIELD_TITLE], record->title, + show_labels, fp) != 0) { + return; + } + } + if (fields[FIELD_GROUP]) { + if (print_field(field_labels[FIELD_GROUP], record->group, + show_labels, fp)) { + return; + } + } + if (fields[FIELD_USERNAME]) { + if (print_field(field_labels[FIELD_USERNAME], record->username, + show_labels, fp)) { + return; + } + } + if (fields[FIELD_PASSWORD]) { + if (print_field(field_labels[FIELD_PASSWORD], record->password, + show_labels, fp)) { + return; + } + } + if (fields[FIELD_NOTES]) { + if (print_field(field_labels[FIELD_NOTES], record->notes, + show_labels, fp)) { + return; + } + } + if (fields[FIELD_URL]) { + if (print_field(field_labels[FIELD_URL], record->url, + show_labels, fp)) { + return; + } + } +} + +static enum cmd_return +cmd_show(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + unsigned int id; + struct record *record; + int i; + enum field_type type; + int fields[COUNTOF(field_names)] = { 0 }; + + if (argc < 2) { + return (CMD_USAGE); + } + + if (parse_id(argv[1], &id) != 0) { + fprintf(stderr, "invalid id %s\n", argv[1]); + return (CMD_ERR); + } + + for (i = 2; i < argc; i++) { + type = parse_field_name(argv[i]); + if (type < 0) { + fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + return (CMD_ERR); + } + fields[type] = 1; + } + + record = pwfile_get_record(ctx, id); + if (record == NULL) { + fprintf(stderr, "record %u does not exist\n", id); + return (CMD_ERR); + } + print_record(record, fields, 1, stdout); + pwfile_destroy_record(record); + + return (CMD_OK); +} + +static enum cmd_return +cmd_pipe(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + enum cmd_return retval = CMD_ERR; + unsigned int id; + struct record *record = NULL; + enum field_type type; + int fields[COUNTOF(field_names)] = { 0 }; + FILE *fp = NULL; + + if (argc != 4) { + return (CMD_USAGE); + } + + if (parse_id(argv[1], &id) != 0) { + fprintf(stderr, "invalid id %s\n", argv[1]); + return (CMD_ERR); + } + + type = parse_field_name(argv[2]); + if (type < 0) { + fprintf(stderr, "bad field name \"%s\"\n", argv[2]); + return (CMD_ERR); + } + fields[type] = 1; + + fp = popen(argv[3], "w"); + if (fp == NULL) { + warn("popen"); + goto out; + } + + record = pwfile_get_record(ctx, id); + if (record == NULL) { + fprintf(stderr, "record %u does not exist\n", id); + goto out; + } + + print_record(record, fields, 0, fp); + + retval = CMD_OK; + +out: + pwfile_destroy_record(record); + if (fp != NULL) { + pclose(fp); + } + + return (retval); +} + +static enum cmd_return +cmd_creategroup(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + if (argc != 2) { + return (CMD_USAGE); + } + + if (pwfile_create_group(ctx, argv[1]) != 0) { + fprintf(stderr, "group \"%s\" already exists\n", argv[1]); + return (CMD_ERR); + } + + return (CMD_OK); +} + +static enum cmd_return +cmd_removegroup(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + if (argc != 2) { + return (CMD_USAGE); + } + + if (pwfile_remove_group(ctx, argv[1]) != 0) { + fprintf(stderr, "group \"%s\" does not exist\n", argv[1]); + return (CMD_ERR); + } + + return (CMD_OK); +} + +static enum cmd_return +cmd_changepassword(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + size_t password_len; + char password_buf[PWS3_MAX_PASSWORD_LEN + 1]; + char confirm_buf[PWS3_MAX_PASSWORD_LEN + 1]; + + if (argc > 2) { + return (CMD_USAGE); + } else if (argc == 2) { + password_len = strlen(argv[1]); + if (password_len == 0) { + fprintf(stderr, "password must not be empty\n"); + return (CMD_ERR); + } else if (password_len + 1 > sizeof (ctx->password)) { + fprintf(stderr, "password too long\n"); + return (CMD_ERR); + } + memcpy(ctx->password, argv[1], password_len + 1); + } else { + if (readpassphrase("Enter password: ", password_buf, + sizeof (password_buf), RPP_ECHO_OFF | RPP_REQUIRE_TTY) == + NULL) { + err(1, "readpassphrase"); + } + password_len = strlen(password_buf); + if (password_len == 0) { + fprintf(stderr, "password must not be empty\n"); + return (CMD_ERR); + } + if (readpassphrase("Confirm password: ", confirm_buf, + sizeof (confirm_buf), + RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { + err(1, "readpassphrase"); + } + if (strcmp(password_buf, confirm_buf) != 0) { + fprintf(stderr, "passwords do not match\n"); + return (CMD_ERR); + } + memcpy(ctx->password, password_buf, password_len + 1); + } + + return (CMD_OK); +} + +static enum cmd_return +cmd_help(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + struct cmd *cmd; + + if (argc != 1) { + return (CMD_USAGE); + } + + printf("Commands:\n"); + for (cmd = cmds; cmd->cmd_func != NULL; cmd++) { + printf("%-2s %-16s %s\n", cmd->abbrev_cmd, cmd->full_cmd, + cmd->description); + } + + return (CMD_OK); +} + +static enum cmd_return +cmd_write(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + if (argc != 1) { + return (CMD_USAGE); + } + + return ((pwfile_write_file(ctx) == 0) ? CMD_OK : CMD_ERR); +} + +static enum cmd_return +cmd_quit(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + if (argc != 1) { + return (CMD_USAGE); + } + + return (CMD_QUIT); +} + +struct cmd * +cmd_match(const char *cmd_name) +{ + size_t i; + + for (i = 0; cmds[i].cmd_func != NULL; i++) { + if ((strcmp(cmds[i].abbrev_cmd, cmd_name) == 0) || + (strcmp(cmds[i].full_cmd, cmd_name) == 0)) { + return (&cmds[i]); + } + } + + return (NULL); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmd.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 CMD_H +#define CMD_H + +#include "pwm.h" + +enum cmd_return { + CMD_OK, + CMD_ERR, + CMD_USAGE, + CMD_QUIT +}; + +struct cmd { + const char *abbrev_cmd; + const char *full_cmd; + const char *usage; + const char *description; + enum cmd_return (*cmd_func)(struct pwm_ctx *, int argc, char *argv[]); +}; + +struct cmd * cmd_match(const char *); + +#endif /* CMD_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 COMPAT_H +#define COMPAT_H + +/* for glibc asprintf, getline */ +#define _BSD_SOURCE + +#ifndef HAVE_ASPRINTF +#include "compat/asprintf.h" +#endif /* !HAVE_ASPRINTF */ + +#ifndef HAVE_ERR_H +#include "compat/err.h" +#endif /* !HAVE_ERR_H */ + +#ifndef HAVE_GETENTROPY +#include "compat/getentropy.h" +#endif /* !HAVE_GETENTROPY */ + +#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 */ + +#endif /* COMPAT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/asprintf.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "asprintf.h" + +int +vasprintf(char **strp, const char *fmt, va_list args) +{ + int len; + va_list args_new; + char *str; + + *strp = NULL; + va_copy(args_new, args); + + len = vsnprintf(NULL, 0, fmt, args); + if (len < 0) { + goto out; + } + + str = malloc(len + 1); + if (str == NULL) { + goto out; + } + len = vsnprintf(str, len + 1, fmt, args_new); + if (len < 0) { + free(str); + goto out; + } + *strp = str; +out: + va_end(args_new); + + return (len); +} + +int +asprintf(char **strp, const char *fmt, ...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = vasprintf(strp, fmt, args); + va_end(args); + + return (len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/asprintf.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 ASPRINTF_H +#define ASPRINTF_H + +#include <stdarg.h> + +int asprintf(char **, const char *, ...); +int vasprintf(char **, const char *, va_list); + +#endif /* !ASPRINTF_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/err.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "err.h" +#include "../compat.h" + +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); +} + +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 +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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/err.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 <stdarg.h> + +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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/getentropy.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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. + */ + +/* needed for syscall(2) on Linux */ +#define _GNU_SOURCE + +/* Linux >= 3.17 has getrandom(2) system call */ +#ifdef __linux__ +#include <unistd.h> +#include <sys/syscall.h> +#include <linux/random.h> +#ifdef SYS_getrandom +#define HAVE_GETRANDOM +#endif /* SYS_getrandom */ +#endif /* __linux__ */ +/* + * on unknown Unix systems without getentropy(2) or Linux without getrandom(2) + * fall back to * reading from /dev/(u)random + */ +#ifndef HAVE_GETRANDOM +#include <stdio.h> +#ifndef RANDOM_DEVICE +#ifdef __linux__ +/* on Linux /dev/urandom should be good enough */ +#define RANDOM_DEVICE "/dev/urandom" +#else /* __linux__ */ +/* on unknown Unix systems use the possibly blocking /dev/random */ +#define RANDOM_DEVICE "/dev/random" +#endif /* __linux__ */ +#endif /* !RANDOM_DEVICE */ +#endif /* !HAVE_GETRANDOM */ +#include <errno.h> + +#ifdef HAVE_GETRANDOM +static int +getentropy_linux_getrandom(void *buf, size_t buf_len) +{ + int retval; + + retval = syscall(SYS_getrandom, buf, buf_len, 0); + if (retval < 0) { + return (-1); + } else if ((size_t)retval != buf_len) { + errno = EIO; + return (-1); + } + + return (0); +} +#else +static int +getentropy_dev_random(void *buf, size_t buf_len) +{ + FILE *fp; + int saved_errno; + + fp = fopen(RANDOM_DEVICE, "r"); + if (fp == NULL) { + return (-1); + } + if (fread(buf, 1, buf_len, fp) != buf_len) { + saved_errno = errno; + fclose(fp); + errno = saved_errno; + return (-1); + } + if (fclose(fp) != 0) { + return (-1); + } + + return (0); +} +#endif /* HAVE_GETRANDOM */ + +int +getentropy(void *buf, size_t buf_len) +{ + if (buf_len > 256) { + errno = EIO; + return (-1); + } + + return ( +#ifdef HAVE_GETRANDOM + getentropy_linux_getrandom( +#else /* HAVE_GETRANDOM */ + getentropy_dev_random( +#endif /* HAVE_GETRANDOM */ + buf, buf_len)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/getentropy.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 GETENTROPY_H +#define GETENTROPY_H + +#include <stddef.h> + +int getentropy(void *, size_t); + +#endif /* !GETENTROPY_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/readpassphrase.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,209 @@ +/* $OpenBSD: readpassphrase.c,v 1.24 2013/11/24 23:51:29 deraadt 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> +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif /* HAVE_PATHS_H */ +#include <pwd.h> +#include <signal.h> +#include <string.h> +#include <termios.h> +#include <unistd.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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/readpassphrase.h Thu Jan 19 22:39:51 2017 +0100 @@ -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 <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. + */ + +#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 <sys/types.h> + +char * readpassphrase(const char *, char *, size_t, int); + +#endif /* !_READPASSPHRASE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/setprogname.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 <string.h> + +#include "setprogname.h" +#include "../compat.h" + +static const char *progname = "<unknown_program>"; + +void +setprogname(const char *name) +{ + const char *p; + + p = strrchr(name, '/'); + progname = (p != NULL) ? p + 1 : name; +} + +const char * +getprogname(void) +{ + return (progname); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/setprogname.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/compat/tree.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,748 @@ +/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps.sed Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,26 @@ +/^[^:]\{1,\}:.*\\$/{ + h + s/\([^:]\{1,\}:\).*/\1/ + x + s/[^:]\{1,\}:// +} +/\\$/,/^$/bgen +/\\$/,/[^\\]$/{ +:gen + s/[[:blank:]]*\\$// + s/^[[:blank:]]*// + G + s/\(.*\)\n\(.*\)/\2 \1/ +} +/^[^:]\{1,\}:[[:blank:]]*$/d +/^[^:]\{1,\}\.o:/{ + s/[[:blank:]]*[^[:blank:]]\{1,\}\.[cC][[:blank:]]*/ /g + s/[[:blank:]]*[^[:blank:]]\{1,\}\.[cC]$//g + s/[[:blank:]]*[^[:blank:]]\{1,\}\.cc[[:blank:]]*/ /g + s/[[:blank:]]*[^[:blank:]]\{1,\}\.cc$//g + s/[[:blank:]]*[^[:blank:]]\{1,\}\.cpp[[:blank:]]*/ /g + s/[[:blank:]]*[^[:blank:]]\{1,\}\.cpp$//g + /^[^:]\{1,\}:[[:blank:]]*$/d + s/^\([^:]\{1,\}\)\.o[[:blank:]]*:[[:blank:]]*\(.*\)/\1.d: $(wildcard \2)\ +&/ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pwfile.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,783 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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" + +#ifdef HAVE_ERR_H +#include <err.h> +#endif /* HAVE_ERR_H */ +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_TREE_H +#include <sys/tree.h> +#endif +#include <unistd.h> + +#include "pwfile.h" +#include "util.h" + +#define MIN_ARRAY_SIZE 1024 + +struct record_id_entry { + RB_ENTRY(record_id_entry) record_id_entry; + unsigned int id; + unsigned char uuid[PWS3_UUID_SIZE]; +}; + +RB_HEAD(record_id_tree, record_id_entry); + +static int record_id_entry_cmp(struct record_id_entry *, + struct record_id_entry *); +RB_PROTOTYPE_STATIC(record_id_tree, record_id_entry, record_id_entry, + record_id_entry_cmp) +RB_GENERATE_STATIC(record_id_tree, record_id_entry, record_id_entry, + record_id_entry_cmp) + +static int +record_id_entry_cmp(struct record_id_entry *entry1, + struct record_id_entry *entry2) +{ + if (entry1->id > entry2->id) { + return (-1); + } else if (entry1->id < entry2->id) { + return (1); + } + return (0); +} + +static int +pws_record_cmp(const void *p1, const void *p2) +{ + int retval; + struct pws3_record *record1 = *(struct pws3_record **)p1; + struct pws3_record *record2 = *(struct pws3_record **)p2; + struct pws3_field *group_field1; + const char *group1; + struct pws3_field *group_field2; + const char *group2; + struct pws3_field *title_field1; + const char *title1; + struct pws3_field *title_field2; + const char *title2; + + group_field1 = pws3_record_get_field(record1, PWS3_RECORD_FIELD_GROUP); + group1 = (group_field1 != NULL) ? pws3_field_get_text(group_field1) : + ""; + group_field2 = pws3_record_get_field(record2, PWS3_RECORD_FIELD_GROUP); + group2 = (group_field1 != NULL) ? pws3_field_get_text(group_field2) : + ""; + retval = strcmp(group1, group2); + if (retval != 0) { + return (retval); + } + + title_field1 = pws3_record_get_field(record1, PWS3_RECORD_FIELD_TITLE); + title1 = (title_field1 != NULL) ? pws3_field_get_text(title_field1) : + ""; + title_field2 = pws3_record_get_field(record2, PWS3_RECORD_FIELD_TITLE); + title2 = (title_field2 != NULL) ? pws3_field_get_text(title_field2) : + ""; + return (strcmp(title1, title2)); +} + +static void +record_id_tree_clear(struct record_id_tree *tree) +{ + struct record_id_entry *entry; + struct record_id_entry *entry_tmp; + + RB_FOREACH_SAFE(entry, record_id_tree, tree, entry_tmp) { + RB_REMOVE(record_id_tree, tree, entry); + free(entry); + } +} + +static void +record_id_tree_destroy(struct record_id_tree *tree) +{ + if (tree == NULL) { + return; + } + + record_id_tree_clear(tree); + free(tree); +} + +static const unsigned char * +record_id_tree_get_uuid(struct record_id_tree *tree, unsigned int id) +{ + struct record_id_entry *entry; + + entry = RB_FIND(record_id_tree, tree, + &(struct record_id_entry){ .id = id }); + if (entry == NULL) { + return (NULL); + } + + return (entry->uuid); +} + +void +pwfile_init(struct pwm_ctx *ctx) +{ + ctx->file = pws3_file_create(); + if (ctx->file == NULL) { + err(1, "pws3_file_create"); + } + + ctx->next_id = 1; + + ctx->record_id_tree = xmalloc(sizeof (struct record_id_tree)); + RB_INIT(ctx->record_id_tree); +} + +void +pwfile_destroy(struct pwm_ctx *ctx) +{ + record_id_tree_destroy(ctx->record_id_tree); + ctx->record_id_tree = NULL; + pws3_file_destroy(ctx->file); + ctx->file = NULL; +} + +int +pwfile_read_file(struct pwm_ctx *ctx, FILE *fp) +{ + struct pws3_record *pws3_record; + struct pws3_record **pws3_record_list; + size_t record_list_size = MIN_ARRAY_SIZE; + size_t record_list_len = 0; + size_t i; + struct pws3_field *uuid_field; + const unsigned char *uuid; + struct record_id_entry *entry; + + if (pws3_file_read_stream(ctx->file, ctx->password, fp) != 0) { + warnx("failed to read password database: %s", + pws3_file_get_error_message(ctx->file)); + return (-1); + } + + record_id_tree_clear(ctx->record_id_tree); + + /* sort records by group and title */ + pws3_record_list = xmalloc(sizeof (struct pws3_record *) * + record_list_size); + for (pws3_record = pws3_file_first_record(ctx->file); + pws3_record != NULL; pws3_record = pws3_file_next_record(ctx->file, + pws3_record)) { + if (record_list_len == record_list_size) { + record_list_size *= 2; + pws3_record_list = xrealloc(pws3_record_list, + sizeof (struct pws3_record *) * record_list_size); + } + pws3_record_list[record_list_len++] = pws3_record; + } + qsort(pws3_record_list, record_list_len, sizeof (struct pws3_record *), + pws_record_cmp); + + /* build the tree of record IDs */ + for (i = 0; i < record_list_len; i++) { + uuid_field = pws3_record_get_field(pws3_record_list[i], + PWS3_RECORD_FIELD_UUID); + uuid = pws3_field_get_uuid(uuid_field); + + entry = xmalloc(sizeof (struct record_id_entry)); + entry->id = ctx->next_id++; + memcpy(entry->uuid, uuid, sizeof (entry->uuid)); + + RB_INSERT(record_id_tree, ctx->record_id_tree, entry); + } + + free(pws3_record_list); + + return (0); +} + +static int +make_backup_copy(const char *filename) +{ + int retval = -1; + FILE *fp_orig = NULL; + char *backup_filename = NULL; + char *tmpfilename = NULL; + mode_t old_mode; + int fd_backup = -1; + unsigned char buf[BUFSIZ]; + size_t read_len; + FILE *fp_backup = NULL; + + fp_orig = fopen(filename, "r"); + if (fp_orig == NULL) { + if (errno != ENOENT) { + warn("fopen"); + return (-1); + } + return (0); + } + + xasprintf(&backup_filename, "%s~", filename); + xasprintf(&tmpfilename, "%s.XXXXXX", filename); + + /* create temporary file */ + old_mode = umask(S_IRWXG | S_IRWXO); + fd_backup = mkstemp(tmpfilename); + umask(old_mode); + if (fd_backup == -1) { + warn("mkstemp"); + goto out; + } + fp_backup = fdopen(fd_backup, "w"); + if (fp_backup == NULL) { + warn("fdopen"); + goto out; + } + + /* copy file contents */ + while (!feof(fp_orig)) { + read_len = fread(buf, 1, sizeof (buf), fp_orig); + if ((read_len < sizeof (buf)) && ferror(fp_orig)) { + warn("fread"); + goto out; + } + if (fwrite(buf, 1, read_len, fp_backup) != read_len) { + warn("fwrite"); + goto out; + } + } + if (fflush(fp_backup) != 0) { + warn("fflush"); + goto out; + } + if (fsync(fileno(fp_backup)) != 0) { + warn("fsync"); + goto out; + } + + retval = 0; + +out: + if ((fd_backup != -1) && (fp_backup == NULL)) { + close(fd_backup); + } + if (fp_backup != NULL) { + fclose(fp_backup); + } + if (fp_orig != NULL) { + fclose(fp_orig); + } + if (retval == 0) { + /* rename temporary file and overwrite existing file */ + if (rename(tmpfilename, backup_filename) != 0) { + warn("rename"); + retval = -1; + } + } + if ((retval != 0) && ((fd_backup != -1) || (fp_backup != NULL))) { + unlink(tmpfilename); + } + free(tmpfilename); + free(backup_filename); + + return (retval); +} + +int +pwfile_write_file(struct pwm_ctx *ctx) +{ + int retval = -1; + char *tmpfilename = NULL; + mode_t old_mode; + int fd = -1; + FILE *fp = NULL; + + if (make_backup_copy(ctx->filename) != 0) { + goto out; + } + + xasprintf(&tmpfilename, "%s.XXXXXX", ctx->filename); + + /* create temporary file */ + old_mode = umask(S_IRWXG | S_IRWXO); + fd = mkstemp(tmpfilename); + if (fd == -1) { + warn("mkstemp"); + goto out; + } + umask(old_mode); + fp = fdopen(fd, "w"); + if (fp == NULL) { + warn("fdopen"); + goto out; + } + + /* write contents */ + if (pws3_file_write_stream(ctx->file, ctx->password, 10000, fp) != 0) { + warnx("pws3_file_write_stream: %s", + pws3_file_get_error_message(ctx->file)); + goto out; + } + if (fflush(fp) != 0) { + warn("fflush"); + goto out; + } + if (fsync(fileno(fp)) != 0) { + warn("fsync"); + goto out; + } + + retval = 0; + +out: + if ((fd != -1) && (fp == NULL)) { + close(fd); + } + if (fp != NULL) { + fclose(fp); + } + if (retval == 0) { + /* rename temporary file and overwrite existing file */ + if (rename(tmpfilename, ctx->filename) != 0) { + warn("rename"); + retval = -1; + } + } + if ((retval != 0) && ((fd != -1) || (fp != NULL))) { + unlink(tmpfilename); + } + free(tmpfilename); + + return (retval); +} + +static int +list_item_cmp(const void *p1, const void *p2) +{ + int retval; + const union list_item *item1 = *(const union list_item **)p1; + const union list_item *item2 = *(const union list_item **)p2; + const char *group1; + const char *group2; + const char *title1; + const char *title2; + + /* sort both groups and records first by group name */ + group1 = (item1->any.group != NULL) ? item1->any.group : ""; + group2 = (item2->any.group != NULL) ? item2->any.group : ""; + retval = strcmp(group1, group2); + if ((retval != 0) || ((item1->any.type == ITEM_TYPE_GROUP) && + (item2->any.type == ITEM_TYPE_GROUP))) { + return (retval); + } else if ((item1->any.type == ITEM_TYPE_GROUP) && + (item2->any.type == ITEM_TYPE_RECORD)) { + /* groups come before records belonging to it */ + return (-1); + } else if ((item1->any.type == ITEM_TYPE_RECORD) && + (item2->any.type == ITEM_TYPE_GROUP)) { + return (1); + } + + /* sort records also by title */ + title1 = (item1->record.title != NULL) ? item1->record.title : ""; + title2 = (item2->record.title != NULL) ? item2->record.title : ""; + return (strcmp(title1, title2)); +} + +union list_item ** +pwfile_create_list(struct pwm_ctx *ctx) +{ + union list_item **list; + size_t list_size = MIN_ARRAY_SIZE; + size_t list_len = 0; + struct record_id_entry *entry; + union list_item *item; + struct pws3_record *pws3_record; + struct pws3_field *group_field; + const char *group; + struct pws3_field *title_field; + const char *title; + size_t i; + size_t records_len; + const char *prev_group = ""; + struct pws3_field *empty_group_field; + + list = xmalloc(sizeof (union list_item *) * list_size); + list[0] = NULL; + + /* build list of records and sort it by group and title */ + RB_FOREACH(entry, record_id_tree, ctx->record_id_tree) { + if (list_len == list_size - 1) { + list_size *= 2; + list = xrealloc(list, sizeof (union list_item *) * + list_size); + } + + pws3_record = pws3_file_get_record(ctx->file, entry->uuid); + group_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_GROUP); + group = (group_field != NULL) ? + pws3_field_get_text(group_field) : NULL; + title_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_TITLE); + title = (title_field != NULL) ? + pws3_field_get_text(title_field) : NULL; + + item = xmalloc(sizeof (union list_item)); + item->record.type = ITEM_TYPE_RECORD; + item->record.group = (group != NULL) ? xstrdup(group) : NULL; + item->record.title = (title != NULL) ? xstrdup(title) : NULL; + item->record.id = entry->id; + memcpy(item->record.uuid, entry->uuid, + sizeof (item->record.uuid)); + + list[list_len++] = item; + list[list_len] = NULL; + } + qsort(list, list_len, sizeof (union list_item *), list_item_cmp); + + /* build list of groups by comparing the groups of the sorted records */ + for (i = 0, records_len = list_len; i < records_len; i++) { + if (list_len == list_size - 1) { + list_size *= 1.5; + list = xrealloc(list, sizeof (union list_item *) * + list_size); + } + + group = (list[i]->record.group != NULL) ? + list[i]->record.group : ""; + if (strcmp(prev_group, group) != 0) { + item = xmalloc(sizeof (union list_item)); + item->record.type = ITEM_TYPE_GROUP; + item->record.group = (group != NULL) ? xstrdup(group) : + NULL; + + list[list_len++] = item; + list[list_len] = NULL; + + prev_group = group; + } + } + + /* add empty groups to the list */ + for (empty_group_field = pws3_file_first_empty_group(ctx->file); + empty_group_field != NULL; + empty_group_field = pws3_file_next_empty_group(ctx->file, + empty_group_field)) { + if (list_len == list_size - 1) { + list_size *= 1.5; + list = xrealloc(list, sizeof (union list_item *) * + list_size); + } + + group = pws3_field_get_text(empty_group_field); + + item = xmalloc(sizeof (union list_item)); + item->record.type = ITEM_TYPE_GROUP; + item->record.group = xstrdup(group); + + list[list_len++] = item; + list[list_len] = NULL; + } + + list_size = list_len + 2; + list = xrealloc(list, sizeof (union list_item *) * list_size); + /* sort the final list by group and title */ + qsort(list, list_len, sizeof (union list_item *), list_item_cmp); + + return (list); +} + +void +pwfile_destroy_list(union list_item **list) +{ + size_t i; + + if (list == NULL) { + return; + } + + for (i = 0; list[i] != NULL; i++) { + if (list[i]->any.type == ITEM_TYPE_RECORD) { + free(list[i]->record.title); + } + free(list[i]->any.group); + free(list[i]); + } + + free(list); +} + +static void +update_record(struct pws3_record *pws3_record, struct record *record) +{ + struct pws3_field *title_field; + struct pws3_field *group_field; + struct pws3_field *username_field; + struct pws3_field *password_field; + struct pws3_field *notes_field; + struct pws3_field *url_field; + + if (record->title != NULL) { + title_field = pws3_field_create(0, PWS3_RECORD_FIELD_TITLE); + if (title_field == NULL) { + err(1, "pws3_record_field_create"); + } + if (pws3_field_set_text(title_field, + record->title) != 0) { + err(1, "pws3_field_set_text"); + } + pws3_record_set_field(pws3_record, title_field); + } + if (record->group != NULL) { + group_field = pws3_field_create(0, PWS3_RECORD_FIELD_GROUP); + if (group_field == NULL) { + err(1, "pws3_record_field_create"); + } + if (pws3_field_set_text(group_field, + record->group) != 0) { + err(1, "pws3_field_set_text"); + } + pws3_record_set_field(pws3_record, group_field); + } + if (record->username != NULL) { + username_field = pws3_field_create(0, + PWS3_RECORD_FIELD_USERNAME); + if (username_field == NULL) { + err(1, "pws3_record_field_create"); + } + if (pws3_field_set_text(username_field, + record->username) != 0) { + err(1, "pws3_field_set_text"); + } + pws3_record_set_field(pws3_record, username_field); + } + if (record->password != NULL) { + password_field = pws3_field_create(0, + PWS3_RECORD_FIELD_PASSWORD); + if (password_field == NULL) { + err(1, "pws3_record_field_create"); + } + if (pws3_field_set_text(password_field, + record->password) != 0) { + err(1, "pws3_field_set_text"); + } + pws3_record_set_field(pws3_record, password_field); + } + if (record->notes != NULL) { + notes_field = pws3_field_create(0, PWS3_RECORD_FIELD_NOTES); + if (notes_field == NULL) { + err(1, "pws3_record_field_create"); + } + if (pws3_field_set_text(notes_field, record->notes) != 0) { + err(1, "pws3_field_set_text"); + } + pws3_record_set_field(pws3_record, notes_field); + } + if (record->url != NULL) { + url_field = pws3_field_create(0, PWS3_RECORD_FIELD_URL); + if (url_field == NULL) { + err(1, "pws3_record_field_create"); + } + if (pws3_field_set_text(url_field, record->url) != 0) { + err(1, "pws3_field_set_text"); + } + pws3_record_set_field(pws3_record, url_field); + } +} + +int +pwfile_create_record(struct pwm_ctx *ctx, struct record *record) +{ + struct pws3_record *pws3_record; + const unsigned char *uuid; + struct record_id_entry *entry; + + pws3_record = pws3_record_create(); + if (pws3_record == NULL) { + err(1, "pws3_record_create"); + } + update_record(pws3_record, record); + pws3_file_insert_record(ctx->file, pws3_record); + + uuid = pws3_field_get_uuid(pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_UUID)); + entry = xmalloc(sizeof (struct record_id_entry)); + entry->id = ctx->next_id++; + memcpy(entry->uuid, uuid, sizeof (entry->uuid)); + RB_INSERT(record_id_tree, ctx->record_id_tree, entry); + + return (0); +} + +int +pwfile_modify_record(struct pwm_ctx *ctx, unsigned int id, + struct record *record) +{ + const unsigned char *uuid; + + uuid = record_id_tree_get_uuid(ctx->record_id_tree, id); + if (uuid == NULL) { + return (-1); + } + + update_record(pws3_file_get_record(ctx->file, uuid), record); + + return (0); +} + +int +pwfile_remove_record(struct pwm_ctx *ctx, unsigned int id) +{ + const unsigned char *uuid; + struct record_id_entry *entry; + + uuid = record_id_tree_get_uuid(ctx->record_id_tree, id); + if (uuid == NULL) { + return (-1); + } + + pws3_record_destroy(pws3_file_remove_record(ctx->file, uuid)); + + entry = RB_FIND(record_id_tree, ctx->record_id_tree, + &(struct record_id_entry){ .id = id }); + free(RB_REMOVE(record_id_tree, ctx->record_id_tree, entry)); + + return (0); +} + +int +pwfile_create_group(struct pwm_ctx *ctx, const char *group) +{ + struct pws3_record *pws3_record; + struct pws3_field *group_field; + struct pws3_field *empty_group_field; + + /* check for a record in the given group */ + for (pws3_record = pws3_file_first_record(ctx->file); + pws3_record != NULL; pws3_record = pws3_file_next_record(ctx->file, + pws3_record)) { + group_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_GROUP); + if ((group_field != NULL) && + (strcmp(group, pws3_field_get_text(group_field)) == 0)) { + return (-1); + } + } + + empty_group_field = pws3_field_create(1, + PWS3_HEADER_FIELD_EMPTY_GROUPS); + if (empty_group_field == NULL) { + err(1, "pws3_field_create"); + } + if (pws3_field_set_text(empty_group_field, group) != 0) { + err(1, "pws3_field_set_text"); + } + pws3_file_insert_empty_group(ctx->file, empty_group_field); + + return (0); +} + +int +pwfile_remove_group(struct pwm_ctx *ctx, const char *group) +{ + struct pws3_field *empty_group_field; + + empty_group_field = pws3_file_remove_empty_group(ctx->file, group); + if (empty_group_field != NULL) { + return (-1); + } + pws3_field_destroy(empty_group_field); + + return (0); +} + +struct record * +pwfile_get_record(struct pwm_ctx *ctx, unsigned int id) +{ + struct record *record; + const unsigned char *uuid; + struct pws3_record *pws3_record; + struct pws3_field *title_field; + struct pws3_field *group_field; + struct pws3_field *username_field; + struct pws3_field *password_field; + struct pws3_field *notes_field; + struct pws3_field *url_field; + + uuid = record_id_tree_get_uuid(ctx->record_id_tree, id); + if (uuid == NULL) { + return (NULL); + } + pws3_record = pws3_file_get_record(ctx->file, uuid); + + record = xmalloc(sizeof (struct record)); + + title_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_TITLE); + record->title = (title_field != NULL) ? + xstrdup(pws3_field_get_text(title_field)) : NULL; + + group_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_GROUP); + record->group = (group_field != NULL) ? + xstrdup(pws3_field_get_text(group_field)) : NULL; + + username_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_USERNAME); + record->username = (username_field != NULL) ? + xstrdup(pws3_field_get_text(username_field)) : NULL; + + password_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_PASSWORD); + record->password = (password_field != NULL) ? + xstrdup(pws3_field_get_text(password_field)) : NULL; + + notes_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_NOTES); + record->notes = (notes_field != NULL) ? + xstrdup(pws3_field_get_text(notes_field)) : NULL; + + url_field = pws3_record_get_field(pws3_record, PWS3_RECORD_FIELD_URL); + record->url = (url_field != NULL) ? + xstrdup(pws3_field_get_text(url_field)) : NULL; + + return (record); +} + +void +pwfile_destroy_record(struct record *record) +{ + if (record == NULL) { + return; + } + + free(record->title); + free(record->group); + free(record->username); + free(record->password); + free(record->notes); + free(record->url); + free(record); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pwfile.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 PWFILE_H +#define PWFILE_H + +#include "pwm.h" + +enum item_type { + ITEM_TYPE_RECORD, + ITEM_TYPE_GROUP +}; + +union list_item { + struct any_item { + enum item_type type; + char *group; + } any; + struct record_item { + enum item_type type; + char *group; + char *title; + unsigned int id; + unsigned char uuid[PWS3_UUID_SIZE]; + } record; + struct group_item { + enum item_type type; + char *group; + } group; +}; + +struct record_id_tree; + +struct record { + char *title; + char *group; + char *username; + char *password; + char *notes; + char *url; +}; + +void pwfile_init(struct pwm_ctx *ctx); +void pwfile_destroy(struct pwm_ctx *); +int pwfile_read_file(struct pwm_ctx *, FILE *); +int pwfile_write_file(struct pwm_ctx *); +union list_item ** pwfile_create_list(struct pwm_ctx *); +void pwfile_destroy_list(union list_item **); +int pwfile_create_record(struct pwm_ctx *, struct record *); +int pwfile_modify_record(struct pwm_ctx *, unsigned int, + struct record *); +int pwfile_remove_record(struct pwm_ctx *, unsigned int); +int pwfile_create_group(struct pwm_ctx *, const char *); +int pwfile_remove_group(struct pwm_ctx *, const char *); +struct record * pwfile_get_record(struct pwm_ctx *, unsigned int); +void pwfile_destroy_record(struct record *); + +#endif /* !PWFILE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pwm.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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" + +#ifdef HAVE_ERR_H +#include <err.h> +#endif /* HAVE_ERR_H */ +#include <errno.h> +#include <langinfo.h> +#include <locale.h> +#include <pwd.h> +#ifdef HAVE_READPASSPHRASE_H +#include <readpassphrase.h> +#endif /* READPASSPHRASE_H */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <pwd.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> + +#include "pwm.h" +#include "cmd.h" +#include "pwfile.h" +#include "tok.h" +#include "util.h" + +#ifndef PWM_LINE_MAX +#define PWM_LINE_MAX 16384 +#endif /* !PWM_LINE_MAX */ + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-P file] [filename]\n", getprogname()); +} + +static int +run_input_loop(struct pwm_ctx *ctx, int is_interactive) +{ + int retval = -1; + char buf[PWM_LINE_MAX]; + int c; + int argc = 0; + char **argv = NULL; + struct cmd *cmd; + int i; + + for (;;) { + if (fgets(buf, (int)sizeof (buf), stdin) == NULL) { + if (ferror(stdin)) { + /* error */ + warn("failed to read command"); + goto out; + } else if (feof(stdin)) { + /* EOF */ + break; + } + } + if ((buf[strlen(buf) - 1] != '\n') && !feof(stdin)) { + /* line was truncated */ + fprintf(stderr, "line too long\n"); + if (is_interactive) { + /* skip input to next newline */ + do { + errno = 0; + c = fgetc(stdin); + if ((c == EOF) && (errno != 0)) { + warn("failed to read command"); + goto out; + } + } while ((c != '\n') && (c != EOF)); + } else { + /* fatal error in non-interactive mode */ + goto out; + } + } + + /* tokenize line */ + switch(tok_tokenize(buf, &argc, &argv)) { + case TOK_ERR_SYSTEM_ERROR: + err(1, "tok_tokenize"); + case TOK_ERR_UNTERMINATED_QUOTE: + fprintf(stderr, "unterminated quote\n"); + if (!is_interactive) { + goto out; + } + goto next; + case TOK_ERR_TRAILING_BACKSLASH: + fprintf(stderr, "trailing backslash\n"); + if (!is_interactive) { + goto out; + } + goto next; + } + + if (argc == 0) { + /* empty line */ + goto next; + } + + /* find and execute the command */ + cmd = cmd_match(argv[0]); + if (cmd == NULL) { + fprintf(stderr, "unknown command: %s\n", argv[0]); + if (is_interactive) { + goto next; + } else { + goto out; + } + } + switch (cmd->cmd_func(ctx, argc, argv)) { + case CMD_OK: + break; + case CMD_USAGE: + fprintf(stderr, "usage: %s\n", cmd->usage); + case CMD_ERR: /* FALLTHROUGH */ + if (!is_interactive) { + goto out; + } + break; + case CMD_QUIT: + goto quit; + } + +next: + for (i = 0; i < argc; i++) { + free(argv[i]); + } + free(argv); + argc = 0; + argv = NULL; + } + +quit: + retval = 0; + +out: + for (i = 0; i < argc; i++) { + free(argv[i]); + } + free(argv); + + return (retval); +} + +static int +read_password_from_file(const char *filename, char *password, + size_t password_size) +{ + int retval = -1; + char *buf = NULL; + FILE *fp = NULL; + size_t password_len = 0; + + buf = xmalloc(password_size); + + fp = fopen(filename, "r"); + if (fp == NULL) { + warn("failed to open master password file \"%s\"", filename); + goto out; + } + if (fgets(buf, password_size, fp) == NULL) { + /* read error or empty file */ + if (ferror(fp)) { + /* read error */ + warn("failed to read master password from \"%s\"", + filename); + } + goto out; + } + password_len = strlen(buf); + if (buf[password_len - 1] == '\n') { + /* strip trailing newline */ + password_len--; + if (password_len == 0) { + /* first line is empty */ + goto out; + } + } else if (!feof(fp)) { + /* the first line was truncated, password is too long */ + goto out; + } + memcpy(password, buf, password_size); + retval = 0; + +out: + password[password_len] = '\0'; + + if (fp != NULL) { + fclose(fp); + } + free(buf); + + return (retval); +} + +int +main(int argc, char *argv[]) +{ + int status = EXIT_FAILURE; + char *locale; + int is_interactive; + int errflag = 0; + int c; + const char *master_password_filename = NULL; + struct pwm_ctx ctx = { 0 }; + struct passwd *passwd; + char *pwm_dirname = NULL; + FILE *fp = NULL; + char password_buf[PWS3_MAX_PASSWORD_LEN + 1]; + + setprogname(argv[0]); + + /* set up locale and check for UTF-8 codeset */ + locale = setlocale(LC_ALL, ""); + if (locale == NULL) { + errx(1, "invalid locale"); + } + if ((strcasecmp(nl_langinfo(CODESET), "UTF-8") != 0) && + (strcasecmp(nl_langinfo(CODESET), "UTF8") != 0)) { + fprintf(stderr, "pwm requires a locale with UTF-8 character " + "encoding.\n"); + goto out; + } + + /* timestamps are processed as UTC */ + if (setenv("TZ", "", 1) != 0) { + goto out; + } + tzset(); + + /* initialize libpws */ + if (pws_init() != 0) { + goto out; + } + + is_interactive = isatty(STDIN_FILENO); + + while (!errflag && (c = getopt(argc, argv, "P:h")) != -1) { + switch (c) { + case 'P': + master_password_filename = optarg; + break; + case 'h': + usage(); + status = EXIT_SUCCESS; + goto out; + default: + errflag = 1; + } + } + if (errflag || (optind + 1 < argc)) { + usage(); + status = EXIT_USAGE; + goto out; + } + + if (optind == argc) { + passwd = getpwuid(getuid()); + if (passwd == NULL) { + err(1, "getpwuid"); + } + xasprintf(&pwm_dirname, "%s/.pwm", passwd->pw_dir); + xasprintf(&ctx.filename, "%s/pwm.psafe3", pwm_dirname); + + /* create ~/.pwm directory if necessary */ + if ((mkdir(pwm_dirname, S_IRWXU) != 0) && (errno != EEXIST)) { + warn("failed to create directory \"%s\"", pwm_dirname); + goto out; + } + } else if (optind + 1 == argc) { + ctx.filename = xstrdup(argv[optind++]); + } else { + usage(); + status = EXIT_USAGE; + goto out; + } + + if (is_interactive) { + printf("pwm version %s\n", VERSION); + } else if (master_password_filename == NULL) { + fprintf(stderr, "master password file must be specified when " + "running non-interactively\n"); + goto out; + } + + pwfile_init(&ctx); + + fp = fopen(ctx.filename, "r"); + if ((fp == NULL) && (errno != ENOENT)) { + warn("failed to open password database"); + goto out; + } + /* obtain master password */ + if (master_password_filename != NULL) { + if (read_password_from_file(master_password_filename, + ctx.password, sizeof (ctx.password)) != 0) { + fprintf(stderr, "malformed password database\n"); + goto out; + } + } else { + if (readpassphrase("Enter password: ", ctx.password, + sizeof (ctx.password), RPP_ECHO_OFF | RPP_REQUIRE_TTY) == + NULL) { + err(1, "readpassphrase"); + } + if (ctx.password[0] == '\0') { + fprintf(stderr, "password must not be empty\n"); + goto out; + } + if (fp == NULL) { + if (readpassphrase("Confirm password: ", password_buf, + sizeof (password_buf), + RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { + err(1, "readpassphrase"); + } + if (strcmp(ctx.password, password_buf) != 0) { + fprintf(stderr, "passwords do not match\n"); + goto out; + } + } + } + if (fp != NULL) { + if (pwfile_read_file(&ctx, fp) != 0) { + goto out; + } + fclose(fp); + fp = NULL; + } + + /* run main input loop */ + status = (run_input_loop(&ctx, is_interactive) != 0); + +out: + pwfile_destroy(&ctx); + if (fp != NULL) { + fclose(fp); + } + free(ctx.filename); + free(pwm_dirname); + + exit(status); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pwm.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 PWM_H +#define PWM_H + +#include <pws.h> + +struct pwm_ctx { + char *filename; + struct pws3_file *file; + struct record_id_tree *record_id_tree; + unsigned int next_id; + char password[PWS3_MAX_PASSWORD_LEN + 1]; +}; + +#endif /* PWM_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tok.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tok.h" + +enum tok_states { + STATE_INITIAL, + STATE_IN_WORD, + STATE_IN_QUOTE, + STATE_IN_WORD_ESCAPE, + STATE_IN_QUOTE_ESCAPE +}; + +static inline int +strbuf_appendc(char **bufp, size_t *buf_sizep, int c) +{ + char *buf = *bufp; + size_t buf_size = *buf_sizep; + size_t len; + + len = ((buf != NULL) && (c >= 0)) ? strlen(buf) : 0; + + /* allocate buffer if *bufp is NULL and *buf_sizep is 0 */ + if (buf_size < len + (c >= 0) + 1) { + buf_size = (buf_size * 2 > BUFSIZ) ? buf_size * 2 : BUFSIZ; + buf = realloc(buf, buf_size); + if (buf == NULL) { + return (-1); + } + } + + /* append character to string buffer or reset buffer if c is -1 */ + if (c >= 0) { + buf[len++] = c; + } + buf[len] = '\0'; + + *bufp = buf; + *buf_sizep = buf_size; + + return (0); +} + +enum tok_err +tok_tokenize(const char *s, int *tokencp, char ***tokenvp) +{ + int retval = TOK_ERR_SYSTEM_ERROR; + int saved_errno = 0; + char **tokenv; + size_t tokenc = 0; + const char *p = s; + enum tok_states state = STATE_INITIAL; + char quote; + char *buf = NULL; + size_t buf_size = 0; + char *token; + size_t i; + + /* + * allocate maximum number of tokens including the terminating NULL + * pointer: ceil(length / 2) + 1 + */ + tokenv = malloc(((strlen(s) + 2 - 1) / 2 + 1) * sizeof (char *)); + if (tokenv == NULL) { + saved_errno = errno; + goto out; + } + tokenv[0] = NULL; + + for (;;) { + switch (state) { + case STATE_INITIAL: + switch (*p) { + case ' ': /* FALLTHROUGH */ + case '\t': /* FALLTHROUGH */ + case '\n': + /* skip initial whitespace */ + break; + case '"': /* FALLTHROUGH */ + case '\'': + /* start quoted part of token */ + state = STATE_IN_QUOTE; + quote = *p; + if (strbuf_appendc(&buf, &buf_size, -1) != 0) { + saved_errno = errno; + goto out; + } + break; + case '\\': + /* start token with a backslash escape */ + state = STATE_IN_WORD_ESCAPE; + if (strbuf_appendc(&buf, &buf_size, -1) != 0) { + saved_errno = errno; + goto out; + } + break; + case '\0': + /* end of input */ + retval = 0; + goto out; + default: + /* start token with a word */ + state = STATE_IN_WORD; + if (strbuf_appendc(&buf, &buf_size, -1) != 0) { + saved_errno = errno; + goto out; + } + if (strbuf_appendc(&buf, &buf_size, *p) != 0) { + saved_errno = errno; + goto out; + } + } + break; + case STATE_IN_WORD: + switch (*p) { + case ' ': /* FALLTHROUGH */ + case '\t': /* FALLTHROUGH */ + case '\n': /* FALLTHROUGH */ + case '\0': + /* end of token */ + token = strdup(buf); + if (token == NULL) { + saved_errno = errno; + goto out; + } + tokenv[tokenc++] = token; + tokenv[tokenc] = NULL; + if (*p == '\0') { + retval = 0; + goto out; + } + state = STATE_INITIAL; + break; + case '"': /* FALLTHROUGH */ + case '\'': + /* start quoted part of token */ + state = STATE_IN_QUOTE; + quote = *p; + break; + case '\\': + /* start backslash escape */ + state = STATE_IN_WORD_ESCAPE; + break; + default: + /* regular character */ + if (strbuf_appendc(&buf, &buf_size, *p) != 0) { + saved_errno = errno; + goto out; + } + } + break; + case STATE_IN_QUOTE: + switch (*p) { + case '"': /* FALLTHROUGH */ + case '\'': + if (*p == quote) { + /* end quoted part of token */ + state = STATE_IN_WORD; + } else { + /* quote quote character */ + if (strbuf_appendc(&buf, &buf_size, + *p) != 0) { + saved_errno = errno; + goto out; + } + } + break; + case '\\': + /* start quoted backslash escape */ + state = STATE_IN_QUOTE_ESCAPE; + break; + case '\0': + /* unclosed quote */ + retval = TOK_ERR_UNTERMINATED_QUOTE; + goto out; + default: + /* regular character */ + if (strbuf_appendc(&buf, &buf_size, *p) != 0) { + saved_errno = errno; + goto out; + } + } + break; + case STATE_IN_WORD_ESCAPE: /* FALLTHROUGH */ + case STATE_IN_QUOTE_ESCAPE: + if (*p == '\0') { + /* trailing backslash */ + retval = TOK_ERR_TRAILING_BACKSLASH; + goto out; + } + /* escaped character */ + state = (state == STATE_IN_WORD_ESCAPE) ? + STATE_IN_WORD : STATE_IN_QUOTE; + if (strbuf_appendc(&buf, &buf_size, *p) != 0) { + saved_errno = errno; + goto out; + } + break; + } + p++; + } + +out: + if (retval < 0) { + for (i = 0; i < tokenc; i++) { + free(tokenv[i]); + } + free(tokenv); + } else { + *tokencp = tokenc; + *tokenvp = realloc(tokenv, (tokenc + 1) * sizeof (char *)); + } + free(buf); + if (retval < 0) { + errno = saved_errno; + } + + return (retval); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tok.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 TOK_H +#define TOK_H + +enum tok_err { + TOK_ERR_SYSTEM_ERROR = -1, + TOK_ERR_UNTERMINATED_QUOTE = -2, + TOK_ERR_TRAILING_BACKSLASH = -3 +}; + +enum tok_err tok_tokenize(const char *, int *, char ***); + +#endif /* !TOK_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util.c Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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" + +#ifdef HAVE_ERR_H +#include <err.h> +#endif /* HAVE_ERR_H */ +#include <stdio.h> +#include <string.h> + +#include "util.h" + +void * +xmalloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) { + err(1, "malloc"); + } + + return (ptr); +} + +void * +xrealloc(void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + if (ptr == NULL) { + err(1, "realloc"); + } + + return (ptr); +} + +char * +xstrdup(const char *str) +{ + char *new; + + new = strdup(str); + if (new == NULL) { + err(1, "strdup"); + } + + return (new); +} + +char * +xasprintf(char **strp, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (vasprintf(strp, fmt, args) < 0) { + err(1, "vasprintf"); + } + va_end(args); + + return (*strp); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util.h Thu Jan 19 22:39:51 2017 +0100 @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * + * 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 UTIL_H +#define UTIL_H + +#include <stdlib.h> + +#define EXIT_USAGE 2 + +#define COUNTOF(x) ((sizeof (x)/sizeof (0[x])) / ((size_t)(!(sizeof (x) % \ + sizeof (0[x]))))) + +void * xmalloc(size_t); +void * xrealloc(void *, size_t); +char * xstrdup(const char *); +char * xasprintf(char **, const char *, ...); + +#endif /* UTIL_H */