comparison 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
comparison
equal deleted inserted replaced
1:e1309515d111 2:97097b4b6bfb
1 /*
2 * Copyright (C) 2015 Guido Berhoerster <guido+libpws@berhoerster.name>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "compat.h"
25
26 #include <ctype.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <strings.h>
32 #include <stdio.h>
33 #include <inttypes.h>
34 #include <limits.h>
35 #include <time.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <libgen.h>
39 #include <sys/stat.h>
40 #ifdef HAVE_READPASSPHRASE_H
41 #include <readpassphrase.h>
42 #endif /* HAVE_READPASSPHRASE_H */
43 #ifdef HAVE_VIS_H
44 #include <vis.h>
45 #endif /* HAVE_VIS_H */
46 #ifdef HAVE_SYS_ENDIAN_H
47 #include <sys/endian.h>
48 #else
49 #ifdef HAVE_ENDIAN_H
50 #include <endian.h>
51 #endif /* HAVE_ENDIAN_H */
52 #endif /* HAVE_SYS_ENDIAN_H */
53 #ifdef HAVE_ERR_H
54 #include <err.h>
55 #endif /* HAVE_ERR_H */
56 #include <pws.h>
57
58 #define EXIT_USAGE 2
59 #define TIME_FORMAT "%Y-%m-%dT%TZ"
60 #define TIME_SIZE (4 + 1 + 2 + 1 + 2 + 1 + 8 + 1 + 1)
61
62 enum {
63 FORMAT_PWS3,
64 FORMAT_DUMP
65 };
66
67 enum {
68 INITIAL,
69 IN_HEADER,
70 IN_RECORD
71 };
72
73 static void
74 header_field_dump_write(struct pws3_field *field, FILE *fp) {
75 size_t i;
76 const char *text;
77 char *vis_text;
78 const unsigned char *bytes;
79 time_t time;
80 struct tm *tm;
81 char time_buf[TIME_SIZE];
82 size_t len;
83
84 fprintf(fp, "%02x:", pws3_field_get_type(field));
85 switch (pws3_field_get_data_type(field)) {
86 case PWS_DATA_TYPE_UUID:
87 bytes = pws3_field_get_uuid(field);
88 for (i = 0; i < 16; i++) {
89 fprintf(fp, "%02x", bytes[i]);
90 }
91 fprintf(fp, "\n");
92 break;
93 case PWS_DATA_TYPE_TEXT:
94 text = pws3_field_get_text(field);
95 text = (text != NULL) ? text : "";
96 vis_text = malloc(4 * strlen(text) + 1);
97 if (vis_text == NULL) {
98 err(1, NULL);
99 }
100 strvis(vis_text, text, VIS_TAB | VIS_NL | VIS_CSTYLE);
101 fprintf(fp, "%s\n", vis_text);
102 free(vis_text);
103 break;
104 case PWS_DATA_TYPE_TIME:
105 time = pws3_field_get_time(field);
106 tm = gmtime(&time);
107 strftime(time_buf, sizeof (time_buf), TIME_FORMAT, tm);
108 fprintf(fp, "%s\n", time_buf);
109 break;
110 case PWS_DATA_TYPE_UINT8:
111 fprintf(fp, "%02" PRIx8 "\n",
112 pws3_field_get_uint8(field));
113 break;
114 case PWS_DATA_TYPE_UINT16:
115 fprintf(fp, "%04" PRIx16 "\n",
116 pws3_field_get_uint16(field));
117 break;
118 case PWS_DATA_TYPE_UINT32:
119 fprintf(fp, "%08" PRIx32 "\n",
120 pws3_field_get_uint32(field));
121 break;
122 case PWS_DATA_TYPE_BYTES:
123 pws3_field_get_bytes(field, &bytes, &len);
124 for (i = 0; i < len; i++) {
125 fprintf(fp, "%s%02x", (i > 0) ? " " : "", bytes[i]);
126 }
127 fprintf(fp, "\n");
128 }
129 }
130
131 static void
132 record_field_dump_write(struct pws3_field *field, FILE *fp) {
133 uint8_t field_type;
134 size_t i;
135 const char *text;
136 char *vis_text;
137 const unsigned char *bytes;
138 time_t time;
139 struct tm *tm;
140 char time_buf[TIME_SIZE];
141 size_t len;
142
143 field_type = pws3_field_get_type(field);
144 fprintf(fp, "%02x:", field_type);
145 switch (pws3_field_get_data_type(field)) {
146 case PWS_DATA_TYPE_UUID:
147 bytes = pws3_field_get_uuid(field);
148 for (i = 0; i < 16; i++) {
149 fprintf(fp, "%02x", bytes[i]);
150 }
151 fprintf(fp, "\n");
152 break;
153 case PWS_DATA_TYPE_TEXT:
154 text = pws3_field_get_text(field);
155 text = (text != NULL) ? text : "";
156
157 if (field_type == PWS3_RECORD_FIELD_PASSWORD) {
158 vis_text = malloc(4 * strlen(text) + 1);
159 if (vis_text == NULL) {
160 err(1, NULL);
161 }
162 } else {
163 vis_text = malloc(4 * strlen(text) + 1);
164 if (vis_text == NULL) {
165 err(1, NULL);
166 }
167 }
168 strvis(vis_text, text, VIS_TAB | VIS_NL | VIS_CSTYLE);
169 fprintf(fp, "%s\n", vis_text);
170 if (field_type == PWS3_RECORD_FIELD_PASSWORD) {
171 free(vis_text);
172 } else {
173 free(vis_text);
174 }
175 break;
176 case PWS_DATA_TYPE_TIME:
177 time = pws3_field_get_time(field);
178 tm = gmtime(&time);
179 strftime(time_buf, sizeof (time_buf), TIME_FORMAT, tm);
180 fprintf(fp, "%s\n", time_buf);
181 break;
182 case PWS_DATA_TYPE_UINT8:
183 fprintf(fp, "%02" PRIx8 "\n",
184 pws3_field_get_uint8(field));
185 break;
186 case PWS_DATA_TYPE_UINT16:
187 fprintf(fp, "%04" PRIx16 "\n",
188 pws3_field_get_uint16(field));
189 break;
190 case PWS_DATA_TYPE_UINT32:
191 fprintf(fp, "%08" PRIx32 "\n",
192 pws3_field_get_uint32(field));
193 break;
194 case PWS_DATA_TYPE_BYTES:
195 pws3_field_get_bytes(field, &bytes, &len);
196 for (i = 0; i < len; i++) {
197 fprintf(fp, "%s%02x", (i > 0) ? " " : "", bytes[i]);
198 }
199 fprintf(fp, "\n");
200 }
201 }
202
203 static int
204 parse_hex_byte(const char *str, uint8_t *u8p)
205 {
206 size_t i;
207 uint8_t value;
208 uint8_t u8 = 0;
209
210 if (!(isascii(str[0]) && isxdigit(str[0])) ||
211 !(isascii(str[1]) && isxdigit(str[1]))) {
212 return (-1);
213 }
214
215 for (i = 0; i < 2; i++) {
216 if (str[i] >= '0' && str[i] <= '9') {
217 value = (str[i] - '0');
218 } else if (str[i] >= 'A' && str[i] <= 'F') {
219 value = (10 + (str[i] - 'A'));
220 } else {
221 value = (10 + (str[i] - 'a'));
222 }
223 u8 += value << ((2 - 1 - i) * 4);
224 }
225
226 *u8p = u8;
227
228 return (0);
229 }
230
231 static struct pws3_field *
232 header_field_dump_parse(const char *line)
233 {
234 const char *p = line;
235 uint8_t field_type;
236 struct pws3_field *field = NULL;
237 size_t len;
238 size_t i;
239 unsigned char uuid[32];
240 char *text = NULL;
241 struct tm tm;
242 uint8_t u8;
243 uint16_t u16;
244 uint32_t u32;
245 unsigned char *bytes = NULL;
246
247 if (strlen(line) < 3) {
248 goto err;
249 }
250
251 if (parse_hex_byte(p, &field_type) != 0) {
252 goto err;
253 }
254 p += 2;
255 field = pws3_field_create(1, field_type);
256 if (field == NULL) {
257 err(1, NULL);
258 }
259
260 if (*p++ != ':') {
261 goto err;
262 }
263
264 len = strlen(p);
265
266 switch (pws3_field_get_data_type(field)) {
267 case PWS_DATA_TYPE_UUID:
268 for (i = 0; i < 16; i++) {
269 if (parse_hex_byte(p, &uuid[i]) != 0) {
270 goto err;
271 }
272 p += 2;
273
274 while (*p == ' ') { p++; }
275 }
276
277 if (*p != '\0') {
278 goto err;
279 }
280
281 if (pws3_field_set_uuid(field, uuid) != 0) {
282 goto err;
283 }
284 break;
285 case PWS_DATA_TYPE_TEXT:
286 if (len > PWS3_MAX_FIELD_SIZE) {
287 goto err;
288 }
289 text = malloc(len + 1);
290 if (text == NULL) {
291 err(1, NULL);
292 }
293 if (strunvis(text, p) == -1) {
294 goto err;
295 }
296 if (pws3_field_set_text(field, text) != 0) {
297 goto err;
298 }
299 break;
300 case PWS_DATA_TYPE_TIME:
301 p = strptime(p, TIME_FORMAT, &tm);
302 if ((p == NULL) || (*p != '\0')) {
303 goto err;
304 }
305 tm.tm_isdst = -1;
306 pws3_field_set_time(field, mktime(&tm));
307 break;
308 case PWS_DATA_TYPE_UINT8:
309 if (len != 2) {
310 goto err;
311 }
312 if (parse_hex_byte(p, &u8) != 0) {
313 goto err;
314 }
315 pws3_field_set_uint8(field, u8);
316 break;
317 case PWS_DATA_TYPE_UINT16:
318 if (len != 4) {
319 goto err;
320 }
321 for (i = 0; i < 2; i++) {
322 if (parse_hex_byte(p, &((unsigned char *)&u16)[i]) !=
323 0) {
324 goto err;
325 }
326 p += 2;
327 }
328 pws3_field_set_uint16(field, be16toh(u16));
329 break;
330 case PWS_DATA_TYPE_UINT32:
331 if (len != 8) {
332 goto err;
333 }
334 for (i = 0; i < 4; i++) {
335 if (parse_hex_byte(p,
336 &((unsigned char *)&u32)[i]) != 0) {
337 goto err;
338 }
339 p += 2;
340 }
341 pws3_field_set_uint16(field, be32toh(u32));
342 break;
343 case PWS_DATA_TYPE_BYTES:
344 bytes = malloc(len / 2);
345 if (bytes == NULL) {
346 err(1, NULL);
347 }
348 for (i = 0; (*p != '\0') && (i < PWS3_MAX_FIELD_SIZE); i++) {
349 if (parse_hex_byte(p, &bytes[i]) != 0) {
350 goto err;
351 }
352 p += 2;
353
354 while (*p == ' ') { p++; }
355 }
356
357 if (*p != '\0') {
358 goto err;
359 }
360
361 if (pws3_field_set_bytes(field, bytes, i) != 0) {
362 goto err;
363 }
364 }
365
366 free(bytes);
367 free(text);
368
369 return (field);
370 err:
371 free(bytes);
372 free(text);
373 pws3_field_destroy(field);
374
375 return (NULL);
376 }
377
378 static struct pws3_field *
379 record_field_dump_parse(const char *line)
380 {
381 const char *p = line;
382 uint8_t field_type = 0xff;
383 struct pws3_field *field = NULL;
384 size_t len;
385 size_t i;
386 unsigned char uuid[32];
387 char *text = NULL;
388 struct tm tm;
389 uint8_t u8;
390 uint16_t u16;
391 uint32_t u32;
392 unsigned char *bytes = NULL;
393
394 if (strlen(line) < 3) {
395 goto err;
396 }
397
398 if (parse_hex_byte(p, &field_type) != 0) {
399 goto err;
400 }
401 p += 2;
402 field = pws3_field_create(0, field_type);
403 if (field == NULL) {
404 err(1, NULL);
405 }
406
407 if (*p++ != ':') {
408 goto err;
409 }
410
411 len = strlen(p);
412
413 switch (pws3_field_get_data_type(field)) {
414 case PWS_DATA_TYPE_UUID:
415 for (i = 0; i < 16; i++) {
416 if (parse_hex_byte(p, &uuid[i]) != 0) {
417 goto err;
418 }
419 p += 2;
420
421 while (*p == ' ') { p++; }
422 }
423
424 if (*p != '\0') {
425 goto err;
426 }
427
428 if (pws3_field_set_uuid(field, uuid) != 0) {
429 goto err;
430 }
431 break;
432 case PWS_DATA_TYPE_TEXT:
433 if (((field_type == PWS3_RECORD_FIELD_PASSWORD) &&
434 (len > PWS3_MAX_PASSWORD_LEN)) ||
435 (len > PWS3_MAX_FIELD_SIZE)) {
436 goto err;
437 }
438 if (field_type == PWS3_RECORD_FIELD_PASSWORD) {
439 text = malloc(len + 1);
440 if (text == NULL) {
441 err(1, NULL);
442 }
443 } else {
444 text = malloc(len + 1);
445 }
446 if (strunvis(text, p) == -1) {
447 goto err;
448 }
449 if (pws3_field_set_text(field, text) != 0) {
450 goto err;
451 }
452 break;
453 case PWS_DATA_TYPE_TIME:
454 p = strptime(p, TIME_FORMAT, &tm);
455 if ((p == NULL) || (*p != '\0')) {
456 goto err;
457 }
458 tm.tm_isdst = -1;
459 pws3_field_set_time(field, mktime(&tm));
460 break;
461 case PWS_DATA_TYPE_UINT8:
462 if (len != 2) {
463 goto err;
464 }
465 if (parse_hex_byte(p, &u8) != 0) {
466 goto err;
467 }
468 pws3_field_set_uint8(field, u8);
469 break;
470 case PWS_DATA_TYPE_UINT16:
471 if (len != 4) {
472 goto err;
473 }
474 for (i = 0; i < 2; i++) {
475 if (parse_hex_byte(p, &((unsigned char *)&u16)[i]) !=
476 0) {
477 goto err;
478 }
479 p += 2;
480 }
481 pws3_field_set_uint16(field, be16toh(u16));
482 break;
483 case PWS_DATA_TYPE_UINT32:
484 if (len != 8) {
485 goto err;
486 }
487 for (i = 0; i < 4; i++) {
488 if (parse_hex_byte(p, &((unsigned char *)&u32)[i]) !=
489 0) {
490 goto err;
491 }
492 p += 2;
493 }
494 pws3_field_set_uint16(field, be32toh(u32));
495 break;
496 case PWS_DATA_TYPE_BYTES:
497 bytes = malloc(len / 2);
498 if (bytes == NULL) {
499 err(1, NULL);
500 }
501 for (i = 0; (*p != '\0') && (i < PWS3_MAX_FIELD_SIZE); i++) {
502 if (parse_hex_byte(p, &bytes[i]) != 0) {
503 goto err;
504 }
505 p += 2;
506
507 while (*p == ' ') { p++; }
508 }
509
510 if (*p != '\0') {
511 goto err;
512 }
513
514 if (pws3_field_set_bytes(field, bytes, i) != 0) {
515 goto err;
516 }
517 }
518
519 free(bytes);
520 if (field_type == PWS3_RECORD_FIELD_PASSWORD) {
521 free(text);
522 } else {
523 free(text);
524 }
525
526 return (field);
527 err:
528 free(bytes);
529 if (field_type == PWS3_RECORD_FIELD_PASSWORD) {
530 free(text);
531 } else {
532 free(text);
533 }
534 pws3_field_destroy(field);
535
536 return (NULL);
537 }
538
539 static int
540 dump_read(struct pws3_file *file, FILE *fp)
541 {
542 int retval = -1;
543 ssize_t line_len;
544 char *line = NULL;
545 size_t line_size = 0;
546 size_t line_no = 0;
547 int state = INITIAL;
548 struct pws3_field *header_field = NULL;
549 struct pws3_record *record = NULL;
550 struct pws3_field *record_field = NULL;
551 struct pws3_field *field_uuid;
552
553 errno = 0;
554 while ((line_len = getline(&line, &line_size, fp)) != -1) {
555 line_no++;
556
557 /* skip empty lines and comments */
558 if (line_len <= 1 || (line[0] == '#')) {
559 continue;
560 }
561
562 /* remove trailing newline */
563 if (line[line_len - 1] == '\n') {
564 line[line_len - 1] = '\0';
565 }
566
567 switch (state) {
568 case INITIAL:
569 if (strcasecmp(line, "HEADER") == 0) {
570 state = IN_HEADER;
571 } else {
572 warnx("syntax error in line %zu", line_no);
573 goto out;
574 }
575 break;
576 case IN_HEADER:
577 if (strncasecmp(line, "RECORD",
578 strlen("RECORD")) == 0) {
579 state = IN_RECORD;
580 } else {
581 header_field = header_field_dump_parse(line);
582 if (header_field == NULL) {
583 warnx("syntax error in line %zu",
584 line_no);
585 goto out;
586 }
587 pws3_file_set_header_field(file, header_field);
588 header_field = NULL;
589 }
590 break;
591 case IN_RECORD:
592 if (strncasecmp(line, "RECORD",
593 strlen("RECORD")) == 0) {
594 if (record == NULL) {
595 warnx("syntax error in line %zu",
596 line_no);
597 goto out;
598 }
599
600 /* check for mandatory UUID field */
601 if (((field_uuid = pws3_record_get_field(record,
602 PWS3_RECORD_FIELD_UUID)) == NULL) ||
603 (pws3_field_get_uuid(field_uuid) ==
604 NULL)) {
605 warnx("record ending on line %zu is "
606 "missing UUID field", line_no);
607 goto out;
608 }
609 pws3_file_insert_record(file, record);
610 record = NULL;
611 } else {
612 if (record == NULL) {
613 record = pws3_record_create();
614 if (record == NULL) {
615 err(1, NULL);
616 }
617 }
618
619 record_field = record_field_dump_parse(line);
620 if (record_field == NULL) {
621 warnx("syntax error in line %zu",
622 line_no);
623 goto out;
624 }
625 pws3_record_set_field(record, record_field);
626 record_field = NULL;
627 }
628 }
629 errno = 0;
630 }
631 if (errno != 0) {
632 warn("failed to read from input file");
633 goto out;
634 }
635 if (record != NULL) {
636 /* check for mandatory UUID field */
637 if (((field_uuid = pws3_record_get_field(record,
638 PWS3_RECORD_FIELD_UUID)) == NULL) ||
639 (pws3_field_get_uuid(field_uuid) == NULL)) {
640 warnx("record ending on line %zu is missing UUID "
641 "field", line_no);
642 goto out;
643 }
644 pws3_file_insert_record(file, record);
645 record = NULL;
646 }
647
648 retval = 0;
649
650 out:
651 pws3_field_destroy(header_field);
652 pws3_field_destroy(record_field);
653 pws3_record_destroy(record);
654 free(line);
655
656 return (retval);
657 }
658
659 static int
660 dump_write(struct pws3_file *file, FILE *fp)
661 {
662 size_t i;
663 struct pws3_field *header_field;
664 struct pws3_record *record;
665 size_t n;
666 struct pws3_field *record_field;
667
668 if (fprintf(fp, "# Passwordsafe v3 database dump\nHeader\n") < 0) {
669 warn("failed to write to output file");
670 return (-1);
671 }
672 for (i = 0x00; i < 0xff; i++) {
673 header_field = pws3_file_get_header_field(file, i);
674 if (header_field != NULL) {
675 header_field_dump_write(header_field, fp);
676 }
677
678 while ((i == PWS3_HEADER_FIELD_EMPTY_GROUPS) &&
679 (header_field != NULL) &&
680 (header_field = pws3_file_next_empty_group(file,
681 header_field)) != NULL) {
682 header_field_dump_write(header_field, fp);
683 }
684 }
685
686 for (record = pws3_file_first_record(file), n = 1; record != NULL;
687 record = pws3_file_next_record(file, record), n++) {
688 if (fprintf(fp, "Record %zu\n", n) < 0) {
689 warn("failed to write to output file");
690 return (-1);
691 }
692
693 for (i = 0x00; i <= 0xff; i++) {
694 record_field = pws3_record_get_field(record, i);
695 if (record_field != NULL) {
696 record_field_dump_write(record_field, fp);
697 }
698 }
699 }
700
701 return (0);
702 }
703
704 void
705 usage(void)
706 {
707 fprintf(stderr, "usage: pwsdump [-f pws3 | dump] [-o filename] "
708 "[-p password_file] [-P password_fd] [-t pws3 | dump] "
709 "input_file\n"
710 " pwsdump -f dump [-o filename] [-t pws3 | dump]\n");
711 }
712
713 int
714 main(int argc, char *argv[])
715 {
716 int status = EXIT_FAILURE;
717 int c;
718 int errflag = 0;
719 int from_format = FORMAT_PWS3;
720 int to_format = FORMAT_DUMP;
721 const char *in_filename = NULL;
722 const char *out_filename = NULL;
723 const char *new_password_filename = NULL;
724 const char *password_filename = NULL;
725 int fd_value = -1;
726 char *p;
727 int fd_new_password = -1;
728 FILE * fp_new_password = NULL;
729 int fd_password = -1;
730 FILE * fp_password = NULL;
731 struct pws3_file *file = NULL;
732 FILE *fp_in = stdin;
733 char *password = NULL;
734 ssize_t password_len;
735 size_t password_size = PWS3_MAX_PASSWORD_LEN + 1;
736 char *new_password = NULL;
737 ssize_t new_password_len;
738 size_t new_password_size = PWS3_MAX_PASSWORD_LEN + 1;
739 char *confirm_password = NULL;
740 FILE *fp_out = stdout;
741 struct stat statbuf_in;
742 struct stat statbuf_out;
743 int need_tmpfile = 0;
744 int fd_tmp = -1;
745 char *out_filename_tmp = NULL;
746 char *out_dir = NULL;
747 char *tmp_filename = NULL;
748 int len;
749 mode_t old_mode;
750
751 setprogname(argv[0]);
752
753 if (pws_init() != 0) {
754 goto out;
755 }
756
757 /* timestamps are processed as UTC */
758 if (setenv("TZ", "", 1) != 0) {
759 goto out;
760 }
761 tzset();
762
763 while (!errflag && ((c = getopt(argc, argv, "f:n:N:o:p:P:t:")) != -1)) {
764 switch (c) {
765 case 'f':
766 if (strcmp(optarg, "pws3") == 0) {
767 from_format = FORMAT_PWS3;
768 } else if (strcmp(optarg, "dump") == 0) {
769 from_format = FORMAT_DUMP;
770 } else {
771 errflag = 1;
772 }
773 break;
774 case 'n':
775 fd_new_password = -1;
776 new_password_filename = optarg;
777 break;
778 case 'N':
779 new_password_filename = NULL;
780 errno = 0;
781 fd_value = strtol(optarg, &p, 10);
782 if ((errno == 0) && (p != optarg) && (*p == '\0') &&
783 (fd_value >= 0) && (fd_value < INT_MAX)) {
784 fd_new_password = (int)fd_value;
785 } else {
786 errflag = 1;
787 }
788 break;
789 case 'o':
790 out_filename = optarg;
791 break;
792 case 'p':
793 fd_password = -1;
794 password_filename = optarg;
795 break;
796 case 'P':
797 password_filename = NULL;
798 errno = 0;
799 fd_value = strtol(optarg, &p, 10);
800 if ((errno == 0) && (p != optarg) && (*p == '\0') &&
801 (fd_value >= 0) &&
802 (fd_value < INT_MAX)) {
803 fd_password = (int)fd_value;
804 } else {
805 errflag = 1;
806 }
807 break;
808 case 't':
809 if (strcmp(optarg, "pws3") == 0) {
810 to_format = FORMAT_PWS3;
811 } else if (strcmp(optarg, "dump") == 0) {
812 to_format = FORMAT_DUMP;
813 } else {
814 errflag = 1;
815 }
816 break;
817 default:
818 errflag = 1;
819 }
820 }
821
822 if (errflag || ((from_format == FORMAT_PWS3) && (argc != optind + 1)) ||
823 (argc > optind + 1)) {
824 usage();
825 status = EXIT_USAGE;
826 goto out;
827 }
828
829 if (optind == argc - 1) {
830 in_filename = argv[optind];
831 }
832
833 if (fd_password != -1) {
834 fp_password = fdopen(fd_password, "r");
835 if (fp_password == NULL) {
836 warn("invalid password fd %d", fd_password);
837 goto out;
838 }
839 fd_password = -1;
840 } else if (password_filename != NULL) {
841 fp_password = fopen(password_filename, "r");
842 if (fp_password == NULL) {
843 warn("could not open password file");
844 goto out;
845 }
846 }
847
848 if (fd_new_password != -1) {
849 fp_new_password = fdopen(fd_new_password, "r");
850 if (fp_new_password == NULL) {
851 warn("invalid password fd %d", fd_new_password);
852 goto out;
853 }
854 fd_new_password = -1;
855 } else if (new_password_filename != NULL) {
856 fp_new_password = fopen(new_password_filename, "r");
857 if (fp_new_password == NULL) {
858 warn("could not open password file");
859 goto out;
860 }
861 }
862
863 if (in_filename != NULL) {
864 fp_in = fopen(in_filename, "r");
865 if (fp_in == NULL) {
866 warn("could not open input file");
867 goto out;
868 }
869 }
870
871 if (out_filename != NULL) {
872 if (in_filename != NULL) {
873 if (fstat(fileno(fp_in), &statbuf_in) == -1) {
874 warn("could not stat input file");
875 status = EXIT_FAILURE;
876 goto out;
877 }
878 if (stat(out_filename, &statbuf_out) == -1) {
879 if (errno != ENOENT) {
880 warn("could not stat output file");
881 status = 1;
882 goto out;
883 }
884 } else if ((statbuf_in.st_ino == statbuf_out.st_ino) &&
885 (statbuf_in.st_dev == statbuf_out.st_dev)) {
886 need_tmpfile = 1;
887 }
888 }
889
890 if (need_tmpfile) {
891 out_filename_tmp = strdup(out_filename);
892 if (out_filename_tmp == NULL) {
893 err(1, NULL);
894 }
895 out_dir = dirname(out_filename_tmp);
896 len = snprintf(NULL, 0, "%s/pwsdumpXXXXXX", out_dir);
897 if (len < 0) {
898 warn(NULL);
899 goto out;
900 }
901 tmp_filename = malloc((size_t)len + 1);
902 if (tmp_filename == NULL) {
903 err(1, NULL);
904 }
905 if (snprintf(tmp_filename, (size_t)len + 1,
906 "%s/pwsdumpXXXXXX", out_dir) != len) {
907 warn(NULL);
908 goto out;
909 }
910 old_mode = umask(077);
911 fd_tmp = mkstemp(tmp_filename);
912 umask(old_mode);
913 if (fd_tmp == -1) {
914 warn("could not create temporary file");
915 goto out;
916 }
917 fp_out = fdopen(fd_tmp, "w");
918 if (fp_out == NULL) {
919 warn("could not open temporary file");
920 goto out;
921 }
922 fd_tmp = -1;
923 } else {
924 old_mode = umask(077);
925 fp_out = fopen(out_filename, "w");
926 umask(old_mode);
927 if (fp_out == NULL) {
928 warn("could not open output file");
929 goto out;
930 }
931 }
932 }
933
934 file = pws3_file_create();
935 if (file == NULL) {
936 err(1, NULL);
937 }
938
939 if (from_format == FORMAT_PWS3) {
940 password = malloc(password_size);
941 if (password == NULL) {
942 err(1, NULL);
943 }
944 if (fp_password != NULL) {
945 errno = 0;
946 if (getline(&password, &password_size,
947 fp_password) == -1) {
948 if (errno != 0) {
949 warn("failed to read password");
950 } else {
951 warnx("failed to read password");
952 }
953 goto out;
954 }
955 password_len = strlen(password);
956 /* strip trailing newline */
957 if ((password_len > 0) &&
958 (password[password_len - 1] == '\n')) {
959 password[password_len - 1] = '\0';
960 password_len--;
961 }
962 if (password_len == 0) {
963 warnx("invalid password");
964 goto out;
965 } else if (password_len > PWS3_MAX_PASSWORD_LEN) {
966 warnx("password too long");
967 goto out;
968 }
969 } else {
970 if (readpassphrase("Enter password: ", password,
971 password_size, RPP_ECHO_OFF |
972 RPP_REQUIRE_TTY) == NULL) {
973 err(1, NULL);
974 }
975 password_len = strlen(password);
976 }
977 if (password_len == 0) {
978 warnx("invalid password");
979 goto out;
980 }
981
982 if (pws3_file_read_stream(file, password, fp_in) != 0) {
983 warnx("%s", pws3_file_get_error_message(file));
984 goto out;
985 }
986 } else {
987 if (dump_read(file, fp_in) != 0) {
988 goto out;
989 }
990 }
991
992 if (to_format == FORMAT_PWS3) {
993 new_password = malloc(new_password_size);
994 if (new_password == NULL) {
995 err(1, NULL);
996 }
997 if (fp_new_password != NULL) {
998 errno = 0;
999 if (getline(&new_password, &new_password_size,
1000 fp_new_password) == -1) {
1001 if (errno != 0) {
1002 warn("failed to read password");
1003 } else {
1004 warnx("failed to read password");
1005 }
1006 goto out;
1007 }
1008 new_password_len = strlen(new_password);
1009 /* strip trailing newline */
1010 if ((new_password_len > 0) &&
1011 (new_password[new_password_len - 1] == '\n')) {
1012 new_password[new_password_len - 1] = '\0';
1013 new_password_len--;
1014 }
1015 if (new_password_len == 0) {
1016 warnx("invalid password");
1017 goto out;
1018 } else if (new_password_len > PWS3_MAX_PASSWORD_LEN) {
1019 warnx("password too long");
1020 goto out;
1021 }
1022 } else {
1023 if (readpassphrase("Enter new password: ", new_password,
1024 PWS3_MAX_PASSWORD_LEN + 1, RPP_ECHO_OFF |
1025 RPP_REQUIRE_TTY) == NULL) {
1026 err(1, NULL);
1027 }
1028 if (strlen(new_password) == 0) {
1029 warnx("invalid password");
1030 goto out;
1031 }
1032
1033 confirm_password = malloc(PWS3_MAX_PASSWORD_LEN + 1);
1034 if (confirm_password == NULL) {
1035 err(1, NULL);
1036 }
1037 if (readpassphrase("Confirm new password: ",
1038 confirm_password, PWS3_MAX_PASSWORD_LEN + 1,
1039 RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) {
1040 err(1, NULL);
1041 }
1042 if (strcmp(new_password, confirm_password) != 0) {
1043 warnx("password mismatch");
1044 goto out;
1045 }
1046 }
1047
1048 if (pws3_file_write_stream(file, new_password, 10000, fp_out) !=
1049 0) {
1050 goto out;
1051 }
1052 } else {
1053 if (dump_write(file, fp_out) != 0) {
1054 goto out;
1055 }
1056 if (fflush(fp_out) != 0) {
1057 warn("failed to flush output file");
1058 goto out;
1059 }
1060 }
1061
1062 status = EXIT_SUCCESS;
1063
1064 out:
1065 if (fd_new_password != -1) {
1066 close(fd_new_password);
1067 }
1068
1069 if (fp_new_password != NULL) {
1070 fclose(fp_new_password);
1071 }
1072
1073 if (fd_password != -1) {
1074 close(fd_password);
1075 }
1076
1077 if (fp_password != NULL) {
1078 fclose(fp_password);
1079 }
1080
1081 if ((fp_in != NULL) && (fp_in != stdin)) {
1082 fclose(fp_in);
1083 }
1084
1085 if (fd_tmp != -1) {
1086 close(fd_tmp);
1087 }
1088
1089 if ((fp_out != NULL) && (fp_out != stdout)) {
1090 fclose(fp_out);
1091 if (status == EXIT_SUCCESS) {
1092 if (need_tmpfile) {
1093 if (rename(tmp_filename, out_filename) == -1) {
1094 warn("could not create output file");
1095 status = EXIT_FAILURE;
1096 unlink(tmp_filename);
1097 }
1098 }
1099 } else {
1100 if (need_tmpfile) {
1101 unlink(tmp_filename);
1102 } else {
1103 unlink(out_filename);
1104 }
1105 }
1106 }
1107
1108 pws3_file_destroy(file);
1109 free(out_filename_tmp);
1110 free(tmp_filename);
1111 free(confirm_password);
1112 free(new_password);
1113 free(password);
1114
1115 pws_finalize();
1116
1117 exit(status);
1118 }