Commit 1b59e7d contained a typo that didn't really fix pwmd_command().
[libpwmd.git] / src / pwmc.c
blob0c3ce261a48804e287a673c6490bc961a16fb478
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 for (i = 0, n = 0; i < nfds; i++) {
201 FD_SET(pfds[i].fd, &rfds);
202 n = pfds[i].fd > n ? pfds[i].fd : n;
205 if (input)
206 FD_SET(STDIN_FILENO, &rfds);
208 nfds = select(n+1, &rfds, NULL, NULL, NULL);
210 if (nfds == -1) {
211 rc = gpg_error_from_errno(errno);
212 return rc;
215 if (input && FD_ISSET(STDIN_FILENO, &rfds))
216 return 0;
218 s = pwmd_process(pwm, &rc, result);
219 } while (s == ASYNC_PROCESS);
221 return rc;
224 int main(int argc, char *argv[])
226 int opt;
227 char *password = NULL;
228 char *filename = NULL;
229 char *socketpath = NULL;
230 char command[ASSUAN_LINELENGTH], *p;
231 int ret = EXIT_SUCCESS;
232 gpg_error_t error;
233 char *result = NULL;
234 int save = 0;
235 char *pinentry_path = NULL;
236 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
237 *lcmessages = NULL;
238 int outfd = STDOUT_FILENO;
239 FILE *outfp = stdout;
240 int inquirefd = STDIN_FILENO;
241 FILE *inquirefp = stdin;
242 int show_status = 1;
243 char *clientname = "pwmc";
244 char *inquire = NULL;
245 long iter = -2;
246 int have_iter = 0;
247 int timeout = 0;
248 #ifdef WITH_TCP
249 char *host = NULL;
250 int port = DEFAULT_PORT;
251 char *username = NULL;
252 char *ident = NULL;
253 char *known_hosts = NULL;
254 int get = 0;
255 int prot = PWMD_IP_ANY;
256 #endif
257 int tries = 0;
258 #ifdef DEBUG
259 int method = 0;
260 fd_set rfds;
261 #endif
262 /* The order is important. */
263 enum {
264 #ifdef DEBUG
265 OPT_DEBUG,
266 #endif
267 #ifdef WITH_TCP
268 OPT_HOST, OPT_PORT, OPT_IDENTITY, OPT_KNOWN_HOSTS, OPT_USER,
269 OPT_GET_HOSTKEY, OPT_IPV4, OPT_IPV6,
270 #endif
271 OPT_TTYNAME, OPT_TTYTYPE, OPT_DISPLAY, OPT_LC_CTYPE, OPT_LC_MESSAGES,
272 OPT_TIMEOUT, OPT_TRIES, OPT_PINENTRY,
273 OPT_PASSPHRASE, OPT_SOCKET, OPT_SAVE, OPT_ITERATIONS, OPT_OUTPUT_FD,
274 OPT_INQUIRE_FD, OPT_NO_STATUS, OPT_NAME, OPT_VERSION, OPT_HELP,
276 const struct option long_opts[] = {
277 #ifdef DEBUG
278 { "debug", 1, 0, 0 },
279 #endif
280 #ifdef WITH_TCP
281 { "host", 1, 0, 'h' },
282 { "port", 1, 0, 'p' },
283 { "identity", 1, 0, 'i' },
284 { "known-hosts", 1, 0, 'k' },
285 { "user", 1, 0, 'u' },
286 { "get-hostkey", 0, 0, 'g' },
287 { "ipv4", 0, 0, '4' },
288 { "ipv6", 0, 0, '6' },
289 #endif
290 { "ttyname", 1, 0, 'y' },
291 { "ttytype", 1, 0, 't' },
292 { "display", 1, 0, 'd' },
293 { "lc-ctype", 1, 0, 0 },
294 { "lc-messages", 1, 0, 0 },
295 { "timeout", 1, 0, 0 },
296 { "tries", 1, 0, 0 },
297 { "pinentry", 1, 0, 0 },
298 { "passphrase", 1, 0, 'P' },
299 { "socket", 1, 0, 0 },
300 { "save", 0, 0, 'S' },
301 { "iterations", 1, 0, 'I' },
302 { "output-fd", 1, 0, 0 },
303 { "inquire-fd", 1, 0, 0 },
304 { "no-status", 0, 0, 0 },
305 { "name", 1, 0, 'n' },
306 { "version", 0, 0, 0 },
307 { "help", 0, 0, 0 },
308 { 0, 0, 0, 0}
310 #ifdef WITH_TCP
311 const char *optstring = "46h:p:i:k:u:gy:t:d:P:I:Sn:";
312 #else
313 const char *optstring = "y:t:d:P:I:Sn:";
314 #endif
315 int opt_index = 0;
317 #ifdef ENABLE_NLS
318 setlocale(LC_ALL, "");
319 bindtextdomain("libpwmd", LOCALEDIR);
320 #endif
322 while ((opt = getopt_long(argc, argv, optstring, long_opts, &opt_index)) != -1) {
323 switch (opt) {
324 /* Handle long options without a short option part. */
325 case 0:
326 switch (opt_index) {
327 #ifdef DEBUG
328 case OPT_DEBUG:
329 method = atoi(optarg);
331 if (method > 3)
332 method = 3;
333 break;
334 #endif
335 case OPT_LC_CTYPE:
336 lcctype = pwmd_strdup(optarg);
337 break;
338 case OPT_LC_MESSAGES:
339 lcmessages = pwmd_strdup(optarg);
340 break;
341 case OPT_TIMEOUT:
342 timeout = atoi(optarg);
343 break;
344 case OPT_TRIES:
345 tries = atoi(optarg);
346 break;
347 case OPT_SOCKET:
348 socketpath = pwmd_strdup(optarg);
349 break;
350 case OPT_INQUIRE_FD:
351 inquirefd = atoi(optarg);
352 inquirefp = fdopen(inquirefd, "r");
354 if (!inquirefp) {
355 pwmd_free(password);
356 err(EXIT_FAILURE, "%i", inquirefd);
358 break;
359 case OPT_OUTPUT_FD:
360 outfd = atoi(optarg);
361 outfp = fdopen(outfd, "w");
363 if (!outfp) {
364 pwmd_free(password);
365 err(EXIT_FAILURE, "%i", outfd);
367 break;
368 case OPT_NO_STATUS:
369 show_status = 0;
370 break;
371 case OPT_VERSION:
372 pwmd_free(password);
373 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
374 exit(EXIT_SUCCESS);
375 case OPT_HELP:
376 usage(argv[0], EXIT_SUCCESS);
377 default:
378 usage(argv[0], EXIT_FAILURE);
381 break;
382 #ifdef WITH_TCP
383 case '4':
384 prot = PWMD_IPV4;
385 break;
386 case '6':
387 prot = PWMD_IPV6;
388 break;
389 case 'h':
390 host = pwmd_strdup(optarg);
391 break;
392 case 'p':
393 port = atoi(optarg);
394 break;
395 case 'i':
396 ident = pwmd_strdup(optarg);
397 break;
398 case 'u':
399 username = pwmd_strdup(optarg);
400 break;
401 case 'k':
402 known_hosts = pwmd_strdup(optarg);
403 break;
404 case 'g':
405 get = 1;
406 break;
407 #endif
408 case 'y':
409 tty = optarg;
410 break;
411 case 't':
412 ttytype = optarg;
413 break;
414 case 'd':
415 display = optarg;
416 break;
417 case 'S':
418 save = 1;
419 break;
420 case 'I':
421 iter = strtol(optarg, NULL, 10);
422 have_iter = 1;
423 break;
424 case 'P':
425 password = pwmd_strdup(optarg);
426 memset(optarg, 0, strlen(optarg));
427 break;
428 case 'n':
429 clientname = optarg;
430 break;
431 default:
432 pwmd_free(password);
433 usage(argv[0], EXIT_FAILURE);
437 #ifdef WITH_TCP
438 if (host && !get && (!known_hosts || !ident)) {
439 pwmd_free(password);
440 usage(argv[0], EXIT_FAILURE);
443 if (get && !host) {
444 pwmd_free(password);
445 usage(argv[0], EXIT_FAILURE);
447 #endif
449 filename = argv[optind];
450 pwmd_init();
451 pwm = pwmd_new(clientname);
452 #ifdef DEBUG
453 FD_ZERO(&rfds);
454 #endif
456 #ifdef WITH_TCP
457 if (host) {
458 if (prot != PWMD_IP_ANY) {
459 error = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, prot);
461 if (error)
462 goto done;
465 #ifdef DEBUG
466 if (method >= 2) {
467 if (get) {
468 char *hostkey;
470 error = pwmd_get_hostkey_async(pwm, host, port);
472 if (error)
473 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
475 error = process_cmd(pwm, &hostkey, 0);
477 if (error)
478 goto done;
480 printf("%s\n", hostkey);
481 pwmd_free(hostkey);
482 pwmd_free(password);
483 pwmd_close(pwm);
484 exit(EXIT_SUCCESS);
487 error = pwmd_ssh_connect_async(pwm, host, port, ident, username,
488 known_hosts);
490 if (error)
491 goto done;
493 error = process_cmd(pwm, NULL, 0);
495 if (error)
496 goto done;
498 else {
499 #endif
500 if (get) {
501 char *hostkey;
503 error = pwmd_get_hostkey(pwm, host, port, &hostkey);
505 if (error)
506 goto done;
508 printf("%s\n", hostkey);
509 pwmd_free(hostkey);
510 pwmd_free(password);
511 pwmd_close(pwm);
512 exit(EXIT_SUCCESS);
515 error = pwmd_ssh_connect(pwm, host, port, ident, username, known_hosts);
517 if (error)
518 goto done;
519 #ifdef DEBUG
521 #endif
523 else {
524 #endif
525 error = pwmd_connect(pwm, socketpath);
527 if (error)
528 goto done;
529 #ifdef WITH_TCP
531 #endif
533 if (have_iter) {
534 error = pwmd_command(pwm, &result, "VERSION");
536 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD)
537 goto done;
539 pwmd_free(result);
541 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
542 if (iter < -1) {
543 pwmd_free(password);
544 pwmd_close(pwm);
545 usage(argv[0], EXIT_FAILURE);
548 else {
549 /* pwmd version 2 or later. */
550 if (iter < 0) {
551 pwmd_free(password);
552 pwmd_close(pwm);
553 usage(argv[0], EXIT_FAILURE);
558 if (timeout > 0) {
559 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
561 if (error)
562 goto done;
565 if (password) {
566 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
568 if (error)
569 goto done;
571 else {
572 if (pinentry_path) {
573 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
575 if (error)
576 goto done;
579 if (display) {
580 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
582 if (error)
583 goto done;
586 if (tty) {
587 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
589 if (error)
590 goto done;
593 if (ttytype) {
594 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
596 if (error)
597 goto done;
600 if (lcctype) {
601 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
603 if (error)
604 goto done;
607 if (lcmessages) {
608 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
609 lcmessages);
611 if (error)
612 goto done;
615 if (tries > 0) {
616 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
618 if (error)
619 goto done;
623 if (show_status) {
624 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_CB, status_msg_cb);
626 if (error)
627 goto done;
630 if (filename) {
631 #ifdef DEBUG
632 switch (method) {
633 case 0:
634 error = pwmd_open(pwm, filename);
635 break;
636 case 1:
637 error = pwmd_open2(pwm, filename);
638 break;
639 case 2:
640 error = pwmd_open_async(pwm, filename);
642 break;
643 case 3:
644 error = pwmd_open_async2(pwm, filename);
645 break;
648 if (error)
649 goto done;
651 if (method >= 2)
652 error = process_cmd(pwm, &result, 0);
653 #else
654 error = pwmd_open(pwm, filename);
655 #endif
657 if (error)
658 goto done;
661 if (filename) {
662 error = pwmd_command(pwm, &result, "LOCK");
664 if (error)
665 goto done;
668 #ifdef DEBUG
669 if (method >= 2) {
670 for (;;) {
671 ssize_t n;
673 error = process_cmd(pwm, NULL, 1);
675 if (error)
676 goto done;
678 n = read(STDIN_FILENO, command, sizeof(command));
680 if (n == -1) {
681 error = gpg_error_from_errno(errno);
682 goto done;
685 if (n && command[strlen(command)-1] == '\n')
686 command[strlen(command)-1] = 0;
688 command[n] = 0;
689 p = command;
690 break;
693 else
694 p = fgets(command, sizeof(command), stdin);
695 #else
696 p = fgets(command, sizeof(command), stdin);
697 #endif
699 if (!p || !*p)
700 goto done;
703 * This is a known INQUIRE command. We use pwmd_inquire() to send the
704 * data from the do_inquire() callback function.
706 if (strncasecmp(p, "STORE ", 6) == 0) {
707 p += 6;
708 inquire = (char *)"STORE";
710 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
711 p += 7;
712 inquire = (char *)"IMPORT";
715 if (inquire) {
716 struct inquire_s *inq = (struct inquire_s *)pwmd_malloc(sizeof(struct inquire_s));
718 if (!inq) {
719 error = gpg_error_from_errno(ENOMEM);
720 goto done;
723 inq->data = pwmd_strdup(p);
724 inq->fp = inquirefp;
725 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
726 pwmd_free(inq);
727 goto done;
730 if (strcasecmp(p, "BYE") == 0)
731 goto done;
733 error = pwmd_command(pwm, &result, command);
734 memset(command, 0, sizeof(command));
736 if (error)
737 goto done;
739 if (result) {
740 fwrite(result, 1, strlen(result), outfp);
741 pwmd_free(result);
744 done:
745 memset(command, 0, sizeof(command));
746 pwmd_free(password);
748 if (!error && save) {
749 if (iter != -2) {
750 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
752 if (error)
753 goto done;
756 #ifdef DEBUG
757 switch (method) {
758 case 0:
759 error = pwmd_save(pwm);
760 break;
761 case 1:
762 error = pwmd_save2(pwm);
763 break;
764 case 2:
765 error = pwmd_save_async(pwm);
766 break;
767 case 3:
768 error = pwmd_save_async2(pwm);
769 break;
772 if (!error && method >= 2)
773 error = process_cmd(pwm, NULL, 0);
775 #else
776 error = pwmd_save(pwm);
777 #endif
780 if (!error && filename)
781 error = pwmd_command(pwm, &result, "UNLOCK");
783 if (error) {
784 show_error(error);
785 ret = EXIT_FAILURE;
788 pwmd_close(pwm);
790 if (socketpath)
791 pwmd_free(socketpath);
793 exit(ret);