Revert "Let pwmd_process() work when not inside a command. This is needed so"
[libpwmd.git] / src / pwmc.c
blob582564c1d439e8174f25a6700268824f0fb2e37f
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 "Reads PWMD protocol commands 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 " -y number of pinentry tries before failing (3)\n"
72 #endif
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 " -h this help text\n"), DEFAULT_PORT);
98 exit(EXIT_FAILURE);
101 struct inquire_s {
102 FILE *fp;
103 char *data;
106 static gpg_error_t do_inquire(void *data, const char *keyword, gpg_error_t rc,
107 char **result, size_t *result_len)
109 int c;
110 static char buf[ASSUAN_LINELENGTH];
111 char *p;
112 size_t len = 0;
113 struct inquire_s *inq = (struct inquire_s *)data;
115 if (rc) {
116 memset(buf, 0, sizeof(buf));
117 return rc;
120 buf[0] = 0;
121 p = buf;
123 if (inq->data) {
124 snprintf(buf, sizeof(buf), "%s", inq->data);
125 xfree(inq->data);
126 inq->data = NULL;
127 len = strlen(buf);
128 p = buf + len;
131 while ((c = fgetc(inq->fp)) != EOF) {
132 if (len == sizeof(buf)) {
133 ungetc(c, inq->fp);
134 break;
137 *p++ = c;
138 len++;
141 if (!buf[0]) {
142 memset(buf, 0, sizeof(buf));
143 return GPG_ERR_EOF;
146 *result = buf;
147 *result_len = len;
148 return 0;
151 static int status_msg_cb(void *data, const char *line)
153 fprintf(stderr, "%s\n", line);
154 return 0;
157 #ifdef DEBUG
158 static gpg_error_t do_nb_command(int fd, int which)
160 int n;
161 gpg_error_t error = 0;
163 fcntl(fd, F_SETFL, O_NONBLOCK);
165 do {
166 fd_set fds;
167 struct timeval tv = {0, 50000};
169 FD_ZERO(&fds);
170 FD_SET(fd, &fds);
171 n = select(fd+1, &fds, NULL, NULL, &tv);
173 if (n > 0) {
174 if (FD_ISSET(fd, &fds)) {
175 pwmd_nb_status_t status;
177 n = read(fd, &status, sizeof(status));
179 if (n == -1) {
180 error = gpg_error_from_errno(errno);
181 goto done;
184 if (!which)
185 error = pwmd_open_nb_finalize(pwm, &status);
186 else
187 error = pwmd_save_nb_finalize(pwm, &status);
189 if (error)
190 goto done;
193 else
194 fputc('.', stderr);
195 } while (n == 0);
197 done:
198 return error;
200 #endif
202 int main(int argc, char *argv[])
204 int opt;
205 char *password = NULL;
206 char *filename = NULL;
207 char *socketpath = NULL;
208 char command[ASSUAN_LINELENGTH], *p;
209 int ret = EXIT_SUCCESS;
210 gpg_error_t error;
211 char *result = NULL;
212 int save = 0;
213 char *pinentry_path = NULL;
214 char *display = NULL, *tty = NULL, *ttytype = NULL, *lcctype = NULL,
215 *lcmessages = NULL;
216 int outfd = STDOUT_FILENO;
217 FILE *outfp = stdout;
218 int inquirefd = STDIN_FILENO;
219 FILE *inquirefp = stdin;
220 int show_status = 1;
221 char *clientname = NULL;
222 char *inquire = NULL;
223 long iter = -2;
224 int have_iter = 0;
225 int timeout = 0;
226 #ifdef WITH_TCP
227 char *host = NULL;
228 int port = DEFAULT_PORT;
229 char *username = NULL;
230 char *ident = NULL;
231 char *known_hosts = NULL;
232 int get = 0;
233 #endif
234 #ifdef DEBUG
235 int tries = 0;
236 int method = 0;
237 pwmd_async_t s;
238 #endif
240 #ifdef ENABLE_NLS
241 setlocale(LC_ALL, "");
242 bindtextdomain("libpwmd", LOCALEDIR);
243 #endif
245 #ifdef DEBUG
246 #ifdef WITH_TCP
247 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) {
248 #else
249 while ((opt = getopt(argc, argv, "C:M:y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
250 #endif
251 #else
252 #ifdef WITH_TCP
253 while ((opt = getopt(argc, argv, "C:M:GK:U:Y:H:R:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
254 #else
255 while ((opt = getopt(argc, argv, "C:M:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
256 #endif
257 #endif
258 switch (opt) {
259 #ifdef DEBUG
260 case 'E':
261 method = atoi(optarg);
263 if (method > 2)
264 method = 2;
265 break;
266 case 'y':
267 tries = atoi(optarg);
268 break;
269 #endif
270 #ifdef WITH_TCP
271 case 'H':
272 host = xstrdup(optarg);
273 break;
274 case 'R':
275 port = atoi(optarg);
276 break;
277 case 'Y':
278 ident = xstrdup(optarg);
279 break;
280 case 'U':
281 username = xstrdup(optarg);
282 break;
283 case 'K':
284 known_hosts = xstrdup(optarg);
285 break;
286 case 'G':
287 get = 1;
288 break;
289 #endif
290 case 'C':
291 lcctype = xstrdup(optarg);
292 break;
293 case 'M':
294 lcmessages = xstrdup(optarg);
295 break;
296 case 't':
297 timeout = atoi(optarg);
298 break;
299 case 'c':
300 clientname = xstrdup(optarg);
301 break;
302 case 'X':
303 show_status = 0;
304 break;
305 case 'T':
306 tty = optarg;
307 break;
308 case 'N':
309 ttytype = optarg;
310 break;
311 case 'D':
312 display = optarg;
313 break;
314 case 'I':
315 inquirefd = atoi(optarg);
316 inquirefp = fdopen(inquirefd, "r");
318 if (!inquirefp) {
319 xfree(password);
320 err(EXIT_FAILURE, "%i", inquirefd);
322 break;
323 case 'd':
324 outfd = atoi(optarg);
325 outfp = fdopen(outfd, "w");
327 if (!outfp) {
328 xfree(password);
329 err(EXIT_FAILURE, "%i", outfd);
331 break;
332 case 'S':
333 save = 1;
334 break;
335 case 'i':
336 iter = strtol(optarg, NULL, 10);
337 have_iter = 1;
338 break;
339 case 's':
340 socketpath = xstrdup(optarg);
341 break;
342 case 'p':
343 password = xstrdup(optarg);
344 memset(optarg, 0, strlen(optarg));
345 break;
346 case 'P':
347 pinentry_path = xstrdup(optarg);
348 break;
349 case 'v':
350 xfree(password);
351 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
352 exit(EXIT_SUCCESS);
353 case 'h':
354 default:
355 xfree(password);
356 usage(argv[0]);
360 filename = argv[optind];
361 pwmd_init();
363 #ifdef WITH_TCP
364 if (host) {
365 #ifdef DEBUG
366 if (method) {
367 if (get) {
368 const char *hostkey;
370 xfree(password);
371 pwm = pwmd_get_hostkey_async(host, port, &error);
373 if (error)
374 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
376 do {
377 s = pwmd_process(pwm, &error);
378 fputc('.', stderr);
379 usleep(50000);
380 } while (s == ASYNC_PROCESS);
382 if (error) {
383 pwmd_close(pwm);
384 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
387 error = pwmd_get_result(pwm, &hostkey);
389 if (error) {
390 pwmd_close(pwm);
391 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
394 printf("%s\n", hostkey);
395 pwmd_finalize(pwm);
396 pwmd_close(pwm);
397 exit(EXIT_SUCCESS);
400 if ((pwm = pwmd_tcp_connect_async(host, port, ident, username, known_hosts, &error)) == NULL) {
401 xfree(password);
402 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
405 do {
406 s = pwmd_process(pwm, &error);
407 fputc('.', stderr);
408 usleep(50000);
409 } while (s == ASYNC_PROCESS);
411 if (error) {
412 pwmd_close(pwm);
413 xfree(password);
414 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
417 pwmd_finalize(pwm);
419 else {
420 #endif
421 if (get) {
422 xfree(password);
423 char *hostkey = pwmd_get_hostkey(host, port, &error);
425 if (error)
426 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
428 printf("%s\n", hostkey);
429 pwmd_free_result(hostkey);
430 exit(EXIT_SUCCESS);
433 if ((pwm = pwmd_tcp_connect(host, port, ident, username, known_hosts, &error)) == NULL) {
434 xfree(password);
435 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
437 #ifdef DEBUG
439 #endif
441 else {
442 #endif
443 if ((pwm = pwmd_connect(socketpath, &error)) == NULL) {
444 xfree(password);
445 errx(EXIT_FAILURE, "pwmd_connect(): %s", pwmd_strerror(error));
447 #ifdef WITH_TCP
449 #endif
451 error = pwmd_command(pwm, &result, "OPTION CLIENT NAME=%s", clientname ? clientname : "pwmc");
452 xfree(clientname);
454 if (error) {
455 xfree(password);
456 goto done;
459 if (have_iter) {
460 error = pwmd_command(pwm, &result, "VERSION");
462 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD) {
463 xfree(password);
464 goto done;
467 pwmd_free_result(result);
469 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
470 if (iter < -1) {
471 xfree(password);
472 pwmd_close(pwm);
473 usage(argv[0]);
476 else {
477 /* pwmd version 2 or later. */
478 if (iter < 0) {
479 xfree(password);
480 pwmd_close(pwm);
481 usage(argv[0]);
486 if (timeout > 0) {
487 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TIMEOUT, timeout);
489 if (error) {
490 xfree(password);
491 goto done;
495 if (password) {
496 error = pwmd_setopt(pwm, PWMD_OPTION_PASSWORD, password);
498 if (error) {
499 xfree(password);
500 goto done;
503 xfree(password);
505 else {
506 if (pinentry_path) {
507 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pinentry_path);
509 if (error)
510 goto done;
513 if (display) {
514 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DISPLAY, display);
516 if (error)
517 goto done;
520 if (tty) {
521 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TTY, tty);
523 if (error)
524 goto done;
527 if (ttytype) {
528 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TERM, ttytype);
530 if (error)
531 goto done;
534 if (lcctype) {
535 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, lcctype);
537 if (error)
538 goto done;
541 if (lcmessages) {
542 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES,
543 lcmessages);
545 if (error)
546 goto done;
549 #ifdef DEBUG
550 if (method >= 2) {
551 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1);
553 if (error)
554 goto done;
557 if (tries > 0) {
558 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
560 if (error)
561 goto done;
563 #endif
566 if (show_status) {
567 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_FUNC, status_msg_cb);
569 if (error)
570 goto done;
573 if (filename) {
574 #ifdef DEBUG
575 /* This method doesn't support PWMD_OPTION_PINENTRY_TRIES. */
576 if (method == 1) {
577 error = pwmd_open_async(pwm, filename);
579 if (!error) {
580 do {
581 s = pwmd_process(pwm, &error);
582 fputc('.', stderr);
583 usleep(50000);
584 } while (s == ASYNC_PROCESS);
586 pwmd_finalize(pwm);
589 else if (method == 2) {
590 int fd = pwmd_open_nb(pwm, &error, filename, timeout);
592 if (fd == -1)
593 goto done;
594 else if (fd >= 0)
595 error = do_nb_command(fd, 0);
597 else
598 error = pwmd_open(pwm, filename);
599 #else
600 error = pwmd_open(pwm, filename);
601 #endif
603 if (error)
604 goto done;
607 if (filename) {
608 error = pwmd_command(pwm, &result, "LOCK");
610 if (error)
611 goto done;
614 p = fgets(command, sizeof(command), stdin);
616 if (!p)
617 goto done;
620 * This is a known INQUIRE command. We use pwmd_inquire() to send the
621 * data from the do_inquire() callback function.
623 if (strncasecmp(p, "STORE ", 6) == 0) {
624 p += 6;
625 inquire = (char *)"STORE";
627 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
628 p += 7;
629 inquire = (char *)"IMPORT";
632 if (inquire) {
633 struct inquire_s *inq = (struct inquire_s *)malloc(sizeof(struct inquire_s));
635 if (!inq) {
636 error = gpg_error_from_errno(ENOMEM);
637 goto done;
640 inq->data = xstrdup(p);
641 inq->fp = inquirefp;
642 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
643 free(inq);
644 goto done;
647 if (strcasecmp(p, "BYE") == 0)
648 goto done;
650 error = pwmd_command(pwm, &result, command);
651 memset(command, 0, sizeof(command));
653 if (error)
654 goto done;
656 if (result) {
657 fwrite(result, 1, strlen(result), outfp);
658 pwmd_free_result(result);
661 done:
662 memset(command, 0, sizeof(command));
664 if (!error && save) {
665 if (iter != -2) {
666 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
668 if (error)
669 goto done;
672 #ifdef DEBUG
673 if (method == 1) {
674 error = pwmd_save_async(pwm);
676 if (!error) {
677 do {
678 s = pwmd_process(pwm, &error);
679 fputc('.', stderr);
680 usleep(50000);
681 } while (s == ASYNC_PROCESS);
683 pwmd_finalize(pwm);
686 else if (method == 3) {
687 int fd = pwmd_save_nb(pwm, &error);
689 if (fd == -1)
690 goto done;
691 else if (fd >= 0)
692 error = do_nb_command(fd, 1);
694 else
695 error = pwmd_save(pwm);
696 #else
697 error = pwmd_save(pwm);
698 #endif
701 if (!error && filename)
702 error = pwmd_command(pwm, &result, "UNLOCK");
704 if (error) {
705 show_error(error);
706 ret = EXIT_FAILURE;
709 pwmd_close(pwm);
711 if (socketpath)
712 xfree(socketpath);
714 exit(ret);