Use secure memory functions.
[libpwmd.git] / src / pwmc.c
blobefb1f5e5a94f297f476ef27d4b6d15cdb95e4241
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>] [-V] [-C <cipher>]] [filename]\n"
73 #else
74 " [filename]\n"
75 #endif
76 #ifdef DEBUG
77 " -E pinentry method (0=pwmd, 1=pwmd async, 2=libpwmd nb)\n"
78 " -y number of pinentry tries before failing (3)\n"
79 #endif
80 #ifdef WITH_TCP
81 " -H connect to hostname\n"
82 " -R alterate port (%i)\n"
83 " -V verify peer hostname\n"
84 " -C cipher suite to use (in priority format)\n"
85 #endif
86 " -t pinentry timeout\n"
87 " -X disable showing of status messages from the server\n"
88 " -c set the client name\n"
89 " -s socket path (~/.pwmd/socket)\n"
90 " -p passphrase\n"
91 " -P path to the pinentry binary (server default)\n"
92 " -T pinentry tty\n"
93 " -N pinentry terminal type\n"
94 " -D pinentry display\n"
95 " -d redirect command output to the specified file descriptor\n"
96 " -I read inquire data from the specified file descriptor\n"
97 " -S send the SAVE command before exiting\n"
98 " -i encrypt with the specified number of iterations\n"
99 " -v version\n"
100 " -h this help text\n"), DEFAULT_PORT);
101 exit(EXIT_FAILURE);
104 struct inquire_s {
105 FILE *fp;
106 char *data;
109 static gpg_error_t do_inquire(void *data, const char *keyword, gpg_error_t rc,
110 char **result, size_t *result_len)
112 int c;
113 static char buf[ASSUAN_LINELENGTH];
114 char *p;
115 size_t len = 0;
116 struct inquire_s *inq = (struct inquire_s *)data;
118 if (rc) {
119 memset(buf, 0, sizeof(buf));
120 return rc;
123 buf[0] = 0;
124 p = buf;
126 if (inq->data) {
127 snprintf(buf, sizeof(buf), "%s", inq->data);
128 xfree(inq->data);
129 inq->data = NULL;
130 len = strlen(buf);
131 p = buf + len;
134 while ((c = fgetc(inq->fp)) != EOF) {
135 if (len == sizeof(buf)) {
136 ungetc(c, inq->fp);
137 break;
140 *p++ = c;
141 len++;
144 if (!buf[0]) {
145 memset(buf, 0, sizeof(buf));
146 return GPG_ERR_EOF;
149 *result = buf;
150 *result_len = len;
151 return 0;
154 static int status_msg_cb(void *data, const char *line)
156 fprintf(stderr, "%s\n", line);
157 return 0;
160 #ifdef DEBUG
161 static gpg_error_t do_nb_command(int fd, int which)
163 int n;
164 gpg_error_t error = 0;
166 fcntl(fd, F_SETFL, O_NONBLOCK);
168 do {
169 fd_set fds;
170 struct timeval tv = {0, 50000};
172 FD_ZERO(&fds);
173 FD_SET(fd, &fds);
174 n = select(fd+1, &fds, NULL, NULL, &tv);
176 if (n > 0) {
177 if (FD_ISSET(fd, &fds)) {
178 pwmd_nb_status_t status;
180 n = read(fd, &status, sizeof(status));
182 if (n == -1) {
183 error = gpg_error_from_errno(errno);
184 goto done;
187 if (!which)
188 error = pwmd_open_nb_finalize(pwm, &status);
189 else
190 error = pwmd_save_nb_finalize(pwm, &status);
192 if (error)
193 goto done;
196 else
197 fputc('.', stderr);
198 } while (n == 0);
200 done:
201 return error;
203 #endif
205 int main(int argc, char *argv[])
207 int opt;
208 char *password = NULL;
209 char *filename = NULL;
210 char *socketpath = NULL;
211 char command[ASSUAN_LINELENGTH], *p;
212 int ret = EXIT_SUCCESS;
213 gpg_error_t error;
214 char *result = NULL;
215 int save = 0;
216 char *pinentry_path = NULL;
217 char *display = NULL, *tty = NULL, *ttytype = NULL;
218 int outfd = STDOUT_FILENO;
219 FILE *outfp = stdout;
220 int inquirefd = STDIN_FILENO;
221 FILE *inquirefp = stdin;
222 int show_status = 1;
223 char *clientname = NULL;
224 char *inquire = NULL;
225 long iter = -2;
226 int have_iter = 0;
227 int timeout = 0;
228 #ifdef WITH_TCP
229 char *host = NULL;
230 int port = DEFAULT_PORT;
231 char *username = NULL;
232 char *ident = NULL;
233 #endif
234 #ifdef DEBUG
235 int tries = 0;
236 int method = 0;
237 pwmd_async_t s;
238 #endif
240 setlocale(LC_ALL, "");
241 bindtextdomain("libpwmd", LOCALEDIR);
243 #ifdef DEBUG
244 #ifdef WITH_TCP
245 while ((opt = getopt(argc, argv, "U:Y:H:R:y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
246 #else
247 while ((opt = getopt(argc, argv, "y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
248 #endif
249 #else
250 #ifdef WITH_TCP
251 while ((opt = getopt(argc, argv, "U:Y:H:R:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
252 #else
253 while ((opt = getopt(argc, argv, "t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
254 #endif
255 #endif
256 switch (opt) {
257 #ifdef DEBUG
258 case 'E':
259 method = atoi(optarg);
261 if (method > 2)
262 method = 2;
263 break;
264 case 'y':
265 tries = atoi(optarg);
266 break;
267 #endif
268 #ifdef WITH_TCP
269 case 'H':
270 host = xstrdup(optarg);
271 break;
272 case 'R':
273 port = atoi(optarg);
274 break;
275 case 'Y':
276 ident = xstrdup(optarg);
277 break;
278 case 'U':
279 username = xstrdup(optarg);
280 break;
281 #endif
282 case 't':
283 timeout = atoi(optarg);
284 break;
285 case 'c':
286 clientname = xstrdup(optarg);
287 break;
288 case 'X':
289 show_status = 0;
290 break;
291 case 'T':
292 tty = optarg;
293 break;
294 case 'N':
295 ttytype = optarg;
296 break;
297 case 'D':
298 display = optarg;
299 break;
300 case 'I':
301 inquirefd = atoi(optarg);
302 inquirefp = fdopen(inquirefd, "r");
304 if (!inquirefp) {
305 xfree(password);
306 err(EXIT_FAILURE, "%i", inquirefd);
308 break;
309 case 'd':
310 outfd = atoi(optarg);
311 outfp = fdopen(outfd, "w");
313 if (!outfp) {
314 xfree(password);
315 err(EXIT_FAILURE, "%i", outfd);
317 break;
318 case 'S':
319 save = 1;
320 break;
321 case 'i':
322 iter = strtol(optarg, NULL, 10);
323 have_iter = 1;
324 break;
325 case 's':
326 socketpath = xstrdup(optarg);
327 break;
328 case 'p':
329 password = xstrdup(optarg);
330 memset(optarg, 0, strlen(optarg));
331 break;
332 case 'P':
333 pinentry_path = xstrdup(optarg);
334 break;
335 case 'v':
336 xfree(password);
337 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
338 exit(EXIT_SUCCESS);
339 case 'h':
340 default:
341 xfree(password);
342 usage(argv[0]);
346 filename = argv[optind];
347 pwmd_init();
349 #ifdef WITH_TCP
350 if (host) {
351 #ifdef DEBUG
352 if (method == 1) {
353 if ((pwm = pwmd_tcp_connect_async(host, port, ident, username, password, &error)) == NULL) {
354 xfree(password);
355 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
358 do {
359 s = pwmd_process(pwm, &error);
360 fputc('.', stderr);
361 usleep(50000);
362 } while (s == ASYNC_PROCESS);
364 if (error) {
365 pwmd_close(pwm);
366 xfree(password);
367 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
370 pwmd_finalize(pwm);
372 else {
373 #endif
374 if ((pwm = pwmd_tcp_connect(host, port, ident, username, password, &error)) == NULL) {
375 xfree(password);
376 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
378 #ifdef DEBUG
380 #endif
382 else {
383 #endif
384 if ((pwm = pwmd_connect(socketpath, &error)) == NULL) {
385 xfree(password);
386 errx(EXIT_FAILURE, "pwmd_connect(): %s", pwmd_strerror(error));
388 #ifdef WITH_TCP
390 #endif
392 error = pwmd_command(pwm, &result, "OPTION CLIENT NAME=%s", clientname ? clientname : "pwmc");
393 xfree(clientname);
395 if (error) {
396 xfree(password);
397 goto done;
400 if (have_iter) {
401 error = pwmd_command(pwm, &result, "VERSION");
403 if (error && error != GPG_ERR_ASS_UNKNOWN_CMD) {
404 xfree(password);
405 goto done;
408 pwmd_free_result(result);
410 if (error == GPG_ERR_ASS_UNKNOWN_CMD) {
411 if (iter < -1) {
412 xfree(password);
413 pwmd_close(pwm);
414 usage(argv[0]);
417 else {
418 /* pwmd version 2 or later. */
419 if (iter < 0) {
420 xfree(password);
421 pwmd_close(pwm);
422 usage(argv[0]);
427 if (timeout > 0) {
428 error = pwmd_command(pwm, &result, "OPTION TIMEOUT=%i", timeout);
430 if (error) {
431 xfree(password);
432 goto done;
436 if (password) {
437 error = pwmd_setopt(pwm, PWMD_OPTION_PASSWORD, password);
439 if (error) {
440 xfree(password);
441 goto done;
444 xfree(password);
446 else {
447 if (pinentry_path) {
448 error = pwmd_command(pwm, &result, "OPTION PATH=%s", pinentry_path);
450 if (error)
451 goto done;
454 if (display) {
455 error = pwmd_command(pwm, &result, "OPTION DISPLAY=%s", display);
457 if (error)
458 goto done;
461 if (tty) {
462 error = pwmd_command(pwm, &result, "OPTION TTYNAME=%s", tty);
464 if (error)
465 goto done;
468 if (ttytype) {
469 error = pwmd_command(pwm, &result, "OPTION TTYTYPE=%s", ttytype);
471 if (error)
472 goto done;
474 #ifdef DEBUG
475 if (method >= 2) {
476 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1);
478 if (error)
479 goto done;
482 if (tries > 0) {
483 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
485 if (error)
486 goto done;
488 #endif
491 if (show_status) {
492 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_FUNC, status_msg_cb);
494 if (error)
495 goto done;
498 if (filename) {
499 #ifdef DEBUG
500 /* This method doesn't support PWMD_OPTION_PINENTRY_TRIES. */
501 if (method == 1) {
502 error = pwmd_open_async(pwm, filename);
504 if (!error) {
505 do {
506 s = pwmd_process(pwm, &error);
507 fputc('.', stderr);
508 usleep(50000);
509 } while (s == ASYNC_PROCESS);
511 pwmd_finalize(pwm);
514 else if (method == 2) {
515 int fd = pwmd_open_nb(pwm, &error, filename, timeout);
517 if (fd == -1)
518 goto done;
519 else if (fd >= 0)
520 error = do_nb_command(fd, 0);
522 else
523 error = pwmd_open(pwm, filename);
524 #else
525 error = pwmd_open(pwm, filename);
526 #endif
528 if (error)
529 goto done;
532 if (filename) {
533 error = pwmd_command(pwm, &result, "LOCK");
535 if (error)
536 goto done;
539 p = fgets(command, sizeof(command), stdin);
541 if (!p)
542 goto done;
545 * This is a known INQUIRE command. We use pwmd_inquire() to send the
546 * data from the do_inquire() callback function.
548 if (strncasecmp(p, "STORE ", 6) == 0) {
549 p += 6;
550 inquire = (char *)"STORE";
552 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
553 p += 7;
554 inquire = (char *)"IMPORT";
557 if (inquire) {
558 struct inquire_s *inq = (struct inquire_s *)malloc(sizeof(struct inquire_s));
560 if (!inq) {
561 error = gpg_error_from_errno(ENOMEM);
562 goto done;
565 inq->data = xstrdup(p);
566 inq->fp = inquirefp;
567 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
568 free(inq);
569 goto done;
572 if (strcasecmp(p, "BYE") == 0)
573 goto done;
575 error = pwmd_command(pwm, &result, command);
576 memset(command, 0, sizeof(command));
578 if (error)
579 goto done;
581 if (result) {
582 fwrite(result, 1, strlen(result), outfp);
583 pwmd_free_result(result);
586 done:
587 memset(command, 0, sizeof(command));
589 if (!error && save) {
590 if (iter != -2) {
591 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
593 if (error)
594 goto done;
597 #ifdef DEBUG
598 if (method == 1) {
599 error = pwmd_save_async(pwm);
601 if (!error) {
602 do {
603 s = pwmd_process(pwm, &error);
604 fputc('.', stderr);
605 usleep(50000);
606 } while (s == ASYNC_PROCESS);
608 pwmd_finalize(pwm);
611 else if (method == 3) {
612 int fd = pwmd_save_nb(pwm, &error);
614 if (fd == -1)
615 goto done;
616 else if (fd >= 0)
617 error = do_nb_command(fd, 1);
619 else
620 error = pwmd_save(pwm);
621 #else
622 error = pwmd_save(pwm);
623 #endif
626 if (!error && filename)
627 error = pwmd_command(pwm, &result, "UNLOCK");
629 if (error) {
630 show_error(error);
631 ret = EXIT_FAILURE;
634 pwmd_close(pwm);
636 if (socketpath)
637 xfree(socketpath);
639 exit(ret);