pwmc: show the inquire help text during each inquire.
[libpwmd.git] / src / pwmc.c
blob08e69cd353106a717ac8bbd7df40948e2f13efbd
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>
36 #include <termios.h>
37 #include <limits.h>
39 #ifdef HAVE_LOCALE_H
40 #include <locale.h>
41 #endif
43 #ifdef HAVE_GETOPT_LONG
44 #ifdef HAVE_GETOPT_H
45 #include <getopt.h>
46 #endif
47 #else
48 #include "getopt_long.h"
49 #endif
51 #ifdef HAVE_LIBREADLINE
52 # if defined(HAVE_READLINE_READLINE_H)
53 # include <readline/readline.h>
54 # elif defined(HAVE_READLINE_H)
55 # include <readline.h>
56 # endif /* !defined(HAVE_READLINE_H) */
57 static int interactive_error;
58 static int interactive;
59 #endif /* HAVE_LIBREADLINE */
61 #ifdef HAVE_READLINE_HISTORY
62 # if defined(HAVE_READLINE_HISTORY_H)
63 # include <readline/history.h>
64 # elif defined(HAVE_HISTORY_H)
65 # include <history.h>
66 # endif
67 #endif /* HAVE_READLINE_HISTORY */
69 #include "gettext.h"
70 #define N_(msgid) gettext(msgid)
72 #include "mem.h"
74 #define DEFAULT_PORT 22
76 static int in_keyfile;
77 static int no_pinentry;
78 static pwm_t *pwm;
79 static char *filename;
80 static int save;
81 static int local_pin;
82 static int force_save;
83 static char *cipher;
84 static long iter;
85 static char *password;
86 static char *keyfile;
87 #ifdef DEBUG
88 static int method;
89 #endif
90 #ifdef WITH_TCP
91 char *host = NULL;
92 #endif
94 struct inquire_s {
95 int fd;
96 char *line;
97 size_t len;
100 static gpg_error_t finalize();
102 static void show_error(gpg_error_t error)
104 fprintf(stderr, "ERR %i %s\n", gpg_err_code(error), pwmd_strerror(error));
107 static void usage(const char *pn, int status)
109 fprintf(status == EXIT_FAILURE ? stderr : stdout, N_(
110 "Read a PWMD protocol command from standard input.\n\n"
111 "Usage: pwmc [options] [file]\n"
112 #ifdef DEBUG
113 " --debug <N>\n"
114 " pinentry method (0=pwmd, 1=libpwmd, 2=pwmd async, "
115 "3=libpwmd async)\n"
116 #endif
117 "\n"
118 #ifdef WITH_TCP
119 " --host, -h <hostname>\n"
120 " connect to the specified hostname\n"
121 "\n"
122 " --port <N>\n"
123 " alterate port (22)\n"
124 "\n"
125 " --user <username>\n"
126 " SSH username (default is the invoking user)\n"
127 "\n"
128 " --use-agent\n"
129 " use the SSH agent for authentication\n"
130 "\n"
131 " --identity, -i <filename>\n"
132 " SSH identity file (if not using the SSH agent)\n"
133 "\n"
134 " --known-hosts, -k <filename>\n"
135 " known hosts file (~/.ssh/known_hosts)\n"
136 "\n"
137 " --get-hostkey, -g\n"
138 " retrieve the remote SSH host key and exit\n"
139 "\n"
140 " --ipv4, -4\n"
141 " try connecting via IPv4 only\n"
142 "\n"
143 " --ipv6, -6\n"
144 " try connecting via IPv6 only\n"
145 "\n"
146 #endif
147 " --url <string>\n"
148 " a url string to parse (see below)\n"
149 "\n"
150 " --no-status\n"
151 " disable showing of status messages from the server\n"
152 "\n"
153 " --name, -n <string>\n"
154 " set the client name\n"
155 "\n"
156 " --socket <filename>\n"
157 " local socket to connect to (~/.pwmd/socket)\n"
158 "\n"
159 " --passphrase, -P <string>\n"
160 " passphrase to use (disables pinentry use)\n"
161 "\n"
162 " --key-file <filename>\n"
163 " obtain the passphrase from the specified filename\n"
164 "\n"
165 " --base64\n"
166 " the passphrase is base64 encoded\n"
167 "\n"
168 " --timeout <seconds>\n"
169 " pinentry timeout\n"
170 "\n"
171 " --tries <N>\n"
172 " number of pinentry tries before failing (3)\n"
173 "\n"
174 " --no-pinentry\n"
175 " disable pinentry both remotely and locally\n"
176 "\n"
177 " --pinentry <path>\n"
178 " the full path to the pinentry binary (server default)\n"
179 "\n"
180 " --ttyname, -y <path>\n"
181 " tty that pinentry will use\n"
182 "\n"
183 " --ttytype, -t <string>\n"
184 " pinentry terminal type (default is $TERM)\n"
185 "\n"
186 " --display, -d\n"
187 " pinentry display (default is $DISPLAY)\n"
188 "\n"
189 " --lc-ctype <string>\n"
190 " locale setting for pinentry\n"
191 "\n"
192 " --lc-messages <string>\n"
193 " locale setting for pinentry\n"
194 "\n"
195 " --local-pinentry\n"
196 " force using a local pinentry\n"
197 "\n"
198 " --interactive\n"
199 " use a shell like interface to pwmd (allows more than one command)\n"
200 "\n"
201 " --output-fd <FD>\n"
202 " redirect command output to the specified file descriptor\n"
203 "\n"
204 " --inquire <COMMAND>\n"
205 " the specified command (with any options) uses a server inquire while\n"
206 " command data is read via the inquire file descriptor (stdin)\n"
207 "\n"
208 " --inquire-fd <FD>\n"
209 " read inquire data from the specified file descriptor (stdin)\n"
210 "\n"
211 " --inquire-line, -L <STRING>\n"
212 " the initial line to send (i.e., element path) before the inquire data\n"
213 "\n"
214 " --cipher <string>\n"
215 " the cipher to use when saving (see pwmd(1))\n"
216 "\n"
217 " --save, -S\n"
218 " send the SAVE command before exiting\n"
219 "\n"
220 " --force-save\n"
221 " like --save but always ask for a passphrase\n"
222 "\n"
223 " --iterations, -I <N>\n"
224 " encrypt with the specified number of iterations\n"
225 "\n"
226 " --version\n"
227 " --help\n"));
228 fprintf(status == EXIT_FAILURE ? stderr : stdout, N_(
229 "\n"
230 "A url string (specified with --url) may be in the form of:\n"
231 " file://[path/to/socket]\n"
232 #ifdef WITH_TCP
233 " ssh[46]://[username@]hostname[:port],identity[,known_hosts]\n"
234 " or\n"
235 " ssh[46]://[username@]hostname[:port][,,known_hosts] --use-agent\n"
236 #endif
238 exit(status);
241 static gpg_error_t inquire_cb(void *user, const char *cmd, gpg_error_t rc,
242 char **data, size_t *len)
244 struct inquire_s *inq = user;
246 *data = NULL;
247 *len = 0;
249 if (rc)
250 return rc;
252 if (!in_keyfile) {
253 fprintf(stderr, N_(
254 "------------------------------------------------------------------------------\n"
255 "Press CTRL-D to end. Press twice on a non-empty line. Pressing ENTER after\n"
256 "each line during the inquire will send that line including the newline\n"
257 "character.\n"
258 "------------------------------------------------------------------------------\n"
262 /* The first part of the command data. */
263 if (inq->len) {
264 *data = inq->line;
265 *len = inq->len;
266 inq->len = 0;
267 return 0;
270 *len = read(inq->fd, inq->line, ASSUAN_LINELENGTH);
272 if (*len == -1)
273 return gpg_error_from_syserror();
275 if (*len)
276 *data = inq->line;
278 return *len ? 0 : GPG_ERR_EOF;
281 static int status_msg_cb(void *data, const char *line)
283 char *p = strchr(line, ' ');
285 if (p && strchr(p, ' ') && *++p) {
286 char *p1 = strchr(p, ' ');
287 int a = atoi(p);
289 if (p1) {
290 int b = atoi(p1);
291 char l[64] = {0};
292 int t = a && b ? a*100/b : 0;
294 strncpy(l, line, strlen(line)-strlen(p)-1);
295 fprintf(stderr, "\r%s %i/%i %i%%%s", l, a, b, t, a == b ? "\n" : "");
296 fflush(stderr);
297 return 0;
301 fprintf(stderr, "%s\n", line);
302 fflush(stderr);
303 return 0;
306 static gpg_error_t process_cmd(pwm_t *pwm, char **result, int input, int once)
308 gpg_error_t rc;
309 pwmd_async_t s;
311 do {
312 int i, n;
313 fd_set rfds;
314 int nfds = 5;
315 pwmd_fd_t pfds[nfds];
316 struct timeval tv = {0, 50000};
318 FD_ZERO(&rfds);
319 rc = pwmd_get_fds(pwm, pfds, &nfds);
321 if (rc)
322 return rc;
324 if (!nfds) {
325 s = pwmd_process(pwm, &rc, result);
326 break;
329 for (i = 0, n = 0; i < nfds; i++) {
330 FD_SET(pfds[i].fd, &rfds);
331 n = pfds[i].fd > n ? pfds[i].fd : n;
334 if (input)
335 FD_SET(STDIN_FILENO, &rfds);
337 nfds = select(n+1, &rfds, NULL, NULL, &tv);
339 if (nfds == -1) {
340 rc = gpg_error_from_errno(errno);
341 return rc;
344 if (input && FD_ISSET(STDIN_FILENO, &rfds))
345 return 0;
347 s = pwmd_process(pwm, &rc, result);
349 if (once)
350 break;
351 } while (s == ASYNC_PROCESS);
353 return rc;
356 static gpg_error_t get_password(char **result, pwmd_pinentry_t w, int echo)
358 char buf[LINE_MAX] = {0}, *p;
359 struct termios told, tnew;
360 char *key = NULL;
362 *result = NULL;
364 if (!isatty(STDIN_FILENO)) {
365 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
366 return GPG_ERR_ENOTTY;
369 if (!echo) {
370 if (tcgetattr(STDIN_FILENO, &told) == -1)
371 return gpg_error_from_syserror();
373 memcpy(&tnew, &told, sizeof(struct termios));
374 tnew.c_lflag &= ~(ECHO);
375 tnew.c_lflag |= ICANON|ECHONL;
377 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
378 int n = errno;
380 tcsetattr(STDIN_FILENO, TCSANOW, &told);
381 return gpg_error_from_errno(n);
385 switch (w) {
386 case PWMD_PINENTRY_OPEN:
387 fprintf(stderr, N_("Password for %s: "), filename);
388 break;
389 case PWMD_PINENTRY_OPEN_FAILED:
390 fprintf(stderr, N_("Invalid password. Password for %s: "), filename);
391 break;
392 case PWMD_PINENTRY_SAVE:
393 fprintf(stderr, N_("New password for %s: "), filename);
394 break;
395 case PWMD_PINENTRY_SAVE_CONFIRM:
396 fprintf(stderr, N_("Confirm password: "));
397 break;
398 default:
399 break;
402 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
403 tcsetattr(STDIN_FILENO, TCSANOW, &told);
404 return 0;
407 if (!echo)
408 tcsetattr(STDIN_FILENO, TCSANOW, &told);
410 if (feof(stdin)) {
411 clearerr(stdin);
412 return GPG_ERR_CANCELED;
415 p[strlen(p) - 1] = 0;
417 if (buf[0]) {
418 key = pwmd_strdup_printf("%s", p);
419 memset(&buf, 0, sizeof(buf));
421 if (!key)
422 return GPG_ERR_ENOMEM;
425 *result = key;
426 return 0;
429 #ifdef WITH_TCP
430 static gpg_error_t knownhost_cb(void *data, const char *host, const char *key,
431 size_t len)
433 gpg_error_t rc;
434 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);
436 if (no_pinentry && !isatty(STDIN_FILENO)) {
437 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
438 pwmd_free(buf);
439 return GPG_ERR_ENOTTY;
441 else if (no_pinentry) {
442 for (char *p = buf, len = 0; *p; p++, len++) {
443 if (*p == '\n')
444 len = 0;
446 if (len == 78) {
447 char *t = p;
449 while (!isspace(*(--p)));
450 *p = '\n';
451 p = t;
452 len = 0;
456 fprintf(stderr, "%s\n\n", buf);
457 pwmd_free(buf);
459 do {
460 char *result;
462 fprintf(stderr, N_("Trust this connection [y/N]: "));
463 fflush(stderr);
464 rc = get_password(&result, PWMD_PINENTRY_CONFIRM, 1);
466 if (rc)
467 return rc;
469 if (!result || !*result || *result == 'n' || *result == 'N') {
470 if (result && *result)
471 pwmd_free(result);
473 return GPG_ERR_NOT_CONFIRMED;
476 if ((*result == 'y' || *result == 'Y') && *(result+1) == 0) {
477 pwmd_free(result);
478 return 0;
480 } while (1);
483 rc = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TITLE, buf);
484 pwmd_free(buf);
486 if (rc)
487 return rc;
489 return pwmd_getpin(pwm, NULL, NULL, PWMD_PINENTRY_CONFIRM);
492 static int is_remote_url(const char *str)
494 if (str && (strstr(str, "file://") || strstr(str, "local://")))
495 return 0;
497 return !str ? 0 : 1;
499 #endif
501 static char *escape(const char *str)
503 const char *p;
504 char *buf = pwmd_malloc(ASSUAN_LINELENGTH+1), *b = buf;
505 size_t len = 0;
507 for (p = str; *p; p++, len++) {
508 if (len == ASSUAN_LINELENGTH)
509 break;
511 if (*p == '\\') {
512 switch (*++p) {
513 case 't':
514 *b++ = '\t';
515 break;
516 case 'n':
517 *b++ = '\n';
518 break;
519 case 'v':
520 *b++ = '\v';
521 break;
522 case 'b':
523 *b++ = '\b';
524 break;
525 case 'f':
526 *b++ = '\f';
527 break;
528 case 'r':
529 *b++ = '\r';
530 break;
531 default:
532 *b++ = *p;
533 break;
536 if (!*p)
537 break;
539 continue;
542 *b++ = *p;
545 *b = 0;
546 return buf;
549 static gpg_error_t send_inquire(int fd, const char *command, const char *line)
551 struct inquire_s inq;
552 struct stat st;
553 gpg_error_t rc;
555 if (fstat(fd, &st) == -1)
556 return gpg_error_from_syserror();
558 memset(&inq, 0, sizeof(inq));
559 inq.fd = fd;
560 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
561 inq.len = 0;
563 if (!inq.line)
564 return GPG_ERR_ENOMEM;
566 if (line) {
567 char *s = escape(line);
569 strncpy(inq.line, s, ASSUAN_LINELENGTH-1);
570 inq.len = strlen(s);
571 pwmd_free(s);
574 rc = pwmd_setopt(pwm, PWMD_OPTION_INQUIRE_TOTAL,
575 st.st_size ? st.st_size+strlen(inq.line) : 0);
577 if (rc)
578 return rc;
580 rc = pwmd_inquire(pwm, command, inquire_cb, &inq);
581 pwmd_free(inq.line);
582 return rc;
585 #ifdef HAVE_LIBREADLINE
586 static int interactive_hook(void)
588 interactive_error = process_cmd(pwm, NULL, 0, 1);
590 if (interactive_error)
591 rl_event_hook = NULL;
593 return 0;
596 static void print_help()
598 fprintf(stderr, N_(
599 "------------------------------------------------------------------------------\n"
600 "Type HELP for protocol command help (since pwmd 2.19). Commands which use a\n"
601 "server inquire need a non-protocol command to send the data. These commands\n"
602 "including any options are:\n"
603 "\n"
604 " INQUIRE <command> - to read from the terminal\n"
605 " INQUIRE_FILE <filename> <command> - to read the specified filename\n"
606 "\n"
607 "Elements are TAB delimited. Press CTRL-V then TAB to insert from the prompt.\n"
608 "\n"
609 "Press CTRL-D to quit.\n"
610 "------------------------------------------------------------------------------\n"
614 static gpg_error_t do_interactive()
616 gpg_error_t rc = process_cmd(pwm, NULL, 0, 1);
618 if (rc)
619 return rc;
621 fprintf(stderr, N_("WARNING: interactive mode doesn't use secure memory!\n"));
622 print_help();
623 rl_initialize();
624 rl_event_hook = &interactive_hook;
625 rl_set_keyboard_input_timeout(100000);
627 for (;;) {
628 char *line;
629 char *result = NULL;
631 line = readline("pwm> ");
633 if (interactive_error)
634 return interactive_error;
636 if (!line) {
637 rc = finalize();
639 if (!rc)
640 return 0;
642 if (rc != GPG_ERR_CANCELED && rc != GPG_ERR_EOF)
643 fprintf(stderr, "ERR %i: %s\n", rc, pwmd_strerror(rc));
645 continue;
647 else if (!*line) {
648 free(line);
649 continue;
652 #ifdef HAVE_READLINE_HISTORY
653 add_history(line);
654 #endif
656 /* INQUIRE <COMMAND [options ...]> */
657 if (!strncasecmp(line, "inquire ", 8))
658 rc = send_inquire(STDIN_FILENO, line+8, NULL);
659 /* INQUIRE_FILE <FILENAME> <COMMAND [options ...]> */
660 else if (!strncasecmp(line, "inquire_file ", 13)) {
661 char *p = strchr(line+13, ' ');
662 char *f = p ? line+13 : NULL;
663 int fd;
665 if (!f || !*f || !*p) {
666 fprintf(stderr, N_("Syntax error. Usage: INQUIRE_FILE <filename> <command>\n"));
667 free(line);
668 continue;
671 f[strlen(f) - strlen(p++)] = 0;
672 fd = open(f, O_RDONLY);
674 if (fd == -1) {
675 fprintf(stderr, "%s\n", strerror(errno));
676 free(line);
677 continue;
679 else {
680 rc = send_inquire(fd, p, NULL);
681 close(fd);
684 else
685 rc = pwmd_command(pwm, &result, line);
687 free(line);
689 if (rc)
690 fprintf(stderr, "ERR %i: %s\n", rc, pwmd_strerror(rc));
691 else if (result && *result)
692 printf("%s%s", result, result[strlen(result)-1] != '\n' ? "\n" : "");
694 if (result)
695 pwmd_free(result);
698 return 0;
700 #endif
702 static gpg_error_t finalize()
704 gpg_error_t rc = 0;
705 int quit = 0;
707 #ifdef HAVE_LIBREADLINE
708 if (!force_save && interactive && filename) {
709 char *p, buf[16];
710 int finished = 0;
712 fprintf(stderr, "\n");
714 do {
715 fprintf(stderr, N_("(c)ancel/(f)orget password/(s)ave/(Q)uit/(S)ave and quit/(h)elp?: "));
716 p = fgets(buf, sizeof(buf), stdin);
718 if (feof(stdin)) {
719 clearerr(stdin);
720 return GPG_ERR_EOF;
723 switch (*p) {
724 case 'h':
725 print_help();
726 break;
727 case 'c':
728 return GPG_ERR_CANCELED;
729 case 'Q':
730 return 0;
731 case 'f':
732 rc = pwmd_command(pwm, NULL, "CLEARCACHE %s", filename);
734 if (rc)
735 return rc;
737 interactive_hook();
738 break;
739 case 'S':
740 quit = 1;
741 case 's':
742 save = 1;
743 finished = 1;
744 break;
745 default:
746 break;
748 } while (!finished);
750 #endif
752 if (save && filename) {
753 fprintf(stderr, "\n");
754 fprintf(stderr, N_("Saving changes ...\n"));
756 if (iter != -2) {
757 rc = pwmd_setopt(pwm, PWMD_OPTION_ITERATIONS, iter);
759 if (rc)
760 return rc;
763 if (cipher) {
764 rc = pwmd_setopt(pwm, PWMD_OPTION_CIPHER, cipher);
766 if (rc)
767 return rc;
770 if (local_pin || no_pinentry) {
771 char *p1;
772 again:
773 if (!force_save) {
774 rc = pwmd_command(pwm, NULL, "ISCACHED %s", filename);
776 if (rc && rc != GPG_ERR_NOT_FOUND &&
777 rc != GPG_ERR_ENOENT)
778 return rc;
779 else if (!rc)
780 goto do_save;
783 if (!no_pinentry) {
784 rc = pwmd_getpin(pwm, filename, &p1, PWMD_PINENTRY_SAVE);
786 if (rc)
787 return rc;
789 rc = pwmd_getpin(pwm, filename, &password,
790 PWMD_PINENTRY_SAVE_CONFIRM);
792 if (rc) {
793 pwmd_free(p1);
794 return rc;
797 else {
798 rc = get_password(&p1, PWMD_PINENTRY_SAVE, 0);
800 if (rc)
801 return rc;
803 rc = get_password(&password, PWMD_PINENTRY_SAVE_CONFIRM, 0);
805 if (rc) {
806 if (p1)
807 pwmd_free(p1);
809 return rc;
813 if ((p1 || password) && ((!p1 && password) || (!password && p1) ||
814 strcmp(p1, password))) {
815 pwmd_free(p1);
816 pwmd_free(password);
817 password = NULL;
818 goto again;
821 if (p1)
822 pwmd_free(p1);
824 rc = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
826 if (password)
827 pwmd_free(password);
829 if (rc)
830 return rc;
833 if (force_save) {
834 rc = pwmd_command(pwm, NULL, "CLEARCACHE %s", filename);
836 if (rc)
837 return rc;
839 if (!local_pin) {
840 rc = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, NULL);
842 if (rc)
843 return rc;
847 do_save:
848 if (keyfile && !local_pin) {
849 struct inquire_s inq;
851 memset(&inq, 0, sizeof(inq));
852 inq.fd = open(keyfile, O_RDONLY);
854 if (inq.fd == -1) {
855 rc = gpg_error_from_syserror();
856 return rc;
859 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
861 if (!inq.line) {
862 rc = GPG_ERR_ENOMEM;
863 return rc;
866 rc = pwmd_save_inquire(pwm, inquire_cb, &inq);
867 pwmd_free(inq.line);
868 close(inq.fd);
870 else {
871 #ifdef DEBUG
872 switch (method) {
873 case 0:
874 rc = pwmd_save(pwm);
875 break;
876 case 1:
877 rc = pwmd_save2(pwm);
878 break;
879 case 2:
880 rc = pwmd_save_async(pwm);
881 break;
882 case 3:
883 rc = pwmd_save_async2(pwm);
884 break;
887 if (!rc && method >= 2)
888 rc = process_cmd(pwm, NULL, 0, 0);
890 #else
891 #if defined(WITH_PINENTRY) && defined(WITH_TCP)
892 if (host)
893 rc = pwmd_save2(pwm);
894 else
895 #endif
896 rc = pwmd_save(pwm);
897 #endif
901 if (interactive)
902 return rc ? rc : quit ? 0 : GPG_ERR_CANCELED;
904 return rc;
907 int main(int argc, char *argv[])
909 int connected = 0;
910 gpg_error_t error;
911 int opt;
912 int base64 = 0;
913 char *socketpath = NULL;
914 char command[ASSUAN_LINELENGTH], *p;
915 int ret = EXIT_SUCCESS;
916 char *result = NULL;
917 char *pinentry_path = NULL;
918 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
919 *lcmessages = NULL;
920 int outfd = STDOUT_FILENO;
921 FILE *outfp = stdout;
922 int inquirefd = STDIN_FILENO;
923 FILE *inquirefp = stdin;
924 int show_status = 1;
925 char *clientname = "pwmc";
926 char *inquire = NULL;
927 char *inquire_line = NULL;
928 int have_iter = 0;
929 int timeout = 0;
930 #ifdef WITH_TCP
931 char *host = NULL;
932 int port = DEFAULT_PORT;
933 char *username = NULL;
934 char *ident = NULL;
935 char *known_hosts = NULL;
936 int get = 0;
937 int prot = PWMD_IP_ANY;
938 int use_agent = 0;
939 #endif
940 int tries = 0;
941 int local_tries = 3;
942 int try = 0;
943 pwmd_socket_t s;
944 int lock_on_open = 1;
945 #ifdef DEBUG
946 fd_set rfds;
947 #endif
948 char *url_string = NULL;
949 /* The order is important. */
950 enum {
951 #ifdef DEBUG
952 OPT_DEBUG,
953 #endif
954 #ifdef WITH_TCP
955 OPT_HOST, OPT_PORT, OPT_IDENTITY, OPT_KNOWN_HOSTS, OPT_USER,
956 OPT_GET_HOSTKEY, OPT_IPV4, OPT_IPV6, OPT_USE_AGENT,
957 #endif
958 OPT_URL, OPT_LOCAL, OPT_FORCE_SAVE, OPT_TTYNAME, OPT_TTYTYPE,
959 OPT_DISPLAY, OPT_LC_CTYPE, OPT_LC_MESSAGES, OPT_TIMEOUT, OPT_TRIES,
960 OPT_PINENTRY, OPT_PASSPHRASE, OPT_KEYFILE, OPT_BASE64, OPT_SOCKET,
961 OPT_NOLOCK, OPT_SAVE, OPT_ITERATIONS, OPT_OUTPUT_FD, OPT_INQUIRE,
962 OPT_INQUIRE_FD, OPT_INQUIRE_LINE, OPT_NO_STATUS, OPT_NAME,
963 OPT_VERSION, OPT_HELP, OPT_CIPHER, OPT_NO_PINENTRY,
964 #ifdef HAVE_LIBREADLINE
965 OPT_INTERACTIVE,
966 #endif
968 const struct option long_opts[] = {
969 #ifdef DEBUG
970 { "debug", 1, 0, 0 },
971 #endif
972 #ifdef WITH_TCP
973 { "host", 1, 0, 'h' },
974 { "port", 1, 0, 'p' },
975 { "identity", 1, 0, 'i' },
976 { "known-hosts", 1, 0, 'k' },
977 { "user", 1, 0, 'u' },
978 { "get-hostkey", 0, 0, 'g' },
979 { "ipv4", 0, 0, '4' },
980 { "ipv6", 0, 0, '6' },
981 { "use-agent", 0, 0, 0 },
982 #endif
983 { "url", 1, 0, 0 },
984 { "local-pinentry", 0, 0 },
985 { "force-save", 0, 0 },
986 { "ttyname", 1, 0, 'y' },
987 { "ttytype", 1, 0, 't' },
988 { "display", 1, 0, 'd' },
989 { "lc-ctype", 1, 0, 0 },
990 { "lc-messages", 1, 0, 0 },
991 { "timeout", 1, 0, 0 },
992 { "tries", 1, 0, 0 },
993 { "pinentry", 1, 0, 0 },
994 { "passphrase", 1, 0, 'P' },
995 { "key-file", 1, 0, 0 },
996 { "base64", 0, 0, 0 },
997 { "socket", 1, 0, 0 },
998 { "no-lock", 0, 0, 0 },
999 { "save", 0, 0, 'S' },
1000 { "iterations", 1, 0, 'I' },
1001 { "output-fd", 1, 0, 0 },
1002 { "inquire", 1, 0, 0 },
1003 { "inquire-fd", 1, 0, 0 },
1004 { "inquire-line", 1, 0, 'L' },
1005 { "no-status", 0, 0, 0 },
1006 { "name", 1, 0, 'n' },
1007 { "version", 0, 0, 0 },
1008 { "help", 0, 0, 0 },
1009 { "cipher", 1, 0, 0 },
1010 { "no-pinentry", 0, 0, 0 },
1011 #ifdef HAVE_LIBREADLINE
1012 { "interactive", 0, 0 },
1013 #endif
1014 { 0, 0, 0, 0}
1016 #ifdef WITH_TCP
1017 const char *optstring = "L:46h:p:i:k:u:gy:t:d:P:I:Sn:";
1018 #else
1019 const char *optstring = "L:y:t:d:P:I:Sn:";
1020 #endif
1021 int opt_index = 0;
1023 #ifdef ENABLE_NLS
1024 setlocale(LC_ALL, "");
1025 bindtextdomain("libpwmd", LOCALEDIR);
1026 #endif
1028 #ifdef HAVE_LIBREADLINE
1029 if (!strcmp(basename(argv[0]), "pwmsh"))
1030 interactive = 1;
1031 #endif
1033 iter = -2;
1035 while ((opt = getopt_long(argc, argv, optstring, long_opts, &opt_index)) != -1) {
1036 switch (opt) {
1037 /* Handle long options without a short option part. */
1038 case 0:
1039 switch (opt_index) {
1040 #ifdef DEBUG
1041 case OPT_DEBUG:
1042 method = atoi(optarg);
1044 if (method > 3)
1045 method = 3;
1046 break;
1047 #endif
1048 #ifdef WITH_TCP
1049 case OPT_USE_AGENT:
1050 use_agent = 1;
1051 break;
1052 #endif
1053 case OPT_KEYFILE:
1054 keyfile = optarg;
1055 break;
1056 case OPT_BASE64:
1057 base64 = 1;
1058 break;
1059 case OPT_NOLOCK:
1060 lock_on_open = 0;
1061 break;
1062 case OPT_URL:
1063 url_string = optarg;
1064 break;
1065 case OPT_LOCAL:
1066 local_pin = 1;
1067 break;
1068 case OPT_FORCE_SAVE:
1069 save = force_save = 1;
1070 break;
1071 case OPT_LC_CTYPE:
1072 lcctype = pwmd_strdup(optarg);
1073 break;
1074 case OPT_LC_MESSAGES:
1075 lcmessages = pwmd_strdup(optarg);
1076 break;
1077 case OPT_TIMEOUT:
1078 timeout = atoi(optarg);
1079 break;
1080 case OPT_TRIES:
1081 tries = atoi(optarg);
1082 local_tries = tries;
1083 break;
1084 case OPT_SOCKET:
1085 socketpath = pwmd_strdup(optarg);
1086 break;
1087 case OPT_INQUIRE:
1088 inquire = optarg;
1089 break;
1090 case OPT_INQUIRE_FD:
1091 inquirefd = atoi(optarg);
1092 inquirefp = fdopen(inquirefd, "r");
1094 if (!inquirefp) {
1095 pwmd_free(password);
1096 err(EXIT_FAILURE, "%i", inquirefd);
1098 break;
1099 case OPT_OUTPUT_FD:
1100 outfd = atoi(optarg);
1101 outfp = fdopen(outfd, "w");
1103 if (!outfp) {
1104 pwmd_free(password);
1105 err(EXIT_FAILURE, "%i", outfd);
1107 break;
1108 case OPT_NO_STATUS:
1109 show_status = 0;
1110 break;
1111 case OPT_VERSION:
1112 pwmd_free(password);
1113 printf("%s (pwmc)\n%s\n\n"
1114 "Compile-time features:\n"
1115 #ifdef WITH_TCP
1116 "+SSH "
1117 #else
1118 "-SSH "
1119 #endif
1120 #ifdef WITH_PINENTRY
1121 "+PINENTRY "
1122 #else
1123 "-PINENTRY "
1124 #endif
1125 #ifdef WITH_QUALITY
1126 "+CRACK "
1127 #else
1128 "-CRACK "
1129 #endif
1130 #ifdef MEM_DEBUG
1131 "+MEM_DEBUG "
1132 #else
1133 "-MEM_DEBUG "
1134 #endif
1135 #ifdef HAVE_LIBREADLINE
1136 "+INTERACTIVE "
1137 #else
1138 "-INTERACTIVE "
1139 #endif
1140 "\n"
1141 , PACKAGE_STRING, PACKAGE_BUGREPORT);
1142 exit(EXIT_SUCCESS);
1143 case OPT_PINENTRY:
1144 pinentry_path = optarg;
1145 break;
1146 case OPT_HELP:
1147 usage(argv[0], EXIT_SUCCESS);
1148 case OPT_CIPHER:
1149 cipher = optarg;
1150 break;
1151 case OPT_NO_PINENTRY:
1152 no_pinentry = 1;
1153 break;
1154 #ifdef HAVE_LIBREADLINE
1155 case OPT_INTERACTIVE:
1156 interactive = 1;
1157 break;
1158 #endif
1159 default:
1160 usage(argv[0], EXIT_FAILURE);
1163 break;
1164 #ifdef WITH_TCP
1165 case '4':
1166 prot = PWMD_IPV4;
1167 break;
1168 case '6':
1169 prot = PWMD_IPV6;
1170 break;
1171 case 'h':
1172 host = pwmd_strdup(optarg);
1173 break;
1174 case 'p':
1175 port = atoi(optarg);
1176 break;
1177 case 'i':
1178 ident = pwmd_strdup(optarg);
1179 break;
1180 case 'u':
1181 username = pwmd_strdup(optarg);
1182 break;
1183 case 'k':
1184 known_hosts = pwmd_strdup(optarg);
1185 break;
1186 case 'g':
1187 get = 1;
1188 break;
1189 #endif
1190 case 'L':
1191 inquire_line = optarg;
1192 break;
1193 case 'y':
1194 tty = optarg;
1195 break;
1196 case 't':
1197 ttytype = optarg;
1198 break;
1199 case 'd':
1200 display = optarg;
1201 break;
1202 case 'S':
1203 save = 1;
1204 break;
1205 case 'I':
1206 iter = strtol(optarg, NULL, 10);
1207 have_iter = 1;
1208 break;
1209 case 'P':
1210 password = pwmd_strdup(optarg);
1211 memset(optarg, 0, strlen(optarg));
1212 break;
1213 case 'n':
1214 clientname = optarg;
1215 break;
1216 default:
1217 pwmd_free(password);
1218 usage(argv[0], EXIT_FAILURE);
1222 #ifdef HAVE_LIBREADLINE
1223 if (interactive && !isatty(STDIN_FILENO)) {
1224 pwmd_free(password);
1225 usage(argv[0], EXIT_FAILURE);
1227 #endif
1229 #ifdef DEBUG
1230 if (!url_string) {
1231 #endif
1232 #ifdef WITH_TCP
1233 if (host && !get && !ident) {
1234 pwmd_free(password);
1235 usage(argv[0], EXIT_FAILURE);
1238 if (get && !host) {
1239 pwmd_free(password);
1240 usage(argv[0], EXIT_FAILURE);
1242 #endif
1243 #ifdef DEBUG
1245 #endif
1247 filename = argv[optind];
1248 pwmd_init();
1249 pwm = pwmd_new(clientname);
1250 #ifdef DEBUG
1251 FD_ZERO(&rfds);
1252 #endif
1254 #ifdef WITH_TCP
1255 if (host || is_remote_url(url_string)) {
1256 #ifndef WITH_PINENTRY
1257 no_pinentry = 1;
1258 #endif
1259 #ifndef DEBUG
1260 local_pin = 1;
1261 #endif
1263 if (prot != PWMD_IP_ANY) {
1264 error = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, prot);
1266 if (error)
1267 goto done;
1270 error = pwmd_setopt(pwm, PWMD_OPTION_KNOWNHOST_CB, knownhost_cb);
1272 if (error)
1273 goto done;
1275 error = pwmd_setopt(pwm, PWMD_OPTION_KNOWNHOST_DATA, clientname);
1277 if (error)
1278 goto done;
1280 error = pwmd_setopt(pwm, PWMD_OPTION_SSH_AGENT, use_agent);
1282 if (error)
1283 goto done;
1285 fprintf(stderr, N_("Connecting ...\n"));
1287 #ifdef DEBUG
1288 if (method >= 2) {
1289 if (get) {
1290 char *hostkey;
1292 error = pwmd_get_hostkey_async(pwm, host, port);
1294 if (error)
1295 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
1297 error = process_cmd(pwm, &hostkey, 0, 0);
1299 if (error)
1300 goto done;
1302 printf("%s", hostkey);
1303 pwmd_free(hostkey);
1304 pwmd_free(password);
1305 pwmd_close(pwm);
1306 exit(EXIT_SUCCESS);
1309 if (url_string)
1310 error = pwmd_connect_url_async(pwm, url_string);
1311 else
1312 error = pwmd_ssh_connect_async(pwm, host, port, ident, username,
1313 known_hosts);
1315 if (error)
1316 goto done;
1318 error = process_cmd(pwm, NULL, 0, 0);
1320 if (error)
1321 goto done;
1323 else {
1324 #endif
1325 if (get) {
1326 char *hostkey;
1328 error = pwmd_get_hostkey(pwm, host, port, &hostkey);
1330 if (error)
1331 goto done;
1333 printf("%s", hostkey);
1334 pwmd_free(hostkey);
1335 pwmd_free(password);
1336 pwmd_close(pwm);
1337 exit(EXIT_SUCCESS);
1340 if (url_string)
1341 error = pwmd_connect_url(pwm, url_string);
1342 else
1343 error = pwmd_ssh_connect(pwm, host, port, ident, username, known_hosts);
1345 if (error)
1346 goto done;
1347 #ifdef DEBUG
1349 #endif
1351 else {
1352 #endif
1353 if (url_string)
1354 error = pwmd_connect_url(pwm, url_string);
1355 else
1356 error = pwmd_connect(pwm, socketpath);
1358 if (error)
1359 goto done;
1360 #ifdef WITH_TCP
1362 #endif
1364 fprintf(stderr, N_("Connected.\n"));
1365 connected = 1;
1366 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TITLE, NULL);
1367 error = pwmd_socket_type(pwm, &s);
1369 if (error)
1370 goto done;
1372 #ifndef DEBUG
1373 if (s == PWMD_SOCKET_SSH && force_save && !local_pin) {
1374 error = GPG_ERR_WRONG_KEY_USAGE;
1375 goto done;
1377 #endif
1379 if (have_iter) {
1380 error = pwmd_command(pwm, &result, "VERSION");
1382 if (error)
1383 goto done;
1385 pwmd_free(result);
1387 if (iter < 0) {
1388 pwmd_free(password);
1389 pwmd_close(pwm);
1390 usage(argv[0], EXIT_FAILURE);
1394 if (filename && lock_on_open) {
1395 error = pwmd_setopt(pwm, PWMD_OPTION_LOCK_ON_OPEN, 1);
1397 if (error)
1398 goto done;
1401 if (base64) {
1402 error = pwmd_setopt(pwm, PWMD_OPTION_BASE64, 1);
1404 if (error)
1405 goto done;
1408 if (timeout > 0) {
1409 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
1411 if (error)
1412 goto done;
1415 if (no_pinentry) {
1416 error = pwmd_setopt(pwm, PWMD_OPTION_NO_PINENTRY, 1);
1418 if (error)
1419 goto done;
1422 if ((no_pinentry || local_pin) && filename && !keyfile && !password) {
1423 error = pwmd_command(pwm, NULL, "ISCACHED %s", filename);
1425 if (error == GPG_ERR_NOT_FOUND) {
1426 local_password:
1427 if (try++ == local_tries)
1428 goto done;
1430 if (password)
1431 pwmd_free(password);
1433 password = NULL;
1435 if (!no_pinentry) {
1437 if (try > 1)
1438 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, 0);
1440 error = pwmd_getpin(pwm, filename, &password,
1441 try > 1 ? PWMD_PINENTRY_OPEN_FAILED : PWMD_PINENTRY_OPEN);
1443 if (error)
1444 goto done;
1446 if (!password)
1447 password = pwmd_strdup("");
1449 else {
1450 error = get_password(&password,try > 1 ? PWMD_PINENTRY_OPEN_FAILED : PWMD_PINENTRY_OPEN, 0);
1453 if (error)
1454 goto done;
1456 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
1458 if (error)
1459 goto done;
1461 if (try > 1)
1462 goto do_open;
1464 else if (error && error != GPG_ERR_ENOENT)
1465 goto done;
1468 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
1470 if (error)
1471 goto done;
1473 if (!no_pinentry) {
1474 if (pinentry_path) {
1475 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
1477 if (error)
1478 goto done;
1481 if (display) {
1482 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
1484 if (error)
1485 goto done;
1488 if (tty) {
1489 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
1491 if (error)
1492 goto done;
1495 if (ttytype) {
1496 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
1498 if (error)
1499 goto done;
1502 if (lcctype) {
1503 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
1505 if (error)
1506 goto done;
1509 if (lcmessages) {
1510 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
1511 lcmessages);
1513 if (error)
1514 goto done;
1517 if (tries > 0) {
1518 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
1520 if (error)
1521 goto done;
1525 if (show_status) {
1526 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_CB, status_msg_cb);
1528 if (error)
1529 goto done;
1532 do_open:
1533 if (filename) {
1534 fprintf(stderr, N_("Opening data file \"%s\" ...\n"), filename);
1536 #ifdef WITH_TCP
1537 if (keyfile && (!local_pin || host || is_remote_url(url_string))) {
1538 struct inquire_s inq;
1540 memset(&inq, 0, sizeof(inq));
1541 inq.fd = open(keyfile, O_RDONLY);
1543 if (inq.fd == -1) {
1544 error = gpg_error_from_syserror();
1545 goto done;
1548 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
1550 if (!inq.line) {
1551 error = GPG_ERR_ENOMEM;
1552 goto done;
1555 in_keyfile = 1;
1556 error = pwmd_open_inquire(pwm, filename, inquire_cb, &inq);
1557 in_keyfile = 0;
1558 pwmd_free(inq.line);
1559 close(inq.fd);
1561 if (error)
1562 goto done;
1564 goto read_command;
1566 #endif
1567 #ifdef DEBUG
1568 switch (method) {
1569 case 0:
1570 error = pwmd_open(pwm, filename);
1571 break;
1572 case 1:
1573 error = pwmd_open2(pwm, filename);
1574 break;
1575 case 2:
1576 error = pwmd_open_async(pwm, filename);
1578 break;
1579 case 3:
1580 error = pwmd_open_async2(pwm, filename);
1581 break;
1584 if (error && (local_pin || no_pinentry) &&
1585 error == GPG_ERR_INV_PASSPHRASE)
1586 goto local_password;
1588 if (error)
1589 goto done;
1591 if (method >= 2)
1592 error = process_cmd(pwm, &result, 0, 0);
1593 #else
1594 #if defined(WITH_PINENTRY) && defined(WITH_TCP)
1595 if (local_pin)
1596 error = pwmd_open2(pwm, filename);
1597 else
1598 #endif
1599 error = pwmd_open(pwm, filename);
1601 if (error && (local_pin || no_pinentry) &&
1602 error == GPG_ERR_INV_PASSPHRASE)
1603 goto local_password;
1604 #endif
1606 if (error)
1607 goto done;
1610 #ifdef WITH_TCP
1611 read_command:
1612 #endif
1613 #ifdef HAVE_LIBREADLINE
1614 if (interactive) {
1615 if (!force_save)
1616 save = 0;
1618 error = do_interactive();
1619 goto do_exit;
1621 #endif
1623 if (inquire) {
1624 int fd = fileno(inquirefp);
1626 if (fd == -1) {
1627 error = gpg_error_from_syserror();
1628 goto done;
1631 error = send_inquire(fd, inquire, inquire_line);
1632 close(fd);
1633 goto done;
1636 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
1637 ssize_t n;
1639 for (;;) {
1640 error = process_cmd(pwm, NULL, 1, 0);
1642 if (error)
1643 goto done;
1645 n = read(STDIN_FILENO, command, sizeof(command));
1647 if (n == -1) {
1648 if (errno == EAGAIN)
1649 continue;
1651 error = gpg_error_from_errno(errno);
1652 goto done;
1654 else if (!n)
1655 goto done;
1657 p = command;
1658 command[n] = 0;
1659 break;
1662 if (!p || !*p)
1663 goto done;
1665 if (strcasecmp(p, "BYE") == 0)
1666 goto done;
1668 error = pwmd_command(pwm, &result, command);
1670 if (error)
1671 goto done;
1673 if (result) {
1674 fwrite(result, 1, strlen(result), outfp);
1675 fflush(outfp);
1676 pwmd_free(result);
1679 done:
1680 if (password)
1681 pwmd_free(password);
1683 password = NULL;
1685 if (!error)
1686 error = finalize();
1688 do_exit:
1689 memset(command, 0, sizeof(command));
1691 if (error) {
1692 show_error(error);
1693 ret = EXIT_FAILURE;
1696 pwmd_close(pwm);
1698 if (connected)
1699 fprintf(stderr, N_("Connection closed.\n"));
1701 if (socketpath)
1702 pwmd_free(socketpath);
1704 exit(ret);