When trying to save when exiting interactive mode, loop over the save
[libpwmd.git] / src / pwmc.c
blob75cb34a87c2199c154555b764e2395a4feb763d9
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2007-2010 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <libpwmd.h>
30 #include <assuan.h>
31 #include <sys/select.h>
32 #include <fcntl.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <libgen.h>
37 #ifdef HAVE_LOCALE_H
38 #include <locale.h>
39 #endif
41 #ifdef HAVE_GETOPT_LONG
42 #ifdef HAVE_GETOPT_H
43 #include <getopt.h>
44 #endif
45 #else
46 #include "getopt_long.h"
47 #endif
49 #ifdef HAVE_LIBREADLINE
50 # if defined(HAVE_READLINE_READLINE_H)
51 # include <readline/readline.h>
52 # elif defined(HAVE_READLINE_H)
53 # include <readline.h>
54 # endif /* !defined(HAVE_READLINE_H) */
55 static int interactive_error;
56 static int interactive;
57 #endif /* HAVE_LIBREADLINE */
59 #ifdef HAVE_READLINE_HISTORY
60 # if defined(HAVE_READLINE_HISTORY_H)
61 # include <readline/history.h>
62 # elif defined(HAVE_HISTORY_H)
63 # include <history.h>
64 # endif
65 #endif /* HAVE_READLINE_HISTORY */
67 #include "gettext.h"
68 #define N_(msgid) gettext(msgid)
70 #include "mem.h"
72 #define DEFAULT_PORT 22
74 static pwm_t *pwm;
75 static char *filename;
76 static int save;
77 static int local_pin;
78 static int force_save;
79 static char *cipher;
80 static long iter;
81 static char *password;
82 static char *keyfile;
83 #ifdef DEBUG
84 static int method;
85 #endif
86 #ifdef WITH_TCP
87 char *host = NULL;
88 #endif
90 struct inquire_s {
91 int fd;
92 char *line;
93 size_t len;
96 static gpg_error_t finalize();
98 static void show_error(gpg_error_t error)
100 fprintf(stderr, "ERR %i %s\n", gpg_err_code(error), pwmd_strerror(error));
103 static void usage(const char *pn, int status)
105 fprintf(status == EXIT_FAILURE ? stderr : stdout, N_(
106 "Read a PWMD protocol command from standard input.\n\n"
107 "Usage: pwmc [options] [file]\n"
108 #ifdef DEBUG
109 " --debug <N>\n"
110 " pinentry method (0=pwmd, 1=libpwmd, 2=pwmd async, "
111 "3=libpwmd async)\n"
112 #endif
113 "\n"
114 #ifdef WITH_TCP
115 " --host, -h <hostname>\n"
116 " connect to the specified hostname\n"
117 "\n"
118 " --port <N>\n"
119 " alterate port (22)\n"
120 "\n"
121 " --user <username>\n"
122 " SSH username (default is the invoking user)\n"
123 "\n"
124 " --use-agent\n"
125 " use the SSH agent for authentication\n"
126 "\n"
127 " --identity, -i <filename>\n"
128 " SSH identity file (if not using the SSH agent)\n"
129 "\n"
130 " --known-hosts, -k <filename>\n"
131 " known hosts file (~/.ssh/known_hosts)\n"
132 "\n"
133 " --get-hostkey, -g\n"
134 " retrieve the remote SSH host key and exit\n"
135 "\n"
136 " --ipv4, -4\n"
137 " try connecting via IPv4 only\n"
138 "\n"
139 " --ipv6, -6\n"
140 " try connecting via IPv6 only\n"
141 "\n"
142 #endif
143 " --url <string>\n"
144 " a url string to parse (see below)\n"
145 "\n"
146 " --no-status\n"
147 " disable showing of status messages from the server\n"
148 "\n"
149 " --name, -n <string>\n"
150 " set the client name\n"
151 "\n"
152 " --socket <filename>\n"
153 " local socket to connect to (~/.pwmd/socket)\n"
154 "\n"
155 " --passphrase, -P <string>\n"
156 " passphrase to use (disables pinentry use)\n"
157 "\n"
158 " --key-file <filename>\n"
159 " obtain the passphrase from the specified filename\n"
160 "\n"
161 " --base64\n"
162 " the passphrase is base64 encoded\n"
163 "\n"
164 " --timeout <seconds>\n"
165 " pinentry timeout\n"
166 "\n"
167 " --tries <N>\n"
168 " number of pinentry tries before failing (3)\n"
169 "\n"
170 " --pinentry <path>\n"
171 " the full path to the pinentry binary (server default)\n"
172 "\n"
173 " --ttyname, -y <path>\n"
174 " tty that pinentry will use\n"
175 "\n"
176 " --ttytype, -t <string>\n"
177 " pinentry terminal type (default is $TERM)\n"
178 "\n"
179 " --display, -d\n"
180 " pinentry display (default is $DISPLAY)\n"
181 "\n"
182 " --lc-ctype <string>\n"
183 " locale setting for pinentry\n"
184 "\n"
185 " --lc-messages <string>\n"
186 " locale setting for pinentry\n"
187 "\n"
188 " --local-pinentry\n"
189 " force using a local pinentry\n"
190 "\n"
191 " --interactive\n"
192 " use a shell like interface to pwmd (allows more than one command)\n"
193 "\n"
194 " --output-fd <FD>\n"
195 " redirect command output to the specified file descriptor\n"
196 "\n"
197 " --inquire <COMMAND>\n"
198 " the specified command (with any options) uses a server inquire while\n"
199 " command data is read via the inquire file descriptor (stdin)\n"
200 "\n"
201 " --inquire-fd <FD>\n"
202 " read inquire data from the specified file descriptor (stdin)\n"
203 "\n"
204 " --inquire-line, -L <STRING>\n"
205 " the initial line to send (i.e., element path) before the inquire data\n"
206 "\n"
207 " --cipher <string>\n"
208 " the cipher to use when saving (see pwmd(1))\n"
209 "\n"
210 " --save, -S\n"
211 " send the SAVE command before exiting\n"
212 "\n"
213 " --force-save\n"
214 " like --save but always ask for a passphrase\n"
215 "\n"
216 " --iterations, -I <N>\n"
217 " encrypt with the specified number of iterations\n"
218 "\n"
219 " --version\n"
220 " --help\n"));
221 fprintf(status == EXIT_FAILURE ? stderr : stdout, N_(
222 "\n"
223 "A url string (specified with --url) may be in the form of:\n"
224 " file://[path/to/socket]\n"
225 #ifdef WITH_TCP
226 " ssh[46]://[username@]hostname[:port],identity[,known_hosts]\n"
227 " or\n"
228 " ssh[46]://[username@]hostname[:port][,,known_hosts] --use-agent\n"
229 #endif
231 exit(status);
234 static gpg_error_t inquire_cb(void *user, const char *cmd, gpg_error_t rc,
235 char **data, size_t *len)
237 struct inquire_s *inq = user;
239 *data = NULL;
240 *len = 0;
242 if (rc)
243 return rc;
245 if (interactive == 1) {
246 interactive++;
247 fprintf(stderr, N_(
248 "------------------------------------------------------------------------------\n"
249 "Press CTRL-D to end. Press twice on a non-empty line. Pressing ENTER after\n"
250 "each line during the inquire will send that line including the newline\n"
251 "character.\n"
252 "------------------------------------------------------------------------------\n"
256 /* The first part of the command data. */
257 if (inq->len) {
258 *data = inq->line;
259 *len = inq->len;
260 inq->len = 0;
261 return 0;
264 *len = read(inq->fd, inq->line, ASSUAN_LINELENGTH);
266 if (*len == -1)
267 return gpg_error_from_syserror();
269 if (*len)
270 *data = inq->line;
272 return *len ? 0 : GPG_ERR_EOF;
275 static int status_msg_cb(void *data, const char *line)
277 fprintf(stderr, "%s\n", line);
278 return 0;
281 static gpg_error_t process_cmd(pwm_t *pwm, char **result, int input, int once)
283 gpg_error_t rc;
284 pwmd_async_t s;
286 do {
287 int i, n;
288 fd_set rfds;
289 int nfds = 5;
290 pwmd_fd_t pfds[nfds];
291 struct timeval tv = {0, 50000};
293 FD_ZERO(&rfds);
294 rc = pwmd_get_fds(pwm, pfds, &nfds);
296 if (rc)
297 return rc;
299 if (!nfds) {
300 s = pwmd_process(pwm, &rc, result);
301 break;
304 for (i = 0, n = 0; i < nfds; i++) {
305 FD_SET(pfds[i].fd, &rfds);
306 n = pfds[i].fd > n ? pfds[i].fd : n;
309 if (input)
310 FD_SET(STDIN_FILENO, &rfds);
312 nfds = select(n+1, &rfds, NULL, NULL, &tv);
314 if (nfds == -1) {
315 rc = gpg_error_from_errno(errno);
316 return rc;
319 if (input && FD_ISSET(STDIN_FILENO, &rfds))
320 return 0;
322 s = pwmd_process(pwm, &rc, result);
324 if (once)
325 break;
326 } while (s == ASYNC_PROCESS);
328 return rc;
331 #ifdef WITH_TCP
332 static gpg_error_t knownhost_cb(void *data, const char *host, const char *key,
333 size_t len)
335 gpg_error_t rc;
336 char *buf = pwmd_strdup_printf(N_("Password Manager Daemon: %s\n\nWhile attempting an SSH connection to %s there was a problem verifying it's hostkey against the known and trusted hosts file because it's hostkey was not found.\n\nWould you like to treat this connection as trusted for this and future connections by adding %s's hostkey to the known hosts file?"), (char *)data, host, host);
338 rc = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TITLE, buf);
339 pwmd_free(buf);
341 if (rc)
342 return rc;
344 return pwmd_getpin(pwm, NULL, NULL, PWMD_PINENTRY_CONFIRM);
347 static int is_remote_url(const char *str)
349 if (strstr(str, "file://") || strstr(str, "local://"))
350 return 0;
352 return 1;
354 #endif
356 static char *escape(const char *str)
358 const char *p;
359 char *buf = pwmd_malloc(ASSUAN_LINELENGTH+1), *b = buf;
360 size_t len = 0;
362 for (p = str; *p; p++, len++) {
363 if (len == ASSUAN_LINELENGTH)
364 break;
366 if (*p == '\\') {
367 switch (*++p) {
368 case 't':
369 *b++ = '\t';
370 break;
371 case 'n':
372 *b++ = '\n';
373 break;
374 case 'v':
375 *b++ = '\v';
376 break;
377 case 'b':
378 *b++ = '\b';
379 break;
380 case 'f':
381 *b++ = '\f';
382 break;
383 case 'r':
384 *b++ = '\r';
385 break;
386 default:
387 *b++ = *p;
388 break;
391 if (!*p)
392 break;
394 continue;
397 *b++ = *p;
400 *b = 0;
401 return buf;
404 static gpg_error_t send_inquire(int fd, const char *command, const char *line)
406 struct inquire_s inq;
407 struct stat st;
408 gpg_error_t rc;
410 if (fstat(fd, &st) == -1)
411 return gpg_error_from_syserror();
413 memset(&inq, 0, sizeof(inq));
414 inq.fd = fd;
415 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
416 inq.len = 0;
418 if (!inq.line)
419 return GPG_ERR_ENOMEM;
421 if (line) {
422 char *s = escape(line);
424 strncpy(inq.line, s, ASSUAN_LINELENGTH-1);
425 inq.len = strlen(s);
426 pwmd_free(s);
429 rc = pwmd_setopt(pwm, PWMD_OPTION_INQUIRE_TOTAL,
430 st.st_size ? st.st_size+strlen(inq.line) : 0);
432 if (rc)
433 return rc;
435 rc = pwmd_inquire(pwm, command, inquire_cb, &inq);
436 pwmd_free(inq.line);
437 return rc;
440 #ifdef HAVE_LIBREADLINE
441 static int interactive_hook(void)
443 interactive_error = process_cmd(pwm, NULL, 0, 1);
445 if (interactive_error)
446 rl_event_hook = NULL;
448 return 0;
451 static gpg_error_t do_interactive()
453 gpg_error_t rc = process_cmd(pwm, NULL, 0, 1);
455 if (rc)
456 return rc;
458 fprintf(stderr, N_("WARNING: interactive mode doesn't use secure memory!\n"));
459 fprintf(stderr, N_(
460 "------------------------------------------------------------------------------\n"
461 "Type HELP for protocol command help (since pwmd 2.19). Commands which use a\n"
462 "server inquire need a non-protocol command to send the data. These commands\n"
463 "including any options are:\n"
464 "\n"
465 " INQUIRE <command> - to read from the terminal\n"
466 " INQUIRE_FILE <filename> <command> - to read the specified filename\n"
467 "\n"
468 "Elements are TAB delimited. Press CTRL-V then TAB to insert from the prompt.\n"
469 "------------------------------------------------------------------------------\n"
471 rl_initialize();
472 rl_event_hook = &interactive_hook;
473 rl_set_keyboard_input_timeout(100000);
475 for (;;) {
476 char *line;
477 char *result = NULL;
479 line = readline("pwm> ");
481 if (interactive_error)
482 return interactive_error;
484 if (!line) {
485 rc = finalize();
487 if (!rc)
488 return 0;
490 fprintf(stderr, "ERR %i: %s\n", rc, pwmd_strerror(rc));
491 continue;
493 else if (!*line) {
494 free(line);
495 continue;
498 #ifdef HAVE_READLINE_HISTORY
499 add_history(line);
500 #endif
502 /* INQUIRE <COMMAND [options ...]> */
503 if (!strncasecmp(line, "inquire ", 8))
504 rc = send_inquire(STDIN_FILENO, line+8, NULL);
505 /* INQUIRE_FILE <FILENAME> <COMMAND [options ...]> */
506 else if (!strncasecmp(line, "inquire_file ", 13)) {
507 char *p = strchr(line+13, ' ');
508 char *f = p ? line+13 : NULL;
509 int fd;
511 if (!f || !*f || !*p) {
512 fprintf(stderr, N_("Syntax error. Usage: INQUIRE_FILE <filename> <command>\n"));
513 free(line);
514 continue;
517 f[strlen(f) - strlen(p++)] = 0;
518 fd = open(f, O_RDONLY);
520 if (fd == -1) {
521 fprintf(stderr, "%s\n", strerror(errno));
522 free(line);
523 continue;
525 else {
526 rc = send_inquire(fd, p, NULL);
527 close(fd);
530 else
531 rc = pwmd_command(pwm, &result, line);
533 free(line);
535 if (rc)
536 fprintf(stderr, "ERR %i: %s\n", rc, pwmd_strerror(rc));
537 else if (result && *result)
538 printf("%s%s", result, result[strlen(result)-1] != '\n' ? "\n" : "");
540 if (result)
541 pwmd_free(result);
544 return 0;
546 #endif
548 static gpg_error_t finalize()
550 gpg_error_t rc = 0;
552 #ifdef HAVE_LIBREADLINE
553 if (!force_save && interactive && filename) {
554 char *p, buf[16];
556 fprintf(stderr, "\n");
558 do {
559 fprintf(stderr, N_("Do you want to save your changes? [y/n]: "));
560 p = fgets(buf, sizeof(buf), stdin);
561 } while (!p || (*p != 'n' && *p != 'y'));
563 if (*p == 'n')
564 return 0;
566 save = 1;
568 #endif
570 if (save && filename) {
571 fprintf(stderr, N_("Saving changes ...\n"));
573 if (iter != -2) {
574 rc = pwmd_setopt(pwm, PWMD_OPTION_ITERATIONS, iter);
576 if (rc)
577 return rc;
580 if (cipher) {
581 rc = pwmd_setopt(pwm, PWMD_OPTION_CIPHER, cipher);
583 if (rc)
584 return rc;
587 if (local_pin) {
588 char *p1;
589 again:
590 if (!force_save) {
591 rc = pwmd_command(pwm, NULL, "ISCACHED %s", filename);
593 if (rc && rc != GPG_ERR_NOT_FOUND &&
594 rc != GPG_ERR_ENOENT)
595 return rc;
596 else if (!rc)
597 goto do_save;
600 rc = pwmd_getpin(pwm, filename, &p1, PWMD_PINENTRY_SAVE);
602 if (rc)
603 return rc;
605 rc = pwmd_getpin(pwm, filename, &password,
606 PWMD_PINENTRY_SAVE_CONFIRM);
608 if (rc) {
609 pwmd_free(p1);
610 return rc;
613 if ((p1 || password) && ((!p1 && password) || (!password && p1) ||
614 strcmp(p1, password))) {
615 pwmd_free(p1);
616 pwmd_free(password);
617 password = NULL;
618 goto again;
621 if (p1)
622 pwmd_free(p1);
624 rc = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
626 if (password)
627 pwmd_free(password);
629 if (rc)
630 return rc;
633 if (force_save) {
634 rc = pwmd_command(pwm, NULL, "CLEARCACHE %s", filename);
636 if (rc)
637 return rc;
639 if (!local_pin) {
640 rc = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, NULL);
642 if (rc)
643 return rc;
647 do_save:
648 if (keyfile && !local_pin) {
649 struct inquire_s inq;
651 memset(&inq, 0, sizeof(inq));
652 inq.fd = open(keyfile, O_RDONLY);
654 if (inq.fd == -1) {
655 rc = gpg_error_from_syserror();
656 return rc;
659 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
661 if (!inq.line) {
662 rc = GPG_ERR_ENOMEM;
663 return rc;
666 rc = pwmd_save_inquire(pwm, inquire_cb, &inq);
667 pwmd_free(inq.line);
668 close(inq.fd);
670 else {
671 #ifdef DEBUG
672 switch (method) {
673 case 0:
674 rc = pwmd_save(pwm);
675 break;
676 case 1:
677 rc = pwmd_save2(pwm);
678 break;
679 case 2:
680 rc = pwmd_save_async(pwm);
681 break;
682 case 3:
683 rc = pwmd_save_async2(pwm);
684 break;
687 if (!rc && method >= 2)
688 rc = process_cmd(pwm, NULL, 0, 0);
690 #else
691 #ifdef WITH_TCP
692 if (host)
693 rc = pwmd_save2(pwm);
694 else
695 #endif
696 rc = pwmd_save(pwm);
697 #endif
701 return rc;
704 int main(int argc, char *argv[])
706 gpg_error_t error;
707 int opt;
708 int base64 = 0;
709 char *socketpath = NULL;
710 char command[ASSUAN_LINELENGTH], *p;
711 int ret = EXIT_SUCCESS;
712 char *result = NULL;
713 char *pinentry_path = NULL;
714 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
715 *lcmessages = NULL;
716 int outfd = STDOUT_FILENO;
717 FILE *outfp = stdout;
718 int inquirefd = STDIN_FILENO;
719 FILE *inquirefp = stdin;
720 int show_status = 1;
721 char *clientname = "pwmc";
722 char *inquire = NULL;
723 char *inquire_line = NULL;
724 int have_iter = 0;
725 int timeout = 0;
726 #ifdef WITH_TCP
727 char *host = NULL;
728 int port = DEFAULT_PORT;
729 char *username = NULL;
730 char *ident = NULL;
731 char *known_hosts = NULL;
732 int get = 0;
733 int prot = PWMD_IP_ANY;
734 int use_agent = 0;
735 #endif
736 int tries = 0;
737 int local_tries = 3;
738 int try = 0;
739 pwmd_socket_t s;
740 int lock_on_open = 1;
741 #ifdef DEBUG
742 fd_set rfds;
743 #endif
744 char *url_string = NULL;
745 /* The order is important. */
746 enum {
747 #ifdef DEBUG
748 OPT_DEBUG,
749 #endif
750 #ifdef WITH_TCP
751 OPT_HOST, OPT_PORT, OPT_IDENTITY, OPT_KNOWN_HOSTS, OPT_USER,
752 OPT_GET_HOSTKEY, OPT_IPV4, OPT_IPV6, OPT_USE_AGENT,
753 #endif
754 OPT_URL, OPT_LOCAL, OPT_FORCE_SAVE, OPT_TTYNAME, OPT_TTYTYPE,
755 OPT_DISPLAY, OPT_LC_CTYPE, OPT_LC_MESSAGES, OPT_TIMEOUT, OPT_TRIES,
756 OPT_PINENTRY, OPT_PASSPHRASE, OPT_KEYFILE, OPT_BASE64, OPT_SOCKET,
757 OPT_NOLOCK, OPT_SAVE, OPT_ITERATIONS, OPT_OUTPUT_FD, OPT_INQUIRE,
758 OPT_INQUIRE_FD, OPT_INQUIRE_LINE, OPT_NO_STATUS, OPT_NAME,
759 OPT_VERSION, OPT_HELP, OPT_CIPHER,
760 #ifdef HAVE_LIBREADLINE
761 OPT_INTERACTIVE,
762 #endif
764 const struct option long_opts[] = {
765 #ifdef DEBUG
766 { "debug", 1, 0, 0 },
767 #endif
768 #ifdef WITH_TCP
769 { "host", 1, 0, 'h' },
770 { "port", 1, 0, 'p' },
771 { "identity", 1, 0, 'i' },
772 { "known-hosts", 1, 0, 'k' },
773 { "user", 1, 0, 'u' },
774 { "get-hostkey", 0, 0, 'g' },
775 { "ipv4", 0, 0, '4' },
776 { "ipv6", 0, 0, '6' },
777 { "use-agent", 0, 0, 0 },
778 #endif
779 { "url", 1, 0, 0 },
780 { "local-pinentry", 0, 0 },
781 { "force-save", 0, 0 },
782 { "ttyname", 1, 0, 'y' },
783 { "ttytype", 1, 0, 't' },
784 { "display", 1, 0, 'd' },
785 { "lc-ctype", 1, 0, 0 },
786 { "lc-messages", 1, 0, 0 },
787 { "timeout", 1, 0, 0 },
788 { "tries", 1, 0, 0 },
789 { "pinentry", 1, 0, 0 },
790 { "passphrase", 1, 0, 'P' },
791 { "key-file", 1, 0, 0 },
792 { "base64", 0, 0, 0 },
793 { "socket", 1, 0, 0 },
794 { "no-lock", 0, 0, 0 },
795 { "save", 0, 0, 'S' },
796 { "iterations", 1, 0, 'I' },
797 { "output-fd", 1, 0, 0 },
798 { "inquire", 1, 0, 0 },
799 { "inquire-fd", 1, 0, 0 },
800 { "inquire-line", 1, 0, 'L' },
801 { "no-status", 0, 0, 0 },
802 { "name", 1, 0, 'n' },
803 { "version", 0, 0, 0 },
804 { "help", 0, 0, 0 },
805 { "cipher", 1, 0, 0 },
806 #ifdef HAVE_LIBREADLINE
807 { "interactive", 0, 0 },
808 #endif
809 { 0, 0, 0, 0}
811 #ifdef WITH_TCP
812 const char *optstring = "L:46h:p:i:k:u:gy:t:d:P:I:Sn:";
813 #else
814 const char *optstring = "L:y:t:d:P:I:Sn:";
815 #endif
816 int opt_index = 0;
818 #ifdef ENABLE_NLS
819 setlocale(LC_ALL, "");
820 bindtextdomain("libpwmd", LOCALEDIR);
821 #endif
823 #ifdef HAVE_LIBREADLINE
824 if (!strcmp(basename(argv[0]), "pwmsh"))
825 interactive = 1;
826 #endif
828 iter = -2;
830 while ((opt = getopt_long(argc, argv, optstring, long_opts, &opt_index)) != -1) {
831 switch (opt) {
832 /* Handle long options without a short option part. */
833 case 0:
834 switch (opt_index) {
835 #ifdef DEBUG
836 case OPT_DEBUG:
837 method = atoi(optarg);
839 if (method > 3)
840 method = 3;
841 break;
842 #endif
843 #ifdef WITH_TCP
844 case OPT_USE_AGENT:
845 use_agent = 1;
846 break;
847 #endif
848 case OPT_KEYFILE:
849 keyfile = optarg;
850 break;
851 case OPT_BASE64:
852 base64 = 1;
853 break;
854 case OPT_NOLOCK:
855 lock_on_open = 0;
856 break;
857 case OPT_URL:
858 url_string = optarg;
859 break;
860 case OPT_LOCAL:
861 local_pin = 1;
862 break;
863 case OPT_FORCE_SAVE:
864 save = force_save = 1;
865 break;
866 case OPT_LC_CTYPE:
867 lcctype = pwmd_strdup(optarg);
868 break;
869 case OPT_LC_MESSAGES:
870 lcmessages = pwmd_strdup(optarg);
871 break;
872 case OPT_TIMEOUT:
873 timeout = atoi(optarg);
874 break;
875 case OPT_TRIES:
876 tries = atoi(optarg);
877 local_tries = tries;
878 break;
879 case OPT_SOCKET:
880 socketpath = pwmd_strdup(optarg);
881 break;
882 case OPT_INQUIRE:
883 inquire = optarg;
884 break;
885 case OPT_INQUIRE_FD:
886 inquirefd = atoi(optarg);
887 inquirefp = fdopen(inquirefd, "r");
889 if (!inquirefp) {
890 pwmd_free(password);
891 err(EXIT_FAILURE, "%i", inquirefd);
893 break;
894 case OPT_OUTPUT_FD:
895 outfd = atoi(optarg);
896 outfp = fdopen(outfd, "w");
898 if (!outfp) {
899 pwmd_free(password);
900 err(EXIT_FAILURE, "%i", outfd);
902 break;
903 case OPT_NO_STATUS:
904 show_status = 0;
905 break;
906 case OPT_VERSION:
907 pwmd_free(password);
908 printf("%s (pwmc)\n%s\n\n"
909 "Compile-time features:\n"
910 #ifdef WITH_TCP
911 "+SSH "
912 #else
913 "-SSH "
914 #endif
915 #ifdef WITH_PINENTRY
916 "+PINENTRY "
917 #else
918 "-PINENTRY "
919 #endif
920 #ifdef WITH_QUALITY
921 "+CRACK "
922 #else
923 "-CRACK "
924 #endif
925 #ifdef MEM_DEBUG
926 "+MEM_DEBUG "
927 #else
928 "-MEM_DEBUG "
929 #endif
930 #ifdef HAVE_LIBREADLINE
931 "+INTERACTIVE "
932 #else
933 "-INTERACTIVE "
934 #endif
935 "\n"
936 , PACKAGE_STRING, PACKAGE_BUGREPORT);
937 exit(EXIT_SUCCESS);
938 case OPT_PINENTRY:
939 pinentry_path = optarg;
940 break;
941 case OPT_HELP:
942 usage(argv[0], EXIT_SUCCESS);
943 case OPT_CIPHER:
944 cipher = optarg;
945 break;
946 #ifdef HAVE_LIBREADLINE
947 case OPT_INTERACTIVE:
948 interactive = 1;
949 break;
950 #endif
951 default:
952 usage(argv[0], EXIT_FAILURE);
955 break;
956 #ifdef WITH_TCP
957 case '4':
958 prot = PWMD_IPV4;
959 break;
960 case '6':
961 prot = PWMD_IPV6;
962 break;
963 case 'h':
964 host = pwmd_strdup(optarg);
965 break;
966 case 'p':
967 port = atoi(optarg);
968 break;
969 case 'i':
970 ident = pwmd_strdup(optarg);
971 break;
972 case 'u':
973 username = pwmd_strdup(optarg);
974 break;
975 case 'k':
976 known_hosts = pwmd_strdup(optarg);
977 break;
978 case 'g':
979 get = 1;
980 break;
981 #endif
982 case 'L':
983 inquire_line = optarg;
984 break;
985 case 'y':
986 tty = optarg;
987 break;
988 case 't':
989 ttytype = optarg;
990 break;
991 case 'd':
992 display = optarg;
993 break;
994 case 'S':
995 save = 1;
996 break;
997 case 'I':
998 iter = strtol(optarg, NULL, 10);
999 have_iter = 1;
1000 break;
1001 case 'P':
1002 password = pwmd_strdup(optarg);
1003 memset(optarg, 0, strlen(optarg));
1004 break;
1005 case 'n':
1006 clientname = optarg;
1007 break;
1008 default:
1009 pwmd_free(password);
1010 usage(argv[0], EXIT_FAILURE);
1014 #ifdef HAVE_LIBREADLINE
1015 if (interactive && !isatty(STDIN_FILENO)) {
1016 pwmd_free(password);
1017 usage(argv[0], EXIT_FAILURE);
1019 #endif
1021 #ifdef DEBUG
1022 if (!url_string) {
1023 #endif
1024 #ifdef WITH_TCP
1025 if (host && !get && !ident) {
1026 pwmd_free(password);
1027 usage(argv[0], EXIT_FAILURE);
1030 if (get && !host) {
1031 pwmd_free(password);
1032 usage(argv[0], EXIT_FAILURE);
1034 #endif
1035 #ifdef DEBUG
1037 #endif
1039 filename = argv[optind];
1040 pwmd_init();
1041 pwm = pwmd_new(clientname);
1042 #ifdef DEBUG
1043 FD_ZERO(&rfds);
1044 #endif
1046 #ifdef WITH_TCP
1047 if (host || url_string) {
1048 #ifndef DEBUG
1049 local_pin = 1;
1050 #endif
1052 if (prot != PWMD_IP_ANY) {
1053 error = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, prot);
1055 if (error)
1056 goto done;
1059 error = pwmd_setopt(pwm, PWMD_OPTION_KNOWNHOST_CB, knownhost_cb);
1061 if (error)
1062 goto done;
1064 error = pwmd_setopt(pwm, PWMD_OPTION_KNOWNHOST_DATA, clientname);
1066 if (error)
1067 goto done;
1069 error = pwmd_setopt(pwm, PWMD_OPTION_SSH_AGENT, use_agent);
1071 if (error)
1072 goto done;
1074 fprintf(stderr, N_("Connecting ...\n"));
1076 #ifdef DEBUG
1077 if (method >= 2) {
1078 if (get) {
1079 char *hostkey;
1081 error = pwmd_get_hostkey_async(pwm, host, port);
1083 if (error)
1084 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
1086 error = process_cmd(pwm, &hostkey, 0, 0);
1088 if (error)
1089 goto done;
1091 printf("%s", hostkey);
1092 pwmd_free(hostkey);
1093 pwmd_free(password);
1094 pwmd_close(pwm);
1095 exit(EXIT_SUCCESS);
1098 if (url_string)
1099 error = pwmd_connect_url_async(pwm, url_string);
1100 else
1101 error = pwmd_ssh_connect_async(pwm, host, port, ident, username,
1102 known_hosts);
1104 if (error)
1105 goto done;
1107 error = process_cmd(pwm, NULL, 0, 0);
1109 if (error)
1110 goto done;
1112 else {
1113 #endif
1114 if (get) {
1115 char *hostkey;
1117 error = pwmd_get_hostkey(pwm, host, port, &hostkey);
1119 if (error)
1120 goto done;
1122 printf("%s", hostkey);
1123 pwmd_free(hostkey);
1124 pwmd_free(password);
1125 pwmd_close(pwm);
1126 exit(EXIT_SUCCESS);
1129 if (url_string)
1130 error = pwmd_connect_url(pwm, url_string);
1131 else
1132 error = pwmd_ssh_connect(pwm, host, port, ident, username, known_hosts);
1134 if (error)
1135 goto done;
1136 #ifdef DEBUG
1138 #endif
1140 else {
1141 #endif
1142 if (url_string)
1143 error = pwmd_connect_url(pwm, url_string);
1144 else
1145 error = pwmd_connect(pwm, socketpath);
1147 if (error)
1148 goto done;
1149 #ifdef WITH_TCP
1151 #endif
1153 fprintf(stderr, N_("Connected.\n"));
1154 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TITLE, NULL);
1155 error = pwmd_socket_type(pwm, &s);
1157 if (error)
1158 goto done;
1160 #ifndef DEBUG
1161 if (s == PWMD_SOCKET_SSH && force_save && !local_pin) {
1162 error = GPG_ERR_WRONG_KEY_USAGE;
1163 goto done;
1165 #endif
1167 if (have_iter) {
1168 error = pwmd_command(pwm, &result, "VERSION");
1170 if (error)
1171 goto done;
1173 pwmd_free(result);
1175 if (iter < 0) {
1176 pwmd_free(password);
1177 pwmd_close(pwm);
1178 usage(argv[0], EXIT_FAILURE);
1182 if (filename && lock_on_open) {
1183 error = pwmd_setopt(pwm, PWMD_OPTION_LOCK_ON_OPEN, 1);
1185 if (error)
1186 goto done;
1189 if (base64) {
1190 error = pwmd_setopt(pwm, PWMD_OPTION_BASE64, 1);
1192 if (error)
1193 goto done;
1196 if (timeout > 0) {
1197 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
1199 if (error)
1200 goto done;
1203 if (local_pin && filename && !keyfile) {
1204 error = pwmd_command(pwm, NULL, "ISCACHED %s", filename);
1206 if (error == GPG_ERR_NOT_FOUND) {
1207 local_password:
1208 if (try++ == local_tries)
1209 goto done;
1211 if (password)
1212 pwmd_free(password);
1214 password = NULL;
1216 if (try > 1)
1217 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, 0);
1219 error = pwmd_getpin(pwm, filename, &password,
1220 try > 1 ? PWMD_PINENTRY_OPEN_FAILED : PWMD_PINENTRY_OPEN);
1222 if (error)
1223 goto done;
1225 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
1227 if (error)
1228 goto done;
1230 if (try > 1)
1231 goto do_open;
1233 else if (error && error != GPG_ERR_ENOENT)
1234 goto done;
1237 if (password) {
1238 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
1240 if (error)
1241 goto done;
1243 else {
1244 if (pinentry_path) {
1245 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
1247 if (error)
1248 goto done;
1251 if (display) {
1252 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
1254 if (error)
1255 goto done;
1258 if (tty) {
1259 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
1261 if (error)
1262 goto done;
1265 if (ttytype) {
1266 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
1268 if (error)
1269 goto done;
1272 if (lcctype) {
1273 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
1275 if (error)
1276 goto done;
1279 if (lcmessages) {
1280 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
1281 lcmessages);
1283 if (error)
1284 goto done;
1287 if (tries > 0) {
1288 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
1290 if (error)
1291 goto done;
1295 if (show_status) {
1296 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_CB, status_msg_cb);
1298 if (error)
1299 goto done;
1302 do_open:
1303 if (filename) {
1304 fprintf(stderr, N_("Opening data file \"%s\" ...\n"), filename);
1306 #ifdef WITH_TCP
1307 if (keyfile && (!local_pin || host || is_remote_url(url_string))) {
1308 struct inquire_s inq;
1310 memset(&inq, 0, sizeof(inq));
1311 inq.fd = open(keyfile, O_RDONLY);
1313 if (inq.fd == -1) {
1314 error = gpg_error_from_syserror();
1315 goto done;
1318 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
1320 if (!inq.line) {
1321 error = GPG_ERR_ENOMEM;
1322 goto done;
1325 error = pwmd_open_inquire(pwm, filename, inquire_cb, &inq);
1326 pwmd_free(inq.line);
1327 close(inq.fd);
1329 if (error)
1330 goto done;
1332 goto read_command;
1334 #endif
1335 #ifdef DEBUG
1336 switch (method) {
1337 case 0:
1338 error = pwmd_open(pwm, filename);
1339 break;
1340 case 1:
1341 error = pwmd_open2(pwm, filename);
1342 break;
1343 case 2:
1344 error = pwmd_open_async(pwm, filename);
1346 break;
1347 case 3:
1348 error = pwmd_open_async2(pwm, filename);
1349 break;
1352 if (error && local_pin && error == GPG_ERR_INV_PASSPHRASE)
1353 goto local_password;
1355 if (error)
1356 goto done;
1358 if (method >= 2)
1359 error = process_cmd(pwm, &result, 0, 0);
1360 #else
1361 #ifdef WITH_TCP
1362 if (host)
1363 error = pwmd_open2(pwm, filename);
1364 else
1365 #endif
1366 error = pwmd_open(pwm, filename);
1368 if (error && local_pin && error == GPG_ERR_INV_PASSPHRASE)
1369 goto local_password;
1370 #endif
1372 if (error)
1373 goto done;
1376 #ifdef WITH_TCP
1377 read_command:
1378 #endif
1379 #ifdef HAVE_LIBREADLINE
1380 if (interactive) {
1381 if (!force_save)
1382 save = 0;
1384 error = do_interactive();
1385 goto do_exit;
1387 #endif
1389 if (inquire) {
1390 int fd = fileno(inquirefp);
1392 if (fd == -1) {
1393 error = gpg_error_from_syserror();
1394 goto done;
1397 error = send_inquire(fd, inquire, inquire_line);
1398 close(fd);
1399 goto done;
1402 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
1403 ssize_t n;
1405 for (;;) {
1406 error = process_cmd(pwm, NULL, 1, 0);
1408 if (error)
1409 goto done;
1411 n = read(STDIN_FILENO, command, sizeof(command));
1413 if (n == -1) {
1414 if (errno == EAGAIN)
1415 continue;
1417 error = gpg_error_from_errno(errno);
1418 goto done;
1420 else if (!n)
1421 goto done;
1423 p = command;
1424 command[n] = 0;
1425 break;
1428 if (!p || !*p)
1429 goto done;
1431 if (strcasecmp(p, "BYE") == 0)
1432 goto done;
1434 error = pwmd_command(pwm, &result, command);
1436 if (error)
1437 goto done;
1439 if (result) {
1440 fwrite(result, 1, strlen(result), outfp);
1441 fflush(outfp);
1442 pwmd_free(result);
1445 done:
1446 if (password)
1447 pwmd_free(password);
1449 password = NULL;
1451 if (!error)
1452 error = finalize();
1454 do_exit:
1455 memset(command, 0, sizeof(command));
1457 if (error) {
1458 show_error(error);
1459 ret = EXIT_FAILURE;
1462 pwmd_close(pwm);
1464 if (socketpath)
1465 pwmd_free(socketpath);
1467 exit(ret);