Fixed a typo in pwmc.c preventing reading STDIN.
[libpwmd.git] / src / pwmc.c
blob10c48d2c39e32d098043b87eb22535dc1fccf189
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 #include "gettext.h"
42 #define N_(msgid) gettext(msgid)
44 #include "mem.h"
46 #define DEFAULT_PORT 22
47 pwm_t *pwm;
49 static void show_error(gpg_error_t error)
51 fprintf(stderr, "ERR %i %s\n", gpg_err_code(error), pwmd_strerror(error));
54 static void usage(const char *pn)
56 fprintf(stderr, N_(
57 "Reads PWMD protocol commands from standard input.\n\n"
58 "Usage: pwmc [-hvX] [-s <socket>] "
59 #ifdef DEBUG
60 "[-E <n>] [-y <n>]"
61 #endif
62 "[-PTNDCM <string>] [-p <passphrase>]\n"
63 " [-S [-i <iter>]] [-c <name>] [-t <n>] [-d <fd>] [-I <fd>]\n"
64 #ifdef WITH_TCP
65 " [-H <hostname> [-R <port>] -Y <identity> -K <known_hosts>\n"
66 " -U <username> [-G]] [filename]\n"
67 #else
68 " [filename]\n"
69 #endif
70 #ifdef DEBUG
71 " -E pinentry method (0=pwmd, 1=pwmd async, 2=libpwmd nb)\n"
72 " -y number of pinentry tries before failing (3)\n"
73 #endif
74 #ifdef WITH_TCP
75 " -H connect to hostname\n"
76 " -R alterate port (%i)\n"
77 " -U SSH username\n"
78 " -Y SSH identity file\n"
79 " -K known host's file (for server validation)\n"
80 " -G retrieve the remote SSH host key and exit\n"
81 #endif
82 " -t pinentry timeout\n"
83 " -X disable showing of status messages from the server\n"
84 " -c set the client name\n"
85 " -s socket path (~/.pwmd/socket)\n"
86 " -p passphrase\n"
87 " -P path to the pinentry binary (server default)\n"
88 " -T pinentry tty\n"
89 " -N pinentry terminal type\n"
90 " -D pinentry display\n"
91 " -C pinentry LC_CTYPE\n"
92 " -M pinentry LC_MESSAGES\n"
93 " -d redirect command output to the specified file descriptor\n"
94 " -I read inquire data from the specified file descriptor\n"
95 " -S send the SAVE command before exiting\n"
96 " -i encrypt with the specified number of iterations\n"
97 " -v version\n"
98 " -h this help text\n"), DEFAULT_PORT);
99 exit(EXIT_FAILURE);
102 struct inquire_s {
103 FILE *fp;
104 char *data;
107 static gpg_error_t do_inquire(void *data, const char *keyword, gpg_error_t rc,
108 char **result, size_t *result_len)
110 int c;
111 static char buf[ASSUAN_LINELENGTH];
112 char *p;
113 size_t len = 0;
114 struct inquire_s *inq = (struct inquire_s *)data;
116 if (rc) {
117 memset(buf, 0, sizeof(buf));
118 return rc;
121 buf[0] = 0;
122 p = buf;
124 if (inq->data) {
125 snprintf(buf, sizeof(buf), "%s", inq->data);
126 xfree(inq->data);
127 inq->data = NULL;
128 len = strlen(buf);
129 p = buf + len;
132 while ((c = fgetc(inq->fp)) != EOF) {
133 if (len == sizeof(buf)) {
134 ungetc(c, inq->fp);
135 break;
138 *p++ = c;
139 len++;
142 if (!buf[0]) {
143 memset(buf, 0, sizeof(buf));
144 return GPG_ERR_EOF;
147 *result = buf;
148 *result_len = len;
149 return 0;
152 static int status_msg_cb(void *data, const char *line)
154 fprintf(stderr, "%s\n", line);
155 return 0;
158 #ifdef DEBUG
159 static gpg_error_t do_nb_command(int fd, int which)
161 int n;
162 gpg_error_t error = 0;
164 fcntl(fd, F_SETFL, O_NONBLOCK);
166 do {
167 fd_set fds;
168 struct timeval tv = {0, 50000};
170 FD_ZERO(&fds);
171 FD_SET(fd, &fds);
172 n = select(fd+1, &fds, NULL, NULL, &tv);
174 if (n > 0) {
175 if (FD_ISSET(fd, &fds)) {
176 pwmd_nb_status_t status;
178 n = read(fd, &status, sizeof(status));
180 if (n == -1) {
181 error = gpg_error_from_errno(errno);
182 goto done;
185 if (!which)
186 error = pwmd_open_nb_finalize(pwm, &status);
187 else
188 error = pwmd_save_nb_finalize(pwm, &status);
190 if (error)
191 goto done;
194 else
195 fputc('.', stderr);
196 } while (n == 0);
198 done:
199 return error;
201 #endif
203 int main(int argc, char *argv[])
205 int opt;
206 char *password = NULL;
207 char *filename = NULL;
208 char *socketpath = NULL;
209 char command[ASSUAN_LINELENGTH], *p;
210 int ret = EXIT_SUCCESS;
211 gpg_error_t error;
212 char *result = NULL;
213 int save = 0;
214 char *pinentry_path = NULL;
215 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
216 *lcmessages = NULL;
217 int outfd = STDOUT_FILENO;
218 FILE *outfp = stdout;
219 int inquirefd = STDIN_FILENO;
220 FILE *inquirefp = stdin;
221 int show_status = 1;
222 char *clientname = NULL;
223 char *inquire = NULL;
224 long iter = -2;
225 int have_iter = 0;
226 int timeout = 0;
227 #ifdef WITH_TCP
228 char *host = NULL;
229 int port = DEFAULT_PORT;
230 char *username = NULL;
231 char *ident = NULL;
232 char *known_hosts = NULL;
233 int get = 0;
234 #endif
235 #ifdef DEBUG
236 int tries = 0;
237 int method = 0;
238 pwmd_async_t s;
239 #endif
241 #ifdef ENABLE_NLS
242 setlocale(LC_ALL, "");
243 bindtextdomain("libpwmd", LOCALEDIR);
244 #endif
246 #ifdef DEBUG
247 #ifdef WITH_TCP
248 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) {
249 #else
250 while ((opt = getopt(argc, argv, "C:M:y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
251 #endif
252 #else
253 #ifdef WITH_TCP
254 while ((opt = getopt(argc, argv, "C:M:GK:U:Y:H:R:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
255 #else
256 while ((opt = getopt(argc, argv, "C:M:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
257 #endif
258 #endif
259 switch (opt) {
260 #ifdef DEBUG
261 case 'E':
262 method = atoi(optarg);
264 if (method > 2)
265 method = 2;
266 break;
267 case 'y':
268 tries = atoi(optarg);
269 break;
270 #endif
271 #ifdef WITH_TCP
272 case 'H':
273 host = xstrdup(optarg);
274 break;
275 case 'R':
276 port = atoi(optarg);
277 break;
278 case 'Y':
279 ident = xstrdup(optarg);
280 break;
281 case 'U':
282 username = xstrdup(optarg);
283 break;
284 case 'K':
285 known_hosts = xstrdup(optarg);
286 break;
287 case 'G':
288 get = 1;
289 break;
290 #endif
291 case 'C':
292 lcctype = xstrdup(optarg);
293 break;
294 case 'M':
295 lcmessages = xstrdup(optarg);
296 break;
297 case 't':
298 timeout = atoi(optarg);
299 break;
300 case 'c':
301 clientname = xstrdup(optarg);
302 break;
303 case 'X':
304 show_status = 0;
305 break;
306 case 'T':
307 tty = optarg;
308 break;
309 case 'N':
310 ttytype = optarg;
311 break;
312 case 'D':
313 display = optarg;
314 break;
315 case 'I':
316 inquirefd = atoi(optarg);
317 inquirefp = fdopen(inquirefd, "r");
319 if (!inquirefp) {
320 xfree(password);
321 err(EXIT_FAILURE, "%i", inquirefd);
323 break;
324 case 'd':
325 outfd = atoi(optarg);
326 outfp = fdopen(outfd, "w");
328 if (!outfp) {
329 xfree(password);
330 err(EXIT_FAILURE, "%i", outfd);
332 break;
333 case 'S':
334 save = 1;
335 break;
336 case 'i':
337 iter = strtol(optarg, NULL, 10);
338 have_iter = 1;
339 break;
340 case 's':
341 socketpath = xstrdup(optarg);
342 break;
343 case 'p':
344 password = xstrdup(optarg);
345 memset(optarg, 0, strlen(optarg));
346 break;
347 case 'P':
348 pinentry_path = xstrdup(optarg);
349 break;
350 case 'v':
351 xfree(password);
352 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
353 exit(EXIT_SUCCESS);
354 case 'h':
355 default:
356 xfree(password);
357 usage(argv[0]);
361 filename = argv[optind];
362 pwmd_init();
364 #ifdef WITH_TCP
365 if (host) {
366 #ifdef DEBUG
367 if (method) {
368 if (get) {
369 const char *hostkey;
371 xfree(password);
372 pwm = pwmd_get_hostkey_async(host, port, &error);
374 if (error)
375 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
377 do {
378 s = pwmd_process(pwm, &error);
379 fputc('.', stderr);
380 usleep(50000);
381 } while (s == ASYNC_PROCESS);
383 if (error) {
384 pwmd_close(pwm);
385 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
388 error = pwmd_get_result(pwm, &hostkey);
390 if (error) {
391 pwmd_close(pwm);
392 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
395 printf("%s\n", hostkey);
396 pwmd_finalize(pwm);
397 pwmd_close(pwm);
398 exit(EXIT_SUCCESS);
401 if ((pwm = pwmd_tcp_connect_async(host, port, ident, username, known_hosts, &error)) == NULL) {
402 xfree(password);
403 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
406 do {
407 s = pwmd_process(pwm, &error);
408 fputc('.', stderr);
409 usleep(50000);
410 } while (s == ASYNC_PROCESS);
412 if (error) {
413 pwmd_close(pwm);
414 xfree(password);
415 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
418 pwmd_finalize(pwm);
420 else {
421 #endif
422 if (get) {
423 xfree(password);
424 char *hostkey = pwmd_get_hostkey(host, port, &error);
426 if (error)
427 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
429 printf("%s\n", hostkey);
430 pwmd_free_result(hostkey);
431 exit(EXIT_SUCCESS);
434 if ((pwm = pwmd_tcp_connect(host, port, ident, username, known_hosts, &error)) == NULL) {
435 xfree(password);
436 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
438 #ifdef DEBUG
440 #endif
442 else {
443 #endif
444 if ((pwm = pwmd_connect(socketpath, &error)) == NULL) {
445 xfree(password);
446 errx(EXIT_FAILURE, "pwmd_connect(): %s", pwmd_strerror(error));
448 #ifdef WITH_TCP
450 #endif
452 error = pwmd_command(pwm, &result, "OPTION CLIENT NAME=%s", clientname ? clientname : "pwmc");
453 xfree(clientname);
455 if (error) {
456 xfree(password);
457 goto done;
460 if (have_iter) {
461 error = pwmd_command(pwm, &result, "VERSION");
463 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD) {
464 xfree(password);
465 goto done;
468 pwmd_free_result(result);
470 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
471 if (iter < -1) {
472 xfree(password);
473 pwmd_close(pwm);
474 usage(argv[0]);
477 else {
478 /* pwmd version 2 or later. */
479 if (iter < 0) {
480 xfree(password);
481 pwmd_close(pwm);
482 usage(argv[0]);
487 if (timeout > 0) {
488 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
490 if (error) {
491 xfree(password);
492 goto done;
496 if (password) {
497 error = pwmd_setopt(pwm, PWMD_OPTION_PASSWORD, password);
499 if (error) {
500 xfree(password);
501 goto done;
504 xfree(password);
506 else {
507 if (pinentry_path) {
508 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
510 if (error)
511 goto done;
514 if (display) {
515 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
517 if (error)
518 goto done;
521 if (tty) {
522 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
524 if (error)
525 goto done;
528 if (ttytype) {
529 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
531 if (error)
532 goto done;
535 if (lcctype) {
536 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
538 if (error)
539 goto done;
542 if (lcmessages) {
543 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
544 lcmessages);
546 if (error)
547 goto done;
550 #ifdef DEBUG
551 if (method >= 2) {
552 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1);
554 if (error)
555 goto done;
558 if (tries > 0) {
559 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
561 if (error)
562 goto done;
564 #endif
567 if (show_status) {
568 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_FUNC, status_msg_cb);
570 if (error)
571 goto done;
574 if (filename) {
575 #ifdef DEBUG
576 /* This method doesn't support PWMD_OPTION_PINENTRY_TRIES. */
577 if (method == 1) {
578 error = pwmd_open_async(pwm, filename);
580 if (!error) {
581 do {
582 s = pwmd_process(pwm, &error);
583 fputc('.', stderr);
584 usleep(50000);
585 } while (s == ASYNC_PROCESS);
587 pwmd_finalize(pwm);
590 else if (method == 2) {
591 int fd = pwmd_open_nb(pwm, &error, filename, timeout);
593 if (fd == -1)
594 goto done;
595 else if (fd >= 0)
596 error = do_nb_command(fd, 0);
598 else
599 error = pwmd_open(pwm, filename);
600 #else
601 error = pwmd_open(pwm, filename);
602 #endif
604 if (error)
605 goto done;
608 if (filename) {
609 error = pwmd_command(pwm, &result, "LOCK");
611 if (error)
612 goto done;
615 #ifdef DEBUG
616 if (method) {
617 for (;;) {
618 struct timeval tv = {0, 100000};
619 fd_set rfds;
620 int n;
622 FD_ZERO(&rfds);
623 FD_SET(STDIN_FILENO, &rfds);
624 n = select(STDIN_FILENO+1, &rfds, NULL, NULL, &tv);
626 if (n == 0) {
627 s = pwmd_process(pwm, &error);
629 if (error)
630 goto done;
632 fprintf(stderr, ".");
633 continue;
636 if (n == -1) {
637 error = gpg_error_from_errno(errno);
638 goto done;
641 fprintf(stderr, "\n");
642 n = read(STDIN_FILENO, command, sizeof(command));
644 if (n == -1) {
645 error = gpg_error_from_errno(errno);
646 goto done;
649 if (n && command[strlen(command)-1] == '\n')
650 command[strlen(command)-1] = 0;
652 command[n] = 0;
653 p = command;
654 break;
657 else
658 p = fgets(command, sizeof(command), stdin);
659 #else
660 p = fgets(command, sizeof(command), stdin);
661 #endif
663 if (!p || !*p)
664 goto done;
667 * This is a known INQUIRE command. We use pwmd_inquire() to send the
668 * data from the do_inquire() callback function.
670 if (strncasecmp(p, "STORE ", 6) == 0) {
671 p += 6;
672 inquire = (char *)"STORE";
674 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
675 p += 7;
676 inquire = (char *)"IMPORT";
679 if (inquire) {
680 struct inquire_s *inq = (struct inquire_s *)malloc(sizeof(struct inquire_s));
682 if (!inq) {
683 error = gpg_error_from_errno(ENOMEM);
684 goto done;
687 inq->data = xstrdup(p);
688 inq->fp = inquirefp;
689 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
690 free(inq);
691 goto done;
694 if (strcasecmp(p, "BYE") == 0)
695 goto done;
697 error = pwmd_command(pwm, &result, command);
698 memset(command, 0, sizeof(command));
700 if (error)
701 goto done;
703 if (result) {
704 fwrite(result, 1, strlen(result), outfp);
705 pwmd_free_result(result);
708 done:
709 memset(command, 0, sizeof(command));
711 if (!error && save) {
712 if (iter != -2) {
713 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
715 if (error)
716 goto done;
719 #ifdef DEBUG
720 if (method == 1) {
721 error = pwmd_save_async(pwm);
723 if (!error) {
724 do {
725 s = pwmd_process(pwm, &error);
726 fputc('.', stderr);
727 usleep(50000);
728 } while (s == ASYNC_PROCESS);
730 pwmd_finalize(pwm);
733 else if (method == 3) {
734 int fd = pwmd_save_nb(pwm, &error);
736 if (fd == -1)
737 goto done;
738 else if (fd >= 0)
739 error = do_nb_command(fd, 1);
741 else
742 error = pwmd_save(pwm);
743 #else
744 error = pwmd_save(pwm);
745 #endif
748 if (!error && filename)
749 error = pwmd_command(pwm, &result, "UNLOCK");
751 if (error) {
752 show_error(error);
753 ret = EXIT_FAILURE;
756 pwmd_close(pwm);
758 if (socketpath)
759 xfree(socketpath);
761 exit(ret);