Mercurial > projects > libpws
view pwsdump.c @ 10:0feba0fea9d0
Allow local makefile overrides in local.mk
author | Guido Berhoerster <guido+libpws@berhoerster.name> |
---|---|
date | Wed, 31 Jul 2019 11:21:50 +0200 |
parents | 97097b4b6bfb |
children |
line wrap: on
line source
/* * 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); }