pwmc: let the -i option have priority over the ssh agent. The --url
[libpwmd.git] / src / pwmc.c
blob32d5b4a63223b5ac717715536f1d12b256656578
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 " --no-ssh-agent\n"
129 " disable SSH agent (enabled when SSH_AGENT_PID is set)\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] --no-ssh-agent\n"
234 " or\n"
235 " ssh[46]://[username@]hostname[:port][,,known_hosts]\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 #ifdef DEBUG
319 fprintf(stderr, ".");
320 #endif
321 FD_ZERO(&rfds);
322 rc = pwmd_get_fds(pwm, pfds, &nfds);
324 if (rc)
325 break;
327 if (!nfds) {
328 s = pwmd_process(pwm, &rc, result);
329 break;
332 for (i = 0, n = 0; i < nfds; i++) {
333 FD_SET(pfds[i].fd, &rfds);
334 n = pfds[i].fd > n ? pfds[i].fd : n;
337 if (input)
338 FD_SET(STDIN_FILENO, &rfds);
340 nfds = select(n+1, &rfds, NULL, NULL, &tv);
342 if (nfds == -1) {
343 rc = gpg_error_from_errno(errno);
344 break;
347 if (input && FD_ISSET(STDIN_FILENO, &rfds)) {
348 rc = 0;
349 break;
352 s = pwmd_process(pwm, &rc, result);
354 if (once)
355 break;
356 } while (s == ASYNC_PROCESS);
358 #ifdef DEBUG
359 fprintf(stderr, "\n");
360 #endif
361 return rc;
364 static gpg_error_t get_password(char **result, pwmd_pinentry_t w, int echo)
366 char buf[LINE_MAX] = {0}, *p;
367 struct termios told, tnew;
368 char *key = NULL;
370 *result = NULL;
372 if (!isatty(STDIN_FILENO)) {
373 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
374 return GPG_ERR_ENOTTY;
377 if (!echo) {
378 if (tcgetattr(STDIN_FILENO, &told) == -1)
379 return gpg_error_from_syserror();
381 memcpy(&tnew, &told, sizeof(struct termios));
382 tnew.c_lflag &= ~(ECHO);
383 tnew.c_lflag |= ICANON|ECHONL;
385 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
386 int n = errno;
388 tcsetattr(STDIN_FILENO, TCSANOW, &told);
389 return gpg_error_from_errno(n);
393 switch (w) {
394 case PWMD_PINENTRY_OPEN:
395 fprintf(stderr, N_("Password for %s: "), filename);
396 break;
397 case PWMD_PINENTRY_OPEN_FAILED:
398 fprintf(stderr, N_("Invalid password. Password for %s: "), filename);
399 break;
400 case PWMD_PINENTRY_SAVE:
401 fprintf(stderr, N_("New password for %s: "), filename);
402 break;
403 case PWMD_PINENTRY_SAVE_CONFIRM:
404 fprintf(stderr, N_("Confirm password: "));
405 break;
406 default:
407 break;
410 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
411 tcsetattr(STDIN_FILENO, TCSANOW, &told);
412 return 0;
415 if (!echo)
416 tcsetattr(STDIN_FILENO, TCSANOW, &told);
418 if (feof(stdin)) {
419 clearerr(stdin);
420 return GPG_ERR_CANCELED;
423 p[strlen(p) - 1] = 0;
425 if (buf[0]) {
426 key = pwmd_strdup_printf("%s", p);
427 memset(&buf, 0, sizeof(buf));
429 if (!key)
430 return GPG_ERR_ENOMEM;
433 *result = key;
434 return 0;
437 #ifdef WITH_TCP
438 static gpg_error_t knownhost_cb(void *data, const char *host, const char *key,
439 size_t len)
441 gpg_error_t rc;
442 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);
444 if (no_pinentry && !isatty(STDIN_FILENO)) {
445 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
446 pwmd_free(buf);
447 return GPG_ERR_ENOTTY;
449 else if (no_pinentry) {
450 for (char *p = buf, len = 0; *p; p++, len++) {
451 if (*p == '\n')
452 len = 0;
454 if (len == 78) {
455 char *t = p;
457 while (!isspace(*(--p)));
458 *p = '\n';
459 p = t;
460 len = 0;
464 fprintf(stderr, "%s\n\n", buf);
465 pwmd_free(buf);
467 do {
468 char *result;
470 fprintf(stderr, N_("Trust this connection [y/N]: "));
471 fflush(stderr);
472 rc = get_password(&result, PWMD_PINENTRY_CONFIRM, 1);
474 if (rc)
475 return rc;
477 if (!result || !*result || *result == 'n' || *result == 'N') {
478 if (result && *result)
479 pwmd_free(result);
481 return GPG_ERR_NOT_CONFIRMED;
484 if ((*result == 'y' || *result == 'Y') && *(result+1) == 0) {
485 pwmd_free(result);
486 return 0;
488 } while (1);
491 rc = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TITLE, buf);
492 pwmd_free(buf);
494 if (rc)
495 return rc;
497 return pwmd_getpin(pwm, NULL, NULL, PWMD_PINENTRY_CONFIRM);
500 static int is_remote_url(const char *str)
502 if (str && (strstr(str, "file://") || strstr(str, "local://")))
503 return 0;
505 return !str ? 0 : 1;
507 #endif
509 static char *escape(const char *str)
511 const char *p;
512 char *buf = pwmd_malloc(ASSUAN_LINELENGTH+1), *b = buf;
513 size_t len = 0;
515 for (p = str; *p; p++, len++) {
516 if (len == ASSUAN_LINELENGTH)
517 break;
519 if (*p == '\\') {
520 switch (*++p) {
521 case 't':
522 *b++ = '\t';
523 break;
524 case 'n':
525 *b++ = '\n';
526 break;
527 case 'v':
528 *b++ = '\v';
529 break;
530 case 'b':
531 *b++ = '\b';
532 break;
533 case 'f':
534 *b++ = '\f';
535 break;
536 case 'r':
537 *b++ = '\r';
538 break;
539 default:
540 *b++ = *p;
541 break;
544 if (!*p)
545 break;
547 continue;
550 *b++ = *p;
553 *b = 0;
554 return buf;
557 static gpg_error_t send_inquire(int fd, const char *command, const char *line)
559 struct inquire_s inq;
560 struct stat st;
561 gpg_error_t rc;
563 if (fstat(fd, &st) == -1)
564 return gpg_error_from_syserror();
566 memset(&inq, 0, sizeof(inq));
567 inq.fd = fd;
568 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
569 inq.len = 0;
571 if (!inq.line)
572 return GPG_ERR_ENOMEM;
574 if (line) {
575 char *s = escape(line);
577 strncpy(inq.line, s, ASSUAN_LINELENGTH-1);
578 inq.len = strlen(s);
579 pwmd_free(s);
582 rc = pwmd_setopt(pwm, PWMD_OPTION_INQUIRE_TOTAL,
583 st.st_size ? st.st_size+strlen(inq.line) : 0);
585 if (rc)
586 return rc;
588 rc = pwmd_inquire(pwm, command, inquire_cb, &inq);
589 pwmd_free(inq.line);
590 return rc;
593 #ifdef HAVE_LIBREADLINE
594 static int interactive_hook(void)
596 interactive_error = process_cmd(pwm, NULL, 0, 1);
598 if (interactive_error)
599 rl_event_hook = NULL;
601 return 0;
604 static void print_help()
606 fprintf(stderr, N_(
607 "------------------------------------------------------------------------------\n"
608 "Type HELP for protocol command help (since pwmd 2.19). Commands which use a\n"
609 "server inquire need a non-protocol command to send the data. These commands\n"
610 "including any options are:\n"
611 "\n"
612 " INQUIRE <command> - to read from the terminal\n"
613 " INQUIRE_FILE <filename> <command> - to read the specified filename\n"
614 "\n"
615 "Elements are TAB delimited. Press CTRL-V then TAB to insert from the prompt.\n"
616 "\n"
617 "Press CTRL-D to quit.\n"
618 "------------------------------------------------------------------------------\n"
622 static gpg_error_t do_interactive()
624 gpg_error_t rc = process_cmd(pwm, NULL, 0, 1);
626 if (rc)
627 return rc;
629 fprintf(stderr, N_("WARNING: interactive mode doesn't use secure memory!\n"));
630 print_help();
631 rl_initialize();
632 rl_event_hook = &interactive_hook;
633 rl_set_keyboard_input_timeout(100000);
635 for (;;) {
636 char *line;
637 char *result = NULL;
639 line = readline("pwm> ");
641 if (interactive_error)
642 return interactive_error;
644 if (!line) {
645 rc = finalize();
647 if (!rc)
648 return 0;
650 if (rc != GPG_ERR_CANCELED && rc != GPG_ERR_EOF)
651 fprintf(stderr, "ERR %i: %s\n", rc, pwmd_strerror(rc));
653 continue;
655 else if (!*line) {
656 free(line);
657 continue;
660 #ifdef HAVE_READLINE_HISTORY
661 add_history(line);
662 #endif
664 /* INQUIRE <COMMAND [options ...]> */
665 if (!strncasecmp(line, "inquire ", 8))
666 rc = send_inquire(STDIN_FILENO, line+8, NULL);
667 /* INQUIRE_FILE <FILENAME> <COMMAND [options ...]> */
668 else if (!strncasecmp(line, "inquire_file ", 13)) {
669 char *p = strchr(line+13, ' ');
670 char *f = p ? line+13 : NULL;
671 int fd;
673 if (!f || !*f || !*p) {
674 fprintf(stderr, N_("Syntax error. Usage: INQUIRE_FILE <filename> <command>\n"));
675 free(line);
676 continue;
679 f[strlen(f) - strlen(p++)] = 0;
680 fd = open(f, O_RDONLY);
682 if (fd == -1) {
683 fprintf(stderr, "%s\n", strerror(errno));
684 free(line);
685 continue;
687 else {
688 rc = send_inquire(fd, p, NULL);
689 close(fd);
692 else
693 rc = pwmd_command(pwm, &result, line);
695 free(line);
697 if (rc)
698 fprintf(stderr, "ERR %i: %s\n", rc, pwmd_strerror(rc));
699 else if (result && *result)
700 printf("%s%s", result, result[strlen(result)-1] != '\n' ? "\n" : "");
702 if (result)
703 pwmd_free(result);
706 return 0;
708 #endif
710 static gpg_error_t finalize()
712 gpg_error_t rc = 0;
713 int quit = 0;
715 #ifdef HAVE_LIBREADLINE
716 if (!force_save && interactive) {
717 char *p, buf[16];
718 int finished = 0;
720 fprintf(stderr, "\n");
722 do {
723 fprintf(stderr, N_("(c)ancel/(f)orget password/(s)ave/(Q)uit/(S)ave and quit/(h)elp?: "));
724 p = fgets(buf, sizeof(buf), stdin);
726 if (feof(stdin)) {
727 clearerr(stdin);
728 return GPG_ERR_EOF;
731 switch (*p) {
732 case 'h':
733 print_help();
734 break;
735 case 'c':
736 return GPG_ERR_CANCELED;
737 case 'Q':
738 return 0;
739 case 'f':
740 rc = pwmd_command(pwm, NULL, "CLEARCACHE %s", filename);
742 if (rc)
743 return rc;
745 interactive_hook();
746 break;
747 case 'S':
748 quit = 1;
749 case 's':
750 save = 1;
751 finished = 1;
752 break;
753 default:
754 break;
756 } while (!finished);
758 #endif
760 if (save && !filename) {
761 fprintf(stderr, N_("No filename was specified on the command line. Aborting.\n"));
762 return GPG_ERR_CANCELED;
765 if (save && filename) {
766 fprintf(stderr, "\n");
767 fprintf(stderr, N_("Saving changes ...\n"));
769 if (iter != -2) {
770 rc = pwmd_setopt(pwm, PWMD_OPTION_ITERATIONS, iter);
772 if (rc)
773 return rc;
776 if (cipher) {
777 rc = pwmd_setopt(pwm, PWMD_OPTION_CIPHER, cipher);
779 if (rc)
780 return rc;
783 if ((local_pin || no_pinentry) && !keyfile) {
784 char *p1;
785 again:
786 if (!force_save) {
787 rc = pwmd_command(pwm, NULL, "ISCACHED %s", filename);
789 if (rc && rc != GPG_ERR_NOT_FOUND &&
790 rc != GPG_ERR_ENOENT)
791 return rc;
792 else if (!rc)
793 goto do_save;
796 if (!no_pinentry) {
797 rc = pwmd_getpin(pwm, filename, &p1, PWMD_PINENTRY_SAVE);
799 if (rc)
800 return rc;
802 rc = pwmd_getpin(pwm, filename, &password,
803 PWMD_PINENTRY_SAVE_CONFIRM);
805 if (rc) {
806 pwmd_free(p1);
807 return rc;
810 else {
811 rc = get_password(&p1, PWMD_PINENTRY_SAVE, 0);
813 if (rc)
814 return rc;
816 rc = get_password(&password, PWMD_PINENTRY_SAVE_CONFIRM, 0);
818 if (rc) {
819 if (p1)
820 pwmd_free(p1);
822 return rc;
826 if ((p1 || password) && ((!p1 && password) || (!password && p1) ||
827 strcmp(p1, password))) {
828 pwmd_free(p1);
829 pwmd_free(password);
830 password = NULL;
831 goto again;
834 if (p1)
835 pwmd_free(p1);
837 rc = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
839 if (password)
840 pwmd_free(password);
842 if (rc)
843 return rc;
846 if (force_save) {
847 rc = pwmd_command(pwm, NULL, "CLEARCACHE %s", filename);
849 if (rc)
850 return rc;
852 if (!local_pin) {
853 rc = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, NULL);
855 if (rc)
856 return rc;
860 do_save:
861 if (keyfile) {
862 struct inquire_s inq;
864 memset(&inq, 0, sizeof(inq));
865 inq.fd = open(keyfile, O_RDONLY);
867 if (inq.fd == -1) {
868 rc = gpg_error_from_syserror();
869 return rc;
872 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
874 if (!inq.line) {
875 rc = GPG_ERR_ENOMEM;
876 return rc;
879 in_keyfile = 1;
880 rc = pwmd_save_inquire(pwm, inquire_cb, &inq);
881 in_keyfile = 0;
882 pwmd_free(inq.line);
883 close(inq.fd);
885 else {
886 #ifdef DEBUG
887 switch (method) {
888 case 0:
889 rc = pwmd_save(pwm);
890 break;
891 case 1:
892 rc = pwmd_save2(pwm);
893 break;
894 case 2:
895 rc = pwmd_save_async(pwm);
896 break;
897 case 3:
898 rc = pwmd_save_async2(pwm);
899 break;
902 if (!rc && method >= 2)
903 rc = process_cmd(pwm, NULL, 0, 0);
905 #else
906 #if defined(WITH_PINENTRY) && defined(WITH_TCP)
907 if (host)
908 rc = pwmd_save2(pwm);
909 else
910 #endif
911 rc = pwmd_save(pwm);
912 #endif
916 if (interactive)
917 return rc ? rc : quit ? 0 : GPG_ERR_CANCELED;
919 return rc;
922 int main(int argc, char *argv[])
924 int connected = 0;
925 gpg_error_t error;
926 int opt;
927 int base64 = 0;
928 char *socketpath = NULL;
929 char command[ASSUAN_LINELENGTH], *p;
930 int ret = EXIT_SUCCESS;
931 char *result = NULL;
932 char *pinentry_path = NULL;
933 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
934 *lcmessages = NULL;
935 int outfd = STDOUT_FILENO;
936 FILE *outfp = stdout;
937 int inquirefd = STDIN_FILENO;
938 FILE *inquirefp = stdin;
939 int show_status = 1;
940 char *clientname = "pwmc";
941 char *inquire = NULL;
942 char *inquire_line = NULL;
943 int have_iter = 0;
944 int timeout = 0;
945 #ifdef WITH_TCP
946 char *host = NULL;
947 int port = DEFAULT_PORT;
948 char *username = NULL;
949 char *ident = NULL;
950 char *known_hosts = NULL;
951 int get = 0;
952 int prot = PWMD_IP_ANY;
953 int use_ssh_agent = 1;
954 #endif
955 int tries = 0;
956 int local_tries = 3;
957 int try = 0;
958 pwmd_socket_t s;
959 int lock_on_open = 1;
960 #ifdef DEBUG
961 fd_set rfds;
962 #endif
963 char *url_string = NULL;
964 /* The order is important. */
965 enum {
966 #ifdef DEBUG
967 OPT_DEBUG,
968 #endif
969 #ifdef WITH_TCP
970 OPT_HOST, OPT_PORT, OPT_IDENTITY, OPT_KNOWN_HOSTS, OPT_USER,
971 OPT_GET_HOSTKEY, OPT_IPV4, OPT_IPV6, OPT_USE_SSH_AGENT,
972 #endif
973 OPT_URL, OPT_LOCAL, OPT_FORCE_SAVE, OPT_TTYNAME, OPT_TTYTYPE,
974 OPT_DISPLAY, OPT_LC_CTYPE, OPT_LC_MESSAGES, OPT_TIMEOUT, OPT_TRIES,
975 OPT_PINENTRY, OPT_PASSPHRASE, OPT_KEYFILE, OPT_BASE64, OPT_SOCKET,
976 OPT_NOLOCK, OPT_SAVE, OPT_ITERATIONS, OPT_OUTPUT_FD, OPT_INQUIRE,
977 OPT_INQUIRE_FD, OPT_INQUIRE_LINE, OPT_NO_STATUS, OPT_NAME,
978 OPT_VERSION, OPT_HELP, OPT_CIPHER, OPT_NO_PINENTRY,
979 #ifdef HAVE_LIBREADLINE
980 OPT_INTERACTIVE,
981 #endif
983 const struct option long_opts[] = {
984 #ifdef DEBUG
985 { "debug", 1, 0, 0 },
986 #endif
987 #ifdef WITH_TCP
988 { "host", 1, 0, 'h' },
989 { "port", 1, 0, 'p' },
990 { "identity", 1, 0, 'i' },
991 { "known-hosts", 1, 0, 'k' },
992 { "user", 1, 0, 'u' },
993 { "get-hostkey", 0, 0, 'g' },
994 { "ipv4", 0, 0, '4' },
995 { "ipv6", 0, 0, '6' },
996 { "no-ssh-agent", 0, 0, 0 },
997 #endif
998 { "url", 1, 0, 0 },
999 { "local-pinentry", 0, 0 },
1000 { "force-save", 0, 0 },
1001 { "ttyname", 1, 0, 'y' },
1002 { "ttytype", 1, 0, 't' },
1003 { "display", 1, 0, 'd' },
1004 { "lc-ctype", 1, 0, 0 },
1005 { "lc-messages", 1, 0, 0 },
1006 { "timeout", 1, 0, 0 },
1007 { "tries", 1, 0, 0 },
1008 { "pinentry", 1, 0, 0 },
1009 { "passphrase", 1, 0, 'P' },
1010 { "key-file", 1, 0, 0 },
1011 { "base64", 0, 0, 0 },
1012 { "socket", 1, 0, 0 },
1013 { "no-lock", 0, 0, 0 },
1014 { "save", 0, 0, 'S' },
1015 { "iterations", 1, 0, 'I' },
1016 { "output-fd", 1, 0, 0 },
1017 { "inquire", 1, 0, 0 },
1018 { "inquire-fd", 1, 0, 0 },
1019 { "inquire-line", 1, 0, 'L' },
1020 { "no-status", 0, 0, 0 },
1021 { "name", 1, 0, 'n' },
1022 { "version", 0, 0, 0 },
1023 { "help", 0, 0, 0 },
1024 { "cipher", 1, 0, 0 },
1025 { "no-pinentry", 0, 0, 0 },
1026 #ifdef HAVE_LIBREADLINE
1027 { "interactive", 0, 0 },
1028 #endif
1029 { 0, 0, 0, 0}
1031 #ifdef WITH_TCP
1032 const char *optstring = "L:46h:p:i:k:u:gy:t:d:P:I:Sn:";
1033 #else
1034 const char *optstring = "L:y:t:d:P:I:Sn:";
1035 #endif
1036 int opt_index = 0;
1038 #ifdef ENABLE_NLS
1039 setlocale(LC_ALL, "");
1040 bindtextdomain("libpwmd", LOCALEDIR);
1041 #endif
1043 #ifdef HAVE_LIBREADLINE
1044 if (!strcmp(basename(argv[0]), "pwmsh"))
1045 interactive = 1;
1046 #endif
1048 iter = -2;
1050 while ((opt = getopt_long(argc, argv, optstring, long_opts, &opt_index)) != -1) {
1051 switch (opt) {
1052 /* Handle long options without a short option part. */
1053 case 0:
1054 switch (opt_index) {
1055 #ifdef DEBUG
1056 case OPT_DEBUG:
1057 method = atoi(optarg);
1059 if (method > 3)
1060 method = 3;
1061 break;
1062 #endif
1063 #ifdef WITH_TCP
1064 case OPT_USE_SSH_AGENT:
1065 use_ssh_agent = 0;
1066 break;
1067 #endif
1068 case OPT_KEYFILE:
1069 keyfile = optarg;
1070 break;
1071 case OPT_BASE64:
1072 base64 = 1;
1073 break;
1074 case OPT_NOLOCK:
1075 lock_on_open = 0;
1076 break;
1077 case OPT_URL:
1078 url_string = optarg;
1079 break;
1080 case OPT_LOCAL:
1081 local_pin = 1;
1082 break;
1083 case OPT_FORCE_SAVE:
1084 save = force_save = 1;
1085 break;
1086 case OPT_LC_CTYPE:
1087 lcctype = pwmd_strdup(optarg);
1088 break;
1089 case OPT_LC_MESSAGES:
1090 lcmessages = pwmd_strdup(optarg);
1091 break;
1092 case OPT_TIMEOUT:
1093 timeout = atoi(optarg);
1094 break;
1095 case OPT_TRIES:
1096 tries = atoi(optarg);
1097 local_tries = tries;
1098 break;
1099 case OPT_SOCKET:
1100 socketpath = pwmd_strdup(optarg);
1101 break;
1102 case OPT_INQUIRE:
1103 inquire = optarg;
1104 break;
1105 case OPT_INQUIRE_FD:
1106 inquirefd = atoi(optarg);
1107 inquirefp = fdopen(inquirefd, "r");
1109 if (!inquirefp) {
1110 pwmd_free(password);
1111 err(EXIT_FAILURE, "%i", inquirefd);
1113 break;
1114 case OPT_OUTPUT_FD:
1115 outfd = atoi(optarg);
1116 outfp = fdopen(outfd, "w");
1118 if (!outfp) {
1119 pwmd_free(password);
1120 err(EXIT_FAILURE, "%i", outfd);
1122 break;
1123 case OPT_NO_STATUS:
1124 show_status = 0;
1125 break;
1126 case OPT_VERSION:
1127 pwmd_free(password);
1128 printf("%s (pwmc)\n%s\n\n"
1129 "Compile-time features:\n"
1130 #ifdef WITH_TCP
1131 "+SSH "
1132 #else
1133 "-SSH "
1134 #endif
1135 #ifdef WITH_PINENTRY
1136 "+PINENTRY "
1137 #else
1138 "-PINENTRY "
1139 #endif
1140 #ifdef WITH_QUALITY
1141 "+CRACK "
1142 #else
1143 "-CRACK "
1144 #endif
1145 #ifdef MEM_DEBUG
1146 "+MEM_DEBUG "
1147 #else
1148 "-MEM_DEBUG "
1149 #endif
1150 #ifdef HAVE_LIBREADLINE
1151 "+INTERACTIVE "
1152 #else
1153 "-INTERACTIVE "
1154 #endif
1155 "\n"
1156 , PACKAGE_STRING, PACKAGE_BUGREPORT);
1157 exit(EXIT_SUCCESS);
1158 case OPT_PINENTRY:
1159 pinentry_path = optarg;
1160 break;
1161 case OPT_HELP:
1162 usage(argv[0], EXIT_SUCCESS);
1163 case OPT_CIPHER:
1164 cipher = optarg;
1165 break;
1166 case OPT_NO_PINENTRY:
1167 no_pinentry = 1;
1168 break;
1169 #ifdef HAVE_LIBREADLINE
1170 case OPT_INTERACTIVE:
1171 interactive = 1;
1172 break;
1173 #endif
1174 default:
1175 usage(argv[0], EXIT_FAILURE);
1178 break;
1179 #ifdef WITH_TCP
1180 case '4':
1181 prot = PWMD_IPV4;
1182 break;
1183 case '6':
1184 prot = PWMD_IPV6;
1185 break;
1186 case 'h':
1187 host = pwmd_strdup(optarg);
1188 break;
1189 case 'p':
1190 port = atoi(optarg);
1191 break;
1192 case 'i':
1193 ident = pwmd_strdup(optarg);
1194 break;
1195 case 'u':
1196 username = pwmd_strdup(optarg);
1197 break;
1198 case 'k':
1199 known_hosts = pwmd_strdup(optarg);
1200 break;
1201 case 'g':
1202 get = 1;
1203 break;
1204 #endif
1205 case 'L':
1206 inquire_line = optarg;
1207 break;
1208 case 'y':
1209 tty = optarg;
1210 break;
1211 case 't':
1212 ttytype = optarg;
1213 break;
1214 case 'd':
1215 display = optarg;
1216 break;
1217 case 'S':
1218 save = 1;
1219 break;
1220 case 'I':
1221 iter = strtol(optarg, NULL, 10);
1222 have_iter = 1;
1223 break;
1224 case 'P':
1225 password = pwmd_strdup(optarg);
1226 memset(optarg, 0, strlen(optarg));
1227 break;
1228 case 'n':
1229 clientname = optarg;
1230 break;
1231 default:
1232 pwmd_free(password);
1233 usage(argv[0], EXIT_FAILURE);
1237 #ifdef HAVE_LIBREADLINE
1238 if (interactive && !isatty(STDIN_FILENO)) {
1239 pwmd_free(password);
1240 usage(argv[0], EXIT_FAILURE);
1242 #endif
1244 #ifdef DEBUG
1245 if (!url_string) {
1246 #endif
1247 #ifdef WITH_TCP
1248 if (host && !get && !ident) {
1249 pwmd_free(password);
1250 usage(argv[0], EXIT_FAILURE);
1253 if (get && !host) {
1254 pwmd_free(password);
1255 usage(argv[0], EXIT_FAILURE);
1257 #endif
1258 #ifdef DEBUG
1260 #endif
1262 filename = argv[optind];
1263 pwmd_init();
1264 pwm = pwmd_new(clientname);
1265 #ifdef DEBUG
1266 FD_ZERO(&rfds);
1267 #endif
1269 #ifdef WITH_TCP
1270 if (host || is_remote_url(url_string)) {
1271 #ifndef WITH_PINENTRY
1272 no_pinentry = 1;
1273 #endif
1274 #ifndef DEBUG
1275 local_pin = 1;
1276 #endif
1278 if (prot != PWMD_IP_ANY) {
1279 error = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, prot);
1281 if (error)
1282 goto done;
1285 error = pwmd_setopt(pwm, PWMD_OPTION_KNOWNHOST_CB, knownhost_cb);
1287 if (error)
1288 goto done;
1290 error = pwmd_setopt(pwm, PWMD_OPTION_KNOWNHOST_DATA, clientname);
1292 if (error)
1293 goto done;
1295 if (!getenv("SSH_AGENT_PID") || ident)
1296 use_ssh_agent = 0;
1298 error = pwmd_setopt(pwm, PWMD_OPTION_SSH_AGENT, use_ssh_agent);
1300 if (error)
1301 goto done;
1303 fprintf(stderr, N_("Connecting ...\n"));
1305 #ifdef DEBUG
1306 if (method >= 2) {
1307 if (get) {
1308 char *hostkey;
1310 error = pwmd_get_hostkey_async(pwm, host, port);
1312 if (error)
1313 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
1315 error = process_cmd(pwm, &hostkey, 0, 0);
1317 if (error)
1318 goto done;
1320 printf("%s", hostkey);
1321 pwmd_free(hostkey);
1322 pwmd_free(password);
1323 pwmd_close(pwm);
1324 exit(EXIT_SUCCESS);
1327 if (url_string)
1328 error = pwmd_connect_url_async(pwm, url_string);
1329 else
1330 error = pwmd_ssh_connect_async(pwm, host, port, ident, username,
1331 known_hosts);
1333 if (error)
1334 goto done;
1336 error = process_cmd(pwm, NULL, 0, 0);
1338 if (error)
1339 goto done;
1341 else {
1342 #endif
1343 if (get) {
1344 char *hostkey;
1346 error = pwmd_get_hostkey(pwm, host, port, &hostkey);
1348 if (error)
1349 goto done;
1351 printf("%s", hostkey);
1352 pwmd_free(hostkey);
1353 pwmd_free(password);
1354 pwmd_close(pwm);
1355 exit(EXIT_SUCCESS);
1358 if (url_string)
1359 error = pwmd_connect_url(pwm, url_string);
1360 else
1361 error = pwmd_ssh_connect(pwm, host, port, ident, username, known_hosts);
1363 if (error)
1364 goto done;
1365 #ifdef DEBUG
1367 #endif
1369 else {
1370 #endif
1371 if (url_string)
1372 error = pwmd_connect_url(pwm, url_string);
1373 else
1374 error = pwmd_connect(pwm, socketpath);
1376 if (error)
1377 goto done;
1378 #ifdef WITH_TCP
1380 #endif
1382 fprintf(stderr, N_("Connected.\n"));
1383 connected = 1;
1384 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TITLE, NULL);
1385 error = pwmd_socket_type(pwm, &s);
1387 if (error)
1388 goto done;
1390 #ifndef DEBUG
1391 if (s == PWMD_SOCKET_SSH && force_save && !local_pin) {
1392 error = GPG_ERR_WRONG_KEY_USAGE;
1393 goto done;
1395 #endif
1397 if (have_iter) {
1398 error = pwmd_command(pwm, &result, "VERSION");
1400 if (error)
1401 goto done;
1403 pwmd_free(result);
1405 if (iter < 0) {
1406 pwmd_free(password);
1407 pwmd_close(pwm);
1408 usage(argv[0], EXIT_FAILURE);
1412 if (filename && lock_on_open) {
1413 error = pwmd_setopt(pwm, PWMD_OPTION_LOCK_ON_OPEN, 1);
1415 if (error)
1416 goto done;
1419 if (base64) {
1420 error = pwmd_setopt(pwm, PWMD_OPTION_BASE64, 1);
1422 if (error)
1423 goto done;
1426 if (timeout > 0) {
1427 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
1429 if (error)
1430 goto done;
1433 if (no_pinentry) {
1434 error = pwmd_setopt(pwm, PWMD_OPTION_NO_PINENTRY, 1);
1436 if (error)
1437 goto done;
1440 if ((no_pinentry || local_pin) && filename && !keyfile && !password) {
1441 error = pwmd_command(pwm, NULL, "ISCACHED %s", filename);
1443 if (error == GPG_ERR_NOT_FOUND) {
1444 local_password:
1445 if (try++ == local_tries)
1446 goto done;
1448 if (password)
1449 pwmd_free(password);
1451 password = NULL;
1453 if (!no_pinentry) {
1455 if (try > 1)
1456 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, 0);
1458 error = pwmd_getpin(pwm, filename, &password,
1459 try > 1 ? PWMD_PINENTRY_OPEN_FAILED : PWMD_PINENTRY_OPEN);
1461 if (error)
1462 goto done;
1464 if (!password)
1465 password = pwmd_strdup("");
1467 else {
1468 error = get_password(&password,try > 1 ? PWMD_PINENTRY_OPEN_FAILED : PWMD_PINENTRY_OPEN, 0);
1471 if (error)
1472 goto done;
1474 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
1476 if (error)
1477 goto done;
1479 if (try > 1)
1480 goto do_open;
1482 else if (error && error != GPG_ERR_ENOENT)
1483 goto done;
1486 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
1488 if (error)
1489 goto done;
1491 if (!no_pinentry) {
1492 if (pinentry_path) {
1493 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
1495 if (error)
1496 goto done;
1499 if (display) {
1500 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
1502 if (error)
1503 goto done;
1506 if (tty) {
1507 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
1509 if (error)
1510 goto done;
1513 if (ttytype) {
1514 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
1516 if (error)
1517 goto done;
1520 if (lcctype) {
1521 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
1523 if (error)
1524 goto done;
1527 if (lcmessages) {
1528 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
1529 lcmessages);
1531 if (error)
1532 goto done;
1535 if (tries > 0) {
1536 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
1538 if (error)
1539 goto done;
1543 if (show_status) {
1544 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_CB, status_msg_cb);
1546 if (error)
1547 goto done;
1550 do_open:
1551 if (filename) {
1552 fprintf(stderr, N_("Opening data file \"%s\" ...\n"), filename);
1554 #ifdef WITH_TCP
1555 if (keyfile && (!local_pin || host || is_remote_url(url_string))) {
1556 struct inquire_s inq;
1558 memset(&inq, 0, sizeof(inq));
1559 inq.fd = open(keyfile, O_RDONLY);
1561 if (inq.fd == -1) {
1562 error = gpg_error_from_syserror();
1563 goto done;
1566 inq.line = pwmd_calloc(1, ASSUAN_LINELENGTH);
1568 if (!inq.line) {
1569 error = GPG_ERR_ENOMEM;
1570 goto done;
1573 in_keyfile = 1;
1574 error = pwmd_open_inquire(pwm, filename, inquire_cb, &inq);
1575 in_keyfile = 0;
1576 pwmd_free(inq.line);
1577 close(inq.fd);
1579 if (error)
1580 goto done;
1582 goto read_command;
1584 #endif
1585 #ifdef DEBUG
1586 switch (method) {
1587 case 0:
1588 error = pwmd_open(pwm, filename);
1589 break;
1590 case 1:
1591 error = pwmd_open2(pwm, filename);
1592 break;
1593 case 2:
1594 error = pwmd_open_async(pwm, filename);
1596 break;
1597 case 3:
1598 error = pwmd_open_async2(pwm, filename);
1599 break;
1602 if (error && (local_pin || no_pinentry) &&
1603 error == GPG_ERR_INV_PASSPHRASE)
1604 goto local_password;
1606 if (error)
1607 goto done;
1609 if (method >= 2)
1610 error = process_cmd(pwm, &result, 0, 0);
1611 #else
1612 #ifdef WITH_PINENTRY
1613 if (local_pin)
1614 error = pwmd_open2(pwm, filename);
1615 else
1616 #endif
1617 error = pwmd_open(pwm, filename);
1619 if (error && (local_pin || no_pinentry) &&
1620 error == GPG_ERR_INV_PASSPHRASE)
1621 goto local_password;
1622 #endif
1624 if (error)
1625 goto done;
1628 #ifdef WITH_TCP
1629 read_command:
1630 #endif
1631 #ifdef HAVE_LIBREADLINE
1632 if (interactive) {
1633 if (!force_save)
1634 save = 0;
1636 error = do_interactive();
1637 goto do_exit;
1639 #endif
1641 if (inquire) {
1642 int fd = fileno(inquirefp);
1644 if (fd == -1) {
1645 error = gpg_error_from_syserror();
1646 goto done;
1649 error = send_inquire(fd, inquire, inquire_line);
1650 close(fd);
1651 goto done;
1654 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
1655 ssize_t n;
1657 for (;;) {
1658 error = process_cmd(pwm, NULL, 1, 0);
1660 if (error)
1661 goto done;
1663 n = read(STDIN_FILENO, command, sizeof(command));
1665 if (n == -1) {
1666 if (errno == EAGAIN)
1667 continue;
1669 error = gpg_error_from_errno(errno);
1670 goto done;
1672 else if (!n)
1673 goto done;
1675 p = command;
1676 command[n] = 0;
1677 break;
1680 if (!p || !*p)
1681 goto done;
1683 if (strcasecmp(p, "BYE") == 0)
1684 goto done;
1686 error = pwmd_command(pwm, &result, command);
1688 if (error)
1689 goto done;
1691 if (result) {
1692 fwrite(result, 1, strlen(result), outfp);
1693 fflush(outfp);
1694 pwmd_free(result);
1697 done:
1698 if (password)
1699 pwmd_free(password);
1701 password = NULL;
1703 if (!error)
1704 error = finalize();
1706 do_exit:
1707 memset(command, 0, sizeof(command));
1709 if (error) {
1710 show_error(error);
1711 ret = EXIT_FAILURE;
1714 pwmd_close(pwm);
1716 if (connected)
1717 fprintf(stderr, N_("Connection closed.\n"));
1719 if (socketpath)
1720 pwmd_free(socketpath);
1722 exit(ret);