Added PWMD_OPTION_VERIFY_HOSTNAME and pwmc option -V. This will verify
[libpwmd.git] / pwmc.c
blob78dae0905a4eafc49b5c98eea6cff252837e1e7c
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 6466
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 HAVE_LIBGNUTLS
72 " [-H <hostname> [-R <port>] [-V]] [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 HAVE_LIBGNUTLS
81 " -H connect to hostname\n"
82 " -R alterate port (%i)\n"
83 " -V verify peer hostname\n"
84 #endif
85 " -t pinentry timeout\n"
86 " -X disable showing of status messages from the server\n"
87 " -c set the client name\n"
88 " -s socket path (~/.pwmd/socket)\n"
89 " -p passphrase\n"
90 " -P path to the pinentry binary (server default)\n"
91 " -T pinentry tty\n"
92 " -N pinentry terminal type\n"
93 " -D pinentry display\n"
94 " -d redirect command output to the specified file descriptor\n"
95 " -I read inquire data from the specified file descriptor\n"
96 " -S send the SAVE command before exiting\n"
97 " -i encrypt with the specified number of iterations (-1 = 0 iterations)\n"
98 " -v version\n"
99 " -h this help text\n"), DEFAULT_PORT);
100 exit(EXIT_FAILURE);
103 struct inquire_s {
104 FILE *fp;
105 char *data;
108 static gpg_error_t do_inquire(void *data, const char *keyword, gpg_error_t rc,
109 char **result, size_t *result_len)
111 int c;
112 static char buf[ASSUAN_LINELENGTH];
113 char *p;
114 size_t len = 0;
115 struct inquire_s *inq = (struct inquire_s *)data;
117 if (rc) {
118 memset(buf, 0, sizeof(buf));
119 return rc;
122 buf[0] = 0;
123 p = buf;
125 if (inq->data) {
126 snprintf(buf, sizeof(buf), "%s", inq->data);
127 xfree(inq->data);
128 inq->data = NULL;
129 len = strlen(buf);
130 p = buf + len;
133 while ((c = fgetc(inq->fp)) != EOF) {
134 if (len == sizeof(buf)) {
135 ungetc(c, inq->fp);
136 break;
139 *p++ = c;
140 len++;
143 if (!buf[0]) {
144 memset(buf, 0, sizeof(buf));
145 return GPG_ERR_EOF;
148 *result = buf;
149 *result_len = len;
150 return 0;
153 static int status_msg_cb(void *data, const char *line)
155 fprintf(stderr, "%s\n", line);
156 return 0;
159 #ifdef DEBUG
160 static gpg_error_t do_nb_command(int fd, int which)
162 int n;
163 gpg_error_t error = 0;
165 fcntl(fd, F_SETFL, O_NONBLOCK);
167 do {
168 fd_set fds;
169 struct timeval tv = {0, 50000};
171 FD_ZERO(&fds);
172 FD_SET(fd, &fds);
173 n = select(fd+1, &fds, NULL, NULL, &tv);
175 if (n > 0) {
176 if (FD_ISSET(fd, &fds)) {
177 pwmd_nb_status_t status;
179 n = read(fd, &status, sizeof(status));
181 if (n == -1) {
182 error = gpg_error_from_errno(errno);
183 goto done;
186 if (!which)
187 error = pwmd_open_nb_finalize(pwm, &status);
188 else
189 error = pwmd_save_nb_finalize(pwm, &status);
191 if (error)
192 goto done;
195 else
196 fputc('.', stderr);
197 } while (n == 0);
199 done:
200 return error;
202 #endif
204 int main(int argc, char *argv[])
206 int opt;
207 char *password = NULL;
208 char *filename = NULL;
209 char *socketpath = NULL;
210 char command[ASSUAN_LINELENGTH], *p;
211 int ret = EXIT_SUCCESS;
212 gpg_error_t error;
213 char *result = NULL;
214 int save = 0;
215 char *pinentry_path = NULL;
216 char *display = NULL, *tty = NULL, *ttytype = 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 int iter = -2;
225 int timeout = 0;
226 #ifdef HAVE_LIBGNUTLS
227 char *host = NULL;
228 int port = DEFAULT_PORT;
229 int verify_hostname = 0;
230 #endif
231 #ifdef DEBUG
232 int tries = 0;
233 int method = 0;
234 pwmd_async_t s;
235 #endif
237 setlocale(LC_ALL, "");
238 bindtextdomain("libpwmd", LOCALEDIR);
240 #ifdef DEBUG
241 #ifdef HAVE_LIBGNUTLS
242 while ((opt = getopt(argc, argv, "VH:R:y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
243 #else
244 while ((opt = getopt(argc, argv, "y:t:E:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
245 #endif
246 #else
247 #ifdef HAVE_LIBGNUTLS
248 while ((opt = getopt(argc, argv, "VH:R:t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
249 #else
250 while ((opt = getopt(argc, argv, "t:c:I:XT:N:D:hvP:p:s:Si:d:")) != EOF) {
251 #endif
252 #endif
253 switch (opt) {
254 #ifdef DEBUG
255 case 'E':
256 method = atoi(optarg);
258 if (method > 2)
259 method = 2;
260 break;
261 case 'y':
262 tries = atoi(optarg);
263 break;
264 #endif
265 #ifdef HAVE_LIBGNUTLS
266 case 'H':
267 host = xstrdup(optarg);
268 break;
269 case 'R':
270 port = atoi(optarg);
271 break;
272 case 'V':
273 verify_hostname = 1;
274 break;
275 #endif
276 case 't':
277 timeout = atoi(optarg);
278 break;
279 case 'c':
280 clientname = xstrdup(optarg);
281 break;
282 case 'X':
283 show_status = 0;
284 break;
285 case 'T':
286 tty = optarg;
287 break;
288 case 'N':
289 ttytype = optarg;
290 break;
291 case 'D':
292 display = optarg;
293 break;
294 case 'I':
295 inquirefd = atoi(optarg);
296 inquirefp = fdopen(inquirefd, "r");
298 if (!inquirefp) {
299 xfree(password);
300 err(EXIT_FAILURE, "%i", inquirefd);
302 break;
303 case 'd':
304 outfd = atoi(optarg);
305 outfp = fdopen(outfd, "w");
307 if (!outfp) {
308 xfree(password);
309 err(EXIT_FAILURE, "%i", outfd);
311 break;
312 case 'S':
313 save = 1;
314 break;
315 case 'i':
316 iter = atoi(optarg);
318 if (iter < -1) {
319 xfree(password);
320 usage(argv[0]);
322 break;
323 case 's':
324 socketpath = xstrdup(optarg);
325 break;
326 case 'p':
327 password = xstrdup(optarg);
328 memset(optarg, 0, strlen(optarg));
329 break;
330 case 'P':
331 pinentry_path = xstrdup(optarg);
332 break;
333 case 'v':
334 xfree(password);
335 printf("%s (pwmc)\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
336 exit(EXIT_SUCCESS);
337 case 'h':
338 default:
339 xfree(password);
340 usage(argv[0]);
344 filename = argv[optind];
345 pwmd_init();
347 #ifdef HAVE_LIBGNUTLS
348 if (host) {
349 if (verify_hostname) {
350 error = pwmd_setopt(pwm, PWMD_OPTION_VERIFY_HOSTNAME, 1);
352 if (error) {
353 xfree(password);
354 goto done;
358 #ifdef DEBUG
359 if (method == 1) {
360 if ((pwm = pwmd_tcp_connect_async(host, port, &error, NULL, NULL,
361 NULL)) == NULL) {
362 xfree(password);
363 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
366 do {
367 s = pwmd_process(pwm, &error);
368 fputc('.', stderr);
369 usleep(50000);
370 } while (s == ASYNC_PROCESS);
372 if (error) {
373 pwmd_close(pwm);
374 xfree(password);
375 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
378 pwmd_finalize(pwm);
380 else {
381 #endif
382 if ((pwm = pwmd_tcp_connect(host, port, &error, NULL, NULL, NULL))
383 == NULL) {
384 xfree(password);
385 errx(EXIT_FAILURE, "%s: %s", host, pwmd_strerror(error));
387 #ifdef DEBUG
389 #endif
391 else {
392 #endif
393 if ((pwm = pwmd_connect(socketpath, &error)) == NULL) {
394 xfree(password);
395 errx(EXIT_FAILURE, "pwmd_connect(): %s", pwmd_strerror(error));
397 #ifdef HAVE_LIBGNUTLS
399 #endif
401 error = pwmd_command(pwm, &result, "OPTION CLIENT NAME=%s", clientname ? clientname : "pwmc");
402 xfree(clientname);
404 if (error) {
405 xfree(password);
406 errx(EXIT_FAILURE, "pwmd_command(): %s", pwmd_strerror(error));
409 if (timeout > 0) {
410 error = pwmd_command(pwm, &result, "OPTION TIMEOUT=%i", timeout);
412 if (error) {
413 xfree(password);
414 errx(EXIT_FAILURE, "pwmd_command(): %s", pwmd_strerror(error));
418 if (password) {
419 error = pwmd_setopt(pwm, PWMD_OPTION_PASSWORD, password);
421 if (error) {
422 xfree(password);
423 goto done;
426 xfree(password);
428 else {
429 if (pinentry_path) {
430 error = pwmd_command(pwm, &result, "OPTION PATH=%s", pinentry_path);
432 if (error)
433 goto done;
436 if (display) {
437 error = pwmd_command(pwm, &result, "OPTION DISPLAY=%s", display);
439 if (error)
440 goto done;
443 if (tty) {
444 error = pwmd_command(pwm, &result, "OPTION TTYNAME=%s", tty);
446 if (error)
447 goto done;
450 if (ttytype) {
451 error = pwmd_command(pwm, &result, "OPTION TTYTYPE=%s", ttytype);
453 if (error)
454 goto done;
456 #ifdef DEBUG
457 if (method >= 2) {
458 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1);
460 if (error)
461 goto done;
464 if (tries > 0) {
465 error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TRIES, tries);
467 if (error)
468 goto done;
470 #endif
473 if (show_status) {
474 error = pwmd_setopt(pwm, PWMD_OPTION_STATUS_FUNC, status_msg_cb);
476 if (error)
477 goto done;
480 if (filename) {
481 #ifdef DEBUG
482 /* This method doesn't support PWMD_OPTION_PINENTRY_TRIES. */
483 if (method == 1) {
484 error = pwmd_open_async(pwm, filename);
486 if (!error) {
487 do {
488 s = pwmd_process(pwm, &error);
489 fputc('.', stderr);
490 usleep(50000);
491 } while (s == ASYNC_PROCESS);
493 pwmd_finalize(pwm);
496 else if (method == 2) {
497 int fd = pwmd_open_nb(pwm, &error, filename, timeout);
499 if (fd == -1)
500 goto done;
501 else if (fd >= 0)
502 error = do_nb_command(fd, 0);
504 else
505 error = pwmd_open(pwm, filename);
506 #else
507 error = pwmd_open(pwm, filename);
508 #endif
510 if (error)
511 goto done;
514 if (filename) {
515 error = pwmd_command(pwm, &result, "LOCK");
517 if (error)
518 goto done;
521 p = fgets(command, sizeof(command), stdin);
523 if (!p)
524 goto done;
527 * This is a known INQUIRE command. We use pwmd_inquire() to send the
528 * data from the do_inquire() callback function.
530 if (strncasecmp(p, "STORE ", 6) == 0) {
531 p += 6;
532 inquire = (char *)"STORE";
534 else if (strncasecmp(p, "IMPORT ", 7) == 0) {
535 p += 7;
536 inquire = (char *)"IMPORT";
539 if (inquire) {
540 struct inquire_s *inq = (struct inquire_s *)malloc(sizeof(struct inquire_s));
542 if (!inq) {
543 error = gpg_error_from_errno(ENOMEM);
544 goto done;
547 inq->data = xstrdup(p);
548 inq->fp = inquirefp;
549 error = pwmd_inquire(pwm, inquire, do_inquire, inq);
550 free(inq);
551 goto done;
554 if (strcasecmp(p, "BYE") == 0)
555 goto done;
557 error = pwmd_command(pwm, &result, command);
558 memset(command, 0, sizeof(command));
560 if (error)
561 goto done;
563 if (result) {
564 fwrite(result, 1, strlen(result), outfp);
565 pwmd_free_result(result);
568 done:
569 memset(command, 0, sizeof(command));
571 if (!error && save) {
572 if (iter != -2) {
573 error = pwmd_command(pwm, &result, "OPTION ITERATIONS=%i", iter);
575 if (error)
576 goto done;
579 #ifdef DEBUG
580 if (method == 1) {
581 error = pwmd_save_async(pwm);
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 == 3) {
594 int fd = pwmd_save_nb(pwm, &error);
596 if (fd == -1)
597 goto done;
598 else if (fd >= 0)
599 error = do_nb_command(fd, 1);
601 else
602 error = pwmd_save(pwm);
603 #else
604 error = pwmd_save(pwm);
605 #endif
608 if (!error && filename)
609 error = pwmd_command(pwm, &result, "UNLOCK");
611 if (error) {
612 show_error(error);
613 ret = EXIT_FAILURE;
616 pwmd_close(pwm);
618 if (socketpath)
619 xfree(socketpath);
621 exit(ret);