Added pwmd_open2() and pwmd_save2() which will use the local pinentry.
[libpwmd.git] / src / pwmc.c
blobaf088506b8782fec165586518e139736c0c918da
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 <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 int main(int argc, char *argv[])
185 int opt;
186 char *password = NULL;
187 char *filename = NULL;
188 char *socketpath = NULL;
189 char command[ASSUAN_LINELENGTH], *p;
190 int ret = EXIT_SUCCESS;
191 gpg_error_t error;
192 char *result = NULL;
193 int save = 0;
194 char *pinentry_path = NULL;
195 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
196 *lcmessages = NULL;
197 int outfd = STDOUT_FILENO;
198 FILE *outfp = stdout;
199 int inquirefd = STDIN_FILENO;
200 FILE *inquirefp = stdin;
201 int show_status = 1;
202 char *clientname = NULL;
203 char *inquire = NULL;
204 long iter = -2;
205 int have_iter = 0;
206 int timeout = 0;
207 #ifdef WITH_TCP
208 char *host = NULL;
209 int port = DEFAULT_PORT;
210 char *username = NULL;
211 char *ident = NULL;
212 char *known_hosts = NULL;
213 int get = 0;
214 int prot = PWMD_IP_ANY;
215 #endif
216 int tries = 0;
217 #ifdef DEBUG
218 int method = 0;
219 pwmd_async_t s;
220 #endif
221 /* The order is important. */
222 enum {
223 #ifdef DEBUG
224 OPT_DEBUG,
225 #endif
226 #ifdef WITH_TCP
227 OPT_HOST, OPT_PORT, OPT_IDENTITY, OPT_KNOWN_HOSTS, OPT_USER,
228 OPT_GET_HOSTKEY, OPT_IPV4, OPT_IPV6,
229 #endif
230 OPT_TTYNAME, OPT_TTYTYPE, OPT_DISPLAY, OPT_LC_CTYPE, OPT_LC_MESSAGES,
231 OPT_TIMEOUT, OPT_TRIES, OPT_PINENTRY,
232 OPT_PASSPHRASE, OPT_SOCKET, OPT_SAVE, OPT_ITERATIONS, OPT_OUTPUT_FD,
233 OPT_INQUIRE_FD, OPT_NO_STATUS, OPT_NAME, OPT_VERSION, OPT_HELP,
235 const struct option long_opts[] = {
236 #ifdef DEBUG
237 { "debug", 1, 0, 0 },
238 #endif
239 #ifdef WITH_TCP
240 { "host", 1, 0, 'h' },
241 { "port", 1, 0, 'p' },
242 { "identity", 1, 0, 'i' },
243 { "known-hosts", 1, 0, 'k' },
244 { "user", 1, 0, 'u' },
245 { "get-hostkey", 0, 0, 'g' },
246 { "ipv4", 0, 0, '4' },
247 { "ipv6", 0, 0, '6' },
248 #endif
249 { "ttyname", 1, 0, 'y' },
250 { "ttytype", 1, 0, 't' },
251 { "display", 1, 0, 'd' },
252 { "lc-ctype", 1, 0, 0 },
253 { "lc-messages", 1, 0, 0 },
254 { "timeout", 1, 0, 0 },
255 { "tries", 1, 0, 0 },
256 { "pinentry", 1, 0, 0 },
257 { "passphrase", 1, 0, 'P' },
258 { "socket", 1, 0, 0 },
259 { "save", 0, 0, 'S' },
260 { "iterations", 1, 0, 'I' },
261 { "output-fd", 1, 0, 0 },
262 { "inquire-fd", 1, 0, 0 },
263 { "no-status", 0, 0, 0 },
264 { "name", 1, 0, 'n' },
265 { "version", 0, 0, 0 },
266 { "help", 0, 0, 0 },
267 { 0, 0, 0, 0}
269 #ifdef WITH_TCP
270 const char *optstring = "46h:p:i:k:u:gy:t:d:P:I:Sn:";
271 #else
272 const char *optstring = "y:t:d:P:I:Sn:";
273 #endif
274 int opt_index = 0;
276 #ifdef ENABLE_NLS
277 setlocale(LC_ALL, "");
278 bindtextdomain("libpwmd", LOCALEDIR);
279 #endif
281 while ((opt = getopt_long(argc, argv, optstring, &long_opts, &opt_index)) != -1) {
282 switch (opt) {
283 /* Handle long options without a short option part. */
284 case 0:
285 switch (opt_index) {
286 #ifdef DEBUG
287 case OPT_DEBUG:
288 method = atoi(optarg);
290 if (method > 3)
291 method = 3;
292 break;
293 #endif
294 case OPT_LC_CTYPE:
295 lcctype = pwmd_strdup(optarg);
296 break;
297 case OPT_LC_MESSAGES:
298 lcmessages = pwmd_strdup(optarg);
299 break;
300 case OPT_TIMEOUT:
301 timeout = atoi(optarg);
302 break;
303 case OPT_TRIES:
304 tries = atoi(optarg);
305 break;
306 case OPT_SOCKET:
307 socketpath = pwmd_strdup(optarg);
308 break;
309 case OPT_INQUIRE_FD:
310 inquirefd = atoi(optarg);
311 inquirefp = fdopen(inquirefd, "r");
313 if (!inquirefp) {
314 pwmd_free(password);
315 err(EXIT_FAILURE, "%i", inquirefd);
317 break;
318 case OPT_OUTPUT_FD:
319 outfd = atoi(optarg);
320 outfp = fdopen(outfd, "w");
322 if (!outfp) {
323 pwmd_free(password);
324 err(EXIT_FAILURE, "%i", outfd);
326 break;
327 case OPT_NO_STATUS:
328 show_status = 0;
329 break;
330 case OPT_VERSION:
331 pwmd_free(password);
332 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
333 exit(EXIT_SUCCESS);
334 case OPT_HELP:
335 usage(argv[0], EXIT_SUCCESS);
336 default:
337 usage(argv[0], EXIT_FAILURE);
340 break;
341 #ifdef WITH_TCP
342 case '4':
343 prot = PWMD_IPV4;
344 break;
345 case '6':
346 prot = PWMD_IPV6;
347 break;
348 case 'h':
349 host = pwmd_strdup(optarg);
350 break;
351 case 'p':
352 port = atoi(optarg);
353 break;
354 case 'i':
355 ident = pwmd_strdup(optarg);
356 break;
357 case 'u':
358 username = pwmd_strdup(optarg);
359 break;
360 case 'k':
361 known_hosts = pwmd_strdup(optarg);
362 break;
363 case 'g':
364 get = 1;
365 break;
366 #endif
367 case 'y':
368 tty = optarg;
369 break;
370 case 't':
371 ttytype = optarg;
372 break;
373 case 'd':
374 display = optarg;
375 break;
376 case 'S':
377 save = 1;
378 break;
379 case 'I':
380 iter = strtol(optarg, NULL, 10);
381 have_iter = 1;
382 break;
383 case 'P':
384 password = pwmd_strdup(optarg);
385 memset(optarg, 0, strlen(optarg));
386 break;
387 case 'n':
388 clientname = pwmd_strdup(optarg);
389 break;
390 default:
391 pwmd_free(password);
392 usage(argv[0], EXIT_FAILURE);
396 #ifdef WITH_TCP
397 if (host && !get && (!known_hosts || !ident)) {
398 pwmd_free(password);
399 usage(argv[0], EXIT_FAILURE);
402 if (get && !host) {
403 pwmd_free(password);
404 usage(argv[0], EXIT_FAILURE);
406 #endif
408 filename = argv[optind];
409 pwmd_init();
410 pwm = pwmd_new("pwmc");
412 #ifdef WITH_TCP
413 if (host) {
414 if (prot != PWMD_IP_ANY) {
415 error = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, prot);
417 if (error)
418 goto done;
421 #ifdef DEBUG
422 if (method) {
423 if (get) {
424 char *hostkey;
426 error = pwmd_get_hostkey_async(pwm, host, port);
428 if (error)
429 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
431 do {
432 s = pwmd_process(pwm, &error, &hostkey);
433 fputc('.', stderr);
434 usleep(50000);
435 } while (s == ASYNC_PROCESS);
437 if (error)
438 goto done;
440 printf("%s\n", hostkey);
441 pwmd_free(hostkey);
442 pwmd_free(password);
443 pwmd_close(pwm);
444 exit(EXIT_SUCCESS);
447 error = pwmd_ssh_connect_async(pwm, host, port, ident, username, known_hosts);
449 if (error)
450 goto done;
452 do {
453 s = pwmd_process(pwm, &error, &result);
454 fputc('.', stderr);
455 usleep(50000);
456 } while (s == ASYNC_PROCESS);
458 if (error)
459 goto done;
461 else {
462 #endif
463 if (get) {
464 char *hostkey;
466 error = pwmd_get_hostkey(pwm, host, port, &hostkey);
468 if (error)
469 goto done;
471 printf("%s\n", hostkey);
472 pwmd_free(hostkey);
473 pwmd_free(password);
474 pwmd_close(pwm);
475 exit(EXIT_SUCCESS);
478 error = pwmd_ssh_connect(pwm, host, port, ident, username, known_hosts);
480 if (error)
481 goto done;
482 #ifdef DEBUG
484 #endif
486 else {
487 #endif
488 error = pwmd_connect(pwm, socketpath);
490 if (error)
491 goto done;
492 #ifdef WITH_TCP
494 #endif
496 if (have_iter) {
497 error = pwmd_command(pwm, &result, "VERSION");
499 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD)
500 goto done;
502 pwmd_free(result);
504 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
505 if (iter < -1) {
506 pwmd_free(password);
507 pwmd_close(pwm);
508 usage(argv[0], EXIT_FAILURE);
511 else {
512 /* pwmd version 2 or later. */
513 if (iter < 0) {
514 pwmd_free(password);
515 pwmd_close(pwm);
516 usage(argv[0], EXIT_FAILURE);
521 if (timeout > 0) {
522 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
524 if (error)
525 goto done;
528 if (password) {
529 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
531 if (error)
532 goto done;
534 else {
535 if (pinentry_path) {
536 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
538 if (error)
539 goto done;
542 if (display) {
543 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
545 if (error)
546 goto done;
549 if (tty) {
550 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
552 if (error)
553 goto done;
556 if (ttytype) {
557 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
559 if (error)
560 goto done;
563 if (lcctype) {
564 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
566 if (error)
567 goto done;
570 if (lcmessages) {
571 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
572 lcmessages);
574 if (error)
575 goto done;
578 if (tries > 0) {
579 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
581 if (error)
582 goto done;
586 if (show_status) {
587 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_CB, status_msg_cb);
589 if (error)
590 goto done;
593 if (filename) {
594 #ifdef DEBUG
595 switch (method) {
596 case 0:
597 error = pwmd_open(pwm, filename);
598 break;
599 case 1:
600 error = pwmd_open2(pwm, filename);
601 break;
602 case 2:
603 error = pwmd_open_async(pwm, filename);
604 break;
605 case 3:
606 error = pwmd_open_async2(pwm, filename);
607 break;
610 if (!error && method > 1) {
611 do {
612 s = pwmd_process(pwm, &error, &result);
613 fputc('.', stderr);
614 usleep(50000);
615 } while (s == ASYNC_PROCESS);
617 #else
618 error = pwmd_open(pwm, filename);
619 #endif
621 if (error)
622 goto done;
625 if (filename) {
626 error = pwmd_command(pwm, &result, "LOCK");
628 if (error)
629 goto done;
632 #ifdef DEBUG
633 if (method > 1) {
634 for (;;) {
635 struct timeval tv = {0, 100000};
636 fd_set rfds;
637 int n;
639 FD_ZERO(&rfds);
640 FD_SET(STDIN_FILENO, &rfds);
641 n = select(STDIN_FILENO+1, &rfds, NULL, NULL, &tv);
643 if (n == 0) {
644 s = pwmd_process(pwm, &error, &result);
646 if (error)
647 goto done;
649 fprintf(stderr, ".");
650 continue;
653 if (n == -1) {
654 error = gpg_error_from_errno(errno);
655 goto done;
658 fprintf(stderr, "\n");
659 n = read(STDIN_FILENO, command, sizeof(command));
661 if (n == -1) {
662 error = gpg_error_from_errno(errno);
663 goto done;
666 if (n && command[strlen(command)-1] == '\n')
667 command[strlen(command)-1] = 0;
669 command[n] = 0;
670 p = command;
671 break;
674 else
675 p = fgets(command, sizeof(command), stdin);
676 #else
677 p = fgets(command, sizeof(command), stdin);
678 #endif
680 if (!p || !*p)
681 goto done;
684 * This is a known INQUIRE command. We use pwmd_inquire() to send the
685 * data from the do_inquire() callback function.
687 if (strncasecmp(p, "STORE ", 6) == 0) {
688 p += 6;
689 inquire = (char *)"STORE";
691 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
692 p += 7;
693 inquire = (char *)"IMPORT";
696 if (inquire) {
697 struct inquire_s *inq = (struct inquire_s *)pwmd_malloc(sizeof(struct inquire_s));
699 if (!inq) {
700 error = gpg_error_from_errno(ENOMEM);
701 goto done;
704 inq->data = pwmd_strdup(p);
705 inq->fp = inquirefp;
706 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
707 pwmd_free(inq);
708 goto done;
711 if (strcasecmp(p, "BYE") == 0)
712 goto done;
714 error = pwmd_command(pwm, &result, command);
715 memset(command, 0, sizeof(command));
717 if (error)
718 goto done;
720 if (result) {
721 fwrite(result, 1, strlen(result), outfp);
722 pwmd_free(result);
725 done:
726 memset(command, 0, sizeof(command));
727 pwmd_free(password);
729 if (!error && save) {
730 if (iter != -2) {
731 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
733 if (error)
734 goto done;
737 #ifdef DEBUG
738 switch (method) {
739 case 0:
740 error = pwmd_save(pwm);
741 break;
742 case 1:
743 error = pwmd_save2(pwm);
744 break;
745 case 2:
746 error = pwmd_save_async(pwm);
747 break;
748 case 3:
749 error = pwmd_save_async2(pwm);
750 break;
753 if (!error && method > 1) {
754 do {
755 s = pwmd_process(pwm, &error, &result);
756 fputc('.', stderr);
757 usleep(50000);
758 } while (s == ASYNC_PROCESS);
760 #else
761 error = pwmd_save(pwm);
762 #endif
765 if (!error && filename)
766 error = pwmd_command(pwm, &result, "UNLOCK");
768 if (error) {
769 show_error(error);
770 ret = EXIT_FAILURE;
773 pwmd_close(pwm);
775 if (socketpath)
776 pwmd_free(socketpath);
778 exit(ret);