Call pinentry_disconnect() from pwmd_open() even when there is no
[libpwmd.git] / libpwmd.c
blob8d56406352f443cf1e481555aa0541519c07b7fb
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 02111-1307 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 <libpwmd.h>
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
43 #ifdef HAVE_ASSUAN_H
44 #include <assuan.h>
45 #endif
47 #ifdef HAVE_SETLOCALE
48 #include <locale.h>
49 #endif
51 #include "gettext.h"
52 #define N_(msgid) dgettext("libpwmd", msgid)
54 #ifndef MEM_DEBUG
55 #include "mem.h"
56 #else
57 #define xfree free
58 #define xrealloc realloc
59 #define xmalloc malloc
60 #define xstrdup strdup
61 #define xcalloc calloc
62 #endif
64 #include "types.h"
66 #ifdef USE_PINENTRY
67 static pwm_t *gpwm;
68 static int gelapsed, gtimeout;
69 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd);
70 static gpg_error_t global_error;
71 #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;
119 if (!path) {
120 pw = getpwuid(getuid());
121 socketpath = (char *)xmalloc(strlen(pw->pw_dir) + strlen("/.pwmd/socket") + 1);
122 sprintf(socketpath, "%s/.pwmd/socket", pw->pw_dir);
124 else
125 socketpath = xstrdup(path);
127 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
128 xfree(socketpath);
130 if (rc) {
131 *error = rc;
132 return NULL;
135 if ((pwm = (pwm_t *)xcalloc(1, sizeof(pwm_t))) == NULL) {
136 *error = gpg_error_from_errno(errno);
137 assuan_disconnect(ctx);
138 return NULL;
141 pwm->ctx = ctx;
142 #ifdef USE_PINENTRY
143 pwm->pid = -1;
144 pwm->pinentry_tries = 3;
145 #endif
146 time(&now);
147 srandom(now);
148 *error = 0;
149 return pwm;
152 void pwmd_close(pwm_t *pwm)
154 if (!pwm)
155 return;
157 if (pwm->ctx)
158 assuan_disconnect(pwm->ctx);
160 if (pwm->password)
161 xfree(pwm->password);
163 if (pwm->title)
164 xfree(pwm->title);
166 if (pwm->desc)
167 xfree(pwm->desc);
169 if (pwm->prompt)
170 xfree(pwm->prompt);
172 if (pwm->pinentry_tty)
173 xfree(pwm->pinentry_tty);
175 if (pwm->pinentry_display)
176 xfree(pwm->pinentry_display);
178 if (pwm->pinentry_term)
179 xfree(pwm->pinentry_term);
181 if (pwm->filename)
182 xfree(pwm->filename);
184 xfree(pwm);
187 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
189 membuf_t *mem = (membuf_t *)data;
190 void *p;
192 if (!buffer)
193 return 0;
195 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
196 return 1;
198 mem->buf = p;
199 memcpy((char *)mem->buf + mem->len, buffer, len);
200 mem->len += len;
201 return 0;
204 void pwmd_free_result(void *data)
206 xfree(data);
209 static int _inquire_cb(void *data, const char *keyword)
211 pwm_t *pwm = (pwm_t *)data;
212 gpg_error_t rc = 0;
214 /* Shouldn't get this far without a callback. */
215 if (!pwm->inquire_func)
216 return GPG_ERR_INV_ARG;
218 for (;;) {
219 char *result = NULL;
220 size_t len;
221 gpg_error_t arc;
223 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
224 rc = gpg_err_code(rc);
226 if (rc == GPG_ERR_EOF || !rc) {
227 if (len <= 0 || !result || !*result) {
228 rc = 0;
229 break;
232 arc = assuan_send_data(pwm->ctx, result, len);
234 if (rc == GPG_ERR_EOF) {
235 rc = arc;
236 break;
239 rc = arc;
241 else if (rc)
242 break;
245 return rc;
248 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
249 char **result, const char *cmd)
251 membuf_t data;
252 gpg_error_t rc;
254 data.len = 0;
255 data.buf = NULL;
257 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
258 pwm->status_func, pwm->status_data);
260 if (rc) {
261 if (data.buf) {
262 xfree(data.buf);
263 data.buf = NULL;
266 else {
267 if (data.buf) {
268 mem_realloc_cb(&data, "", 1);
269 *result = (char *)data.buf;
273 return gpg_err_code(rc);
276 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_fn fn,
277 void *data)
279 if (!pwm || !cmd || !fn)
280 return GPG_ERR_INV_ARG;
282 pwm->inquire_func = fn;
283 pwm->inquire_data = data;
284 return assuan_command(pwm, pwm->ctx, NULL, cmd);
287 gpg_error_t pwmd_terminate_pinentry(pwm_t *pwm)
289 #ifndef USE_PINENTRY
290 return GPG_ERR_NOT_IMPLEMENTED;
291 #else
292 if (!pwm || pwm->pid == -1)
293 return GPG_ERR_INV_ARG;
295 if (kill(pwm->pid, 0) == 0) {
296 if (kill(pwm->pid, SIGTERM) == -1) {
297 if (kill(pwm->pid, SIGKILL) == -1)
298 return gpg_error_from_errno(errno);
301 pwm->pin_error = GPG_ERR_TIMEOUT;
303 else
304 return gpg_error_from_errno(errno);
306 return 0;
307 #endif
310 #ifdef USE_PINENTRY
311 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
313 char *buf;
314 char tmp[ASSUAN_LINELENGTH];
315 gpg_error_t error;
317 if (!pwm->title)
318 pwm->title = xstrdup(N_("LibPWMD"));
320 if (!pwm->prompt)
321 pwm->prompt = xstrdup(N_("Password:"));
323 if (!pwm->desc && !which)
324 pwm->desc = xstrdup(N_("Enter a password."));
326 if (which == 1) {
327 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Invalid password, please try again."));
328 buf = xstrdup(tmp);
330 else if (which == 2) {
331 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Please type the password again for confirmation."));
332 buf = xstrdup(tmp);
334 else {
335 buf = (char *)xmalloc(strlen("SETERROR ") + strlen(pwm->desc) + 1);
336 sprintf(buf, "SETERROR %s", pwm->desc);
339 error = pinentry_command(pwm, NULL, buf);
340 xfree(buf);
342 if (error)
343 return error;
345 buf = (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm->prompt) + 1);
346 sprintf(buf, "SETPROMPT %s", pwm->prompt);
347 error = pinentry_command(pwm, NULL, buf);
348 xfree(buf);
350 if (error)
351 return error;
353 buf = (char *)xmalloc(strlen("SETDESC ") + strlen(pwm->title) + 1);
354 sprintf(buf, "SETDESC %s", pwm->title);
355 error = pinentry_command(pwm, NULL, buf);
356 xfree(buf);
357 return error;
360 static void update_pinentry_settings(pwm_t *pwm)
362 FILE *fp;
363 char buf[LINE_MAX];
364 struct passwd *pw = getpwuid(getuid());
365 char *p;
367 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
369 if ((fp = fopen(buf, "r")) == NULL)
370 return;
372 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
373 char name[32], val[256];
375 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
376 continue;
378 if (strcasecmp(name, "TTYNAME") == 0) {
379 xfree(pwm->pinentry_tty);
380 pwm->pinentry_tty = xstrdup(val);
382 else if (strcasecmp(name, "TTYTYPE") == 0) {
383 xfree(pwm->pinentry_term);
384 pwm->pinentry_term = xstrdup(val);
386 else if (strcasecmp(name, "DISPLAY") == 0) {
387 xfree(pwm->pinentry_display);
388 pwm->pinentry_display = xstrdup(val);
390 else if (strcasecmp(name, "PATH") == 0) {
391 xfree(pwm->pinentry_path);
392 pwm->pinentry_path = xstrdup(val);
396 fclose(fp);
399 static gpg_error_t launch_pinentry(pwm_t *pwm)
401 int rc;
402 assuan_context_t ctx;
403 int child_list[] = {-1};
404 char *display = getenv("DISPLAY");
405 const char *argv[6];
406 int have_display = 0;
407 char *tty = NULL;
409 update_pinentry_settings(pwm);
411 if (pwm->pinentry_display || display)
412 have_display = 1;
413 else {
414 tty = pwm->pinentry_tty ? pwm->pinentry_tty : ttyname(STDOUT_FILENO);
416 if (!tty)
417 return gpg_error_from_errno(errno);
420 if (!have_display && !tty)
421 return GPG_ERR_ENOTTY;
423 argv[0] = "pinentry";
424 argv[1] = have_display ? "--display" : "--ttyname";
425 argv[2] = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
426 argv[3] = NULL;
428 if (!have_display) {
429 argv[3] = "--ttytype";
430 argv[4] = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
431 argv[5] = NULL;
434 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
436 if (rc)
437 return rc;
439 pwm->pid = assuan_get_pid(ctx);
440 pwm->pctx = ctx;
441 return set_pinentry_strings(pwm, 0);
444 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
446 gpg_error_t n;
448 if (!pwm->pctx) {
449 n = launch_pinentry(pwm);
451 if (n)
452 return n;
455 return assuan_command(pwm, pwm->pctx, result, cmd);
458 static void pinentry_disconnect(pwm_t *pwm)
460 if (pwm->pctx)
461 assuan_disconnect(pwm->pctx);
463 pwm->pctx = NULL;
464 pwm->pid = -1;
468 * Only called from a child process.
470 static void catchsig(int sig)
472 switch (sig) {
473 case SIGALRM:
474 if (gelapsed++ >= gtimeout) {
475 global_error = pwmd_terminate_pinentry(gpwm);
477 if (!global_error)
478 global_error = GPG_ERR_TIMEOUT;
480 break;
483 alarm(1);
484 break;
485 default:
486 break;
490 static char *percent_escape(const char *atext)
492 const unsigned char *s;
493 int len = strlen(atext) * 3 + 1;
494 char *buf = (char *)xmalloc(len), *p = buf;
496 if (!buf)
497 return NULL;
499 for (s=(const unsigned char *)atext; *s; s++) {
500 if (*s < ' ') {
501 sprintf (p, "%%%02X", *s);
502 p += 3;
504 else
505 *p++ = *s;
508 *p = 0;
509 return buf;
511 #endif
513 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
515 if (!cmd)
516 return GPG_ERR_INV_ARG;
518 return assuan_command(pwm, pwm->ctx, result, cmd);
522 * Avoid sending the BYE command here. libassuan will close the file
523 * descriptor and release the assuan context. Use pwmd_close() instead.
525 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
527 va_list ap;
528 char *buf;
529 size_t len;
530 gpg_error_t error;
532 if (!pwm || !cmd)
533 return GPG_ERR_INV_ARG;
535 *result = NULL;
536 va_start(ap, cmd);
538 * C99
540 len = vsnprintf(NULL, 0, cmd, ap);
541 buf = (char *)xmalloc(len + 1);
542 len = vsnprintf(buf, len + 1, cmd, ap);
543 va_end(ap);
544 error = send_command(pwm, result, buf);
545 xfree(buf);
546 return error;
549 #ifdef USE_PINENTRY
550 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
552 if (gtimeout) {
553 signal(SIGALRM, catchsig);
554 alarm(1);
557 *result = NULL;
558 return pinentry_command(pwm, result, "GETPIN");
561 static gpg_error_t getpin(pwm_t *pwm, char **result, int *try_n, int which)
563 int pin_try = *try_n;
564 gpg_error_t error;
566 getpin_again:
567 *try_n = pin_try;
569 if (pin_try == -1) {
570 error = set_pinentry_strings(pwm, which);
572 if (error) {
573 pinentry_disconnect(pwm);
574 return error;
577 else {
578 if (pwm->pinentry_tries-1 != pin_try) {
579 error = set_pinentry_strings(pwm, 1);
581 if (error) {
582 pinentry_disconnect(pwm);
583 return error;
588 error = do_getpin(pwm, result);
591 * Since there was input cancel any timeout.
593 alarm(0);
595 if (error) {
596 if (error == GPG_ERR_ASS_CANCELED || error == ASSUAN_Canceled)
597 return GPG_ERR_ASS_CANCELED;
599 if (pin_try != -1 && pin_try--)
600 goto getpin_again;
602 if (pwm->pctx)
603 pinentry_disconnect(pwm);
605 *try_n = pin_try;
606 return error;
609 return 0;
611 #endif
613 gpg_error_t pwmd_open_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
615 char *result;
616 gpg_error_t error;
618 #ifndef USE_PINENTRY
619 return GPG_ERR_NOT_IMPLEMENTED;
620 #endif
622 if (!pwm || !pw || !pw->filename[0])
623 return GPG_ERR_INV_ARG;
625 close(pw->fd);
627 if (pw->error) {
628 error = pw->error;
629 goto fail;
632 error = pwmd_command(pwm, &result, "ISCACHED %s", pw->filename);
634 if (error)
635 goto fail;
637 if (pwm->filename)
638 xfree(pwm->filename);
640 pwm->filename = xstrdup(pw->filename);
641 memset(pw, 0, sizeof(pwmd_nb_status_t));
642 return 0;
644 fail:
645 memset(pw, 0, sizeof(pwmd_nb_status_t));
646 return error;
649 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
651 char buf[ASSUAN_LINELENGTH];
652 gpg_error_t error;
653 char *result = NULL;
655 snprintf(buf, sizeof(buf), "OPEN %s %s", filename, password ? password : "");
656 error = send_command(pwm, &result, buf);
657 memset(buf, 0, sizeof(buf));
659 if (error && result)
660 xfree(result);
662 return error;
665 static int do_pwmd_open(pwm_t *pwm, gpg_error_t *error, const char *filename,
666 int nb, int timeout)
668 char *result = NULL;
669 char *password = NULL;
670 char path[PATH_MAX];
671 #ifdef USE_PINENTRY
672 int pin_try;
673 #endif
675 if (!pwm || !filename || !*filename) {
676 *error = GPG_ERR_INV_ARG;
677 return nb ? -1 : 1;
680 #ifdef USE_PINENTRY
681 pin_try = pwm->pinentry_tries - 1;
682 #endif
685 * Avoid calling pinentry if the password is cached on the server or if
686 * this is a new file.
688 *error = pwmd_command(pwm, &result, "GETCONFIG data_directory");
690 if (*error)
691 return nb ? -1 : 1;
693 snprintf(path, sizeof(path), "%s/%s", result, filename);
694 pwmd_free_result(result);
696 if (access(path, R_OK) == -1) {
697 if (errno == ENOENT)
698 goto gotpassword;
700 *error = gpg_error_from_errno(errno);
701 return nb ? -1 : 1;
704 *error = pwmd_command(pwm, &result, "ISCACHED %s", filename);
706 if (*error == EPWMD_CACHE_NOT_FOUND) {
707 if (pwm->passfunc) {
708 password = pwm->passfunc(pwm, pwm->passdata);
709 goto gotpassword;
712 if (*error == EPWMD_CACHE_NOT_FOUND) {
713 #ifdef USE_PINENTRY
715 * Get the password from pinentry.
717 if (pwm->use_pinentry) {
719 * Nonblocking is wanted. fork() then return a file descriptor
720 * that the client can use to read() from.
722 if (nb) {
723 int p[2];
724 pid_t pid;
725 pwmd_nb_status_t pw;
727 if (pipe(p) == -1) {
728 *error = gpg_error_from_syserror();
729 return -1;
732 pid = fork();
734 switch (pid) {
735 case 0:
736 close(p[0]);
737 strncpy(pw.filename, filename, sizeof(pw.filename));
738 pw.filename[sizeof(pw.filename)-1] = 0;
739 pw.fd = p[0];
741 if (timeout > 0) {
742 gpwm = pwm;
743 gtimeout = timeout;
744 gelapsed = 0;
747 getpin_nb_again:
748 *error = getpin(pwm, &password, &pin_try, 0);
750 if (*error) {
751 getpin_nb_fail:
752 if (pwm->pctx)
753 pinentry_disconnect(pwm);
755 if (gtimeout && gelapsed >= gtimeout)
756 *error = GPG_ERR_TIMEOUT;
758 pw.error = *error;
759 write(p[1], &pw, sizeof(pw));
760 close(p[1]);
761 _exit(1);
765 * Don't count the time it takes to open the file
766 * which may have many iterations.
768 signal(SIGALRM, SIG_DFL);
769 *error = do_open_command(pwm, filename, password);
771 if (timeout)
772 signal(SIGALRM, catchsig);
774 if (pwm->pctx && *error == EPWMD_BADKEY) {
775 if (pin_try-- > 0)
776 goto getpin_nb_again;
778 goto getpin_nb_fail;
781 pinentry_disconnect(pwm);
782 pw.error = 0;
783 write(p[1], &pw, sizeof(pw));
784 close(p[1]);
785 _exit(0);
786 break;
787 case -1:
788 *error = gpg_error_from_syserror();
789 close(p[0]);
790 close(p[1]);
791 return -1;
792 default:
793 break;
796 close(p[1]);
797 return p[0];
800 getpin_again:
801 *error = getpin(pwm, &password, &pin_try, 1);
803 if (*error) {
804 if (pwm->pctx)
805 pinentry_disconnect(pwm);
807 if (pwm->pin_error) {
808 *error = pwm->pin_error;
809 pwm->pin_error = 0;
812 return 1;
815 else {
816 #endif
818 * Not using pinentry and the file was not found
819 * in the cache.
821 #if 0
822 if (pwm->password == NULL) {
823 *error = EPWMD_KEY;
824 return 1;
826 #endif
828 password = pwm->password;
829 #ifdef USE_PINENTRY
831 #endif
834 else if (*error)
835 return nb ? -1 : 1;
837 gotpassword:
838 *error = do_open_command(pwm, filename, password);
841 * Keep the user defined password set with pwmd_setopt(). The password may
842 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
844 if (password && password != pwm->password)
845 xfree(password);
847 #ifdef USE_PINENTRY
848 if (pwm->pctx && *error == EPWMD_BADKEY) {
849 if (pin_try-- > 0)
850 goto getpin_again;
852 pinentry_disconnect(pwm);
853 return nb ? -1 : 1;
855 else if (pwm->pctx)
856 pinentry_disconnect(pwm);
857 #endif
859 if (!*error) {
860 if (pwm->filename)
861 xfree(pwm->filename);
863 pwm->filename = xstrdup(filename);
867 * The file is cached or the file is a new file.
869 if (nb)
870 return *error ? -1 : -2;
872 return *error ? 1 : 0;
875 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
877 gpg_error_t error;
879 do_pwmd_open(pwm, &error, filename, 0, 0);
880 return error;
883 int pwmd_open_nb(pwm_t *pwm, gpg_error_t *error, const char *filename,
884 int timeout)
886 #ifndef USE_PINENTRY
887 *error = GPG_ERR_NOT_IMPLEMENTED;
888 return -1;
889 #else
890 return do_pwmd_open(pwm, error, filename, 1, timeout);
891 #endif
894 #ifdef USE_PINENTRY
895 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
897 int confirm = 0;
898 gpg_error_t error;
899 char *result = NULL;
900 int pin_try = -1;
902 again:
903 error = getpin(pwm, &result, &pin_try, confirm ? 2 : 0);
905 if (error) {
906 if (pwm->pctx)
907 pinentry_disconnect(pwm);
909 if (*password)
910 xfree(*password);
912 return error;
915 if (!confirm++) {
916 *password = result;
917 goto again;
920 if (strcmp(*password, result)) {
921 xfree(*password);
922 xfree(result);
923 pinentry_disconnect(pwm);
924 error = EPWMD_BADKEY;
925 return error;
928 xfree(result);
929 pinentry_disconnect(pwm);
930 return 0;
932 #endif
934 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
936 char buf[ASSUAN_LINELENGTH];
937 gpg_error_t error;
938 char *result = NULL;
940 snprintf(buf, sizeof(buf), "SAVE %s", password ? password : "");
941 error = send_command(pwm, &result, buf);
942 memset(&buf, 0, sizeof(buf));
944 if (error && result)
945 xfree(result);
947 return error;
950 gpg_error_t pwmd_save_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
952 gpg_error_t error;
954 #ifndef USE_PINENTRY
955 return GPG_ERR_NOT_IMPLEMENTED;
956 #endif
958 if (!pwm || !pw || !pw->filename[0])
959 return GPG_ERR_INV_ARG;
961 close(pw->fd);
963 if (pw->error) {
964 error = pw->error;
965 memset(pw, 0, sizeof(pwmd_nb_status_t));
966 return error;
969 memset(pw, 0, sizeof(pwmd_nb_status_t));
970 return 0;
973 static int do_pwmd_save(pwm_t *pwm, gpg_error_t *error, int nb)
975 char *result = NULL;
976 char *password = NULL;
978 if (!pwm) {
979 *error = GPG_ERR_INV_ARG;
980 return nb ? -1 : 1;
983 if (pwm->use_pinentry || pwm->passfunc) {
984 *error = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
986 if (*error == EPWMD_CACHE_NOT_FOUND) {
987 #ifdef USE_PINENTRY
988 if (pwm->use_pinentry) {
989 if (nb) {
990 int p[2];
991 pid_t pid;
992 pwmd_nb_status_t pw;
994 if (pipe(p) == -1) {
995 *error = gpg_error_from_syserror();
996 return -1;
999 pid = fork();
1001 switch (pid) {
1002 case 0:
1003 close(p[0]);
1004 strncpy(pw.filename, pwm->filename, sizeof(pw.filename));
1005 pw.filename[sizeof(pw.filename)-1] = 0;
1006 pw.fd = p[0];
1008 do {
1009 password = NULL;
1010 *error = do_save_getpin(pwm, &password);
1011 } while (*error == EPWMD_BADKEY);
1013 if (*error) {
1014 if (pwm->pctx)
1015 pinentry_disconnect(pwm);
1017 pw.error = *error;
1018 write(p[1], &pw, sizeof(pw));
1019 close(p[1]);
1020 _exit(1);
1023 *error = do_save_command(pwm, password);
1024 pinentry_disconnect(pwm);
1025 pw.error = *error;
1026 write(p[1], &pw, sizeof(pw));
1027 close(p[1]);
1028 _exit(0);
1029 break;
1030 case -1:
1031 *error = gpg_error_from_syserror();
1032 close(p[0]);
1033 close(p[1]);
1034 return -1;
1035 default:
1036 break;
1039 close(p[1]);
1040 return p[0];
1043 *error = do_save_getpin(pwm, &password);
1045 if (*error)
1046 return 1;
1048 else {
1049 #endif
1050 if (pwm->passfunc)
1051 password = (*pwm->passfunc)(pwm, pwm->passdata);
1052 #ifdef USE_PINENTRY
1054 #endif
1056 else {
1057 if (*error)
1058 return nb ? -1 : 1;
1061 else
1062 password = pwm->password;
1064 *error = do_save_command(pwm, password);
1066 if (password && password != pwm->password)
1067 xfree(password);
1069 if (nb)
1070 return *error ? -1 : -2;
1072 return *error ? 1 : 0;
1075 int pwmd_save_nb(pwm_t *pwm, gpg_error_t *error)
1077 #ifndef USE_PINENTRY
1078 *error = GPG_ERR_NOT_IMPLEMENTED;
1079 return -1;
1080 #else
1081 return do_pwmd_save(pwm, error, 1);
1082 #endif
1085 gpg_error_t pwmd_save(pwm_t *pwm)
1087 gpg_error_t error;
1089 do_pwmd_save(pwm, &error, 0);
1090 return error;
1093 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1095 va_list ap;
1096 #ifdef USE_PINENTRY
1097 int n = va_arg(ap, int);
1098 char *result;
1099 #endif
1100 char *arg1;
1101 gpg_error_t error = 0;
1103 if (!pwm)
1104 return GPG_ERR_INV_ARG;
1106 va_start(ap, opt);
1108 switch (opt) {
1109 case PWMD_OPTION_STATUS_FUNC:
1110 pwm->status_func = va_arg(ap, pwmd_status_fn);
1111 break;
1112 case PWMD_OPTION_STATUS_DATA:
1113 pwm->status_data = va_arg(ap, void *);
1114 break;
1115 case PWMD_OPTION_PASSWORD_FUNC:
1116 pwm->passfunc = va_arg(ap, pwmd_password_fn);
1117 break;
1118 case PWMD_OPTION_PASSWORD_DATA:
1119 pwm->passdata = va_arg(ap, void *);
1120 break;
1121 case PWMD_OPTION_PASSWORD:
1122 arg1 = va_arg(ap, char *);
1124 if (pwm->password)
1125 xfree(pwm->password);
1127 pwm->password = xstrdup(arg1);
1128 break;
1129 #ifdef USE_PINENTRY
1130 case PWMD_OPTION_PINENTRY:
1131 n = va_arg(ap, int);
1133 if (n != 0 && n != 1) {
1134 va_end(ap);
1135 error = GPG_ERR_INV_VALUE;
1137 else {
1138 pwm->use_pinentry = n;
1139 error = pwmd_command(pwm, &result, "OPTION PINENTRY=%i",
1140 !pwm->use_pinentry);
1142 break;
1143 case PWMD_OPTION_PINENTRY_TRIES:
1144 n = va_arg(ap, int);
1146 if (n <= 0) {
1147 va_end(ap);
1148 error = GPG_ERR_INV_VALUE;
1150 else
1151 pwm->pinentry_tries = n;
1152 break;
1153 case PWMD_OPTION_PINENTRY_PATH:
1154 if (pwm->pinentry_path)
1155 xfree(pwm->pinentry_path);
1157 pwm->pinentry_path = xstrdup(va_arg(ap, char *));
1158 break;
1159 case PWMD_OPTION_PINENTRY_TTY:
1160 if (pwm->pinentry_tty)
1161 xfree(pwm->pinentry_tty);
1163 pwm->pinentry_tty = xstrdup(va_arg(ap, char *));
1164 break;
1165 case PWMD_OPTION_PINENTRY_DISPLAY:
1166 if (pwm->pinentry_display)
1167 xfree(pwm->pinentry_display);
1169 pwm->pinentry_display = xstrdup(va_arg(ap, char *));
1170 break;
1171 case PWMD_OPTION_PINENTRY_TERM:
1172 if (pwm->pinentry_term)
1173 xfree(pwm->pinentry_term);
1175 pwm->pinentry_term = xstrdup(va_arg(ap, char *));
1176 break;
1177 case PWMD_OPTION_PINENTRY_TITLE:
1178 if (pwm->title)
1179 xfree(pwm->title);
1180 pwm->title = percent_escape(va_arg(ap, char *));
1181 break;
1182 case PWMD_OPTION_PINENTRY_PROMPT:
1183 if (pwm->prompt)
1184 xfree(pwm->prompt);
1185 pwm->prompt = percent_escape(va_arg(ap, char *));
1186 break;
1187 case PWMD_OPTION_PINENTRY_DESC:
1188 if (pwm->desc)
1189 xfree(pwm->desc);
1190 pwm->desc = percent_escape(va_arg(ap, char *));
1191 break;
1192 #else
1193 case PWMD_OPTION_PINENTRY:
1194 case PWMD_OPTION_PINENTRY_TRIES:
1195 case PWMD_OPTION_PINENTRY_PATH:
1196 case PWMD_OPTION_PINENTRY_TTY:
1197 case PWMD_OPTION_PINENTRY_DISPLAY:
1198 case PWMD_OPTION_PINENTRY_TERM:
1199 case PWMD_OPTION_PINENTRY_TITLE:
1200 case PWMD_OPTION_PINENTRY_PROMPT:
1201 case PWMD_OPTION_PINENTRY_DESC:
1202 error = GPG_ERR_NOT_IMPLEMENTED;
1203 break;
1204 #endif
1205 default:
1206 error = GPG_ERR_NOT_IMPLEMENTED;
1207 break;
1210 va_end(ap);
1211 return error;