Fixed some memory leaks.
[libpwmd.git] / src / pwmc.c
blob3957eb01c33adea3e99e533a582d0dfc07f7ca1e
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2007-2008 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 #ifndef MEM_DEBUG
44 #include "mem.h"
45 #else
46 #define xfree free
47 #define xrealloc realloc
48 #define xmalloc malloc
49 #define xstrdup strdup
50 #define xcalloc calloc
51 #endif
53 #define DEFAULT_PORT 22
54 pwm_t *pwm;
56 static void show_error(gpg_error_t error)
58 fprintf(stderr, "ERR %i %s\n", gpg_err_code(error), pwmd_strerror(error));
61 static void usage(const char *pn)
63 fprintf(stderr, N_(
64 "Reads PWMD protocol commands from standard input.\n\n"
65 "Usage: pwmc [-hvX] [-s <socket>] "
66 #ifdef DEBUG
67 "[-E <n>] [-y <n>]"
68 #endif
69 "[-PTND <string>] [-p <passphrase>]\n"
70 " [-S [-i <iter>]] [-c <name>] [-t <n>] [-d <fd>] [-I <fd>]\n"
71 #ifdef WITH_TCP
72 " [-H <hostname> [-R <port>] -Y <identity> -K <known_hosts>\n"
73 " -U <username> [-G]] [filename]\n"
74 #else
75 " [filename]\n"
76 #endif
77 #ifdef DEBUG
78 " -E pinentry method (0=pwmd, 1=pwmd async, 2=libpwmd nb)\n"
79 " -y number of pinentry tries before failing (3)\n"
80 #endif
81 #ifdef WITH_TCP
82 " -H connect to hostname\n"
83 " -R alterate port (%i)\n"
84 " -U SSH username\n"
85 " -Y SSH identity file\n"
86 " -K known host's file (for server validation)\n"
87 " -G retrieve the remote SSH host key and exit\n"
88 #endif
89 " -t pinentry timeout\n"
90 " -X disable showing of status messages from the server\n"
91 " -c set the client name\n"
92 " -s socket path (~/.pwmd/socket)\n"
93 " -p passphrase\n"
94 " -P path to the pinentry binary (server default)\n"
95 " -T pinentry tty\n"
96 " -N pinentry terminal type\n"
97 " -D pinentry display\n"
98 " -d redirect command output to the specified file descriptor\n"
99 " -I read inquire data from the specified file descriptor\n"
100 " -S send the SAVE command before exiting\n"
101 " -i encrypt with the specified number of iterations\n"
102 " -v version\n"
103 " -h this help text\n"), DEFAULT_PORT);
104 exit(EXIT_FAILURE);
107 struct inquire_s {
108 FILE *fp;
109 char *data;
112 static gpg_error_t do_inquire(void *data, const char *keyword, gpg_error_t rc,
113 char **result, size_t *result_len)
115 int c;
116 static char buf[ASSUAN_LINELENGTH];
117 char *p;
118 size_t len = 0;
119 struct inquire_s *inq = (struct inquire_s *)data;
121 if (rc) {
122 memset(buf, 0, sizeof(buf));
123 return rc;
126 buf[0] = 0;
127 p = buf;
129 if (inq->data) {
130 snprintf(buf, sizeof(buf), "%s", inq->data);
131 xfree(inq->data);
132 inq->data = NULL;
133 len = strlen(buf);
134 p = buf + len;
137 while ((c = fgetc(inq->fp)) != EOF) {
138 if (len == sizeof(buf)) {
139 ungetc(c, inq->fp);
140 break;
143 *p++ = c;
144 len++;
147 if (!buf[0]) {
148 memset(buf, 0, sizeof(buf));
149 return GPG_ERR_EOF;
152 *result = buf;
153 *result_len = len;
154 return 0;
157 static int status_msg_cb(void *data, const char *line)
159 fprintf(stderr, "%s\n", line);
160 return 0;
163 #ifdef DEBUG
164 static gpg_error_t do_nb_command(int fd, int which)
166 int n;
167 gpg_error_t error = 0;
169 fcntl(fd, F_SETFL, O_NONBLOCK);
171 do {
172 fd_set fds;
173 struct timeval tv = {0, 50000};
175 FD_ZERO(&fds);
176 FD_SET(fd, &fds);
177 n = select(fd+1, &fds, NULL, NULL, &tv);
179 if (n > 0) {
180 if (FD_ISSET(fd, &fds)) {
181 pwmd_nb_status_t status;
183 n = read(fd, &status, sizeof(status));
185 if (n == -1) {
186 error = gpg_error_from_errno(errno);
187 goto done;
190 if (!which)
191 error = pwmd_open_nb_finalize(pwm, &status);
192 else
193 error = pwmd_save_nb_finalize(pwm, &status);
195 if (error)
196 goto done;
199 else
200 fputc('.', stderr);
201 } while (n == 0);
203 done:
204 return error;
206 #endif
208 int main(int argc, char *argv[])
210 int opt;
211 char *password = NULL;
212 char *filename = NULL;
213 char *socketpath = NULL;
214 char command[ASSUAN_LINELENGTH], *p;
215 int ret = EXIT_SUCCESS;
216 gpg_error_t error;
217 char *result = NULL;
218 int save = 0;
219 char *pinentry_path = NULL;
220 char *display = NULL, *tty = NULL, *ttytype = NULL;
221 int outfd = STDOUT_FILENO;
222 FILE *outfp = stdout;
223 int inquirefd = STDIN_FILENO;
224 FILE *inquirefp = stdin;
225 int show_status = 1;
226 char *clientname = NULL;
227 char *inquire = NULL;
228 long iter = -2;
229 int have_iter = 0;
230 int timeout = 0;
231 #ifdef WITH_TCP
232 char *host = NULL;
233 int port = DEFAULT_PORT;
234 char *username = NULL;
235 char *ident = NULL;
236 char *known_hosts = NULL;
237 int get = 0;
238 #endif
239 #ifdef DEBUG
240 int tries = 0;
241 int method = 0;
242 pwmd_async_t s;
243 #endif
245 setlocale(LC_ALL, "");
246 bindtextdomain("libpwmd", LOCALEDIR);
248 #ifdef DEBUG
249 #ifdef WITH_TCP
250 while ((opt = getopt(argc, argv, "GK:U:Y:H:R:y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
251 #else
252 while ((opt = getopt(argc, argv, "y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
253 #endif
254 #else
255 #ifdef WITH_TCP
256 while ((opt = getopt(argc, argv, "GK:U:Y:H:R:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
257 #else
258 while ((opt = getopt(argc, argv, "t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
259 #endif
260 #endif
261 switch (opt) {
262 #ifdef DEBUG
263 case 'E':
264 method = atoi(optarg);
266 if (method > 2)
267 method = 2;
268 break;
269 case 'y':
270 tries = atoi(optarg);
271 break;
272 #endif
273 #ifdef WITH_TCP
274 case 'H':
275 host = xstrdup(optarg);
276 break;
277 case 'R':
278 port = atoi(optarg);
279 break;
280 case 'Y':
281 ident = xstrdup(optarg);
282 break;
283 case 'U':
284 username = xstrdup(optarg);
285 break;
286 case 'K':
287 known_hosts = xstrdup(optarg);
288 break;
289 case 'G':
290 get = 1;
291 break;
292 #endif
293 case 't':
294 timeout = atoi(optarg);
295 break;
296 case 'c':
297 clientname = xstrdup(optarg);
298 break;
299 case 'X':
300 show_status = 0;
301 break;
302 case 'T':
303 tty = optarg;
304 break;
305 case 'N':
306 ttytype = optarg;
307 break;
308 case 'D':
309 display = optarg;
310 break;
311 case 'I':
312 inquirefd = atoi(optarg);
313 inquirefp = fdopen(inquirefd, "r");
315 if (!inquirefp) {
316 xfree(password);
317 err(EXIT_FAILURE, "%i", inquirefd);
319 break;
320 case 'd':
321 outfd = atoi(optarg);
322 outfp = fdopen(outfd, "w");
324 if (!outfp) {
325 xfree(password);
326 err(EXIT_FAILURE, "%i", outfd);
328 break;
329 case 'S':
330 save = 1;
331 break;
332 case 'i':
333 iter = strtol(optarg, NULL, 10);
334 have_iter = 1;
335 break;
336 case 's':
337 socketpath = xstrdup(optarg);
338 break;
339 case 'p':
340 password = xstrdup(optarg);
341 memset(optarg, 0, strlen(optarg));
342 break;
343 case 'P':
344 pinentry_path = xstrdup(optarg);
345 break;
346 case 'v':
347 xfree(password);
348 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
349 exit(EXIT_SUCCESS);
350 case 'h':
351 default:
352 xfree(password);
353 usage(argv[0]);
357 filename = argv[optind];
358 pwmd_init();
360 #ifdef WITH_TCP
361 if (host) {
362 #ifdef DEBUG
363 if (method) {
364 if (get) {
365 const char *hostkey;
367 xfree(password);
368 pwm = pwmd_get_hostkey_async(host, port, &error);
370 if (error)
371 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
373 do {
374 s = pwmd_process(pwm, &error);
375 fputc('.', stderr);
376 usleep(50000);
377 } while (s == ASYNC_PROCESS);
379 if (error) {
380 pwmd_close(pwm);
381 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
384 error = pwmd_get_result(pwm, &hostkey);
386 if (error) {
387 pwmd_close(pwm);
388 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
391 printf("%s\n", hostkey);
392 pwmd_finalize(pwm);
393 pwmd_close(pwm);
394 exit(EXIT_SUCCESS);
397 if ((pwm = pwmd_tcp_connect_async(host, port, ident, username, known_hosts, &error)) == NULL) {
398 xfree(password);
399 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
402 do {
403 s = pwmd_process(pwm, &error);
404 fputc('.', stderr);
405 usleep(50000);
406 } while (s == ASYNC_PROCESS);
408 if (error) {
409 pwmd_close(pwm);
410 xfree(password);
411 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
414 pwmd_finalize(pwm);
416 else {
417 #endif
418 if (get) {
419 xfree(password);
420 char *hostkey = pwmd_get_hostkey(host, port, &error);
422 if (error)
423 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
425 printf("%s\n", hostkey);
426 pwmd_free_result(hostkey);
427 exit(EXIT_SUCCESS);
430 if ((pwm = pwmd_tcp_connect(host, port, ident, username, known_hosts, &error)) == NULL) {
431 xfree(password);
432 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
434 #ifdef DEBUG
436 #endif
438 else {
439 #endif
440 if ((pwm = pwmd_connect(socketpath, &error)) == NULL) {
441 xfree(password);
442 errx(EXIT_FAILURE, "pwmd_connect(): %s", pwmd_strerror(error));
444 #ifdef WITH_TCP
446 #endif
448 error = pwmd_command(pwm, &result, "OPTION CLIENT NAME=%s", clientname ? clientname : "pwmc");
449 xfree(clientname);
451 if (error) {
452 xfree(password);
453 goto done;
456 if (have_iter) {
457 error = pwmd_command(pwm, &result, "VERSION");
459 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD) {
460 xfree(password);
461 goto done;
464 pwmd_free_result(result);
466 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
467 if (iter < -1) {
468 xfree(password);
469 pwmd_close(pwm);
470 usage(argv[0]);
473 else {
474 /* pwmd version 2 or later. */
475 if (iter < 0) {
476 xfree(password);
477 pwmd_close(pwm);
478 usage(argv[0]);
483 if (timeout > 0) {
484 error = pwmd_command(pwm, &result, "OPTION TIMEOUT=%i", timeout);
486 if (error) {
487 xfree(password);
488 goto done;
492 if (password) {
493 error = pwmd_setopt(pwm, PWMD_OPTION_PASSWORD, password);
495 if (error) {
496 xfree(password);
497 goto done;
500 xfree(password);
502 else {
503 if (pinentry_path) {
504 error = pwmd_command(pwm, &result, "OPTION PATH=%s", pinentry_path);
506 if (error)
507 goto done;
510 if (display) {
511 error = pwmd_command(pwm, &result, "OPTION DISPLAY=%s", display);
513 if (error)
514 goto done;
517 if (tty) {
518 error = pwmd_command(pwm, &result, "OPTION TTYNAME=%s", tty);
520 if (error)
521 goto done;
524 if (ttytype) {
525 error = pwmd_command(pwm, &result, "OPTION TTYTYPE=%s", ttytype);
527 if (error)
528 goto done;
530 #ifdef DEBUG
531 if (method >= 2) {
532 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1);
534 if (error)
535 goto done;
538 if (tries > 0) {
539 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
541 if (error)
542 goto done;
544 #endif
547 if (show_status) {
548 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_FUNC, status_msg_cb);
550 if (error)
551 goto done;
554 if (filename) {
555 #ifdef DEBUG
556 /* This method doesn't support PWMD_OPTION_PINENTRY_TRIES. */
557 if (method == 1) {
558 error = pwmd_open_async(pwm, filename);
560 if (!error) {
561 do {
562 s = pwmd_process(pwm, &error);
563 fputc('.', stderr);
564 usleep(50000);
565 } while (s == ASYNC_PROCESS);
567 pwmd_finalize(pwm);
570 else if (method == 2) {
571 int fd = pwmd_open_nb(pwm, &error, filename, timeout);
573 if (fd == -1)
574 goto done;
575 else if (fd >= 0)
576 error = do_nb_command(fd, 0);
578 else
579 error = pwmd_open(pwm, filename);
580 #else
581 error = pwmd_open(pwm, filename);
582 #endif
584 if (error)
585 goto done;
588 if (filename) {
589 error = pwmd_command(pwm, &result, "LOCK");
591 if (error)
592 goto done;
595 p = fgets(command, sizeof(command), stdin);
597 if (!p)
598 goto done;
601 * This is a known INQUIRE command. We use pwmd_inquire() to send the
602 * data from the do_inquire() callback function.
604 if (strncasecmp(p, "STORE ", 6) == 0) {
605 p += 6;
606 inquire = (char *)"STORE";
608 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
609 p += 7;
610 inquire = (char *)"IMPORT";
613 if (inquire) {
614 struct inquire_s *inq = (struct inquire_s *)malloc(sizeof(struct inquire_s));
616 if (!inq) {
617 error = gpg_error_from_errno(ENOMEM);
618 goto done;
621 inq->data = xstrdup(p);
622 inq->fp = inquirefp;
623 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
624 free(inq);
625 goto done;
628 if (strcasecmp(p, "BYE") == 0)
629 goto done;
631 error = pwmd_command(pwm, &result, command);
632 memset(command, 0, sizeof(command));
634 if (error)
635 goto done;
637 if (result) {
638 fwrite(result, 1, strlen(result), outfp);
639 pwmd_free_result(result);
642 done:
643 memset(command, 0, sizeof(command));
645 if (!error && save) {
646 if (iter != -2) {
647 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
649 if (error)
650 goto done;
653 #ifdef DEBUG
654 if (method == 1) {
655 error = pwmd_save_async(pwm);
657 if (!error) {
658 do {
659 s = pwmd_process(pwm, &error);
660 fputc('.', stderr);
661 usleep(50000);
662 } while (s == ASYNC_PROCESS);
664 pwmd_finalize(pwm);
667 else if (method == 3) {
668 int fd = pwmd_save_nb(pwm, &error);
670 if (fd == -1)
671 goto done;
672 else if (fd >= 0)
673 error = do_nb_command(fd, 1);
675 else
676 error = pwmd_save(pwm);
677 #else
678 error = pwmd_save(pwm);
679 #endif
682 if (!error && filename)
683 error = pwmd_command(pwm, &result, "UNLOCK");
685 if (error) {
686 show_error(error);
687 ret = EXIT_FAILURE;
690 pwmd_close(pwm);
692 if (socketpath)
693 xfree(socketpath);
695 exit(ret);