Call _pwmd_process() in _inquire_cb() so status messages can be parsed
[libpwmd.git] / src / libpwmd.c
blob396b4b22fb1f6d1c8b0d56bc9c72e61d97d00497
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->session && 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_close(conn->channel);
318 libssh2_channel_free(conn->channel);
321 if (conn->session) {
322 libssh2_session_disconnect(conn->session, "Bye!");
323 libssh2_session_free(conn->session);
326 conn->session = NULL;
327 conn->channel = NULL;
328 free_tcp_conn(conn);
331 static void _ssh_assuan_deinit(assuan_context_t ctx)
333 pwm_t *pwm = assuan_get_pointer(ctx);
335 if (pwm->tcp_conn) {
336 pwm->tcp_conn->fd = -1;
337 _ssh_deinit(pwm->tcp_conn);
338 pwm->tcp_conn = NULL;
343 * Sets common options from both pwmd_ssh_connect() and
344 * pwmd_ssh_connect_async().
346 static gpg_error_t init_tcp_conn(pwmd_tcp_conn_t **dst, const char *host,
347 int port, const char *identity, const char *user,
348 const char *known_hosts, int get)
350 pwmd_tcp_conn_t *conn;
351 gpg_error_t rc = 0;
352 char *pwbuf = NULL;
354 if (get) {
355 if (!host || !*host)
356 return GPG_ERR_INV_ARG;
358 else {
359 if (!host || !*host || !identity || !*identity || !known_hosts ||
360 !*known_hosts)
361 return GPG_ERR_INV_ARG;
364 conn = pwmd_calloc(1, sizeof(pwmd_tcp_conn_t));
366 if (!conn)
367 return gpg_error_from_errno(ENOMEM);
369 conn->port = port == -1 ? 22 : port;
370 conn->host = pwmd_strdup(host);
372 if (!conn->host) {
373 rc = gpg_error_from_errno(ENOMEM);
374 goto fail;
377 if (!get) {
378 struct passwd pw;
380 pwbuf = _getpwuid(&pw);
382 if (!pwbuf) {
383 rc = gpg_error_from_errno(errno);
384 goto fail;
387 conn->username = pwmd_strdup(user ? user : pw.pw_name);
389 if (!conn->username) {
390 rc = gpg_error_from_errno(ENOMEM);
391 goto fail;
394 conn->identity = expand_homedir((char *)identity, &pw);
396 if (!conn->identity) {
397 rc = gpg_error_from_errno(ENOMEM);
398 goto fail;
401 conn->identity_pub = pwmd_malloc(strlen(conn->identity)+5);
403 if (!conn->identity_pub) {
404 rc = gpg_error_from_errno(ENOMEM);
405 goto fail;
408 sprintf(conn->identity_pub, "%s.pub", conn->identity);
409 conn->known_hosts = expand_homedir((char *)known_hosts, &pw);
411 if (!conn->known_hosts) {
412 rc = gpg_error_from_errno(ENOMEM);
413 goto fail;
416 pwmd_free(pwbuf);
419 *dst = conn;
420 return 0;
422 fail:
423 if (pwbuf)
424 pwmd_free(pwbuf);
426 free_tcp_conn(conn);
427 return rc;
430 static gpg_error_t do_connect(pwm_t *pwm, int prot, void *addr)
432 struct sockaddr_in their_addr;
434 pwm->tcp_conn->fd = socket(prot, SOCK_STREAM, 0);
436 if (pwm->tcp_conn->fd == -1)
437 return gpg_error_from_syserror();
439 if (pwm->tcp_conn->async)
440 fcntl(pwm->tcp_conn->fd, F_SETFL, O_NONBLOCK);
442 pwm->cmd = ASYNC_CMD_CONNECT;
443 their_addr.sin_family = prot;
444 their_addr.sin_port = htons(pwm->tcp_conn->port);
445 their_addr.sin_addr = *((struct in_addr *)addr);
446 memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
448 #ifdef WITH_LIBPTH
449 if (pth_connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
450 sizeof(their_addr)) == -1)
451 #else
452 if (connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
453 sizeof(their_addr)) == -1)
454 #endif
455 return gpg_error_from_syserror();
457 return 0;
460 static gpg_error_t ares_error_to_pwmd(int status)
462 if (status != ARES_SUCCESS)
463 warnx("%s", ares_strerror(status));
465 switch (status) {
466 case ARES_ENODATA:
467 case ARES_EFORMERR:
468 case ARES_ENOTFOUND:
469 return GPG_ERR_UNKNOWN_HOST;
470 case ARES_ESERVFAIL:
471 return GPG_ERR_EHOSTDOWN;
472 case ARES_ETIMEOUT:
473 return GPG_ERR_TIMEOUT;
474 case ARES_ENOMEM:
475 return gpg_error_from_errno(ENOMEM);
476 case ARES_ECONNREFUSED:
477 return GPG_ERR_ECONNREFUSED;
478 default:
479 /* FIXME ??? */
480 return GPG_ERR_EHOSTUNREACH;
483 return ARES_SUCCESS;
486 static void dns_resolve_cb(void *arg, int status, int timeouts,
487 unsigned char *abuf, int alen)
489 pwm_t *pwm = arg;
490 int rc;
491 struct hostent *he;
493 if (status == ARES_EDESTRUCTION)
494 return;
496 if (status != ARES_SUCCESS) {
497 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
498 return;
501 /* Check for an IPv6 address first. */
502 if (pwm->prot == PWMD_IP_ANY || pwm->prot == PWMD_IPV6)
503 rc = ares_parse_aaaa_reply(abuf, alen, &he, NULL, NULL);
504 else
505 rc = ares_parse_a_reply(abuf, alen, &he, NULL, NULL);
507 if (rc != ARES_SUCCESS) {
508 if (pwm->prot != PWMD_IP_ANY || rc != ARES_ENODATA) {
509 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
510 return;
513 rc = ares_parse_a_reply(abuf, alen, &he, NULL, NULL);
515 if (rc != ARES_SUCCESS) {
516 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
517 return;
521 pwm->tcp_conn->he = he;
522 pwm->tcp_conn->rc = do_connect(pwm, he->h_addrtype, he->h_addr);
525 static gpg_error_t _do_pwmd_tcp_connect_async(pwm_t *pwm, const char *host,
526 int port, const char *identity, const char *user,
527 const char *known_hosts, pwmd_async_cmd_t which)
529 pwmd_tcp_conn_t *conn;
530 gpg_error_t rc;
532 if (!pwm)
533 return GPG_ERR_INV_ARG;
535 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts,
536 which == ASYNC_CMD_HOSTKEY ? 1 : 0);
538 if (rc)
539 return rc;
541 conn->async = 1;
542 pwm->tcp_conn = conn;
543 pwm->tcp_conn->cmd = which;
545 if (pwm->tcp_conn->cmd == ASYNC_CMD_HOSTKEY)
546 pwm->tcp_conn->get_only = 1;
548 pwm->cmd = ASYNC_CMD_DNS;
549 pwm->state = ASYNC_PROCESS;
550 ares_init(&pwm->tcp_conn->chan);
551 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
552 dns_resolve_cb, pwm);
553 return 0;
556 gpg_error_t pwmd_ssh_connect_async(pwm_t *pwm, const char *host, int port,
557 const char *identity, const char *user, const char *known_hosts)
559 return _do_pwmd_tcp_connect_async(pwm, host, port, identity, user,
560 known_hosts, ASYNC_CMD_CONNECT);
563 static void *_ssh_malloc(size_t size, void **data)
565 return pwmd_malloc(size);
568 static void _ssh_free(void *ptr, void **data)
570 pwmd_free(ptr);
573 static void *_ssh_realloc(void *ptr, size_t size, void **data)
575 return pwmd_realloc(ptr, size);
578 static char *to_hex(const char *str, size_t slen)
580 int i;
581 char *buf = pwmd_malloc(slen*2+1);
583 if (!buf)
584 return NULL;
586 for (i = 0, buf[0] = 0; i < slen; i++) {
587 char tmp[3];
589 sprintf(tmp, "%02x", (unsigned char)str[i]);
590 strcat(buf, tmp);
593 return buf;
596 static int verify_host_key(pwm_t *pwm)
598 FILE *fp = fopen(pwm->tcp_conn->known_hosts, "r");
599 char *buf, *p;
601 if (!fp)
602 return 1;
604 buf = pwmd_malloc(LINE_MAX);
606 if (!buf)
607 goto fail;
609 while ((p = fgets(buf, LINE_MAX, fp))) {
610 if (*p == '#' || isspace(*p))
611 continue;
613 if (p[strlen(p)-1] == '\n')
614 p[strlen(p)-1] = 0;
616 if (!strcmp(buf, pwm->tcp_conn->hostkey))
617 goto done;
620 fail:
621 if (buf)
622 pwmd_free(buf);
624 fclose(fp);
625 return 1;
627 done:
628 pwmd_free(buf);
629 fclose(fp);
630 return 0;
633 static gpg_error_t authenticate_ssh(pwm_t *pwm)
635 const char *fp = libssh2_hostkey_hash(pwm->tcp_conn->session,
636 LIBSSH2_HOSTKEY_HASH_SHA1);
637 char *userauth;
639 pwm->tcp_conn->hostkey = to_hex(fp, 20);
641 if (!pwm->tcp_conn->hostkey)
642 return gpg_error_from_errno(ENOMEM);
644 if (pwm->tcp_conn->get_only)
645 return 0;
647 if (!fp || verify_host_key(pwm))
648 return GPG_ERR_BAD_CERT;
650 userauth = libssh2_userauth_list(pwm->tcp_conn->session,
651 pwm->tcp_conn->username, strlen(pwm->tcp_conn->username));
653 if (!userauth || !strstr(userauth, "publickey"))
654 return GPG_ERR_BAD_PIN_METHOD;
656 if (libssh2_userauth_publickey_fromfile(pwm->tcp_conn->session,
657 pwm->tcp_conn->username, pwm->tcp_conn->identity_pub,
658 pwm->tcp_conn->identity, NULL))
659 return GPG_ERR_BAD_SECKEY;
661 return 0;
664 static gpg_error_t setup_tcp_session(pwm_t *pwm)
666 assuan_context_t ctx;
667 struct assuan_io_hooks io_hooks = {read_hook, write_hook};
668 gpg_error_t rc;
670 pwm->tcp_conn->session = libssh2_session_init_ex(_ssh_malloc, _ssh_free,
671 _ssh_realloc, NULL);
673 if (!pwm->tcp_conn->session) {
674 rc = gpg_error_from_errno(ENOMEM);
675 goto fail;
678 if (libssh2_session_startup(pwm->tcp_conn->session, pwm->tcp_conn->fd)) {
679 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
680 goto fail;
683 rc = authenticate_ssh(pwm);
685 if (rc)
686 goto fail;
688 /* pwmd_get_hostkey(). */
689 if (pwm->tcp_conn->get_only) {
690 pwm->result = pwmd_strdup(pwm->tcp_conn->hostkey);
692 if (!pwm->result) {
693 rc = gpg_error_from_errno(ENOMEM);
694 goto fail;
697 return 0;
700 pwm->tcp_conn->channel = libssh2_channel_open_session(pwm->tcp_conn->session);
702 if (!pwm->tcp_conn->channel) {
703 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
704 goto fail;
707 if (libssh2_channel_shell(pwm->tcp_conn->channel)) {
708 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
709 goto fail;
712 assuan_set_io_hooks(&io_hooks);
713 rc = assuan_socket_connect_fd(&ctx, pwm->tcp_conn->fd, 0, pwm);
715 if (rc)
716 goto fail;
718 assuan_set_finish_handler(ctx, _ssh_assuan_deinit);
719 pwm->ctx = ctx;
720 rc = _socket_connect_finalize(pwm);
722 if (rc)
723 goto fail;
725 return 0;
727 fail:
728 free_tcp_conn(pwm->tcp_conn);
729 pwm->tcp_conn = NULL;
730 return rc;
733 static gpg_error_t _do_pwmd_tcp_connect(pwm_t *pwm, const char *host, int port,
734 const char *identity, const char *user, const char *known_hosts, int get)
736 pwmd_tcp_conn_t *conn;
737 gpg_error_t rc;
739 if (!pwm)
740 return GPG_ERR_INV_ARG;
742 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts, get);
744 if (rc)
745 return rc;
747 pwm->tcp_conn = conn;
748 pwm->tcp_conn->get_only = get;
749 pwm->cmd = ASYNC_CMD_DNS;
750 ares_init(&pwm->tcp_conn->chan);
751 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
752 dns_resolve_cb, pwm);
754 /* dns_resolve_cb() may have already been called. */
755 if (pwm->tcp_conn->rc) {
756 rc = pwm->tcp_conn->rc;
757 goto fail;
761 * Fake a blocking DNS lookup. libcares does a better job than
762 * getaddrinfo().
764 do {
765 fd_set rfds, wfds;
766 int n;
767 struct timeval tv;
769 FD_ZERO(&rfds);
770 FD_ZERO(&wfds);
771 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
772 ares_timeout(pwm->tcp_conn->chan, NULL, &tv);
773 #ifdef WITH_LIBPTH
774 n = pth_select(n, &rfds, &wfds, NULL, &tv);
775 #else
776 n = select(n, &rfds, &wfds, NULL, &tv);
777 #endif
779 if (n == -1) {
780 rc = gpg_error_from_syserror();
781 goto fail;
783 else if (n == 0) {
784 rc = GPG_ERR_TIMEOUT;
785 goto fail;
788 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
790 if (pwm->tcp_conn->rc)
791 break;
792 } while (pwm->cmd == ASYNC_CMD_DNS);
794 if (pwm->tcp_conn->rc) {
795 rc = pwm->tcp_conn->rc;
796 goto fail;
799 return setup_tcp_session(pwm);
801 fail:
802 return rc;
805 gpg_error_t pwmd_ssh_connect(pwm_t *pwm, const char *host, int port,
806 const char *identity, const char *user, const char *known_hosts)
808 return _do_pwmd_tcp_connect(pwm, host, port, identity, user, known_hosts, 0);
811 gpg_error_t pwmd_get_hostkey(pwm_t *pwm, const char *host, int port,
812 char **result)
814 char *hostkey;
815 gpg_error_t rc;
817 rc = _do_pwmd_tcp_connect(pwm, host, port, NULL, NULL, NULL, 1);
819 if (rc)
820 return rc;
822 hostkey = pwmd_strdup(pwm->tcp_conn->hostkey);
824 if (!hostkey)
825 rc = gpg_error_from_errno(ENOMEM);
827 *result = hostkey;
828 return rc;
831 gpg_error_t pwmd_get_hostkey_async(pwm_t *pwm, const char *host, int port)
833 return _do_pwmd_tcp_connect_async(pwm, host, port, NULL, NULL, NULL,
834 ASYNC_CMD_HOSTKEY);
838 * ssh://[username@]hostname[:port],identity,known_hosts
840 * Any missing parameters are checked for in init_tcp_conn().
842 static int parse_ssh_url(char *str, char **host, int *port, char **user,
843 char **identity, char **known_hosts)
845 char *p;
846 char *t;
847 int len;
849 *host = *user = *identity = *known_hosts = NULL;
850 *port = -1;
851 p = strrchr(str, '@');
853 if (p) {
854 len = strlen(str)-strlen(p)+1;
855 *user = pwmd_malloc(len);
856 snprintf(*user, len, "%s", str);
857 p++;
859 else
860 p = str;
862 t = strchr(p, ':');
864 if (t) {
865 len = strlen(p)-strlen(t)+1;
866 *host = pwmd_malloc(len);
867 snprintf(*host, len, "%s", p);
868 t++;
869 *port = atoi(t);
871 while (*t && isdigit(*t))
872 t++;
874 p = t;
877 t = strchr(p, ',');
879 if (t) {
880 char *t2;
882 if (!*host) {
883 len = strlen(p)-strlen(t)+1;
884 *host = pwmd_malloc(len);
885 snprintf(*host, len, "%s", p);
888 t++;
889 t2 = strchr(t, ',');
891 if (t2)
892 len = strlen(t)-strlen(t2)+1;
893 else
894 len = strlen(t)+1;
896 *identity = pwmd_malloc(len);
897 snprintf(*identity, len, "%s", t);
899 if (t2) {
900 t2++;
901 t += len+1;
902 len = strlen(t2)+1;
903 *known_hosts = pwmd_malloc(len);
904 snprintf(*known_hosts, len, "%s", t2);
907 else {
908 if (!*host) {
909 len = strlen(p)+1;
910 *host = pwmd_malloc(len);
911 snprintf(*host, len, "%s", p);
915 return 0;
917 #endif
919 static gpg_error_t _pwmd_connect_url(pwm_t *pwm, const char *url, int async)
921 char *p = (char *)url;
922 gpg_error_t rc;
924 if (!pwm || !url)
925 return GPG_ERR_INV_ARG;
927 if (!strncmp(p, "socket://", 9)) {
928 p += 9;
929 rc = pwmd_connect(pwm, p);
930 pwm->state = ASYNC_DONE;
931 return rc;
933 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
934 !strncmp(p, "ssh4://", 7)) {
935 #ifndef WITH_TCP
936 return GPG_ERR_NOT_IMPLEMENTED;
937 #else
938 char *host = NULL;
939 int port = -1;
940 char *identity = NULL;
941 char *known_hosts = NULL;
942 char *username = NULL;
944 if (!strncmp(p, "ssh6://", 7)) {
945 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV6);
946 p += 7;
948 else if (!strncmp(p, "ssh4://", 7)) {
949 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV4);
950 p += 7;
952 else {
953 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IP_ANY);
954 p += 6;
957 if (rc)
958 return rc;
960 rc = parse_ssh_url(p, &host, &port, &username, &identity,
961 &known_hosts);
963 if (rc)
964 return rc;
966 if (async)
967 rc = pwmd_ssh_connect_async(pwm, host, port, identity, username,
968 known_hosts);
969 else
970 rc = pwmd_ssh_connect(pwm, host, port, identity, username,
971 known_hosts);
973 if (host)
974 pwmd_free(host);
976 if (username)
977 pwmd_free(username);
979 if (identity)
980 pwmd_free(identity);
982 if (known_hosts)
983 pwmd_free(known_hosts);
985 return rc;
986 #endif
989 return GPG_ERR_UNSUPPORTED_PROTOCOL;
992 gpg_error_t pwmd_connect_url(pwm_t *pwm, const char *url)
994 return _pwmd_connect_url(pwm, url, 0);
997 gpg_error_t pwmd_connect_url_async(pwm_t *pwm, const char *url)
999 return _pwmd_connect_url(pwm, url, 1);
1002 static char *expand_homedir(char *str, struct passwd *pw)
1004 char *p = str;
1005 char *pwbuf = NULL;
1006 char *result;
1008 if (*p != '~' || *(p+1) != '/')
1009 return pwmd_strdup(p);
1011 if (!pw) {
1012 struct passwd t;
1014 pwbuf = _getpwuid(&t);
1016 if (!pwbuf)
1017 return NULL;
1019 pw = &t;
1022 p += 2;
1023 result = pwmd_strdup_printf("%s/%s", pw->pw_dir, p);
1025 if (pwbuf)
1026 pwmd_free(pwbuf);
1028 return result;
1031 gpg_error_t pwmd_connect(pwm_t *pwm, const char *path)
1033 char *socketpath = NULL;
1034 assuan_context_t ctx;
1035 struct passwd pw;
1036 char *pwbuf;
1037 gpg_error_t rc;
1039 if (!pwm)
1040 return GPG_ERR_INV_ARG;
1042 pwbuf = _getpwuid(&pw);
1044 if (!pwbuf)
1045 return gpg_error_from_errno(errno);
1047 if (!path || !*path) {
1048 socketpath = (char *)pwmd_malloc(strlen(pw.pw_dir) + strlen("/.pwmd/socket") + 1);
1049 sprintf(socketpath, "%s/.pwmd/socket", pw.pw_dir);
1051 else {
1052 socketpath = expand_homedir((char *)path, &pw);
1054 if (!socketpath) {
1055 pwmd_free(pwbuf);
1056 return gpg_error_from_errno(ENOMEM);
1060 pwmd_free(pwbuf);
1061 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
1062 pwmd_free(socketpath);
1064 if (rc)
1065 return rc;
1067 pwm->ctx = ctx;
1068 return _socket_connect_finalize(pwm);
1071 void pwmd_close(pwm_t *pwm)
1073 if (!pwm)
1074 return;
1076 if (pwm->ctx)
1077 assuan_disconnect(pwm->ctx);
1079 if (pwm->password)
1080 pwmd_free(pwm->password);
1082 if (pwm->title)
1083 pwmd_free(pwm->title);
1085 if (pwm->desc)
1086 pwmd_free(pwm->desc);
1088 if (pwm->prompt)
1089 pwmd_free(pwm->prompt);
1091 if (pwm->pinentry_tty)
1092 pwmd_free(pwm->pinentry_tty);
1094 if (pwm->pinentry_display)
1095 pwmd_free(pwm->pinentry_display);
1097 if (pwm->pinentry_term)
1098 pwmd_free(pwm->pinentry_term);
1100 if (pwm->lcctype)
1101 pwmd_free(pwm->lcctype);
1103 if (pwm->lcmessages)
1104 pwmd_free(pwm->lcmessages);
1106 if (pwm->filename)
1107 pwmd_free(pwm->filename);
1109 if (pwm->name)
1110 pwmd_free(pwm->name);
1112 #ifdef WITH_TCP
1113 if (pwm->tcp_conn)
1114 free_tcp_conn(pwm->tcp_conn);
1115 #endif
1117 pwmd_free(pwm);
1120 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
1122 membuf_t *mem = (membuf_t *)data;
1123 void *p;
1125 if (!buffer)
1126 return 0;
1128 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
1129 return 1;
1131 mem->buf = p;
1132 memcpy((char *)mem->buf + mem->len, buffer, len);
1133 mem->len += len;
1134 return 0;
1137 static int _inquire_cb(void *data, const char *keyword)
1139 pwm_t *pwm = (pwm_t *)data;
1140 gpg_error_t rc = 0;
1141 int flags = fcntl(pwm->fd, F_GETFL);
1143 /* Shouldn't get this far without a callback. */
1144 if (!pwm->inquire_func)
1145 return GPG_ERR_INV_ARG;
1147 /* Set to non-blocking so _pwmd_process() can return. */
1148 fcntl(pwm->fd, F_SETFL, O_NONBLOCK);
1150 for (;;) {
1151 char *result = NULL;
1152 size_t len;
1153 gpg_error_t arc;
1155 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
1156 rc = gpg_err_code(rc);
1158 if (rc == GPG_ERR_EOF || !rc) {
1159 if (len <= 0 || !result) {
1160 rc = 0;
1161 break;
1164 arc = assuan_send_data(pwm->ctx, result, len);
1166 if (rc == GPG_ERR_EOF) {
1167 rc = arc;
1168 break;
1171 rc = arc;
1173 else if (rc)
1174 break;
1176 if (!rc)
1177 rc = _pwmd_process(pwm);
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 rc = 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;
1326 * Used for processing status messages when not in an async command and for
1327 * waiting for the result from pwmd_open_async() and pwmd_save_async().
1329 static gpg_error_t _pwmd_process(pwm_t *pwm)
1331 gpg_error_t rc = 0;
1332 fd_set fds;
1333 struct timeval tv = {0, 0};
1334 int n;
1336 FD_ZERO(&fds);
1337 FD_SET(pwm->fd, &fds);
1338 #ifdef WITH_LIBPTH
1339 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
1340 #else
1341 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
1342 #endif
1344 if (n == -1)
1345 return gpg_error_from_syserror();
1347 if (n > 0) {
1348 if (FD_ISSET(pwm->fd, &fds))
1349 rc = parse_assuan_line(pwm);
1352 while (!rc && assuan_pending_line(pwm->ctx))
1353 rc = parse_assuan_line(pwm);
1355 return rc;
1358 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
1360 fd_set fds;
1361 int n;
1362 struct timeval tv = {0, 0};
1364 if (result)
1365 *result = NULL;
1367 if (!rc)
1368 return GPG_ERR_INV_ARG;
1370 *rc = 0;
1372 if (!pwm) {
1373 *rc = GPG_ERR_INV_ARG;
1374 return ASYNC_DONE;
1376 else if (!pwm->ctx) {
1377 switch (pwm->cmd) {
1378 default:
1379 *rc = GPG_ERR_INV_STATE;
1380 return ASYNC_DONE;
1381 #ifdef WITH_TCP
1382 case ASYNC_CMD_DNS:
1383 case ASYNC_CMD_CONNECT:
1384 case ASYNC_CMD_HOSTKEY:
1385 break;
1386 #endif
1390 /* When not in a command, this will let libassuan process status messages
1391 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
1392 * descriptor returned by pwmd_get_fd() to determine when this should be
1393 * called or call pwmd_pending_line() to determine whether a buffered line
1394 * needs to be processed. */
1395 if (pwm->cmd == ASYNC_CMD_NONE) {
1396 *rc = _pwmd_process(pwm);
1397 return ASYNC_DONE;
1400 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
1401 if (pwm->state == ASYNC_DONE) {
1402 reset_async(pwm, 0);
1403 return ASYNC_DONE;
1406 if (pwm->state != ASYNC_PROCESS) {
1407 *rc = GPG_ERR_INV_STATE;
1408 return ASYNC_DONE;
1411 #ifdef WITH_TCP
1412 if (pwm->cmd == ASYNC_CMD_DNS) {
1413 fd_set rfds, wfds;
1415 if (pwm->tcp_conn->rc) {
1416 *rc = pwm->tcp_conn->rc;
1417 reset_async(pwm, 1);
1418 return ASYNC_DONE;
1421 FD_ZERO(&rfds);
1422 FD_ZERO(&wfds);
1423 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
1425 /* Shouldn't happen. */
1426 if (!n)
1427 return pwm->state;
1429 #ifdef WITH_LIBPTH
1430 n = pth_select(n, &rfds, &wfds, NULL, &tv);
1431 #else
1432 n = select(n, &rfds, &wfds, NULL, &tv);
1433 #endif
1435 if (n > 0)
1436 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
1438 return pwm->state;
1440 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
1441 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
1442 int ret;
1443 socklen_t len = sizeof(int);
1445 FD_ZERO(&fds);
1446 FD_SET(pwm->tcp_conn->fd, &fds);
1447 #ifdef WITH_LIBPTH
1448 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1449 #else
1450 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1451 #endif
1453 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
1454 return pwm->state;
1455 else if (n == -1) {
1456 *rc = gpg_error_from_syserror();
1457 reset_async(pwm, 1);
1458 return ASYNC_DONE;
1461 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
1463 if (ret || n) {
1464 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
1465 reset_async(pwm, 1);
1466 return ASYNC_DONE;
1469 else if (pwm->tcp_conn->rc) {
1470 *rc = pwm->tcp_conn->rc;
1471 reset_async(pwm, 1);
1472 return ASYNC_DONE;
1475 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
1476 *rc = setup_tcp_session(pwm);
1478 if (!*rc) {
1479 switch (pwm->tcp_conn->cmd) {
1480 case ASYNC_CMD_HOSTKEY:
1481 if (!*rc)
1482 *result = pwm->result;
1483 break;
1484 default:
1485 break;
1489 return reset_async(pwm, *rc ? 1 : 0);
1491 #endif
1493 #ifdef WITH_PINENTRY
1494 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
1495 int status;
1497 if (pwm->nb_fd == -1) {
1498 *rc = GPG_ERR_INV_STATE;
1499 return reset_async(pwm, 0);
1502 FD_ZERO(&fds);
1503 FD_SET(pwm->nb_fd, &fds);
1504 FD_SET(pwm->fd, &fds);
1505 #ifdef WITH_LIBPTH
1506 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1507 #else
1508 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1509 #endif
1510 if (n == -1) {
1511 *rc = gpg_error_from_syserror();
1512 return reset_async(pwm, 0);
1515 if (n > 0 && FD_ISSET(pwm->nb_fd, &fds)) {
1516 pwmd_nb_status_t nb;
1517 #ifdef WITH_LIBPTH
1518 size_t len = pth_read(pwm->nb_fd, &nb, sizeof(nb));
1519 #else
1520 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
1521 #endif
1522 waitpid(pwm->nb_pid, &status, WNOHANG);
1524 if (len != sizeof(nb)) {
1525 *rc = gpg_error_from_syserror();
1526 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1529 *rc = nb.error;
1531 if (*rc == GPG_ERR_INV_PASSPHRASE && pwm->cmd == ASYNC_CMD_SAVE2) {
1532 reset_async(pwm, 0);
1533 *rc = pwmd_save_async2(pwm);
1534 return ASYNC_PROCESS;
1536 else if (*rc)
1537 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1539 if (pwm->cmd == ASYNC_CMD_SAVE2) {
1540 *rc = do_save_command(pwm, nb.password);
1541 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1542 return reset_async(pwm, 0);
1545 if (pwm->cmd == ASYNC_CMD_OPEN2) {
1546 *rc = do_open_command(pwm, pwm->filename, nb.password);
1547 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1549 if (*rc == GPG_ERR_INV_PASSPHRASE) {
1550 if (++pwm->pin_try < pwm->pinentry_tries) {
1551 int n = pwm->pin_try;
1553 reset_async(pwm, 0);
1554 pwm->pin_try = n;
1555 pwm->cmd = ASYNC_CMD_OPEN2;
1556 *rc = pwmd_open_async2(pwm, pwm->filename);
1558 if (*rc)
1559 return reset_async(pwm, 1);
1561 return pwm->state;
1565 return reset_async(pwm, *rc ? 1 : 0);
1569 /* Fall through so status messages can be processed during the
1570 * pinentry. */
1572 #endif
1574 if (pwm->fd < 0) {
1575 *rc = GPG_ERR_INV_STATE;
1576 return reset_async(pwm, 0);
1579 /* This is for pwmd_open_async() and pwmd_save_async(). For pinentry
1580 * retries. */
1581 *rc = _pwmd_process(pwm);
1583 if (*rc && gpg_err_code(*rc) != GPG_ERR_INV_PASSPHRASE) {
1584 reset_async(pwm, 0);
1585 return ASYNC_DONE;
1588 #ifdef WITH_TCP
1589 if (!pwm->tcp_conn && pwm->cmd == ASYNC_CMD_OPEN &&
1590 #else
1591 if (pwm->cmd == ASYNC_CMD_OPEN &&
1592 #endif
1593 gpg_err_code(*rc) == GPG_ERR_INV_PASSPHRASE &&
1594 ++pwm->pin_try < pwm->pinentry_tries) {
1595 pwm->state = ASYNC_INIT;
1596 *rc = pwmd_open_async(pwm, pwm->filename);
1599 if (*rc)
1600 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN ? 1 : 0);
1602 if (pwm->state == ASYNC_DONE) {
1603 reset_async(pwm, 0);
1604 return ASYNC_DONE;
1607 return pwm->state;
1610 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
1611 char **result, const char *cmd)
1613 membuf_t data;
1614 gpg_error_t rc;
1616 data.len = 0;
1617 data.buf = NULL;
1619 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
1620 pwm->status_func, pwm->status_data);
1622 if (rc) {
1623 if (data.buf) {
1624 pwmd_free(data.buf);
1625 data.buf = NULL;
1628 else {
1629 if (data.buf) {
1630 mem_realloc_cb(&data, "", 1);
1632 if (!result) {
1633 pwmd_free(data.buf);
1634 rc = GPG_ERR_INV_ARG;
1636 else
1637 *result = (char *)data.buf;
1641 return gpg_err_code(rc);
1644 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_cb_t fn,
1645 void *data)
1647 if (!pwm || !cmd || !fn)
1648 return GPG_ERR_INV_ARG;
1650 if (!pwm->ctx)
1651 return GPG_ERR_INV_STATE;
1653 pwm->inquire_func = fn;
1654 pwm->inquire_data = data;
1655 return assuan_command(pwm, pwm->ctx, NULL, cmd);
1658 #ifdef WITH_PINENTRY
1659 static gpg_error_t terminate_pinentry(pwm_t *pwm)
1661 pid_t pid = pwm->pid;
1663 pwm->pid = -1;
1665 if (!pwm || pid == -1)
1666 return GPG_ERR_INV_ARG;
1668 if (kill(pid, 0) == 0) {
1669 if (kill(pid, SIGTERM) == -1) {
1670 if (kill(pid, SIGKILL) == -1)
1671 return gpg_error_from_errno(errno);
1674 else
1675 return gpg_error_from_errno(errno);
1677 return 0;
1680 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
1682 char *tmp, *desc;
1683 gpg_error_t error;
1685 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
1687 if (!tmp)
1688 return gpg_error_from_errno(ENOMEM);
1690 if (!pwm->title)
1691 pwm->title = pwmd_strdup_printf(N_("Password Manager Daemon: %s"),
1692 pwm->name ? pwm->name : "libpwmd");
1694 if (!pwm->title)
1695 goto fail_no_mem;
1697 if (!pwm->prompt)
1698 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
1700 if (!pwm->prompt)
1701 goto fail_no_mem;
1703 if (!pwm->desc && (which == PINENTRY_OPEN || which == PINENTRY_SAVE)) {
1704 if (which == PINENTRY_OPEN)
1705 desc = pwmd_strdup_printf(N_("A passphrase is required to open the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1706 else
1707 desc = pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1709 if (!desc)
1710 goto fail_no_mem;
1713 if (pwm->desc)
1714 desc = pwm->desc;
1716 switch (which) {
1717 case PINENTRY_OPEN:
1718 case PINENTRY_SAVE:
1719 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s", desc);
1721 if (pwm->desc != desc)
1722 pwmd_free(desc);
1723 break;
1724 case PINENTRY_OPEN_FAILED:
1725 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1726 N_("Invalid passphrase, please try again."));
1727 break;
1728 case PINENTRY_SAVE_CONFIRM:
1729 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1730 N_("Please type the passphrase again for confirmation."));
1731 break;
1734 error = pinentry_command(pwm, NULL, tmp);
1736 if (error) {
1737 pwmd_free(tmp);
1738 return error;
1741 snprintf(tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->prompt);
1742 error = pinentry_command(pwm, NULL, tmp);
1744 if (error) {
1745 pwmd_free(tmp);
1746 return error;
1749 snprintf(tmp, ASSUAN_LINELENGTH, "SETDESC %s", pwm->title);
1750 error = pinentry_command(pwm, NULL, tmp);
1751 pwmd_free(tmp);
1752 return error;
1754 fail_no_mem:
1755 pwmd_free(tmp);
1756 return gpg_error_from_errno(ENOMEM);
1759 static void update_pinentry_settings(pwm_t *pwm)
1761 FILE *fp;
1762 char buf[LINE_MAX];
1763 char *p;
1764 struct passwd pw;
1765 char *pwbuf = _getpwuid(&pw);
1767 if (!pwbuf)
1768 return;
1770 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw.pw_dir);
1772 if ((fp = fopen(buf, "r")) == NULL) {
1773 pwmd_free(pwbuf);
1774 return;
1777 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
1778 char name[32], val[256];
1780 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
1781 continue;
1783 if (strcasecmp(name, "TTYNAME") == 0) {
1784 pwmd_free(pwm->pinentry_tty);
1785 pwm->pinentry_tty = pwmd_strdup(val);
1787 else if (strcasecmp(name, "TTYTYPE") == 0) {
1788 pwmd_free(pwm->pinentry_term);
1789 pwm->pinentry_term = pwmd_strdup(val);
1791 else if (strcasecmp(name, "DISPLAY") == 0) {
1792 pwmd_free(pwm->pinentry_display);
1793 pwm->pinentry_display = pwmd_strdup(val);
1795 else if (strcasecmp(name, "PATH") == 0) {
1796 pwmd_free(pwm->pinentry_path);
1797 pwm->pinentry_path = expand_homedir(val, &pw);
1801 pwmd_free(pwbuf);
1802 fclose(fp);
1805 static gpg_error_t launch_pinentry(pwm_t *pwm)
1807 int rc;
1808 assuan_context_t ctx;
1809 int child_list[] = {-1};
1810 char *display = getenv("DISPLAY");
1811 const char *argv[10];
1812 const char **p = argv;
1813 int have_display = 0;
1814 char *tty = NULL;
1815 char *ttybuf = NULL;
1817 update_pinentry_settings(pwm);
1819 if (pwm->pinentry_display || display)
1820 have_display = 1;
1821 else {
1822 if (!pwm->pinentry_tty) {
1823 ttybuf = pwmd_malloc(255);
1825 if (!ttybuf)
1826 return gpg_error_from_errno(ENOMEM);
1828 rc = ttyname_r(STDOUT_FILENO, ttybuf, 255);
1830 if (rc) {
1831 pwmd_free(ttybuf);
1832 return gpg_error_from_errno(rc);
1835 tty = ttybuf;
1837 else
1838 tty = pwm->pinentry_tty;
1841 if (!have_display && !tty)
1842 return GPG_ERR_ENOTTY;
1844 *p++ = "pinentry";
1845 *p++ = have_display ? "--display" : "--ttyname";
1846 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
1848 if (pwm->lcctype) {
1849 *p++ = "--lc-ctype";
1850 *p++ = pwm->lcctype;
1853 if (pwm->lcmessages) {
1854 *p++ = "--lc-messages";
1855 *p++ = pwm->lcmessages;
1858 *p = NULL;
1860 if (!have_display) {
1861 *p++ = "--ttytype";
1862 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
1863 *p = NULL;
1866 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
1868 if (ttybuf)
1869 pwmd_free(ttybuf);
1871 if (rc)
1872 return rc;
1874 pwm->pid = assuan_get_pid(ctx);
1875 pwm->pctx = ctx;
1876 return set_pinentry_strings(pwm, 0);
1879 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
1881 gpg_error_t n;
1883 if (!pwm->pctx) {
1884 n = launch_pinentry(pwm);
1886 if (n)
1887 return n;
1890 return assuan_command(pwm, pwm->pctx, result, cmd);
1893 static void pinentry_disconnect(pwm_t *pwm)
1895 if (pwm->pctx)
1896 assuan_disconnect(pwm->pctx);
1898 pwm->pctx = NULL;
1899 pwm->pid = -1;
1903 * Only called from a child process.
1905 static void catchsig(int sig)
1907 switch (sig) {
1908 case SIGALRM:
1909 if (gelapsed++ >= gtimeout) {
1910 terminate_pinentry(gpwm);
1911 gerror = GPG_ERR_TIMEOUT;
1913 else
1914 alarm(1);
1915 break;
1916 default:
1917 break;
1920 #endif
1923 * Borrowed from libassuan.
1925 static char *percent_escape(const char *atext)
1927 const unsigned char *s;
1928 int len = strlen(atext) * 3 + 1;
1929 char *buf = (char *)pwmd_malloc(len), *p = buf;
1931 if (!buf)
1932 return NULL;
1934 for (s=(const unsigned char *)atext; *s; s++) {
1935 if (*s < ' ') {
1936 sprintf (p, "%%%02X", *s);
1937 p += 3;
1939 else
1940 *p++ = *s;
1943 *p = 0;
1944 return buf;
1947 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
1949 if (!cmd)
1950 return GPG_ERR_INV_ARG;
1952 return assuan_command(pwm, pwm->ctx, result, cmd);
1955 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1956 va_list ap)
1958 char *buf;
1959 size_t len;
1960 gpg_error_t error;
1961 va_list ap2;
1963 if (!pwm || !cmd)
1964 return GPG_ERR_INV_ARG;
1966 if (!pwm->ctx)
1967 return GPG_ERR_INV_STATE;
1970 * C99 allows the dst pointer to be null which will calculate the length
1971 * of the would-be result and return it.
1973 va_copy(ap2, ap);
1974 len = vsnprintf(NULL, 0, cmd, ap)+1;
1975 buf = (char *)pwmd_malloc(len);
1977 if (!buf) {
1978 va_end(ap2);
1979 return gpg_error_from_errno(ENOMEM);
1982 len = vsnprintf(buf, len, cmd, ap2);
1983 va_end(ap2);
1985 if (buf[strlen(buf)-1] == '\n')
1986 buf[strlen(buf)-1] = 0;
1988 if (buf[strlen(buf)-1] == '\r')
1989 buf[strlen(buf)-1] = 0;
1991 error = send_command(pwm, result, buf);
1992 pwmd_free(buf);
1993 return error;
1996 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1998 va_list ap;
1999 gpg_error_t error;
2001 if (!pwm || !cmd)
2002 return GPG_ERR_INV_ARG;
2004 if (!pwm->ctx)
2005 return GPG_ERR_INV_STATE;
2007 if (result)
2008 *result = NULL;
2010 va_start(ap, cmd);
2011 error = pwmd_command_ap(pwm, result, cmd, ap);
2012 va_end(ap);
2013 return error;
2016 #ifdef WITH_PINENTRY
2017 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
2019 if (gtimeout) {
2020 signal(SIGALRM, catchsig);
2021 alarm(1);
2024 *result = NULL;
2025 return pinentry_command(pwm, result, "GETPIN");
2028 static gpg_error_t getpin(pwm_t *pwm, char **result, int which)
2030 gpg_error_t rc;
2032 gerror = 0;
2033 rc = set_pinentry_strings(pwm, which);
2035 if (rc) {
2036 pinentry_disconnect(pwm);
2037 return rc;
2040 rc = do_getpin(pwm, result);
2043 * Since there was input cancel any timeout setting.
2045 alarm(0);
2046 signal(SIGALRM, SIG_DFL);
2048 if (rc) {
2049 if (pwm->pctx)
2050 pinentry_disconnect(pwm);
2052 /* This lets pwmd_open2() with PWMD_OPTION_PINENTRY_TIMEOUT work. */
2053 if (rc== GPG_ERR_EOF && gerror == GPG_ERR_TIMEOUT)
2054 return gerror;
2056 return rc;
2059 return 0;
2061 #endif
2063 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
2065 char *buf;
2066 gpg_error_t error;
2067 char *result = NULL;
2069 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2071 if (!buf)
2072 return gpg_error_from_errno(ENOMEM);
2074 snprintf(buf, ASSUAN_LINELENGTH, "OPEN %s %s", filename,
2075 password ? password : "");
2076 error = send_command(pwm, &result, buf);
2077 pwmd_free(buf);
2079 if (error && result)
2080 pwmd_free(result);
2082 return error;
2085 static gpg_error_t send_pinentry_options(pwm_t *pwm)
2087 gpg_error_t rc;
2089 if (pwm->pinentry_path) {
2090 rc = pwmd_command(pwm, NULL, "OPTION PATH=%s", pwm->pinentry_path);
2092 if (rc)
2093 return rc;
2096 if (pwm->pinentry_tty) {
2097 rc = pwmd_command(pwm, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
2099 if (rc)
2100 return rc;
2103 if (pwm->pinentry_term) {
2104 rc = pwmd_command(pwm, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
2106 if (rc)
2107 return rc;
2110 if (pwm->pinentry_display) {
2111 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->pinentry_display);
2113 if (rc)
2114 return rc;
2117 if (pwm->title) {
2118 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->title);
2120 if (rc)
2121 return rc;
2124 if (pwm->desc) {
2125 rc = pwmd_command(pwm, NULL, "OPTION DESC=%s", pwm->desc);
2127 if (rc)
2128 return rc;
2131 if (pwm->prompt) {
2132 rc = pwmd_command(pwm, NULL, "OPTION PROMPT=%s", pwm->prompt);
2134 if (rc)
2135 return rc;
2138 if (pwm->lcctype) {
2139 rc = pwmd_command(pwm, NULL, "OPTION LC_CTYPE=%s", pwm->lcctype);
2141 if (rc)
2142 return rc;
2145 if (pwm->lcmessages) {
2146 rc = pwmd_command(pwm, NULL, "OPTION LC_MESSAGES=%s", pwm->lcmessages);
2148 if (rc)
2149 return rc;
2152 if (pwm->pinentry_timeout >= 0) {
2153 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=%i", pwm->pinentry_timeout);
2155 if (rc)
2156 return rc;
2159 return 0;
2162 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb,
2163 int local_pinentry)
2165 char *result = NULL;
2166 char *password = NULL;
2167 char *path;
2168 int pin_try;
2169 gpg_error_t rc;
2171 if (!pwm || !filename || !*filename)
2172 return GPG_ERR_INV_ARG;
2174 if (!pwm->ctx)
2175 return GPG_ERR_INV_STATE;
2177 pin_try = pwm->pinentry_tries - 1;
2180 * Avoid calling pinentry if the password is cached on the server or if
2181 * this is a new file. pwmd version 2 adds a VERSION command which is
2182 * determined in _socket_connect_finalize(). If the server is version 2,
2183 * ISCACHED can determine if a file exists.
2185 #ifdef WITH_TCP
2186 if (!pwm->tcp_conn && pwm->version == PWMD_V1) {
2187 #else
2188 if (pwm->version == PWMD_V1) {
2189 #endif
2190 rc = pwmd_command(pwm, &result, "GETCONFIG data_directory");
2192 if (rc)
2193 return rc;
2195 path = pwmd_strdup_printf("%s/%s", result, filename);
2196 pwmd_free(result);
2198 if (!path)
2199 return gpg_error_from_errno(ENOMEM);
2201 if (access(path, R_OK) == -1) {
2202 if (errno == ENOENT) {
2203 pwmd_free(path);
2204 goto gotpassword;
2208 pwmd_free(path);
2211 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
2213 if (gpg_err_code(rc) == GPG_ERR_ENOENT)
2214 goto gotpassword;
2216 if (rc && rc != GPG_ERR_NOT_FOUND)
2217 return rc;
2219 if (rc == GPG_ERR_NOT_FOUND) {
2220 if (pwm->password) {
2221 password = pwm->password;
2222 goto gotpassword;
2225 if (pwm->passfunc) {
2226 rc = pwm->passfunc(pwm->passdata, &password);
2228 if (rc)
2229 return rc;
2231 goto gotpassword;
2235 #ifdef WITH_PINENTRY
2236 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2237 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
2239 if (rc)
2240 return rc;
2242 if (!pwm->filename)
2243 pwm->filename = pwmd_strdup(filename);
2245 if (!pwm->filename)
2246 return gpg_error_from_errno(ENOMEM);
2248 /* Get the passphrase using the LOCAL pinentry. */
2249 if (nb) {
2250 int p[2];
2251 pid_t pid;
2252 pwmd_nb_status_t pw;
2254 if (pipe(p) == -1)
2255 return gpg_error_from_syserror();
2257 #ifdef WITH_LIBPTH
2258 pid = pth_fork();
2259 #else
2260 pid = fork();
2261 #endif
2263 switch (pid) {
2264 case 0:
2265 close(p[0]);
2266 pw.fd = p[0];
2268 if (pwm->pinentry_timeout != 0) {
2269 gpwm = pwm;
2270 gtimeout = abs(pwm->pinentry_timeout);
2271 gelapsed = 0;
2274 pw.error = getpin(pwm, &password, PINENTRY_OPEN);
2276 if (gtimeout && gelapsed >= gtimeout)
2277 pw.error = GPG_ERR_TIMEOUT;
2279 if (!pw.error)
2280 snprintf(pw.password, sizeof(pw.password), "%s",
2281 password);
2283 pinentry_disconnect(pwm);
2284 #ifdef WITH_LIBPTH
2285 pth_write(p[1], &pw, sizeof(pw));
2286 #else
2287 write(p[1], &pw, sizeof(pw));
2288 #endif
2289 memset(&pw, 0, sizeof(pw));
2290 close(p[1]);
2291 _exit(0);
2292 break;
2293 case -1:
2294 rc = gpg_error_from_syserror();
2295 close(p[0]);
2296 close(p[1]);
2297 return rc;
2298 default:
2299 break;
2302 close(p[1]);
2303 pwm->nb_fd = p[0];
2304 pwm->nb_pid = pid;
2305 return 0;
2308 if (pwm->pinentry_timeout != 0) {
2309 gpwm = pwm;
2310 gtimeout = abs(pwm->pinentry_timeout);
2311 gelapsed = 0;
2314 rc = getpin(pwm, &password, PINENTRY_OPEN);
2316 /* Don't timeout when an invalid passphrase was entered. */
2317 gtimeout = 0;
2319 if (rc)
2320 return rc;
2322 #endif
2324 gotpassword:
2325 pwm->state = ASYNC_DONE;
2327 #ifdef WITH_TCP
2328 if (!local_pinentry && !pwm->tcp_conn) {
2329 #else
2330 if (!local_pinentry) {
2331 #endif
2332 rc = send_pinentry_options(pwm);
2334 if (rc)
2335 return rc;
2338 rc = do_open_command(pwm, filename, password);
2341 * Keep the user defined password set with pwmd_setopt(). The password may
2342 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
2344 if (!pwm->passfunc && password && password != pwm->password)
2345 pwmd_free(password);
2347 #ifdef WITH_TCP
2348 if (rc == GPG_ERR_INV_PASSPHRASE && !pwm->tcp_conn) {
2349 #else
2350 if (rc == GPG_ERR_INV_PASSPHRASE) {
2351 #endif
2352 if (pin_try-- > 0 && !nb) {
2354 #ifdef WITH_PINENTRY
2355 if (local_pinentry)
2356 rc = getpin(pwm, &password, PINENTRY_OPEN_FAILED);
2357 else
2358 #endif
2359 rc = pwmd_command(pwm, &result, "OPTION TITLE=%s",
2360 N_("Invalid passphrase, please try again."));
2362 if (rc)
2363 return rc;
2365 goto gotpassword;
2368 #ifdef WITH_PINENTRY
2369 if (nb)
2370 pinentry_disconnect(pwm);
2371 #endif
2373 return rc;
2376 if (!rc) {
2377 if (pwm->filename)
2378 pwmd_free(pwm->filename);
2380 pwm->filename = pwmd_strdup(filename);
2383 return rc;
2386 gpg_error_t pwmd_open2(pwm_t *pwm, const char *filename)
2388 #ifndef WITH_PINENTRY
2389 return GPG_ERR_NOT_IMPLEMENTED;
2390 #else
2391 return do_pwmd_open(pwm, filename, 0, 1);
2392 #endif
2395 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
2397 return do_pwmd_open(pwm, filename, 0, 0);
2400 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
2402 #ifndef WITH_PINENTRY
2403 return GPG_ERR_NOT_IMPLEMENTED;
2404 #else
2405 gpg_error_t rc;
2407 if (!pwm || !filename)
2408 return GPG_ERR_INV_ARG;
2410 if (!pwm->ctx)
2411 return GPG_ERR_INV_STATE;
2413 if (pwm->cmd != ASYNC_CMD_OPEN2)
2414 pwm->pin_try = 0;
2416 pwm->cmd = ASYNC_CMD_OPEN2;
2417 pwm->state = ASYNC_PROCESS;
2418 rc = do_pwmd_open(pwm, filename, 1, 1);
2420 if (rc)
2421 reset_async(pwm, 1);
2423 return rc;
2424 #endif
2427 #ifdef WITH_PINENTRY
2428 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
2430 int confirm = 0;
2431 gpg_error_t error;
2432 char *result = NULL;
2434 again:
2435 error = getpin(pwm, &result, confirm ? PINENTRY_SAVE_CONFIRM : PINENTRY_SAVE);
2437 if (error) {
2438 if (pwm->pctx)
2439 pinentry_disconnect(pwm);
2441 if (*password)
2442 pwmd_free(*password);
2444 return error;
2447 if (!confirm++) {
2448 *password = result;
2449 goto again;
2452 if (strcmp(*password, result)) {
2453 pwmd_free(*password);
2454 pwmd_free(result);
2455 confirm = 0;
2456 *password = NULL;
2457 goto again;
2460 pwmd_free(result);
2461 pinentry_disconnect(pwm);
2462 return 0;
2464 #endif
2466 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
2468 char *buf;
2469 gpg_error_t error;
2470 char *result = NULL;
2472 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2474 if (!buf)
2475 return gpg_error_from_errno(ENOMEM);
2477 snprintf(buf, ASSUAN_LINELENGTH, "SAVE %s", password ? password : "");
2478 error = send_command(pwm, &result, buf);
2479 pwmd_free(buf);
2481 if (error && result)
2482 pwmd_free(result);
2484 return error;
2487 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb, int local_pinentry)
2489 char *result = NULL;
2490 char *password = NULL;
2491 gpg_error_t rc;
2493 if (!pwm)
2494 return GPG_ERR_INV_ARG;
2496 if (!pwm->ctx)
2497 return GPG_ERR_INV_STATE;
2499 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
2501 if (rc && rc != GPG_ERR_NOT_FOUND)
2502 return rc;
2504 if (rc == GPG_ERR_NOT_FOUND) {
2505 if (pwm->password) {
2506 password = pwm->password;
2507 goto gotpassword;
2510 if (pwm->passfunc) {
2511 rc = pwm->passfunc(pwm->passdata, &password);
2513 if (rc)
2514 return rc;
2516 goto gotpassword;
2520 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2521 #ifdef WITH_PINENTRY
2522 /* Get the password using the LOCAL pinentry. */
2523 if (nb) {
2524 int p[2];
2525 pid_t pid;
2526 pwmd_nb_status_t pw;
2528 if (pipe(p) == -1)
2529 return gpg_error_from_syserror();
2531 #ifdef WITH_LIBPTH
2532 pid = pth_fork();
2533 #else
2534 pid = fork();
2535 #endif
2537 switch (pid) {
2538 case 0:
2539 close(p[0]);
2540 pw.fd = p[0];
2541 password = NULL;
2542 pw.error = do_save_getpin(pwm, &password);
2543 pinentry_disconnect(pwm);
2544 snprintf(pw.password, sizeof(pw.password), "%s",
2545 password);
2546 #ifdef WITH_LIBPTH
2547 pth_write(p[1], &pw, sizeof(pw));
2548 #else
2549 write(p[1], &pw, sizeof(pw));
2550 #endif
2551 memset(&pw, 0, sizeof(pw));
2552 close(p[1]);
2553 _exit(0);
2554 break;
2555 case -1:
2556 rc = gpg_error_from_syserror();
2557 close(p[0]);
2558 close(p[1]);
2559 return rc;
2560 default:
2561 break;
2564 close(p[1]);
2565 pwm->nb_fd = p[0];
2566 pwm->nb_pid = pid;
2567 return 0;
2570 rc = do_save_getpin(pwm, &password);
2572 if (rc)
2573 return rc;
2574 #endif
2576 else
2577 pwm->state = ASYNC_DONE;
2579 gotpassword:
2580 #ifdef WITH_TCP
2581 if (!local_pinentry && !pwm->tcp_conn) {
2582 #else
2583 if (!local_pinentry) {
2584 #endif
2585 rc = send_pinentry_options(pwm);
2587 if (rc)
2588 return rc;
2591 rc = do_save_command(pwm, password);
2593 if (!pwm->passfunc && password && password != pwm->password)
2594 pwmd_free(password);
2596 return rc;
2599 gpg_error_t pwmd_save_async2(pwm_t *pwm)
2601 #ifndef WITH_PINENTRY
2602 return GPG_ERR_NOT_IMPLEMENTED;
2603 #else
2604 gpg_error_t rc;
2606 if (!pwm)
2607 return GPG_ERR_INV_ARG;
2609 if (!pwm->ctx)
2610 return GPG_ERR_INV_STATE;
2612 pwm->cmd = ASYNC_CMD_SAVE2;
2613 pwm->state = ASYNC_PROCESS;
2614 rc = do_pwmd_save(pwm, 1, 1);
2616 if (rc)
2617 reset_async(pwm, 0);
2619 return rc;
2620 #endif
2623 gpg_error_t pwmd_save2(pwm_t *pwm)
2625 #ifndef WITH_PINENTRY
2626 return GPG_ERR_NOT_IMPLEMENTED;
2627 #else
2628 return do_pwmd_save(pwm, 0, 1);
2629 #endif
2632 gpg_error_t pwmd_save(pwm_t *pwm)
2634 return do_pwmd_save(pwm, 0, 0);
2637 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
2639 va_list ap;
2640 int n = va_arg(ap, int);
2641 char *arg1;
2642 gpg_error_t error = 0;
2644 if (!pwm)
2645 return GPG_ERR_INV_ARG;
2647 va_start(ap, opt);
2649 switch (opt) {
2650 case PWMD_OPTION_STATUS_CB:
2651 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
2652 break;
2653 case PWMD_OPTION_STATUS_DATA:
2654 pwm->status_data = va_arg(ap, void *);
2655 break;
2656 case PWMD_OPTION_PASSPHRASE_CB:
2657 pwm->passfunc = va_arg(ap, pwmd_passphrase_cb_t);
2658 break;
2659 case PWMD_OPTION_PASSPHRASE_DATA:
2660 pwm->passdata = va_arg(ap, void *);
2661 break;
2662 case PWMD_OPTION_PASSPHRASE:
2663 arg1 = va_arg(ap, char *);
2665 if (pwm->password)
2666 pwmd_free(pwm->password);
2668 pwm->password = pwmd_strdup(arg1);
2669 break;
2670 case PWMD_OPTION_PINENTRY_TRIES:
2671 n = va_arg(ap, int);
2673 if (n <= 0) {
2674 va_end(ap);
2675 error = GPG_ERR_INV_VALUE;
2677 else
2678 pwm->pinentry_tries = n;
2679 break;
2680 case PWMD_OPTION_PINENTRY_TIMEOUT:
2681 n = va_arg(ap, int);
2683 if (n < 0) {
2684 va_end(ap);
2685 error = GPG_ERR_INV_VALUE;
2687 else
2688 pwm->pinentry_timeout = n;
2689 break;
2690 case PWMD_OPTION_PINENTRY_PATH:
2691 if (pwm->pinentry_path)
2692 pwmd_free(pwm->pinentry_path);
2694 pwm->pinentry_path = expand_homedir(va_arg(ap, char *), NULL);
2695 break;
2696 case PWMD_OPTION_PINENTRY_TTY:
2697 if (pwm->pinentry_tty)
2698 pwmd_free(pwm->pinentry_tty);
2700 pwm->pinentry_tty = pwmd_strdup(va_arg(ap, char *));
2701 break;
2702 case PWMD_OPTION_PINENTRY_DISPLAY:
2703 if (pwm->pinentry_display)
2704 pwmd_free(pwm->pinentry_display);
2706 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
2707 break;
2708 case PWMD_OPTION_PINENTRY_TERM:
2709 if (pwm->pinentry_term)
2710 pwmd_free(pwm->pinentry_term);
2712 pwm->pinentry_term = pwmd_strdup(va_arg(ap, char *));
2713 break;
2714 case PWMD_OPTION_PINENTRY_TITLE:
2715 if (pwm->title)
2716 pwmd_free(pwm->title);
2718 pwm->title = percent_escape(va_arg(ap, char *));
2719 break;
2720 case PWMD_OPTION_PINENTRY_PROMPT:
2721 if (pwm->prompt)
2722 pwmd_free(pwm->prompt);
2724 pwm->prompt = percent_escape(va_arg(ap, char *));
2725 break;
2726 case PWMD_OPTION_PINENTRY_DESC:
2727 if (pwm->desc)
2728 pwmd_free(pwm->desc);
2730 pwm->desc = percent_escape(va_arg(ap, char *));
2731 break;
2732 case PWMD_OPTION_PINENTRY_LC_CTYPE:
2733 if (pwm->lcctype)
2734 pwmd_free(pwm->lcctype);
2736 pwm->lcctype = pwmd_strdup(va_arg(ap, char *));
2737 break;
2738 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
2739 if (pwm->lcmessages)
2740 pwmd_free(pwm->lcmessages);
2742 pwm->lcmessages = pwmd_strdup(va_arg(ap, char *));
2743 break;
2744 #ifdef WITH_TCP
2745 case PWMD_OPTION_IP_VERSION:
2746 n = va_arg(ap, int);
2748 switch (n) {
2749 case PWMD_IP_ANY:
2750 case PWMD_IPV4:
2751 case PWMD_IPV6:
2752 pwm->prot = n;
2753 break;
2754 default:
2755 error = GPG_ERR_INV_VALUE;
2756 break;
2759 va_end(ap);
2760 break;
2761 #endif
2762 default:
2763 error = GPG_ERR_NOT_IMPLEMENTED;
2764 break;
2767 va_end(ap);
2768 return error;
2771 gpg_error_t pwmd_get_fds(pwm_t *pwm, pwmd_fd_t *fds, int *n_fds)
2773 int in_total;
2774 int fd = 0;
2775 #ifdef WITH_TCP
2776 int afds[ARES_GETSOCK_MAXNUM];
2777 int got_sock = 0;
2778 int n, i;
2779 #endif
2781 if (!pwm || !fds || !n_fds || *n_fds <= 0)
2782 return GPG_ERR_INV_ARG;
2784 in_total = *n_fds;
2785 #ifdef WITH_TCP
2786 memset(afds, 0, sizeof(int)*ARES_GETSOCK_MAXNUM);
2787 #endif
2788 memset(fds, 0, sizeof(pwmd_fd_t)*in_total);
2789 *n_fds = 0;
2791 switch (pwm->cmd) {
2792 default:
2793 case ASYNC_CMD_NONE:
2794 case ASYNC_CMD_OPEN:
2795 case ASYNC_CMD_SAVE:
2796 #ifdef WITH_PINENTRY
2797 async1:
2798 #endif
2799 if (pwm->fd == -1)
2800 return GPG_ERR_INV_STATE;
2802 (*n_fds)++;
2803 fds[fd].fd = pwm->fd;
2804 fds[fd++].flags = PWMD_FD_READABLE;
2805 return 0;
2806 #ifdef WITH_PINENTRY
2807 case ASYNC_CMD_OPEN2:
2808 case ASYNC_CMD_SAVE2:
2809 /* The command has already completed (cached or new). */
2810 if (pwm->state == ASYNC_DONE)
2811 return 0;
2813 if (pwm->nb_fd == -1)
2814 return GPG_ERR_INV_STATE;
2816 (*n_fds)++;
2817 fds[fd].fd = pwm->nb_fd;
2818 fds[fd++].flags = PWMD_FD_READABLE;
2819 goto async1;
2820 #endif
2821 #ifdef WITH_TCP
2822 case ASYNC_CMD_DNS:
2823 if (!pwm->tcp_conn || !pwm->tcp_conn->chan)
2824 return GPG_ERR_INV_STATE;
2826 n = ares_getsock(pwm->tcp_conn->chan, afds, ARES_GETSOCK_MAXNUM);
2828 for (i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
2829 got_sock = 0;
2831 if (fd > in_total) {
2832 *n_fds = fd;
2833 return GPG_ERR_ERANGE;
2836 if (ARES_GETSOCK_READABLE(n, i)) {
2837 got_sock++;
2838 fds[fd].flags |= PWMD_FD_READABLE;
2841 if (ARES_GETSOCK_WRITABLE(n, i)) {
2842 got_sock++;
2843 fds[fd].flags |= PWMD_FD_WRITABLE;
2846 if (got_sock)
2847 fds[fd++].fd = afds[i];
2850 *n_fds = fd;
2851 return 0;
2852 case ASYNC_CMD_CONNECT:
2853 case ASYNC_CMD_HOSTKEY:
2854 if (!pwm->tcp_conn || pwm->tcp_conn->fd == -1)
2855 return GPG_ERR_INV_STATE;
2857 (*n_fds)++;
2858 fds[fd].fd = pwm->tcp_conn->fd;
2859 fds[fd++].flags = PWMD_FD_READABLE;
2860 return 0;
2861 #endif
2864 return GPG_ERR_INV_STATE;
2867 pwm_t *pwmd_new(const char *name)
2869 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
2871 if (!h)
2872 return NULL;
2874 if (name) {
2875 h->name = pwmd_strdup(name);
2877 if (!h->name) {
2878 pwmd_free(h);
2879 return NULL;
2883 h->fd = -1;
2884 #ifdef WITH_PINENTRY
2885 h->nb_fd = -1;
2886 #endif
2887 h->pinentry_timeout = -30;
2888 h->pinentry_tries = 3;
2889 #ifdef WITH_TCP
2890 h->prot = PWMD_IP_ANY;
2891 #endif
2892 return h;
2895 void pwmd_free(void *ptr)
2897 xfree(ptr);
2900 void *pwmd_malloc(size_t size)
2902 return xmalloc(size);
2905 void *pwmd_calloc(size_t nmemb, size_t size)
2907 return xcalloc(nmemb, size);
2910 void *pwmd_realloc(void *ptr, size_t size)
2912 return xrealloc(ptr, size);
2915 char *pwmd_strdup(const char *str)
2917 return xstrdup(str);
2920 char *pwmd_strdup_printf(const char *fmt, ...)
2922 va_list ap, ap2;
2923 int len;
2924 char *buf;
2926 if (!fmt)
2927 return NULL;
2929 va_start(ap, fmt);
2930 va_copy(ap2, ap);
2931 len = vsnprintf(NULL, 0, fmt, ap);
2932 va_end(ap);
2933 buf = pwmd_malloc(++len);
2935 if (buf)
2936 vsnprintf(buf, len, fmt, ap2);
2938 va_end(ap2);
2939 return buf;