Show the client name in the pinentry dialog.
[libpwmd.git] / src / libpwmd.c
blobda71ca4b83d145d83e9ba542cf7bd4c7f18dbff2
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,
346 const char *known_hosts, int get)
348 pwmd_tcp_conn_t *conn;
349 gpg_error_t rc = 0;
350 char *pwbuf = NULL;
352 if (get) {
353 if (!host || !*host)
354 return GPG_ERR_INV_ARG;
356 else {
357 if (!host || !*host || !identity || !*identity || !known_hosts ||
358 !*known_hosts)
359 return GPG_ERR_INV_ARG;
362 conn = pwmd_calloc(1, sizeof(pwmd_tcp_conn_t));
364 if (!conn)
365 return gpg_error_from_errno(ENOMEM);
367 conn->port = port == -1 ? 22 : port;
368 conn->host = pwmd_strdup(host);
370 if (!conn->host) {
371 rc = gpg_error_from_errno(ENOMEM);
372 goto fail;
375 if (!get) {
376 struct passwd pw;
378 pwbuf = _getpwuid(&pw);
380 if (!pwbuf) {
381 rc = gpg_error_from_errno(errno);
382 goto fail;
385 conn->username = pwmd_strdup(user ? user : pw.pw_name);
387 if (!conn->username) {
388 rc = gpg_error_from_errno(ENOMEM);
389 goto fail;
392 conn->identity = expand_homedir((char *)identity, &pw);
394 if (!conn->identity) {
395 rc = gpg_error_from_errno(ENOMEM);
396 goto fail;
399 conn->identity_pub = pwmd_malloc(strlen(conn->identity)+5);
401 if (!conn->identity_pub) {
402 rc = gpg_error_from_errno(ENOMEM);
403 goto fail;
406 sprintf(conn->identity_pub, "%s.pub", conn->identity);
407 conn->known_hosts = expand_homedir((char *)known_hosts, &pw);
409 if (!conn->known_hosts) {
410 rc = gpg_error_from_errno(ENOMEM);
411 goto fail;
414 pwmd_free(pwbuf);
417 *dst = conn;
418 return 0;
420 fail:
421 if (pwbuf)
422 pwmd_free(pwbuf);
424 free_tcp_conn(conn);
425 return rc;
428 static gpg_error_t do_connect(pwm_t *pwm, int prot, void *addr)
430 struct sockaddr_in their_addr;
432 pwm->tcp_conn->fd = socket(prot, SOCK_STREAM, 0);
434 if (pwm->tcp_conn->fd == -1)
435 return gpg_error_from_syserror();
437 if (pwm->tcp_conn->async)
438 fcntl(pwm->tcp_conn->fd, F_SETFL, O_NONBLOCK);
440 pwm->cmd = ASYNC_CMD_CONNECT;
441 their_addr.sin_family = prot;
442 their_addr.sin_port = htons(pwm->tcp_conn->port);
443 their_addr.sin_addr = *((struct in_addr *)addr);
444 memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
446 #ifdef WITH_LIBPTH
447 if (pth_connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
448 sizeof(their_addr)) == -1)
449 #else
450 if (connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
451 sizeof(their_addr)) == -1)
452 #endif
453 return gpg_error_from_syserror();
455 return 0;
458 static gpg_error_t ares_error_to_pwmd(int status)
460 if (status != ARES_SUCCESS)
461 warnx("%s", ares_strerror(status));
463 switch (status) {
464 case ARES_ENODATA:
465 case ARES_EFORMERR:
466 case ARES_ENOTFOUND:
467 return GPG_ERR_UNKNOWN_HOST;
468 case ARES_ESERVFAIL:
469 return GPG_ERR_EHOSTDOWN;
470 case ARES_ETIMEOUT:
471 return GPG_ERR_TIMEOUT;
472 case ARES_ENOMEM:
473 return gpg_error_from_errno(ENOMEM);
474 case ARES_ECONNREFUSED:
475 return GPG_ERR_ECONNREFUSED;
476 default:
477 /* FIXME ??? */
478 return GPG_ERR_EHOSTUNREACH;
481 return ARES_SUCCESS;
484 static void dns_resolve_cb(void *arg, int status, int timeouts,
485 unsigned char *abuf, int alen)
487 pwm_t *pwm = arg;
488 int rc;
489 struct hostent *he;
491 if (status == ARES_EDESTRUCTION)
492 return;
494 if (status != ARES_SUCCESS) {
495 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
496 return;
499 /* Check for an IPv6 address first. */
500 if (pwm->prot == PWMD_IP_ANY || pwm->prot == PWMD_IPV6)
501 rc = ares_parse_aaaa_reply(abuf, alen, &he, NULL, NULL);
502 else
503 rc = ares_parse_a_reply(abuf, alen, &he, NULL, NULL);
505 if (rc != ARES_SUCCESS) {
506 if (pwm->prot != PWMD_IP_ANY || rc != ARES_ENODATA) {
507 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
508 return;
511 rc = ares_parse_a_reply(abuf, alen, &he, NULL, NULL);
513 if (rc != ARES_SUCCESS) {
514 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
515 return;
519 pwm->tcp_conn->he = he;
520 pwm->tcp_conn->rc = do_connect(pwm, he->h_addrtype, he->h_addr);
523 static gpg_error_t _do_pwmd_tcp_connect_async(pwm_t *pwm, const char *host,
524 int port, const char *identity, const char *user,
525 const char *known_hosts, pwmd_async_cmd_t which)
527 pwmd_tcp_conn_t *conn;
528 gpg_error_t rc;
530 if (!pwm)
531 return GPG_ERR_INV_ARG;
533 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts,
534 which == ASYNC_CMD_HOSTKEY ? 1 : 0);
536 if (rc)
537 return rc;
539 conn->async = 1;
540 pwm->tcp_conn = conn;
541 pwm->tcp_conn->cmd = which;
543 if (pwm->tcp_conn->cmd == ASYNC_CMD_HOSTKEY)
544 pwm->tcp_conn->get_only = 1;
546 pwm->cmd = ASYNC_CMD_DNS;
547 pwm->state = ASYNC_PROCESS;
548 ares_init(&pwm->tcp_conn->chan);
549 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
550 dns_resolve_cb, pwm);
551 return 0;
554 gpg_error_t pwmd_ssh_connect_async(pwm_t *pwm, const char *host, int port,
555 const char *identity, const char *user, const char *known_hosts)
557 return _do_pwmd_tcp_connect_async(pwm, host, port, identity, user,
558 known_hosts, ASYNC_CMD_CONNECT);
561 static void *_ssh_malloc(size_t size, void **data)
563 return pwmd_malloc(size);
566 static void _ssh_free(void *ptr, void **data)
568 pwmd_free(ptr);
571 static void *_ssh_realloc(void *ptr, size_t size, void **data)
573 return pwmd_realloc(ptr, size);
576 static char *to_hex(const char *str, size_t slen)
578 int i;
579 char *buf = pwmd_malloc(slen*2+1);
581 if (!buf)
582 return NULL;
584 for (i = 0, buf[0] = 0; i < slen; i++) {
585 char tmp[3];
587 sprintf(tmp, "%02x", (unsigned char)str[i]);
588 strcat(buf, tmp);
591 return buf;
594 static int verify_host_key(pwm_t *pwm)
596 FILE *fp = fopen(pwm->tcp_conn->known_hosts, "r");
597 char *buf, *p;
599 if (!fp)
600 return 1;
602 buf = pwmd_malloc(LINE_MAX);
604 if (!buf)
605 goto fail;
607 while ((p = fgets(buf, LINE_MAX, fp))) {
608 if (*p == '#' || isspace(*p))
609 continue;
611 if (p[strlen(p)-1] == '\n')
612 p[strlen(p)-1] = 0;
614 if (!strcmp(buf, pwm->tcp_conn->hostkey))
615 goto done;
618 fail:
619 if (buf)
620 pwmd_free(buf);
622 fclose(fp);
623 return 1;
625 done:
626 pwmd_free(buf);
627 fclose(fp);
628 return 0;
631 static gpg_error_t authenticate_ssh(pwm_t *pwm)
633 const char *fp = libssh2_hostkey_hash(pwm->tcp_conn->session,
634 LIBSSH2_HOSTKEY_HASH_SHA1);
635 char *userauth;
637 pwm->tcp_conn->hostkey = to_hex(fp, 20);
639 if (!pwm->tcp_conn->hostkey)
640 return gpg_error_from_errno(ENOMEM);
642 if (pwm->tcp_conn->get_only)
643 return 0;
645 if (!fp || verify_host_key(pwm))
646 return GPG_ERR_BAD_CERT;
648 userauth = libssh2_userauth_list(pwm->tcp_conn->session,
649 pwm->tcp_conn->username, strlen(pwm->tcp_conn->username));
651 if (!userauth || !strstr(userauth, "publickey"))
652 return GPG_ERR_BAD_PIN_METHOD;
654 if (libssh2_userauth_publickey_fromfile(pwm->tcp_conn->session,
655 pwm->tcp_conn->username, pwm->tcp_conn->identity_pub,
656 pwm->tcp_conn->identity, NULL))
657 return GPG_ERR_BAD_SECKEY;
659 return 0;
662 static gpg_error_t setup_tcp_session(pwm_t *pwm)
664 assuan_context_t ctx;
665 struct assuan_io_hooks io_hooks = {read_hook, write_hook};
666 gpg_error_t rc;
668 pwm->tcp_conn->session = libssh2_session_init_ex(_ssh_malloc, _ssh_free,
669 _ssh_realloc, NULL);
671 if (!pwm->tcp_conn->session) {
672 rc = gpg_error_from_errno(ENOMEM);
673 goto fail;
676 if (libssh2_session_startup(pwm->tcp_conn->session, pwm->tcp_conn->fd)) {
677 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
678 goto fail;
681 rc = authenticate_ssh(pwm);
683 if (rc)
684 goto fail;
686 /* pwmd_get_hostkey(). */
687 if (pwm->tcp_conn->get_only) {
688 pwm->result = pwmd_strdup(pwm->tcp_conn->hostkey);
690 if (!pwm->result) {
691 rc = gpg_error_from_errno(ENOMEM);
692 goto fail;
695 return 0;
698 pwm->tcp_conn->channel = libssh2_channel_open_session(pwm->tcp_conn->session);
700 if (!pwm->tcp_conn->channel) {
701 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
702 goto fail;
705 if (libssh2_channel_shell(pwm->tcp_conn->channel)) {
706 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
707 goto fail;
710 assuan_set_io_hooks(&io_hooks);
711 rc = assuan_socket_connect_fd(&ctx, pwm->tcp_conn->fd, 0, pwm);
713 if (rc)
714 goto fail;
716 assuan_set_finish_handler(ctx, _ssh_assuan_deinit);
717 pwm->ctx = ctx;
718 rc = _socket_connect_finalize(pwm);
720 if (rc)
721 goto fail;
723 return 0;
725 fail:
726 free_tcp_conn(pwm->tcp_conn);
727 pwm->tcp_conn = NULL;
728 return rc;
731 static gpg_error_t _do_pwmd_tcp_connect(pwm_t *pwm, const char *host, int port,
732 const char *identity, const char *user, const char *known_hosts, int get)
734 pwmd_tcp_conn_t *conn;
735 gpg_error_t rc;
737 if (!pwm)
738 return GPG_ERR_INV_ARG;
740 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts, get);
742 if (rc)
743 return rc;
745 pwm->tcp_conn = conn;
746 pwm->tcp_conn->get_only = get;
747 pwm->cmd = ASYNC_CMD_DNS;
748 ares_init(&pwm->tcp_conn->chan);
749 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
750 dns_resolve_cb, pwm);
752 /* dns_resolve_cb() may have already been called. */
753 if (pwm->tcp_conn->rc) {
754 rc = pwm->tcp_conn->rc;
755 goto fail;
759 * Fake a blocking DNS lookup. libcares does a better job than
760 * getaddrinfo().
762 do {
763 fd_set rfds, wfds;
764 int n;
765 struct timeval tv;
767 FD_ZERO(&rfds);
768 FD_ZERO(&wfds);
769 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
770 ares_timeout(pwm->tcp_conn->chan, NULL, &tv);
771 #ifdef WITH_LIBPTH
772 n = pth_select(n, &rfds, &wfds, NULL, &tv);
773 #else
774 n = select(n, &rfds, &wfds, NULL, &tv);
775 #endif
777 if (n == -1) {
778 rc = gpg_error_from_syserror();
779 goto fail;
781 else if (n == 0) {
782 rc = GPG_ERR_TIMEOUT;
783 goto fail;
786 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
788 if (pwm->tcp_conn->rc)
789 break;
790 } while (pwm->cmd == ASYNC_CMD_DNS);
792 if (pwm->tcp_conn->rc) {
793 rc = pwm->tcp_conn->rc;
794 goto fail;
797 return setup_tcp_session(pwm);
799 fail:
800 return rc;
803 gpg_error_t pwmd_ssh_connect(pwm_t *pwm, const char *host, int port,
804 const char *identity, const char *user, const char *known_hosts)
806 return _do_pwmd_tcp_connect(pwm, host, port, identity, user, known_hosts, 0);
809 gpg_error_t pwmd_get_hostkey(pwm_t *pwm, const char *host, int port,
810 char **result)
812 char *hostkey;
813 gpg_error_t rc;
815 rc = _do_pwmd_tcp_connect(pwm, host, port, NULL, NULL, NULL, 1);
817 if (rc)
818 return rc;
820 hostkey = pwmd_strdup(pwm->tcp_conn->hostkey);
822 if (!hostkey)
823 rc = gpg_error_from_errno(ENOMEM);
825 *result = hostkey;
826 return rc;
829 gpg_error_t pwmd_get_hostkey_async(pwm_t *pwm, const char *host, int port)
831 return _do_pwmd_tcp_connect_async(pwm, host, port, NULL, NULL, NULL,
832 ASYNC_CMD_HOSTKEY);
836 * ssh://[username@]hostname[:port],identity,known_hosts
838 * Any missing parameters are checked for in init_tcp_conn().
840 static int parse_ssh_url(char *str, char **host, int *port, char **user,
841 char **identity, char **known_hosts)
843 char *p;
844 char *t;
845 int len;
847 *host = *user = *identity = *known_hosts = NULL;
848 *port = -1;
849 p = strrchr(str, '@');
851 if (p) {
852 len = strlen(str)-strlen(p)+1;
853 *user = pwmd_malloc(len);
854 snprintf(*user, len, "%s", str);
855 p++;
857 else
858 p = str;
860 t = strchr(p, ':');
862 if (t) {
863 len = strlen(p)-strlen(t)+1;
864 *host = pwmd_malloc(len);
865 snprintf(*host, len, "%s", p);
866 t++;
867 *port = atoi(t);
869 while (*t && isdigit(*t))
870 t++;
872 p = t;
875 t = strchr(p, ',');
877 if (t) {
878 char *t2;
880 if (!*host) {
881 len = strlen(p)-strlen(t)+1;
882 *host = pwmd_malloc(len);
883 snprintf(*host, len, "%s", p);
886 t++;
887 t2 = strchr(t, ',');
889 if (t2)
890 len = strlen(t)-strlen(t2)+1;
891 else
892 len = strlen(t)+1;
894 *identity = pwmd_malloc(len);
895 snprintf(*identity, len, "%s", t);
897 if (t2) {
898 t2++;
899 t += len+1;
900 len = strlen(t2)+1;
901 *known_hosts = pwmd_malloc(len);
902 snprintf(*known_hosts, len, "%s", t2);
905 else {
906 if (!*host) {
907 len = strlen(p)+1;
908 *host = pwmd_malloc(len);
909 snprintf(*host, len, "%s", p);
913 return 0;
915 #endif
917 static gpg_error_t _pwmd_connect_url(pwm_t *pwm, const char *url, int async)
919 char *p = (char *)url;
920 gpg_error_t rc;
922 if (!pwm || !url)
923 return GPG_ERR_INV_ARG;
925 if (!strncmp(p, "socket://", 9)) {
926 p += 9;
927 rc = pwmd_connect(pwm, p);
928 pwm->state = ASYNC_DONE;
929 return rc;
931 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
932 !strncmp(p, "ssh4://", 7)) {
933 #ifndef WITH_TCP
934 return GPG_ERR_NOT_IMPLEMENTED;
935 #else
936 char *host = NULL;
937 int port = -1;
938 char *identity = NULL;
939 char *known_hosts = NULL;
940 char *username = NULL;
942 if (!strncmp(p, "ssh6://", 7)) {
943 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV6);
944 p += 7;
946 else if (!strncmp(p, "ssh4://", 7)) {
947 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV4);
948 p += 7;
950 else {
951 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IP_ANY);
952 p += 6;
955 if (rc)
956 return rc;
958 rc = parse_ssh_url(p, &host, &port, &username, &identity,
959 &known_hosts);
961 if (rc)
962 return rc;
964 if (async)
965 rc = pwmd_ssh_connect_async(pwm, host, port, identity, username,
966 known_hosts);
967 else
968 rc = pwmd_ssh_connect(pwm, host, port, identity, username,
969 known_hosts);
971 if (host)
972 pwmd_free(host);
974 if (username)
975 pwmd_free(username);
977 if (identity)
978 pwmd_free(identity);
980 if (known_hosts)
981 pwmd_free(known_hosts);
983 return rc;
984 #endif
987 return GPG_ERR_UNSUPPORTED_PROTOCOL;
990 gpg_error_t pwmd_connect_url(pwm_t *pwm, const char *url)
992 return _pwmd_connect_url(pwm, url, 0);
995 gpg_error_t pwmd_connect_url_async(pwm_t *pwm, const char *url)
997 return _pwmd_connect_url(pwm, url, 1);
1000 static char *expand_homedir(char *str, struct passwd *pw)
1002 char *p = str;
1003 char *pwbuf = NULL;
1004 char *result;
1006 if (*p != '~' || *(p+1) != '/')
1007 return pwmd_strdup(p);
1009 if (!pw) {
1010 struct passwd t;
1012 pwbuf = _getpwuid(&t);
1014 if (!pwbuf)
1015 return NULL;
1017 pw = &t;
1020 p += 2;
1021 result = pwmd_strdup_printf("%s/%s", pw->pw_dir, p);
1023 if (pwbuf)
1024 pwmd_free(pwbuf);
1026 return result;
1029 gpg_error_t pwmd_connect(pwm_t *pwm, const char *path)
1031 char *socketpath = NULL;
1032 assuan_context_t ctx;
1033 struct passwd pw;
1034 char *pwbuf;
1035 gpg_error_t rc;
1037 if (!pwm)
1038 return GPG_ERR_INV_ARG;
1040 pwbuf = _getpwuid(&pw);
1042 if (!pwbuf)
1043 return gpg_error_from_errno(errno);
1045 if (!path || !*path) {
1046 socketpath = (char *)pwmd_malloc(strlen(pw.pw_dir) + strlen("/.pwmd/socket") + 1);
1047 sprintf(socketpath, "%s/.pwmd/socket", pw.pw_dir);
1049 else {
1050 socketpath = expand_homedir((char *)path, &pw);
1052 if (!socketpath) {
1053 pwmd_free(pwbuf);
1054 return gpg_error_from_errno(ENOMEM);
1058 pwmd_free(pwbuf);
1059 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
1060 pwmd_free(socketpath);
1062 if (rc)
1063 return rc;
1065 pwm->ctx = ctx;
1066 return _socket_connect_finalize(pwm);
1069 void pwmd_close(pwm_t *pwm)
1071 if (!pwm)
1072 return;
1074 if (pwm->ctx)
1075 assuan_disconnect(pwm->ctx);
1077 if (pwm->password)
1078 pwmd_free(pwm->password);
1080 if (pwm->title)
1081 pwmd_free(pwm->title);
1083 if (pwm->desc)
1084 pwmd_free(pwm->desc);
1086 if (pwm->prompt)
1087 pwmd_free(pwm->prompt);
1089 if (pwm->pinentry_tty)
1090 pwmd_free(pwm->pinentry_tty);
1092 if (pwm->pinentry_display)
1093 pwmd_free(pwm->pinentry_display);
1095 if (pwm->pinentry_term)
1096 pwmd_free(pwm->pinentry_term);
1098 if (pwm->lcctype)
1099 pwmd_free(pwm->lcctype);
1101 if (pwm->lcmessages)
1102 pwmd_free(pwm->lcmessages);
1104 if (pwm->filename)
1105 pwmd_free(pwm->filename);
1107 if (pwm->name)
1108 pwmd_free(pwm->name);
1110 #ifdef WITH_TCP
1111 if (pwm->tcp_conn)
1112 free_tcp_conn(pwm->tcp_conn);
1113 #endif
1115 pwmd_free(pwm);
1118 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
1120 membuf_t *mem = (membuf_t *)data;
1121 void *p;
1123 if (!buffer)
1124 return 0;
1126 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
1127 return 1;
1129 mem->buf = p;
1130 memcpy((char *)mem->buf + mem->len, buffer, len);
1131 mem->len += len;
1132 return 0;
1135 static int _inquire_cb(void *data, const char *keyword)
1137 pwm_t *pwm = (pwm_t *)data;
1138 gpg_error_t rc = 0;
1139 int flags = fcntl(pwm->fd, F_GETFL);
1141 /* Shouldn't get this far without a callback. */
1142 if (!pwm->inquire_func)
1143 return GPG_ERR_INV_ARG;
1146 * Since the socket file descriptor is probably set to non-blocking, set to
1147 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixed when
1148 * asynchronous INQUIRE is supported by either libassuan or a later
1149 * libpwmd.
1151 fcntl(pwm->fd, F_SETFL, 0);
1153 for (;;) {
1154 char *result = NULL;
1155 size_t len;
1156 gpg_error_t arc;
1158 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
1159 rc = gpg_err_code(rc);
1161 if (rc == GPG_ERR_EOF || !rc) {
1162 if (len <= 0 || !result) {
1163 rc = 0;
1164 break;
1167 arc = assuan_send_data(pwm->ctx, result, len);
1169 if (rc == GPG_ERR_EOF) {
1170 rc = arc;
1171 break;
1174 rc = arc;
1176 else if (rc)
1177 break;
1180 fcntl(pwm->fd, F_SETFL, flags);
1181 return rc;
1184 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, ...)
1186 char *buf;
1187 gpg_error_t rc;
1188 va_list ap;
1190 if (pwm->state == ASYNC_DONE)
1191 pwm->state = ASYNC_INIT;
1193 if (pwm->state != ASYNC_INIT)
1194 return GPG_ERR_INV_STATE;
1196 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
1198 if (!buf)
1199 return gpg_error_from_errno(ENOMEM);
1201 va_start(ap, cmd);
1202 vsnprintf(buf, ASSUAN_LINELENGTH, cmd, ap);
1203 va_end(ap);
1204 rc = assuan_write_line(pwm->ctx, buf);
1205 pwmd_free(buf);
1207 if (!rc)
1208 pwm->state = ASYNC_PROCESS;
1210 return rc;
1213 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
1215 if (!pwm || !filename)
1216 return GPG_ERR_INV_ARG;
1218 if (!pwm->ctx)
1219 return GPG_ERR_INV_STATE;
1221 if (pwm->cmd != ASYNC_CMD_OPEN) {
1222 gpg_error_t rc;
1224 pwm->pin_try = 0;
1226 if (pwm->filename)
1227 pwmd_free(pwm->filename);
1229 pwm->filename = pwmd_strdup(filename);
1231 rc = send_pinentry_options(pwm);
1233 if (rc)
1234 return rc;
1237 pwm->cmd = ASYNC_CMD_OPEN;
1238 return do_nb_command(pwm, "OPEN %s %s", filename,
1239 pwm->password ? pwm->password : "");
1242 gpg_error_t pwmd_save_async(pwm_t *pwm)
1244 gpg_error_t rc;
1246 if (!pwm)
1247 return GPG_ERR_INV_ARG;
1249 if (!pwm->ctx)
1250 return GPG_ERR_INV_STATE;
1252 rc = send_pinentry_options(pwm);
1254 if (rc)
1255 return rc;
1257 pwm->cmd = ASYNC_CMD_SAVE;
1258 return do_nb_command(pwm, "SAVE %s", pwm->password ? pwm->password : "");
1261 static gpg_error_t parse_assuan_line(pwm_t *pwm)
1263 gpg_error_t rc;
1264 char *line;
1265 size_t len;
1267 rc = assuan_read_line(pwm->ctx, &line, &len);
1269 if (!rc) {
1270 if (line[0] == 'O' && line[1] == 'K' &&
1271 (line[2] == 0 || line[2] == ' ')) {
1272 pwm->state = ASYNC_DONE;
1274 else if (line[0] == '#') {
1276 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
1277 if (pwm->status_func) {
1278 pwm->status_func(pwm->status_data,
1279 line[1] == 0 ? line+1 : line+2);
1282 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
1283 (line[3] == 0 || line[3] == ' ')) {
1284 line += 4;
1285 rc = atoi(line);
1286 pwm->state = ASYNC_DONE;
1290 return rc;
1293 gpg_error_t pwmd_pending_line(pwm_t *pwm)
1295 if (!pwm)
1296 return GPG_ERR_INV_ARG;
1298 if (!pwm->ctx)
1299 return GPG_ERR_INV_STATE;
1301 return assuan_pending_line(pwm->ctx) ? 0 : GPG_ERR_NO_DATA;
1304 static pwmd_async_t reset_async(pwm_t *pwm, int done)
1306 pwm->state = ASYNC_INIT;
1307 pwm->cmd = ASYNC_CMD_NONE;
1309 #ifdef WITH_PINENTRY
1310 if (pwm->nb_fd != -1) {
1311 close(pwm->nb_fd);
1312 pwm->nb_fd = -1;
1314 #endif
1315 #ifdef WITH_TCP
1316 if (done && pwm->tcp_conn) {
1317 free_tcp_conn(pwm->tcp_conn);
1318 pwm->tcp_conn = NULL;
1320 #endif
1322 return ASYNC_DONE;
1325 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
1327 fd_set fds;
1328 int n;
1329 struct timeval tv = {0, 0};
1331 if (result)
1332 *result = NULL;
1334 if (!rc)
1335 return GPG_ERR_INV_ARG;
1337 *rc = 0;
1339 if (!pwm) {
1340 *rc = GPG_ERR_INV_ARG;
1341 return ASYNC_DONE;
1343 else if (!pwm->ctx) {
1344 switch (pwm->cmd) {
1345 default:
1346 *rc = GPG_ERR_INV_STATE;
1347 return ASYNC_DONE;
1348 #ifdef WITH_TCP
1349 case ASYNC_CMD_DNS:
1350 case ASYNC_CMD_CONNECT:
1351 case ASYNC_CMD_HOSTKEY:
1352 break;
1353 #endif
1357 /* When not in a command, this will let libassuan process status messages
1358 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
1359 * descriptor returned by pwmd_get_fd() to determine when this should be
1360 * called or call pwmd_pending_line() to determine whether a buffered line
1361 * needs to be processed. */
1362 if (pwm->cmd == ASYNC_CMD_NONE) {
1363 *rc = assuan_command(pwm, pwm->ctx, NULL, "NOP");
1364 return ASYNC_DONE;
1367 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
1368 if (pwm->state == ASYNC_DONE) {
1369 reset_async(pwm, 0);
1370 return ASYNC_DONE;
1373 if (pwm->state != ASYNC_PROCESS) {
1374 *rc = GPG_ERR_INV_STATE;
1375 return ASYNC_DONE;
1378 #ifdef WITH_TCP
1379 if (pwm->cmd == ASYNC_CMD_DNS) {
1380 fd_set rfds, wfds;
1382 if (pwm->tcp_conn->rc) {
1383 *rc = pwm->tcp_conn->rc;
1384 reset_async(pwm, 1);
1385 return ASYNC_DONE;
1388 FD_ZERO(&rfds);
1389 FD_ZERO(&wfds);
1390 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
1392 /* Shouldn't happen. */
1393 if (!n)
1394 return pwm->state;
1396 #ifdef WITH_LIBPTH
1397 n = pth_select(n, &rfds, &wfds, NULL, &tv);
1398 #else
1399 n = select(n, &rfds, &wfds, NULL, &tv);
1400 #endif
1402 if (n > 0)
1403 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
1405 return pwm->state;
1407 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
1408 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
1409 int ret;
1410 socklen_t len = sizeof(int);
1412 FD_ZERO(&fds);
1413 FD_SET(pwm->tcp_conn->fd, &fds);
1414 #ifdef WITH_LIBPTH
1415 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1416 #else
1417 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1418 #endif
1420 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
1421 return pwm->state;
1422 else if (n == -1) {
1423 *rc = gpg_error_from_syserror();
1424 reset_async(pwm, 1);
1425 return ASYNC_DONE;
1428 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
1430 if (ret || n) {
1431 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
1432 reset_async(pwm, 1);
1433 return ASYNC_DONE;
1436 else if (pwm->tcp_conn->rc) {
1437 *rc = pwm->tcp_conn->rc;
1438 reset_async(pwm, 1);
1439 return ASYNC_DONE;
1442 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
1443 *rc = setup_tcp_session(pwm);
1445 if (!*rc) {
1446 switch (pwm->tcp_conn->cmd) {
1447 case ASYNC_CMD_HOSTKEY:
1448 if (!*rc)
1449 *result = pwm->result;
1450 break;
1451 default:
1452 break;
1456 return reset_async(pwm, *rc ? 1 : 0);
1458 #endif
1460 #ifdef WITH_PINENTRY
1461 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
1462 int status;
1464 if (pwm->nb_fd == -1) {
1465 *rc = GPG_ERR_INV_STATE;
1466 return reset_async(pwm, 0);
1469 FD_ZERO(&fds);
1470 FD_SET(pwm->nb_fd, &fds);
1471 FD_SET(pwm->fd, &fds);
1472 #ifdef WITH_LIBPTH
1473 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1474 #else
1475 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1476 #endif
1477 if (n == -1) {
1478 *rc = gpg_error_from_syserror();
1479 return reset_async(pwm, 0);
1482 if (n > 0 && FD_ISSET(pwm->nb_fd, &fds)) {
1483 pwmd_nb_status_t nb;
1484 #ifdef WITH_LIBPTH
1485 size_t len = pth_read(pwm->nb_fd, &nb, sizeof(nb));
1486 #else
1487 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
1488 #endif
1489 waitpid(pwm->nb_pid, &status, WNOHANG);
1491 if (len != sizeof(nb)) {
1492 *rc = gpg_error_from_syserror();
1493 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1496 *rc = nb.error;
1498 if (*rc == GPG_ERR_INV_PASSPHRASE && pwm->cmd == ASYNC_CMD_SAVE2) {
1499 reset_async(pwm, 0);
1500 *rc = pwmd_save_async2(pwm);
1501 return ASYNC_PROCESS;
1503 else if (*rc)
1504 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1506 if (pwm->cmd == ASYNC_CMD_SAVE2) {
1507 *rc = do_save_command(pwm, nb.password);
1508 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1509 return reset_async(pwm, 0);
1512 if (pwm->cmd == ASYNC_CMD_OPEN2) {
1513 *rc = do_open_command(pwm, pwm->filename, nb.password);
1514 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1516 if (*rc == GPG_ERR_INV_PASSPHRASE) {
1517 if (++pwm->pin_try < pwm->pinentry_tries) {
1518 int n = pwm->pin_try;
1520 reset_async(pwm, 0);
1521 pwm->pin_try = n;
1522 pwm->cmd = ASYNC_CMD_OPEN2;
1523 *rc = pwmd_open_async2(pwm, pwm->filename);
1525 if (*rc)
1526 return reset_async(pwm, 1);
1528 return pwm->state;
1532 return reset_async(pwm, *rc ? 1 : 0);
1536 /* Fall through so status messages can be processed during the
1537 * pinentry. */
1539 #endif
1541 if (pwm->fd < 0) {
1542 *rc = GPG_ERR_INV_STATE;
1543 return reset_async(pwm, 0);
1546 /* This is for the non-blocking OPEN and SAVE commands. */
1547 FD_ZERO(&fds);
1548 FD_SET(pwm->fd, &fds);
1549 #ifdef WITH_LIBPTH
1550 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
1551 #else
1552 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
1553 #endif
1555 if (n == -1) {
1556 *rc = gpg_error_from_syserror();
1557 return reset_async(pwm, 0);
1560 if (n > 0) {
1561 if (FD_ISSET(pwm->fd, &fds))
1562 *rc = parse_assuan_line(pwm);
1565 while (!*rc && assuan_pending_line(pwm->ctx))
1566 *rc = parse_assuan_line(pwm);
1568 /* For pinentry retries. */
1569 if (!pwm->tcp_conn && pwm->cmd == ASYNC_CMD_OPEN &&
1570 gpg_err_code(*rc) == GPG_ERR_INV_PASSPHRASE &&
1571 ++pwm->pin_try < pwm->pinentry_tries) {
1572 pwm->state = ASYNC_INIT;
1573 *rc = pwmd_open_async(pwm, pwm->filename);
1576 if (*rc)
1577 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN ? 1 : 0);
1579 if (pwm->state == ASYNC_DONE) {
1580 reset_async(pwm, 0);
1581 return ASYNC_DONE;
1584 return pwm->state;
1587 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
1588 char **result, const char *cmd)
1590 membuf_t data;
1591 gpg_error_t rc;
1593 data.len = 0;
1594 data.buf = NULL;
1596 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
1597 pwm->status_func, pwm->status_data);
1599 if (rc) {
1600 if (data.buf) {
1601 pwmd_free(data.buf);
1602 data.buf = NULL;
1605 else {
1606 if (data.buf) {
1607 mem_realloc_cb(&data, "", 1);
1609 if (!result) {
1610 pwmd_free(data.buf);
1611 rc = GPG_ERR_INV_ARG;
1613 else
1614 *result = (char *)data.buf;
1618 return gpg_err_code(rc);
1621 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_cb_t fn,
1622 void *data)
1624 if (!pwm || !cmd || !fn)
1625 return GPG_ERR_INV_ARG;
1627 if (!pwm->ctx)
1628 return GPG_ERR_INV_STATE;
1630 pwm->inquire_func = fn;
1631 pwm->inquire_data = data;
1632 return assuan_command(pwm, pwm->ctx, NULL, cmd);
1635 #ifdef WITH_PINENTRY
1636 static gpg_error_t terminate_pinentry(pwm_t *pwm)
1638 pid_t pid = pwm->pid;
1640 pwm->pid = -1;
1642 if (!pwm || pid == -1)
1643 return GPG_ERR_INV_ARG;
1645 if (kill(pid, 0) == 0) {
1646 if (kill(pid, SIGTERM) == -1) {
1647 if (kill(pid, SIGKILL) == -1)
1648 return gpg_error_from_errno(errno);
1651 else
1652 return gpg_error_from_errno(errno);
1654 return 0;
1657 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
1659 char *tmp, *desc;
1660 gpg_error_t error;
1662 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
1664 if (!tmp)
1665 return gpg_error_from_errno(ENOMEM);
1667 if (!pwm->title)
1668 pwm->title = pwmd_strdup_printf(N_("Password Manager Daemon: %s"),
1669 pwm->name ? pwm->name : "libpwmd");
1671 if (!pwm->title)
1672 goto fail_no_mem;
1674 if (!pwm->prompt)
1675 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
1677 if (!pwm->prompt)
1678 goto fail_no_mem;
1680 if (!pwm->desc && (which == PINENTRY_OPEN || which == PINENTRY_SAVE)) {
1681 if (which == PINENTRY_OPEN)
1682 desc = pwmd_strdup_printf(N_("A passphrase is required to open the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1683 else
1684 desc = pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1686 if (!desc)
1687 goto fail_no_mem;
1690 if (pwm->desc)
1691 desc = pwm->desc;
1693 switch (which) {
1694 case PINENTRY_OPEN:
1695 case PINENTRY_SAVE:
1696 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s", desc);
1698 if (pwm->desc != desc)
1699 pwmd_free(desc);
1700 break;
1701 case PINENTRY_OPEN_FAILED:
1702 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1703 N_("Invalid passphrase, please try again."));
1704 break;
1705 case PINENTRY_SAVE_CONFIRM:
1706 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1707 N_("Please type the passphrase again for confirmation."));
1708 break;
1711 error = pinentry_command(pwm, NULL, tmp);
1713 if (error) {
1714 pwmd_free(tmp);
1715 return error;
1718 snprintf(tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->prompt);
1719 error = pinentry_command(pwm, NULL, tmp);
1721 if (error) {
1722 pwmd_free(tmp);
1723 return error;
1726 snprintf(tmp, ASSUAN_LINELENGTH, "SETDESC %s", pwm->title);
1727 error = pinentry_command(pwm, NULL, tmp);
1728 pwmd_free(tmp);
1729 return error;
1731 fail_no_mem:
1732 pwmd_free(tmp);
1733 return gpg_error_from_errno(ENOMEM);
1736 static void update_pinentry_settings(pwm_t *pwm)
1738 FILE *fp;
1739 char buf[LINE_MAX];
1740 char *p;
1741 struct passwd pw;
1742 char *pwbuf = _getpwuid(&pw);
1744 if (!pwbuf)
1745 return;
1747 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw.pw_dir);
1749 if ((fp = fopen(buf, "r")) == NULL) {
1750 pwmd_free(pwbuf);
1751 return;
1754 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
1755 char name[32], val[256];
1757 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
1758 continue;
1760 if (strcasecmp(name, "TTYNAME") == 0) {
1761 pwmd_free(pwm->pinentry_tty);
1762 pwm->pinentry_tty = pwmd_strdup(val);
1764 else if (strcasecmp(name, "TTYTYPE") == 0) {
1765 pwmd_free(pwm->pinentry_term);
1766 pwm->pinentry_term = pwmd_strdup(val);
1768 else if (strcasecmp(name, "DISPLAY") == 0) {
1769 pwmd_free(pwm->pinentry_display);
1770 pwm->pinentry_display = pwmd_strdup(val);
1772 else if (strcasecmp(name, "PATH") == 0) {
1773 pwmd_free(pwm->pinentry_path);
1774 pwm->pinentry_path = expand_homedir(val, &pw);
1778 pwmd_free(pwbuf);
1779 fclose(fp);
1782 static gpg_error_t launch_pinentry(pwm_t *pwm)
1784 int rc;
1785 assuan_context_t ctx;
1786 int child_list[] = {-1};
1787 char *display = getenv("DISPLAY");
1788 const char *argv[10];
1789 const char **p = argv;
1790 int have_display = 0;
1791 char *tty = NULL;
1792 char *ttybuf = NULL;
1794 update_pinentry_settings(pwm);
1796 if (pwm->pinentry_display || display)
1797 have_display = 1;
1798 else {
1799 if (!pwm->pinentry_tty) {
1800 ttybuf = pwmd_malloc(255);
1802 if (!ttybuf)
1803 return gpg_error_from_errno(ENOMEM);
1805 rc = ttyname_r(STDOUT_FILENO, ttybuf, 255);
1807 if (rc) {
1808 pwmd_free(ttybuf);
1809 return gpg_error_from_errno(rc);
1812 tty = ttybuf;
1814 else
1815 tty = pwm->pinentry_tty;
1818 if (!have_display && !tty)
1819 return GPG_ERR_ENOTTY;
1821 *p++ = "pinentry";
1822 *p++ = have_display ? "--display" : "--ttyname";
1823 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
1825 if (pwm->lcctype) {
1826 *p++ = "--lc-ctype";
1827 *p++ = pwm->lcctype;
1830 if (pwm->lcmessages) {
1831 *p++ = "--lc-messages";
1832 *p++ = pwm->lcmessages;
1835 *p = NULL;
1837 if (!have_display) {
1838 *p++ = "--ttytype";
1839 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
1840 *p = NULL;
1843 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
1845 if (ttybuf)
1846 pwmd_free(ttybuf);
1848 if (rc)
1849 return rc;
1851 pwm->pid = assuan_get_pid(ctx);
1852 pwm->pctx = ctx;
1853 return set_pinentry_strings(pwm, 0);
1856 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
1858 gpg_error_t n;
1860 if (!pwm->pctx) {
1861 n = launch_pinentry(pwm);
1863 if (n)
1864 return n;
1867 return assuan_command(pwm, pwm->pctx, result, cmd);
1870 static void pinentry_disconnect(pwm_t *pwm)
1872 if (pwm->pctx)
1873 assuan_disconnect(pwm->pctx);
1875 pwm->pctx = NULL;
1876 pwm->pid = -1;
1880 * Only called from a child process.
1882 static void catchsig(int sig)
1884 switch (sig) {
1885 case SIGALRM:
1886 if (gelapsed++ >= gtimeout) {
1887 terminate_pinentry(gpwm);
1888 gerror = GPG_ERR_TIMEOUT;
1890 else
1891 alarm(1);
1892 break;
1893 default:
1894 break;
1897 #endif
1900 * Borrowed from libassuan.
1902 static char *percent_escape(const char *atext)
1904 const unsigned char *s;
1905 int len = strlen(atext) * 3 + 1;
1906 char *buf = (char *)pwmd_malloc(len), *p = buf;
1908 if (!buf)
1909 return NULL;
1911 for (s=(const unsigned char *)atext; *s; s++) {
1912 if (*s < ' ') {
1913 sprintf (p, "%%%02X", *s);
1914 p += 3;
1916 else
1917 *p++ = *s;
1920 *p = 0;
1921 return buf;
1924 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
1926 if (!cmd)
1927 return GPG_ERR_INV_ARG;
1929 return assuan_command(pwm, pwm->ctx, result, cmd);
1932 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1933 va_list ap)
1935 char *buf;
1936 size_t len;
1937 gpg_error_t error;
1938 va_list ap2;
1940 if (!pwm || !cmd)
1941 return GPG_ERR_INV_ARG;
1943 if (!pwm->ctx)
1944 return GPG_ERR_INV_STATE;
1947 * C99 allows the dst pointer to be null which will calculate the length
1948 * of the would-be result and return it.
1950 va_copy(ap2, ap);
1951 len = vsnprintf(NULL, 0, cmd, ap)+1;
1952 buf = (char *)pwmd_malloc(len);
1954 if (!buf) {
1955 va_end(ap2);
1956 return gpg_error_from_errno(ENOMEM);
1959 len = vsnprintf(buf, len, cmd, ap2);
1960 va_end(ap2);
1962 if (buf[strlen(buf)-1] == '\n')
1963 buf[strlen(buf)-1] = 0;
1965 if (buf[strlen(buf)-1] == '\r')
1966 buf[strlen(buf)-1] = 0;
1968 error = send_command(pwm, result, buf);
1969 pwmd_free(buf);
1970 return error;
1973 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1975 va_list ap;
1976 gpg_error_t error;
1978 if (!pwm || !cmd)
1979 return GPG_ERR_INV_ARG;
1981 if (!pwm->ctx)
1982 return GPG_ERR_INV_STATE;
1984 if (result)
1985 *result = NULL;
1987 va_start(ap, cmd);
1988 error = pwmd_command_ap(pwm, result, cmd, ap);
1989 va_end(ap);
1990 return error;
1993 #ifdef WITH_PINENTRY
1994 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
1996 if (gtimeout) {
1997 signal(SIGALRM, catchsig);
1998 alarm(1);
2001 *result = NULL;
2002 return pinentry_command(pwm, result, "GETPIN");
2005 static gpg_error_t getpin(pwm_t *pwm, char **result, int which)
2007 gpg_error_t rc;
2009 gerror = 0;
2010 rc = set_pinentry_strings(pwm, which);
2012 if (rc) {
2013 pinentry_disconnect(pwm);
2014 return rc;
2017 rc = do_getpin(pwm, result);
2020 * Since there was input cancel any timeout setting.
2022 alarm(0);
2023 signal(SIGALRM, SIG_DFL);
2025 if (rc) {
2026 if (pwm->pctx)
2027 pinentry_disconnect(pwm);
2029 /* This lets pwmd_open2() with PWMD_OPTION_PINENTRY_TIMEOUT work. */
2030 if (rc== GPG_ERR_EOF && gerror == GPG_ERR_TIMEOUT)
2031 return gerror;
2033 return rc;
2036 return 0;
2038 #endif
2040 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
2042 char *buf;
2043 gpg_error_t error;
2044 char *result = NULL;
2046 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2048 if (!buf)
2049 return gpg_error_from_errno(ENOMEM);
2051 snprintf(buf, ASSUAN_LINELENGTH, "OPEN %s %s", filename,
2052 password ? password : "");
2053 error = send_command(pwm, &result, buf);
2054 pwmd_free(buf);
2056 if (error && result)
2057 pwmd_free(result);
2059 return error;
2062 static gpg_error_t send_pinentry_options(pwm_t *pwm)
2064 gpg_error_t rc;
2066 if (pwm->pinentry_path) {
2067 rc = pwmd_command(pwm, NULL, "OPTION PATH=%s", pwm->pinentry_path);
2069 if (rc)
2070 return rc;
2073 if (pwm->pinentry_tty) {
2074 rc = pwmd_command(pwm, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
2076 if (rc)
2077 return rc;
2080 if (pwm->pinentry_term) {
2081 rc = pwmd_command(pwm, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
2083 if (rc)
2084 return rc;
2087 if (pwm->pinentry_display) {
2088 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->pinentry_display);
2090 if (rc)
2091 return rc;
2094 if (pwm->title) {
2095 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->title);
2097 if (rc)
2098 return rc;
2101 if (pwm->desc) {
2102 rc = pwmd_command(pwm, NULL, "OPTION DESC=%s", pwm->desc);
2104 if (rc)
2105 return rc;
2108 if (pwm->prompt) {
2109 rc = pwmd_command(pwm, NULL, "OPTION PROMPT=%s", pwm->prompt);
2111 if (rc)
2112 return rc;
2115 if (pwm->lcctype) {
2116 rc = pwmd_command(pwm, NULL, "OPTION LC_CTYPE=%s", pwm->lcctype);
2118 if (rc)
2119 return rc;
2122 if (pwm->lcmessages) {
2123 rc = pwmd_command(pwm, NULL, "OPTION LC_MESSAGES=%s", pwm->lcmessages);
2125 if (rc)
2126 return rc;
2129 if (pwm->pinentry_timeout >= 0) {
2130 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=%i", pwm->pinentry_timeout);
2132 if (rc)
2133 return rc;
2136 return 0;
2139 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb,
2140 int local_pinentry)
2142 char *result = NULL;
2143 char *password = NULL;
2144 char *path;
2145 int pin_try;
2146 gpg_error_t rc;
2148 if (!pwm || !filename || !*filename)
2149 return GPG_ERR_INV_ARG;
2151 if (!pwm->ctx)
2152 return GPG_ERR_INV_STATE;
2154 pin_try = pwm->pinentry_tries - 1;
2157 * Avoid calling pinentry if the password is cached on the server or if
2158 * this is a new file. pwmd version 2 adds a VERSION command which is
2159 * determined in _socket_connect_finalize(). If the server is version 2,
2160 * ISCACHED can determine if a file exists.
2162 #ifdef WITH_TCP
2163 if (!pwm->tcp_conn && pwm->version == PWMD_V1) {
2164 #else
2165 if (pwm->version == PWMD_V1) {
2166 #endif
2167 rc = pwmd_command(pwm, &result, "GETCONFIG data_directory");
2169 if (rc)
2170 return rc;
2172 path = pwmd_strdup_printf("%s/%s", result, filename);
2173 pwmd_free(result);
2175 if (!path)
2176 return gpg_error_from_errno(ENOMEM);
2178 if (access(path, R_OK) == -1) {
2179 if (errno == ENOENT) {
2180 pwmd_free(path);
2181 goto gotpassword;
2185 pwmd_free(path);
2188 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
2190 if (gpg_err_code(rc) == GPG_ERR_ENOENT)
2191 goto gotpassword;
2193 if (rc && rc != GPG_ERR_NOT_FOUND)
2194 return rc;
2196 if (rc == GPG_ERR_NOT_FOUND) {
2197 if (pwm->password) {
2198 password = pwm->password;
2199 goto gotpassword;
2202 if (pwm->passfunc) {
2203 rc = pwm->passfunc(pwm->passdata, &password);
2205 if (rc)
2206 return rc;
2208 goto gotpassword;
2212 #ifdef WITH_PINENTRY
2213 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2214 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
2216 if (rc)
2217 return rc;
2219 if (!pwm->filename)
2220 pwm->filename = pwmd_strdup(filename);
2222 if (!pwm->filename)
2223 return gpg_error_from_errno(ENOMEM);
2225 /* Get the passphrase using the LOCAL pinentry. */
2226 if (nb) {
2227 int p[2];
2228 pid_t pid;
2229 pwmd_nb_status_t pw;
2231 if (pipe(p) == -1)
2232 return gpg_error_from_syserror();
2234 #ifdef WITH_LIBPTH
2235 pid = pth_fork();
2236 #else
2237 pid = fork();
2238 #endif
2240 switch (pid) {
2241 case 0:
2242 close(p[0]);
2243 pw.fd = p[0];
2245 if (pwm->pinentry_timeout != 0) {
2246 gpwm = pwm;
2247 gtimeout = abs(pwm->pinentry_timeout);
2248 gelapsed = 0;
2251 pw.error = getpin(pwm, &password, PINENTRY_OPEN);
2253 if (gtimeout && gelapsed >= gtimeout)
2254 pw.error = GPG_ERR_TIMEOUT;
2256 if (!pw.error)
2257 snprintf(pw.password, sizeof(pw.password), "%s",
2258 password);
2260 pinentry_disconnect(pwm);
2261 #ifdef WITH_LIBPTH
2262 pth_write(p[1], &pw, sizeof(pw));
2263 #else
2264 write(p[1], &pw, sizeof(pw));
2265 #endif
2266 memset(&pw, 0, sizeof(pw));
2267 close(p[1]);
2268 _exit(0);
2269 break;
2270 case -1:
2271 rc = gpg_error_from_syserror();
2272 close(p[0]);
2273 close(p[1]);
2274 return rc;
2275 default:
2276 break;
2279 close(p[1]);
2280 pwm->nb_fd = p[0];
2281 pwm->nb_pid = pid;
2282 return 0;
2285 if (pwm->pinentry_timeout != 0) {
2286 gpwm = pwm;
2287 gtimeout = abs(pwm->pinentry_timeout);
2288 gelapsed = 0;
2291 rc = getpin(pwm, &password, PINENTRY_OPEN);
2293 /* Don't timeout when an invalid passphrase was entered. */
2294 gtimeout = 0;
2296 if (rc)
2297 return rc;
2299 #endif
2301 gotpassword:
2302 pwm->state = ASYNC_DONE;
2304 if (!local_pinentry && !pwm->tcp_conn) {
2305 rc = send_pinentry_options(pwm);
2307 if (rc)
2308 return rc;
2311 rc = do_open_command(pwm, filename, password);
2314 * Keep the user defined password set with pwmd_setopt(). The password may
2315 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
2317 if (!pwm->passfunc && password && password != pwm->password)
2318 pwmd_free(password);
2320 if (rc == GPG_ERR_INV_PASSPHRASE && !pwm->tcp_conn) {
2321 if (pin_try-- > 0 && !nb) {
2323 #ifdef WITH_PINENTRY
2324 if (local_pinentry)
2325 rc = getpin(pwm, &password, PINENTRY_OPEN_FAILED);
2326 else
2327 #endif
2328 rc = pwmd_command(pwm, &result, "OPTION TITLE=%s",
2329 N_("Invalid passphrase, please try again."));
2331 if (rc)
2332 return rc;
2334 goto gotpassword;
2337 #ifdef WITH_PINENTRY
2338 if (nb)
2339 pinentry_disconnect(pwm);
2340 #endif
2342 return rc;
2345 if (!rc) {
2346 if (pwm->filename)
2347 pwmd_free(pwm->filename);
2349 pwm->filename = pwmd_strdup(filename);
2352 return rc;
2355 gpg_error_t pwmd_open2(pwm_t *pwm, const char *filename)
2357 #ifndef WITH_PINENTRY
2358 return GPG_ERR_NOT_IMPLEMENTED;
2359 #else
2360 return do_pwmd_open(pwm, filename, 0, 1);
2361 #endif
2364 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
2366 return do_pwmd_open(pwm, filename, 0, 0);
2369 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
2371 #ifndef WITH_PINENTRY
2372 return GPG_ERR_NOT_IMPLEMENTED;
2373 #else
2374 gpg_error_t rc;
2376 if (!pwm || !filename)
2377 return GPG_ERR_INV_ARG;
2379 if (!pwm->ctx)
2380 return GPG_ERR_INV_STATE;
2382 if (pwm->cmd != ASYNC_CMD_OPEN2)
2383 pwm->pin_try = 0;
2385 pwm->cmd = ASYNC_CMD_OPEN2;
2386 pwm->state = ASYNC_PROCESS;
2387 rc = do_pwmd_open(pwm, filename, 1, 1);
2389 if (rc)
2390 reset_async(pwm, 1);
2392 return rc;
2393 #endif
2396 #ifdef WITH_PINENTRY
2397 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
2399 int confirm = 0;
2400 gpg_error_t error;
2401 char *result = NULL;
2403 again:
2404 error = getpin(pwm, &result, confirm ? PINENTRY_SAVE_CONFIRM : PINENTRY_SAVE);
2406 if (error) {
2407 if (pwm->pctx)
2408 pinentry_disconnect(pwm);
2410 if (*password)
2411 pwmd_free(*password);
2413 return error;
2416 if (!confirm++) {
2417 *password = result;
2418 goto again;
2421 if (strcmp(*password, result)) {
2422 pwmd_free(*password);
2423 pwmd_free(result);
2424 confirm = 0;
2425 *password = NULL;
2426 goto again;
2429 pwmd_free(result);
2430 pinentry_disconnect(pwm);
2431 return 0;
2433 #endif
2435 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
2437 char *buf;
2438 gpg_error_t error;
2439 char *result = NULL;
2441 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2443 if (!buf)
2444 return gpg_error_from_errno(ENOMEM);
2446 snprintf(buf, ASSUAN_LINELENGTH, "SAVE %s", password ? password : "");
2447 error = send_command(pwm, &result, buf);
2448 pwmd_free(buf);
2450 if (error && result)
2451 pwmd_free(result);
2453 return error;
2456 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb, int local_pinentry)
2458 char *result = NULL;
2459 char *password = NULL;
2460 gpg_error_t rc;
2462 if (!pwm)
2463 return GPG_ERR_INV_ARG;
2465 if (!pwm->ctx)
2466 return GPG_ERR_INV_STATE;
2468 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
2470 if (rc && rc != GPG_ERR_NOT_FOUND)
2471 return rc;
2473 if (rc == GPG_ERR_NOT_FOUND) {
2474 if (pwm->password) {
2475 password = pwm->password;
2476 goto gotpassword;
2479 if (pwm->passfunc) {
2480 rc = pwm->passfunc(pwm->passdata, &password);
2482 if (rc)
2483 return rc;
2485 goto gotpassword;
2489 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2490 #ifdef WITH_PINENTRY
2491 /* Get the password using the LOCAL pinentry. */
2492 if (nb) {
2493 int p[2];
2494 pid_t pid;
2495 pwmd_nb_status_t pw;
2497 if (pipe(p) == -1)
2498 return gpg_error_from_syserror();
2500 #ifdef WITH_LIBPTH
2501 pid = pth_fork();
2502 #else
2503 pid = fork();
2504 #endif
2506 switch (pid) {
2507 case 0:
2508 close(p[0]);
2509 pw.fd = p[0];
2510 password = NULL;
2511 pw.error = do_save_getpin(pwm, &password);
2512 pinentry_disconnect(pwm);
2513 snprintf(pw.password, sizeof(pw.password), "%s",
2514 password);
2515 #ifdef WITH_LIBPTH
2516 pth_write(p[1], &pw, sizeof(pw));
2517 #else
2518 write(p[1], &pw, sizeof(pw));
2519 #endif
2520 memset(&pw, 0, sizeof(pw));
2521 close(p[1]);
2522 _exit(0);
2523 break;
2524 case -1:
2525 rc = gpg_error_from_syserror();
2526 close(p[0]);
2527 close(p[1]);
2528 return rc;
2529 default:
2530 break;
2533 close(p[1]);
2534 pwm->nb_fd = p[0];
2535 pwm->nb_pid = pid;
2536 return 0;
2539 rc = do_save_getpin(pwm, &password);
2541 if (rc)
2542 return rc;
2543 #endif
2545 else
2546 pwm->state = ASYNC_DONE;
2548 gotpassword:
2549 if (!local_pinentry && !pwm->tcp_conn) {
2550 rc = send_pinentry_options(pwm);
2552 if (rc)
2553 return rc;
2556 rc = do_save_command(pwm, password);
2558 if (!pwm->passfunc && password && password != pwm->password)
2559 pwmd_free(password);
2561 return rc;
2564 gpg_error_t pwmd_save_async2(pwm_t *pwm)
2566 #ifndef WITH_PINENTRY
2567 return GPG_ERR_NOT_IMPLEMENTED;
2568 #else
2569 gpg_error_t rc;
2571 if (!pwm)
2572 return GPG_ERR_INV_ARG;
2574 if (!pwm->ctx)
2575 return GPG_ERR_INV_STATE;
2577 pwm->cmd = ASYNC_CMD_SAVE2;
2578 pwm->state = ASYNC_PROCESS;
2579 rc = do_pwmd_save(pwm, 1, 1);
2581 if (rc)
2582 reset_async(pwm, 0);
2584 return rc;
2585 #endif
2588 gpg_error_t pwmd_save2(pwm_t *pwm)
2590 #ifndef WITH_PINENTRY
2591 return GPG_ERR_NOT_IMPLEMENTED;
2592 #else
2593 return do_pwmd_save(pwm, 0, 1);
2594 #endif
2597 gpg_error_t pwmd_save(pwm_t *pwm)
2599 return do_pwmd_save(pwm, 0, 0);
2602 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
2604 va_list ap;
2605 int n = va_arg(ap, int);
2606 char *arg1;
2607 gpg_error_t error = 0;
2609 if (!pwm)
2610 return GPG_ERR_INV_ARG;
2612 va_start(ap, opt);
2614 switch (opt) {
2615 case PWMD_OPTION_STATUS_CB:
2616 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
2617 break;
2618 case PWMD_OPTION_STATUS_DATA:
2619 pwm->status_data = va_arg(ap, void *);
2620 break;
2621 case PWMD_OPTION_PASSPHRASE_CB:
2622 pwm->passfunc = va_arg(ap, pwmd_passphrase_cb_t);
2623 break;
2624 case PWMD_OPTION_PASSPHRASE_DATA:
2625 pwm->passdata = va_arg(ap, void *);
2626 break;
2627 case PWMD_OPTION_PASSPHRASE:
2628 arg1 = va_arg(ap, char *);
2630 if (pwm->password)
2631 pwmd_free(pwm->password);
2633 pwm->password = pwmd_strdup(arg1);
2634 break;
2635 case PWMD_OPTION_PINENTRY_TRIES:
2636 n = va_arg(ap, int);
2638 if (n <= 0) {
2639 va_end(ap);
2640 error = GPG_ERR_INV_VALUE;
2642 else
2643 pwm->pinentry_tries = n;
2644 break;
2645 case PWMD_OPTION_PINENTRY_TIMEOUT:
2646 n = va_arg(ap, int);
2648 if (n < 0) {
2649 va_end(ap);
2650 error = GPG_ERR_INV_VALUE;
2652 else
2653 pwm->pinentry_timeout = n;
2654 break;
2655 case PWMD_OPTION_PINENTRY_PATH:
2656 if (pwm->pinentry_path)
2657 pwmd_free(pwm->pinentry_path);
2659 pwm->pinentry_path = expand_homedir(va_arg(ap, char *), NULL);
2660 break;
2661 case PWMD_OPTION_PINENTRY_TTY:
2662 if (pwm->pinentry_tty)
2663 pwmd_free(pwm->pinentry_tty);
2665 pwm->pinentry_tty = pwmd_strdup(va_arg(ap, char *));
2666 break;
2667 case PWMD_OPTION_PINENTRY_DISPLAY:
2668 if (pwm->pinentry_display)
2669 pwmd_free(pwm->pinentry_display);
2671 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
2672 break;
2673 case PWMD_OPTION_PINENTRY_TERM:
2674 if (pwm->pinentry_term)
2675 pwmd_free(pwm->pinentry_term);
2677 pwm->pinentry_term = pwmd_strdup(va_arg(ap, char *));
2678 break;
2679 case PWMD_OPTION_PINENTRY_TITLE:
2680 if (pwm->title)
2681 pwmd_free(pwm->title);
2683 pwm->title = percent_escape(va_arg(ap, char *));
2684 break;
2685 case PWMD_OPTION_PINENTRY_PROMPT:
2686 if (pwm->prompt)
2687 pwmd_free(pwm->prompt);
2689 pwm->prompt = percent_escape(va_arg(ap, char *));
2690 break;
2691 case PWMD_OPTION_PINENTRY_DESC:
2692 if (pwm->desc)
2693 pwmd_free(pwm->desc);
2695 pwm->desc = percent_escape(va_arg(ap, char *));
2696 break;
2697 case PWMD_OPTION_PINENTRY_LC_CTYPE:
2698 if (pwm->lcctype)
2699 pwmd_free(pwm->lcctype);
2701 pwm->lcctype = pwmd_strdup(va_arg(ap, char *));
2702 break;
2703 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
2704 if (pwm->lcmessages)
2705 pwmd_free(pwm->lcmessages);
2707 pwm->lcmessages = pwmd_strdup(va_arg(ap, char *));
2708 break;
2709 #ifdef WITH_TCP
2710 case PWMD_OPTION_IP_VERSION:
2711 n = va_arg(ap, int);
2713 switch (n) {
2714 case PWMD_IP_ANY:
2715 case PWMD_IPV4:
2716 case PWMD_IPV6:
2717 pwm->prot = n;
2718 break;
2719 default:
2720 error = GPG_ERR_INV_VALUE;
2721 break;
2724 va_end(ap);
2725 break;
2726 #endif
2727 default:
2728 error = GPG_ERR_NOT_IMPLEMENTED;
2729 break;
2732 va_end(ap);
2733 return error;
2736 gpg_error_t pwmd_get_fds(pwm_t *pwm, pwmd_fd_t *fds, int *n_fds)
2738 int in_total;
2739 int fd = 0;
2740 #ifdef WITH_TCP
2741 int afds[ARES_GETSOCK_MAXNUM];
2742 int got_sock = 0;
2743 int n, i;
2744 #endif
2746 if (!pwm || !fds || !n_fds || *n_fds <= 0)
2747 return GPG_ERR_INV_ARG;
2749 in_total = *n_fds;
2750 #ifdef WITH_TCP
2751 memset(afds, 0, sizeof(int)*ARES_GETSOCK_MAXNUM);
2752 #endif
2753 memset(fds, 0, sizeof(pwmd_fd_t)*in_total);
2754 *n_fds = 0;
2756 switch (pwm->cmd) {
2757 default:
2758 case ASYNC_CMD_NONE:
2759 case ASYNC_CMD_OPEN:
2760 case ASYNC_CMD_SAVE:
2761 #ifdef WITH_PINENTRY
2762 async1:
2763 #endif
2764 if (pwm->fd == -1)
2765 return GPG_ERR_INV_STATE;
2767 (*n_fds)++;
2768 fds[fd].fd = pwm->fd;
2769 fds[fd++].flags = PWMD_FD_READABLE;
2770 return 0;
2771 #ifdef WITH_PINENTRY
2772 case ASYNC_CMD_OPEN2:
2773 case ASYNC_CMD_SAVE2:
2774 /* The command has already completed (cached or new). */
2775 if (pwm->state == ASYNC_DONE)
2776 return 0;
2778 if (pwm->nb_fd == -1)
2779 return GPG_ERR_INV_STATE;
2781 (*n_fds)++;
2782 fds[fd].fd = pwm->nb_fd;
2783 fds[fd++].flags = PWMD_FD_READABLE;
2784 goto async1;
2785 #endif
2786 #ifdef WITH_TCP
2787 case ASYNC_CMD_DNS:
2788 if (!pwm->tcp_conn || !pwm->tcp_conn->chan)
2789 return GPG_ERR_INV_STATE;
2791 n = ares_getsock(pwm->tcp_conn->chan, afds, ARES_GETSOCK_MAXNUM);
2793 for (i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
2794 got_sock = 0;
2796 if (fd > in_total) {
2797 *n_fds = fd;
2798 return GPG_ERR_ERANGE;
2801 if (ARES_GETSOCK_READABLE(n, i)) {
2802 got_sock++;
2803 fds[fd].flags |= PWMD_FD_READABLE;
2806 if (ARES_GETSOCK_WRITABLE(n, i)) {
2807 got_sock++;
2808 fds[fd].flags |= PWMD_FD_WRITABLE;
2811 if (got_sock)
2812 fds[fd++].fd = afds[i];
2815 *n_fds = fd;
2816 return 0;
2817 case ASYNC_CMD_CONNECT:
2818 case ASYNC_CMD_HOSTKEY:
2819 if (!pwm->tcp_conn || pwm->tcp_conn->fd == -1)
2820 return GPG_ERR_INV_STATE;
2822 (*n_fds)++;
2823 fds[fd].fd = pwm->tcp_conn->fd;
2824 fds[fd++].flags = PWMD_FD_READABLE;
2825 return 0;
2826 #endif
2829 return GPG_ERR_INV_STATE;
2832 pwm_t *pwmd_new(const char *name)
2834 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
2836 if (!h)
2837 return NULL;
2839 if (name) {
2840 h->name = pwmd_strdup(name);
2842 if (!h->name) {
2843 pwmd_free(h);
2844 return NULL;
2848 h->fd = -1;
2849 #ifdef WITH_PINENTRY
2850 h->nb_fd = -1;
2851 #endif
2852 h->pinentry_timeout = -30;
2853 h->pinentry_tries = 3;
2854 #ifdef WITH_TCP
2855 h->prot = PWMD_IP_ANY;
2856 #endif
2857 return h;
2860 void pwmd_free(void *ptr)
2862 xfree(ptr);
2865 void *pwmd_malloc(size_t size)
2867 return xmalloc(size);
2870 void *pwmd_calloc(size_t nmemb, size_t size)
2872 return xcalloc(nmemb, size);
2875 void *pwmd_realloc(void *ptr, size_t size)
2877 return xrealloc(ptr, size);
2880 char *pwmd_strdup(const char *str)
2882 return xstrdup(str);
2885 char *pwmd_strdup_printf(const char *fmt, ...)
2887 va_list ap, ap2;
2888 int len;
2889 char *buf;
2891 if (!fmt)
2892 return NULL;
2894 va_start(ap, fmt);
2895 va_copy(ap2, ap);
2896 len = vsnprintf(NULL, 0, fmt, ap);
2897 va_end(ap);
2898 buf = pwmd_malloc(++len);
2900 if (buf)
2901 vsnprintf(buf, len, fmt, ap2);
2903 va_end(ap2);
2904 return buf;