Added PWMD_PINENTRY_CLOSE. This will terminate the pinentry process used
[libpwmd.git] / src / libpwmd.c
blob967b326afc03da2c3f7acc398643261678af666e
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2009 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 #include "mem.h"
49 #include "misc.h"
50 #include "types.h"
52 #ifdef WITH_PINENTRY
53 #include "pinentry.h"
54 #endif
56 #ifdef WITH_TCP
57 #include "ssh.h"
58 #endif
60 static gpg_error_t send_pinentry_options(pwm_t *pwm);
61 static gpg_error_t _pwmd_process(pwm_t *pwm);
62 static gpg_error_t set_pinentry_retry(pwm_t *pwm);
63 static gpg_error_t get_custom_passphrase(pwm_t *pwm, char **result);
65 static const char *_pwmd_strerror(gpg_error_t e)
67 gpg_err_code_t code = gpg_err_code(e);
69 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
70 switch (code) {
71 default:
72 return NULL;
73 case GPG_ERR_USER_1:
74 return N_("Unknown error");
75 case GPG_ERR_USER_2:
76 return N_("No cache slots available");
77 case GPG_ERR_USER_3:
78 return N_("Recursion loop");
79 case GPG_ERR_USER_4:
80 return N_("No file is open");
81 case GPG_ERR_USER_5:
82 return N_("General LibXML error");
83 case GPG_ERR_USER_6:
84 return N_("File modified");
88 return NULL;
91 const char *pwmd_strerror(gpg_error_t code)
93 const char *p = _pwmd_strerror(code);
95 return p ? p : gpg_strerror(code);
98 int pwmd_strerror_r(gpg_error_t code, char *buf, size_t size)
100 const char *p = _pwmd_strerror(code);
102 if (p) {
103 snprintf(buf, size, "%s", p);
105 if (strlen(p) > size)
106 return ERANGE;
108 return 0;
111 return gpg_strerror_r(code, buf, size);
114 gpg_error_t pwmd_init()
116 static int initialized;
118 if (initialized)
119 return 0;
121 #ifndef MEM_DEBUG
122 _xmem_init();
123 #endif
124 #ifdef ENABLE_NLS
125 bindtextdomain("libpwmd", LOCALEDIR);
126 #endif
127 gpg_err_init();
128 assuan_set_malloc_hooks(pwmd_malloc, pwmd_realloc, pwmd_free);
129 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
130 initialized = 1;
131 return 0;
134 gpg_error_t _connect_finalize(pwm_t *pwm)
136 int active[2];
137 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
138 gpg_error_t rc;
139 char *result;
141 if (n <= 0)
142 return GPG_ERR_EBADFD;
144 pwm->fd = active[0];
145 #ifdef WITH_PINENTRY
146 pwm->pid = -1;
147 #endif
148 assuan_set_pointer(pwm->ctx, pwm);
150 #ifdef WITH_TCP
151 // Until X11 forwarding is supported, disable the remote pwmd pinentry.
152 if (pwm->tcp_conn) {
153 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
155 if (rc)
156 return rc;
158 #endif
160 if (pwm->name) {
161 rc = pwmd_command(pwm, NULL, "OPTION CLIENT NAME=%s", pwm->name);
163 if (rc)
164 return rc;
167 rc = pwmd_command(pwm, &result, "VERSION");
169 if (rc && rc != GPG_ERR_ASS_UNKNOWN_CMD)
170 return rc;
172 if (rc)
173 pwm->version = PWMD_V1;
174 else
175 pwm->version = PWMD_V2;
177 pwmd_free(result);
178 return 0;
181 static gpg_error_t _pwmd_connect_url(pwm_t *pwm, const char *url, int async)
183 char *p = (char *)url;
184 gpg_error_t rc;
186 if (!pwm)
187 return GPG_ERR_INV_ARG;
189 if (!p || !strncmp(p, "socket://", 9)) {
190 if (p)
191 p += 9;
193 goto connect_uds;
195 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
196 !strncmp(p, "ssh4://", 7)) {
197 #ifndef WITH_TCP
198 return GPG_ERR_NOT_IMPLEMENTED;
199 #else
200 char *host = NULL;
201 int port = -1;
202 char *identity = NULL;
203 char *known_hosts = NULL;
204 char *username = NULL;
206 if (!strncmp(p, "ssh6://", 7)) {
207 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV6);
208 p += 7;
210 else if (!strncmp(p, "ssh4://", 7)) {
211 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV4);
212 p += 7;
214 else {
215 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IP_ANY);
216 p += 6;
219 if (rc)
220 return rc;
222 rc = _parse_ssh_url(p, &host, &port, &username, &identity,
223 &known_hosts);
225 if (rc)
226 goto fail;
228 if (async)
229 rc = pwmd_ssh_connect_async(pwm, host, port, identity, username,
230 known_hosts);
231 else
232 rc = pwmd_ssh_connect(pwm, host, port, identity, username,
233 known_hosts);
235 fail:
236 if (host)
237 pwmd_free(host);
239 if (username)
240 pwmd_free(username);
242 if (identity)
243 pwmd_free(identity);
245 if (known_hosts)
246 pwmd_free(known_hosts);
248 return rc;
249 #endif
252 connect_uds:
253 rc = pwmd_connect(pwm, p);
254 pwm->state = ASYNC_DONE;
255 return rc;
258 gpg_error_t pwmd_connect_url(pwm_t *pwm, const char *url)
260 return _pwmd_connect_url(pwm, url, 0);
263 gpg_error_t pwmd_connect_url_async(pwm_t *pwm, const char *url)
265 return _pwmd_connect_url(pwm, url, 1);
268 gpg_error_t pwmd_connect(pwm_t *pwm, const char *path)
270 char *socketpath = NULL;
271 assuan_context_t ctx;
272 struct passwd pw;
273 char *pwbuf;
274 gpg_error_t rc;
276 if (!pwm)
277 return GPG_ERR_INV_ARG;
279 pwbuf = _getpwuid(&pw);
281 if (!pwbuf)
282 return gpg_error_from_errno(errno);
284 if (!path || !*path)
285 socketpath = pwmd_strdup_printf("%s/.pwmd/socket", pw.pw_dir);
286 else
287 socketpath = _expand_homedir((char *)path, &pw);
289 pwmd_free(pwbuf);
291 if (!socketpath)
292 return gpg_error_from_errno(ENOMEM);
294 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
295 pwmd_free(socketpath);
297 if (rc)
298 return gpg_err_code(rc);
300 pwm->ctx = ctx;
301 return _connect_finalize(pwm);
304 static void disconnect(pwm_t *pwm)
306 if (!pwm || !pwm->ctx)
307 return;
309 assuan_disconnect(pwm->ctx);
310 pwm->ctx = NULL;
311 pwm->fd = -1;
314 void pwmd_close(pwm_t *pwm)
316 if (!pwm)
317 return;
319 disconnect(pwm);
321 if (pwm->password)
322 pwmd_free(pwm->password);
324 if (pwm->title)
325 pwmd_free(pwm->title);
327 if (pwm->desc)
328 pwmd_free(pwm->desc);
330 if (pwm->prompt)
331 pwmd_free(pwm->prompt);
333 if (pwm->pinentry_tty)
334 pwmd_free(pwm->pinentry_tty);
336 if (pwm->pinentry_display)
337 pwmd_free(pwm->pinentry_display);
339 if (pwm->pinentry_term)
340 pwmd_free(pwm->pinentry_term);
342 if (pwm->lcctype)
343 pwmd_free(pwm->lcctype);
345 if (pwm->lcmessages)
346 pwmd_free(pwm->lcmessages);
348 if (pwm->filename)
349 pwmd_free(pwm->filename);
351 if (pwm->name)
352 pwmd_free(pwm->name);
354 #ifdef WITH_TCP
355 if (pwm->tcp_conn)
356 _free_ssh_conn(pwm->tcp_conn);
357 #endif
359 #ifdef WITH_PINENTRY
360 if (pwm->pctx)
361 _pinentry_disconnect(pwm);
362 #endif
364 pwmd_free(pwm);
367 gpg_error_t pwmd_ssh_connect_async(pwm_t *pwm, const char *host, int port,
368 const char *identity, const char *user, const char *known_hosts)
370 #ifndef WITH_TCP
371 return GPG_ERR_NOT_IMPLEMENTED;
372 #else
373 return _do_pwmd_ssh_connect_async(pwm, host, port, identity, user,
374 known_hosts, ASYNC_CMD_CONNECT);
375 #endif
378 gpg_error_t pwmd_ssh_connect(pwm_t *pwm, const char *host, int port,
379 const char *identity, const char *user, const char *known_hosts)
381 #ifndef WITH_TCP
382 return GPG_ERR_NOT_IMPLEMENTED;
383 #else
384 return _do_pwmd_ssh_connect(pwm, host, port, identity, user, known_hosts, 0);
385 #endif
388 gpg_error_t pwmd_get_hostkey(pwm_t *pwm, const char *host, int port,
389 char **result)
391 #ifndef WITH_TCP
392 return GPG_ERR_NOT_IMPLEMENTED;
393 #else
394 char *hostkey;
395 gpg_error_t rc;
397 rc = _do_pwmd_ssh_connect(pwm, host, port, NULL, NULL, NULL, 1);
399 if (rc)
400 return rc;
402 hostkey = pwmd_strdup(pwm->tcp_conn->hostkey);
404 if (!hostkey)
405 rc = gpg_error_from_errno(ENOMEM);
407 *result = hostkey;
408 return rc;
409 #endif
412 gpg_error_t pwmd_get_hostkey_async(pwm_t *pwm, const char *host, int port)
414 #ifndef WITH_TCP
415 return GPG_ERR_NOT_IMPLEMENTED;
416 #else
417 return _do_pwmd_ssh_connect_async(pwm, host, port, NULL, NULL, NULL,
418 ASYNC_CMD_HOSTKEY);
419 #endif
422 static int inquire_realloc_cb(void *data, const void *buffer, size_t len)
424 membuf_t *mem = (membuf_t *)data;
425 void *p;
427 if (!buffer)
428 return 0;
430 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
431 return gpg_error_from_errno(ENOMEM);
433 mem->buf = p;
434 memcpy((char *)mem->buf + mem->len, buffer, len);
435 mem->len += len;
436 return 0;
439 static int inquire_cb(void *data, const char *keyword)
441 pwm_t *pwm = (pwm_t *)data;
442 gpg_error_t rc = 0;
443 int flags = fcntl(pwm->fd, F_GETFL);
445 /* Shouldn't get this far without a callback. */
446 if (!pwm->inquire_func)
447 return GPG_ERR_INV_ARG;
449 for (;;) {
450 char *result = NULL;
451 size_t len;
452 gpg_error_t arc;
454 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
455 rc = gpg_err_code(rc);
457 if (rc == GPG_ERR_EOF || !rc) {
458 if (len <= 0 || !result) {
459 rc = 0;
460 break;
463 arc = assuan_send_data(pwm->ctx, result, len);
464 arc = gpg_err_code(arc);
466 if (rc == GPG_ERR_EOF) {
467 rc = arc;
468 break;
471 rc = arc;
473 else if (rc)
474 break;
476 if (!rc) {
477 /* Set to non-blocking so _pwmd_process() can return. */
478 fcntl(pwm->fd, F_SETFL, O_NONBLOCK);
479 rc = _pwmd_process(pwm);
480 fcntl(pwm->fd, F_SETFL, flags);
484 fcntl(pwm->fd, F_SETFL, flags);
485 return gpg_err_code(rc);
488 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, ...)
490 char *buf;
491 gpg_error_t rc;
492 va_list ap;
493 int len;
495 if (pwm->state == ASYNC_DONE)
496 pwm->state = ASYNC_INIT;
498 if (pwm->state != ASYNC_INIT)
499 return GPG_ERR_INV_STATE;
501 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
503 if (!buf)
504 return gpg_error_from_errno(ENOMEM);
506 va_start(ap, cmd);
507 len = vsnprintf(buf, ASSUAN_LINELENGTH+1, cmd, ap);
508 va_end(ap);
510 if (len >= ASSUAN_LINELENGTH+1) {
511 pwmd_free(buf);
512 return GPG_ERR_LINE_TOO_LONG;
515 rc = assuan_write_line(pwm->ctx, buf);
516 pwmd_free(buf);
518 if (!rc)
519 pwm->state = ASYNC_PROCESS;
521 return gpg_err_code(rc);
524 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
526 char *p = NULL;
527 const char *f = NULL;
528 gpg_error_t rc;
530 if (!pwm || !filename)
531 return GPG_ERR_INV_ARG;
533 if (!pwm->ctx)
534 return GPG_ERR_INV_STATE;
536 if (pwm->cmd != ASYNC_CMD_NONE)
537 return GPG_ERR_ASS_NESTED_COMMANDS;
539 if (pwm->lastcmd == ASYNC_CMD_NONE) {
540 pwm->pin_try = 0;
542 if (pwm->filename)
543 pwmd_free(pwm->filename);
545 pwm->filename = pwmd_strdup(filename);
547 if (!pwm->filename)
548 return gpg_error_from_errno(ENOMEM);
550 gpg_error_t rc = send_pinentry_options(pwm);
552 if (rc)
553 return rc;
555 rc = get_custom_passphrase(pwm, &p);
557 if (rc && rc != GPG_ERR_NO_DATA)
558 return rc;
560 f = filename;
562 #ifdef WITH_PINENTRY
563 else if (pwm->lastcmd == ASYNC_CMD_OPEN2) {
564 p = pwm->_password;
565 f = pwm->filename;
567 #endif
568 else if (pwm->lastcmd == ASYNC_CMD_OPEN) {
569 rc = set_pinentry_retry(pwm);
571 if (rc)
572 return rc;
574 p = pwm->password;
575 f = filename;
577 else
578 return GPG_ERR_INV_STATE;
580 pwm->cmd = ASYNC_CMD_OPEN;
581 return do_nb_command(pwm, "OPEN %s %s", f, p ? p : "");
584 gpg_error_t pwmd_save_async(pwm_t *pwm)
586 char *p = NULL;
588 if (!pwm)
589 return GPG_ERR_INV_ARG;
591 if (!pwm->ctx)
592 return GPG_ERR_INV_STATE;
594 if (pwm->cmd != ASYNC_CMD_NONE)
595 return GPG_ERR_ASS_NESTED_COMMANDS;
597 if (pwm->lastcmd != ASYNC_CMD_SAVE2) {
598 gpg_error_t rc = send_pinentry_options(pwm);
600 if (rc)
601 return rc;
603 rc = get_custom_passphrase(pwm, &p);
605 if (rc && rc != GPG_ERR_NO_DATA)
606 return rc;
608 #ifdef WITH_PINENTRY
609 else
610 p = pwm->_password;
611 #endif
613 pwm->cmd = ASYNC_CMD_SAVE;
614 return do_nb_command(pwm, "SAVE %s", p ? p : "");
617 static gpg_error_t parse_assuan_line(pwm_t *pwm)
619 gpg_error_t rc;
620 char *line;
621 size_t len;
623 rc = assuan_read_line(pwm->ctx, &line, &len);
625 if (!rc) {
626 if (line[0] == 'O' && line[1] == 'K' &&
627 (line[2] == 0 || line[2] == ' ')) {
628 pwm->state = ASYNC_DONE;
630 else if (line[0] == '#') {
632 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
633 if (pwm->status_func) {
634 rc = pwm->status_func(pwm->status_data,
635 line[1] == 0 ? line+1 : line+2);
638 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
639 (line[3] == 0 || line[3] == ' ')) {
640 line += 4;
641 rc = atoi(line);
642 pwm->state = ASYNC_DONE;
646 return gpg_err_code(rc);
649 gpg_error_t pwmd_pending_line(pwm_t *pwm)
651 if (!pwm)
652 return GPG_ERR_INV_ARG;
654 if (!pwm->ctx)
655 return GPG_ERR_INV_STATE;
657 return assuan_pending_line(pwm->ctx) ? 0 : GPG_ERR_NO_DATA;
660 static pwmd_async_t reset_async(pwm_t *pwm, int done)
662 pwm->state = ASYNC_INIT;
663 pwm->cmd = pwm->lastcmd = ASYNC_CMD_NONE;
665 #ifdef WITH_PINENTRY
666 if (pwm->nb_fd != -1) {
667 close(pwm->nb_fd);
668 pwm->nb_fd = -1;
671 if (pwm->_password) {
672 pwmd_free(pwm->_password);
673 pwm->_password = NULL;
675 #endif
676 #ifdef WITH_TCP
677 if (done && pwm->tcp_conn) {
678 _free_ssh_conn(pwm->tcp_conn);
679 pwm->tcp_conn = NULL;
681 #endif
683 return ASYNC_DONE;
687 * Used for processing status messages when not in an async command and for
688 * waiting for the result from pwmd_open_async() and pwmd_save_async().
690 static gpg_error_t _pwmd_process(pwm_t *pwm)
692 gpg_error_t rc = 0;
693 fd_set fds;
694 struct timeval tv = {0, 0};
695 int n;
697 FD_ZERO(&fds);
698 FD_SET(pwm->fd, &fds);
699 #ifdef WITH_LIBPTH
700 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
701 #else
702 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
703 #endif
705 if (n == -1)
706 return gpg_error_from_syserror();
708 if (n > 0) {
709 if (FD_ISSET(pwm->fd, &fds))
710 rc = parse_assuan_line(pwm);
713 while (!rc && assuan_pending_line(pwm->ctx))
714 rc = parse_assuan_line(pwm);
716 return gpg_err_code(rc);
719 static void reset_handle(pwm_t *h)
721 h->fd = -1;
722 #ifdef WITH_PINENTRY
723 if (h->pctx)
724 _pinentry_disconnect(h);
726 h->nb_fd = -1;
727 #endif
728 h->pin_try = 0;
729 reset_async(h, 0);
732 gpg_error_t pwmd_disconnect(pwm_t *pwm)
734 if (!pwm)
735 return GPG_ERR_INV_ARG;
737 #ifdef WITH_TCP
738 if (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1)
739 #else
740 if (pwm->fd == -1)
741 #endif
742 return GPG_ERR_INV_STATE;
744 if (pwm->fd != 1)
745 disconnect(pwm);
746 #ifdef WITH_TCP
747 else
748 _ssh_disconnect(pwm);
749 #endif
751 reset_handle(pwm);
752 return 0;
755 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
757 #if defined(WITH_PINENTRY) || defined(WITH_TCP)
758 fd_set fds;
759 int n;
760 struct timeval tv = {0, 0};
761 #endif
763 if (result)
764 *result = NULL;
766 if (!rc)
767 return GPG_ERR_INV_ARG;
769 *rc = 0;
771 if (!pwm) {
772 *rc = GPG_ERR_INV_ARG;
773 return ASYNC_DONE;
775 else if (!pwm->ctx) {
776 switch (pwm->cmd) {
777 default:
778 *rc = GPG_ERR_INV_STATE;
779 return ASYNC_DONE;
780 #ifdef WITH_TCP
781 case ASYNC_CMD_DNS:
782 case ASYNC_CMD_CONNECT:
783 case ASYNC_CMD_HOSTKEY:
784 break;
785 #endif
789 /* When not in a command, this will let libassuan process status messages
790 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
791 * descriptor returned by pwmd_get_fd() to determine when this should be
792 * called or call pwmd_pending_line() to determine whether a buffered line
793 * needs to be processed. */
794 if (pwm->cmd == ASYNC_CMD_NONE) {
795 *rc = _pwmd_process(pwm);
796 return ASYNC_DONE;
799 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
800 if (pwm->state == ASYNC_DONE) {
801 *rc = _pwmd_process(pwm);
802 return reset_async(pwm, 0);
805 if (pwm->state != ASYNC_PROCESS) {
806 *rc = GPG_ERR_INV_STATE;
807 return ASYNC_DONE;
810 #ifdef WITH_TCP
811 if (pwm->cmd == ASYNC_CMD_DNS) {
812 fd_set rfds, wfds;
814 if (pwm->tcp_conn->rc) {
815 *rc = pwm->tcp_conn->rc;
816 return reset_async(pwm, 1);
819 FD_ZERO(&rfds);
820 FD_ZERO(&wfds);
821 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
823 /* Shouldn't happen. */
824 if (!n)
825 return pwm->state;
827 #ifdef WITH_LIBPTH
828 n = pth_select(n, &rfds, &wfds, NULL, &tv);
829 #else
830 n = select(n, &rfds, &wfds, NULL, &tv);
831 #endif
833 if (n < 0) {
834 *rc = gpg_error_from_syserror();
835 return reset_async(pwm, 1);
838 if (n > 0)
839 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
841 return pwm->state;
843 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
844 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
845 int ret;
846 socklen_t len = sizeof(int);
848 FD_ZERO(&fds);
849 FD_SET(pwm->tcp_conn->fd, &fds);
850 #ifdef WITH_LIBPTH
851 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
852 #else
853 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
854 #endif
856 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
857 return pwm->state;
858 else if (n == -1) {
859 *rc = gpg_error_from_syserror();
860 return reset_async(pwm, 1);
863 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
865 if (ret || n) {
866 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
867 return reset_async(pwm, 1);
870 else if (pwm->tcp_conn->rc) {
871 *rc = pwm->tcp_conn->rc;
872 return reset_async(pwm, 1);
875 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
876 *rc = _setup_ssh_session(pwm);
878 if (!*rc) {
879 switch (pwm->tcp_conn->cmd) {
880 case ASYNC_CMD_HOSTKEY:
881 *result = pwm->result;
882 break;
883 default:
884 break;
888 return reset_async(pwm, *rc ? 1 : 0);
890 #endif
892 #ifdef WITH_PINENTRY
893 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
894 int status;
896 if (pwm->nb_fd == -1) {
897 *rc = GPG_ERR_INV_STATE;
898 return reset_async(pwm, 0);
901 FD_ZERO(&fds);
902 FD_SET(pwm->nb_fd, &fds);
903 FD_SET(pwm->fd, &fds);
904 #ifdef WITH_LIBPTH
905 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
906 #else
907 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
908 #endif
909 if (n < 0) {
910 *rc = gpg_error_from_syserror();
911 return reset_async(pwm, 0);
914 if (n > 0 && FD_ISSET(pwm->nb_fd, &fds)) {
915 pwmd_nb_status_t nb;
916 #ifdef WITH_LIBPTH
917 size_t len = pth_read(pwm->nb_fd, &nb, sizeof(nb));
918 #else
919 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
920 #endif
921 waitpid(pwm->nb_pid, &status, WNOHANG);
923 if (len != sizeof(nb)) {
924 memset(&nb, 0, sizeof(pwmd_nb_status_t));
925 *rc = gpg_error_from_syserror();
926 return reset_async(pwm, 0);
929 *rc = nb.error;
931 if (*rc)
932 return reset_async(pwm, 0);
934 /* Since the non-blocking pinentry returned a success, do a
935 * non-blocking OPEN or SAVE. */
936 pwmd_async_cmd_t c = pwm->cmd;
937 reset_async(pwm, 0);
938 pwm->_password = pwmd_strdup(nb.password);
939 memset(&nb, 0, sizeof(pwmd_nb_status_t));
940 pwm->lastcmd = c;
942 if (!pwm->_password) {
943 *rc = gpg_error_from_errno(ENOMEM);
944 return reset_async(pwm, 0);
947 if (c == ASYNC_CMD_SAVE2)
948 *rc = pwmd_save_async(pwm);
949 else
950 *rc = pwmd_open_async(pwm, pwm->filename);
952 if (*rc) {
953 reset_async(pwm, 0);
954 return ASYNC_DONE;
957 return ASYNC_PROCESS;
960 /* Fall through so status messages can be processed during the
961 * pinentry. */
963 #endif
965 if (pwm->fd < 0) {
966 *rc = GPG_ERR_INV_STATE;
967 return reset_async(pwm, 0);
970 /* This is for pwmd_open_async() and pwmd_save_async(). For pinentry
971 * retries. */
972 *rc = _pwmd_process(pwm);
974 if (*rc && *rc != GPG_ERR_INV_PASSPHRASE)
975 return reset_async(pwm, 0);
977 if (pwm->cmd == ASYNC_CMD_OPEN &&
978 *rc == GPG_ERR_INV_PASSPHRASE &&
979 #ifdef WITH_TCP
980 (!pwm->tcp_conn || (pwm->tcp_conn && pwm->lastcmd == ASYNC_CMD_OPEN2)) &&
981 #endif
982 ++pwm->pin_try < pwm->pinentry_tries) {
983 if (!get_custom_passphrase(pwm, NULL))
984 goto done;
986 #ifdef WITH_PINENTRY
987 if (pwm->_password) {
988 reset_async(pwm, 0);
989 pwm->lastcmd = ASYNC_CMD_OPEN2;
990 *rc = pwmd_open_async2(pwm, pwm->filename);
992 else {
993 #endif
994 reset_async(pwm, 0);
995 pwm->lastcmd = ASYNC_CMD_OPEN;
996 *rc = pwmd_open_async(pwm, pwm->filename);
997 #ifdef WITH_PINENTRY
999 #endif
1002 done:
1003 if (*rc || pwm->state == ASYNC_DONE)
1004 return reset_async(pwm, 0);
1006 return pwm->state;
1009 gpg_error_t _assuan_command(pwm_t *pwm, assuan_context_t ctx,
1010 char **result, const char *cmd)
1012 membuf_t data;
1013 gpg_error_t rc;
1015 if (!cmd || !*cmd)
1016 return GPG_ERR_INV_ARG;
1018 if (strlen(cmd) >= ASSUAN_LINELENGTH+1)
1019 return GPG_ERR_LINE_TOO_LONG;
1021 data.len = 0;
1022 data.buf = NULL;
1023 rc = assuan_transact(ctx, cmd, inquire_realloc_cb, &data,
1024 #ifdef WITH_QUALITY
1025 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
1026 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
1027 #else
1028 inquire_cb, pwm,
1029 #endif
1030 pwm->status_func, pwm->status_data);
1032 if (rc) {
1033 if (data.buf) {
1034 pwmd_free(data.buf);
1035 data.buf = NULL;
1038 else {
1039 if (data.buf) {
1040 inquire_realloc_cb(&data, "", 1);
1042 if (!result) {
1043 pwmd_free(data.buf);
1044 rc = GPG_ERR_INV_ARG;
1046 else
1047 *result = (char *)data.buf;
1051 return gpg_err_code(rc);
1054 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_cb_t fn,
1055 void *data)
1057 if (!pwm || !cmd || !fn)
1058 return GPG_ERR_INV_ARG;
1060 if (!pwm->ctx)
1061 return GPG_ERR_INV_STATE;
1063 pwm->inquire_func = fn;
1064 pwm->inquire_data = data;
1065 return _assuan_command(pwm, pwm->ctx, NULL, cmd);
1068 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1069 va_list ap)
1071 char *buf;
1072 size_t len;
1073 va_list ap2;
1075 if (!pwm || !cmd)
1076 return GPG_ERR_INV_ARG;
1078 if (!pwm->ctx)
1079 return GPG_ERR_INV_STATE;
1082 * C99 allows the dst pointer to be null which will calculate the length
1083 * of the would-be result and return it.
1085 va_copy(ap2, ap);
1086 len = vsnprintf(NULL, 0, cmd, ap)+1;
1087 buf = (char *)pwmd_malloc(len);
1089 if (!buf) {
1090 va_end(ap2);
1091 return gpg_error_from_errno(ENOMEM);
1094 len = vsnprintf(buf, len, cmd, ap2);
1095 va_end(ap2);
1097 if (buf[strlen(buf)-1] == '\n')
1098 buf[strlen(buf)-1] = 0;
1100 if (buf[strlen(buf)-1] == '\r')
1101 buf[strlen(buf)-1] = 0;
1103 gpg_error_t rc = _assuan_command(pwm, pwm->ctx, result, buf);
1104 pwmd_free(buf);
1105 return rc;
1108 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1110 va_list ap;
1112 if (!pwm || !cmd)
1113 return GPG_ERR_INV_ARG;
1115 if (!pwm->ctx)
1116 return GPG_ERR_INV_STATE;
1118 if (result)
1119 *result = NULL;
1121 va_start(ap, cmd);
1122 gpg_error_t rc = pwmd_command_ap(pwm, result, cmd, ap);
1123 va_end(ap);
1124 return rc;
1127 static gpg_error_t send_pinentry_options(pwm_t *pwm)
1129 gpg_error_t rc;
1131 if (pwm->pinentry_path) {
1132 rc = pwmd_command(pwm, NULL, "OPTION PATH=%s", pwm->pinentry_path);
1134 if (rc)
1135 return rc;
1138 if (pwm->pinentry_tty) {
1139 rc = pwmd_command(pwm, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
1141 if (rc)
1142 return rc;
1145 if (pwm->pinentry_term) {
1146 rc = pwmd_command(pwm, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
1148 if (rc)
1149 return rc;
1152 if (pwm->pinentry_display) {
1153 rc = pwmd_command(pwm, NULL, "OPTION DISPLAY=%s",
1154 pwm->pinentry_display);
1156 if (rc)
1157 return rc;
1160 if (pwm->title) {
1161 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->title);
1163 if (rc)
1164 return rc;
1167 if (pwm->desc) {
1168 rc = pwmd_command(pwm, NULL, "OPTION DESC=%s", pwm->desc);
1170 if (rc)
1171 return rc;
1174 if (pwm->prompt) {
1175 rc = pwmd_command(pwm, NULL, "OPTION PROMPT=%s", pwm->prompt);
1177 if (rc)
1178 return rc;
1181 if (pwm->lcctype) {
1182 rc = pwmd_command(pwm, NULL, "OPTION LC_CTYPE=%s", pwm->lcctype);
1184 if (rc)
1185 return rc;
1188 if (pwm->lcmessages) {
1189 rc = pwmd_command(pwm, NULL, "OPTION LC_MESSAGES=%s", pwm->lcmessages);
1191 if (rc)
1192 return rc;
1195 if (pwm->pinentry_timeout >= 0 && !pwm->pin_try) {
1196 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=%i", pwm->pinentry_timeout);
1198 if (rc)
1199 return rc;
1202 return 0;
1205 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
1207 if (!pwm || !result)
1208 return GPG_ERR_INV_ARG;
1210 #ifdef WITH_TCP
1211 if (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1)
1212 #else
1213 if (pwm->fd == -1)
1214 #endif
1215 return GPG_ERR_INV_STATE;
1217 #ifdef WITH_TCP
1218 *result = pwm->tcp_conn ? PWMD_SOCKET_SSH : PWMD_SOCKET_UDS;
1219 #else
1220 *result = PWMD_SOCKET_UDS;
1221 #endif
1222 return 0;
1225 static gpg_error_t set_pinentry_retry(pwm_t *pwm)
1227 gpg_error_t rc = 0;
1229 if (pwm->pin_try == 1) {
1230 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s",
1231 N_("Invalid passphrase, please try again."));
1233 if (rc)
1234 return rc;
1236 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=0");
1239 return rc;
1242 static gpg_error_t get_custom_passphrase(pwm_t *pwm, char **result)
1244 gpg_error_t rc = GPG_ERR_NO_DATA;
1246 if (result)
1247 *result = NULL;
1248 else {
1249 if (pwm->password || pwm->passfunc)
1250 return 0;
1253 if (pwm->password) {
1254 rc = 0;
1255 *result = pwm->password;
1257 else if (pwm->passfunc)
1258 rc = pwm->passfunc(pwm->passdata, result);
1260 return rc;
1263 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb,
1264 int local_pinentry)
1266 char *result = NULL;
1267 char *password = NULL;
1268 char *path;
1269 gpg_error_t rc;
1271 if (pwm->lastcmd != ASYNC_CMD_OPEN2)
1272 pwm->pin_try = 0;
1274 if (!pwm || !filename || !*filename)
1275 return GPG_ERR_INV_ARG;
1277 if (!pwm->ctx)
1278 return GPG_ERR_INV_STATE;
1281 * Avoid calling pinentry if the password is cached on the server or if
1282 * this is a new file. pwmd version 2 adds a VERSION command which is
1283 * determined in _connect_finalize(). If the server is version 2,
1284 * ISCACHED will return GPG_ERR_ENOENT if it doesn't exist.
1286 #ifdef WITH_TCP
1287 /* Don't try a local filesystem lookup of the data file over a remote
1288 * connection. */
1289 if (!pwm->tcp_conn && pwm->version == PWMD_V1) {
1290 #else
1291 if (pwm->version == PWMD_V1) {
1292 #endif
1293 rc = pwmd_command(pwm, &result, "GETCONFIG data_directory");
1295 if (rc)
1296 return rc;
1298 path = pwmd_strdup_printf("%s/%s", result, filename);
1299 pwmd_free(result);
1301 if (!path)
1302 return gpg_error_from_errno(ENOMEM);
1304 if (access(path, R_OK) == -1) {
1305 if (errno == ENOENT) {
1306 pwmd_free(path);
1307 goto gotpassword;
1311 pwmd_free(path);
1314 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
1316 /* pwmd >= 2.0 specific. This is a new file which doesn't require a
1317 * passphrase. */
1318 if (rc == GPG_ERR_ENOENT)
1319 goto gotpassword;
1321 if (rc && rc != GPG_ERR_NOT_FOUND)
1322 return rc;
1324 if (rc == GPG_ERR_NOT_FOUND) {
1325 rc = get_custom_passphrase(pwm, &password);
1327 if (rc && rc != GPG_ERR_NO_DATA)
1328 return rc;
1329 else if (rc == GPG_ERR_NO_DATA)
1330 rc = GPG_ERR_NOT_FOUND;
1331 else
1332 goto gotpassword;
1335 #ifdef WITH_PINENTRY
1336 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
1337 /* Prevent pwmd from using it's pinentry if the passphrase fails. */
1338 if (!pwm->pin_try) {
1339 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
1341 if (rc)
1342 return rc;
1345 rc = _pinentry_open(pwm, filename, &password, nb);
1347 /* pwmd_process() should be called if using a non-blocking local
1348 * pinentry. */
1349 if (rc || (!rc && nb))
1350 return rc;
1352 #endif
1354 gotpassword:
1355 reset_async(pwm, 0);
1357 #ifdef WITH_TCP
1358 if (!local_pinentry && !pwm->tcp_conn && !pwm->pin_try) {
1359 #else
1360 if (!local_pinentry && !pwm->pin_try) {
1361 #endif
1362 rc = send_pinentry_options(pwm);
1364 if (rc)
1365 return rc;
1368 rc = pwmd_command(pwm, NULL, "OPEN %s %s", filename,
1369 password ? password : "");
1372 * Keep the user defined password set with pwmd_setopt(). The password may
1373 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1375 if (!pwm->passfunc && password && password != pwm->password)
1376 pwmd_free(password);
1378 if (rc == GPG_ERR_INV_PASSPHRASE) {
1379 if (++pwm->pin_try < pwm->pinentry_tries) {
1380 if (!get_custom_passphrase(pwm, NULL))
1381 goto done;
1383 #ifdef WITH_PINENTRY
1384 #ifdef WITH_TCP
1385 if (pwm->tcp_conn && !local_pinentry)
1386 return rc;
1387 else if (local_pinentry)
1388 rc = _getpin(pwm, &password, PWMD_PINENTRY_OPEN_FAILED);
1389 else
1390 #else
1391 if (local_pinentry)
1392 rc = _getpin(pwm, &password, PWMD_PINENTRY_OPEN_FAILED);
1393 else
1394 #endif
1395 #else
1396 #ifdef WITH_TCP
1397 if (pwm->tcp_conn)
1398 return rc;
1399 else
1400 #endif
1401 #endif
1402 rc = set_pinentry_retry(pwm);
1404 if (rc)
1405 return rc;
1407 goto gotpassword;
1409 #ifdef WITH_PINENTRY
1410 else if (local_pinentry)
1411 _pinentry_disconnect(pwm);
1412 #endif
1414 done:
1415 return rc;
1417 #ifdef WITH_PINENTRY
1418 else if (rc && local_pinentry)
1419 _pinentry_disconnect(pwm);
1420 #endif
1422 if (!rc) {
1423 if (pwm->filename)
1424 pwmd_free(pwm->filename);
1426 pwm->filename = pwmd_strdup(filename);
1429 return rc;
1432 gpg_error_t pwmd_open2(pwm_t *pwm, const char *filename)
1434 #ifndef WITH_PINENTRY
1435 return GPG_ERR_NOT_IMPLEMENTED;
1436 #else
1437 return do_pwmd_open(pwm, filename, 0, 1);
1438 #endif
1441 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
1443 return do_pwmd_open(pwm, filename, 0, 0);
1446 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
1448 #ifndef WITH_PINENTRY
1449 return GPG_ERR_NOT_IMPLEMENTED;
1450 #else
1451 if (!pwm || !filename)
1452 return GPG_ERR_INV_ARG;
1454 if (!pwm->ctx)
1455 return GPG_ERR_INV_STATE;
1457 if (pwm->cmd != ASYNC_CMD_NONE)
1458 return GPG_ERR_ASS_NESTED_COMMANDS;
1460 /* Initialize a new command since this is not a pinentry retry. */
1461 if (pwm->lastcmd != ASYNC_CMD_OPEN2)
1462 pwm->pin_try = 0;
1464 pwm->cmd = ASYNC_CMD_OPEN2;
1465 pwm->state = ASYNC_PROCESS;
1466 gpg_error_t rc = do_pwmd_open(pwm, filename, 1, 1);
1468 if (rc)
1469 reset_async(pwm, 0);
1471 return rc;
1472 #endif
1475 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb, int local_pinentry)
1477 char *result = NULL;
1478 char *password = NULL;
1479 gpg_error_t rc;
1481 if (!pwm)
1482 return GPG_ERR_INV_ARG;
1484 if (!pwm->ctx)
1485 return GPG_ERR_INV_STATE;
1487 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
1489 if (rc == GPG_ERR_ENOENT)
1490 rc = GPG_ERR_NOT_FOUND;
1492 if (rc && rc != GPG_ERR_NOT_FOUND)
1493 return rc;
1495 if (rc == GPG_ERR_NOT_FOUND) {
1496 rc = get_custom_passphrase(pwm, &password);
1498 if (rc && rc != GPG_ERR_NO_DATA)
1499 return rc;
1500 else if (rc == GPG_ERR_NO_DATA)
1501 rc = GPG_ERR_NOT_FOUND;
1502 else
1503 goto gotpassword;
1506 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
1507 #ifdef WITH_PINENTRY
1508 /* Get the password using the LOCAL pinentry. */
1509 if (nb) {
1510 int p[2];
1511 pid_t pid;
1512 pwmd_nb_status_t pw;
1514 if (pipe(p) == -1)
1515 return gpg_error_from_syserror();
1517 #ifdef WITH_LIBPTH
1518 pid = pth_fork();
1519 #else
1520 pid = fork();
1521 #endif
1523 switch (pid) {
1524 case 0:
1525 close(p[0]);
1526 pw.fd = p[0];
1527 password = NULL;
1528 pw.error = _do_save_getpin(pwm, &password);
1530 if (!pw.error) {
1531 snprintf(pw.password, sizeof(pw.password), "%s",
1532 password);
1533 pwmd_free(password);
1535 #ifdef WITH_LIBPTH
1536 pth_write(p[1], &pw, sizeof(pw));
1537 #else
1538 write(p[1], &pw, sizeof(pw));
1539 #endif
1540 memset(&pw, 0, sizeof(pw));
1541 close(p[1]);
1542 _exit(0);
1543 break;
1544 case -1:
1545 rc = gpg_error_from_syserror();
1546 close(p[0]);
1547 close(p[1]);
1548 return rc;
1549 default:
1550 break;
1553 close(p[1]);
1554 pwm->nb_fd = p[0];
1555 pwm->nb_pid = pid;
1556 return 0;
1559 rc = _do_save_getpin(pwm, &password);
1561 if (rc)
1562 return rc;
1563 #endif
1565 else
1566 pwm->state = ASYNC_DONE;
1568 gotpassword:
1569 reset_async(pwm, 0);
1571 #ifdef WITH_TCP
1572 if (!local_pinentry && !pwm->tcp_conn) {
1573 #else
1574 if (!local_pinentry) {
1575 #endif
1576 rc = send_pinentry_options(pwm);
1578 if (rc)
1579 return rc;
1582 rc = pwmd_command(pwm, NULL, "SAVE %s", password ? password : "");
1584 if (!pwm->passfunc && password && password != pwm->password)
1585 pwmd_free(password);
1587 return rc;
1590 gpg_error_t pwmd_save_async2(pwm_t *pwm)
1592 #ifndef WITH_PINENTRY
1593 return GPG_ERR_NOT_IMPLEMENTED;
1594 #else
1595 if (!pwm)
1596 return GPG_ERR_INV_ARG;
1598 if (!pwm->ctx)
1599 return GPG_ERR_INV_STATE;
1601 if (pwm->cmd != ASYNC_CMD_NONE)
1602 return GPG_ERR_ASS_NESTED_COMMANDS;
1604 pwm->cmd = ASYNC_CMD_SAVE2;
1605 pwm->state = ASYNC_PROCESS;
1606 gpg_error_t rc = do_pwmd_save(pwm, 1, 1);
1608 if (rc)
1609 reset_async(pwm, 0);
1611 return rc;
1612 #endif
1615 gpg_error_t pwmd_save2(pwm_t *pwm)
1617 #ifndef WITH_PINENTRY
1618 return GPG_ERR_NOT_IMPLEMENTED;
1619 #else
1620 return do_pwmd_save(pwm, 0, 1);
1621 #endif
1624 gpg_error_t pwmd_save(pwm_t *pwm)
1626 return do_pwmd_save(pwm, 0, 0);
1629 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1631 va_list ap;
1632 int n = va_arg(ap, int);
1633 char *arg1;
1634 gpg_error_t rc = 0;
1636 if (!pwm)
1637 return GPG_ERR_INV_ARG;
1639 va_start(ap, opt);
1641 switch (opt) {
1642 case PWMD_OPTION_STATUS_CB:
1643 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
1644 break;
1645 case PWMD_OPTION_STATUS_DATA:
1646 pwm->status_data = va_arg(ap, void *);
1647 break;
1648 case PWMD_OPTION_PASSPHRASE_CB:
1649 pwm->passfunc = va_arg(ap, pwmd_passphrase_cb_t);
1650 break;
1651 case PWMD_OPTION_PASSPHRASE_DATA:
1652 pwm->passdata = va_arg(ap, void *);
1653 break;
1654 case PWMD_OPTION_PASSPHRASE:
1655 arg1 = va_arg(ap, char *);
1657 if (pwm->password)
1658 pwmd_free(pwm->password);
1660 pwm->password = pwmd_strdup(arg1);
1661 break;
1662 case PWMD_OPTION_PINENTRY_TRIES:
1663 n = va_arg(ap, int);
1665 if (n <= 0) {
1666 va_end(ap);
1667 rc = GPG_ERR_INV_VALUE;
1669 else
1670 pwm->pinentry_tries = n;
1671 break;
1672 case PWMD_OPTION_PINENTRY_TIMEOUT:
1673 n = va_arg(ap, int);
1675 if (n < 0) {
1676 va_end(ap);
1677 rc = GPG_ERR_INV_VALUE;
1679 else
1680 pwm->pinentry_timeout = n;
1681 break;
1682 case PWMD_OPTION_PINENTRY_PATH:
1683 if (pwm->pinentry_path)
1684 pwmd_free(pwm->pinentry_path);
1686 pwm->pinentry_path = _expand_homedir(va_arg(ap, char *), NULL);
1687 break;
1688 case PWMD_OPTION_PINENTRY_TTY:
1689 if (pwm->pinentry_tty)
1690 pwmd_free(pwm->pinentry_tty);
1692 pwm->pinentry_tty = pwmd_strdup(va_arg(ap, char *));
1693 break;
1694 case PWMD_OPTION_PINENTRY_DISPLAY:
1695 if (pwm->pinentry_display)
1696 pwmd_free(pwm->pinentry_display);
1698 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
1699 break;
1700 case PWMD_OPTION_PINENTRY_TERM:
1701 if (pwm->pinentry_term)
1702 pwmd_free(pwm->pinentry_term);
1704 pwm->pinentry_term = pwmd_strdup(va_arg(ap, char *));
1705 break;
1706 case PWMD_OPTION_PINENTRY_TITLE:
1707 if (pwm->title)
1708 pwmd_free(pwm->title);
1710 pwm->title = _percent_escape(va_arg(ap, char *));
1711 break;
1712 case PWMD_OPTION_PINENTRY_PROMPT:
1713 if (pwm->prompt)
1714 pwmd_free(pwm->prompt);
1716 pwm->prompt = _percent_escape(va_arg(ap, char *));
1717 break;
1718 case PWMD_OPTION_PINENTRY_DESC:
1719 if (pwm->desc)
1720 pwmd_free(pwm->desc);
1722 pwm->desc = _percent_escape(va_arg(ap, char *));
1723 break;
1724 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1725 if (pwm->lcctype)
1726 pwmd_free(pwm->lcctype);
1728 pwm->lcctype = pwmd_strdup(va_arg(ap, char *));
1729 break;
1730 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1731 if (pwm->lcmessages)
1732 pwmd_free(pwm->lcmessages);
1734 pwm->lcmessages = pwmd_strdup(va_arg(ap, char *));
1735 break;
1736 case PWMD_OPTION_IP_VERSION:
1737 #ifdef WITH_TCP
1738 n = va_arg(ap, int);
1740 switch (n) {
1741 case PWMD_IP_ANY:
1742 case PWMD_IPV4:
1743 case PWMD_IPV6:
1744 pwm->prot = n;
1745 break;
1746 default:
1747 rc = GPG_ERR_INV_VALUE;
1748 break;
1751 va_end(ap);
1752 #else
1753 rc = GPG_ERR_NOT_IMPLEMENTED;
1754 #endif
1755 break;
1756 default:
1757 rc = GPG_ERR_UNKNOWN_OPTION;
1758 break;
1761 va_end(ap);
1762 return rc;
1765 gpg_error_t pwmd_get_fds(pwm_t *pwm, pwmd_fd_t *fds, int *n_fds)
1767 int in_total;
1768 int fd = 0;
1769 #ifdef WITH_TCP
1770 int afds[ARES_GETSOCK_MAXNUM];
1771 int got_sock = 0;
1772 int n, i;
1773 #endif
1775 if (!pwm || !fds || !n_fds || *n_fds <= 0)
1776 return GPG_ERR_INV_ARG;
1778 in_total = *n_fds;
1779 #ifdef WITH_TCP
1780 memset(afds, 0, sizeof(int)*ARES_GETSOCK_MAXNUM);
1781 #endif
1782 memset(fds, 0, sizeof(pwmd_fd_t)*in_total);
1783 *n_fds = 0;
1785 switch (pwm->cmd) {
1786 default:
1787 case ASYNC_CMD_NONE:
1788 case ASYNC_CMD_OPEN:
1789 case ASYNC_CMD_SAVE:
1790 #ifdef WITH_PINENTRY
1791 async1:
1792 #endif
1793 if (pwm->fd == -1)
1794 return GPG_ERR_INV_STATE;
1796 (*n_fds)++;
1797 fds[fd].fd = pwm->fd;
1798 fds[fd++].flags = PWMD_FD_READABLE;
1799 return 0;
1800 #ifdef WITH_PINENTRY
1801 case ASYNC_CMD_OPEN2:
1802 case ASYNC_CMD_SAVE2:
1803 /* The command has already completed (cached or new). */
1804 if (pwm->state == ASYNC_DONE)
1805 return 0;
1807 if (pwm->nb_fd == -1)
1808 return GPG_ERR_INV_STATE;
1810 (*n_fds)++;
1811 fds[fd].fd = pwm->nb_fd;
1812 fds[fd++].flags = PWMD_FD_READABLE;
1813 goto async1;
1814 #endif
1815 #ifdef WITH_TCP
1816 case ASYNC_CMD_DNS:
1817 if (!pwm->tcp_conn || !pwm->tcp_conn->chan)
1818 return GPG_ERR_INV_STATE;
1820 n = ares_getsock(pwm->tcp_conn->chan, afds, ARES_GETSOCK_MAXNUM);
1822 for (i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
1823 got_sock = 0;
1825 if (fd > in_total) {
1826 *n_fds = fd;
1827 return GPG_ERR_ERANGE;
1830 if (ARES_GETSOCK_READABLE(n, i)) {
1831 got_sock++;
1832 fds[fd].flags |= PWMD_FD_READABLE;
1835 if (ARES_GETSOCK_WRITABLE(n, i)) {
1836 got_sock++;
1837 fds[fd].flags |= PWMD_FD_WRITABLE;
1840 if (got_sock)
1841 fds[fd++].fd = afds[i];
1844 *n_fds = fd;
1845 return 0;
1846 case ASYNC_CMD_CONNECT:
1847 case ASYNC_CMD_HOSTKEY:
1848 if (!pwm->tcp_conn || pwm->tcp_conn->fd == -1)
1849 return GPG_ERR_INV_STATE;
1851 (*n_fds)++;
1852 fds[fd].fd = pwm->tcp_conn->fd;
1853 fds[fd++].flags = PWMD_FD_READABLE;
1854 return 0;
1855 #endif
1858 return GPG_ERR_INV_STATE;
1861 pwm_t *pwmd_new(const char *name)
1863 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
1865 if (!h)
1866 return NULL;
1868 if (name) {
1869 h->name = pwmd_strdup(name);
1871 if (!h->name) {
1872 pwmd_free(h);
1873 return NULL;
1877 reset_handle(h);
1878 h->pinentry_timeout = -30;
1879 h->pinentry_tries = 3;
1880 #ifdef WITH_TCP
1881 h->prot = PWMD_IP_ANY;
1882 #endif
1884 if (ttyname(STDOUT_FILENO)) {
1885 char buf[256];
1887 ttyname_r(STDOUT_FILENO, buf, sizeof(buf));
1888 h->pinentry_tty = pwmd_strdup(buf);
1890 if (!h->pinentry_tty)
1891 goto fail;
1894 if (getenv("TERM") && h->pinentry_tty) {
1895 h->pinentry_term = pwmd_strdup(getenv("TERM"));
1897 if (!h->pinentry_term)
1898 goto fail;
1901 if (getenv("DISPLAY")) {
1902 h->pinentry_display = pwmd_strdup(getenv("DISPLAY"));
1904 if (!h->pinentry_display)
1905 goto fail;
1908 return h;
1910 fail:
1911 pwmd_close(h);
1912 return NULL;
1915 void pwmd_free(void *ptr)
1917 _xfree(ptr);
1920 void *pwmd_malloc(size_t size)
1922 return _xmalloc(size);
1925 void *pwmd_calloc(size_t nmemb, size_t size)
1927 return _xcalloc(nmemb, size);
1930 void *pwmd_realloc(void *ptr, size_t size)
1932 return _xrealloc(ptr, size);
1935 char *pwmd_strdup(const char *str)
1937 return _xstrdup(str);
1940 char *pwmd_strdup_printf(const char *fmt, ...)
1942 va_list ap, ap2;
1943 int len;
1944 char *buf;
1946 if (!fmt)
1947 return NULL;
1949 va_start(ap, fmt);
1950 va_copy(ap2, ap);
1951 len = vsnprintf(NULL, 0, fmt, ap);
1952 va_end(ap);
1953 buf = pwmd_malloc(++len);
1955 if (buf)
1956 vsnprintf(buf, len, fmt, ap2);
1958 va_end(ap2);
1959 return buf;
1962 gpg_error_t pwmd_getpin(pwm_t *pwm, const char *filename, char **result,
1963 pwmd_pinentry_t which)
1965 #ifndef WITH_PINENTRY
1966 return GPG_ERR_NOT_IMPLEMENTED;
1967 #else
1968 return _pwmd_getpin(pwm, filename, result, which);
1969 #endif