Return GPG_ERR_BAD_CERT if the host key cannot be verified rather than
[libpwmd.git] / src / pwmc.c
blob69e5c3ca50ddd5b9efb7ef051124f1fed62d884e
1 #define DEBUG 1
2 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 /*
4 Copyright (C) 2007-2009 Ben Kibbey <bjk@luxsci.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <libpwmd.h>
27 #include <assuan.h>
28 #ifdef DEBUG
29 #include <sys/select.h>
30 #include <fcntl.h>
31 #endif
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
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 #include "gettext.h"
50 #define N_(msgid) gettext(msgid)
52 #include "mem.h"
54 #define DEFAULT_PORT 22
55 pwm_t *pwm;
57 static void show_error(gpg_error_t error)
59 fprintf(stderr, "ERR %i %s\n", gpg_err_code(error), pwmd_strerror(error));
62 static void usage(const char *pn, int status)
64 fprintf(status == EXIT_FAILURE ? stderr : stdout, N_(
65 "Read a PWMD protocol command from standard input.\n\n"
66 "Usage: pwmc [options] [file]\n"
67 #ifdef DEBUG
68 " --debug <N>\n"
69 " pinentry method (0=pwmd, 1=libpwmd, 2=pwmd async, "
70 "3=libpwmd async)\n"
71 #endif
72 " --tries <N>\n"
73 " number of pinentry tries before failing (3)\n"
74 #ifdef WITH_TCP
75 " --host, -h <hostname>\n"
76 " connect to the specified hostname\n"
77 " --port\n"
78 " alterate port (22)\n"
79 " --user\n"
80 " SSH username (default is the invoking user)\n"
81 " --identity, -i <filename>\n"
82 " SSH identity file\n"
83 " --known-hosts, -k <filename>\n"
84 " known host's file (for server validation)\n"
85 " --get-hostkey, -g\n"
86 " retrieve the remote SSH host key and exit\n"
87 " --ipv4, -4\n"
88 " try connecting via IPv4 only\n"
89 " --ipv6, -6\n"
90 " try connecting via IPv6 only\n"
91 #endif
92 " --timeout <seconds>\n"
93 " pinentry timeout\n"
94 " --no-status\n"
95 " disable showing of status messages from the server\n"
96 " --name, -n <string>\n"
97 " set the client name\n"
98 " --socket <filename>\n"
99 " local socket to connect to (~/.pwmd/socket)\n"
100 " --passphrase, -P <string>\n"
101 " passphrase to use (disables pinentry use)\n"
102 " --pinentry <path>\n"
103 " the full path to the pinentry binary (server default)\n"
104 " --ttyname, -y <path>\n"
105 " tty that pinentry will use\n"
106 " --ttytype, -t <string>\n"
107 " pinentry terminal type (default is TERM)\n"
108 " --display, -d\n"
109 " pinentry display (default is DISPLAY)\n"
110 " --lc-ctype <string>\n"
111 " locale setting for pinentry\n"
112 " --lc-messages <string>\n"
113 " locale setting for pinentry\n"
114 " --output-fd <FD>\n"
115 " redirect command output to the specified file descriptor\n"
116 " --inquire-fd <FD>\n"
117 " read inquire data from the specified file descriptor\n"
118 " --save, -S\n"
119 " send the SAVE command before exiting\n"
120 " --iterations, -I <N>\n"
121 " encrypt with the specified number of iterations when saving\n"
122 " --version\n"
123 " --help\n"));
124 exit(status);
127 struct inquire_s {
128 FILE *fp;
129 char *data;
132 static gpg_error_t do_inquire(void *data, const char *keyword, gpg_error_t rc,
133 char **result, size_t *result_len)
135 int c;
136 static char buf[ASSUAN_LINELENGTH];
137 char *p;
138 size_t len = 0;
139 struct inquire_s *inq = (struct inquire_s *)data;
141 if (rc) {
142 memset(buf, 0, sizeof(buf));
143 return rc;
146 buf[0] = 0;
147 p = buf;
149 if (inq->data) {
150 snprintf(buf, sizeof(buf), "%s", inq->data);
151 pwmd_free(inq->data);
152 inq->data = NULL;
153 len = strlen(buf);
154 p = buf + len;
157 while ((c = fgetc(inq->fp)) != EOF) {
158 if (len == sizeof(buf)) {
159 ungetc(c, inq->fp);
160 break;
163 *p++ = c;
164 len++;
167 if (!buf[0]) {
168 memset(buf, 0, sizeof(buf));
169 return GPG_ERR_EOF;
172 *result = buf;
173 *result_len = len;
174 return 0;
177 static int status_msg_cb(void *data, const char *line)
179 fprintf(stderr, "%s\n", line);
180 return 0;
183 static gpg_error_t process_cmd(pwm_t *pwm, char **result, int input)
185 gpg_error_t rc;
186 pwmd_async_t s;
188 do {
189 int i, n;
190 fd_set rfds;
191 int nfds = 5;
192 pwmd_fd_t pfds[nfds];
194 FD_ZERO(&rfds);
195 rc = pwmd_get_fds(pwm, pfds, &nfds);
197 if (rc)
198 return rc;
200 if (!nfds)
201 return 0;
203 for (i = 0, n = 0; i < nfds; i++) {
204 FD_SET(pfds[i].fd, &rfds);
205 n = pfds[i].fd > n ? pfds[i].fd : n;
208 if (input)
209 FD_SET(STDIN_FILENO, &rfds);
211 nfds = select(n+1, &rfds, NULL, NULL, NULL);
213 if (nfds == -1) {
214 rc = gpg_error_from_errno(errno);
215 return rc;
218 if (input && FD_ISSET(STDIN_FILENO, &rfds))
219 return 0;
221 s = pwmd_process(pwm, &rc, result);
222 } while (s == ASYNC_PROCESS);
224 return rc;
227 int main(int argc, char *argv[])
229 int opt;
230 char *password = NULL;
231 char *filename = NULL;
232 char *socketpath = NULL;
233 char command[ASSUAN_LINELENGTH], *p;
234 int ret = EXIT_SUCCESS;
235 gpg_error_t error;
236 char *result = NULL;
237 int save = 0;
238 char *pinentry_path = NULL;
239 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
240 *lcmessages = NULL;
241 int outfd = STDOUT_FILENO;
242 FILE *outfp = stdout;
243 int inquirefd = STDIN_FILENO;
244 FILE *inquirefp = stdin;
245 int show_status = 1;
246 char *clientname = "pwmc";
247 char *inquire = NULL;
248 long iter = -2;
249 int have_iter = 0;
250 int timeout = 0;
251 #ifdef WITH_TCP
252 char *host = NULL;
253 int port = DEFAULT_PORT;
254 char *username = NULL;
255 char *ident = NULL;
256 char *known_hosts = NULL;
257 int get = 0;
258 int prot = PWMD_IP_ANY;
259 #endif
260 int tries = 0;
261 #ifdef DEBUG
262 int method = 0;
263 fd_set rfds;
264 #endif
265 /* The order is important. */
266 enum {
267 #ifdef DEBUG
268 OPT_DEBUG,
269 #endif
270 #ifdef WITH_TCP
271 OPT_HOST, OPT_PORT, OPT_IDENTITY, OPT_KNOWN_HOSTS, OPT_USER,
272 OPT_GET_HOSTKEY, OPT_IPV4, OPT_IPV6,
273 #endif
274 OPT_TTYNAME, OPT_TTYTYPE, OPT_DISPLAY, OPT_LC_CTYPE, OPT_LC_MESSAGES,
275 OPT_TIMEOUT, OPT_TRIES, OPT_PINENTRY,
276 OPT_PASSPHRASE, OPT_SOCKET, OPT_SAVE, OPT_ITERATIONS, OPT_OUTPUT_FD,
277 OPT_INQUIRE_FD, OPT_NO_STATUS, OPT_NAME, OPT_VERSION, OPT_HELP,
279 const struct option long_opts[] = {
280 #ifdef DEBUG
281 { "debug", 1, 0, 0 },
282 #endif
283 #ifdef WITH_TCP
284 { "host", 1, 0, 'h' },
285 { "port", 1, 0, 'p' },
286 { "identity", 1, 0, 'i' },
287 { "known-hosts", 1, 0, 'k' },
288 { "user", 1, 0, 'u' },
289 { "get-hostkey", 0, 0, 'g' },
290 { "ipv4", 0, 0, '4' },
291 { "ipv6", 0, 0, '6' },
292 #endif
293 { "ttyname", 1, 0, 'y' },
294 { "ttytype", 1, 0, 't' },
295 { "display", 1, 0, 'd' },
296 { "lc-ctype", 1, 0, 0 },
297 { "lc-messages", 1, 0, 0 },
298 { "timeout", 1, 0, 0 },
299 { "tries", 1, 0, 0 },
300 { "pinentry", 1, 0, 0 },
301 { "passphrase", 1, 0, 'P' },
302 { "socket", 1, 0, 0 },
303 { "save", 0, 0, 'S' },
304 { "iterations", 1, 0, 'I' },
305 { "output-fd", 1, 0, 0 },
306 { "inquire-fd", 1, 0, 0 },
307 { "no-status", 0, 0, 0 },
308 { "name", 1, 0, 'n' },
309 { "version", 0, 0, 0 },
310 { "help", 0, 0, 0 },
311 { 0, 0, 0, 0}
313 #ifdef WITH_TCP
314 const char *optstring = "46h:p:i:k:u:gy:t:d:P:I:Sn:";
315 #else
316 const char *optstring = "y:t:d:P:I:Sn:";
317 #endif
318 int opt_index = 0;
320 #ifdef ENABLE_NLS
321 setlocale(LC_ALL, "");
322 bindtextdomain("libpwmd", LOCALEDIR);
323 #endif
325 while ((opt = getopt_long(argc, argv, optstring, long_opts, &opt_index)) != -1) {
326 switch (opt) {
327 /* Handle long options without a short option part. */
328 case 0:
329 switch (opt_index) {
330 #ifdef DEBUG
331 case OPT_DEBUG:
332 method = atoi(optarg);
334 if (method > 3)
335 method = 3;
336 break;
337 #endif
338 case OPT_LC_CTYPE:
339 lcctype = pwmd_strdup(optarg);
340 break;
341 case OPT_LC_MESSAGES:
342 lcmessages = pwmd_strdup(optarg);
343 break;
344 case OPT_TIMEOUT:
345 timeout = atoi(optarg);
346 break;
347 case OPT_TRIES:
348 tries = atoi(optarg);
349 break;
350 case OPT_SOCKET:
351 socketpath = pwmd_strdup(optarg);
352 break;
353 case OPT_INQUIRE_FD:
354 inquirefd = atoi(optarg);
355 inquirefp = fdopen(inquirefd, "r");
357 if (!inquirefp) {
358 pwmd_free(password);
359 err(EXIT_FAILURE, "%i", inquirefd);
361 break;
362 case OPT_OUTPUT_FD:
363 outfd = atoi(optarg);
364 outfp = fdopen(outfd, "w");
366 if (!outfp) {
367 pwmd_free(password);
368 err(EXIT_FAILURE, "%i", outfd);
370 break;
371 case OPT_NO_STATUS:
372 show_status = 0;
373 break;
374 case OPT_VERSION:
375 pwmd_free(password);
376 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
377 exit(EXIT_SUCCESS);
378 case OPT_HELP:
379 usage(argv[0], EXIT_SUCCESS);
380 default:
381 usage(argv[0], EXIT_FAILURE);
384 break;
385 #ifdef WITH_TCP
386 case '4':
387 prot = PWMD_IPV4;
388 break;
389 case '6':
390 prot = PWMD_IPV6;
391 break;
392 case 'h':
393 host = pwmd_strdup(optarg);
394 break;
395 case 'p':
396 port = atoi(optarg);
397 break;
398 case 'i':
399 ident = pwmd_strdup(optarg);
400 break;
401 case 'u':
402 username = pwmd_strdup(optarg);
403 break;
404 case 'k':
405 known_hosts = pwmd_strdup(optarg);
406 break;
407 case 'g':
408 get = 1;
409 break;
410 #endif
411 case 'y':
412 tty = optarg;
413 break;
414 case 't':
415 ttytype = optarg;
416 break;
417 case 'd':
418 display = optarg;
419 break;
420 case 'S':
421 save = 1;
422 break;
423 case 'I':
424 iter = strtol(optarg, NULL, 10);
425 have_iter = 1;
426 break;
427 case 'P':
428 password = pwmd_strdup(optarg);
429 memset(optarg, 0, strlen(optarg));
430 break;
431 case 'n':
432 clientname = optarg;
433 break;
434 default:
435 pwmd_free(password);
436 usage(argv[0], EXIT_FAILURE);
440 #ifdef WITH_TCP
441 if (host && !get && (!known_hosts || !ident)) {
442 pwmd_free(password);
443 usage(argv[0], EXIT_FAILURE);
446 if (get && !host) {
447 pwmd_free(password);
448 usage(argv[0], EXIT_FAILURE);
450 #endif
452 filename = argv[optind];
453 pwmd_init();
454 pwm = pwmd_new(clientname);
455 #ifdef DEBUG
456 FD_ZERO(&rfds);
457 #endif
459 #ifdef WITH_TCP
460 if (host) {
461 if (prot != PWMD_IP_ANY) {
462 error = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, prot);
464 if (error)
465 goto done;
468 #ifdef DEBUG
469 if (method >= 2) {
470 if (get) {
471 char *hostkey;
473 error = pwmd_get_hostkey_async(pwm, host, port);
475 if (error)
476 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
478 error = process_cmd(pwm, &hostkey, 0);
480 if (error)
481 goto done;
483 printf("%s\n", hostkey);
484 pwmd_free(hostkey);
485 pwmd_free(password);
486 pwmd_close(pwm);
487 exit(EXIT_SUCCESS);
490 error = pwmd_ssh_connect_async(pwm, host, port, ident, username,
491 known_hosts);
493 if (error)
494 goto done;
496 error = process_cmd(pwm, NULL, 0);
498 if (error)
499 goto done;
501 else {
502 #endif
503 if (get) {
504 char *hostkey;
506 error = pwmd_get_hostkey(pwm, host, port, &hostkey);
508 if (error)
509 goto done;
511 printf("%s\n", hostkey);
512 pwmd_free(hostkey);
513 pwmd_free(password);
514 pwmd_close(pwm);
515 exit(EXIT_SUCCESS);
518 error = pwmd_ssh_connect(pwm, host, port, ident, username, known_hosts);
520 if (error)
521 goto done;
522 #ifdef DEBUG
524 #endif
526 else {
527 #endif
528 error = pwmd_connect(pwm, socketpath);
530 if (error)
531 goto done;
532 #ifdef WITH_TCP
534 #endif
536 if (have_iter) {
537 error = pwmd_command(pwm, &result, "VERSION");
539 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD)
540 goto done;
542 pwmd_free(result);
544 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
545 if (iter < -1) {
546 pwmd_free(password);
547 pwmd_close(pwm);
548 usage(argv[0], EXIT_FAILURE);
551 else {
552 /* pwmd version 2 or later. */
553 if (iter < 0) {
554 pwmd_free(password);
555 pwmd_close(pwm);
556 usage(argv[0], EXIT_FAILURE);
561 if (timeout > 0) {
562 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
564 if (error)
565 goto done;
568 if (password) {
569 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
571 if (error)
572 goto done;
574 else {
575 if (pinentry_path) {
576 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
578 if (error)
579 goto done;
582 if (display) {
583 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
585 if (error)
586 goto done;
589 if (tty) {
590 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
592 if (error)
593 goto done;
596 if (ttytype) {
597 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
599 if (error)
600 goto done;
603 if (lcctype) {
604 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
606 if (error)
607 goto done;
610 if (lcmessages) {
611 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
612 lcmessages);
614 if (error)
615 goto done;
618 if (tries > 0) {
619 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
621 if (error)
622 goto done;
626 if (show_status) {
627 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_CB, status_msg_cb);
629 if (error)
630 goto done;
633 if (filename) {
634 #ifdef DEBUG
635 switch (method) {
636 case 0:
637 error = pwmd_open(pwm, filename);
638 break;
639 case 1:
640 error = pwmd_open2(pwm, filename);
641 break;
642 case 2:
643 error = pwmd_open_async(pwm, filename);
645 break;
646 case 3:
647 error = pwmd_open_async2(pwm, filename);
648 break;
651 if (error)
652 goto done;
654 if (method >= 2)
655 error = process_cmd(pwm, &result, 0);
656 #else
657 error = pwmd_open(pwm, filename);
658 #endif
660 if (error)
661 goto done;
664 if (filename) {
665 error = pwmd_command(pwm, &result, "LOCK");
667 if (error)
668 goto done;
671 #ifdef DEBUG
672 if (method >= 2) {
673 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
675 for (;;) {
676 ssize_t n;
678 error = process_cmd(pwm, NULL, 1);
680 if (error)
681 goto done;
683 n = read(STDIN_FILENO, command, sizeof(command));
685 if (n == -1) {
686 if (errno == EAGAIN)
687 continue;
689 error = gpg_error_from_errno(errno);
690 goto done;
693 if (n && command[strlen(command)-1] == '\n')
694 command[strlen(command)-1] = 0;
696 command[n] = 0;
697 p = command;
698 break;
701 else
702 p = fgets(command, sizeof(command), stdin);
703 #else
704 p = fgets(command, sizeof(command), stdin);
705 #endif
707 if (!p || !*p)
708 goto done;
711 * This is a known INQUIRE command. We use pwmd_inquire() to send the
712 * data from the do_inquire() callback function.
714 if (strncasecmp(p, "STORE ", 6) == 0) {
715 p += 6;
716 inquire = (char *)"STORE";
718 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
719 p += 7;
720 inquire = (char *)"IMPORT";
723 if (inquire) {
724 struct inquire_s *inq = (struct inquire_s *)pwmd_malloc(sizeof(struct inquire_s));
726 if (!inq) {
727 error = gpg_error_from_errno(ENOMEM);
728 goto done;
731 inq->data = pwmd_strdup(p);
732 inq->fp = inquirefp;
733 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
734 pwmd_free(inq);
735 goto done;
738 if (strcasecmp(p, "BYE") == 0)
739 goto done;
741 error = pwmd_command(pwm, &result, command);
742 memset(command, 0, sizeof(command));
744 if (error)
745 goto done;
747 if (result) {
748 fwrite(result, 1, strlen(result), outfp);
749 pwmd_free(result);
752 done:
753 memset(command, 0, sizeof(command));
754 pwmd_free(password);
756 if (!error && save) {
757 if (iter != -2) {
758 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
760 if (error)
761 goto done;
764 #ifdef DEBUG
765 switch (method) {
766 case 0:
767 error = pwmd_save(pwm);
768 break;
769 case 1:
770 error = pwmd_save2(pwm);
771 break;
772 case 2:
773 error = pwmd_save_async(pwm);
774 break;
775 case 3:
776 error = pwmd_save_async2(pwm);
777 break;
780 if (!error && method >= 2)
781 error = process_cmd(pwm, NULL, 0);
783 #else
784 error = pwmd_save(pwm);
785 #endif
788 if (!error && filename)
789 error = pwmd_command(pwm, &result, "UNLOCK");
791 if (error) {
792 show_error(error);
793 ret = EXIT_FAILURE;
796 pwmd_close(pwm);
798 if (socketpath)
799 pwmd_free(socketpath);
801 exit(ret);