Allow -y for pinentry retries even without DEBUG.
[libpwmd.git] / src / pwmc.c
blob5484eb58c2b161c90a16583c59b137ee040bf464
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2007-2009 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 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <libpwmd.h>
26 #include <assuan.h>
27 #ifdef DEBUG
28 #include <sys/select.h>
29 #include <fcntl.h>
30 #endif
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #ifdef HAVE_LOCALE_H
37 #include <locale.h>
38 #endif
40 #include "gettext.h"
41 #define N_(msgid) gettext(msgid)
43 #include "mem.h"
45 #define DEFAULT_PORT 22
46 pwm_t *pwm;
48 static void show_error(gpg_error_t error)
50 fprintf(stderr, "ERR %i %s\n", gpg_err_code(error), pwmd_strerror(error));
53 static void usage(const char *pn)
55 fprintf(stderr, N_(
56 "Read a PWMD protocol command from standard input.\n\n"
57 "Usage: pwmc [-hvX] [-s <socket>] "
58 #ifdef DEBUG
59 "[-E <n>] [-y <n>]"
60 #endif
61 "[-PTNDCM <string>] [-p <passphrase>]\n"
62 " [-S [-i <iter>]] [-c <name>] [-t <n>] [-d <fd>] [-I <fd>]\n"
63 #ifdef WITH_TCP
64 " [-H <hostname> [-R <port>] -Y <identity> -K <known_hosts>\n"
65 " -U <username> [-G]] [filename]\n"
66 #else
67 " [filename]\n"
68 #endif
69 #ifdef DEBUG
70 " -E pinentry method (0=pwmd, 1=pwmd async, 2=libpwmd nb)\n"
71 #endif
72 " -y number of pinentry tries before failing (3)\n"
73 #ifdef WITH_TCP
74 " -H connect to hostname\n"
75 " -R alterate port (%i)\n"
76 " -U SSH username\n"
77 " -Y SSH identity file\n"
78 " -K known host's file (for server validation)\n"
79 " -G retrieve the remote SSH host key and exit\n"
80 #endif
81 " -t pinentry timeout\n"
82 " -X disable showing of status messages from the server\n"
83 " -c set the client name\n"
84 " -s socket path (~/.pwmd/socket)\n"
85 " -p passphrase\n"
86 " -P path to the pinentry binary (server default)\n"
87 " -T pinentry tty\n"
88 " -N pinentry terminal type\n"
89 " -D pinentry display\n"
90 " -C pinentry LC_CTYPE\n"
91 " -M pinentry LC_MESSAGES\n"
92 " -d redirect command output to the specified file descriptor\n"
93 " -I read inquire data from the specified file descriptor\n"
94 " -S send the SAVE command before exiting\n"
95 " -i encrypt with the specified number of iterations\n"
96 " -v version\n"
97 #ifdef WITH_TCP
98 " -h this help text\n"), DEFAULT_PORT);
99 #else
100 " -h this help text\n"));
101 #endif
102 exit(EXIT_FAILURE);
105 struct inquire_s {
106 FILE *fp;
107 char *data;
110 static gpg_error_t do_inquire(void *data, const char *keyword, gpg_error_t rc,
111 char **result, size_t *result_len)
113 int c;
114 static char buf[ASSUAN_LINELENGTH];
115 char *p;
116 size_t len = 0;
117 struct inquire_s *inq = (struct inquire_s *)data;
119 if (rc) {
120 memset(buf, 0, sizeof(buf));
121 return rc;
124 buf[0] = 0;
125 p = buf;
127 if (inq->data) {
128 snprintf(buf, sizeof(buf), "%s", inq->data);
129 xfree(inq->data);
130 inq->data = NULL;
131 len = strlen(buf);
132 p = buf + len;
135 while ((c = fgetc(inq->fp)) != EOF) {
136 if (len == sizeof(buf)) {
137 ungetc(c, inq->fp);
138 break;
141 *p++ = c;
142 len++;
145 if (!buf[0]) {
146 memset(buf, 0, sizeof(buf));
147 return GPG_ERR_EOF;
150 *result = buf;
151 *result_len = len;
152 return 0;
155 static int status_msg_cb(void *data, const char *line)
157 fprintf(stderr, "%s\n", line);
158 return 0;
161 #ifdef DEBUG
162 static gpg_error_t do_nb_command(int fd, int which)
164 int n;
165 gpg_error_t error = 0;
167 fcntl(fd, F_SETFL, O_NONBLOCK);
169 do {
170 fd_set fds;
171 struct timeval tv = {0, 50000};
173 FD_ZERO(&fds);
174 FD_SET(fd, &fds);
175 n = select(fd+1, &fds, NULL, NULL, &tv);
177 if (n > 0) {
178 if (FD_ISSET(fd, &fds)) {
179 pwmd_nb_status_t status;
181 n = read(fd, &status, sizeof(status));
183 if (n == -1) {
184 error = gpg_error_from_errno(errno);
185 goto done;
188 if (!which)
189 error = pwmd_open_nb_finalize(pwm, &status);
190 else
191 error = pwmd_save_nb_finalize(pwm, &status);
193 if (error)
194 goto done;
197 else
198 fputc('.', stderr);
199 } while (n == 0);
201 done:
202 return error;
204 #endif
206 int main(int argc, char *argv[])
208 int opt;
209 char *password = NULL;
210 char *filename = NULL;
211 char *socketpath = NULL;
212 char command[ASSUAN_LINELENGTH], *p;
213 int ret = EXIT_SUCCESS;
214 gpg_error_t error;
215 char *result = NULL;
216 int save = 0;
217 char *pinentry_path = NULL;
218 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
219 *lcmessages = NULL;
220 int outfd = STDOUT_FILENO;
221 FILE *outfp = stdout;
222 int inquirefd = STDIN_FILENO;
223 FILE *inquirefp = stdin;
224 int show_status = 1;
225 char *clientname = NULL;
226 char *inquire = NULL;
227 long iter = -2;
228 int have_iter = 0;
229 int timeout = 0;
230 #ifdef WITH_TCP
231 char *host = NULL;
232 int port = DEFAULT_PORT;
233 char *username = NULL;
234 char *ident = NULL;
235 char *known_hosts = NULL;
236 int get = 0;
237 #endif
238 int tries = 0;
239 #ifdef DEBUG
240 int method = 0;
241 pwmd_async_t s;
242 #endif
244 #ifdef ENABLE_NLS
245 setlocale(LC_ALL, "");
246 bindtextdomain("libpwmd", LOCALEDIR);
247 #endif
249 #ifdef DEBUG
250 #ifdef WITH_TCP
251 while ((opt = getopt(argc, argv, "C:M:GK:U:Y:H:R:y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
252 #else
253 while ((opt = getopt(argc, argv, "C:M:y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
254 #endif
255 #else
256 #ifdef WITH_TCP
257 while ((opt = getopt(argc, argv, "y:C:M:GK:U:Y:H:R:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
258 #else
259 while ((opt = getopt(argc, argv, "y:C:M:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
260 #endif
261 #endif
262 switch (opt) {
263 #ifdef DEBUG
264 case 'E':
265 method = atoi(optarg);
267 if (method > 2)
268 method = 2;
269 break;
270 #endif
271 case 'y':
272 tries = atoi(optarg);
273 break;
274 #ifdef WITH_TCP
275 case 'H':
276 host = xstrdup(optarg);
277 break;
278 case 'R':
279 port = atoi(optarg);
280 break;
281 case 'Y':
282 ident = xstrdup(optarg);
283 break;
284 case 'U':
285 username = xstrdup(optarg);
286 break;
287 case 'K':
288 known_hosts = xstrdup(optarg);
289 break;
290 case 'G':
291 get = 1;
292 break;
293 #endif
294 case 'C':
295 lcctype = xstrdup(optarg);
296 break;
297 case 'M':
298 lcmessages = xstrdup(optarg);
299 break;
300 case 't':
301 timeout = atoi(optarg);
302 break;
303 case 'c':
304 clientname = xstrdup(optarg);
305 break;
306 case 'X':
307 show_status = 0;
308 break;
309 case 'T':
310 tty = optarg;
311 break;
312 case 'N':
313 ttytype = optarg;
314 break;
315 case 'D':
316 display = optarg;
317 break;
318 case 'I':
319 inquirefd = atoi(optarg);
320 inquirefp = fdopen(inquirefd, "r");
322 if (!inquirefp) {
323 xfree(password);
324 err(EXIT_FAILURE, "%i", inquirefd);
326 break;
327 case 'd':
328 outfd = atoi(optarg);
329 outfp = fdopen(outfd, "w");
331 if (!outfp) {
332 xfree(password);
333 err(EXIT_FAILURE, "%i", outfd);
335 break;
336 case 'S':
337 save = 1;
338 break;
339 case 'i':
340 iter = strtol(optarg, NULL, 10);
341 have_iter = 1;
342 break;
343 case 's':
344 socketpath = xstrdup(optarg);
345 break;
346 case 'p':
347 password = xstrdup(optarg);
348 memset(optarg, 0, strlen(optarg));
349 break;
350 case 'P':
351 pinentry_path = xstrdup(optarg);
352 break;
353 case 'v':
354 xfree(password);
355 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
356 exit(EXIT_SUCCESS);
357 case 'h':
358 default:
359 xfree(password);
360 usage(argv[0]);
364 filename = argv[optind];
365 pwmd_init();
367 #ifdef WITH_TCP
368 if (host) {
369 #ifdef DEBUG
370 if (method) {
371 if (get) {
372 const char *hostkey;
374 xfree(password);
375 pwm = pwmd_get_hostkey_async(host, port, &error);
377 if (error)
378 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
380 do {
381 s = pwmd_process(pwm, &error);
382 fputc('.', stderr);
383 usleep(50000);
384 } while (s == ASYNC_PROCESS);
386 if (error) {
387 pwmd_close(pwm);
388 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
391 error = pwmd_get_result(pwm, &hostkey);
393 if (error) {
394 pwmd_close(pwm);
395 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
398 printf("%s\n", hostkey);
399 pwmd_finalize(pwm);
400 pwmd_close(pwm);
401 exit(EXIT_SUCCESS);
404 if ((pwm = pwmd_tcp_connect_async(host, port, ident, username, known_hosts, &error)) == NULL) {
405 xfree(password);
406 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
409 do {
410 s = pwmd_process(pwm, &error);
411 fputc('.', stderr);
412 usleep(50000);
413 } while (s == ASYNC_PROCESS);
415 if (error) {
416 pwmd_close(pwm);
417 xfree(password);
418 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
421 pwmd_finalize(pwm);
423 else {
424 #endif
425 if (get) {
426 xfree(password);
427 char *hostkey = pwmd_get_hostkey(host, port, &error);
429 if (error)
430 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
432 printf("%s\n", hostkey);
433 pwmd_free_result(hostkey);
434 exit(EXIT_SUCCESS);
437 if ((pwm = pwmd_tcp_connect(host, port, ident, username, known_hosts, &error)) == NULL) {
438 xfree(password);
439 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
441 #ifdef DEBUG
443 #endif
445 else {
446 #endif
447 if ((pwm = pwmd_connect(socketpath, &error)) == NULL) {
448 xfree(password);
449 errx(EXIT_FAILURE, "pwmd_connect(): %s", pwmd_strerror(error));
451 #ifdef WITH_TCP
453 #endif
455 error = pwmd_command(pwm, &result, "OPTION CLIENT NAME=%s", clientname ? clientname : "pwmc");
456 xfree(clientname);
458 if (error) {
459 xfree(password);
460 goto done;
463 if (have_iter) {
464 error = pwmd_command(pwm, &result, "VERSION");
466 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD) {
467 xfree(password);
468 goto done;
471 pwmd_free_result(result);
473 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
474 if (iter < -1) {
475 xfree(password);
476 pwmd_close(pwm);
477 usage(argv[0]);
480 else {
481 /* pwmd version 2 or later. */
482 if (iter < 0) {
483 xfree(password);
484 pwmd_close(pwm);
485 usage(argv[0]);
490 if (timeout > 0) {
491 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
493 if (error) {
494 xfree(password);
495 goto done;
499 if (password) {
500 error = pwmd_setopt(pwm, PWMD_OPTION_PASSWORD, password);
502 if (error) {
503 xfree(password);
504 goto done;
507 xfree(password);
509 else {
510 if (pinentry_path) {
511 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
513 if (error)
514 goto done;
517 if (display) {
518 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
520 if (error)
521 goto done;
524 if (tty) {
525 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
527 if (error)
528 goto done;
531 if (ttytype) {
532 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
534 if (error)
535 goto done;
538 if (lcctype) {
539 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
541 if (error)
542 goto done;
545 if (lcmessages) {
546 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
547 lcmessages);
549 if (error)
550 goto done;
553 #ifdef DEBUG
554 if (method >= 2) {
555 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1);
557 if (error)
558 goto done;
560 #endif
562 if (tries > 0) {
563 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
565 if (error)
566 goto done;
570 if (show_status) {
571 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_FUNC, status_msg_cb);
573 if (error)
574 goto done;
577 if (filename) {
578 #ifdef DEBUG
579 /* This method doesn't support PWMD_OPTION_PINENTRY_TRIES. */
580 if (method == 1) {
581 error = pwmd_open_async(pwm, filename);
583 if (!error) {
584 do {
585 s = pwmd_process(pwm, &error);
586 fputc('.', stderr);
587 usleep(50000);
588 } while (s == ASYNC_PROCESS);
590 pwmd_finalize(pwm);
593 else if (method == 2) {
594 int fd = pwmd_open_nb(pwm, &error, filename, timeout);
596 if (fd == -1)
597 goto done;
598 else if (fd >= 0)
599 error = do_nb_command(fd, 0);
601 else
602 error = pwmd_open(pwm, filename);
603 #else
604 error = pwmd_open(pwm, filename);
605 #endif
607 if (error)
608 goto done;
611 if (filename) {
612 error = pwmd_command(pwm, &result, "LOCK");
614 if (error)
615 goto done;
618 #ifdef DEBUG
619 if (method) {
620 for (;;) {
621 struct timeval tv = {0, 100000};
622 fd_set rfds;
623 int n;
625 FD_ZERO(&rfds);
626 FD_SET(STDIN_FILENO, &rfds);
627 n = select(STDIN_FILENO+1, &rfds, NULL, NULL, &tv);
629 if (n == 0) {
630 s = pwmd_process(pwm, &error);
632 if (error)
633 goto done;
635 fprintf(stderr, ".");
636 continue;
639 if (n == -1) {
640 error = gpg_error_from_errno(errno);
641 goto done;
644 fprintf(stderr, "\n");
645 n = read(STDIN_FILENO, command, sizeof(command));
647 if (n == -1) {
648 error = gpg_error_from_errno(errno);
649 goto done;
652 if (n && command[strlen(command)-1] == '\n')
653 command[strlen(command)-1] = 0;
655 command[n] = 0;
656 p = command;
657 break;
660 else
661 p = fgets(command, sizeof(command), stdin);
662 #else
663 p = fgets(command, sizeof(command), stdin);
664 #endif
666 if (!p || !*p)
667 goto done;
670 * This is a known INQUIRE command. We use pwmd_inquire() to send the
671 * data from the do_inquire() callback function.
673 if (strncasecmp(p, "STORE ", 6) == 0) {
674 p += 6;
675 inquire = (char *)"STORE";
677 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
678 p += 7;
679 inquire = (char *)"IMPORT";
682 if (inquire) {
683 struct inquire_s *inq = (struct inquire_s *)malloc(sizeof(struct inquire_s));
685 if (!inq) {
686 error = gpg_error_from_errno(ENOMEM);
687 goto done;
690 inq->data = xstrdup(p);
691 inq->fp = inquirefp;
692 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
693 free(inq);
694 goto done;
697 if (strcasecmp(p, "BYE") == 0)
698 goto done;
700 error = pwmd_command(pwm, &result, command);
701 memset(command, 0, sizeof(command));
703 if (error)
704 goto done;
706 if (result) {
707 fwrite(result, 1, strlen(result), outfp);
708 pwmd_free_result(result);
711 done:
712 memset(command, 0, sizeof(command));
714 if (!error && save) {
715 if (iter != -2) {
716 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
718 if (error)
719 goto done;
722 #ifdef DEBUG
723 if (method == 1) {
724 error = pwmd_save_async(pwm);
726 if (!error) {
727 do {
728 s = pwmd_process(pwm, &error);
729 fputc('.', stderr);
730 usleep(50000);
731 } while (s == ASYNC_PROCESS);
733 pwmd_finalize(pwm);
736 else if (method == 3) {
737 int fd = pwmd_save_nb(pwm, &error);
739 if (fd == -1)
740 goto done;
741 else if (fd >= 0)
742 error = do_nb_command(fd, 1);
744 else
745 error = pwmd_save(pwm);
746 #else
747 error = pwmd_save(pwm);
748 #endif
751 if (!error && filename)
752 error = pwmd_command(pwm, &result, "UNLOCK");
754 if (error) {
755 show_error(error);
756 ret = EXIT_FAILURE;
759 pwmd_close(pwm);
761 if (socketpath)
762 xfree(socketpath);
764 exit(ret);