Mercurial > projects > libpws
diff pwsdump.c @ 2:97097b4b6bfb
Add pwsdump utility
The pwsdum utility can dump PasswordSafe database files to a plaintext format
and convert this format back into a PasswordSafe database.
author | Guido Berhoerster <guido+libpws@berhoerster.name> |
---|---|
date | Wed, 01 Apr 2015 14:57:57 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pwsdump.c Wed Apr 01 14:57:57 2015 +0200 @@ -0,0 +1,1118 @@ +/* + * Copyright (C) 2015 Guido Berhoerster <guido+libpws@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 <ctype.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <inttypes.h> +#include <limits.h> +#include <time.h> +#include <errno.h> +#include <assert.h> +#include <libgen.h> +#include <sys/stat.h> +#ifdef HAVE_READPASSPHRASE_H +#include <readpassphrase.h> +#endif /* HAVE_READPASSPHRASE_H */ +#ifdef HAVE_VIS_H +#include <vis.h> +#endif /* HAVE_VIS_H */ +#ifdef HAVE_SYS_ENDIAN_H +#include <sys/endian.h> +#else +#ifdef HAVE_ENDIAN_H +#include <endian.h> +#endif /* HAVE_ENDIAN_H */ +#endif /* HAVE_SYS_ENDIAN_H */ +#ifdef HAVE_ERR_H +#include <err.h> +#endif /* HAVE_ERR_H */ +#include <pws.h> + +#define EXIT_USAGE 2 +#define TIME_FORMAT "%Y-%m-%dT%TZ" +#define TIME_SIZE (4 + 1 + 2 + 1 + 2 + 1 + 8 + 1 + 1) + +enum { + FORMAT_PWS3, + FORMAT_DUMP +}; + +enum { + INITIAL, + IN_HEADER, + IN_RECORD +}; + +static void +header_field_dump_write(struct pws3_field *field, FILE *fp) { + size_t i; + const char *text; + char *vis_text; + const unsigned char *bytes; + time_t time; + struct tm *tm; + char time_buf[TIME_SIZE]; + size_t len; + + fprintf(fp, "%02x:", pws3_field_get_type(field)); + switch (pws3_field_get_data_type(field)) { + case PWS_DATA_TYPE_UUID: + bytes = pws3_field_get_uuid(field); + for (i = 0; i < 16; i++) { + fprintf(fp, "%02x", bytes[i]); + } + fprintf(fp, "\n"); + break; + case PWS_DATA_TYPE_TEXT: + text = pws3_field_get_text(field); + text = (text != NULL) ? text : ""; + vis_text = malloc(4 * strlen(text) + 1); + if (vis_text == NULL) { + err(1, NULL); + } + strvis(vis_text, text, VIS_TAB | VIS_NL | VIS_CSTYLE); + fprintf(fp, "%s\n", vis_text); + free(vis_text); + break; + case PWS_DATA_TYPE_TIME: + time = pws3_field_get_time(field); + tm = gmtime(&time); + strftime(time_buf, sizeof (time_buf), TIME_FORMAT, tm); + fprintf(fp, "%s\n", time_buf); + break; + case PWS_DATA_TYPE_UINT8: + fprintf(fp, "%02" PRIx8 "\n", + pws3_field_get_uint8(field)); + break; + case PWS_DATA_TYPE_UINT16: + fprintf(fp, "%04" PRIx16 "\n", + pws3_field_get_uint16(field)); + break; + case PWS_DATA_TYPE_UINT32: + fprintf(fp, "%08" PRIx32 "\n", + pws3_field_get_uint32(field)); + break; + case PWS_DATA_TYPE_BYTES: + pws3_field_get_bytes(field, &bytes, &len); + for (i = 0; i < len; i++) { + fprintf(fp, "%s%02x", (i > 0) ? " " : "", bytes[i]); + } + fprintf(fp, "\n"); + } +} + +static void +record_field_dump_write(struct pws3_field *field, FILE *fp) { + uint8_t field_type; + size_t i; + const char *text; + char *vis_text; + const unsigned char *bytes; + time_t time; + struct tm *tm; + char time_buf[TIME_SIZE]; + size_t len; + + field_type = pws3_field_get_type(field); + fprintf(fp, "%02x:", field_type); + switch (pws3_field_get_data_type(field)) { + case PWS_DATA_TYPE_UUID: + bytes = pws3_field_get_uuid(field); + for (i = 0; i < 16; i++) { + fprintf(fp, "%02x", bytes[i]); + } + fprintf(fp, "\n"); + break; + case PWS_DATA_TYPE_TEXT: + text = pws3_field_get_text(field); + text = (text != NULL) ? text : ""; + + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + vis_text = malloc(4 * strlen(text) + 1); + if (vis_text == NULL) { + err(1, NULL); + } + } else { + vis_text = malloc(4 * strlen(text) + 1); + if (vis_text == NULL) { + err(1, NULL); + } + } + strvis(vis_text, text, VIS_TAB | VIS_NL | VIS_CSTYLE); + fprintf(fp, "%s\n", vis_text); + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + free(vis_text); + } else { + free(vis_text); + } + break; + case PWS_DATA_TYPE_TIME: + time = pws3_field_get_time(field); + tm = gmtime(&time); + strftime(time_buf, sizeof (time_buf), TIME_FORMAT, tm); + fprintf(fp, "%s\n", time_buf); + break; + case PWS_DATA_TYPE_UINT8: + fprintf(fp, "%02" PRIx8 "\n", + pws3_field_get_uint8(field)); + break; + case PWS_DATA_TYPE_UINT16: + fprintf(fp, "%04" PRIx16 "\n", + pws3_field_get_uint16(field)); + break; + case PWS_DATA_TYPE_UINT32: + fprintf(fp, "%08" PRIx32 "\n", + pws3_field_get_uint32(field)); + break; + case PWS_DATA_TYPE_BYTES: + pws3_field_get_bytes(field, &bytes, &len); + for (i = 0; i < len; i++) { + fprintf(fp, "%s%02x", (i > 0) ? " " : "", bytes[i]); + } + fprintf(fp, "\n"); + } +} + +static int +parse_hex_byte(const char *str, uint8_t *u8p) +{ + size_t i; + uint8_t value; + uint8_t u8 = 0; + + if (!(isascii(str[0]) && isxdigit(str[0])) || + !(isascii(str[1]) && isxdigit(str[1]))) { + return (-1); + } + + for (i = 0; i < 2; i++) { + if (str[i] >= '0' && str[i] <= '9') { + value = (str[i] - '0'); + } else if (str[i] >= 'A' && str[i] <= 'F') { + value = (10 + (str[i] - 'A')); + } else { + value = (10 + (str[i] - 'a')); + } + u8 += value << ((2 - 1 - i) * 4); + } + + *u8p = u8; + + return (0); +} + +static struct pws3_field * +header_field_dump_parse(const char *line) +{ + const char *p = line; + uint8_t field_type; + struct pws3_field *field = NULL; + size_t len; + size_t i; + unsigned char uuid[32]; + char *text = NULL; + struct tm tm; + uint8_t u8; + uint16_t u16; + uint32_t u32; + unsigned char *bytes = NULL; + + if (strlen(line) < 3) { + goto err; + } + + if (parse_hex_byte(p, &field_type) != 0) { + goto err; + } + p += 2; + field = pws3_field_create(1, field_type); + if (field == NULL) { + err(1, NULL); + } + + if (*p++ != ':') { + goto err; + } + + len = strlen(p); + + switch (pws3_field_get_data_type(field)) { + case PWS_DATA_TYPE_UUID: + for (i = 0; i < 16; i++) { + if (parse_hex_byte(p, &uuid[i]) != 0) { + goto err; + } + p += 2; + + while (*p == ' ') { p++; } + } + + if (*p != '\0') { + goto err; + } + + if (pws3_field_set_uuid(field, uuid) != 0) { + goto err; + } + break; + case PWS_DATA_TYPE_TEXT: + if (len > PWS3_MAX_FIELD_SIZE) { + goto err; + } + text = malloc(len + 1); + if (text == NULL) { + err(1, NULL); + } + if (strunvis(text, p) == -1) { + goto err; + } + if (pws3_field_set_text(field, text) != 0) { + goto err; + } + break; + case PWS_DATA_TYPE_TIME: + p = strptime(p, TIME_FORMAT, &tm); + if ((p == NULL) || (*p != '\0')) { + goto err; + } + tm.tm_isdst = -1; + pws3_field_set_time(field, mktime(&tm)); + break; + case PWS_DATA_TYPE_UINT8: + if (len != 2) { + goto err; + } + if (parse_hex_byte(p, &u8) != 0) { + goto err; + } + pws3_field_set_uint8(field, u8); + break; + case PWS_DATA_TYPE_UINT16: + if (len != 4) { + goto err; + } + for (i = 0; i < 2; i++) { + if (parse_hex_byte(p, &((unsigned char *)&u16)[i]) != + 0) { + goto err; + } + p += 2; + } + pws3_field_set_uint16(field, be16toh(u16)); + break; + case PWS_DATA_TYPE_UINT32: + if (len != 8) { + goto err; + } + for (i = 0; i < 4; i++) { + if (parse_hex_byte(p, + &((unsigned char *)&u32)[i]) != 0) { + goto err; + } + p += 2; + } + pws3_field_set_uint16(field, be32toh(u32)); + break; + case PWS_DATA_TYPE_BYTES: + bytes = malloc(len / 2); + if (bytes == NULL) { + err(1, NULL); + } + for (i = 0; (*p != '\0') && (i < PWS3_MAX_FIELD_SIZE); i++) { + if (parse_hex_byte(p, &bytes[i]) != 0) { + goto err; + } + p += 2; + + while (*p == ' ') { p++; } + } + + if (*p != '\0') { + goto err; + } + + if (pws3_field_set_bytes(field, bytes, i) != 0) { + goto err; + } + } + + free(bytes); + free(text); + + return (field); +err: + free(bytes); + free(text); + pws3_field_destroy(field); + + return (NULL); +} + +static struct pws3_field * +record_field_dump_parse(const char *line) +{ + const char *p = line; + uint8_t field_type = 0xff; + struct pws3_field *field = NULL; + size_t len; + size_t i; + unsigned char uuid[32]; + char *text = NULL; + struct tm tm; + uint8_t u8; + uint16_t u16; + uint32_t u32; + unsigned char *bytes = NULL; + + if (strlen(line) < 3) { + goto err; + } + + if (parse_hex_byte(p, &field_type) != 0) { + goto err; + } + p += 2; + field = pws3_field_create(0, field_type); + if (field == NULL) { + err(1, NULL); + } + + if (*p++ != ':') { + goto err; + } + + len = strlen(p); + + switch (pws3_field_get_data_type(field)) { + case PWS_DATA_TYPE_UUID: + for (i = 0; i < 16; i++) { + if (parse_hex_byte(p, &uuid[i]) != 0) { + goto err; + } + p += 2; + + while (*p == ' ') { p++; } + } + + if (*p != '\0') { + goto err; + } + + if (pws3_field_set_uuid(field, uuid) != 0) { + goto err; + } + break; + case PWS_DATA_TYPE_TEXT: + if (((field_type == PWS3_RECORD_FIELD_PASSWORD) && + (len > PWS3_MAX_PASSWORD_LEN)) || + (len > PWS3_MAX_FIELD_SIZE)) { + goto err; + } + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + text = malloc(len + 1); + if (text == NULL) { + err(1, NULL); + } + } else { + text = malloc(len + 1); + } + if (strunvis(text, p) == -1) { + goto err; + } + if (pws3_field_set_text(field, text) != 0) { + goto err; + } + break; + case PWS_DATA_TYPE_TIME: + p = strptime(p, TIME_FORMAT, &tm); + if ((p == NULL) || (*p != '\0')) { + goto err; + } + tm.tm_isdst = -1; + pws3_field_set_time(field, mktime(&tm)); + break; + case PWS_DATA_TYPE_UINT8: + if (len != 2) { + goto err; + } + if (parse_hex_byte(p, &u8) != 0) { + goto err; + } + pws3_field_set_uint8(field, u8); + break; + case PWS_DATA_TYPE_UINT16: + if (len != 4) { + goto err; + } + for (i = 0; i < 2; i++) { + if (parse_hex_byte(p, &((unsigned char *)&u16)[i]) != + 0) { + goto err; + } + p += 2; + } + pws3_field_set_uint16(field, be16toh(u16)); + break; + case PWS_DATA_TYPE_UINT32: + if (len != 8) { + goto err; + } + for (i = 0; i < 4; i++) { + if (parse_hex_byte(p, &((unsigned char *)&u32)[i]) != + 0) { + goto err; + } + p += 2; + } + pws3_field_set_uint16(field, be32toh(u32)); + break; + case PWS_DATA_TYPE_BYTES: + bytes = malloc(len / 2); + if (bytes == NULL) { + err(1, NULL); + } + for (i = 0; (*p != '\0') && (i < PWS3_MAX_FIELD_SIZE); i++) { + if (parse_hex_byte(p, &bytes[i]) != 0) { + goto err; + } + p += 2; + + while (*p == ' ') { p++; } + } + + if (*p != '\0') { + goto err; + } + + if (pws3_field_set_bytes(field, bytes, i) != 0) { + goto err; + } + } + + free(bytes); + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + free(text); + } else { + free(text); + } + + return (field); +err: + free(bytes); + if (field_type == PWS3_RECORD_FIELD_PASSWORD) { + free(text); + } else { + free(text); + } + pws3_field_destroy(field); + + return (NULL); +} + +static int +dump_read(struct pws3_file *file, FILE *fp) +{ + int retval = -1; + ssize_t line_len; + char *line = NULL; + size_t line_size = 0; + size_t line_no = 0; + int state = INITIAL; + struct pws3_field *header_field = NULL; + struct pws3_record *record = NULL; + struct pws3_field *record_field = NULL; + struct pws3_field *field_uuid; + + errno = 0; + while ((line_len = getline(&line, &line_size, fp)) != -1) { + line_no++; + + /* skip empty lines and comments */ + if (line_len <= 1 || (line[0] == '#')) { + continue; + } + + /* remove trailing newline */ + if (line[line_len - 1] == '\n') { + line[line_len - 1] = '\0'; + } + + switch (state) { + case INITIAL: + if (strcasecmp(line, "HEADER") == 0) { + state = IN_HEADER; + } else { + warnx("syntax error in line %zu", line_no); + goto out; + } + break; + case IN_HEADER: + if (strncasecmp(line, "RECORD", + strlen("RECORD")) == 0) { + state = IN_RECORD; + } else { + header_field = header_field_dump_parse(line); + if (header_field == NULL) { + warnx("syntax error in line %zu", + line_no); + goto out; + } + pws3_file_set_header_field(file, header_field); + header_field = NULL; + } + break; + case IN_RECORD: + if (strncasecmp(line, "RECORD", + strlen("RECORD")) == 0) { + if (record == NULL) { + warnx("syntax error in line %zu", + line_no); + goto out; + } + + /* check for mandatory UUID field */ + if (((field_uuid = pws3_record_get_field(record, + PWS3_RECORD_FIELD_UUID)) == NULL) || + (pws3_field_get_uuid(field_uuid) == + NULL)) { + warnx("record ending on line %zu is " + "missing UUID field", line_no); + goto out; + } + pws3_file_insert_record(file, record); + record = NULL; + } else { + if (record == NULL) { + record = pws3_record_create(); + if (record == NULL) { + err(1, NULL); + } + } + + record_field = record_field_dump_parse(line); + if (record_field == NULL) { + warnx("syntax error in line %zu", + line_no); + goto out; + } + pws3_record_set_field(record, record_field); + record_field = NULL; + } + } + errno = 0; + } + if (errno != 0) { + warn("failed to read from input file"); + goto out; + } + if (record != NULL) { + /* check for mandatory UUID field */ + if (((field_uuid = pws3_record_get_field(record, + PWS3_RECORD_FIELD_UUID)) == NULL) || + (pws3_field_get_uuid(field_uuid) == NULL)) { + warnx("record ending on line %zu is missing UUID " + "field", line_no); + goto out; + } + pws3_file_insert_record(file, record); + record = NULL; + } + + retval = 0; + +out: + pws3_field_destroy(header_field); + pws3_field_destroy(record_field); + pws3_record_destroy(record); + free(line); + + return (retval); +} + +static int +dump_write(struct pws3_file *file, FILE *fp) +{ + size_t i; + struct pws3_field *header_field; + struct pws3_record *record; + size_t n; + struct pws3_field *record_field; + + if (fprintf(fp, "# Passwordsafe v3 database dump\nHeader\n") < 0) { + warn("failed to write to output file"); + return (-1); + } + for (i = 0x00; i < 0xff; i++) { + header_field = pws3_file_get_header_field(file, i); + if (header_field != NULL) { + header_field_dump_write(header_field, fp); + } + + while ((i == PWS3_HEADER_FIELD_EMPTY_GROUPS) && + (header_field != NULL) && + (header_field = pws3_file_next_empty_group(file, + header_field)) != NULL) { + header_field_dump_write(header_field, fp); + } + } + + for (record = pws3_file_first_record(file), n = 1; record != NULL; + record = pws3_file_next_record(file, record), n++) { + if (fprintf(fp, "Record %zu\n", n) < 0) { + warn("failed to write to output file"); + return (-1); + } + + for (i = 0x00; i <= 0xff; i++) { + record_field = pws3_record_get_field(record, i); + if (record_field != NULL) { + record_field_dump_write(record_field, fp); + } + } + } + + return (0); +} + +void +usage(void) +{ + fprintf(stderr, "usage: pwsdump [-f pws3 | dump] [-o filename] " + "[-p password_file] [-P password_fd] [-t pws3 | dump] " + "input_file\n" + " pwsdump -f dump [-o filename] [-t pws3 | dump]\n"); +} + +int +main(int argc, char *argv[]) +{ + int status = EXIT_FAILURE; + int c; + int errflag = 0; + int from_format = FORMAT_PWS3; + int to_format = FORMAT_DUMP; + const char *in_filename = NULL; + const char *out_filename = NULL; + const char *new_password_filename = NULL; + const char *password_filename = NULL; + int fd_value = -1; + char *p; + int fd_new_password = -1; + FILE * fp_new_password = NULL; + int fd_password = -1; + FILE * fp_password = NULL; + struct pws3_file *file = NULL; + FILE *fp_in = stdin; + char *password = NULL; + ssize_t password_len; + size_t password_size = PWS3_MAX_PASSWORD_LEN + 1; + char *new_password = NULL; + ssize_t new_password_len; + size_t new_password_size = PWS3_MAX_PASSWORD_LEN + 1; + char *confirm_password = NULL; + FILE *fp_out = stdout; + struct stat statbuf_in; + struct stat statbuf_out; + int need_tmpfile = 0; + int fd_tmp = -1; + char *out_filename_tmp = NULL; + char *out_dir = NULL; + char *tmp_filename = NULL; + int len; + mode_t old_mode; + + setprogname(argv[0]); + + if (pws_init() != 0) { + goto out; + } + + /* timestamps are processed as UTC */ + if (setenv("TZ", "", 1) != 0) { + goto out; + } + tzset(); + + while (!errflag && ((c = getopt(argc, argv, "f:n:N:o:p:P:t:")) != -1)) { + switch (c) { + case 'f': + if (strcmp(optarg, "pws3") == 0) { + from_format = FORMAT_PWS3; + } else if (strcmp(optarg, "dump") == 0) { + from_format = FORMAT_DUMP; + } else { + errflag = 1; + } + break; + case 'n': + fd_new_password = -1; + new_password_filename = optarg; + break; + case 'N': + new_password_filename = NULL; + errno = 0; + fd_value = strtol(optarg, &p, 10); + if ((errno == 0) && (p != optarg) && (*p == '\0') && + (fd_value >= 0) && (fd_value < INT_MAX)) { + fd_new_password = (int)fd_value; + } else { + errflag = 1; + } + break; + case 'o': + out_filename = optarg; + break; + case 'p': + fd_password = -1; + password_filename = optarg; + break; + case 'P': + password_filename = NULL; + errno = 0; + fd_value = strtol(optarg, &p, 10); + if ((errno == 0) && (p != optarg) && (*p == '\0') && + (fd_value >= 0) && + (fd_value < INT_MAX)) { + fd_password = (int)fd_value; + } else { + errflag = 1; + } + break; + case 't': + if (strcmp(optarg, "pws3") == 0) { + to_format = FORMAT_PWS3; + } else if (strcmp(optarg, "dump") == 0) { + to_format = FORMAT_DUMP; + } else { + errflag = 1; + } + break; + default: + errflag = 1; + } + } + + if (errflag || ((from_format == FORMAT_PWS3) && (argc != optind + 1)) || + (argc > optind + 1)) { + usage(); + status = EXIT_USAGE; + goto out; + } + + if (optind == argc - 1) { + in_filename = argv[optind]; + } + + if (fd_password != -1) { + fp_password = fdopen(fd_password, "r"); + if (fp_password == NULL) { + warn("invalid password fd %d", fd_password); + goto out; + } + fd_password = -1; + } else if (password_filename != NULL) { + fp_password = fopen(password_filename, "r"); + if (fp_password == NULL) { + warn("could not open password file"); + goto out; + } + } + + if (fd_new_password != -1) { + fp_new_password = fdopen(fd_new_password, "r"); + if (fp_new_password == NULL) { + warn("invalid password fd %d", fd_new_password); + goto out; + } + fd_new_password = -1; + } else if (new_password_filename != NULL) { + fp_new_password = fopen(new_password_filename, "r"); + if (fp_new_password == NULL) { + warn("could not open password file"); + goto out; + } + } + + if (in_filename != NULL) { + fp_in = fopen(in_filename, "r"); + if (fp_in == NULL) { + warn("could not open input file"); + goto out; + } + } + + if (out_filename != NULL) { + if (in_filename != NULL) { + if (fstat(fileno(fp_in), &statbuf_in) == -1) { + warn("could not stat input file"); + status = EXIT_FAILURE; + goto out; + } + if (stat(out_filename, &statbuf_out) == -1) { + if (errno != ENOENT) { + warn("could not stat output file"); + status = 1; + goto out; + } + } else if ((statbuf_in.st_ino == statbuf_out.st_ino) && + (statbuf_in.st_dev == statbuf_out.st_dev)) { + need_tmpfile = 1; + } + } + + if (need_tmpfile) { + out_filename_tmp = strdup(out_filename); + if (out_filename_tmp == NULL) { + err(1, NULL); + } + out_dir = dirname(out_filename_tmp); + len = snprintf(NULL, 0, "%s/pwsdumpXXXXXX", out_dir); + if (len < 0) { + warn(NULL); + goto out; + } + tmp_filename = malloc((size_t)len + 1); + if (tmp_filename == NULL) { + err(1, NULL); + } + if (snprintf(tmp_filename, (size_t)len + 1, + "%s/pwsdumpXXXXXX", out_dir) != len) { + warn(NULL); + goto out; + } + old_mode = umask(077); + fd_tmp = mkstemp(tmp_filename); + umask(old_mode); + if (fd_tmp == -1) { + warn("could not create temporary file"); + goto out; + } + fp_out = fdopen(fd_tmp, "w"); + if (fp_out == NULL) { + warn("could not open temporary file"); + goto out; + } + fd_tmp = -1; + } else { + old_mode = umask(077); + fp_out = fopen(out_filename, "w"); + umask(old_mode); + if (fp_out == NULL) { + warn("could not open output file"); + goto out; + } + } + } + + file = pws3_file_create(); + if (file == NULL) { + err(1, NULL); + } + + if (from_format == FORMAT_PWS3) { + password = malloc(password_size); + if (password == NULL) { + err(1, NULL); + } + if (fp_password != NULL) { + errno = 0; + if (getline(&password, &password_size, + fp_password) == -1) { + if (errno != 0) { + warn("failed to read password"); + } else { + warnx("failed to read password"); + } + goto out; + } + password_len = strlen(password); + /* strip trailing newline */ + if ((password_len > 0) && + (password[password_len - 1] == '\n')) { + password[password_len - 1] = '\0'; + password_len--; + } + if (password_len == 0) { + warnx("invalid password"); + goto out; + } else if (password_len > PWS3_MAX_PASSWORD_LEN) { + warnx("password too long"); + goto out; + } + } else { + if (readpassphrase("Enter password: ", password, + password_size, RPP_ECHO_OFF | + RPP_REQUIRE_TTY) == NULL) { + err(1, NULL); + } + password_len = strlen(password); + } + if (password_len == 0) { + warnx("invalid password"); + goto out; + } + + if (pws3_file_read_stream(file, password, fp_in) != 0) { + warnx("%s", pws3_file_get_error_message(file)); + goto out; + } + } else { + if (dump_read(file, fp_in) != 0) { + goto out; + } + } + + if (to_format == FORMAT_PWS3) { + new_password = malloc(new_password_size); + if (new_password == NULL) { + err(1, NULL); + } + if (fp_new_password != NULL) { + errno = 0; + if (getline(&new_password, &new_password_size, + fp_new_password) == -1) { + if (errno != 0) { + warn("failed to read password"); + } else { + warnx("failed to read password"); + } + goto out; + } + new_password_len = strlen(new_password); + /* strip trailing newline */ + if ((new_password_len > 0) && + (new_password[new_password_len - 1] == '\n')) { + new_password[new_password_len - 1] = '\0'; + new_password_len--; + } + if (new_password_len == 0) { + warnx("invalid password"); + goto out; + } else if (new_password_len > PWS3_MAX_PASSWORD_LEN) { + warnx("password too long"); + goto out; + } + } else { + if (readpassphrase("Enter new password: ", new_password, + PWS3_MAX_PASSWORD_LEN + 1, RPP_ECHO_OFF | + RPP_REQUIRE_TTY) == NULL) { + err(1, NULL); + } + if (strlen(new_password) == 0) { + warnx("invalid password"); + goto out; + } + + confirm_password = malloc(PWS3_MAX_PASSWORD_LEN + 1); + if (confirm_password == NULL) { + err(1, NULL); + } + if (readpassphrase("Confirm new password: ", + confirm_password, PWS3_MAX_PASSWORD_LEN + 1, + RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { + err(1, NULL); + } + if (strcmp(new_password, confirm_password) != 0) { + warnx("password mismatch"); + goto out; + } + } + + if (pws3_file_write_stream(file, new_password, 10000, fp_out) != + 0) { + goto out; + } + } else { + if (dump_write(file, fp_out) != 0) { + goto out; + } + if (fflush(fp_out) != 0) { + warn("failed to flush output file"); + goto out; + } + } + + status = EXIT_SUCCESS; + +out: + if (fd_new_password != -1) { + close(fd_new_password); + } + + if (fp_new_password != NULL) { + fclose(fp_new_password); + } + + if (fd_password != -1) { + close(fd_password); + } + + if (fp_password != NULL) { + fclose(fp_password); + } + + if ((fp_in != NULL) && (fp_in != stdin)) { + fclose(fp_in); + } + + if (fd_tmp != -1) { + close(fd_tmp); + } + + if ((fp_out != NULL) && (fp_out != stdout)) { + fclose(fp_out); + if (status == EXIT_SUCCESS) { + if (need_tmpfile) { + if (rename(tmp_filename, out_filename) == -1) { + warn("could not create output file"); + status = EXIT_FAILURE; + unlink(tmp_filename); + } + } + } else { + if (need_tmpfile) { + unlink(tmp_filename); + } else { + unlink(out_filename); + } + } + } + + pws3_file_destroy(file); + free(out_filename_tmp); + free(tmp_filename); + free(confirm_password); + free(new_password); + free(password); + + pws_finalize(); + + exit(status); +}