It is probably better to set O_NONBLOCK only for the duration of
[libpwmd.git] / src / libpwmd.c
blob6cfd2b81c9e5cc95e65f049d17bf3b3936ba126f
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 for (;;) {
1148 char *result = NULL;
1149 size_t len;
1150 gpg_error_t arc;
1152 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
1153 rc = gpg_err_code(rc);
1155 if (rc == GPG_ERR_EOF || !rc) {
1156 if (len <= 0 || !result) {
1157 rc = 0;
1158 break;
1161 arc = assuan_send_data(pwm->ctx, result, len);
1163 if (rc == GPG_ERR_EOF) {
1164 rc = arc;
1165 break;
1168 rc = arc;
1170 else if (rc)
1171 break;
1173 if (!rc) {
1174 /* Set to non-blocking so _pwmd_process() can return. */
1175 fcntl(pwm->fd, F_SETFL, O_NONBLOCK);
1176 rc = _pwmd_process(pwm);
1177 fcntl(pwm->fd, F_SETFL, flags);
1181 fcntl(pwm->fd, F_SETFL, flags);
1182 return rc;
1185 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, ...)
1187 char *buf;
1188 gpg_error_t rc;
1189 va_list ap;
1191 if (pwm->state == ASYNC_DONE)
1192 pwm->state = ASYNC_INIT;
1194 if (pwm->state != ASYNC_INIT)
1195 return GPG_ERR_INV_STATE;
1197 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
1199 if (!buf)
1200 return gpg_error_from_errno(ENOMEM);
1202 va_start(ap, cmd);
1203 vsnprintf(buf, ASSUAN_LINELENGTH, cmd, ap);
1204 va_end(ap);
1205 rc = assuan_write_line(pwm->ctx, buf);
1206 pwmd_free(buf);
1208 if (!rc)
1209 pwm->state = ASYNC_PROCESS;
1211 return rc;
1214 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
1216 if (!pwm || !filename)
1217 return GPG_ERR_INV_ARG;
1219 if (!pwm->ctx)
1220 return GPG_ERR_INV_STATE;
1222 if (pwm->cmd != ASYNC_CMD_OPEN) {
1223 gpg_error_t rc;
1225 pwm->pin_try = 0;
1227 if (pwm->filename)
1228 pwmd_free(pwm->filename);
1230 pwm->filename = pwmd_strdup(filename);
1232 rc = send_pinentry_options(pwm);
1234 if (rc)
1235 return rc;
1238 pwm->cmd = ASYNC_CMD_OPEN;
1239 return do_nb_command(pwm, "OPEN %s %s", filename,
1240 pwm->password ? pwm->password : "");
1243 gpg_error_t pwmd_save_async(pwm_t *pwm)
1245 gpg_error_t rc;
1247 if (!pwm)
1248 return GPG_ERR_INV_ARG;
1250 if (!pwm->ctx)
1251 return GPG_ERR_INV_STATE;
1253 rc = send_pinentry_options(pwm);
1255 if (rc)
1256 return rc;
1258 pwm->cmd = ASYNC_CMD_SAVE;
1259 return do_nb_command(pwm, "SAVE %s", pwm->password ? pwm->password : "");
1262 static gpg_error_t parse_assuan_line(pwm_t *pwm)
1264 gpg_error_t rc;
1265 char *line;
1266 size_t len;
1268 rc = assuan_read_line(pwm->ctx, &line, &len);
1270 if (!rc) {
1271 if (line[0] == 'O' && line[1] == 'K' &&
1272 (line[2] == 0 || line[2] == ' ')) {
1273 pwm->state = ASYNC_DONE;
1275 else if (line[0] == '#') {
1277 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
1278 if (pwm->status_func) {
1279 rc = pwm->status_func(pwm->status_data,
1280 line[1] == 0 ? line+1 : line+2);
1283 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
1284 (line[3] == 0 || line[3] == ' ')) {
1285 line += 4;
1286 rc = atoi(line);
1287 pwm->state = ASYNC_DONE;
1291 return rc;
1294 gpg_error_t pwmd_pending_line(pwm_t *pwm)
1296 if (!pwm)
1297 return GPG_ERR_INV_ARG;
1299 if (!pwm->ctx)
1300 return GPG_ERR_INV_STATE;
1302 return assuan_pending_line(pwm->ctx) ? 0 : GPG_ERR_NO_DATA;
1305 static pwmd_async_t reset_async(pwm_t *pwm, int done)
1307 pwm->state = ASYNC_INIT;
1308 pwm->cmd = ASYNC_CMD_NONE;
1310 #ifdef WITH_PINENTRY
1311 if (pwm->nb_fd != -1) {
1312 close(pwm->nb_fd);
1313 pwm->nb_fd = -1;
1315 #endif
1316 #ifdef WITH_TCP
1317 if (done && pwm->tcp_conn) {
1318 free_tcp_conn(pwm->tcp_conn);
1319 pwm->tcp_conn = NULL;
1321 #endif
1323 return ASYNC_DONE;
1327 * Used for processing status messages when not in an async command and for
1328 * waiting for the result from pwmd_open_async() and pwmd_save_async().
1330 static gpg_error_t _pwmd_process(pwm_t *pwm)
1332 gpg_error_t rc = 0;
1333 fd_set fds;
1334 struct timeval tv = {0, 0};
1335 int n;
1337 FD_ZERO(&fds);
1338 FD_SET(pwm->fd, &fds);
1339 #ifdef WITH_LIBPTH
1340 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
1341 #else
1342 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
1343 #endif
1345 if (n == -1)
1346 return gpg_error_from_syserror();
1348 if (n > 0) {
1349 if (FD_ISSET(pwm->fd, &fds))
1350 rc = parse_assuan_line(pwm);
1353 while (!rc && assuan_pending_line(pwm->ctx))
1354 rc = parse_assuan_line(pwm);
1356 return rc;
1359 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
1361 fd_set fds;
1362 int n;
1363 struct timeval tv = {0, 0};
1365 if (result)
1366 *result = NULL;
1368 if (!rc)
1369 return GPG_ERR_INV_ARG;
1371 *rc = 0;
1373 if (!pwm) {
1374 *rc = GPG_ERR_INV_ARG;
1375 return ASYNC_DONE;
1377 else if (!pwm->ctx) {
1378 switch (pwm->cmd) {
1379 default:
1380 *rc = GPG_ERR_INV_STATE;
1381 return ASYNC_DONE;
1382 #ifdef WITH_TCP
1383 case ASYNC_CMD_DNS:
1384 case ASYNC_CMD_CONNECT:
1385 case ASYNC_CMD_HOSTKEY:
1386 break;
1387 #endif
1391 /* When not in a command, this will let libassuan process status messages
1392 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
1393 * descriptor returned by pwmd_get_fd() to determine when this should be
1394 * called or call pwmd_pending_line() to determine whether a buffered line
1395 * needs to be processed. */
1396 if (pwm->cmd == ASYNC_CMD_NONE) {
1397 *rc = _pwmd_process(pwm);
1398 return ASYNC_DONE;
1401 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
1402 if (pwm->state == ASYNC_DONE) {
1403 reset_async(pwm, 0);
1404 return ASYNC_DONE;
1407 if (pwm->state != ASYNC_PROCESS) {
1408 *rc = GPG_ERR_INV_STATE;
1409 return ASYNC_DONE;
1412 #ifdef WITH_TCP
1413 if (pwm->cmd == ASYNC_CMD_DNS) {
1414 fd_set rfds, wfds;
1416 if (pwm->tcp_conn->rc) {
1417 *rc = pwm->tcp_conn->rc;
1418 reset_async(pwm, 1);
1419 return ASYNC_DONE;
1422 FD_ZERO(&rfds);
1423 FD_ZERO(&wfds);
1424 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
1426 /* Shouldn't happen. */
1427 if (!n)
1428 return pwm->state;
1430 #ifdef WITH_LIBPTH
1431 n = pth_select(n, &rfds, &wfds, NULL, &tv);
1432 #else
1433 n = select(n, &rfds, &wfds, NULL, &tv);
1434 #endif
1436 if (n > 0)
1437 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
1439 return pwm->state;
1441 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
1442 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
1443 int ret;
1444 socklen_t len = sizeof(int);
1446 FD_ZERO(&fds);
1447 FD_SET(pwm->tcp_conn->fd, &fds);
1448 #ifdef WITH_LIBPTH
1449 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1450 #else
1451 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1452 #endif
1454 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
1455 return pwm->state;
1456 else if (n == -1) {
1457 *rc = gpg_error_from_syserror();
1458 reset_async(pwm, 1);
1459 return ASYNC_DONE;
1462 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
1464 if (ret || n) {
1465 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
1466 reset_async(pwm, 1);
1467 return ASYNC_DONE;
1470 else if (pwm->tcp_conn->rc) {
1471 *rc = pwm->tcp_conn->rc;
1472 reset_async(pwm, 1);
1473 return ASYNC_DONE;
1476 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
1477 *rc = setup_tcp_session(pwm);
1479 if (!*rc) {
1480 switch (pwm->tcp_conn->cmd) {
1481 case ASYNC_CMD_HOSTKEY:
1482 if (!*rc)
1483 *result = pwm->result;
1484 break;
1485 default:
1486 break;
1490 return reset_async(pwm, *rc ? 1 : 0);
1492 #endif
1494 #ifdef WITH_PINENTRY
1495 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
1496 int status;
1498 if (pwm->nb_fd == -1) {
1499 *rc = GPG_ERR_INV_STATE;
1500 return reset_async(pwm, 0);
1503 FD_ZERO(&fds);
1504 FD_SET(pwm->nb_fd, &fds);
1505 FD_SET(pwm->fd, &fds);
1506 #ifdef WITH_LIBPTH
1507 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1508 #else
1509 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1510 #endif
1511 if (n == -1) {
1512 *rc = gpg_error_from_syserror();
1513 return reset_async(pwm, 0);
1516 if (n > 0 && FD_ISSET(pwm->nb_fd, &fds)) {
1517 pwmd_nb_status_t nb;
1518 #ifdef WITH_LIBPTH
1519 size_t len = pth_read(pwm->nb_fd, &nb, sizeof(nb));
1520 #else
1521 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
1522 #endif
1523 waitpid(pwm->nb_pid, &status, WNOHANG);
1525 if (len != sizeof(nb)) {
1526 *rc = gpg_error_from_syserror();
1527 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1530 *rc = nb.error;
1532 if (*rc == GPG_ERR_INV_PASSPHRASE && pwm->cmd == ASYNC_CMD_SAVE2) {
1533 reset_async(pwm, 0);
1534 *rc = pwmd_save_async2(pwm);
1535 return ASYNC_PROCESS;
1537 else if (*rc)
1538 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1540 if (pwm->cmd == ASYNC_CMD_SAVE2) {
1541 *rc = do_save_command(pwm, nb.password);
1542 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1543 return reset_async(pwm, 0);
1546 if (pwm->cmd == ASYNC_CMD_OPEN2) {
1547 *rc = do_open_command(pwm, pwm->filename, nb.password);
1548 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1550 if (*rc == GPG_ERR_INV_PASSPHRASE) {
1551 if (++pwm->pin_try < pwm->pinentry_tries) {
1552 int n = pwm->pin_try;
1554 reset_async(pwm, 0);
1555 pwm->pin_try = n;
1556 pwm->cmd = ASYNC_CMD_OPEN2;
1557 *rc = pwmd_open_async2(pwm, pwm->filename);
1559 if (*rc)
1560 return reset_async(pwm, 1);
1562 return pwm->state;
1566 return reset_async(pwm, *rc ? 1 : 0);
1570 /* Fall through so status messages can be processed during the
1571 * pinentry. */
1573 #endif
1575 if (pwm->fd < 0) {
1576 *rc = GPG_ERR_INV_STATE;
1577 return reset_async(pwm, 0);
1580 /* This is for pwmd_open_async() and pwmd_save_async(). For pinentry
1581 * retries. */
1582 *rc = _pwmd_process(pwm);
1584 if (*rc && gpg_err_code(*rc) != GPG_ERR_INV_PASSPHRASE) {
1585 reset_async(pwm, 0);
1586 return ASYNC_DONE;
1589 #ifdef WITH_TCP
1590 if (!pwm->tcp_conn && pwm->cmd == ASYNC_CMD_OPEN &&
1591 #else
1592 if (pwm->cmd == ASYNC_CMD_OPEN &&
1593 #endif
1594 gpg_err_code(*rc) == GPG_ERR_INV_PASSPHRASE &&
1595 ++pwm->pin_try < pwm->pinentry_tries) {
1596 pwm->state = ASYNC_INIT;
1597 *rc = pwmd_open_async(pwm, pwm->filename);
1600 if (*rc)
1601 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN ? 1 : 0);
1603 if (pwm->state == ASYNC_DONE) {
1604 reset_async(pwm, 0);
1605 return ASYNC_DONE;
1608 return pwm->state;
1611 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
1612 char **result, const char *cmd)
1614 membuf_t data;
1615 gpg_error_t rc;
1617 data.len = 0;
1618 data.buf = NULL;
1620 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
1621 pwm->status_func, pwm->status_data);
1623 if (rc) {
1624 if (data.buf) {
1625 pwmd_free(data.buf);
1626 data.buf = NULL;
1629 else {
1630 if (data.buf) {
1631 mem_realloc_cb(&data, "", 1);
1633 if (!result) {
1634 pwmd_free(data.buf);
1635 rc = GPG_ERR_INV_ARG;
1637 else
1638 *result = (char *)data.buf;
1642 return gpg_err_code(rc);
1645 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_cb_t fn,
1646 void *data)
1648 if (!pwm || !cmd || !fn)
1649 return GPG_ERR_INV_ARG;
1651 if (!pwm->ctx)
1652 return GPG_ERR_INV_STATE;
1654 pwm->inquire_func = fn;
1655 pwm->inquire_data = data;
1656 return assuan_command(pwm, pwm->ctx, NULL, cmd);
1659 #ifdef WITH_PINENTRY
1660 static gpg_error_t terminate_pinentry(pwm_t *pwm)
1662 pid_t pid = pwm->pid;
1664 pwm->pid = -1;
1666 if (!pwm || pid == -1)
1667 return GPG_ERR_INV_ARG;
1669 if (kill(pid, 0) == 0) {
1670 if (kill(pid, SIGTERM) == -1) {
1671 if (kill(pid, SIGKILL) == -1)
1672 return gpg_error_from_errno(errno);
1675 else
1676 return gpg_error_from_errno(errno);
1678 return 0;
1681 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
1683 char *tmp, *desc;
1684 gpg_error_t error;
1686 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
1688 if (!tmp)
1689 return gpg_error_from_errno(ENOMEM);
1691 if (!pwm->title)
1692 pwm->title = pwmd_strdup_printf(N_("Password Manager Daemon: %s"),
1693 pwm->name ? pwm->name : "libpwmd");
1695 if (!pwm->title)
1696 goto fail_no_mem;
1698 if (!pwm->prompt)
1699 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
1701 if (!pwm->prompt)
1702 goto fail_no_mem;
1704 if (!pwm->desc && (which == PINENTRY_OPEN || which == PINENTRY_SAVE)) {
1705 if (which == PINENTRY_OPEN)
1706 desc = pwmd_strdup_printf(N_("A passphrase is required to open the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1707 else
1708 desc = pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1710 if (!desc)
1711 goto fail_no_mem;
1714 if (pwm->desc)
1715 desc = pwm->desc;
1717 switch (which) {
1718 case PINENTRY_OPEN:
1719 case PINENTRY_SAVE:
1720 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s", desc);
1722 if (pwm->desc != desc)
1723 pwmd_free(desc);
1724 break;
1725 case PINENTRY_OPEN_FAILED:
1726 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1727 N_("Invalid passphrase, please try again."));
1728 break;
1729 case PINENTRY_SAVE_CONFIRM:
1730 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1731 N_("Please type the passphrase again for confirmation."));
1732 break;
1735 error = pinentry_command(pwm, NULL, tmp);
1737 if (error) {
1738 pwmd_free(tmp);
1739 return error;
1742 snprintf(tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->prompt);
1743 error = pinentry_command(pwm, NULL, tmp);
1745 if (error) {
1746 pwmd_free(tmp);
1747 return error;
1750 snprintf(tmp, ASSUAN_LINELENGTH, "SETDESC %s", pwm->title);
1751 error = pinentry_command(pwm, NULL, tmp);
1752 pwmd_free(tmp);
1753 return error;
1755 fail_no_mem:
1756 pwmd_free(tmp);
1757 return gpg_error_from_errno(ENOMEM);
1760 static void update_pinentry_settings(pwm_t *pwm)
1762 FILE *fp;
1763 char buf[LINE_MAX];
1764 char *p;
1765 struct passwd pw;
1766 char *pwbuf = _getpwuid(&pw);
1768 if (!pwbuf)
1769 return;
1771 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw.pw_dir);
1773 if ((fp = fopen(buf, "r")) == NULL) {
1774 pwmd_free(pwbuf);
1775 return;
1778 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
1779 char name[32], val[256];
1781 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
1782 continue;
1784 if (strcasecmp(name, "TTYNAME") == 0) {
1785 pwmd_free(pwm->pinentry_tty);
1786 pwm->pinentry_tty = pwmd_strdup(val);
1788 else if (strcasecmp(name, "TTYTYPE") == 0) {
1789 pwmd_free(pwm->pinentry_term);
1790 pwm->pinentry_term = pwmd_strdup(val);
1792 else if (strcasecmp(name, "DISPLAY") == 0) {
1793 pwmd_free(pwm->pinentry_display);
1794 pwm->pinentry_display = pwmd_strdup(val);
1796 else if (strcasecmp(name, "PATH") == 0) {
1797 pwmd_free(pwm->pinentry_path);
1798 pwm->pinentry_path = expand_homedir(val, &pw);
1802 pwmd_free(pwbuf);
1803 fclose(fp);
1806 static gpg_error_t launch_pinentry(pwm_t *pwm)
1808 int rc;
1809 assuan_context_t ctx;
1810 int child_list[] = {-1};
1811 char *display = getenv("DISPLAY");
1812 const char *argv[10];
1813 const char **p = argv;
1814 int have_display = 0;
1815 char *tty = NULL;
1816 char *ttybuf = NULL;
1818 update_pinentry_settings(pwm);
1820 if (pwm->pinentry_display || display)
1821 have_display = 1;
1822 else {
1823 if (!pwm->pinentry_tty) {
1824 ttybuf = pwmd_malloc(255);
1826 if (!ttybuf)
1827 return gpg_error_from_errno(ENOMEM);
1829 rc = ttyname_r(STDOUT_FILENO, ttybuf, 255);
1831 if (rc) {
1832 pwmd_free(ttybuf);
1833 return gpg_error_from_errno(rc);
1836 tty = ttybuf;
1838 else
1839 tty = pwm->pinentry_tty;
1842 if (!have_display && !tty)
1843 return GPG_ERR_ENOTTY;
1845 *p++ = "pinentry";
1846 *p++ = have_display ? "--display" : "--ttyname";
1847 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
1849 if (pwm->lcctype) {
1850 *p++ = "--lc-ctype";
1851 *p++ = pwm->lcctype;
1854 if (pwm->lcmessages) {
1855 *p++ = "--lc-messages";
1856 *p++ = pwm->lcmessages;
1859 *p = NULL;
1861 if (!have_display) {
1862 *p++ = "--ttytype";
1863 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
1864 *p = NULL;
1867 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
1869 if (ttybuf)
1870 pwmd_free(ttybuf);
1872 if (rc)
1873 return rc;
1875 pwm->pid = assuan_get_pid(ctx);
1876 pwm->pctx = ctx;
1877 return set_pinentry_strings(pwm, 0);
1880 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
1882 gpg_error_t n;
1884 if (!pwm->pctx) {
1885 n = launch_pinentry(pwm);
1887 if (n)
1888 return n;
1891 return assuan_command(pwm, pwm->pctx, result, cmd);
1894 static void pinentry_disconnect(pwm_t *pwm)
1896 if (pwm->pctx)
1897 assuan_disconnect(pwm->pctx);
1899 pwm->pctx = NULL;
1900 pwm->pid = -1;
1904 * Only called from a child process.
1906 static void catchsig(int sig)
1908 switch (sig) {
1909 case SIGALRM:
1910 if (gelapsed++ >= gtimeout) {
1911 terminate_pinentry(gpwm);
1912 gerror = GPG_ERR_TIMEOUT;
1914 else
1915 alarm(1);
1916 break;
1917 default:
1918 break;
1921 #endif
1924 * Borrowed from libassuan.
1926 static char *percent_escape(const char *atext)
1928 const unsigned char *s;
1929 int len = strlen(atext) * 3 + 1;
1930 char *buf = (char *)pwmd_malloc(len), *p = buf;
1932 if (!buf)
1933 return NULL;
1935 for (s=(const unsigned char *)atext; *s; s++) {
1936 if (*s < ' ') {
1937 sprintf (p, "%%%02X", *s);
1938 p += 3;
1940 else
1941 *p++ = *s;
1944 *p = 0;
1945 return buf;
1948 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
1950 if (!cmd)
1951 return GPG_ERR_INV_ARG;
1953 return assuan_command(pwm, pwm->ctx, result, cmd);
1956 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1957 va_list ap)
1959 char *buf;
1960 size_t len;
1961 gpg_error_t error;
1962 va_list ap2;
1964 if (!pwm || !cmd)
1965 return GPG_ERR_INV_ARG;
1967 if (!pwm->ctx)
1968 return GPG_ERR_INV_STATE;
1971 * C99 allows the dst pointer to be null which will calculate the length
1972 * of the would-be result and return it.
1974 va_copy(ap2, ap);
1975 len = vsnprintf(NULL, 0, cmd, ap)+1;
1976 buf = (char *)pwmd_malloc(len);
1978 if (!buf) {
1979 va_end(ap2);
1980 return gpg_error_from_errno(ENOMEM);
1983 len = vsnprintf(buf, len, cmd, ap2);
1984 va_end(ap2);
1986 if (buf[strlen(buf)-1] == '\n')
1987 buf[strlen(buf)-1] = 0;
1989 if (buf[strlen(buf)-1] == '\r')
1990 buf[strlen(buf)-1] = 0;
1992 error = send_command(pwm, result, buf);
1993 pwmd_free(buf);
1994 return error;
1997 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1999 va_list ap;
2000 gpg_error_t error;
2002 if (!pwm || !cmd)
2003 return GPG_ERR_INV_ARG;
2005 if (!pwm->ctx)
2006 return GPG_ERR_INV_STATE;
2008 if (result)
2009 *result = NULL;
2011 va_start(ap, cmd);
2012 error = pwmd_command_ap(pwm, result, cmd, ap);
2013 va_end(ap);
2014 return error;
2017 #ifdef WITH_PINENTRY
2018 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
2020 if (gtimeout) {
2021 signal(SIGALRM, catchsig);
2022 alarm(1);
2025 *result = NULL;
2026 return pinentry_command(pwm, result, "GETPIN");
2029 static gpg_error_t getpin(pwm_t *pwm, char **result, int which)
2031 gpg_error_t rc;
2033 gerror = 0;
2034 rc = set_pinentry_strings(pwm, which);
2036 if (rc) {
2037 pinentry_disconnect(pwm);
2038 return rc;
2041 rc = do_getpin(pwm, result);
2044 * Since there was input cancel any timeout setting.
2046 alarm(0);
2047 signal(SIGALRM, SIG_DFL);
2049 if (rc) {
2050 if (pwm->pctx)
2051 pinentry_disconnect(pwm);
2053 /* This lets pwmd_open2() with PWMD_OPTION_PINENTRY_TIMEOUT work. */
2054 if (rc== GPG_ERR_EOF && gerror == GPG_ERR_TIMEOUT)
2055 return gerror;
2057 return rc;
2060 return 0;
2062 #endif
2064 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
2066 char *buf;
2067 gpg_error_t error;
2068 char *result = NULL;
2070 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2072 if (!buf)
2073 return gpg_error_from_errno(ENOMEM);
2075 snprintf(buf, ASSUAN_LINELENGTH, "OPEN %s %s", filename,
2076 password ? password : "");
2077 error = send_command(pwm, &result, buf);
2078 pwmd_free(buf);
2080 if (error && result)
2081 pwmd_free(result);
2083 return error;
2086 static gpg_error_t send_pinentry_options(pwm_t *pwm)
2088 gpg_error_t rc;
2090 if (pwm->pinentry_path) {
2091 rc = pwmd_command(pwm, NULL, "OPTION PATH=%s", pwm->pinentry_path);
2093 if (rc)
2094 return rc;
2097 if (pwm->pinentry_tty) {
2098 rc = pwmd_command(pwm, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
2100 if (rc)
2101 return rc;
2104 if (pwm->pinentry_term) {
2105 rc = pwmd_command(pwm, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
2107 if (rc)
2108 return rc;
2111 if (pwm->pinentry_display) {
2112 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->pinentry_display);
2114 if (rc)
2115 return rc;
2118 if (pwm->title) {
2119 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->title);
2121 if (rc)
2122 return rc;
2125 if (pwm->desc) {
2126 rc = pwmd_command(pwm, NULL, "OPTION DESC=%s", pwm->desc);
2128 if (rc)
2129 return rc;
2132 if (pwm->prompt) {
2133 rc = pwmd_command(pwm, NULL, "OPTION PROMPT=%s", pwm->prompt);
2135 if (rc)
2136 return rc;
2139 if (pwm->lcctype) {
2140 rc = pwmd_command(pwm, NULL, "OPTION LC_CTYPE=%s", pwm->lcctype);
2142 if (rc)
2143 return rc;
2146 if (pwm->lcmessages) {
2147 rc = pwmd_command(pwm, NULL, "OPTION LC_MESSAGES=%s", pwm->lcmessages);
2149 if (rc)
2150 return rc;
2153 if (pwm->pinentry_timeout >= 0) {
2154 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=%i", pwm->pinentry_timeout);
2156 if (rc)
2157 return rc;
2160 return 0;
2163 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb,
2164 int local_pinentry)
2166 char *result = NULL;
2167 char *password = NULL;
2168 char *path;
2169 int pin_try;
2170 gpg_error_t rc;
2172 if (!pwm || !filename || !*filename)
2173 return GPG_ERR_INV_ARG;
2175 if (!pwm->ctx)
2176 return GPG_ERR_INV_STATE;
2178 pin_try = pwm->pinentry_tries - 1;
2181 * Avoid calling pinentry if the password is cached on the server or if
2182 * this is a new file. pwmd version 2 adds a VERSION command which is
2183 * determined in _socket_connect_finalize(). If the server is version 2,
2184 * ISCACHED can determine if a file exists.
2186 #ifdef WITH_TCP
2187 if (!pwm->tcp_conn && pwm->version == PWMD_V1) {
2188 #else
2189 if (pwm->version == PWMD_V1) {
2190 #endif
2191 rc = pwmd_command(pwm, &result, "GETCONFIG data_directory");
2193 if (rc)
2194 return rc;
2196 path = pwmd_strdup_printf("%s/%s", result, filename);
2197 pwmd_free(result);
2199 if (!path)
2200 return gpg_error_from_errno(ENOMEM);
2202 if (access(path, R_OK) == -1) {
2203 if (errno == ENOENT) {
2204 pwmd_free(path);
2205 goto gotpassword;
2209 pwmd_free(path);
2212 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
2214 if (gpg_err_code(rc) == GPG_ERR_ENOENT)
2215 goto gotpassword;
2217 if (rc && rc != GPG_ERR_NOT_FOUND)
2218 return rc;
2220 if (rc == GPG_ERR_NOT_FOUND) {
2221 if (pwm->password) {
2222 password = pwm->password;
2223 goto gotpassword;
2226 if (pwm->passfunc) {
2227 rc = pwm->passfunc(pwm->passdata, &password);
2229 if (rc)
2230 return rc;
2232 goto gotpassword;
2236 #ifdef WITH_PINENTRY
2237 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2238 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
2240 if (rc)
2241 return rc;
2243 if (!pwm->filename)
2244 pwm->filename = pwmd_strdup(filename);
2246 if (!pwm->filename)
2247 return gpg_error_from_errno(ENOMEM);
2249 /* Get the passphrase using the LOCAL pinentry. */
2250 if (nb) {
2251 int p[2];
2252 pid_t pid;
2253 pwmd_nb_status_t pw;
2255 if (pipe(p) == -1)
2256 return gpg_error_from_syserror();
2258 #ifdef WITH_LIBPTH
2259 pid = pth_fork();
2260 #else
2261 pid = fork();
2262 #endif
2264 switch (pid) {
2265 case 0:
2266 close(p[0]);
2267 pw.fd = p[0];
2269 if (pwm->pinentry_timeout != 0) {
2270 gpwm = pwm;
2271 gtimeout = abs(pwm->pinentry_timeout);
2272 gelapsed = 0;
2275 pw.error = getpin(pwm, &password, PINENTRY_OPEN);
2277 if (gtimeout && gelapsed >= gtimeout)
2278 pw.error = GPG_ERR_TIMEOUT;
2280 if (!pw.error)
2281 snprintf(pw.password, sizeof(pw.password), "%s",
2282 password);
2284 pinentry_disconnect(pwm);
2285 #ifdef WITH_LIBPTH
2286 pth_write(p[1], &pw, sizeof(pw));
2287 #else
2288 write(p[1], &pw, sizeof(pw));
2289 #endif
2290 memset(&pw, 0, sizeof(pw));
2291 close(p[1]);
2292 _exit(0);
2293 break;
2294 case -1:
2295 rc = gpg_error_from_syserror();
2296 close(p[0]);
2297 close(p[1]);
2298 return rc;
2299 default:
2300 break;
2303 close(p[1]);
2304 pwm->nb_fd = p[0];
2305 pwm->nb_pid = pid;
2306 return 0;
2309 if (pwm->pinentry_timeout != 0) {
2310 gpwm = pwm;
2311 gtimeout = abs(pwm->pinentry_timeout);
2312 gelapsed = 0;
2315 rc = getpin(pwm, &password, PINENTRY_OPEN);
2317 /* Don't timeout when an invalid passphrase was entered. */
2318 gtimeout = 0;
2320 if (rc)
2321 return rc;
2323 #endif
2325 gotpassword:
2326 pwm->state = ASYNC_DONE;
2328 #ifdef WITH_TCP
2329 if (!local_pinentry && !pwm->tcp_conn) {
2330 #else
2331 if (!local_pinentry) {
2332 #endif
2333 rc = send_pinentry_options(pwm);
2335 if (rc)
2336 return rc;
2339 rc = do_open_command(pwm, filename, password);
2342 * Keep the user defined password set with pwmd_setopt(). The password may
2343 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
2345 if (!pwm->passfunc && password && password != pwm->password)
2346 pwmd_free(password);
2348 #ifdef WITH_TCP
2349 if (rc == GPG_ERR_INV_PASSPHRASE && !pwm->tcp_conn) {
2350 #else
2351 if (rc == GPG_ERR_INV_PASSPHRASE) {
2352 #endif
2353 if (pin_try-- > 0 && !nb) {
2355 #ifdef WITH_PINENTRY
2356 if (local_pinentry)
2357 rc = getpin(pwm, &password, PINENTRY_OPEN_FAILED);
2358 else
2359 #endif
2360 rc = pwmd_command(pwm, &result, "OPTION TITLE=%s",
2361 N_("Invalid passphrase, please try again."));
2363 if (rc)
2364 return rc;
2366 goto gotpassword;
2369 #ifdef WITH_PINENTRY
2370 if (nb)
2371 pinentry_disconnect(pwm);
2372 #endif
2374 return rc;
2377 if (!rc) {
2378 if (pwm->filename)
2379 pwmd_free(pwm->filename);
2381 pwm->filename = pwmd_strdup(filename);
2384 return rc;
2387 gpg_error_t pwmd_open2(pwm_t *pwm, const char *filename)
2389 #ifndef WITH_PINENTRY
2390 return GPG_ERR_NOT_IMPLEMENTED;
2391 #else
2392 return do_pwmd_open(pwm, filename, 0, 1);
2393 #endif
2396 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
2398 return do_pwmd_open(pwm, filename, 0, 0);
2401 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
2403 #ifndef WITH_PINENTRY
2404 return GPG_ERR_NOT_IMPLEMENTED;
2405 #else
2406 gpg_error_t rc;
2408 if (!pwm || !filename)
2409 return GPG_ERR_INV_ARG;
2411 if (!pwm->ctx)
2412 return GPG_ERR_INV_STATE;
2414 if (pwm->cmd != ASYNC_CMD_OPEN2)
2415 pwm->pin_try = 0;
2417 pwm->cmd = ASYNC_CMD_OPEN2;
2418 pwm->state = ASYNC_PROCESS;
2419 rc = do_pwmd_open(pwm, filename, 1, 1);
2421 if (rc)
2422 reset_async(pwm, 1);
2424 return rc;
2425 #endif
2428 #ifdef WITH_PINENTRY
2429 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
2431 int confirm = 0;
2432 gpg_error_t error;
2433 char *result = NULL;
2435 again:
2436 error = getpin(pwm, &result, confirm ? PINENTRY_SAVE_CONFIRM : PINENTRY_SAVE);
2438 if (error) {
2439 if (pwm->pctx)
2440 pinentry_disconnect(pwm);
2442 if (*password)
2443 pwmd_free(*password);
2445 return error;
2448 if (!confirm++) {
2449 *password = result;
2450 goto again;
2453 if (strcmp(*password, result)) {
2454 pwmd_free(*password);
2455 pwmd_free(result);
2456 confirm = 0;
2457 *password = NULL;
2458 goto again;
2461 pwmd_free(result);
2462 pinentry_disconnect(pwm);
2463 return 0;
2465 #endif
2467 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
2469 char *buf;
2470 gpg_error_t error;
2471 char *result = NULL;
2473 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2475 if (!buf)
2476 return gpg_error_from_errno(ENOMEM);
2478 snprintf(buf, ASSUAN_LINELENGTH, "SAVE %s", password ? password : "");
2479 error = send_command(pwm, &result, buf);
2480 pwmd_free(buf);
2482 if (error && result)
2483 pwmd_free(result);
2485 return error;
2488 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb, int local_pinentry)
2490 char *result = NULL;
2491 char *password = NULL;
2492 gpg_error_t rc;
2494 if (!pwm)
2495 return GPG_ERR_INV_ARG;
2497 if (!pwm->ctx)
2498 return GPG_ERR_INV_STATE;
2500 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
2502 if (rc && rc != GPG_ERR_NOT_FOUND)
2503 return rc;
2505 if (rc == GPG_ERR_NOT_FOUND) {
2506 if (pwm->password) {
2507 password = pwm->password;
2508 goto gotpassword;
2511 if (pwm->passfunc) {
2512 rc = pwm->passfunc(pwm->passdata, &password);
2514 if (rc)
2515 return rc;
2517 goto gotpassword;
2521 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2522 #ifdef WITH_PINENTRY
2523 /* Get the password using the LOCAL pinentry. */
2524 if (nb) {
2525 int p[2];
2526 pid_t pid;
2527 pwmd_nb_status_t pw;
2529 if (pipe(p) == -1)
2530 return gpg_error_from_syserror();
2532 #ifdef WITH_LIBPTH
2533 pid = pth_fork();
2534 #else
2535 pid = fork();
2536 #endif
2538 switch (pid) {
2539 case 0:
2540 close(p[0]);
2541 pw.fd = p[0];
2542 password = NULL;
2543 pw.error = do_save_getpin(pwm, &password);
2544 pinentry_disconnect(pwm);
2545 snprintf(pw.password, sizeof(pw.password), "%s",
2546 password);
2547 #ifdef WITH_LIBPTH
2548 pth_write(p[1], &pw, sizeof(pw));
2549 #else
2550 write(p[1], &pw, sizeof(pw));
2551 #endif
2552 memset(&pw, 0, sizeof(pw));
2553 close(p[1]);
2554 _exit(0);
2555 break;
2556 case -1:
2557 rc = gpg_error_from_syserror();
2558 close(p[0]);
2559 close(p[1]);
2560 return rc;
2561 default:
2562 break;
2565 close(p[1]);
2566 pwm->nb_fd = p[0];
2567 pwm->nb_pid = pid;
2568 return 0;
2571 rc = do_save_getpin(pwm, &password);
2573 if (rc)
2574 return rc;
2575 #endif
2577 else
2578 pwm->state = ASYNC_DONE;
2580 gotpassword:
2581 #ifdef WITH_TCP
2582 if (!local_pinentry && !pwm->tcp_conn) {
2583 #else
2584 if (!local_pinentry) {
2585 #endif
2586 rc = send_pinentry_options(pwm);
2588 if (rc)
2589 return rc;
2592 rc = do_save_command(pwm, password);
2594 if (!pwm->passfunc && password && password != pwm->password)
2595 pwmd_free(password);
2597 return rc;
2600 gpg_error_t pwmd_save_async2(pwm_t *pwm)
2602 #ifndef WITH_PINENTRY
2603 return GPG_ERR_NOT_IMPLEMENTED;
2604 #else
2605 gpg_error_t rc;
2607 if (!pwm)
2608 return GPG_ERR_INV_ARG;
2610 if (!pwm->ctx)
2611 return GPG_ERR_INV_STATE;
2613 pwm->cmd = ASYNC_CMD_SAVE2;
2614 pwm->state = ASYNC_PROCESS;
2615 rc = do_pwmd_save(pwm, 1, 1);
2617 if (rc)
2618 reset_async(pwm, 0);
2620 return rc;
2621 #endif
2624 gpg_error_t pwmd_save2(pwm_t *pwm)
2626 #ifndef WITH_PINENTRY
2627 return GPG_ERR_NOT_IMPLEMENTED;
2628 #else
2629 return do_pwmd_save(pwm, 0, 1);
2630 #endif
2633 gpg_error_t pwmd_save(pwm_t *pwm)
2635 return do_pwmd_save(pwm, 0, 0);
2638 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
2640 va_list ap;
2641 int n = va_arg(ap, int);
2642 char *arg1;
2643 gpg_error_t error = 0;
2645 if (!pwm)
2646 return GPG_ERR_INV_ARG;
2648 va_start(ap, opt);
2650 switch (opt) {
2651 case PWMD_OPTION_STATUS_CB:
2652 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
2653 break;
2654 case PWMD_OPTION_STATUS_DATA:
2655 pwm->status_data = va_arg(ap, void *);
2656 break;
2657 case PWMD_OPTION_PASSPHRASE_CB:
2658 pwm->passfunc = va_arg(ap, pwmd_passphrase_cb_t);
2659 break;
2660 case PWMD_OPTION_PASSPHRASE_DATA:
2661 pwm->passdata = va_arg(ap, void *);
2662 break;
2663 case PWMD_OPTION_PASSPHRASE:
2664 arg1 = va_arg(ap, char *);
2666 if (pwm->password)
2667 pwmd_free(pwm->password);
2669 pwm->password = pwmd_strdup(arg1);
2670 break;
2671 case PWMD_OPTION_PINENTRY_TRIES:
2672 n = va_arg(ap, int);
2674 if (n <= 0) {
2675 va_end(ap);
2676 error = GPG_ERR_INV_VALUE;
2678 else
2679 pwm->pinentry_tries = n;
2680 break;
2681 case PWMD_OPTION_PINENTRY_TIMEOUT:
2682 n = va_arg(ap, int);
2684 if (n < 0) {
2685 va_end(ap);
2686 error = GPG_ERR_INV_VALUE;
2688 else
2689 pwm->pinentry_timeout = n;
2690 break;
2691 case PWMD_OPTION_PINENTRY_PATH:
2692 if (pwm->pinentry_path)
2693 pwmd_free(pwm->pinentry_path);
2695 pwm->pinentry_path = expand_homedir(va_arg(ap, char *), NULL);
2696 break;
2697 case PWMD_OPTION_PINENTRY_TTY:
2698 if (pwm->pinentry_tty)
2699 pwmd_free(pwm->pinentry_tty);
2701 pwm->pinentry_tty = pwmd_strdup(va_arg(ap, char *));
2702 break;
2703 case PWMD_OPTION_PINENTRY_DISPLAY:
2704 if (pwm->pinentry_display)
2705 pwmd_free(pwm->pinentry_display);
2707 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
2708 break;
2709 case PWMD_OPTION_PINENTRY_TERM:
2710 if (pwm->pinentry_term)
2711 pwmd_free(pwm->pinentry_term);
2713 pwm->pinentry_term = pwmd_strdup(va_arg(ap, char *));
2714 break;
2715 case PWMD_OPTION_PINENTRY_TITLE:
2716 if (pwm->title)
2717 pwmd_free(pwm->title);
2719 pwm->title = percent_escape(va_arg(ap, char *));
2720 break;
2721 case PWMD_OPTION_PINENTRY_PROMPT:
2722 if (pwm->prompt)
2723 pwmd_free(pwm->prompt);
2725 pwm->prompt = percent_escape(va_arg(ap, char *));
2726 break;
2727 case PWMD_OPTION_PINENTRY_DESC:
2728 if (pwm->desc)
2729 pwmd_free(pwm->desc);
2731 pwm->desc = percent_escape(va_arg(ap, char *));
2732 break;
2733 case PWMD_OPTION_PINENTRY_LC_CTYPE:
2734 if (pwm->lcctype)
2735 pwmd_free(pwm->lcctype);
2737 pwm->lcctype = pwmd_strdup(va_arg(ap, char *));
2738 break;
2739 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
2740 if (pwm->lcmessages)
2741 pwmd_free(pwm->lcmessages);
2743 pwm->lcmessages = pwmd_strdup(va_arg(ap, char *));
2744 break;
2745 #ifdef WITH_TCP
2746 case PWMD_OPTION_IP_VERSION:
2747 n = va_arg(ap, int);
2749 switch (n) {
2750 case PWMD_IP_ANY:
2751 case PWMD_IPV4:
2752 case PWMD_IPV6:
2753 pwm->prot = n;
2754 break;
2755 default:
2756 error = GPG_ERR_INV_VALUE;
2757 break;
2760 va_end(ap);
2761 break;
2762 #endif
2763 default:
2764 error = GPG_ERR_NOT_IMPLEMENTED;
2765 break;
2768 va_end(ap);
2769 return error;
2772 gpg_error_t pwmd_get_fds(pwm_t *pwm, pwmd_fd_t *fds, int *n_fds)
2774 int in_total;
2775 int fd = 0;
2776 #ifdef WITH_TCP
2777 int afds[ARES_GETSOCK_MAXNUM];
2778 int got_sock = 0;
2779 int n, i;
2780 #endif
2782 if (!pwm || !fds || !n_fds || *n_fds <= 0)
2783 return GPG_ERR_INV_ARG;
2785 in_total = *n_fds;
2786 #ifdef WITH_TCP
2787 memset(afds, 0, sizeof(int)*ARES_GETSOCK_MAXNUM);
2788 #endif
2789 memset(fds, 0, sizeof(pwmd_fd_t)*in_total);
2790 *n_fds = 0;
2792 switch (pwm->cmd) {
2793 default:
2794 case ASYNC_CMD_NONE:
2795 case ASYNC_CMD_OPEN:
2796 case ASYNC_CMD_SAVE:
2797 #ifdef WITH_PINENTRY
2798 async1:
2799 #endif
2800 if (pwm->fd == -1)
2801 return GPG_ERR_INV_STATE;
2803 (*n_fds)++;
2804 fds[fd].fd = pwm->fd;
2805 fds[fd++].flags = PWMD_FD_READABLE;
2806 return 0;
2807 #ifdef WITH_PINENTRY
2808 case ASYNC_CMD_OPEN2:
2809 case ASYNC_CMD_SAVE2:
2810 /* The command has already completed (cached or new). */
2811 if (pwm->state == ASYNC_DONE)
2812 return 0;
2814 if (pwm->nb_fd == -1)
2815 return GPG_ERR_INV_STATE;
2817 (*n_fds)++;
2818 fds[fd].fd = pwm->nb_fd;
2819 fds[fd++].flags = PWMD_FD_READABLE;
2820 goto async1;
2821 #endif
2822 #ifdef WITH_TCP
2823 case ASYNC_CMD_DNS:
2824 if (!pwm->tcp_conn || !pwm->tcp_conn->chan)
2825 return GPG_ERR_INV_STATE;
2827 n = ares_getsock(pwm->tcp_conn->chan, afds, ARES_GETSOCK_MAXNUM);
2829 for (i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
2830 got_sock = 0;
2832 if (fd > in_total) {
2833 *n_fds = fd;
2834 return GPG_ERR_ERANGE;
2837 if (ARES_GETSOCK_READABLE(n, i)) {
2838 got_sock++;
2839 fds[fd].flags |= PWMD_FD_READABLE;
2842 if (ARES_GETSOCK_WRITABLE(n, i)) {
2843 got_sock++;
2844 fds[fd].flags |= PWMD_FD_WRITABLE;
2847 if (got_sock)
2848 fds[fd++].fd = afds[i];
2851 *n_fds = fd;
2852 return 0;
2853 case ASYNC_CMD_CONNECT:
2854 case ASYNC_CMD_HOSTKEY:
2855 if (!pwm->tcp_conn || pwm->tcp_conn->fd == -1)
2856 return GPG_ERR_INV_STATE;
2858 (*n_fds)++;
2859 fds[fd].fd = pwm->tcp_conn->fd;
2860 fds[fd++].flags = PWMD_FD_READABLE;
2861 return 0;
2862 #endif
2865 return GPG_ERR_INV_STATE;
2868 pwm_t *pwmd_new(const char *name)
2870 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
2872 if (!h)
2873 return NULL;
2875 if (name) {
2876 h->name = pwmd_strdup(name);
2878 if (!h->name) {
2879 pwmd_free(h);
2880 return NULL;
2884 h->fd = -1;
2885 #ifdef WITH_PINENTRY
2886 h->nb_fd = -1;
2887 #endif
2888 h->pinentry_timeout = -30;
2889 h->pinentry_tries = 3;
2890 #ifdef WITH_TCP
2891 h->prot = PWMD_IP_ANY;
2892 #endif
2893 return h;
2896 void pwmd_free(void *ptr)
2898 xfree(ptr);
2901 void *pwmd_malloc(size_t size)
2903 return xmalloc(size);
2906 void *pwmd_calloc(size_t nmemb, size_t size)
2908 return xcalloc(nmemb, size);
2911 void *pwmd_realloc(void *ptr, size_t size)
2913 return xrealloc(ptr, size);
2916 char *pwmd_strdup(const char *str)
2918 return xstrdup(str);
2921 char *pwmd_strdup_printf(const char *fmt, ...)
2923 va_list ap, ap2;
2924 int len;
2925 char *buf;
2927 if (!fmt)
2928 return NULL;
2930 va_start(ap, fmt);
2931 va_copy(ap2, ap);
2932 len = vsnprintf(NULL, 0, fmt, ap);
2933 va_end(ap);
2934 buf = pwmd_malloc(++len);
2936 if (buf)
2937 vsnprintf(buf, len, fmt, ap2);
2939 va_end(ap2);
2940 return buf;