Include limits.h. Fixes a FreeBSD build.
[libpwmd.git] / libpwmd.c
bloba8661aa5c41f50c794fbebba3886c8bc214f42bf
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 "mem.h"
52 #include "types.h"
54 #ifdef USE_PINENTRY
55 #define PINENTRY_PATH "/usr/bin/pinentry"
56 static pwm_t *gpwm;
57 static int gelapsed, gtimeout;
58 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd);
59 #endif
61 const char *pwmd_strerror(gpg_error_t error)
63 gpg_err_code_t code = gpg_err_code(error);
65 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
66 switch (code) {
67 case GPG_ERR_USER_1:
68 default:
69 return "Unknown error";
70 case GPG_ERR_USER_2:
71 return "No cache slots available";
72 case GPG_ERR_USER_3:
73 return "Element not found";
74 case GPG_ERR_USER_4:
75 return "Trailing element";
76 case GPG_ERR_USER_5:
77 return "Invalid character in element";
78 case GPG_ERR_USER_6:
79 return "Empty";
80 case GPG_ERR_USER_7:
81 return "Will not overwrite existing account";
82 case GPG_ERR_USER_8:
83 return "File not found";
84 case GPG_ERR_USER_9:
85 return "No file is open";
86 case GPG_ERR_USER_10:
87 return "General LibXML error";
88 case GPG_ERR_USER_11:
89 return "File not found in cache";
90 case GPG_ERR_USER_12:
91 return "Attribute not found";
92 case GPG_ERR_USER_13:
93 return "Invalid filename or link";
94 case GPG_ERR_USER_14:
95 return "File modified";
99 return gpg_strerror(error);
102 gpg_error_t pwmd_init()
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 return pwm;
151 void pwmd_close(pwm_t *pwm)
153 if (!pwm)
154 return;
156 if (pwm->ctx)
157 assuan_disconnect(pwm->ctx);
159 if (pwm->password)
160 xfree(pwm->password);
162 if (pwm->title)
163 xfree(pwm->title);
165 if (pwm->desc)
166 xfree(pwm->desc);
168 if (pwm->prompt)
169 xfree(pwm->prompt);
171 if (pwm->pinentry_tty)
172 xfree(pwm->pinentry_tty);
174 if (pwm->pinentry_display)
175 xfree(pwm->pinentry_display);
177 if (pwm->pinentry_term)
178 xfree(pwm->pinentry_term);
180 if (pwm->filename)
181 xfree(pwm->filename);
183 xfree(pwm);
186 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
188 membuf_t *mem = (membuf_t *)data;
189 void *p;
191 if (!buffer)
192 return 0;
194 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
195 return 1;
197 mem->buf = p;
198 memcpy((char *)mem->buf + mem->len, buffer, len);
199 mem->len += len;
200 return 0;
203 static int inquire_cb(void *data, const char *keyword)
205 struct inquire_s *inquire = (struct inquire_s *)data;
207 return assuan_send_data(inquire->ctx, inquire->buf, inquire->len);
210 void pwmd_free_result(void *data)
212 xfree(data);
215 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
216 char **result, const char *cmd)
218 membuf_t data;
219 gpg_error_t rc;
221 data.len = 0;
222 data.buf = NULL;
225 * This is needed because assuan only accepts 1000 byte command strings.
226 * If the line is more than this the command will fail. So we use an
227 * INQUIRE on the server which waits for an END that assuan_transact()
228 * fulfills.
230 * Other commands shouldn't need the INQUIRE. Let me know if you have an
231 * element path that's greater than 1000 bytes and I'll fix it.
233 if (strncasecmp(cmd, "STORE", 5) == 0) {
234 const char *p = cmd + 5;
235 struct inquire_s *inq = (struct inquire_s *)xmalloc(sizeof(struct inquire_s));
237 if (*p == ' ')
238 p++;
240 inq->ctx = ctx;
241 inq->buf = p;
242 inq->len = strlen(p);
243 rc = assuan_transact(ctx, "STORE", mem_realloc_cb, &data, inquire_cb,
244 inq, pwm->status_func, pwm->status_data);
245 xfree(inq);
247 else {
248 #ifdef USE_PINENTRY
250 * Ignore any status callback function for pinentry.
252 if (ctx == pwm->pctx)
253 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
254 else
255 #endif
256 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, NULL, NULL,
257 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_terminate_pinentry(pwm_t *pwm)
278 #ifndef USE_PINENTRY
279 return GPG_ERR_NOT_IMPLEMENTED;
280 #endif
282 if (!pwm || pwm->pid == -1)
283 return GPG_ERR_INV_ARG;
285 if (kill(pwm->pid, 0) == 0) {
286 if (kill(pwm->pid, SIGTERM) == -1) {
287 if (kill(pwm->pid, SIGKILL) == -1)
288 return gpg_error_from_errno(errno);
291 else
292 return gpg_error_from_errno(errno);
294 return 0;
297 #ifdef USE_PINENTRY
298 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
300 char *buf;
301 gpg_error_t error;
303 if (!pwm->title)
304 pwm->title = xstrdup("LibPWMD");
306 if (!pwm->prompt)
307 pwm->prompt = xstrdup("Password:");
309 if (!pwm->desc && !which)
310 pwm->desc = xstrdup("Enter a password.");
312 if (which == 1)
313 buf = xstrdup("SETERROR Invalid password, please try again.");
314 else if (which == 2)
315 buf = xstrdup("SETERROR Please type the password again for confirmation.");
316 else {
317 buf = (char *)xmalloc(strlen("SETERROR ") + strlen(pwm->desc) + 1);
318 sprintf(buf, "SETERROR %s", pwm->desc);
321 error = pinentry_command(pwm, NULL, buf);
322 xfree(buf);
324 if (error)
325 return error;
327 buf = (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm->prompt) + 1);
328 sprintf(buf, "SETPROMPT %s", pwm->prompt);
329 error = pinentry_command(pwm, NULL, buf);
330 xfree(buf);
332 if (error)
333 return error;
335 buf = (char *)xmalloc(strlen("SETDESC ") + strlen(pwm->title) + 1);
336 sprintf(buf, "SETDESC %s", pwm->title);
337 error = pinentry_command(pwm, NULL, buf);
338 xfree(buf);
339 return error;
342 static void update_pinentry_settings(pwm_t *pwm)
344 FILE *fp;
345 char buf[LINE_MAX];
346 struct passwd *pw = getpwuid(getuid());
347 char *p;
349 snprintf(buf, sizeof(buf), "%s/.pwmd/env", pw->pw_dir);
351 if ((fp = fopen(buf, "r")) == NULL)
352 return;
354 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
355 char name[32], val[256];
357 if (sscanf(p, "%[a-zA-Z]=%s", name, val) != 2)
358 continue;
360 if (strcasecmp(name, "TTY") == 0) {
361 if (!pwm->pinentry_tty) {
362 xfree(pwm->pinentry_tty);
363 pwm->pinentry_tty = xstrdup(val);
366 else if (strcasecmp(name, "TERM") == 0) {
367 if (!pwm->pinentry_term) {
368 xfree(pwm->pinentry_term);
369 pwm->pinentry_term = xstrdup(val);
372 else if (strcasecmp(name, "DISPLAY") == 0) {
373 if (!pwm->pinentry_display) {
374 xfree(pwm->pinentry_display);
375 pwm->pinentry_display = xstrdup(val);
380 fclose(fp);
383 static gpg_error_t launch_pinentry(pwm_t *pwm)
385 int rc;
386 assuan_context_t ctx;
387 int child_list[] = {-1};
388 char *display = getenv("DISPLAY");
389 const char *argv[6];
390 int have_display = 0;
391 char *tty = NULL;
393 update_pinentry_settings(pwm);
395 if (pwm->pinentry_display || display)
396 have_display = 1;
397 else {
398 tty = pwm->pinentry_tty ? pwm->pinentry_tty : ttyname(STDOUT_FILENO);
400 if (!tty)
401 return gpg_error_from_errno(errno);
404 if (!display && !tty)
405 return GPG_ERR_ENOTTY;
407 argv[0] = "pinentry";
408 argv[1] = have_display ? "--display" : "--ttyname";
409 argv[2] = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
410 argv[3] = NULL;
412 if (!have_display) {
413 argv[3] = "--ttytype";
414 argv[4] = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
415 argv[5] = NULL;
418 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
420 if (rc)
421 return rc;
423 pwm->pid = assuan_get_pid(ctx);
424 pwm->pctx = ctx;
425 return set_pinentry_strings(pwm, 0);
428 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
430 gpg_error_t n;
432 if (!pwm->pctx) {
433 n = launch_pinentry(pwm);
435 if (n)
436 return n;
439 return assuan_command(pwm, pwm->pctx, result, cmd);
442 static void pinentry_disconnect(pwm_t *pwm)
444 if (pwm->pctx)
445 assuan_disconnect(pwm->pctx);
447 pwm->pctx = NULL;
448 pwm->pid = -1;
452 * Only called from a child process.
454 static void catchsig(int sig)
456 switch (sig) {
457 case SIGALRM:
458 if (gelapsed++ >= gtimeout) {
459 pwmd_terminate_pinentry(gpwm);
460 break;
463 alarm(1);
464 break;
465 default:
466 break;
470 static char *percent_escape(const char *atext)
472 const unsigned char *s;
473 int len = strlen(atext) * 3 + 1;
474 char *buf = (char *)xmalloc(len), *p = buf;
476 if (!buf)
477 return NULL;
479 for (s=(const unsigned char *)atext; *s; s++) {
480 if (*s < ' ') {
481 sprintf (p, "%%%02X", *s);
482 p += 3;
484 else
485 *p++ = *s;
488 *p = 0;
489 return buf;
491 #endif
493 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
495 if (!cmd)
496 return GPG_ERR_INV_ARG;
498 return assuan_command(pwm, pwm->ctx, result, cmd);
502 * Avoid sending the BYE command here. libassuan will close the file
503 * descriptor and release the assuan context. Use pwmd_close() instead.
505 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
507 va_list ap;
508 char *buf;
509 size_t len;
510 gpg_error_t error;
512 if (!pwm || !cmd)
513 return GPG_ERR_INV_ARG;
515 *result = NULL;
516 va_start(ap, cmd);
518 * C99
520 len = vsnprintf(NULL, 0, cmd, ap);
521 buf = (char *)xmalloc(len + 1);
522 len = vsnprintf(buf, len + 1, cmd, ap);
523 va_end(ap);
524 error = send_command(pwm, result, buf);
525 xfree(buf);
526 return error;
529 #ifdef USE_PINENTRY
530 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
532 gpg_error_t error;
534 if (gtimeout) {
535 signal(SIGALRM, catchsig);
536 alarm(1);
539 *result = NULL;
540 error = pinentry_command(pwm, result, "GETPIN");
542 if (error == GPG_ERR_ASS_CANCELED)
543 return error;
545 if (!*result)
546 return EPWMD_KEY;
548 return 0;
551 static gpg_error_t getpin(pwm_t *pwm, char **result, int *try_n, int which)
553 int pin_try = *try_n;
554 gpg_error_t error;
556 getpin_again:
557 *try_n = pin_try;
559 if (pin_try == -1) {
560 error = set_pinentry_strings(pwm, which);
562 if (error) {
563 pinentry_disconnect(pwm);
564 return error;
567 else {
568 if (pwm->pinentry_tries-1 != pin_try) {
569 error = set_pinentry_strings(pwm, 1);
571 if (error) {
572 pinentry_disconnect(pwm);
573 return error;
578 error = do_getpin(pwm, result);
580 if (error) {
581 if (pin_try != -1 && pin_try-- && error == EPWMD_KEY)
582 goto getpin_again;
584 if (pwm->pctx)
585 pinentry_disconnect(pwm);
587 *try_n = pin_try;
588 return error;
591 return 0;
593 #endif
595 gpg_error_t pwmd_open_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
597 char *result;
598 gpg_error_t error;
600 #ifndef USE_PINENTRY
601 return GPG_ERR_NOT_IMPLEMENTED;
602 #endif
604 if (!pwm || !pw || !pw->filename[0])
605 return GPG_ERR_INV_ARG;
607 close(pw->fd);
609 if (pw->error) {
610 error = pw->error;
611 goto fail;
614 error = pwmd_command(pwm, &result, "ISCACHED %s", pw->filename);
616 if (error)
617 goto fail;
619 if (pwm->filename)
620 xfree(pwm->filename);
622 pwm->filename = xstrdup(pw->filename);
623 memset(pw, 0, sizeof(pwmd_nb_status_t));
624 return 0;
626 fail:
627 memset(pw, 0, sizeof(pwmd_nb_status_t));
628 return error;
631 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
633 char buf[1000];
634 gpg_error_t error;
636 snprintf(buf, sizeof(buf), "OPEN %s %s", filename, password ? password : "");
637 error = send_command(pwm, NULL, buf);
638 memset(&buf, 0, sizeof(buf));
639 xfree(password);
640 return error;
643 static int do_pwmd_open(pwm_t *pwm, gpg_error_t *error, const char *filename,
644 int nb, int timeout)
646 char *result = NULL;
647 char *password = NULL;
648 #ifdef USE_PINENTRY
649 int pin_try = pwm->pinentry_tries - 1;
650 #endif
652 if (!pwm || !filename || !*filename) {
653 *error = GPG_ERR_INV_ARG;
654 return nb ? -1 : 1;
658 * Avoid calling pinentry if the password is cached on the server.
660 *error = pwmd_command(pwm, &result, "ISCACHED %s", filename);
662 if (*error == EPWMD_CACHE_NOT_FOUND) {
663 if (pwm->passfunc) {
664 password = pwm->passfunc(pwm, pwm->passdata);
666 if (!password || !*password) {
667 *error = EPWMD_KEY;
668 return nb ? -1 : 1;
671 password = xstrdup(password);
672 goto gotpassword;
675 if (*error == EPWMD_CACHE_NOT_FOUND) {
676 #ifdef USE_PINENTRY
678 * Get the password from pinentry.
680 if (pwm->use_pinentry) {
682 * Nonblocking is wanted. fork() then return a file descriptor
683 * that the client can use to read() from.
685 if (nb) {
686 int p[2];
687 pid_t pid;
688 pwmd_nb_status_t pw;
690 if (pipe(p) == -1) {
691 *error = gpg_error_from_syserror();
692 return -1;
695 pid = fork();
697 switch (pid) {
698 case 0:
699 close(p[0]);
700 strncpy(pw.filename, filename, sizeof(pw.filename));
701 pw.fd = p[0];
703 if (timeout > 0) {
704 gpwm = pwm;
705 gtimeout = timeout;
706 gelapsed = 0;
709 getpin_nb_again:
710 *error = getpin(pwm, &password, &pin_try, 0);
712 if (*error) {
713 getpin_nb_fail:
714 if (pwm->pctx)
715 pinentry_disconnect(pwm);
717 if (gtimeout && gelapsed >= gtimeout)
718 *error = GPG_ERR_TIMEOUT;
720 pw.error = *error;
721 write(p[1], &pw, sizeof(pw));
722 close(p[1]);
723 _exit(1);
727 * Don't count the time it takes to open the file
728 * which may have many iterations.
730 signal(SIGALRM, SIG_DFL);
731 *error = do_open_command(pwm, filename, password);
733 if (timeout)
734 signal(SIGALRM, catchsig);
736 if (pwm->pctx && *error == EPWMD_BADKEY) {
737 if (pin_try-- > 0)
738 goto getpin_nb_again;
740 goto getpin_nb_fail;
743 pinentry_disconnect(pwm);
744 pw.error = 0;
745 write(p[1], &pw, sizeof(pw));
746 close(p[1]);
747 _exit(0);
748 break;
749 case -1:
750 *error = gpg_error_from_syserror();
751 close(p[0]);
752 close(p[1]);
753 return -1;
754 default:
755 break;
758 close(p[1]);
759 return p[0];
762 getpin_again:
763 *error = getpin(pwm, &password, &pin_try, 1);
765 if (*error) {
766 if (pwm->pctx)
767 pinentry_disconnect(pwm);
769 return 1;
772 else {
773 #endif
775 * Not using pinentry and the file was not found
776 * in the cache.
778 if (pwm->password == NULL) {
779 *error = EPWMD_KEY;
780 return 1;
783 password = pwm->password;
784 #ifdef USE_PINENTRY
786 #endif
789 else if (*error && *error != EPWMD_FILE_NOT_FOUND)
790 return 1;
792 gotpassword:
793 *error = do_open_command(pwm, filename, password);
795 #ifdef USE_PINENTRY
796 if (pwm->pctx && *error == EPWMD_BADKEY) {
797 if (pin_try-- > 0)
798 goto getpin_again;
800 pinentry_disconnect(pwm);
801 return 1;
803 #endif
805 if (!pwm->use_pinentry && pwm->password)
806 pwm->password = NULL;
808 if (!*error) {
809 if (pwm->filename)
810 xfree(pwm->filename);
812 pwm->filename = xstrdup(filename);
816 * The file is cached or the file is a new file.
818 if (nb)
819 return *error ? -1 : -2;
821 return *error ? 1 : 0;
824 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
826 gpg_error_t error;
828 do_pwmd_open(pwm, &error, filename, 0, 0);
829 return error;
832 int pwmd_open_nb(pwm_t *pwm, gpg_error_t *error, const char *filename,
833 int timeout)
835 #ifndef USE_PINENTRY
836 *error = GPG_ERR_NOT_IMPLEMENTED;
837 return -1;
838 #else
839 return do_pwmd_open(pwm, error, filename, 1, timeout);
840 #endif
843 #ifdef USE_PINENTRY
844 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
846 int confirm = 0;
847 gpg_error_t error;
848 char *result = NULL;
849 int pin_try = -1;
851 again:
852 error = getpin(pwm, &result, &pin_try, confirm ? 2 : 0);
854 if (error) {
855 if (pwm->pctx)
856 pinentry_disconnect(pwm);
858 if (*password)
859 xfree(*password);
861 return error;
864 if (!confirm++) {
865 *password = result;
866 goto again;
869 if (strcmp(*password, result)) {
870 xfree(*password);
871 xfree(result);
872 pinentry_disconnect(pwm);
873 error = EPWMD_BADKEY;
874 return error;
877 xfree(result);
878 pinentry_disconnect(pwm);
879 return 0;
881 #endif
883 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
885 char buf[ASSUAN_LINELENGTH];
886 gpg_error_t error;
888 snprintf(buf, sizeof(buf), "SAVE %s", password ? password : "");
889 error = send_command(pwm, NULL, buf);
890 memset(&buf, 0, sizeof(buf));
891 xfree(password);
892 pwm->password = NULL;
893 return error;
896 gpg_error_t pwmd_save_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
898 gpg_error_t error;
900 #ifndef USE_PINENTRY
901 return GPG_ERR_NOT_IMPLEMENTED;
902 #endif
904 if (!pwm || !pw || !pw->filename[0])
905 return GPG_ERR_INV_ARG;
907 close(pw->fd);
909 if (pw->error) {
910 error = pw->error;
911 memset(pw, 0, sizeof(pwmd_nb_status_t));
912 return error;
915 memset(pw, 0, sizeof(pwmd_nb_status_t));
916 return 0;
919 static int do_pwmd_save(pwm_t *pwm, gpg_error_t *error, int nb)
921 char *result = NULL;
922 char *password = NULL;
924 if (!pwm) {
925 *error = GPG_ERR_INV_ARG;
926 return 1;
929 if (pwm->use_pinentry || pwm->passfunc) {
930 *error = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
932 if (*error == EPWMD_CACHE_NOT_FOUND) {
933 #ifdef USE_PINENTRY
934 if (pwm->use_pinentry) {
935 if (nb) {
936 int p[2];
937 pid_t pid;
938 pwmd_nb_status_t pw;
940 if (pipe(p) == -1) {
941 *error = gpg_error_from_syserror();
942 return -1;
945 pid = fork();
947 switch (pid) {
948 case 0:
949 close(p[0]);
950 strncpy(pw.filename, pwm->filename, sizeof(pw.filename));
951 pw.fd = p[0];
953 do {
954 password = NULL;
955 *error = do_save_getpin(pwm, &password);
956 } while (*error == EPWMD_KEY || *error == EPWMD_BADKEY);
958 if (*error) {
959 if (pwm->pctx)
960 pinentry_disconnect(pwm);
962 pw.error = *error;
963 write(p[1], &pw, sizeof(pw));
964 close(p[1]);
965 _exit(1);
968 *error = do_save_command(pwm, password);
969 pinentry_disconnect(pwm);
970 pw.error = *error;
971 write(p[1], &pw, sizeof(pw));
972 close(p[1]);
973 _exit(0);
974 break;
975 case -1:
976 *error = gpg_error_from_syserror();
977 close(p[0]);
978 close(p[1]);
979 return -1;
980 default:
981 break;
984 close(p[1]);
985 return p[0];
988 *error = do_save_getpin(pwm, &password);
990 if (*error)
991 return 1;
993 else {
994 #endif
995 if (pwm->passfunc) {
996 char *tmp = (*pwm->passfunc)(pwm, pwm->passdata);
998 if (!tmp || !*tmp) {
999 *error = EPWMD_KEY;
1000 return 1;
1003 password = xstrdup(tmp);
1005 #ifdef USE_PINENTRY
1007 #endif
1009 if (!password || !*password) {
1010 *error = EPWMD_KEY;
1011 return 1;
1014 else {
1015 if (*error && *error != EPWMD_FILE_NOT_FOUND)
1016 return 1;
1019 else
1020 password = pwm->password;
1022 *error = do_save_command(pwm, password);
1024 if (nb)
1025 return *error ? -1 : -2;
1027 return *error ? 1 : 0;
1030 int pwmd_save_nb(pwm_t *pwm, gpg_error_t *error)
1032 #ifndef USE_PINENTRY
1033 *error = GPG_ERR_NOT_IMPLEMENTED;
1034 return -1;
1035 #else
1036 return do_pwmd_save(pwm, error, 1);
1037 #endif
1040 gpg_error_t pwmd_save(pwm_t *pwm)
1042 gpg_error_t error;
1044 do_pwmd_save(pwm, &error, 0);
1045 return error;
1048 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1050 va_list ap;
1051 #ifdef USE_PINENTRY
1052 int n = va_arg(ap, int);
1053 #endif
1054 char *arg1;
1056 if (!pwm)
1057 return GPG_ERR_INV_ARG;
1059 va_start(ap, opt);
1061 switch (opt) {
1062 case PWMD_OPTION_STATUS_FUNC:
1063 pwm->status_func = va_arg(ap, pwmd_status_fn);
1064 break;
1065 case PWMD_OPTION_STATUS_DATA:
1066 pwm->status_data = va_arg(ap, void *);
1067 break;
1068 case PWMD_OPTION_PASSWORD_FUNC:
1069 pwm->passfunc = va_arg(ap, pwmd_password_fn);
1070 break;
1071 case PWMD_OPTION_PASSWORD_DATA:
1072 pwm->passdata = va_arg(ap, void *);
1073 break;
1074 case PWMD_OPTION_PASSWORD:
1075 arg1 = va_arg(ap, char *);
1077 if (pwm->password)
1078 xfree(pwm->password);
1080 pwm->password = xstrdup(arg1);
1081 break;
1082 #ifdef USE_PINENTRY
1083 case PWMD_OPTION_PINENTRY:
1084 n = va_arg(ap, int);
1086 if (n != 0 && n != 1) {
1087 va_end(ap);
1088 return GPG_ERR_INV_VALUE;
1091 pwm->use_pinentry = n;
1092 break;
1093 case PWMD_OPTION_PINENTRY_TRIES:
1094 n = va_arg(ap, int);
1096 if (n <= 0) {
1097 va_end(ap);
1098 return GPG_ERR_INV_VALUE;
1101 pwm->pinentry_tries = n;
1102 break;
1103 case PWMD_OPTION_PINENTRY_PATH:
1104 if (pwm->pinentry_path)
1105 xfree(pwm->pinentry_path);
1107 pwm->pinentry_path = xstrdup(va_arg(ap, char *));
1108 break;
1109 case PWMD_OPTION_PINENTRY_TTY:
1110 if (pwm->pinentry_tty)
1111 xfree(pwm->pinentry_tty);
1113 pwm->pinentry_tty = xstrdup(va_arg(ap, char *));
1114 break;
1115 case PWMD_OPTION_PINENTRY_DISPLAY:
1116 if (pwm->pinentry_display)
1117 xfree(pwm->pinentry_display);
1119 pwm->pinentry_display = xstrdup(va_arg(ap, char *));
1120 break;
1121 case PWMD_OPTION_PINENTRY_TERM:
1122 if (pwm->pinentry_term)
1123 xfree(pwm->pinentry_term);
1125 pwm->pinentry_term = xstrdup(va_arg(ap, char *));
1126 break;
1127 case PWMD_OPTION_PINENTRY_TITLE:
1128 if (pwm->title)
1129 xfree(pwm->title);
1130 pwm->title = percent_escape(va_arg(ap, char *));
1131 break;
1132 case PWMD_OPTION_PINENTRY_PROMPT:
1133 if (pwm->prompt)
1134 xfree(pwm->prompt);
1135 pwm->prompt = percent_escape(va_arg(ap, char *));
1136 break;
1137 case PWMD_OPTION_PINENTRY_DESC:
1138 if (pwm->desc)
1139 xfree(pwm->desc);
1140 pwm->desc = percent_escape(va_arg(ap, char *));
1141 break;
1142 #else
1143 case PWMD_OPTION_PINENTRY:
1144 case PWMD_OPTION_PINENTRY_TRIES:
1145 case PWMD_OPTION_PINENTRY_PATH:
1146 case PWMD_OPTION_PINENTRY_TTY:
1147 case PWMD_OPTION_PINENTRY_DISPLAY:
1148 case PWMD_OPTION_PINENTRY_TERM:
1149 case PWMD_OPTION_PINENTRY_TITLE:
1150 case PWMD_OPTION_PINENTRY_PROMPT:
1151 case PWMD_OPTION_PINENTRY_DESC:
1152 return GPG_ERR_NOT_IMPLEMENTED;
1153 #endif
1154 default:
1155 va_end(ap);
1156 return GPG_ERR_NOT_IMPLEMENTED;
1159 va_end(ap);
1160 return 0;