Fixed pwmd_process() when an OPEN command failed with
[libpwmd.git] / src / libpwmd.c
blobf91cc5e7a40abc5b972df3396eed13d18661447c
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 <netdb.h>
39 #include <netinet/in.h>
40 #include <sys/socket.h>
41 #include <libpwmd.h>
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
47 #ifdef WITH_LIBPTH
48 #include <pth.h>
49 #endif
51 #ifdef WITH_TCP
52 #define DNS_USE_GETTIMEOFDAY_FOR_ID 1
53 #include <ares.h>
54 #include <arpa/nameser.h>
55 #endif
57 #ifdef HAVE_ASSUAN_H
58 #include <assuan.h>
59 #endif
61 #ifdef HAVE_SETLOCALE
62 #include <locale.h>
63 #endif
65 #include "gettext.h"
66 #define N_(msgid) dgettext("libpwmd", msgid)
68 #include "mem.h"
69 #include "types.h"
71 static char *_getpwuid(struct passwd *pwd)
73 size_t size = sysconf(_SC_GETPW_R_SIZE_MAX);
74 struct passwd *result;
75 char *buf;
76 int n;
78 if (size == -1)
79 size = 16384;
81 buf = pwmd_malloc(size);
83 if (!buf)
84 return NULL;
86 n = getpwuid_r(getuid(), pwd, buf, size, &result);
88 if (n) {
89 pwmd_free(buf);
90 errno = n;
91 return NULL;
94 if (!result) {
95 pwmd_free(buf);
96 return NULL;
99 errno = n;
100 return buf;
103 static const char *_pwmd_strerror(gpg_error_t e)
105 gpg_err_code_t code = gpg_err_code(e);
107 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
108 switch (code) {
109 default:
110 return NULL;
111 case GPG_ERR_USER_1:
112 return N_("Unknown error");
113 case GPG_ERR_USER_2:
114 return N_("No cache slots available");
115 case GPG_ERR_USER_3:
116 return N_("Recursion loop");
117 case GPG_ERR_USER_4:
118 return N_("No file is open");
119 case GPG_ERR_USER_5:
120 return N_("General LibXML error");
121 case GPG_ERR_USER_6:
122 return N_("File modified");
126 return NULL;
129 const char *pwmd_strerror(gpg_error_t code)
131 const char *p = _pwmd_strerror(code);
133 return p ? p : gpg_strerror(code);
136 int pwmd_strerror_r(gpg_error_t code, char *buf, size_t size)
138 const char *p = _pwmd_strerror(code);
140 if (p) {
141 snprintf(buf, size, "%s", p);
143 if (strlen(p) > size)
144 return ERANGE;
146 return 0;
149 return gpg_strerror_r(code, buf, size);
152 gpg_error_t pwmd_init()
154 static int initialized;
156 if (initialized)
157 return 0;
159 #ifndef MEM_DEBUG
160 xmem_init();
161 #endif
162 #ifdef ENABLE_NLS
163 bindtextdomain("libpwmd", LOCALEDIR);
164 #endif
165 gpg_err_init();
166 assuan_set_malloc_hooks(pwmd_malloc, pwmd_realloc, pwmd_free);
167 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
168 initialized = 1;
169 return 0;
172 static gpg_error_t _socket_connect_finalize(pwm_t *pwm)
174 int active[2];
175 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
176 gpg_error_t rc;
177 char *result;
179 if (n <= 0)
180 return GPG_ERR_EBADFD;
182 pwm->fd = active[0];
183 #ifdef WITH_PINENTRY
184 pwm->pid = -1;
185 #endif
186 assuan_set_pointer(pwm->ctx, pwm);
188 #ifdef WITH_TCP
189 // Until X11 forwarding is supported, disable the remote pwmd pinentry.
190 if (pwm->tcp_conn) {
191 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
193 if (rc)
194 return rc;
196 #endif
198 if (pwm->name) {
199 rc = pwmd_command(pwm, NULL, "OPTION CLIENT NAME=%s", pwm->name);
201 if (rc)
202 return rc;
205 rc = pwmd_command(pwm, &result, "VERSION");
207 if (rc && rc != GPG_ERR_ASS_UNKNOWN_CMD)
208 return rc;
210 if (rc)
211 pwm->version = PWMD_V1;
212 else
213 pwm->version = PWMD_V2;
215 pwmd_free(result);
216 return 0;
219 #ifdef WITH_TCP
220 static int read_hook(assuan_context_t ctx, assuan_fd_t fd, void *data,
221 size_t len, ssize_t *ret)
223 pwm_t *pwm = assuan_get_pointer(ctx);
225 if (!pwm || !pwm->tcp_conn)
226 #ifdef WITH_LIBPTH
227 *ret = pth_read((int)fd, data, len);
228 #else
229 *ret = read((int)fd, data, len);
230 #endif
231 else
232 *ret = libssh2_channel_read(pwm->tcp_conn->channel, data, len);
234 return *ret >= 0 ? 1 : 0;
237 static int write_hook(assuan_context_t ctx, assuan_fd_t fd, const void *data,
238 size_t len, ssize_t *ret)
240 pwm_t *pwm = assuan_get_pointer(ctx);
242 if (!pwm || !pwm->tcp_conn)
243 #ifdef WITH_LIBPTH
244 *ret = pth_write((int)fd, data, len);
245 #else
246 *ret = write((int)fd, data, len);
247 #endif
248 else
249 *ret = libssh2_channel_write(pwm->tcp_conn->channel, data, len);
251 return *ret >= 0 ? 1 : 0;
254 static void _ssh_deinit(pwmd_tcp_conn_t *conn);
255 static void free_tcp_conn(pwmd_tcp_conn_t *conn)
257 if (!conn)
258 return;
260 if (conn->username) {
261 pwmd_free(conn->username);
262 conn->username = NULL;
265 if (conn->known_hosts) {
266 pwmd_free(conn->known_hosts);
267 conn->known_hosts = NULL;
270 if (conn->identity) {
271 pwmd_free(conn->identity);
272 conn->identity = NULL;
275 if (conn->identity_pub) {
276 pwmd_free(conn->identity_pub);
277 conn->identity_pub = NULL;
280 if (conn->host) {
281 pwmd_free(conn->host);
282 conn->host = NULL;
285 if (conn->hostkey) {
286 pwmd_free(conn->hostkey);
287 conn->hostkey = NULL;
290 if (conn->chan) {
291 ares_destroy(conn->chan);
292 conn->chan = NULL;
295 if (conn->he) {
296 ares_free_hostent(conn->he);
297 conn->he = NULL;
300 if (conn->fd >= 0) {
301 close(conn->fd);
302 conn->fd = -1;
305 if (conn->session)
306 _ssh_deinit(conn);
307 else
308 pwmd_free(conn);
311 static void _ssh_deinit(pwmd_tcp_conn_t *conn)
313 if (!conn)
314 return;
316 if (conn->channel)
317 libssh2_channel_free(conn->channel);
319 if (conn->session) {
320 libssh2_session_disconnect(conn->session, "Bye!");
321 libssh2_session_free(conn->session);
324 conn->session = NULL;
325 conn->channel = NULL;
326 free_tcp_conn(conn);
329 static void _ssh_assuan_deinit(assuan_context_t ctx)
331 pwm_t *pwm = assuan_get_pointer(ctx);
333 if (pwm->tcp_conn) {
334 pwm->tcp_conn->fd = -1;
335 _ssh_deinit(pwm->tcp_conn);
336 pwm->tcp_conn = NULL;
341 * Sets common options from both pwmd_ssh_connect() and
342 * pwmd_ssh_connect_async().
344 static gpg_error_t init_tcp_conn(pwmd_tcp_conn_t **dst, const char *host,
345 int port, const char *identity, const char *user, const char *hosts,
346 int get)
348 pwmd_tcp_conn_t *conn;
349 gpg_error_t rc = 0;
351 if (get) {
352 if (!host)
353 return GPG_ERR_INV_ARG;
355 else {
356 if (!host || !identity || !hosts)
357 return GPG_ERR_INV_ARG;
360 conn = pwmd_calloc(1, sizeof(pwmd_tcp_conn_t));
362 if (!conn)
363 return gpg_error_from_errno(ENOMEM);
365 conn->port = port == -1 ? 22 : port;
366 conn->host = pwmd_strdup(host);
368 if (!conn->host) {
369 rc = gpg_error_from_errno(ENOMEM);
370 goto fail;
373 if (!get) {
374 struct passwd pw;
375 char *pwbuf = _getpwuid(&pw);
377 if (!pwbuf) {
378 rc = gpg_error_from_errno(errno);
379 goto fail;
382 conn->username = pwmd_strdup(user ? user : pw.pw_name);
383 pwmd_free(pwbuf);
385 if (!conn->username) {
386 rc = gpg_error_from_errno(ENOMEM);
387 goto fail;
390 conn->identity = pwmd_strdup(identity);
392 if (!conn->identity) {
393 rc = gpg_error_from_errno(ENOMEM);
394 goto fail;
397 conn->identity_pub = pwmd_malloc(strlen(conn->identity)+5);
399 if (!conn->identity_pub) {
400 rc = gpg_error_from_errno(ENOMEM);
401 goto fail;
404 sprintf(conn->identity_pub, "%s.pub", conn->identity);
405 conn->known_hosts = pwmd_strdup(hosts);
407 if (!conn->known_hosts) {
408 rc = gpg_error_from_errno(ENOMEM);
409 goto fail;
413 *dst = conn;
414 return 0;
416 fail:
417 free_tcp_conn(conn);
418 return rc;
421 static gpg_error_t do_connect(pwm_t *pwm, int prot, void *addr)
423 struct sockaddr_in their_addr;
425 pwm->tcp_conn->fd = socket(prot, SOCK_STREAM, 0);
427 if (pwm->tcp_conn->fd == -1)
428 return gpg_error_from_syserror();
430 if (pwm->tcp_conn->async)
431 fcntl(pwm->tcp_conn->fd, F_SETFL, O_NONBLOCK);
433 pwm->cmd = ASYNC_CMD_CONNECT;
434 their_addr.sin_family = prot;
435 their_addr.sin_port = htons(pwm->tcp_conn->port);
436 their_addr.sin_addr = *((struct in_addr *)addr);
437 memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
439 #ifdef WITH_LIBPTH
440 if (pth_connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
441 sizeof(their_addr)) == -1)
442 #else
443 if (connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
444 sizeof(their_addr)) == -1)
445 #endif
446 return gpg_error_from_syserror();
448 return 0;
451 static gpg_error_t ares_error_to_pwmd(int status)
453 if (status != ARES_SUCCESS)
454 warnx("%s", ares_strerror(status));
456 switch (status) {
457 case ARES_ENODATA:
458 case ARES_EFORMERR:
459 case ARES_ENOTFOUND:
460 return GPG_ERR_UNKNOWN_HOST;
461 case ARES_ESERVFAIL:
462 return GPG_ERR_EHOSTDOWN;
463 case ARES_ETIMEOUT:
464 return GPG_ERR_TIMEOUT;
465 case ARES_ENOMEM:
466 return gpg_error_from_errno(ENOMEM);
467 case ARES_ECONNREFUSED:
468 return GPG_ERR_ECONNREFUSED;
469 default:
470 /* FIXME ??? */
471 return GPG_ERR_EHOSTUNREACH;
474 return ARES_SUCCESS;
477 static void dns_resolve_cb(void *arg, int status, int timeouts,
478 unsigned char *abuf, int alen)
480 pwm_t *pwm = arg;
481 int rc;
482 struct hostent *he;
484 if (status == ARES_EDESTRUCTION)
485 return;
487 if (status != ARES_SUCCESS) {
488 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
489 return;
492 /* Check for an IPv6 address first. */
493 if (pwm->prot == PWMD_IP_ANY || pwm->prot == PWMD_IPV6)
494 rc = ares_parse_aaaa_reply(abuf, alen, &he, NULL, NULL);
495 else
496 rc = ares_parse_a_reply(abuf, alen, &he, NULL, NULL);
498 if (rc != ARES_SUCCESS) {
499 if (pwm->prot != PWMD_IP_ANY || rc != ARES_ENODATA) {
500 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
501 return;
504 rc = ares_parse_a_reply(abuf, alen, &he, NULL, NULL);
506 if (rc != ARES_SUCCESS) {
507 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
508 return;
512 pwm->tcp_conn->he = he;
513 pwm->tcp_conn->rc = do_connect(pwm, he->h_addrtype, he->h_addr);
516 static gpg_error_t _do_pwmd_tcp_connect_async(pwm_t *pwm, const char *host,
517 int port, const char *identity, const char *user,
518 const char *known_hosts, pwmd_async_cmd_t which)
520 pwmd_tcp_conn_t *conn;
521 gpg_error_t rc;
523 if (!pwm)
524 return GPG_ERR_INV_ARG;
526 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts,
527 which == ASYNC_CMD_HOSTKEY ? 1 : 0);
529 if (rc)
530 return rc;
532 conn->async = 1;
533 pwm->tcp_conn = conn;
534 pwm->tcp_conn->cmd = which;
536 if (pwm->tcp_conn->cmd == ASYNC_CMD_HOSTKEY)
537 pwm->tcp_conn->get_only = 1;
539 pwm->cmd = ASYNC_CMD_DNS;
540 pwm->state = ASYNC_PROCESS;
541 ares_init(&pwm->tcp_conn->chan);
542 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
543 dns_resolve_cb, pwm);
544 return 0;
547 gpg_error_t pwmd_ssh_connect_async(pwm_t *pwm, const char *host, int port,
548 const char *identity, const char *user, const char *known_hosts)
550 return _do_pwmd_tcp_connect_async(pwm, host, port, identity, user,
551 known_hosts, ASYNC_CMD_CONNECT);
554 static void *_ssh_malloc(size_t size, void **data)
556 return pwmd_malloc(size);
559 static void _ssh_free(void *ptr, void **data)
561 pwmd_free(ptr);
564 static void *_ssh_realloc(void *ptr, size_t size, void **data)
566 return pwmd_realloc(ptr, size);
569 static char *to_hex(const char *str, size_t slen)
571 int i;
572 char *buf = pwmd_malloc(slen*2+1);
574 if (!buf)
575 return NULL;
577 for (i = 0, buf[0] = 0; i < slen; i++) {
578 char tmp[3];
580 sprintf(tmp, "%02x", (unsigned char)str[i]);
581 strcat(buf, tmp);
584 return buf;
587 static int verify_host_key(pwm_t *pwm)
589 FILE *fp = fopen(pwm->tcp_conn->known_hosts, "r");
590 char *buf, *p;
592 if (!fp)
593 return 1;
595 buf = pwmd_malloc(LINE_MAX);
597 if (!buf)
598 goto fail;
600 while ((p = fgets(buf, LINE_MAX, fp))) {
601 if (*p == '#' || isspace(*p))
602 continue;
604 if (p[strlen(p)-1] == '\n')
605 p[strlen(p)-1] = 0;
607 if (!strcmp(buf, pwm->tcp_conn->hostkey))
608 goto done;
611 fail:
612 if (buf)
613 pwmd_free(buf);
615 fclose(fp);
616 return 1;
618 done:
619 pwmd_free(buf);
620 fclose(fp);
621 return 0;
624 static gpg_error_t authenticate_ssh(pwm_t *pwm)
626 const char *fp = libssh2_hostkey_hash(pwm->tcp_conn->session,
627 LIBSSH2_HOSTKEY_HASH_SHA1);
628 char *userauth;
630 pwm->tcp_conn->hostkey = to_hex(fp, 20);
632 if (!pwm->tcp_conn->hostkey)
633 return gpg_error_from_errno(ENOMEM);
635 if (pwm->tcp_conn->get_only)
636 return 0;
638 if (!fp || verify_host_key(pwm))
639 return GPG_ERR_BAD_CERT;
641 userauth = libssh2_userauth_list(pwm->tcp_conn->session,
642 pwm->tcp_conn->username, strlen(pwm->tcp_conn->username));
644 if (!userauth || !strstr(userauth, "publickey"))
645 return GPG_ERR_BAD_PIN_METHOD;
647 if (libssh2_userauth_publickey_fromfile(pwm->tcp_conn->session,
648 pwm->tcp_conn->username, pwm->tcp_conn->identity_pub,
649 pwm->tcp_conn->identity, NULL))
650 return GPG_ERR_BAD_SECKEY;
652 return 0;
655 static gpg_error_t setup_tcp_session(pwm_t *pwm)
657 assuan_context_t ctx;
658 struct assuan_io_hooks io_hooks = {read_hook, write_hook};
659 gpg_error_t rc;
661 pwm->tcp_conn->session = libssh2_session_init_ex(_ssh_malloc, _ssh_free,
662 _ssh_realloc, NULL);
664 if (!pwm->tcp_conn->session) {
665 rc = gpg_error_from_errno(ENOMEM);
666 goto fail;
669 if (libssh2_session_startup(pwm->tcp_conn->session, pwm->tcp_conn->fd)) {
670 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
671 goto fail;
674 rc = authenticate_ssh(pwm);
676 if (rc)
677 goto fail;
679 /* pwmd_get_hostkey(). */
680 if (pwm->tcp_conn->get_only) {
681 pwm->result = pwmd_strdup(pwm->tcp_conn->hostkey);
683 if (!pwm->result) {
684 rc = gpg_error_from_errno(ENOMEM);
685 goto fail;
688 return 0;
691 pwm->tcp_conn->channel = libssh2_channel_open_session(pwm->tcp_conn->session);
693 if (!pwm->tcp_conn->channel) {
694 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
695 goto fail;
698 if (libssh2_channel_shell(pwm->tcp_conn->channel)) {
699 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
700 goto fail;
703 assuan_set_io_hooks(&io_hooks);
704 rc = assuan_socket_connect_fd(&ctx, pwm->tcp_conn->fd, 0, pwm);
706 if (rc)
707 goto fail;
709 assuan_set_finish_handler(ctx, _ssh_assuan_deinit);
710 pwm->ctx = ctx;
711 rc = _socket_connect_finalize(pwm);
713 if (rc)
714 goto fail;
716 return 0;
718 fail:
719 free_tcp_conn(pwm->tcp_conn);
720 pwm->tcp_conn = NULL;
721 return rc;
724 static gpg_error_t _do_pwmd_tcp_connect(pwm_t *pwm, const char *host, int port,
725 const char *identity, const char *user, const char *known_hosts, int get)
727 pwmd_tcp_conn_t *conn;
728 gpg_error_t rc;
730 if (!pwm)
731 return GPG_ERR_INV_ARG;
733 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts, get);
735 if (rc)
736 return rc;
738 pwm->tcp_conn = conn;
739 pwm->tcp_conn->get_only = get;
740 pwm->cmd = ASYNC_CMD_DNS;
741 ares_init(&pwm->tcp_conn->chan);
742 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
743 dns_resolve_cb, pwm);
745 /* dns_resolve_cb() may have already been called. */
746 if (pwm->tcp_conn->rc) {
747 rc = pwm->tcp_conn->rc;
748 goto fail;
752 * Fake a blocking DNS lookup. libcares does a better job than
753 * getaddrinfo().
755 do {
756 fd_set rfds, wfds;
757 int n;
758 struct timeval tv;
760 FD_ZERO(&rfds);
761 FD_ZERO(&wfds);
762 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
763 ares_timeout(pwm->tcp_conn->chan, NULL, &tv);
764 #ifdef WITH_LIBPTH
765 n = pth_select(n, &rfds, &wfds, NULL, &tv);
766 #else
767 n = select(n, &rfds, &wfds, NULL, &tv);
768 #endif
770 if (n == -1) {
771 rc = gpg_error_from_syserror();
772 goto fail;
774 else if (n == 0) {
775 rc = GPG_ERR_TIMEOUT;
776 goto fail;
779 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
781 if (pwm->tcp_conn->rc)
782 break;
783 } while (pwm->cmd == ASYNC_CMD_DNS);
785 if (pwm->tcp_conn->rc) {
786 rc = pwm->tcp_conn->rc;
787 goto fail;
790 return setup_tcp_session(pwm);
792 fail:
793 return rc;
796 gpg_error_t pwmd_ssh_connect(pwm_t *pwm, const char *host, int port,
797 const char *identity, const char *user, const char *known_hosts)
799 return _do_pwmd_tcp_connect(pwm, host, port, identity, user, known_hosts, 0);
802 gpg_error_t pwmd_get_hostkey(pwm_t *pwm, const char *host, int port,
803 char **result)
805 char *hostkey;
806 gpg_error_t rc;
808 rc = _do_pwmd_tcp_connect(pwm, host, port, NULL, NULL, NULL, 1);
810 if (rc)
811 return rc;
813 hostkey = pwmd_strdup(pwm->tcp_conn->hostkey);
815 if (!hostkey)
816 rc = gpg_error_from_errno(ENOMEM);
818 *result = hostkey;
819 return rc;
822 gpg_error_t pwmd_get_hostkey_async(pwm_t *pwm, const char *host, int port)
824 return _do_pwmd_tcp_connect_async(pwm, host, port, NULL, NULL, NULL,
825 ASYNC_CMD_HOSTKEY);
829 * ssh://[username@]hostname[:port],identity,known_hosts
831 * Any missing parameters are checked for in init_tcp_conn().
833 static int parse_ssh_url(char *str, char **host, int *port, char **user,
834 char **identity, char **known_hosts)
836 char *p;
837 char *t;
838 int len;
840 *host = *user = *identity = *known_hosts = NULL;
841 *port = -1;
842 p = strrchr(str, '@');
844 if (p) {
845 len = strlen(str)-strlen(p)+1;
846 *user = pwmd_malloc(len);
847 snprintf(*user, len, "%s", str);
848 p++;
850 else
851 p = str;
853 t = strchr(p, ':');
855 if (t) {
856 len = strlen(p)-strlen(t)+1;
857 *host = pwmd_malloc(len);
858 snprintf(*host, len, "%s", p);
859 t++;
860 *port = atoi(t);
862 while (*t && isdigit(*t))
863 t++;
865 p = t;
868 t = strchr(p, ',');
870 if (t) {
871 char *t2;
873 if (!*host) {
874 len = strlen(p)-strlen(t)+1;
875 *host = pwmd_malloc(len);
876 snprintf(*host, len, "%s", p);
879 t++;
880 t2 = strchr(t, ',');
882 if (t2)
883 len = strlen(t)-strlen(t2)+1;
884 else
885 len = strlen(t)+1;
887 *identity = pwmd_malloc(len);
888 snprintf(*identity, len, "%s", t);
890 if (t2) {
891 t2++;
892 t += len+1;
893 len = strlen(t2)+1;
894 *known_hosts = pwmd_malloc(len);
895 snprintf(*known_hosts, len, "%s", t2);
898 else {
899 if (!*host) {
900 len = strlen(p)+1;
901 *host = pwmd_malloc(len);
902 snprintf(*host, len, "%s", p);
906 return 0;
908 #endif
910 static gpg_error_t _pwmd_connect_url(pwm_t *pwm, const char *url, int async)
912 char *p = (char *)url;
913 gpg_error_t rc;
915 if (!pwm || !url)
916 return GPG_ERR_INV_ARG;
918 if (!strncmp(p, "socket://", 9)) {
919 p += 9;
920 rc = pwmd_connect(pwm, p);
921 pwm->state = ASYNC_DONE;
922 return rc;
924 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
925 !strncmp(p, "ssh4://", 7)) {
926 #ifndef WITH_TCP
927 return GPG_ERR_NOT_IMPLEMENTED;
928 #else
929 char *host = NULL;
930 int port = -1;
931 char *identity = NULL;
932 char *known_hosts = NULL;
933 char *username = NULL;
935 if (!strncmp(p, "ssh6://", 7)) {
936 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV6);
937 p += 7;
939 else if (!strncmp(p, "ssh4://", 7)) {
940 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV4);
941 p += 7;
943 else {
944 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IP_ANY);
945 p += 6;
948 if (rc)
949 return rc;
951 rc = parse_ssh_url(p, &host, &port, &username, &identity,
952 &known_hosts);
954 if (rc)
955 return rc;
957 if (async)
958 rc = pwmd_ssh_connect_async(pwm, host, port, identity, username,
959 known_hosts);
960 else
961 rc = pwmd_ssh_connect(pwm, host, port, identity, username,
962 known_hosts);
964 if (host)
965 pwmd_free(host);
967 if (username)
968 pwmd_free(username);
970 if (identity)
971 pwmd_free(identity);
973 if (known_hosts)
974 pwmd_free(known_hosts);
976 return rc;
977 #endif
980 return GPG_ERR_UNSUPPORTED_PROTOCOL;
983 gpg_error_t pwmd_connect_url(pwm_t *pwm, const char *url)
985 return _pwmd_connect_url(pwm, url, 0);
988 gpg_error_t pwmd_connect_url_async(pwm_t *pwm, const char *url)
990 return _pwmd_connect_url(pwm, url, 1);
993 gpg_error_t pwmd_connect(pwm_t *pwm, const char *path)
995 char *socketpath = NULL;
996 assuan_context_t ctx;
997 struct passwd pw;
998 char *pwbuf;
999 gpg_error_t rc;
1001 if (!pwm)
1002 return GPG_ERR_INV_ARG;
1004 pwbuf = _getpwuid(&pw);
1006 if (!pwbuf)
1007 return gpg_error_from_errno(errno);
1009 if (!path || !*path) {
1010 socketpath = (char *)pwmd_malloc(strlen(pw.pw_dir) + strlen("/.pwmd/socket") + 1);
1011 sprintf(socketpath, "%s/.pwmd/socket", pw.pw_dir);
1013 else
1014 socketpath = pwmd_strdup(path);
1016 pwmd_free(pwbuf);
1017 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
1018 pwmd_free(socketpath);
1020 if (rc)
1021 return rc;
1023 pwm->ctx = ctx;
1024 return _socket_connect_finalize(pwm);
1027 void pwmd_close(pwm_t *pwm)
1029 if (!pwm)
1030 return;
1032 if (pwm->ctx)
1033 assuan_disconnect(pwm->ctx);
1035 if (pwm->password)
1036 pwmd_free(pwm->password);
1038 if (pwm->title)
1039 pwmd_free(pwm->title);
1041 if (pwm->desc)
1042 pwmd_free(pwm->desc);
1044 if (pwm->prompt)
1045 pwmd_free(pwm->prompt);
1047 if (pwm->pinentry_tty)
1048 pwmd_free(pwm->pinentry_tty);
1050 if (pwm->pinentry_display)
1051 pwmd_free(pwm->pinentry_display);
1053 if (pwm->pinentry_term)
1054 pwmd_free(pwm->pinentry_term);
1056 if (pwm->lcctype)
1057 pwmd_free(pwm->lcctype);
1059 if (pwm->lcmessages)
1060 pwmd_free(pwm->lcmessages);
1062 if (pwm->filename)
1063 pwmd_free(pwm->filename);
1065 if (pwm->name)
1066 pwmd_free(pwm->name);
1068 #ifdef WITH_TCP
1069 if (pwm->tcp_conn)
1070 free_tcp_conn(pwm->tcp_conn);
1071 #endif
1073 pwmd_free(pwm);
1076 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
1078 membuf_t *mem = (membuf_t *)data;
1079 void *p;
1081 if (!buffer)
1082 return 0;
1084 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
1085 return 1;
1087 mem->buf = p;
1088 memcpy((char *)mem->buf + mem->len, buffer, len);
1089 mem->len += len;
1090 return 0;
1093 static int _inquire_cb(void *data, const char *keyword)
1095 pwm_t *pwm = (pwm_t *)data;
1096 gpg_error_t rc = 0;
1097 int flags = fcntl(pwm->fd, F_GETFL);
1099 /* Shouldn't get this far without a callback. */
1100 if (!pwm->inquire_func)
1101 return GPG_ERR_INV_ARG;
1104 * Since the socket file descriptor is probably set to non-blocking, set to
1105 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixed when
1106 * asynchronous INQUIRE is supported by either libassuan or a later
1107 * libpwmd.
1109 fcntl(pwm->fd, F_SETFL, 0);
1111 for (;;) {
1112 char *result = NULL;
1113 size_t len;
1114 gpg_error_t arc;
1116 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
1117 rc = gpg_err_code(rc);
1119 if (rc == GPG_ERR_EOF || !rc) {
1120 if (len <= 0 || !result) {
1121 rc = 0;
1122 break;
1125 arc = assuan_send_data(pwm->ctx, result, len);
1127 if (rc == GPG_ERR_EOF) {
1128 rc = arc;
1129 break;
1132 rc = arc;
1134 else if (rc)
1135 break;
1138 fcntl(pwm->fd, F_SETFL, flags);
1139 return rc;
1142 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, ...)
1144 char *buf;
1145 gpg_error_t rc;
1146 va_list ap;
1148 if (pwm->state == ASYNC_DONE)
1149 pwm->state = ASYNC_INIT;
1151 if (pwm->state != ASYNC_INIT)
1152 return GPG_ERR_INV_STATE;
1154 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
1156 if (!buf)
1157 return gpg_error_from_errno(ENOMEM);
1159 va_start(ap, cmd);
1160 vsnprintf(buf, ASSUAN_LINELENGTH, cmd, ap);
1161 va_end(ap);
1162 rc = assuan_write_line(pwm->ctx, buf);
1163 pwmd_free(buf);
1165 if (!rc)
1166 pwm->state = ASYNC_PROCESS;
1168 return rc;
1171 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
1173 if (!pwm || !filename)
1174 return GPG_ERR_INV_ARG;
1176 if (!pwm->ctx)
1177 return GPG_ERR_INV_STATE;
1179 if (pwm->cmd != ASYNC_CMD_OPEN) {
1180 gpg_error_t rc;
1182 pwm->pin_try = 0;
1184 if (pwm->filename)
1185 pwmd_free(pwm->filename);
1187 pwm->filename = pwmd_strdup(filename);
1189 rc = send_pinentry_options(pwm);
1191 if (rc)
1192 return rc;
1195 pwm->cmd = ASYNC_CMD_OPEN;
1196 return do_nb_command(pwm, "OPEN %s %s", filename,
1197 pwm->password ? pwm->password : "");
1200 gpg_error_t pwmd_save_async(pwm_t *pwm)
1202 gpg_error_t rc;
1204 if (!pwm)
1205 return GPG_ERR_INV_ARG;
1207 if (!pwm->ctx)
1208 return GPG_ERR_INV_STATE;
1210 rc = send_pinentry_options(pwm);
1212 if (rc)
1213 return rc;
1215 pwm->cmd = ASYNC_CMD_SAVE;
1216 return do_nb_command(pwm, "SAVE %s", pwm->password ? pwm->password : "");
1219 static gpg_error_t parse_assuan_line(pwm_t *pwm)
1221 gpg_error_t rc;
1222 char *line;
1223 size_t len;
1225 rc = assuan_read_line(pwm->ctx, &line, &len);
1227 if (!rc) {
1228 if (line[0] == 'O' && line[1] == 'K' &&
1229 (line[2] == 0 || line[2] == ' ')) {
1230 pwm->state = ASYNC_DONE;
1232 else if (line[0] == '#') {
1234 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
1235 if (pwm->status_func) {
1236 pwm->status_func(pwm->status_data,
1237 line[1] == 0 ? line+1 : line+2);
1240 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
1241 (line[3] == 0 || line[3] == ' ')) {
1242 line += 4;
1243 rc = atoi(line);
1244 pwm->state = ASYNC_DONE;
1248 return rc;
1251 gpg_error_t pwmd_pending_line(pwm_t *pwm)
1253 if (!pwm)
1254 return GPG_ERR_INV_ARG;
1256 if (!pwm->ctx)
1257 return GPG_ERR_INV_STATE;
1259 return assuan_pending_line(pwm->ctx) ? 0 : GPG_ERR_NO_DATA;
1262 static pwmd_async_t reset_async(pwm_t *pwm, int done)
1264 pwm->state = ASYNC_INIT;
1265 pwm->cmd = ASYNC_CMD_NONE;
1267 #ifdef WITH_PINENTRY
1268 if (pwm->nb_fd != -1) {
1269 close(pwm->nb_fd);
1270 pwm->nb_fd = -1;
1272 #endif
1273 #ifdef WITH_TCP
1274 if (done && pwm->tcp_conn) {
1275 free_tcp_conn(pwm->tcp_conn);
1276 pwm->tcp_conn = NULL;
1278 #endif
1280 return ASYNC_DONE;
1283 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
1285 fd_set fds;
1286 int n;
1287 struct timeval tv = {0, 0};
1289 if (result)
1290 *result = NULL;
1292 if (!rc)
1293 return GPG_ERR_INV_ARG;
1295 *rc = 0;
1297 if (!pwm) {
1298 *rc = GPG_ERR_INV_ARG;
1299 return ASYNC_DONE;
1301 else if (!pwm->ctx) {
1302 switch (pwm->cmd) {
1303 default:
1304 *rc = GPG_ERR_INV_STATE;
1305 return ASYNC_DONE;
1306 #ifdef WITH_TCP
1307 case ASYNC_CMD_DNS:
1308 case ASYNC_CMD_CONNECT:
1309 case ASYNC_CMD_HOSTKEY:
1310 break;
1311 #endif
1315 /* When not in a command, this will let libassuan process status messages
1316 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
1317 * descriptor returned by pwmd_get_fd() to determine when this should be
1318 * called or call pwmd_pending_line() to determine whether a buffered line
1319 * needs to be processed. */
1320 if (pwm->cmd == ASYNC_CMD_NONE) {
1321 *rc = assuan_command(pwm, pwm->ctx, NULL, "NOP");
1322 return ASYNC_DONE;
1325 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
1326 if (pwm->state == ASYNC_DONE) {
1327 reset_async(pwm, 0);
1328 return ASYNC_DONE;
1331 if (pwm->state != ASYNC_PROCESS) {
1332 *rc = GPG_ERR_INV_STATE;
1333 return ASYNC_DONE;
1336 #ifdef WITH_TCP
1337 if (pwm->cmd == ASYNC_CMD_DNS) {
1338 fd_set rfds, wfds;
1340 if (pwm->tcp_conn->rc) {
1341 *rc = pwm->tcp_conn->rc;
1342 reset_async(pwm, 1);
1343 return ASYNC_DONE;
1346 FD_ZERO(&rfds);
1347 FD_ZERO(&wfds);
1348 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
1350 /* Shouldn't happen. */
1351 if (!n)
1352 return pwm->state;
1354 #ifdef WITH_LIBPTH
1355 n = pth_select(n, &rfds, &wfds, NULL, &tv);
1356 #else
1357 n = select(n, &rfds, &wfds, NULL, &tv);
1358 #endif
1360 if (n > 0)
1361 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
1363 return pwm->state;
1365 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
1366 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
1367 int ret;
1368 socklen_t len = sizeof(int);
1370 FD_ZERO(&fds);
1371 FD_SET(pwm->tcp_conn->fd, &fds);
1372 #ifdef WITH_LIBPTH
1373 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1374 #else
1375 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1376 #endif
1378 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
1379 return pwm->state;
1380 else if (n == -1) {
1381 *rc = gpg_error_from_syserror();
1382 reset_async(pwm, 1);
1383 return ASYNC_DONE;
1386 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
1388 if (ret || n) {
1389 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
1390 reset_async(pwm, 1);
1391 return ASYNC_DONE;
1394 else if (pwm->tcp_conn->rc) {
1395 *rc = pwm->tcp_conn->rc;
1396 reset_async(pwm, 1);
1397 return ASYNC_DONE;
1400 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
1401 *rc = setup_tcp_session(pwm);
1403 if (!*rc) {
1404 switch (pwm->tcp_conn->cmd) {
1405 case ASYNC_CMD_HOSTKEY:
1406 if (!*rc)
1407 *result = pwm->result;
1408 break;
1409 default:
1410 break;
1414 return reset_async(pwm, *rc ? 1 : 0);
1416 #endif
1418 #ifdef WITH_PINENTRY
1419 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
1420 int status;
1422 if (pwm->nb_fd == -1) {
1423 *rc = GPG_ERR_INV_STATE;
1424 return reset_async(pwm, 0);
1427 FD_ZERO(&fds);
1428 FD_SET(pwm->nb_fd, &fds);
1429 FD_SET(pwm->fd, &fds);
1430 #ifdef WITH_LIBPTH
1431 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1432 #else
1433 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1434 #endif
1435 if (n == -1) {
1436 *rc = gpg_error_from_syserror();
1437 return reset_async(pwm, 0);
1440 if (n > 0 && FD_ISSET(pwm->nb_fd, &fds)) {
1441 pwmd_nb_status_t nb;
1442 #ifdef WITH_LIBPTH
1443 size_t len = pth_read(pwm->nb_fd, &nb, sizeof(nb));
1444 #else
1445 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
1446 #endif
1447 waitpid(pwm->nb_pid, &status, WNOHANG);
1449 if (len != sizeof(nb)) {
1450 *rc = gpg_error_from_syserror();
1451 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1454 *rc = nb.error;
1456 if (*rc == GPG_ERR_INV_PASSPHRASE && pwm->cmd == ASYNC_CMD_SAVE2) {
1457 reset_async(pwm, 0);
1458 *rc = pwmd_save_async2(pwm);
1459 return ASYNC_PROCESS;
1461 else if (*rc)
1462 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1464 if (pwm->cmd == ASYNC_CMD_SAVE2) {
1465 *rc = do_save_command(pwm, nb.password);
1466 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1467 return reset_async(pwm, 0);
1470 if (pwm->cmd == ASYNC_CMD_OPEN2) {
1471 *rc = do_open_command(pwm, pwm->filename, nb.password);
1472 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1474 if (*rc == GPG_ERR_INV_PASSPHRASE) {
1475 if (++pwm->pin_try < pwm->pinentry_tries) {
1476 int n = pwm->pin_try;
1478 reset_async(pwm, 0);
1479 pwm->pin_try = n;
1480 pwm->cmd = ASYNC_CMD_OPEN2;
1481 *rc = pwmd_open_async2(pwm, pwm->filename);
1483 if (*rc)
1484 return reset_async(pwm, 1);
1486 return pwm->state;
1490 return reset_async(pwm, *rc ? 1 : 0);
1494 /* Fall through so status messages can be processed during the
1495 * pinentry. */
1497 #endif
1499 if (pwm->fd < 0) {
1500 *rc = GPG_ERR_INV_STATE;
1501 return reset_async(pwm, 0);
1504 /* This is for the non-blocking OPEN and SAVE commands. */
1505 FD_ZERO(&fds);
1506 FD_SET(pwm->fd, &fds);
1507 #ifdef WITH_LIBPTH
1508 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
1509 #else
1510 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
1511 #endif
1513 if (n == -1) {
1514 *rc = gpg_error_from_syserror();
1515 return reset_async(pwm, 0);
1518 if (n > 0) {
1519 if (FD_ISSET(pwm->fd, &fds))
1520 *rc = parse_assuan_line(pwm);
1523 while (!*rc && assuan_pending_line(pwm->ctx))
1524 *rc = parse_assuan_line(pwm);
1526 /* For pinentry retries. */
1527 if (!pwm->tcp_conn && pwm->cmd == ASYNC_CMD_OPEN &&
1528 gpg_err_code(*rc) == GPG_ERR_INV_PASSPHRASE &&
1529 ++pwm->pin_try < pwm->pinentry_tries) {
1530 pwm->state = ASYNC_INIT;
1531 *rc = pwmd_open_async(pwm, pwm->filename);
1534 if (*rc)
1535 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN ? 1 : 0);
1537 if (pwm->state == ASYNC_DONE) {
1538 reset_async(pwm, 0);
1539 return ASYNC_DONE;
1542 return pwm->state;
1545 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
1546 char **result, const char *cmd)
1548 membuf_t data;
1549 gpg_error_t rc;
1551 data.len = 0;
1552 data.buf = NULL;
1554 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
1555 pwm->status_func, pwm->status_data);
1557 if (rc) {
1558 if (data.buf) {
1559 pwmd_free(data.buf);
1560 data.buf = NULL;
1563 else {
1564 if (data.buf) {
1565 mem_realloc_cb(&data, "", 1);
1567 if (!result) {
1568 pwmd_free(data.buf);
1569 rc = GPG_ERR_INV_ARG;
1571 else
1572 *result = (char *)data.buf;
1576 return gpg_err_code(rc);
1579 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_cb_t fn,
1580 void *data)
1582 if (!pwm || !cmd || !fn)
1583 return GPG_ERR_INV_ARG;
1585 if (!pwm->ctx)
1586 return GPG_ERR_INV_STATE;
1588 pwm->inquire_func = fn;
1589 pwm->inquire_data = data;
1590 return assuan_command(pwm, pwm->ctx, NULL, cmd);
1593 #ifdef WITH_PINENTRY
1594 static gpg_error_t terminate_pinentry(pwm_t *pwm)
1596 pid_t pid = pwm->pid;
1598 pwm->pid = -1;
1600 if (!pwm || pid == -1)
1601 return GPG_ERR_INV_ARG;
1603 if (kill(pid, 0) == 0) {
1604 if (kill(pid, SIGTERM) == -1) {
1605 if (kill(pid, SIGKILL) == -1)
1606 return gpg_error_from_errno(errno);
1609 else
1610 return gpg_error_from_errno(errno);
1612 return 0;
1615 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
1617 char *tmp, *desc;
1618 gpg_error_t error;
1620 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
1622 if (!tmp)
1623 return gpg_error_from_errno(ENOMEM);
1625 if (!pwm->title)
1626 pwm->title = pwmd_strdup(N_("Password Manager Daemon"));
1628 if (!pwm->title)
1629 goto fail_no_mem;
1631 if (!pwm->prompt)
1632 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
1634 if (!pwm->prompt)
1635 goto fail_no_mem;
1637 if (!pwm->desc && (which == PINENTRY_OPEN || which == PINENTRY_SAVE)) {
1638 if (which == PINENTRY_OPEN)
1639 desc = pwmd_strdup_printf(N_("A passphrase is required to open the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1640 else
1641 desc = pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1643 if (!desc)
1644 goto fail_no_mem;
1647 if (pwm->desc)
1648 desc = pwm->desc;
1650 switch (which) {
1651 case PINENTRY_OPEN:
1652 case PINENTRY_SAVE:
1653 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s", desc);
1655 if (pwm->desc != desc)
1656 pwmd_free(desc);
1657 break;
1658 case PINENTRY_OPEN_FAILED:
1659 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1660 N_("Invalid passphrase, please try again."));
1661 break;
1662 case PINENTRY_SAVE_CONFIRM:
1663 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1664 N_("Please type the passphrase again for confirmation."));
1665 break;
1668 error = pinentry_command(pwm, NULL, tmp);
1670 if (error) {
1671 pwmd_free(tmp);
1672 return error;
1675 snprintf(tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->prompt);
1676 error = pinentry_command(pwm, NULL, tmp);
1678 if (error) {
1679 pwmd_free(tmp);
1680 return error;
1683 snprintf(tmp, ASSUAN_LINELENGTH, "SETDESC %s", pwm->title);
1684 error = pinentry_command(pwm, NULL, tmp);
1685 pwmd_free(tmp);
1686 return error;
1688 fail_no_mem:
1689 pwmd_free(tmp);
1690 return gpg_error_from_errno(ENOMEM);
1693 static void update_pinentry_settings(pwm_t *pwm)
1695 FILE *fp;
1696 char buf[LINE_MAX];
1697 char *p;
1698 struct passwd pw;
1699 char *pwbuf = _getpwuid(&pw);
1701 if (!pwbuf)
1702 return;
1704 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw.pw_dir);
1705 pwmd_free(pwbuf);
1707 if ((fp = fopen(buf, "r")) == NULL)
1708 return;
1710 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
1711 char name[32], val[256];
1713 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
1714 continue;
1716 if (strcasecmp(name, "TTYNAME") == 0) {
1717 pwmd_free(pwm->pinentry_tty);
1718 pwm->pinentry_tty = pwmd_strdup(val);
1720 else if (strcasecmp(name, "TTYTYPE") == 0) {
1721 pwmd_free(pwm->pinentry_term);
1722 pwm->pinentry_term = pwmd_strdup(val);
1724 else if (strcasecmp(name, "DISPLAY") == 0) {
1725 pwmd_free(pwm->pinentry_display);
1726 pwm->pinentry_display = pwmd_strdup(val);
1728 else if (strcasecmp(name, "PATH") == 0) {
1729 pwmd_free(pwm->pinentry_path);
1730 pwm->pinentry_path = pwmd_strdup(val);
1734 fclose(fp);
1737 static gpg_error_t launch_pinentry(pwm_t *pwm)
1739 int rc;
1740 assuan_context_t ctx;
1741 int child_list[] = {-1};
1742 char *display = getenv("DISPLAY");
1743 const char *argv[10];
1744 const char **p = argv;
1745 int have_display = 0;
1746 char *tty = NULL;
1747 char *ttybuf = NULL;
1749 update_pinentry_settings(pwm);
1751 if (pwm->pinentry_display || display)
1752 have_display = 1;
1753 else {
1754 if (!pwm->pinentry_tty) {
1755 ttybuf = pwmd_malloc(255);
1757 if (!ttybuf)
1758 return gpg_error_from_errno(ENOMEM);
1760 rc = ttyname_r(STDOUT_FILENO, ttybuf, 255);
1762 if (rc) {
1763 pwmd_free(ttybuf);
1764 return gpg_error_from_errno(rc);
1767 tty = ttybuf;
1769 else
1770 tty = pwm->pinentry_tty;
1773 if (!have_display && !tty)
1774 return GPG_ERR_ENOTTY;
1776 *p++ = "pinentry";
1777 *p++ = have_display ? "--display" : "--ttyname";
1778 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
1780 if (pwm->lcctype) {
1781 *p++ = "--lc-ctype";
1782 *p++ = pwm->lcctype;
1785 if (pwm->lcmessages) {
1786 *p++ = "--lc-messages";
1787 *p++ = pwm->lcmessages;
1790 *p = NULL;
1792 if (!have_display) {
1793 *p++ = "--ttytype";
1794 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
1795 *p = NULL;
1798 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
1800 if (ttybuf)
1801 pwmd_free(ttybuf);
1803 if (rc)
1804 return rc;
1806 pwm->pid = assuan_get_pid(ctx);
1807 pwm->pctx = ctx;
1808 return set_pinentry_strings(pwm, 0);
1811 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
1813 gpg_error_t n;
1815 if (!pwm->pctx) {
1816 n = launch_pinentry(pwm);
1818 if (n)
1819 return n;
1822 return assuan_command(pwm, pwm->pctx, result, cmd);
1825 static void pinentry_disconnect(pwm_t *pwm)
1827 if (pwm->pctx)
1828 assuan_disconnect(pwm->pctx);
1830 pwm->pctx = NULL;
1831 pwm->pid = -1;
1835 * Only called from a child process.
1837 static void catchsig(int sig)
1839 switch (sig) {
1840 case SIGALRM:
1841 if (gelapsed++ >= gtimeout) {
1842 terminate_pinentry(gpwm);
1843 gerror = GPG_ERR_TIMEOUT;
1845 else
1846 alarm(1);
1847 break;
1848 default:
1849 break;
1852 #endif
1855 * Borrowed from libassuan.
1857 static char *percent_escape(const char *atext)
1859 const unsigned char *s;
1860 int len = strlen(atext) * 3 + 1;
1861 char *buf = (char *)pwmd_malloc(len), *p = buf;
1863 if (!buf)
1864 return NULL;
1866 for (s=(const unsigned char *)atext; *s; s++) {
1867 if (*s < ' ') {
1868 sprintf (p, "%%%02X", *s);
1869 p += 3;
1871 else
1872 *p++ = *s;
1875 *p = 0;
1876 return buf;
1879 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
1881 if (!cmd)
1882 return GPG_ERR_INV_ARG;
1884 return assuan_command(pwm, pwm->ctx, result, cmd);
1887 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1888 va_list ap)
1890 char *buf;
1891 size_t len;
1892 gpg_error_t error;
1893 va_list ap2;
1895 if (!pwm || !cmd)
1896 return GPG_ERR_INV_ARG;
1898 if (!pwm->ctx)
1899 return GPG_ERR_INV_STATE;
1902 * C99 allows the dst pointer to be null which will calculate the length
1903 * of the would-be result and return it.
1905 va_copy(ap2, ap);
1906 len = vsnprintf(NULL, 0, cmd, ap)+1;
1907 buf = (char *)pwmd_malloc(len);
1909 if (!buf) {
1910 va_end(ap2);
1911 return gpg_error_from_errno(ENOMEM);
1914 len = vsnprintf(buf, len, cmd, ap2);
1915 va_end(ap2);
1917 if (buf[strlen(buf)-1] == '\n')
1918 buf[strlen(buf)-1] = 0;
1920 if (buf[strlen(buf)-1] == '\r')
1921 buf[strlen(buf)-1] = 0;
1923 error = send_command(pwm, result, buf);
1924 pwmd_free(buf);
1925 return error;
1928 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1930 va_list ap;
1931 gpg_error_t error;
1933 if (!pwm || !cmd)
1934 return GPG_ERR_INV_ARG;
1936 if (!pwm->ctx)
1937 return GPG_ERR_INV_STATE;
1939 if (result)
1940 *result = NULL;
1942 va_start(ap, cmd);
1943 error = pwmd_command_ap(pwm, result, cmd, ap);
1944 va_end(ap);
1945 return error;
1948 #ifdef WITH_PINENTRY
1949 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
1951 if (gtimeout) {
1952 signal(SIGALRM, catchsig);
1953 alarm(1);
1956 *result = NULL;
1957 return pinentry_command(pwm, result, "GETPIN");
1960 static gpg_error_t getpin(pwm_t *pwm, char **result, int which)
1962 gpg_error_t rc;
1964 gerror = 0;
1965 rc = set_pinentry_strings(pwm, which);
1967 if (rc) {
1968 pinentry_disconnect(pwm);
1969 return rc;
1972 rc = do_getpin(pwm, result);
1975 * Since there was input cancel any timeout setting.
1977 alarm(0);
1978 signal(SIGALRM, SIG_DFL);
1980 if (rc) {
1981 if (pwm->pctx)
1982 pinentry_disconnect(pwm);
1984 /* This lets pwmd_open2() with PWMD_OPTION_PINENTRY_TIMEOUT work. */
1985 if (rc== GPG_ERR_EOF && gerror == GPG_ERR_TIMEOUT)
1986 return gerror;
1988 return rc;
1991 return 0;
1993 #endif
1995 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
1997 char *buf;
1998 gpg_error_t error;
1999 char *result = NULL;
2001 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2003 if (!buf)
2004 return gpg_error_from_errno(ENOMEM);
2006 snprintf(buf, ASSUAN_LINELENGTH, "OPEN %s %s", filename,
2007 password ? password : "");
2008 error = send_command(pwm, &result, buf);
2009 pwmd_free(buf);
2011 if (error && result)
2012 pwmd_free(result);
2014 return error;
2017 static gpg_error_t send_pinentry_options(pwm_t *pwm)
2019 gpg_error_t rc;
2021 if (pwm->pinentry_path) {
2022 rc = pwmd_command(pwm, NULL, "OPTION PATH=%s", pwm->pinentry_path);
2024 if (rc)
2025 return rc;
2028 if (pwm->pinentry_tty) {
2029 rc = pwmd_command(pwm, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
2031 if (rc)
2032 return rc;
2035 if (pwm->pinentry_term) {
2036 rc = pwmd_command(pwm, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
2038 if (rc)
2039 return rc;
2042 if (pwm->pinentry_display) {
2043 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->pinentry_display);
2045 if (rc)
2046 return rc;
2049 if (pwm->title) {
2050 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->title);
2052 if (rc)
2053 return rc;
2056 if (pwm->desc) {
2057 rc = pwmd_command(pwm, NULL, "OPTION DESC=%s", pwm->desc);
2059 if (rc)
2060 return rc;
2063 if (pwm->prompt) {
2064 rc = pwmd_command(pwm, NULL, "OPTION PROMPT=%s", pwm->prompt);
2066 if (rc)
2067 return rc;
2070 if (pwm->lcctype) {
2071 rc = pwmd_command(pwm, NULL, "OPTION LC_CTYPE=%s", pwm->lcctype);
2073 if (rc)
2074 return rc;
2077 if (pwm->lcmessages) {
2078 rc = pwmd_command(pwm, NULL, "OPTION LC_MESSAGES=%s", pwm->lcmessages);
2080 if (rc)
2081 return rc;
2084 if (pwm->pinentry_timeout >= 0) {
2085 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=%i", pwm->pinentry_timeout);
2087 if (rc)
2088 return rc;
2091 return 0;
2094 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb,
2095 int local_pinentry)
2097 char *result = NULL;
2098 char *password = NULL;
2099 char *path;
2100 int pin_try;
2101 gpg_error_t rc;
2102 size_t len;
2104 if (!pwm || !filename || !*filename)
2105 return GPG_ERR_INV_ARG;
2107 if (!pwm->ctx)
2108 return GPG_ERR_INV_STATE;
2110 pin_try = pwm->pinentry_tries - 1;
2113 * Avoid calling pinentry if the password is cached on the server or if
2114 * this is a new file. pwmd version 2 adds a VERSION command which is
2115 * determined in _socket_connect_finalize(). If the server is version 2,
2116 * ISCACHED can determine if a file exists.
2118 #ifdef WITH_TCP
2119 if (!pwm->tcp_conn && pwm->version == PWMD_V1) {
2120 #else
2121 if (pwm->version == PWMD_V1) {
2122 #endif
2123 rc = pwmd_command(pwm, &result, "GETCONFIG data_directory");
2125 if (rc)
2126 return rc;
2128 len = strlen(result)+strlen(filename)+2;
2129 path = pwmd_malloc(len);
2131 if (!path) {
2132 pwmd_free(result);
2133 return gpg_error_from_errno(ENOMEM);
2136 snprintf(path, len, "%s/%s", result, filename);
2137 pwmd_free(result);
2139 if (access(path, R_OK) == -1) {
2140 if (errno == ENOENT) {
2141 pwmd_free(path);
2142 goto gotpassword;
2146 pwmd_free(path);
2149 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
2151 if (gpg_err_code(rc) == GPG_ERR_ENOENT)
2152 goto gotpassword;
2154 if (rc && rc != GPG_ERR_NOT_FOUND)
2155 return rc;
2157 if (rc == GPG_ERR_NOT_FOUND) {
2158 if (pwm->password) {
2159 password = pwm->password;
2160 goto gotpassword;
2163 if (pwm->passfunc) {
2164 rc = pwm->passfunc(pwm->passdata, &password);
2166 if (rc)
2167 return rc;
2169 goto gotpassword;
2173 #ifdef WITH_PINENTRY
2174 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2175 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
2177 if (rc)
2178 return rc;
2180 if (!pwm->filename)
2181 pwm->filename = pwmd_strdup(filename);
2183 if (!pwm->filename)
2184 return gpg_error_from_errno(ENOMEM);
2186 /* Get the passphrase using the LOCAL pinentry. */
2187 if (nb) {
2188 int p[2];
2189 pid_t pid;
2190 pwmd_nb_status_t pw;
2192 if (pipe(p) == -1)
2193 return gpg_error_from_syserror();
2195 #ifdef WITH_LIBPTH
2196 pid = pth_fork();
2197 #else
2198 pid = fork();
2199 #endif
2201 switch (pid) {
2202 case 0:
2203 close(p[0]);
2204 pw.fd = p[0];
2206 if (pwm->pinentry_timeout != 0) {
2207 gpwm = pwm;
2208 gtimeout = abs(pwm->pinentry_timeout);
2209 gelapsed = 0;
2212 pw.error = getpin(pwm, &password, PINENTRY_OPEN);
2214 if (gtimeout && gelapsed >= gtimeout)
2215 pw.error = GPG_ERR_TIMEOUT;
2217 if (!pw.error)
2218 snprintf(pw.password, sizeof(pw.password), "%s",
2219 password);
2221 pinentry_disconnect(pwm);
2222 #ifdef WITH_LIBPTH
2223 pth_write(p[1], &pw, sizeof(pw));
2224 #else
2225 write(p[1], &pw, sizeof(pw));
2226 #endif
2227 memset(&pw, 0, sizeof(pw));
2228 close(p[1]);
2229 _exit(0);
2230 break;
2231 case -1:
2232 rc = gpg_error_from_syserror();
2233 close(p[0]);
2234 close(p[1]);
2235 return rc;
2236 default:
2237 break;
2240 close(p[1]);
2241 pwm->nb_fd = p[0];
2242 pwm->nb_pid = pid;
2243 return 0;
2246 if (pwm->pinentry_timeout != 0) {
2247 gpwm = pwm;
2248 gtimeout = abs(pwm->pinentry_timeout);
2249 gelapsed = 0;
2252 rc = getpin(pwm, &password, PINENTRY_OPEN);
2254 /* Don't timeout when an invalid passphrase was entered. */
2255 gtimeout = 0;
2257 if (rc)
2258 return rc;
2260 #endif
2262 gotpassword:
2263 pwm->state = ASYNC_DONE;
2265 if (!local_pinentry && !pwm->tcp_conn) {
2266 rc = send_pinentry_options(pwm);
2268 if (rc)
2269 return rc;
2272 rc = do_open_command(pwm, filename, password);
2275 * Keep the user defined password set with pwmd_setopt(). The password may
2276 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
2278 if (!pwm->passfunc && password && password != pwm->password)
2279 pwmd_free(password);
2281 if (rc == GPG_ERR_INV_PASSPHRASE && !pwm->tcp_conn) {
2282 if (pin_try-- > 0 && !nb) {
2284 #ifdef WITH_PINENTRY
2285 if (local_pinentry)
2286 rc = getpin(pwm, &password, PINENTRY_OPEN_FAILED);
2287 else
2288 #endif
2289 rc = pwmd_command(pwm, &result, "OPTION TITLE=%s",
2290 N_("Invalid passphrase, please try again."));
2292 if (rc)
2293 return rc;
2295 goto gotpassword;
2298 #ifdef WITH_PINENTRY
2299 if (nb)
2300 pinentry_disconnect(pwm);
2301 #endif
2303 return rc;
2306 if (!rc) {
2307 if (pwm->filename)
2308 pwmd_free(pwm->filename);
2310 pwm->filename = pwmd_strdup(filename);
2313 return rc;
2316 gpg_error_t pwmd_open2(pwm_t *pwm, const char *filename)
2318 #ifndef WITH_PINENTRY
2319 return GPG_ERR_NOT_IMPLEMENTED;
2320 #else
2321 return do_pwmd_open(pwm, filename, 0, 1);
2322 #endif
2325 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
2327 return do_pwmd_open(pwm, filename, 0, 0);
2330 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
2332 #ifndef WITH_PINENTRY
2333 return GPG_ERR_NOT_IMPLEMENTED;
2334 #else
2335 gpg_error_t rc;
2337 if (!pwm || !filename)
2338 return GPG_ERR_INV_ARG;
2340 if (!pwm->ctx)
2341 return GPG_ERR_INV_STATE;
2343 if (pwm->cmd != ASYNC_CMD_OPEN2)
2344 pwm->pin_try = 0;
2346 pwm->cmd = ASYNC_CMD_OPEN2;
2347 pwm->state = ASYNC_PROCESS;
2348 rc = do_pwmd_open(pwm, filename, 1, 1);
2350 if (rc)
2351 reset_async(pwm, 1);
2353 return rc;
2354 #endif
2357 #ifdef WITH_PINENTRY
2358 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
2360 int confirm = 0;
2361 gpg_error_t error;
2362 char *result = NULL;
2364 again:
2365 error = getpin(pwm, &result, confirm ? PINENTRY_SAVE_CONFIRM : PINENTRY_SAVE);
2367 if (error) {
2368 if (pwm->pctx)
2369 pinentry_disconnect(pwm);
2371 if (*password)
2372 pwmd_free(*password);
2374 return error;
2377 if (!confirm++) {
2378 *password = result;
2379 goto again;
2382 if (strcmp(*password, result)) {
2383 pwmd_free(*password);
2384 pwmd_free(result);
2385 confirm = 0;
2386 *password = NULL;
2387 goto again;
2390 pwmd_free(result);
2391 pinentry_disconnect(pwm);
2392 return 0;
2394 #endif
2396 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
2398 char *buf;
2399 gpg_error_t error;
2400 char *result = NULL;
2402 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2404 if (!buf)
2405 return gpg_error_from_errno(ENOMEM);
2407 snprintf(buf, ASSUAN_LINELENGTH, "SAVE %s", password ? password : "");
2408 error = send_command(pwm, &result, buf);
2409 pwmd_free(buf);
2411 if (error && result)
2412 pwmd_free(result);
2414 return error;
2417 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb, int local_pinentry)
2419 char *result = NULL;
2420 char *password = NULL;
2421 gpg_error_t rc;
2423 if (!pwm)
2424 return GPG_ERR_INV_ARG;
2426 if (!pwm->ctx)
2427 return GPG_ERR_INV_STATE;
2429 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
2431 if (rc && rc != GPG_ERR_NOT_FOUND)
2432 return rc;
2434 if (rc == GPG_ERR_NOT_FOUND) {
2435 if (pwm->password) {
2436 password = pwm->password;
2437 goto gotpassword;
2440 if (pwm->passfunc) {
2441 rc = pwm->passfunc(pwm->passdata, &password);
2443 if (rc)
2444 return rc;
2446 goto gotpassword;
2450 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2451 #ifdef WITH_PINENTRY
2452 /* Get the password using the LOCAL pinentry. */
2453 if (nb) {
2454 int p[2];
2455 pid_t pid;
2456 pwmd_nb_status_t pw;
2458 if (pipe(p) == -1)
2459 return gpg_error_from_syserror();
2461 #ifdef WITH_LIBPTH
2462 pid = pth_fork();
2463 #else
2464 pid = fork();
2465 #endif
2467 switch (pid) {
2468 case 0:
2469 close(p[0]);
2470 pw.fd = p[0];
2471 password = NULL;
2472 pw.error = do_save_getpin(pwm, &password);
2473 pinentry_disconnect(pwm);
2474 snprintf(pw.password, sizeof(pw.password), "%s",
2475 password);
2476 #ifdef WITH_LIBPTH
2477 pth_write(p[1], &pw, sizeof(pw));
2478 #else
2479 write(p[1], &pw, sizeof(pw));
2480 #endif
2481 memset(&pw, 0, sizeof(pw));
2482 close(p[1]);
2483 _exit(0);
2484 break;
2485 case -1:
2486 rc = gpg_error_from_syserror();
2487 close(p[0]);
2488 close(p[1]);
2489 return rc;
2490 default:
2491 break;
2494 close(p[1]);
2495 pwm->nb_fd = p[0];
2496 pwm->nb_pid = pid;
2497 return 0;
2500 rc = do_save_getpin(pwm, &password);
2502 if (rc)
2503 return rc;
2504 #endif
2506 else
2507 pwm->state = ASYNC_DONE;
2509 gotpassword:
2510 if (!local_pinentry && !pwm->tcp_conn) {
2511 rc = send_pinentry_options(pwm);
2513 if (rc)
2514 return rc;
2517 rc = do_save_command(pwm, password);
2519 if (!pwm->passfunc && password && password != pwm->password)
2520 pwmd_free(password);
2522 return rc;
2525 gpg_error_t pwmd_save_async2(pwm_t *pwm)
2527 #ifndef WITH_PINENTRY
2528 return GPG_ERR_NOT_IMPLEMENTED;
2529 #else
2530 gpg_error_t rc;
2532 if (!pwm)
2533 return GPG_ERR_INV_ARG;
2535 if (!pwm->ctx)
2536 return GPG_ERR_INV_STATE;
2538 pwm->cmd = ASYNC_CMD_SAVE2;
2539 pwm->state = ASYNC_PROCESS;
2540 rc = do_pwmd_save(pwm, 1, 1);
2542 if (rc)
2543 reset_async(pwm, 0);
2545 return rc;
2546 #endif
2549 gpg_error_t pwmd_save2(pwm_t *pwm)
2551 #ifndef WITH_PINENTRY
2552 return GPG_ERR_NOT_IMPLEMENTED;
2553 #else
2554 return do_pwmd_save(pwm, 0, 1);
2555 #endif
2558 gpg_error_t pwmd_save(pwm_t *pwm)
2560 return do_pwmd_save(pwm, 0, 0);
2563 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
2565 va_list ap;
2566 int n = va_arg(ap, int);
2567 char *arg1;
2568 gpg_error_t error = 0;
2570 if (!pwm)
2571 return GPG_ERR_INV_ARG;
2573 va_start(ap, opt);
2575 switch (opt) {
2576 case PWMD_OPTION_STATUS_CB:
2577 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
2578 break;
2579 case PWMD_OPTION_STATUS_DATA:
2580 pwm->status_data = va_arg(ap, void *);
2581 break;
2582 case PWMD_OPTION_PASSPHRASE_CB:
2583 pwm->passfunc = va_arg(ap, pwmd_passphrase_cb_t);
2584 break;
2585 case PWMD_OPTION_PASSPHRASE_DATA:
2586 pwm->passdata = va_arg(ap, void *);
2587 break;
2588 case PWMD_OPTION_PASSPHRASE:
2589 arg1 = va_arg(ap, char *);
2591 if (pwm->password)
2592 pwmd_free(pwm->password);
2594 pwm->password = pwmd_strdup(arg1);
2595 break;
2596 case PWMD_OPTION_PINENTRY_TRIES:
2597 n = va_arg(ap, int);
2599 if (n <= 0) {
2600 va_end(ap);
2601 error = GPG_ERR_INV_VALUE;
2603 else
2604 pwm->pinentry_tries = n;
2605 break;
2606 case PWMD_OPTION_PINENTRY_TIMEOUT:
2607 n = va_arg(ap, int);
2609 if (n < 0) {
2610 va_end(ap);
2611 error = GPG_ERR_INV_VALUE;
2613 else
2614 pwm->pinentry_timeout = n;
2615 break;
2616 case PWMD_OPTION_PINENTRY_PATH:
2617 if (pwm->pinentry_path)
2618 pwmd_free(pwm->pinentry_path);
2620 pwm->pinentry_path = pwmd_strdup(va_arg(ap, char *));
2621 break;
2622 case PWMD_OPTION_PINENTRY_TTY:
2623 if (pwm->pinentry_tty)
2624 pwmd_free(pwm->pinentry_tty);
2626 pwm->pinentry_tty = pwmd_strdup(va_arg(ap, char *));
2627 break;
2628 case PWMD_OPTION_PINENTRY_DISPLAY:
2629 if (pwm->pinentry_display)
2630 pwmd_free(pwm->pinentry_display);
2632 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
2633 break;
2634 case PWMD_OPTION_PINENTRY_TERM:
2635 if (pwm->pinentry_term)
2636 pwmd_free(pwm->pinentry_term);
2638 pwm->pinentry_term = pwmd_strdup(va_arg(ap, char *));
2639 break;
2640 case PWMD_OPTION_PINENTRY_TITLE:
2641 if (pwm->title)
2642 pwmd_free(pwm->title);
2644 pwm->title = percent_escape(va_arg(ap, char *));
2645 break;
2646 case PWMD_OPTION_PINENTRY_PROMPT:
2647 if (pwm->prompt)
2648 pwmd_free(pwm->prompt);
2650 pwm->prompt = percent_escape(va_arg(ap, char *));
2651 break;
2652 case PWMD_OPTION_PINENTRY_DESC:
2653 if (pwm->desc)
2654 pwmd_free(pwm->desc);
2656 pwm->desc = percent_escape(va_arg(ap, char *));
2657 break;
2658 case PWMD_OPTION_PINENTRY_LC_CTYPE:
2659 if (pwm->lcctype)
2660 pwmd_free(pwm->lcctype);
2662 pwm->lcctype = pwmd_strdup(va_arg(ap, char *));
2663 break;
2664 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
2665 if (pwm->lcmessages)
2666 pwmd_free(pwm->lcmessages);
2668 pwm->lcmessages = pwmd_strdup(va_arg(ap, char *));
2669 break;
2670 #ifdef WITH_TCP
2671 case PWMD_OPTION_IP_VERSION:
2672 n = va_arg(ap, int);
2674 switch (n) {
2675 case PWMD_IP_ANY:
2676 case PWMD_IPV4:
2677 case PWMD_IPV6:
2678 pwm->prot = n;
2679 break;
2680 default:
2681 error = GPG_ERR_INV_VALUE;
2682 break;
2685 va_end(ap);
2686 break;
2687 #endif
2688 default:
2689 error = GPG_ERR_NOT_IMPLEMENTED;
2690 break;
2693 va_end(ap);
2694 return error;
2697 gpg_error_t pwmd_get_fds(pwm_t *pwm, pwmd_fd_t *fds, int *n_fds)
2699 int in_total;
2700 int fd = 0;
2701 #ifdef WITH_TCP
2702 int afds[ARES_GETSOCK_MAXNUM];
2703 int got_sock = 0;
2704 int n, i;
2705 #endif
2707 if (!pwm || !fds || !n_fds || *n_fds <= 0)
2708 return GPG_ERR_INV_ARG;
2710 in_total = *n_fds;
2711 #ifdef WITH_TCP
2712 memset(afds, 0, sizeof(int)*ARES_GETSOCK_MAXNUM);
2713 #endif
2714 memset(fds, 0, sizeof(pwmd_fd_t)*in_total);
2715 *n_fds = 0;
2717 switch (pwm->cmd) {
2718 default:
2719 case ASYNC_CMD_NONE:
2720 case ASYNC_CMD_OPEN:
2721 case ASYNC_CMD_SAVE:
2722 #ifdef WITH_PINENTRY
2723 async1:
2724 #endif
2725 if (pwm->fd == -1)
2726 return GPG_ERR_INV_STATE;
2728 (*n_fds)++;
2729 fds[fd].fd = pwm->fd;
2730 fds[fd++].flags = PWMD_FD_READABLE;
2731 return 0;
2732 #ifdef WITH_PINENTRY
2733 case ASYNC_CMD_OPEN2:
2734 case ASYNC_CMD_SAVE2:
2735 /* The command has already completed (cached or new). */
2736 if (pwm->state == ASYNC_DONE)
2737 return 0;
2739 if (pwm->nb_fd == -1)
2740 return GPG_ERR_INV_STATE;
2742 (*n_fds)++;
2743 fds[fd].fd = pwm->nb_fd;
2744 fds[fd++].flags = PWMD_FD_READABLE;
2745 goto async1;
2746 #endif
2747 #ifdef WITH_TCP
2748 case ASYNC_CMD_DNS:
2749 if (!pwm->tcp_conn || !pwm->tcp_conn->chan)
2750 return GPG_ERR_INV_STATE;
2752 n = ares_getsock(pwm->tcp_conn->chan, afds, ARES_GETSOCK_MAXNUM);
2754 for (i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
2755 got_sock = 0;
2757 if (fd > in_total) {
2758 *n_fds = fd;
2759 return GPG_ERR_ERANGE;
2762 if (ARES_GETSOCK_READABLE(n, i)) {
2763 got_sock++;
2764 fds[fd].flags |= PWMD_FD_READABLE;
2767 if (ARES_GETSOCK_WRITABLE(n, i)) {
2768 got_sock++;
2769 fds[fd].flags |= PWMD_FD_WRITABLE;
2772 if (got_sock)
2773 fds[fd++].fd = afds[i];
2776 *n_fds = fd;
2777 return 0;
2778 case ASYNC_CMD_CONNECT:
2779 case ASYNC_CMD_HOSTKEY:
2780 if (!pwm->tcp_conn || pwm->tcp_conn->fd == -1)
2781 return GPG_ERR_INV_STATE;
2783 (*n_fds)++;
2784 fds[fd].fd = pwm->tcp_conn->fd;
2785 fds[fd++].flags = PWMD_FD_READABLE;
2786 return 0;
2787 #endif
2790 return GPG_ERR_INV_STATE;
2793 pwm_t *pwmd_new(const char *name)
2795 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
2797 if (!h)
2798 return NULL;
2800 if (name) {
2801 h->name = pwmd_strdup(name);
2803 if (!h->name) {
2804 pwmd_free(h);
2805 return NULL;
2809 h->fd = -1;
2810 #ifdef WITH_PINENTRY
2811 h->nb_fd = -1;
2812 #endif
2813 h->pinentry_timeout = -30;
2814 h->pinentry_tries = 3;
2815 #ifdef WITH_TCP
2816 h->prot = PWMD_IP_ANY;
2817 #endif
2818 return h;
2821 void pwmd_free(void *ptr)
2823 xfree(ptr);
2826 void *pwmd_malloc(size_t size)
2828 return xmalloc(size);
2831 void *pwmd_calloc(size_t nmemb, size_t size)
2833 return xcalloc(nmemb, size);
2836 void *pwmd_realloc(void *ptr, size_t size)
2838 return xrealloc(ptr, size);
2841 char *pwmd_strdup(const char *str)
2843 return xstrdup(str);
2846 char *pwmd_strdup_printf(const char *fmt, ...)
2848 va_list ap, ap2;
2849 int len;
2850 char *buf;
2852 if (!fmt)
2853 return NULL;
2855 va_start(ap, fmt);
2856 va_copy(ap2, ap);
2857 len = vsnprintf(NULL, 0, fmt, ap);
2858 va_end(ap);
2859 buf = pwmd_malloc(++len);
2861 if (buf)
2862 vsnprintf(buf, len, fmt, ap2);
2864 va_end(ap2);
2865 return buf;