Fixed pwmd_connect() not setting 'error' to 0 when successful.
[libpwmd.git] / libpwmd.c
blob398c5447428c1ecd2ab95c474dea472c0f43a144
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2007 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 #endif
72 static gpg_error_t global_error;
74 const char *pwmd_strerror(gpg_error_t error)
76 gpg_err_code_t code = gpg_err_code(error);
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_("Element not found");
87 case GPG_ERR_USER_4:
88 return N_("Trailing element");
89 case GPG_ERR_USER_5:
90 return N_("Invalid character in element");
91 case GPG_ERR_USER_6:
92 return N_("Empty");
93 case GPG_ERR_USER_7:
94 return N_("Will not overwrite existing account");
95 case GPG_ERR_USER_8:
96 return N_("File not found");
97 case GPG_ERR_USER_9:
98 return N_("No file is open");
99 case GPG_ERR_USER_10:
100 return N_("General LibXML error");
101 case GPG_ERR_USER_11:
102 return N_("File not found in cache");
103 case GPG_ERR_USER_12:
104 return N_("Attribute not found");
105 case GPG_ERR_USER_13:
106 return N_("Invalid filename or link");
107 case GPG_ERR_USER_14:
108 return N_("File modified");
112 return gpg_strerror(error);
115 gpg_error_t pwmd_init()
117 #ifdef ENABLE_NLS
118 bindtextdomain("libpwmd", LOCALEDIR);
119 #endif
120 gpg_err_init();
121 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
122 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
123 return 0;
126 pwm_t *pwmd_connect(const char *path, gpg_error_t *error)
128 pwm_t *pwm = NULL;
129 char *socketpath = NULL;
130 time_t now;
131 struct passwd *pw;
132 assuan_context_t ctx;
133 int rc;
135 if (!path) {
136 pw = getpwuid(getuid());
137 socketpath = (char *)xmalloc(strlen(pw->pw_dir) + strlen("/.pwmd/socket") + 1);
138 sprintf(socketpath, "%s/.pwmd/socket", pw->pw_dir);
140 else
141 socketpath = xstrdup(path);
143 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
144 xfree(socketpath);
146 if (rc) {
147 *error = rc;
148 return NULL;
151 if ((pwm = (pwm_t *)xcalloc(1, sizeof(pwm_t))) == NULL) {
152 *error = gpg_error_from_errno(errno);
153 assuan_disconnect(ctx);
154 return NULL;
157 pwm->ctx = ctx;
158 #ifdef USE_PINENTRY
159 pwm->pid = -1;
160 pwm->pinentry_tries = 3;
161 #endif
162 time(&now);
163 srandom(now);
164 *error = 0;
165 return pwm;
168 void pwmd_close(pwm_t *pwm)
170 if (!pwm)
171 return;
173 if (pwm->ctx)
174 assuan_disconnect(pwm->ctx);
176 if (pwm->password)
177 xfree(pwm->password);
179 if (pwm->title)
180 xfree(pwm->title);
182 if (pwm->desc)
183 xfree(pwm->desc);
185 if (pwm->prompt)
186 xfree(pwm->prompt);
188 if (pwm->pinentry_tty)
189 xfree(pwm->pinentry_tty);
191 if (pwm->pinentry_display)
192 xfree(pwm->pinentry_display);
194 if (pwm->pinentry_term)
195 xfree(pwm->pinentry_term);
197 if (pwm->filename)
198 xfree(pwm->filename);
200 xfree(pwm);
203 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
205 membuf_t *mem = (membuf_t *)data;
206 void *p;
208 if (!buffer)
209 return 0;
211 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
212 return 1;
214 mem->buf = p;
215 memcpy((char *)mem->buf + mem->len, buffer, len);
216 mem->len += len;
217 return 0;
220 static int inquire_cb(void *data, const char *keyword)
222 struct inquire_s *inquire = (struct inquire_s *)data;
224 return assuan_send_data(inquire->ctx, inquire->buf, inquire->len);
227 void pwmd_free_result(void *data)
229 xfree(data);
232 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
233 char **result, const char *cmd)
235 membuf_t data;
236 gpg_error_t rc;
238 data.len = 0;
239 data.buf = NULL;
242 * This is needed because assuan only accepts 1000 byte command strings.
243 * If the line is more than this the command will fail. So we use an
244 * INQUIRE on the server which waits for an END that assuan_transact()
245 * fulfills.
247 * Other commands shouldn't need the INQUIRE. Let me know if you have an
248 * element path that's greater than 1000 bytes and I'll fix it.
250 if (strncasecmp(cmd, "STORE", 5) == 0) {
251 const char *p = cmd + 5;
252 struct inquire_s *inq = (struct inquire_s *)xmalloc(sizeof(struct inquire_s));
254 if (*p == ' ')
255 p++;
257 inq->ctx = ctx;
258 inq->buf = p;
259 inq->len = strlen(p);
260 rc = assuan_transact(ctx, "STORE", mem_realloc_cb, &data, inquire_cb,
261 inq, pwm->status_func, pwm->status_data);
262 xfree(inq);
264 else {
265 #ifdef USE_PINENTRY
267 * Ignore any status callback function for pinentry.
269 if (ctx == pwm->pctx)
270 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
271 else
272 #endif
273 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, NULL, NULL,
274 pwm->status_func, pwm->status_data);
277 if (rc) {
278 if (data.buf) {
279 xfree(data.buf);
280 data.buf = NULL;
283 else {
284 if (data.buf) {
285 mem_realloc_cb(&data, "", 1);
286 *result = (char *)data.buf;
290 return gpg_err_code(rc);
293 gpg_error_t pwmd_terminate_pinentry(pwm_t *pwm)
295 #ifndef USE_PINENTRY
296 return GPG_ERR_NOT_IMPLEMENTED;
297 #endif
299 if (!pwm || pwm->pid == -1)
300 return GPG_ERR_INV_ARG;
302 if (kill(pwm->pid, 0) == 0) {
303 if (kill(pwm->pid, SIGTERM) == -1) {
304 if (kill(pwm->pid, SIGKILL) == -1)
305 return gpg_error_from_errno(errno);
308 global_error = GPG_ERR_TIMEOUT;
310 else
311 return gpg_error_from_errno(errno);
313 return 0;
316 #ifdef USE_PINENTRY
317 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
319 char *buf;
320 char tmp[ASSUAN_LINELENGTH];
321 gpg_error_t error;
323 if (!pwm->title)
324 pwm->title = xstrdup(N_("LibPWMD"));
326 if (!pwm->prompt)
327 pwm->prompt = xstrdup(N_("Password:"));
329 if (!pwm->desc && !which)
330 pwm->desc = xstrdup(N_("Enter a password."));
332 if (which == 1) {
333 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Invalid password, please try again."));
334 buf = xstrdup(tmp);
336 else if (which == 2) {
337 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Please type the password again for confirmation."));
338 buf = xstrdup(tmp);
340 else {
341 buf = (char *)xmalloc(strlen("SETERROR ") + strlen(pwm->desc) + 1);
342 sprintf(buf, "SETERROR %s", pwm->desc);
345 error = pinentry_command(pwm, NULL, buf);
346 xfree(buf);
348 if (error)
349 return error;
351 buf = (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm->prompt) + 1);
352 sprintf(buf, "SETPROMPT %s", pwm->prompt);
353 error = pinentry_command(pwm, NULL, buf);
354 xfree(buf);
356 if (error)
357 return error;
359 buf = (char *)xmalloc(strlen("SETDESC ") + strlen(pwm->title) + 1);
360 sprintf(buf, "SETDESC %s", pwm->title);
361 error = pinentry_command(pwm, NULL, buf);
362 xfree(buf);
363 return error;
366 static void update_pinentry_settings(pwm_t *pwm)
368 FILE *fp;
369 char buf[LINE_MAX];
370 struct passwd *pw = getpwuid(getuid());
371 char *p;
373 snprintf(buf, sizeof(buf), "%s/.pwmd/env", pw->pw_dir);
375 if ((fp = fopen(buf, "r")) == NULL)
376 return;
378 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
379 char name[32], val[256];
381 if (sscanf(p, "%[a-zA-Z]=%s", name, val) != 2)
382 continue;
384 if (strcasecmp(name, "TTY") == 0) {
385 if (!pwm->pinentry_tty) {
386 xfree(pwm->pinentry_tty);
387 pwm->pinentry_tty = xstrdup(val);
390 else if (strcasecmp(name, "TERM") == 0) {
391 if (!pwm->pinentry_term) {
392 xfree(pwm->pinentry_term);
393 pwm->pinentry_term = xstrdup(val);
396 else if (strcasecmp(name, "DISPLAY") == 0) {
397 if (!pwm->pinentry_display) {
398 xfree(pwm->pinentry_display);
399 pwm->pinentry_display = xstrdup(val);
404 fclose(fp);
407 static gpg_error_t launch_pinentry(pwm_t *pwm)
409 int rc;
410 assuan_context_t ctx;
411 int child_list[] = {-1};
412 char *display = getenv("DISPLAY");
413 const char *argv[6];
414 int have_display = 0;
415 char *tty = NULL;
417 update_pinentry_settings(pwm);
419 if (pwm->pinentry_display || display)
420 have_display = 1;
421 else {
422 tty = pwm->pinentry_tty ? pwm->pinentry_tty : ttyname(STDOUT_FILENO);
424 if (!tty)
425 return gpg_error_from_errno(errno);
428 if (!display && !tty)
429 return GPG_ERR_ENOTTY;
431 argv[0] = "pinentry";
432 argv[1] = have_display ? "--display" : "--ttyname";
433 argv[2] = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
434 argv[3] = NULL;
436 if (!have_display) {
437 argv[3] = "--ttytype";
438 argv[4] = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
439 argv[5] = NULL;
442 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
444 if (rc)
445 return rc;
447 pwm->pid = assuan_get_pid(ctx);
448 pwm->pctx = ctx;
449 return set_pinentry_strings(pwm, 0);
452 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
454 gpg_error_t n;
456 if (!pwm->pctx) {
457 n = launch_pinentry(pwm);
459 if (n)
460 return n;
463 return assuan_command(pwm, pwm->pctx, result, cmd);
466 static void pinentry_disconnect(pwm_t *pwm)
468 if (pwm->pctx)
469 assuan_disconnect(pwm->pctx);
471 pwm->pctx = NULL;
472 pwm->pid = -1;
476 * Only called from a child process.
478 static void catchsig(int sig)
480 switch (sig) {
481 case SIGALRM:
482 if (gelapsed++ >= gtimeout) {
483 global_error = pwmd_terminate_pinentry(gpwm);
485 if (!global_error)
486 global_error = GPG_ERR_TIMEOUT;
488 break;
491 alarm(1);
492 break;
493 default:
494 break;
498 static char *percent_escape(const char *atext)
500 const unsigned char *s;
501 int len = strlen(atext) * 3 + 1;
502 char *buf = (char *)xmalloc(len), *p = buf;
504 if (!buf)
505 return NULL;
507 for (s=(const unsigned char *)atext; *s; s++) {
508 if (*s < ' ') {
509 sprintf (p, "%%%02X", *s);
510 p += 3;
512 else
513 *p++ = *s;
516 *p = 0;
517 return buf;
519 #endif
521 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
523 if (!cmd)
524 return GPG_ERR_INV_ARG;
526 return assuan_command(pwm, pwm->ctx, result, cmd);
530 * Avoid sending the BYE command here. libassuan will close the file
531 * descriptor and release the assuan context. Use pwmd_close() instead.
533 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
535 va_list ap;
536 char *buf;
537 size_t len;
538 gpg_error_t error;
540 if (!pwm || !cmd)
541 return GPG_ERR_INV_ARG;
543 *result = NULL;
544 va_start(ap, cmd);
546 * C99
548 len = vsnprintf(NULL, 0, cmd, ap);
549 buf = (char *)xmalloc(len + 1);
550 len = vsnprintf(buf, len + 1, cmd, ap);
551 va_end(ap);
552 error = send_command(pwm, result, buf);
553 xfree(buf);
554 return error;
557 #ifdef USE_PINENTRY
558 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
560 gpg_error_t error;
562 if (gtimeout) {
563 signal(SIGALRM, catchsig);
564 alarm(1);
567 *result = NULL;
568 error = pinentry_command(pwm, result, "GETPIN");
570 if (error == GPG_ERR_ASS_CANCELED)
571 return error;
573 if (!*result)
574 return EPWMD_KEY;
576 return 0;
579 static gpg_error_t getpin(pwm_t *pwm, char **result, int *try_n, int which)
581 int pin_try = *try_n;
582 gpg_error_t error;
584 getpin_again:
585 *try_n = pin_try;
587 if (pin_try == -1) {
588 error = set_pinentry_strings(pwm, which);
590 if (error) {
591 pinentry_disconnect(pwm);
592 return error;
595 else {
596 if (pwm->pinentry_tries-1 != pin_try) {
597 error = set_pinentry_strings(pwm, 1);
599 if (error) {
600 pinentry_disconnect(pwm);
601 return error;
606 error = do_getpin(pwm, result);
609 * Since there was input cancel any timeout.
611 alarm(0);
613 if (error) {
614 if (pin_try != -1 && pin_try-- && error == EPWMD_KEY)
615 goto getpin_again;
617 if (pwm->pctx)
618 pinentry_disconnect(pwm);
620 *try_n = pin_try;
621 return error;
624 return 0;
626 #endif
628 gpg_error_t pwmd_open_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
630 char *result;
631 gpg_error_t error;
633 #ifndef USE_PINENTRY
634 return GPG_ERR_NOT_IMPLEMENTED;
635 #endif
637 if (!pwm || !pw || !pw->filename[0])
638 return GPG_ERR_INV_ARG;
640 close(pw->fd);
642 if (pw->error) {
643 error = pw->error;
644 goto fail;
647 error = pwmd_command(pwm, &result, "ISCACHED %s", pw->filename);
649 if (error)
650 goto fail;
652 if (pwm->filename)
653 xfree(pwm->filename);
655 pwm->filename = xstrdup(pw->filename);
656 memset(pw, 0, sizeof(pwmd_nb_status_t));
657 return 0;
659 fail:
660 memset(pw, 0, sizeof(pwmd_nb_status_t));
661 return error;
664 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
666 char buf[ASSUAN_LINELENGTH];
667 gpg_error_t error;
669 snprintf(buf, sizeof(buf), "OPEN %s %s", filename, password ? password : "");
670 error = send_command(pwm, NULL, buf);
671 memset(buf, 0, sizeof(buf));
672 return error;
675 static int do_pwmd_open(pwm_t *pwm, gpg_error_t *error, const char *filename,
676 int nb, int timeout)
678 char *result = NULL;
679 char *password = NULL;
680 char path[PATH_MAX];
681 #ifdef USE_PINENTRY
682 int pin_try;
683 #endif
685 if (!pwm || !filename || !*filename) {
686 *error = GPG_ERR_INV_ARG;
687 return nb ? -1 : 1;
690 #ifdef USE_PINENTRY
691 pin_try = pwm->pinentry_tries - 1;
692 #endif
695 * Avoid calling pinentry if the password is cached on the server or if
696 * this is a new file.
698 *error = pwmd_command(pwm, &result, "GETCONFIG data_directory");
700 if (*error)
701 return nb ? -1 : 1;
703 snprintf(path, sizeof(path), "%s/%s", result, filename);
704 pwmd_free_result(result);
706 if (access(path, R_OK) == -1) {
707 if (errno == ENOENT)
708 goto gotpassword;
710 *error = gpg_error_from_errno(errno);
711 return nb ? -1 : 1;
714 *error = pwmd_command(pwm, &result, "ISCACHED %s", filename);
716 if (*error == EPWMD_CACHE_NOT_FOUND) {
717 if (pwm->passfunc) {
718 password = pwm->passfunc(pwm, pwm->passdata);
720 if (!password || !*password) {
721 *error = EPWMD_KEY;
722 return nb ? -1 : 1;
725 password = xstrdup(password);
726 goto gotpassword;
729 if (*error == EPWMD_CACHE_NOT_FOUND) {
730 #ifdef USE_PINENTRY
732 * Get the password from pinentry.
734 if (pwm->use_pinentry) {
736 * Nonblocking is wanted. fork() then return a file descriptor
737 * that the client can use to read() from.
739 if (nb) {
740 int p[2];
741 pid_t pid;
742 pwmd_nb_status_t pw;
744 if (pipe(p) == -1) {
745 *error = gpg_error_from_syserror();
746 return -1;
749 pid = fork();
751 switch (pid) {
752 case 0:
753 close(p[0]);
754 strncpy(pw.filename, filename, sizeof(pw.filename));
755 pw.fd = p[0];
757 if (timeout > 0) {
758 gpwm = pwm;
759 gtimeout = timeout;
760 gelapsed = 0;
763 getpin_nb_again:
764 *error = getpin(pwm, &password, &pin_try, 0);
766 if (*error) {
767 getpin_nb_fail:
768 if (pwm->pctx)
769 pinentry_disconnect(pwm);
771 if (gtimeout && gelapsed >= gtimeout)
772 *error = GPG_ERR_TIMEOUT;
774 pw.error = *error;
775 write(p[1], &pw, sizeof(pw));
776 close(p[1]);
777 _exit(1);
781 * Don't count the time it takes to open the file
782 * which may have many iterations.
784 signal(SIGALRM, SIG_DFL);
785 *error = do_open_command(pwm, filename, password);
787 if (timeout)
788 signal(SIGALRM, catchsig);
790 if (pwm->pctx && *error == EPWMD_BADKEY) {
791 if (pin_try-- > 0)
792 goto getpin_nb_again;
794 goto getpin_nb_fail;
797 pinentry_disconnect(pwm);
798 pw.error = 0;
799 write(p[1], &pw, sizeof(pw));
800 close(p[1]);
801 _exit(0);
802 break;
803 case -1:
804 *error = gpg_error_from_syserror();
805 close(p[0]);
806 close(p[1]);
807 return -1;
808 default:
809 break;
812 close(p[1]);
813 return p[0];
816 getpin_again:
817 *error = getpin(pwm, &password, &pin_try, 1);
819 if (*error) {
820 if (pwm->pctx)
821 pinentry_disconnect(pwm);
823 if (global_error) {
824 *error = global_error;
825 global_error = 0;
828 return 1;
831 else {
832 #endif
834 * Not using pinentry and the file was not found
835 * in the cache.
837 if (pwm->password == NULL) {
838 *error = EPWMD_KEY;
839 return 1;
842 password = pwm->password;
843 #ifdef USE_PINENTRY
845 #endif
848 else if (*error)
849 return nb ? -1 : 1;
851 gotpassword:
852 *error = do_open_command(pwm, filename, password);
855 * Keep the user defined password set with pwmd_setopt(). The password may
856 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
858 if (password && password != pwm->password)
859 xfree(password);
861 #ifdef USE_PINENTRY
862 if (pwm->pctx && *error == EPWMD_BADKEY) {
863 if (pin_try-- > 0)
864 goto getpin_again;
866 pinentry_disconnect(pwm);
867 return nb ? -1 : 1;
869 #endif
871 if (!*error) {
872 if (pwm->filename)
873 xfree(pwm->filename);
875 pwm->filename = xstrdup(filename);
879 * The file is cached or the file is a new file.
881 if (nb)
882 return *error ? -1 : -2;
884 return *error ? 1 : 0;
887 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
889 gpg_error_t error;
891 do_pwmd_open(pwm, &error, filename, 0, 0);
892 return error;
895 int pwmd_open_nb(pwm_t *pwm, gpg_error_t *error, const char *filename,
896 int timeout)
898 #ifndef USE_PINENTRY
899 *error = GPG_ERR_NOT_IMPLEMENTED;
900 return -1;
901 #else
902 return do_pwmd_open(pwm, error, filename, 1, timeout);
903 #endif
906 #ifdef USE_PINENTRY
907 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
909 int confirm = 0;
910 gpg_error_t error;
911 char *result = NULL;
912 int pin_try = -1;
914 again:
915 error = getpin(pwm, &result, &pin_try, confirm ? 2 : 0);
917 if (error) {
918 if (pwm->pctx)
919 pinentry_disconnect(pwm);
921 if (*password)
922 xfree(*password);
924 return error;
927 if (!confirm++) {
928 *password = result;
929 goto again;
932 if (strcmp(*password, result)) {
933 xfree(*password);
934 xfree(result);
935 pinentry_disconnect(pwm);
936 error = EPWMD_BADKEY;
937 return error;
940 xfree(result);
941 pinentry_disconnect(pwm);
942 return 0;
944 #endif
946 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
948 char buf[ASSUAN_LINELENGTH];
949 gpg_error_t error;
951 snprintf(buf, sizeof(buf), "SAVE %s", password ? password : "");
952 error = send_command(pwm, NULL, buf);
953 memset(&buf, 0, sizeof(buf));
954 return error;
957 gpg_error_t pwmd_save_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
959 gpg_error_t error;
961 #ifndef USE_PINENTRY
962 return GPG_ERR_NOT_IMPLEMENTED;
963 #endif
965 if (!pwm || !pw || !pw->filename[0])
966 return GPG_ERR_INV_ARG;
968 close(pw->fd);
970 if (pw->error) {
971 error = pw->error;
972 memset(pw, 0, sizeof(pwmd_nb_status_t));
973 return error;
976 memset(pw, 0, sizeof(pwmd_nb_status_t));
977 return 0;
980 static int do_pwmd_save(pwm_t *pwm, gpg_error_t *error, int nb)
982 char *result = NULL;
983 char *password = NULL;
985 if (!pwm) {
986 *error = GPG_ERR_INV_ARG;
987 return nb ? -1 : 1;
990 if (pwm->use_pinentry || pwm->passfunc) {
991 *error = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
993 if (*error == EPWMD_CACHE_NOT_FOUND) {
994 #ifdef USE_PINENTRY
995 if (pwm->use_pinentry) {
996 if (nb) {
997 int p[2];
998 pid_t pid;
999 pwmd_nb_status_t pw;
1001 if (pipe(p) == -1) {
1002 *error = gpg_error_from_syserror();
1003 return -1;
1006 pid = fork();
1008 switch (pid) {
1009 case 0:
1010 close(p[0]);
1011 strncpy(pw.filename, pwm->filename, sizeof(pw.filename));
1012 pw.fd = p[0];
1014 do {
1015 password = NULL;
1016 *error = do_save_getpin(pwm, &password);
1017 } while (*error == EPWMD_KEY || *error == EPWMD_BADKEY);
1019 if (*error) {
1020 if (pwm->pctx)
1021 pinentry_disconnect(pwm);
1023 pw.error = *error;
1024 write(p[1], &pw, sizeof(pw));
1025 close(p[1]);
1026 _exit(1);
1029 *error = do_save_command(pwm, password);
1030 pinentry_disconnect(pwm);
1031 pw.error = *error;
1032 write(p[1], &pw, sizeof(pw));
1033 close(p[1]);
1034 _exit(0);
1035 break;
1036 case -1:
1037 *error = gpg_error_from_syserror();
1038 close(p[0]);
1039 close(p[1]);
1040 return -1;
1041 default:
1042 break;
1045 close(p[1]);
1046 return p[0];
1049 *error = do_save_getpin(pwm, &password);
1051 if (*error)
1052 return 1;
1054 else {
1055 #endif
1056 if (pwm->passfunc) {
1057 char *tmp = (*pwm->passfunc)(pwm, pwm->passdata);
1059 if (!tmp || !*tmp) {
1060 *error = EPWMD_KEY;
1061 return 1;
1064 password = xstrdup(tmp);
1066 #ifdef USE_PINENTRY
1068 #endif
1070 if (!password || !*password) {
1071 *error = EPWMD_KEY;
1072 return 1;
1075 else {
1076 if (*error)
1077 return nb ? -1 : 1;
1080 else
1081 password = pwm->password;
1083 *error = do_save_command(pwm, password);
1085 if (password && password != pwm->password)
1086 xfree(password);
1088 if (nb)
1089 return *error ? -1 : -2;
1091 return *error ? 1 : 0;
1094 int pwmd_save_nb(pwm_t *pwm, gpg_error_t *error)
1096 #ifndef USE_PINENTRY
1097 *error = GPG_ERR_NOT_IMPLEMENTED;
1098 return -1;
1099 #else
1100 return do_pwmd_save(pwm, error, 1);
1101 #endif
1104 gpg_error_t pwmd_save(pwm_t *pwm)
1106 gpg_error_t error;
1108 do_pwmd_save(pwm, &error, 0);
1109 return error;
1112 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1114 va_list ap;
1115 #ifdef USE_PINENTRY
1116 int n = va_arg(ap, int);
1117 #endif
1118 char *arg1;
1120 if (!pwm)
1121 return GPG_ERR_INV_ARG;
1123 va_start(ap, opt);
1125 switch (opt) {
1126 case PWMD_OPTION_STATUS_FUNC:
1127 pwm->status_func = va_arg(ap, pwmd_status_fn);
1128 break;
1129 case PWMD_OPTION_STATUS_DATA:
1130 pwm->status_data = va_arg(ap, void *);
1131 break;
1132 case PWMD_OPTION_PASSWORD_FUNC:
1133 pwm->passfunc = va_arg(ap, pwmd_password_fn);
1134 break;
1135 case PWMD_OPTION_PASSWORD_DATA:
1136 pwm->passdata = va_arg(ap, void *);
1137 break;
1138 case PWMD_OPTION_PASSWORD:
1139 arg1 = va_arg(ap, char *);
1141 if (pwm->password)
1142 xfree(pwm->password);
1144 pwm->password = xstrdup(arg1);
1145 break;
1146 #ifdef USE_PINENTRY
1147 case PWMD_OPTION_PINENTRY:
1148 n = va_arg(ap, int);
1150 if (n != 0 && n != 1) {
1151 va_end(ap);
1152 return GPG_ERR_INV_VALUE;
1155 pwm->use_pinentry = n;
1156 break;
1157 case PWMD_OPTION_PINENTRY_TRIES:
1158 n = va_arg(ap, int);
1160 if (n <= 0) {
1161 va_end(ap);
1162 return GPG_ERR_INV_VALUE;
1165 pwm->pinentry_tries = n;
1166 break;
1167 case PWMD_OPTION_PINENTRY_PATH:
1168 if (pwm->pinentry_path)
1169 xfree(pwm->pinentry_path);
1171 pwm->pinentry_path = xstrdup(va_arg(ap, char *));
1172 break;
1173 case PWMD_OPTION_PINENTRY_TTY:
1174 if (pwm->pinentry_tty)
1175 xfree(pwm->pinentry_tty);
1177 pwm->pinentry_tty = xstrdup(va_arg(ap, char *));
1178 break;
1179 case PWMD_OPTION_PINENTRY_DISPLAY:
1180 if (pwm->pinentry_display)
1181 xfree(pwm->pinentry_display);
1183 pwm->pinentry_display = xstrdup(va_arg(ap, char *));
1184 break;
1185 case PWMD_OPTION_PINENTRY_TERM:
1186 if (pwm->pinentry_term)
1187 xfree(pwm->pinentry_term);
1189 pwm->pinentry_term = xstrdup(va_arg(ap, char *));
1190 break;
1191 case PWMD_OPTION_PINENTRY_TITLE:
1192 if (pwm->title)
1193 xfree(pwm->title);
1194 pwm->title = percent_escape(va_arg(ap, char *));
1195 break;
1196 case PWMD_OPTION_PINENTRY_PROMPT:
1197 if (pwm->prompt)
1198 xfree(pwm->prompt);
1199 pwm->prompt = percent_escape(va_arg(ap, char *));
1200 break;
1201 case PWMD_OPTION_PINENTRY_DESC:
1202 if (pwm->desc)
1203 xfree(pwm->desc);
1204 pwm->desc = percent_escape(va_arg(ap, char *));
1205 break;
1206 #else
1207 case PWMD_OPTION_PINENTRY:
1208 case PWMD_OPTION_PINENTRY_TRIES:
1209 case PWMD_OPTION_PINENTRY_PATH:
1210 case PWMD_OPTION_PINENTRY_TTY:
1211 case PWMD_OPTION_PINENTRY_DISPLAY:
1212 case PWMD_OPTION_PINENTRY_TERM:
1213 case PWMD_OPTION_PINENTRY_TITLE:
1214 case PWMD_OPTION_PINENTRY_PROMPT:
1215 case PWMD_OPTION_PINENTRY_DESC:
1216 return GPG_ERR_NOT_IMPLEMENTED;
1217 #endif
1218 default:
1219 va_end(ap);
1220 return GPG_ERR_NOT_IMPLEMENTED;
1223 va_end(ap);
1224 return 0;