Added libpth support. It's built by default but can be disabled with
[libpwmd.git] / libpwmd.c
blob1cc520959fbd9cd42d1096f810138a3fee685f6c
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-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 <ctype.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <sys/wait.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <time.h>
35 #include <sys/types.h>
36 #include <limits.h>
37 #include <sys/select.h>
38 #include <libpwmd.h>
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
44 #ifdef HAVE_ASSUAN_H
45 #include <assuan.h>
46 #endif
48 #ifdef HAVE_SETLOCALE
49 #include <locale.h>
50 #endif
52 #include "gettext.h"
53 #define N_(msgid) dgettext("libpwmd", msgid)
55 #ifndef MEM_DEBUG
56 #include "mem.h"
57 #else
58 #define xfree free
59 #define xrealloc realloc
60 #define xmalloc malloc
61 #define xstrdup strdup
62 #define xcalloc calloc
63 #endif
65 #include "types.h"
67 #ifdef USE_PINENTRY
68 static pwm_t *gpwm;
69 static int gelapsed, gtimeout;
70 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd);
71 static gpg_error_t global_error;
72 #endif
74 const char *pwmd_strerror(gpg_error_t e)
76 gpg_err_code_t code = gpg_err_code(e);
78 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
79 switch (code) {
80 case GPG_ERR_USER_1:
81 default:
82 return N_("Unknown error");
83 case GPG_ERR_USER_2:
84 return N_("No cache slots available");
85 case GPG_ERR_USER_3:
86 return N_("Recursion loop");
87 case GPG_ERR_USER_4:
88 return N_("No file is open");
89 case GPG_ERR_USER_5:
90 return N_("General LibXML error");
91 case GPG_ERR_USER_6:
92 return N_("File modified");
96 return gpg_strerror(e);
99 gpg_error_t pwmd_init()
101 #ifdef ENABLE_NLS
102 bindtextdomain("libpwmd", LOCALEDIR);
103 #endif
104 gpg_err_init();
105 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
106 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
107 return 0;
110 pwm_t *pwmd_connect(const char *path, gpg_error_t *error)
112 pwm_t *pwm = NULL;
113 char *socketpath = NULL;
114 time_t now;
115 struct passwd *pw;
116 assuan_context_t ctx;
117 int rc, n;
118 int active[2];
120 if (!path) {
121 pw = getpwuid(getuid());
122 socketpath = (char *)xmalloc(strlen(pw->pw_dir) + strlen("/.pwmd/socket") + 1);
123 sprintf(socketpath, "%s/.pwmd/socket", pw->pw_dir);
125 else
126 socketpath = xstrdup(path);
128 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
129 xfree(socketpath);
131 if (rc) {
132 *error = rc;
133 return NULL;
136 n = assuan_get_active_fds(ctx, 0, active, sizeof(active));
138 if ((pwm = (pwm_t *)xcalloc(1, sizeof(pwm_t))) == NULL) {
139 *error = gpg_error_from_errno(errno);
140 assuan_disconnect(ctx);
141 return NULL;
144 pwm->fd = n <= 0 ? -1 : dup(active[0]);
146 if (pwm->fd != -1)
147 fcntl(pwm->fd, F_SETFL, O_NONBLOCK);
149 pwm->ctx = ctx;
150 #ifdef USE_PINENTRY
151 pwm->pid = -1;
152 pwm->pinentry_tries = 3;
153 #endif
154 time(&now);
155 srandom(now);
156 *error = 0;
157 return pwm;
160 gpg_error_t pwmd_pending_line(pwm_t *pwm, char **line, size_t *len)
162 if (!pwm)
163 return GPG_ERR_INV_ARG;
165 if (assuan_pending_line(pwm->ctx))
166 return assuan_read_line(pwm->ctx, line, len);
168 return GPG_ERR_NO_DATA;
171 void pwmd_close(pwm_t *pwm)
173 if (!pwm)
174 return;
176 if (pwm->ctx)
177 assuan_disconnect(pwm->ctx);
179 if (pwm->password)
180 xfree(pwm->password);
182 if (pwm->title)
183 xfree(pwm->title);
185 if (pwm->desc)
186 xfree(pwm->desc);
188 if (pwm->prompt)
189 xfree(pwm->prompt);
191 if (pwm->pinentry_tty)
192 xfree(pwm->pinentry_tty);
194 if (pwm->pinentry_display)
195 xfree(pwm->pinentry_display);
197 if (pwm->pinentry_term)
198 xfree(pwm->pinentry_term);
200 if (pwm->filename)
201 xfree(pwm->filename);
203 xfree(pwm);
206 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
208 membuf_t *mem = (membuf_t *)data;
209 void *p;
211 if (!buffer)
212 return 0;
214 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
215 return 1;
217 mem->buf = p;
218 memcpy((char *)mem->buf + mem->len, buffer, len);
219 mem->len += len;
220 return 0;
223 void pwmd_free_result(void *data)
225 xfree(data);
228 static int _inquire_cb(void *data, const char *keyword)
230 pwm_t *pwm = (pwm_t *)data;
231 gpg_error_t rc = 0;
232 int flags = fcntl(pwm->fd, F_GETFL);
234 /* Shouldn't get this far without a callback. */
235 if (!pwm->inquire_func)
236 return GPG_ERR_INV_ARG;
239 * Since the socket file descriptor is probably set to non-blocking, set to
240 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixes when
241 * asynchronous INQUIRE is supported by either libassuan or a later
242 * libpwmd.
244 fcntl(pwm->fd, F_SETFL, 0);
246 for (;;) {
247 char *result = NULL;
248 size_t len;
249 gpg_error_t arc;
251 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
252 rc = gpg_err_code(rc);
254 if (rc == GPG_ERR_EOF || !rc) {
255 if (len <= 0 || !result || !*result) {
256 rc = 0;
257 break;
260 arc = assuan_send_data(pwm->ctx, result, len);
262 if (rc == GPG_ERR_EOF) {
263 rc = arc;
264 break;
267 rc = arc;
269 else if (rc)
270 break;
273 fcntl(pwm->fd, F_SETFL, flags);
274 return rc;
277 gpg_error_t pwmd_finalize(pwm_t *pwm)
279 if (!pwm || pwm->fd < 0)
280 return GPG_ERR_INV_ARG;
282 pwm->state = ASYNC_INIT;
283 pwm->ntries = 0;
284 pwm->is_open_cmd = 0;
285 return 0;
288 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, const char *arg)
290 char *buf;
291 gpg_error_t rc;
292 size_t len = strlen(cmd) + 2;
294 len += arg ? strlen(arg) : 0;
296 if (pwm->state != ASYNC_INIT)
297 return GPG_ERR_UNEXPECTED;
299 buf = (char *)xmalloc(len);
301 if (!buf) {
302 rc = gpg_error_from_errno(ENOMEM);
303 goto fail;
306 snprintf(buf, len, "%s %s", cmd, arg ? arg : "");
307 rc = assuan_write_line(pwm->ctx, buf);
308 xfree(buf);
310 if (!rc)
311 pwm->state = ASYNC_PROCESS;
313 fail:
314 return rc;
317 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
319 if (!pwm || !filename)
320 return GPG_ERR_INV_ARG;
322 /* For pinentry retries. */
323 if (!pwm->is_open_cmd) {
324 if (pwm->filename)
325 xfree(pwm->filename);
327 pwm->filename = xstrdup(filename);
330 pwm->is_open_cmd = 1;
331 return do_nb_command(pwm, "OPEN", filename);
334 gpg_error_t pwmd_save_async(pwm_t *pwm)
336 if (!pwm)
337 return GPG_ERR_INV_ARG;
339 return do_nb_command(pwm, "SAVE", NULL);
342 static gpg_error_t parse_assuan_line(pwm_t *pwm)
344 gpg_error_t rc;
345 char *line;
346 size_t len;
348 rc = assuan_read_line(pwm->ctx, &line, &len);
350 if (!rc) {
351 if (line[0] == 'O' && line[1] == 'K' &&
352 (line[2] == 0 || line[2] == ' ')) {
353 pwm->state = ASYNC_DONE;
355 else if (line[0] == '#') {
357 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
358 if (pwm->status_func) {
359 pwm->status_func(pwm->status_data,
360 line[1] == 0 ? line+1 : line+2);
363 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
364 (line[3] == 0 || line[3] == ' ')) {
365 line += 4;
366 rc = atoi(line);
367 pwm->state = ASYNC_DONE;
371 return rc;
374 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc)
376 fd_set rfds;
377 int n;
378 struct timeval tv = {0, 0};
380 *rc = 0;
382 if (!pwm || pwm->fd < 0) {
383 *rc = GPG_ERR_INV_ARG;
384 return ASYNC_DONE;
386 else if (pwm->state == ASYNC_DONE)
387 return pwm->state;
388 else if (pwm->state == ASYNC_INIT) {
389 *rc = GPG_ERR_UNEXPECTED;
390 return ASYNC_DONE;
393 FD_ZERO(&rfds);
394 FD_SET(pwm->fd, &rfds);
395 #ifdef WITH_LIBPTH
396 n = pth_select(pwm->fd+1, &rfds, NULL, NULL, &tv);
397 #else
398 n = select(pwm->fd+1, &rfds, NULL, NULL, &tv);
399 #endif
401 if (n > 0) {
402 if (FD_ISSET(pwm->fd, &rfds))
403 *rc = parse_assuan_line(pwm);
406 while (!*rc && assuan_pending_line(pwm->ctx))
407 *rc = parse_assuan_line(pwm);
409 if (pwm->is_open_cmd && gpg_err_code(*rc) == EPWMD_BADKEY &&
410 ++pwm->ntries < pwm->pinentry_tries) {
411 pwm->state = ASYNC_INIT;
412 *rc = pwmd_open_async(pwm, pwm->filename);
415 return pwm->state;
418 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
419 char **result, const char *cmd)
421 membuf_t data;
422 gpg_error_t rc;
424 data.len = 0;
425 data.buf = NULL;
427 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
428 pwm->status_func, pwm->status_data);
430 if (rc) {
431 if (data.buf) {
432 xfree(data.buf);
433 data.buf = NULL;
436 else {
437 if (data.buf) {
438 mem_realloc_cb(&data, "", 1);
439 *result = (char *)data.buf;
443 return gpg_err_code(rc);
446 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_fn fn,
447 void *data)
449 if (!pwm || !cmd || !fn)
450 return GPG_ERR_INV_ARG;
452 pwm->inquire_func = fn;
453 pwm->inquire_data = data;
454 return assuan_command(pwm, pwm->ctx, NULL, cmd);
457 gpg_error_t pwmd_terminate_pinentry(pwm_t *pwm)
459 #ifndef USE_PINENTRY
460 return GPG_ERR_NOT_IMPLEMENTED;
461 #else
462 if (!pwm || pwm->pid == -1)
463 return GPG_ERR_INV_ARG;
465 if (kill(pwm->pid, 0) == 0) {
466 if (kill(pwm->pid, SIGTERM) == -1) {
467 if (kill(pwm->pid, SIGKILL) == -1)
468 return gpg_error_from_errno(errno);
471 pwm->pin_error = GPG_ERR_TIMEOUT;
473 else
474 return gpg_error_from_errno(errno);
476 return 0;
477 #endif
480 #ifdef USE_PINENTRY
481 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
483 char *buf;
484 char tmp[ASSUAN_LINELENGTH];
485 gpg_error_t error;
487 if (!pwm->title)
488 pwm->title = xstrdup(N_("LibPWMD"));
490 if (!pwm->prompt)
491 pwm->prompt = xstrdup(N_("Password:"));
493 if (!pwm->desc && !which)
494 pwm->desc = xstrdup(N_("Enter a password."));
496 if (which == 1) {
497 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Invalid password, please try again."));
498 buf = xstrdup(tmp);
500 else if (which == 2) {
501 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Please type the password again for confirmation."));
502 buf = xstrdup(tmp);
504 else {
505 buf = (char *)xmalloc(strlen("SETERROR ") + strlen(pwm->desc) + 1);
506 sprintf(buf, "SETERROR %s", pwm->desc);
509 error = pinentry_command(pwm, NULL, buf);
510 xfree(buf);
512 if (error)
513 return error;
515 buf = (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm->prompt) + 1);
516 sprintf(buf, "SETPROMPT %s", pwm->prompt);
517 error = pinentry_command(pwm, NULL, buf);
518 xfree(buf);
520 if (error)
521 return error;
523 buf = (char *)xmalloc(strlen("SETDESC ") + strlen(pwm->title) + 1);
524 sprintf(buf, "SETDESC %s", pwm->title);
525 error = pinentry_command(pwm, NULL, buf);
526 xfree(buf);
527 return error;
530 static void update_pinentry_settings(pwm_t *pwm)
532 FILE *fp;
533 char buf[LINE_MAX];
534 struct passwd *pw = getpwuid(getuid());
535 char *p;
537 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
539 if ((fp = fopen(buf, "r")) == NULL)
540 return;
542 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
543 char name[32], val[256];
545 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
546 continue;
548 if (strcasecmp(name, "TTYNAME") == 0) {
549 xfree(pwm->pinentry_tty);
550 pwm->pinentry_tty = xstrdup(val);
552 else if (strcasecmp(name, "TTYTYPE") == 0) {
553 xfree(pwm->pinentry_term);
554 pwm->pinentry_term = xstrdup(val);
556 else if (strcasecmp(name, "DISPLAY") == 0) {
557 xfree(pwm->pinentry_display);
558 pwm->pinentry_display = xstrdup(val);
560 else if (strcasecmp(name, "PATH") == 0) {
561 xfree(pwm->pinentry_path);
562 pwm->pinentry_path = xstrdup(val);
566 fclose(fp);
569 static gpg_error_t launch_pinentry(pwm_t *pwm)
571 int rc;
572 assuan_context_t ctx;
573 int child_list[] = {-1};
574 char *display = getenv("DISPLAY");
575 const char *argv[6];
576 int have_display = 0;
577 char *tty = NULL;
579 update_pinentry_settings(pwm);
581 if (pwm->pinentry_display || display)
582 have_display = 1;
583 else {
584 tty = pwm->pinentry_tty ? pwm->pinentry_tty : ttyname(STDOUT_FILENO);
586 if (!tty)
587 return gpg_error_from_errno(errno);
590 if (!have_display && !tty)
591 return GPG_ERR_ENOTTY;
593 argv[0] = "pinentry";
594 argv[1] = have_display ? "--display" : "--ttyname";
595 argv[2] = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
596 argv[3] = NULL;
598 if (!have_display) {
599 argv[3] = "--ttytype";
600 argv[4] = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
601 argv[5] = NULL;
604 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
606 if (rc)
607 return rc;
609 pwm->pid = assuan_get_pid(ctx);
610 pwm->pctx = ctx;
611 return set_pinentry_strings(pwm, 0);
614 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
616 gpg_error_t n;
618 if (!pwm->pctx) {
619 n = launch_pinentry(pwm);
621 if (n)
622 return n;
625 return assuan_command(pwm, pwm->pctx, result, cmd);
628 static void pinentry_disconnect(pwm_t *pwm)
630 if (pwm->pctx)
631 assuan_disconnect(pwm->pctx);
633 pwm->pctx = NULL;
634 pwm->pid = -1;
638 * Only called from a child process.
640 static void catchsig(int sig)
642 switch (sig) {
643 case SIGALRM:
644 if (gelapsed++ >= gtimeout) {
645 global_error = pwmd_terminate_pinentry(gpwm);
647 if (!global_error)
648 global_error = GPG_ERR_TIMEOUT;
650 break;
653 alarm(1);
654 break;
655 default:
656 break;
661 * Borrowed from libassuan.
663 static char *percent_escape(const char *atext)
665 const unsigned char *s;
666 int len = strlen(atext) * 3 + 1;
667 char *buf = (char *)xmalloc(len), *p = buf;
669 if (!buf)
670 return NULL;
672 for (s=(const unsigned char *)atext; *s; s++) {
673 if (*s < ' ') {
674 sprintf (p, "%%%02X", *s);
675 p += 3;
677 else
678 *p++ = *s;
681 *p = 0;
682 return buf;
684 #endif
686 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
688 if (!cmd)
689 return GPG_ERR_INV_ARG;
691 return assuan_command(pwm, pwm->ctx, result, cmd);
695 * Avoid sending the BYE command here. libassuan will close the file
696 * descriptor and release the assuan context. Use pwmd_close() instead.
698 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
700 va_list ap;
701 char *buf;
702 size_t len;
703 gpg_error_t error;
705 if (!pwm || !cmd)
706 return GPG_ERR_INV_ARG;
708 *result = NULL;
709 va_start(ap, cmd);
711 * C99 allows the dst pointer to be null which will calculate the length
712 * of the result and return it.
714 len = vsnprintf(NULL, 0, cmd, ap);
715 buf = (char *)xmalloc(len + 1);
716 len = vsnprintf(buf, len + 1, cmd, ap);
717 va_end(ap);
718 error = send_command(pwm, result, buf);
719 xfree(buf);
720 return error;
723 #ifdef USE_PINENTRY
724 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
726 if (gtimeout) {
727 signal(SIGALRM, catchsig);
728 alarm(1);
731 *result = NULL;
732 return pinentry_command(pwm, result, "GETPIN");
735 static gpg_error_t getpin(pwm_t *pwm, char **result, int *try_n, int which)
737 int pin_try = *try_n;
738 gpg_error_t error;
740 getpin_again:
741 *try_n = pin_try;
743 if (pin_try == -1) {
744 error = set_pinentry_strings(pwm, which);
746 if (error) {
747 pinentry_disconnect(pwm);
748 return error;
751 else {
752 if (pwm->pinentry_tries-1 != pin_try) {
753 error = set_pinentry_strings(pwm, 1);
755 if (error) {
756 pinentry_disconnect(pwm);
757 return error;
762 error = do_getpin(pwm, result);
765 * Since there was input cancel any timeout setting.
767 alarm(0);
769 if (error) {
770 if (error == GPG_ERR_CANCELED)
771 return GPG_ERR_CANCELED;
773 if (pin_try != -1 && pin_try--)
774 goto getpin_again;
776 if (pwm->pctx)
777 pinentry_disconnect(pwm);
779 *try_n = pin_try;
780 return error;
783 return 0;
785 #endif
787 gpg_error_t pwmd_open_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
789 gpg_error_t error;
791 #ifndef USE_PINENTRY
792 return GPG_ERR_NOT_IMPLEMENTED;
793 #endif
795 if (!pwm || !pw || !pw->filename[0])
796 return GPG_ERR_INV_ARG;
798 close(pw->fd);
800 if (pw->error) {
801 error = pw->error;
802 goto fail;
805 if (pwm->filename)
806 xfree(pwm->filename);
808 pwm->filename = xstrdup(pw->filename);
809 memset(pw, 0, sizeof(pwmd_nb_status_t));
810 return 0;
812 fail:
813 memset(pw, 0, sizeof(pwmd_nb_status_t));
814 return error;
817 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
819 char buf[ASSUAN_LINELENGTH];
820 gpg_error_t error;
821 char *result = NULL;
823 snprintf(buf, sizeof(buf), "OPEN %s %s", filename, password ? password : "");
824 error = send_command(pwm, &result, buf);
825 memset(buf, 0, sizeof(buf));
827 if (error && result)
828 xfree(result);
830 return error;
833 static int do_pwmd_open(pwm_t *pwm, gpg_error_t *error, const char *filename,
834 int nb, int timeout)
836 char *result = NULL;
837 char *password = NULL;
838 char path[PATH_MAX];
839 #ifdef USE_PINENTRY
840 int pin_try;
841 #endif
843 if (!pwm || !filename || !*filename) {
844 *error = GPG_ERR_INV_ARG;
845 return nb ? -1 : 1;
848 #ifdef USE_PINENTRY
849 pin_try = pwm->pinentry_tries - 1;
850 #endif
853 * Avoid calling pinentry if the password is cached on the server or if
854 * this is a new file.
856 *error = pwmd_command(pwm, &result, "GETCONFIG data_directory");
858 if (*error)
859 return nb ? -1 : 1;
861 snprintf(path, sizeof(path), "%s/%s", result, filename);
862 pwmd_free_result(result);
864 if (access(path, R_OK) == -1) {
865 if (errno == ENOENT)
866 goto gotpassword;
869 *error = pwmd_command(pwm, &result, "ISCACHED %s", filename);
871 if (*error == EPWMD_CACHE_NOT_FOUND) {
872 if (pwm->passfunc) {
873 password = pwm->passfunc(pwm, pwm->passdata);
874 goto gotpassword;
877 #ifdef USE_PINENTRY
879 * Get the password from pinentry.
881 if (pwm->use_pinentry) {
883 * Nonblocking is wanted. fork() then return a file descriptor
884 * that the client can use to read() from.
886 if (nb) {
887 int p[2];
888 pid_t pid;
889 pwmd_nb_status_t pw;
891 if (pipe(p) == -1) {
892 *error = gpg_error_from_syserror();
893 return -1;
896 #ifdef WITH_LIBPTH
897 pid = pth_fork();
898 #else
899 pid = fork();
900 #endif
902 switch (pid) {
903 case 0:
904 close(p[0]);
905 strncpy(pw.filename, filename, sizeof(pw.filename));
906 pw.filename[sizeof(pw.filename)-1] = 0;
907 pw.fd = p[0];
909 if (timeout > 0) {
910 gpwm = pwm;
911 gtimeout = timeout;
912 gelapsed = 0;
915 getpin_nb_again:
916 *error = getpin(pwm, &password, &pin_try, 0);
918 if (*error) {
919 getpin_nb_fail:
920 if (pwm->pctx)
921 pinentry_disconnect(pwm);
923 if (gtimeout && gelapsed >= gtimeout)
924 *error = GPG_ERR_TIMEOUT;
926 pw.error = *error;
927 #ifdef WITH_LIBPTH
928 pth_write(p[1], &pw, sizeof(pw));
929 #else
930 write(p[1], &pw, sizeof(pw));
931 #endif
932 close(p[1]);
933 _exit(1);
937 * Don't count the time it takes to open the file
938 * which may have many iterations.
940 signal(SIGALRM, SIG_DFL);
941 *error = do_open_command(pwm, filename, password);
943 if (timeout)
944 signal(SIGALRM, catchsig);
946 if (pwm->pctx && *error == EPWMD_BADKEY) {
947 if (pin_try-- > 0)
948 goto getpin_nb_again;
950 goto getpin_nb_fail;
953 pinentry_disconnect(pwm);
954 pw.error = 0;
955 #ifdef WITH_LIBPTH
956 pth_write(p[1], &pw, sizeof(pw));
957 #else
958 write(p[1], &pw, sizeof(pw));
959 #endif
960 close(p[1]);
961 _exit(0);
962 break;
963 case -1:
964 *error = gpg_error_from_syserror();
965 close(p[0]);
966 close(p[1]);
967 return -1;
968 default:
969 break;
972 close(p[1]);
973 return p[0];
976 else {
977 #endif
979 * Not using pinentry and the file was not found
980 * in the cache.
982 password = pwm->password;
983 #ifdef USE_PINENTRY
985 #endif
987 else if (*error)
988 return nb ? -1 : 1;
990 gotpassword:
991 *error = do_open_command(pwm, filename, password);
994 * Keep the user defined password set with pwmd_setopt(). The password may
995 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
997 if (password && password != pwm->password)
998 xfree(password);
1000 #ifdef USE_PINENTRY
1001 if (*error == EPWMD_BADKEY) {
1002 if (pin_try-- > 0 && !nb) {
1003 *error = pwmd_command(pwm, &result, "OPTION TITLE=%s",
1004 N_("Invalid password, please try again."));
1006 if (*error)
1007 return 1;
1009 goto gotpassword;
1012 if (nb)
1013 pinentry_disconnect(pwm);
1015 return nb ? -1 : 1;
1017 #endif
1019 if (!*error) {
1020 if (pwm->filename)
1021 xfree(pwm->filename);
1023 pwm->filename = xstrdup(filename);
1027 * The file is cached or the file is a new file.
1029 if (nb)
1030 return *error ? -1 : -2;
1032 return *error ? 1 : 0;
1035 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
1037 gpg_error_t error;
1039 do_pwmd_open(pwm, &error, filename, 0, 0);
1040 return error;
1043 int pwmd_open_nb(pwm_t *pwm, gpg_error_t *error, const char *filename,
1044 int timeout)
1046 #ifndef USE_PINENTRY
1047 *error = GPG_ERR_NOT_IMPLEMENTED;
1048 return -1;
1049 #else
1050 return do_pwmd_open(pwm, error, filename, 1, timeout);
1051 #endif
1054 #ifdef USE_PINENTRY
1055 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
1057 int confirm = 0;
1058 gpg_error_t error;
1059 char *result = NULL;
1060 int pin_try = -1;
1062 again:
1063 error = getpin(pwm, &result, &pin_try, confirm ? 2 : 0);
1065 if (error) {
1066 if (pwm->pctx)
1067 pinentry_disconnect(pwm);
1069 if (*password)
1070 xfree(*password);
1072 return error;
1075 if (!confirm++) {
1076 *password = result;
1077 goto again;
1080 if (strcmp(*password, result)) {
1081 xfree(*password);
1082 xfree(result);
1083 pinentry_disconnect(pwm);
1084 error = EPWMD_BADKEY;
1085 return error;
1088 xfree(result);
1089 pinentry_disconnect(pwm);
1090 return 0;
1092 #endif
1094 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
1096 char buf[ASSUAN_LINELENGTH];
1097 gpg_error_t error;
1098 char *result = NULL;
1100 snprintf(buf, sizeof(buf), "SAVE %s", password ? password : "");
1101 error = send_command(pwm, &result, buf);
1102 memset(&buf, 0, sizeof(buf));
1104 if (error && result)
1105 xfree(result);
1107 return error;
1110 gpg_error_t pwmd_save_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
1112 gpg_error_t rc;
1114 #ifndef USE_PINENTRY
1115 return GPG_ERR_NOT_IMPLEMENTED;
1116 #endif
1118 if (!pwm || !pw || !pw->filename[0])
1119 return GPG_ERR_INV_ARG;
1121 close(pw->fd);
1122 rc = pw->error;
1123 memset(pw, 0, sizeof(pwmd_nb_status_t));
1124 return rc;
1127 static int do_pwmd_save(pwm_t *pwm, gpg_error_t *error, int nb)
1129 char *result = NULL;
1130 char *password = NULL;
1132 if (!pwm) {
1133 *error = GPG_ERR_INV_ARG;
1134 return nb ? -1 : 1;
1137 if (pwm->use_pinentry || pwm->passfunc) {
1138 *error = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
1140 if (*error == EPWMD_CACHE_NOT_FOUND) {
1141 #ifdef USE_PINENTRY
1142 if (pwm->use_pinentry) {
1143 if (nb) {
1144 int p[2];
1145 pid_t pid;
1146 pwmd_nb_status_t pw;
1148 if (pipe(p) == -1) {
1149 *error = gpg_error_from_syserror();
1150 return -1;
1153 #ifdef WITH_LIBPTH
1154 pid = pth_fork();
1155 #else
1156 pid = fork();
1157 #endif
1159 switch (pid) {
1160 case 0:
1161 close(p[0]);
1162 strncpy(pw.filename, pwm->filename, sizeof(pw.filename));
1163 pw.filename[sizeof(pw.filename)-1] = 0;
1164 pw.fd = p[0];
1166 do {
1167 password = NULL;
1168 *error = do_save_getpin(pwm, &password);
1169 } while (*error == EPWMD_BADKEY);
1171 if (*error) {
1172 if (pwm->pctx)
1173 pinentry_disconnect(pwm);
1175 pw.error = *error;
1176 #ifdef WITH_LIBPTH
1177 pth_write(p[1], &pw, sizeof(pw));
1178 #else
1179 write(p[1], &pw, sizeof(pw));
1180 #endif
1181 close(p[1]);
1182 _exit(1);
1185 *error = do_save_command(pwm, password);
1186 pinentry_disconnect(pwm);
1187 pw.error = *error;
1188 #ifdef WITH_LIBPTH
1189 pth_write(p[1], &pw, sizeof(pw));
1190 #else
1191 write(p[1], &pw, sizeof(pw));
1192 #endif
1193 close(p[1]);
1194 _exit(0);
1195 break;
1196 case -1:
1197 *error = gpg_error_from_syserror();
1198 close(p[0]);
1199 close(p[1]);
1200 return -1;
1201 default:
1202 break;
1205 close(p[1]);
1206 return p[0];
1209 *error = do_save_getpin(pwm, &password);
1211 if (*error)
1212 return 1;
1214 else {
1215 #endif
1216 if (pwm->passfunc)
1217 password = (*pwm->passfunc)(pwm, pwm->passdata);
1218 #ifdef USE_PINENTRY
1220 #endif
1222 else {
1223 if (*error)
1224 return nb ? -1 : 1;
1227 else
1228 password = pwm->password;
1230 *error = do_save_command(pwm, password);
1232 if (password && password != pwm->password)
1233 xfree(password);
1235 if (nb)
1236 return *error ? -1 : -2;
1238 return *error ? 1 : 0;
1241 int pwmd_save_nb(pwm_t *pwm, gpg_error_t *error)
1243 #ifndef USE_PINENTRY
1244 *error = GPG_ERR_NOT_IMPLEMENTED;
1245 return -1;
1246 #else
1247 return do_pwmd_save(pwm, error, 1);
1248 #endif
1251 gpg_error_t pwmd_save(pwm_t *pwm)
1253 gpg_error_t error;
1255 do_pwmd_save(pwm, &error, 0);
1256 return error;
1259 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1261 va_list ap;
1262 #ifdef USE_PINENTRY
1263 int n = va_arg(ap, int);
1264 char *result;
1265 #endif
1266 char *arg1;
1267 gpg_error_t error = 0;
1269 if (!pwm)
1270 return GPG_ERR_INV_ARG;
1272 va_start(ap, opt);
1274 switch (opt) {
1275 case PWMD_OPTION_STATUS_FUNC:
1276 pwm->status_func = va_arg(ap, pwmd_status_fn);
1277 break;
1278 case PWMD_OPTION_STATUS_DATA:
1279 pwm->status_data = va_arg(ap, void *);
1280 break;
1281 case PWMD_OPTION_PASSWORD_FUNC:
1282 pwm->passfunc = va_arg(ap, pwmd_password_fn);
1283 break;
1284 case PWMD_OPTION_PASSWORD_DATA:
1285 pwm->passdata = va_arg(ap, void *);
1286 break;
1287 case PWMD_OPTION_PASSWORD:
1288 arg1 = va_arg(ap, char *);
1290 if (pwm->password)
1291 xfree(pwm->password);
1293 pwm->password = xstrdup(arg1);
1294 break;
1295 #ifdef USE_PINENTRY
1296 case PWMD_OPTION_PINENTRY:
1297 n = va_arg(ap, int);
1299 if (n != 0 && n != 1) {
1300 va_end(ap);
1301 error = GPG_ERR_INV_VALUE;
1303 else {
1304 pwm->use_pinentry = n;
1305 error = pwmd_command(pwm, &result, "OPTION PINENTRY=%i",
1306 !pwm->use_pinentry);
1308 break;
1309 case PWMD_OPTION_PINENTRY_TRIES:
1310 n = va_arg(ap, int);
1312 if (n <= 0) {
1313 va_end(ap);
1314 error = GPG_ERR_INV_VALUE;
1316 else
1317 pwm->pinentry_tries = n;
1318 break;
1319 case PWMD_OPTION_PINENTRY_PATH:
1320 if (pwm->pinentry_path)
1321 xfree(pwm->pinentry_path);
1323 pwm->pinentry_path = xstrdup(va_arg(ap, char *));
1324 break;
1325 case PWMD_OPTION_PINENTRY_TTY:
1326 if (pwm->pinentry_tty)
1327 xfree(pwm->pinentry_tty);
1329 pwm->pinentry_tty = xstrdup(va_arg(ap, char *));
1330 break;
1331 case PWMD_OPTION_PINENTRY_DISPLAY:
1332 if (pwm->pinentry_display)
1333 xfree(pwm->pinentry_display);
1335 pwm->pinentry_display = xstrdup(va_arg(ap, char *));
1336 break;
1337 case PWMD_OPTION_PINENTRY_TERM:
1338 if (pwm->pinentry_term)
1339 xfree(pwm->pinentry_term);
1341 pwm->pinentry_term = xstrdup(va_arg(ap, char *));
1342 break;
1343 case PWMD_OPTION_PINENTRY_TITLE:
1344 if (pwm->title)
1345 xfree(pwm->title);
1346 pwm->title = percent_escape(va_arg(ap, char *));
1347 break;
1348 case PWMD_OPTION_PINENTRY_PROMPT:
1349 if (pwm->prompt)
1350 xfree(pwm->prompt);
1351 pwm->prompt = percent_escape(va_arg(ap, char *));
1352 break;
1353 case PWMD_OPTION_PINENTRY_DESC:
1354 if (pwm->desc)
1355 xfree(pwm->desc);
1356 pwm->desc = percent_escape(va_arg(ap, char *));
1357 break;
1358 #else
1359 case PWMD_OPTION_PINENTRY:
1360 case PWMD_OPTION_PINENTRY_TRIES:
1361 case PWMD_OPTION_PINENTRY_PATH:
1362 case PWMD_OPTION_PINENTRY_TTY:
1363 case PWMD_OPTION_PINENTRY_DISPLAY:
1364 case PWMD_OPTION_PINENTRY_TERM:
1365 case PWMD_OPTION_PINENTRY_TITLE:
1366 case PWMD_OPTION_PINENTRY_PROMPT:
1367 case PWMD_OPTION_PINENTRY_DESC:
1368 error = GPG_ERR_NOT_IMPLEMENTED;
1369 break;
1370 #endif
1371 default:
1372 error = GPG_ERR_NOT_IMPLEMENTED;
1373 break;
1376 va_end(ap);
1377 return error;
1380 gpg_error_t pwmd_assuan_ctx(pwm_t *pwm, assuan_context_t *ctx, int *fd)
1382 if (!pwm)
1383 return GPG_ERR_INV_ARG;
1385 *ctx = pwm->ctx;
1386 *fd = pwm->fd;
1387 return 0;