Renamed pwmd_password_fn to pwmd_passphrase_cb_t. This also changes the
[libpwmd.git] / src / pwmc.c
blobfde51f4a8017e39f9e87e74ed55e531d84e420dc
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=pwmd async, 2=libpwmd)\n"
70 #endif
71 " --tries <N>\n"
72 " number of pinentry tries before failing (3)\n"
73 #ifdef WITH_TCP
74 " --host, -h <hostname>\n"
75 " connect to the specified hostname\n"
76 " --port\n"
77 " alterate port (22)\n"
78 " --user\n"
79 " SSH username (default is the invoking user)\n"
80 " --identity, -i <filename>\n"
81 " SSH identity file\n"
82 " --known-hosts, -k <filename>\n"
83 " known host's file (for server validation)\n"
84 " --get-hostkey, -g\n"
85 " retrieve the remote SSH host key and exit\n"
86 " --ipv4, -4\n"
87 " try connecting via IPv4 only\n"
88 " --ipv6, -6\n"
89 " try connecting via IPv6 only\n"
90 #endif
91 " --timeout <seconds>\n"
92 " pinentry timeout\n"
93 " --no-status\n"
94 " disable showing of status messages from the server\n"
95 " --name <string>\n"
96 " set the client name\n"
97 " --socket <filename>\n"
98 " local socket to connect to (~/.pwmd/socket)\n"
99 " --passphrase, -P <string>\n"
100 " passphrase to use (disables pinentry use)\n"
101 " --pinentry <path>\n"
102 " the full path to the pinentry binary (server default)\n"
103 " --ttyname, -y <path>\n"
104 " tty that pinentry will use\n"
105 " --ttytype, -t <string>\n"
106 " pinentry terminal type (default is TERM)\n"
107 " --display, -d\n"
108 " pinentry display (default is DISPLAY)\n"
109 " --lc-ctype <string>\n"
110 " locale setting for pinentry\n"
111 " --lc-messages <string>\n"
112 " locale setting for pinentry\n"
113 " --output-fd <FD>\n"
114 " redirect command output to the specified file descriptor\n"
115 " --inquire-fd <FD>\n"
116 " read inquire data from the specified file descriptor\n"
117 " --save, -S\n"
118 " send the SAVE command before exiting\n"
119 " --iterations, -I <N>\n"
120 " encrypt with the specified number of iterations when saving\n"
121 " --version\n"
122 " --help\n"));
123 exit(status);
126 struct inquire_s {
127 FILE *fp;
128 char *data;
131 static gpg_error_t do_inquire(void *data, const char *keyword, gpg_error_t rc,
132 char **result, size_t *result_len)
134 int c;
135 static char buf[ASSUAN_LINELENGTH];
136 char *p;
137 size_t len = 0;
138 struct inquire_s *inq = (struct inquire_s *)data;
140 if (rc) {
141 memset(buf, 0, sizeof(buf));
142 return rc;
145 buf[0] = 0;
146 p = buf;
148 if (inq->data) {
149 snprintf(buf, sizeof(buf), "%s", inq->data);
150 pwmd_free(inq->data);
151 inq->data = NULL;
152 len = strlen(buf);
153 p = buf + len;
156 while ((c = fgetc(inq->fp)) != EOF) {
157 if (len == sizeof(buf)) {
158 ungetc(c, inq->fp);
159 break;
162 *p++ = c;
163 len++;
166 if (!buf[0]) {
167 memset(buf, 0, sizeof(buf));
168 return GPG_ERR_EOF;
171 *result = buf;
172 *result_len = len;
173 return 0;
176 static int status_msg_cb(void *data, const char *line)
178 fprintf(stderr, "%s\n", line);
179 return 0;
182 int main(int argc, char *argv[])
184 int opt;
185 char *password = NULL;
186 char *filename = NULL;
187 char *socketpath = NULL;
188 char command[ASSUAN_LINELENGTH], *p;
189 int ret = EXIT_SUCCESS;
190 gpg_error_t error;
191 char *result = NULL;
192 int save = 0;
193 char *pinentry_path = NULL;
194 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
195 *lcmessages = NULL;
196 int outfd = STDOUT_FILENO;
197 FILE *outfp = stdout;
198 int inquirefd = STDIN_FILENO;
199 FILE *inquirefp = stdin;
200 int show_status = 1;
201 char *clientname = NULL;
202 char *inquire = NULL;
203 long iter = -2;
204 int have_iter = 0;
205 int timeout = 0;
206 #ifdef WITH_TCP
207 char *host = NULL;
208 int port = DEFAULT_PORT;
209 char *username = NULL;
210 char *ident = NULL;
211 char *known_hosts = NULL;
212 int get = 0;
213 int prot = PWMD_IP_ANY;
214 #endif
215 int tries = 0;
216 #ifdef DEBUG
217 int method = 0;
218 pwmd_async_t s;
219 #endif
220 /* The order is important. */
221 enum {
222 #ifdef DEBUG
223 OPT_DEBUG,
224 #endif
225 #ifdef WITH_TCP
226 OPT_HOST, OPT_PORT, OPT_IDENTITY, OPT_KNOWN_HOSTS, OPT_USER,
227 OPT_GET_HOSTKEY, OPT_IPV4, OPT_IPV6,
228 #endif
229 OPT_TTYNAME, OPT_TTYTYPE, OPT_DISPLAY, OPT_LC_CTYPE, OPT_LC_MESSAGES,
230 OPT_TIMEOUT, OPT_TRIES, OPT_PINENTRY,
231 OPT_PASSPHRASE, OPT_SOCKET, OPT_SAVE, OPT_ITERATIONS, OPT_OUTPUT_FD,
232 OPT_INQUIRE_FD, OPT_NO_STATUS, OPT_NAME, OPT_VERSION, OPT_HELP,
234 const struct option long_opts[] = {
235 #ifdef DEBUG
236 { "debug", 1, 0, 0 },
237 #endif
238 #ifdef WITH_TCP
239 { "host", 1, 0, 'h' },
240 { "port", 1, 0, 'p' },
241 { "identity", 1, 0, 'i' },
242 { "known-hosts", 1, 0, 'k' },
243 { "user", 1, 0, 'u' },
244 { "get-hostkey", 0, 0, 'g' },
245 { "ipv4", 0, 0, '4' },
246 { "ipv6", 0, 0, '6' },
247 #endif
248 { "ttyname", 1, 0, 'y' },
249 { "ttytype", 1, 0, 't' },
250 { "display", 1, 0, 'd' },
251 { "lc-ctype", 1, 0, 0 },
252 { "lc-messages", 1, 0, 0 },
253 { "timeout", 1, 0, 0 },
254 { "tries", 1, 0, 0 },
255 { "pinentry", 1, 0, 0 },
256 { "passphrase", 1, 0, 'P' },
257 { "socket", 1, 0, 0 },
258 { "save", 0, 0, 'S' },
259 { "iterations", 1, 0, 'I' },
260 { "output-fd", 1, 0, 0 },
261 { "inquire-fd", 1, 0, 0 },
262 { "no-status", 0, 0, 0 },
263 { "name", 1, 0, 'n' },
264 { "version", 0, 0, 0 },
265 { "help", 0, 0, 0 },
266 { 0, 0, 0, 0}
268 #ifdef WITH_TCP
269 const char *optstring = "46h:p:i:k:u:gy:t:d:P:I:Sn:";
270 #else
271 const char *optstring = "y:t:d:P:I:Sn:";
272 #endif
273 int opt_index = 0;
275 #ifdef ENABLE_NLS
276 setlocale(LC_ALL, "");
277 bindtextdomain("libpwmd", LOCALEDIR);
278 #endif
280 while ((opt = getopt_long(argc, argv, optstring, &long_opts, &opt_index)) != -1) {
281 switch (opt) {
282 /* Handle long options without a short option part. */
283 case 0:
284 switch (opt_index) {
285 #ifdef DEBUG
286 case OPT_DEBUG:
287 method = atoi(optarg);
289 if (method > 2)
290 method = 2;
291 break;
292 #endif
293 case OPT_LC_CTYPE:
294 lcctype = pwmd_strdup(optarg);
295 break;
296 case OPT_LC_MESSAGES:
297 lcmessages = pwmd_strdup(optarg);
298 break;
299 case OPT_TIMEOUT:
300 timeout = atoi(optarg);
301 break;
302 case OPT_TRIES:
303 tries = atoi(optarg);
304 break;
305 case OPT_SOCKET:
306 socketpath = pwmd_strdup(optarg);
307 break;
308 case OPT_INQUIRE_FD:
309 inquirefd = atoi(optarg);
310 inquirefp = fdopen(inquirefd, "r");
312 if (!inquirefp) {
313 pwmd_free(password);
314 err(EXIT_FAILURE, "%i", inquirefd);
316 break;
317 case OPT_OUTPUT_FD:
318 outfd = atoi(optarg);
319 outfp = fdopen(outfd, "w");
321 if (!outfp) {
322 pwmd_free(password);
323 err(EXIT_FAILURE, "%i", outfd);
325 break;
326 case OPT_NO_STATUS:
327 show_status = 0;
328 break;
329 case OPT_VERSION:
330 pwmd_free(password);
331 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
332 exit(EXIT_SUCCESS);
333 case OPT_HELP:
334 usage(argv[0], EXIT_SUCCESS);
335 default:
336 usage(argv[0], EXIT_FAILURE);
339 break;
340 #ifdef WITH_TCP
341 case '4':
342 prot = PWMD_IPV4;
343 break;
344 case '6':
345 prot = PWMD_IPV6;
346 break;
347 case 'h':
348 host = pwmd_strdup(optarg);
349 break;
350 case 'p':
351 port = atoi(optarg);
352 break;
353 case 'i':
354 ident = pwmd_strdup(optarg);
355 break;
356 case 'u':
357 username = pwmd_strdup(optarg);
358 break;
359 case 'k':
360 known_hosts = pwmd_strdup(optarg);
361 break;
362 case 'g':
363 get = 1;
364 break;
365 #endif
366 case 'y':
367 tty = optarg;
368 break;
369 case 't':
370 ttytype = optarg;
371 break;
372 case 'd':
373 display = optarg;
374 break;
375 case 'S':
376 save = 1;
377 break;
378 case 'I':
379 iter = strtol(optarg, NULL, 10);
380 have_iter = 1;
381 break;
382 case 'P':
383 password = pwmd_strdup(optarg);
384 memset(optarg, 0, strlen(optarg));
385 break;
386 case 'n':
387 clientname = pwmd_strdup(optarg);
388 break;
389 default:
390 pwmd_free(password);
391 usage(argv[0], EXIT_FAILURE);
395 #ifdef WITH_TCP
396 if (host && !get && (!known_hosts || !ident)) {
397 pwmd_free(password);
398 usage(argv[0], EXIT_FAILURE);
401 if (get && !host) {
402 pwmd_free(password);
403 usage(argv[0], EXIT_FAILURE);
405 #endif
407 filename = argv[optind];
408 pwmd_init();
409 pwm = pwmd_new("pwmc");
411 #ifdef WITH_TCP
412 if (host) {
413 if (prot != PWMD_IP_ANY) {
414 error = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, prot);
416 if (error)
417 goto done;
420 #ifdef DEBUG
421 if (method) {
422 if (get) {
423 char *hostkey;
425 error = pwmd_get_hostkey_async(pwm, host, port);
427 if (error)
428 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
430 do {
431 s = pwmd_process(pwm, &error, &hostkey);
432 fputc('.', stderr);
433 usleep(50000);
434 } while (s == ASYNC_PROCESS);
436 if (error)
437 goto done;
439 printf("%s\n", hostkey);
440 pwmd_free(hostkey);
441 pwmd_free(password);
442 pwmd_close(pwm);
443 exit(EXIT_SUCCESS);
446 error = pwmd_ssh_connect_async(pwm, host, port, ident, username, known_hosts);
448 if (error)
449 goto done;
451 do {
452 s = pwmd_process(pwm, &error, &result);
453 fputc('.', stderr);
454 usleep(50000);
455 } while (s == ASYNC_PROCESS);
457 if (error)
458 goto done;
460 else {
461 #endif
462 if (get) {
463 char *hostkey;
465 error = pwmd_get_hostkey(host, port, &hostkey);
467 if (error)
468 goto done;
470 printf("%s\n", hostkey);
471 pwmd_free(hostkey);
472 pwmd_free(password);
473 exit(EXIT_SUCCESS);
476 error = pwmd_ssh_connect(pwm, host, port, ident, username, known_hosts);
478 if (error)
479 goto done;
480 #ifdef DEBUG
482 #endif
484 else {
485 #endif
486 error = pwmd_connect(pwm, socketpath);
488 if (error)
489 goto done;
490 #ifdef WITH_TCP
492 #endif
494 if (have_iter) {
495 error = pwmd_command(pwm, &result, "VERSION");
497 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD)
498 goto done;
500 pwmd_free(result);
502 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
503 if (iter < -1) {
504 pwmd_free(password);
505 pwmd_close(pwm);
506 usage(argv[0], EXIT_FAILURE);
509 else {
510 /* pwmd version 2 or later. */
511 if (iter < 0) {
512 pwmd_free(password);
513 pwmd_close(pwm);
514 usage(argv[0], EXIT_FAILURE);
519 if (timeout > 0) {
520 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
522 if (error)
523 goto done;
526 if (password) {
527 error = pwmd_setopt(pwm, PWMD_OPTION_PASSPHRASE, password);
529 if (error)
530 goto done;
532 else {
533 if (pinentry_path) {
534 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
536 if (error)
537 goto done;
540 if (display) {
541 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
543 if (error)
544 goto done;
547 if (tty) {
548 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
550 if (error)
551 goto done;
554 if (ttytype) {
555 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
557 if (error)
558 goto done;
561 if (lcctype) {
562 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
564 if (error)
565 goto done;
568 if (lcmessages) {
569 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
570 lcmessages);
572 if (error)
573 goto done;
576 #ifdef DEBUG
577 if (method >= 2) {
578 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1);
580 if (error)
581 goto done;
583 #endif
585 if (tries > 0) {
586 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
588 if (error)
589 goto done;
593 if (show_status) {
594 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_CB, status_msg_cb);
596 if (error)
597 goto done;
600 if (filename) {
601 #ifdef DEBUG
602 if (method) {
603 if (method == 1)
604 error = pwmd_open_async(pwm, filename);
605 else
606 error = pwmd_open_async2(pwm, filename);
608 if (!error) {
609 do {
610 s = pwmd_process(pwm, &error, &result);
611 fputc('.', stderr);
612 usleep(50000);
613 } while (s == ASYNC_PROCESS);
616 else
617 error = pwmd_open(pwm, filename);
618 #else
619 error = pwmd_open(pwm, filename);
620 #endif
622 if (error)
623 goto done;
626 if (filename) {
627 error = pwmd_command(pwm, &result, "LOCK");
629 if (error)
630 goto done;
633 #ifdef DEBUG
634 if (method) {
635 for (;;) {
636 struct timeval tv = {0, 100000};
637 fd_set rfds;
638 int n;
640 FD_ZERO(&rfds);
641 FD_SET(STDIN_FILENO, &rfds);
642 n = select(STDIN_FILENO+1, &rfds, NULL, NULL, &tv);
644 if (n == 0) {
645 s = pwmd_process(pwm, &error, &result);
647 if (error)
648 goto done;
650 fprintf(stderr, ".");
651 continue;
654 if (n == -1) {
655 error = gpg_error_from_errno(errno);
656 goto done;
659 fprintf(stderr, "\n");
660 n = read(STDIN_FILENO, command, sizeof(command));
662 if (n == -1) {
663 error = gpg_error_from_errno(errno);
664 goto done;
667 if (n && command[strlen(command)-1] == '\n')
668 command[strlen(command)-1] = 0;
670 command[n] = 0;
671 p = command;
672 break;
675 else
676 p = fgets(command, sizeof(command), stdin);
677 #else
678 p = fgets(command, sizeof(command), stdin);
679 #endif
681 if (!p || !*p)
682 goto done;
685 * This is a known INQUIRE command. We use pwmd_inquire() to send the
686 * data from the do_inquire() callback function.
688 if (strncasecmp(p, "STORE ", 6) == 0) {
689 p += 6;
690 inquire = (char *)"STORE";
692 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
693 p += 7;
694 inquire = (char *)"IMPORT";
697 if (inquire) {
698 struct inquire_s *inq = (struct inquire_s *)pwmd_malloc(sizeof(struct inquire_s));
700 if (!inq) {
701 error = gpg_error_from_errno(ENOMEM);
702 goto done;
705 inq->data = pwmd_strdup(p);
706 inq->fp = inquirefp;
707 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
708 pwmd_free(inq);
709 goto done;
712 if (strcasecmp(p, "BYE") == 0)
713 goto done;
715 error = pwmd_command(pwm, &result, command);
716 memset(command, 0, sizeof(command));
718 if (error)
719 goto done;
721 if (result) {
722 fwrite(result, 1, strlen(result), outfp);
723 pwmd_free(result);
726 done:
727 memset(command, 0, sizeof(command));
728 pwmd_free(password);
730 if (!error && save) {
731 if (iter != -2) {
732 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
734 if (error)
735 goto done;
738 #ifdef DEBUG
739 if (method) {
740 if (method == 1)
741 error = pwmd_save_async(pwm);
742 else
743 error = pwmd_save_async2(pwm);
745 if (!error) {
746 do {
747 s = pwmd_process(pwm, &error, &result);
748 fputc('.', stderr);
749 usleep(50000);
750 } while (s == ASYNC_PROCESS);
753 else
754 error = pwmd_save(pwm);
755 #else
756 error = pwmd_save(pwm);
757 #endif
760 if (!error && filename)
761 error = pwmd_command(pwm, &result, "UNLOCK");
763 if (error) {
764 show_error(error);
765 ret = EXIT_FAILURE;
768 pwmd_close(pwm);
770 if (socketpath)
771 pwmd_free(socketpath);
773 exit(ret);