Fixed pwmd_connect_url() with a NULL url parameter.
[libpwmd.git] / src / libpwmd.c
blob3bb8553e99344ad0f969b7e67abda5ab996bd94d
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 rc = setup_tcp_session(pwm);
800 pwm->cmd = ASYNC_CMD_NONE;
802 fail:
803 return rc;
806 gpg_error_t pwmd_ssh_connect(pwm_t *pwm, const char *host, int port,
807 const char *identity, const char *user, const char *known_hosts)
809 return _do_pwmd_tcp_connect(pwm, host, port, identity, user, known_hosts, 0);
812 gpg_error_t pwmd_get_hostkey(pwm_t *pwm, const char *host, int port,
813 char **result)
815 char *hostkey;
816 gpg_error_t rc;
818 rc = _do_pwmd_tcp_connect(pwm, host, port, NULL, NULL, NULL, 1);
820 if (rc)
821 return rc;
823 hostkey = pwmd_strdup(pwm->tcp_conn->hostkey);
825 if (!hostkey)
826 rc = gpg_error_from_errno(ENOMEM);
828 *result = hostkey;
829 return rc;
832 gpg_error_t pwmd_get_hostkey_async(pwm_t *pwm, const char *host, int port)
834 return _do_pwmd_tcp_connect_async(pwm, host, port, NULL, NULL, NULL,
835 ASYNC_CMD_HOSTKEY);
839 * ssh://[username@]hostname[:port],identity,known_hosts
841 * Any missing parameters are checked for in init_tcp_conn().
843 static int parse_ssh_url(char *str, char **host, int *port, char **user,
844 char **identity, char **known_hosts)
846 char *p;
847 char *t;
848 int len;
850 *host = *user = *identity = *known_hosts = NULL;
851 *port = -1;
852 p = strrchr(str, '@');
854 if (p) {
855 len = strlen(str)-strlen(p)+1;
856 *user = pwmd_malloc(len);
857 snprintf(*user, len, "%s", str);
858 p++;
860 else
861 p = str;
863 t = strchr(p, ':');
865 if (t) {
866 len = strlen(p)-strlen(t)+1;
867 *host = pwmd_malloc(len);
868 snprintf(*host, len, "%s", p);
869 t++;
870 *port = atoi(t);
872 while (*t && isdigit(*t))
873 t++;
875 p = t;
878 t = strchr(p, ',');
880 if (t) {
881 char *t2;
883 if (!*host) {
884 len = strlen(p)-strlen(t)+1;
885 *host = pwmd_malloc(len);
886 snprintf(*host, len, "%s", p);
889 t++;
890 t2 = strchr(t, ',');
892 if (t2)
893 len = strlen(t)-strlen(t2)+1;
894 else
895 len = strlen(t)+1;
897 *identity = pwmd_malloc(len);
898 snprintf(*identity, len, "%s", t);
900 if (t2) {
901 t2++;
902 t += len+1;
903 len = strlen(t2)+1;
904 *known_hosts = pwmd_malloc(len);
905 snprintf(*known_hosts, len, "%s", t2);
908 else {
909 if (!*host) {
910 len = strlen(p)+1;
911 *host = pwmd_malloc(len);
912 snprintf(*host, len, "%s", p);
916 return 0;
918 #endif
920 static gpg_error_t _pwmd_connect_url(pwm_t *pwm, const char *url, int async)
922 char *p = (char *)url;
923 gpg_error_t rc;
925 if (!pwm)
926 return GPG_ERR_INV_ARG;
928 if (!p || !strncmp(p, "socket://", 9)) {
929 if (p)
930 p += 9;
932 goto connect_uds;
934 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
935 !strncmp(p, "ssh4://", 7)) {
936 #ifndef WITH_TCP
937 return GPG_ERR_NOT_IMPLEMENTED;
938 #else
939 char *host = NULL;
940 int port = -1;
941 char *identity = NULL;
942 char *known_hosts = NULL;
943 char *username = NULL;
945 if (!strncmp(p, "ssh6://", 7)) {
946 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV6);
947 p += 7;
949 else if (!strncmp(p, "ssh4://", 7)) {
950 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV4);
951 p += 7;
953 else {
954 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IP_ANY);
955 p += 6;
958 if (rc)
959 return rc;
961 rc = parse_ssh_url(p, &host, &port, &username, &identity,
962 &known_hosts);
964 if (rc)
965 return rc;
967 if (async)
968 rc = pwmd_ssh_connect_async(pwm, host, port, identity, username,
969 known_hosts);
970 else
971 rc = pwmd_ssh_connect(pwm, host, port, identity, username,
972 known_hosts);
974 if (host)
975 pwmd_free(host);
977 if (username)
978 pwmd_free(username);
980 if (identity)
981 pwmd_free(identity);
983 if (known_hosts)
984 pwmd_free(known_hosts);
986 return rc;
987 #endif
989 else {
990 connect_uds:
991 rc = pwmd_connect(pwm, p);
992 pwm->state = ASYNC_DONE;
993 return rc;
996 return GPG_ERR_UNSUPPORTED_PROTOCOL;
999 gpg_error_t pwmd_connect_url(pwm_t *pwm, const char *url)
1001 return _pwmd_connect_url(pwm, url, 0);
1004 gpg_error_t pwmd_connect_url_async(pwm_t *pwm, const char *url)
1006 return _pwmd_connect_url(pwm, url, 1);
1009 static char *expand_homedir(char *str, struct passwd *pw)
1011 char *p = str;
1012 char *pwbuf = NULL;
1013 char *result;
1015 if (*p != '~' || *(p+1) != '/')
1016 return pwmd_strdup(p);
1018 if (!pw) {
1019 struct passwd t;
1021 pwbuf = _getpwuid(&t);
1023 if (!pwbuf)
1024 return NULL;
1026 pw = &t;
1029 p += 2;
1030 result = pwmd_strdup_printf("%s/%s", pw->pw_dir, p);
1032 if (pwbuf)
1033 pwmd_free(pwbuf);
1035 return result;
1038 gpg_error_t pwmd_connect(pwm_t *pwm, const char *path)
1040 char *socketpath = NULL;
1041 assuan_context_t ctx;
1042 struct passwd pw;
1043 char *pwbuf;
1044 gpg_error_t rc;
1046 if (!pwm)
1047 return GPG_ERR_INV_ARG;
1049 pwbuf = _getpwuid(&pw);
1051 if (!pwbuf)
1052 return gpg_error_from_errno(errno);
1054 if (!path || !*path) {
1055 socketpath = (char *)pwmd_malloc(strlen(pw.pw_dir) + strlen("/.pwmd/socket") + 1);
1056 sprintf(socketpath, "%s/.pwmd/socket", pw.pw_dir);
1058 else {
1059 socketpath = expand_homedir((char *)path, &pw);
1061 if (!socketpath) {
1062 pwmd_free(pwbuf);
1063 return gpg_error_from_errno(ENOMEM);
1067 pwmd_free(pwbuf);
1068 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
1069 pwmd_free(socketpath);
1071 if (rc)
1072 return rc;
1074 pwm->ctx = ctx;
1075 return _socket_connect_finalize(pwm);
1078 void pwmd_close(pwm_t *pwm)
1080 if (!pwm)
1081 return;
1083 if (pwm->ctx)
1084 assuan_disconnect(pwm->ctx);
1086 if (pwm->password)
1087 pwmd_free(pwm->password);
1089 if (pwm->title)
1090 pwmd_free(pwm->title);
1092 if (pwm->desc)
1093 pwmd_free(pwm->desc);
1095 if (pwm->prompt)
1096 pwmd_free(pwm->prompt);
1098 if (pwm->pinentry_tty)
1099 pwmd_free(pwm->pinentry_tty);
1101 if (pwm->pinentry_display)
1102 pwmd_free(pwm->pinentry_display);
1104 if (pwm->pinentry_term)
1105 pwmd_free(pwm->pinentry_term);
1107 if (pwm->lcctype)
1108 pwmd_free(pwm->lcctype);
1110 if (pwm->lcmessages)
1111 pwmd_free(pwm->lcmessages);
1113 if (pwm->filename)
1114 pwmd_free(pwm->filename);
1116 if (pwm->name)
1117 pwmd_free(pwm->name);
1119 #ifdef WITH_TCP
1120 if (pwm->tcp_conn)
1121 free_tcp_conn(pwm->tcp_conn);
1122 #endif
1124 pwmd_free(pwm);
1127 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
1129 membuf_t *mem = (membuf_t *)data;
1130 void *p;
1132 if (!buffer)
1133 return 0;
1135 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
1136 return 1;
1138 mem->buf = p;
1139 memcpy((char *)mem->buf + mem->len, buffer, len);
1140 mem->len += len;
1141 return 0;
1144 static int _inquire_cb(void *data, const char *keyword)
1146 pwm_t *pwm = (pwm_t *)data;
1147 gpg_error_t rc = 0;
1148 int flags = fcntl(pwm->fd, F_GETFL);
1150 /* Shouldn't get this far without a callback. */
1151 if (!pwm->inquire_func)
1152 return GPG_ERR_INV_ARG;
1154 for (;;) {
1155 char *result = NULL;
1156 size_t len;
1157 gpg_error_t arc;
1159 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
1160 rc = gpg_err_code(rc);
1162 if (rc == GPG_ERR_EOF || !rc) {
1163 if (len <= 0 || !result) {
1164 rc = 0;
1165 break;
1168 arc = assuan_send_data(pwm->ctx, result, len);
1170 if (rc == GPG_ERR_EOF) {
1171 rc = arc;
1172 break;
1175 rc = arc;
1177 else if (rc)
1178 break;
1180 if (!rc) {
1181 /* Set to non-blocking so _pwmd_process() can return. */
1182 fcntl(pwm->fd, F_SETFL, O_NONBLOCK);
1183 rc = _pwmd_process(pwm);
1184 fcntl(pwm->fd, F_SETFL, flags);
1188 fcntl(pwm->fd, F_SETFL, flags);
1189 return rc;
1192 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, ...)
1194 char *buf;
1195 gpg_error_t rc;
1196 va_list ap;
1198 if (pwm->state == ASYNC_DONE)
1199 pwm->state = ASYNC_INIT;
1201 if (pwm->state != ASYNC_INIT)
1202 return GPG_ERR_INV_STATE;
1204 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
1206 if (!buf)
1207 return gpg_error_from_errno(ENOMEM);
1209 va_start(ap, cmd);
1210 vsnprintf(buf, ASSUAN_LINELENGTH, cmd, ap);
1211 va_end(ap);
1212 rc = assuan_write_line(pwm->ctx, buf);
1213 pwmd_free(buf);
1215 if (!rc)
1216 pwm->state = ASYNC_PROCESS;
1218 return rc;
1221 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
1223 if (!pwm || !filename)
1224 return GPG_ERR_INV_ARG;
1226 if (!pwm->ctx)
1227 return GPG_ERR_INV_STATE;
1229 if (pwm->cmd != ASYNC_CMD_OPEN) {
1230 gpg_error_t rc;
1232 pwm->pin_try = 0;
1234 if (pwm->filename)
1235 pwmd_free(pwm->filename);
1237 pwm->filename = pwmd_strdup(filename);
1239 rc = send_pinentry_options(pwm);
1241 if (rc)
1242 return rc;
1245 pwm->cmd = ASYNC_CMD_OPEN;
1246 return do_nb_command(pwm, "OPEN %s %s", filename,
1247 pwm->password ? pwm->password : "");
1250 gpg_error_t pwmd_save_async(pwm_t *pwm)
1252 gpg_error_t rc;
1254 if (!pwm)
1255 return GPG_ERR_INV_ARG;
1257 if (!pwm->ctx)
1258 return GPG_ERR_INV_STATE;
1260 rc = send_pinentry_options(pwm);
1262 if (rc)
1263 return rc;
1265 pwm->cmd = ASYNC_CMD_SAVE;
1266 return do_nb_command(pwm, "SAVE %s", pwm->password ? pwm->password : "");
1269 static gpg_error_t parse_assuan_line(pwm_t *pwm)
1271 gpg_error_t rc;
1272 char *line;
1273 size_t len;
1275 rc = assuan_read_line(pwm->ctx, &line, &len);
1277 if (!rc) {
1278 if (line[0] == 'O' && line[1] == 'K' &&
1279 (line[2] == 0 || line[2] == ' ')) {
1280 pwm->state = ASYNC_DONE;
1282 else if (line[0] == '#') {
1284 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
1285 if (pwm->status_func) {
1286 rc = pwm->status_func(pwm->status_data,
1287 line[1] == 0 ? line+1 : line+2);
1290 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
1291 (line[3] == 0 || line[3] == ' ')) {
1292 line += 4;
1293 rc = atoi(line);
1294 pwm->state = ASYNC_DONE;
1298 return rc;
1301 gpg_error_t pwmd_pending_line(pwm_t *pwm)
1303 if (!pwm)
1304 return GPG_ERR_INV_ARG;
1306 if (!pwm->ctx)
1307 return GPG_ERR_INV_STATE;
1309 return assuan_pending_line(pwm->ctx) ? 0 : GPG_ERR_NO_DATA;
1312 static pwmd_async_t reset_async(pwm_t *pwm, int done)
1314 pwm->state = ASYNC_INIT;
1315 pwm->cmd = ASYNC_CMD_NONE;
1317 #ifdef WITH_PINENTRY
1318 if (pwm->nb_fd != -1) {
1319 close(pwm->nb_fd);
1320 pwm->nb_fd = -1;
1322 #endif
1323 #ifdef WITH_TCP
1324 if (done && pwm->tcp_conn) {
1325 free_tcp_conn(pwm->tcp_conn);
1326 pwm->tcp_conn = NULL;
1328 #endif
1330 return ASYNC_DONE;
1334 * Used for processing status messages when not in an async command and for
1335 * waiting for the result from pwmd_open_async() and pwmd_save_async().
1337 static gpg_error_t _pwmd_process(pwm_t *pwm)
1339 gpg_error_t rc = 0;
1340 fd_set fds;
1341 struct timeval tv = {0, 0};
1342 int n;
1344 FD_ZERO(&fds);
1345 FD_SET(pwm->fd, &fds);
1346 #ifdef WITH_LIBPTH
1347 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
1348 #else
1349 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
1350 #endif
1352 if (n == -1)
1353 return gpg_error_from_syserror();
1355 if (n > 0) {
1356 if (FD_ISSET(pwm->fd, &fds))
1357 rc = parse_assuan_line(pwm);
1360 while (!rc && assuan_pending_line(pwm->ctx))
1361 rc = parse_assuan_line(pwm);
1363 return rc;
1366 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
1368 fd_set fds;
1369 int n;
1370 struct timeval tv = {0, 0};
1372 if (result)
1373 *result = NULL;
1375 if (!rc)
1376 return GPG_ERR_INV_ARG;
1378 *rc = 0;
1380 if (!pwm) {
1381 *rc = GPG_ERR_INV_ARG;
1382 return ASYNC_DONE;
1384 else if (!pwm->ctx) {
1385 switch (pwm->cmd) {
1386 default:
1387 *rc = GPG_ERR_INV_STATE;
1388 return ASYNC_DONE;
1389 #ifdef WITH_TCP
1390 case ASYNC_CMD_DNS:
1391 case ASYNC_CMD_CONNECT:
1392 case ASYNC_CMD_HOSTKEY:
1393 break;
1394 #endif
1398 /* When not in a command, this will let libassuan process status messages
1399 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
1400 * descriptor returned by pwmd_get_fd() to determine when this should be
1401 * called or call pwmd_pending_line() to determine whether a buffered line
1402 * needs to be processed. */
1403 if (pwm->cmd == ASYNC_CMD_NONE) {
1404 *rc = _pwmd_process(pwm);
1405 return ASYNC_DONE;
1408 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
1409 if (pwm->state == ASYNC_DONE) {
1410 reset_async(pwm, 0);
1411 return ASYNC_DONE;
1414 if (pwm->state != ASYNC_PROCESS) {
1415 *rc = GPG_ERR_INV_STATE;
1416 return ASYNC_DONE;
1419 #ifdef WITH_TCP
1420 if (pwm->cmd == ASYNC_CMD_DNS) {
1421 fd_set rfds, wfds;
1423 if (pwm->tcp_conn->rc) {
1424 *rc = pwm->tcp_conn->rc;
1425 reset_async(pwm, 1);
1426 return ASYNC_DONE;
1429 FD_ZERO(&rfds);
1430 FD_ZERO(&wfds);
1431 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
1433 /* Shouldn't happen. */
1434 if (!n)
1435 return pwm->state;
1437 #ifdef WITH_LIBPTH
1438 n = pth_select(n, &rfds, &wfds, NULL, &tv);
1439 #else
1440 n = select(n, &rfds, &wfds, NULL, &tv);
1441 #endif
1443 if (n > 0)
1444 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
1446 return pwm->state;
1448 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
1449 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
1450 int ret;
1451 socklen_t len = sizeof(int);
1453 FD_ZERO(&fds);
1454 FD_SET(pwm->tcp_conn->fd, &fds);
1455 #ifdef WITH_LIBPTH
1456 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1457 #else
1458 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1459 #endif
1461 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
1462 return pwm->state;
1463 else if (n == -1) {
1464 *rc = gpg_error_from_syserror();
1465 reset_async(pwm, 1);
1466 return ASYNC_DONE;
1469 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
1471 if (ret || n) {
1472 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
1473 reset_async(pwm, 1);
1474 return ASYNC_DONE;
1477 else if (pwm->tcp_conn->rc) {
1478 *rc = pwm->tcp_conn->rc;
1479 reset_async(pwm, 1);
1480 return ASYNC_DONE;
1483 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
1484 *rc = setup_tcp_session(pwm);
1486 if (!*rc) {
1487 switch (pwm->tcp_conn->cmd) {
1488 case ASYNC_CMD_HOSTKEY:
1489 if (!*rc)
1490 *result = pwm->result;
1491 break;
1492 default:
1493 break;
1497 return reset_async(pwm, *rc ? 1 : 0);
1499 #endif
1501 #ifdef WITH_PINENTRY
1502 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
1503 int status;
1505 if (pwm->nb_fd == -1) {
1506 *rc = GPG_ERR_INV_STATE;
1507 return reset_async(pwm, 0);
1510 FD_ZERO(&fds);
1511 FD_SET(pwm->nb_fd, &fds);
1512 FD_SET(pwm->fd, &fds);
1513 #ifdef WITH_LIBPTH
1514 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1515 #else
1516 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1517 #endif
1518 if (n == -1) {
1519 *rc = gpg_error_from_syserror();
1520 return reset_async(pwm, 0);
1523 if (n > 0 && FD_ISSET(pwm->nb_fd, &fds)) {
1524 pwmd_nb_status_t nb;
1525 #ifdef WITH_LIBPTH
1526 size_t len = pth_read(pwm->nb_fd, &nb, sizeof(nb));
1527 #else
1528 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
1529 #endif
1530 waitpid(pwm->nb_pid, &status, WNOHANG);
1532 if (len != sizeof(nb)) {
1533 *rc = gpg_error_from_syserror();
1534 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1537 *rc = nb.error;
1539 if (*rc == GPG_ERR_INV_PASSPHRASE && pwm->cmd == ASYNC_CMD_SAVE2) {
1540 reset_async(pwm, 0);
1541 *rc = pwmd_save_async2(pwm);
1542 return ASYNC_PROCESS;
1544 else if (*rc)
1545 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN2 ? 1 : 0);
1547 if (pwm->cmd == ASYNC_CMD_SAVE2) {
1548 *rc = do_save_command(pwm, nb.password);
1549 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1550 return reset_async(pwm, 0);
1553 if (pwm->cmd == ASYNC_CMD_OPEN2) {
1554 *rc = do_open_command(pwm, pwm->filename, nb.password);
1555 memset(&nb, 0, sizeof(pwmd_nb_status_t));
1557 if (*rc == GPG_ERR_INV_PASSPHRASE) {
1558 if (++pwm->pin_try < pwm->pinentry_tries) {
1559 int n = pwm->pin_try;
1561 reset_async(pwm, 0);
1562 pwm->pin_try = n;
1563 pwm->cmd = ASYNC_CMD_OPEN2;
1564 *rc = pwmd_open_async2(pwm, pwm->filename);
1566 if (*rc)
1567 return reset_async(pwm, 1);
1569 return pwm->state;
1573 return reset_async(pwm, *rc ? 1 : 0);
1577 /* Fall through so status messages can be processed during the
1578 * pinentry. */
1580 #endif
1582 if (pwm->fd < 0) {
1583 *rc = GPG_ERR_INV_STATE;
1584 return reset_async(pwm, 0);
1587 /* This is for pwmd_open_async() and pwmd_save_async(). For pinentry
1588 * retries. */
1589 *rc = _pwmd_process(pwm);
1591 if (*rc && gpg_err_code(*rc) != GPG_ERR_INV_PASSPHRASE) {
1592 reset_async(pwm, 0);
1593 return ASYNC_DONE;
1596 #ifdef WITH_TCP
1597 if (!pwm->tcp_conn && pwm->cmd == ASYNC_CMD_OPEN &&
1598 #else
1599 if (pwm->cmd == ASYNC_CMD_OPEN &&
1600 #endif
1601 gpg_err_code(*rc) == GPG_ERR_INV_PASSPHRASE &&
1602 ++pwm->pin_try < pwm->pinentry_tries) {
1603 pwm->state = ASYNC_INIT;
1604 *rc = pwmd_open_async(pwm, pwm->filename);
1607 if (*rc)
1608 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN ? 1 : 0);
1610 if (pwm->state == ASYNC_DONE) {
1611 reset_async(pwm, 0);
1612 return ASYNC_DONE;
1615 return pwm->state;
1618 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
1619 char **result, const char *cmd)
1621 membuf_t data;
1622 gpg_error_t rc;
1624 data.len = 0;
1625 data.buf = NULL;
1627 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
1628 pwm->status_func, pwm->status_data);
1630 if (rc) {
1631 if (data.buf) {
1632 pwmd_free(data.buf);
1633 data.buf = NULL;
1636 else {
1637 if (data.buf) {
1638 mem_realloc_cb(&data, "", 1);
1640 if (!result) {
1641 pwmd_free(data.buf);
1642 rc = GPG_ERR_INV_ARG;
1644 else
1645 *result = (char *)data.buf;
1649 return gpg_err_code(rc);
1652 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_cb_t fn,
1653 void *data)
1655 if (!pwm || !cmd || !fn)
1656 return GPG_ERR_INV_ARG;
1658 if (!pwm->ctx)
1659 return GPG_ERR_INV_STATE;
1661 pwm->inquire_func = fn;
1662 pwm->inquire_data = data;
1663 return assuan_command(pwm, pwm->ctx, NULL, cmd);
1666 #ifdef WITH_PINENTRY
1667 static gpg_error_t terminate_pinentry(pwm_t *pwm)
1669 pid_t pid = pwm->pid;
1671 pwm->pid = -1;
1673 if (!pwm || pid == -1)
1674 return GPG_ERR_INV_ARG;
1676 if (kill(pid, 0) == 0) {
1677 if (kill(pid, SIGTERM) == -1) {
1678 if (kill(pid, SIGKILL) == -1)
1679 return gpg_error_from_errno(errno);
1682 else
1683 return gpg_error_from_errno(errno);
1685 return 0;
1688 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
1690 char *tmp, *desc;
1691 gpg_error_t error;
1693 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
1695 if (!tmp)
1696 return gpg_error_from_errno(ENOMEM);
1698 if (!pwm->title)
1699 pwm->title = pwmd_strdup_printf(N_("Password Manager Daemon: %s"),
1700 pwm->name ? pwm->name : "libpwmd");
1702 if (!pwm->title)
1703 goto fail_no_mem;
1705 if (!pwm->prompt)
1706 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
1708 if (!pwm->prompt)
1709 goto fail_no_mem;
1711 if (!pwm->desc && (which == PINENTRY_OPEN || which == PINENTRY_SAVE)) {
1712 if (which == PINENTRY_OPEN)
1713 desc = pwmd_strdup_printf(N_("A passphrase is required to open the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1714 else
1715 desc = pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
1717 if (!desc)
1718 goto fail_no_mem;
1721 if (pwm->desc)
1722 desc = pwm->desc;
1724 switch (which) {
1725 case PINENTRY_OPEN:
1726 case PINENTRY_SAVE:
1727 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s", desc);
1729 if (pwm->desc != desc)
1730 pwmd_free(desc);
1731 break;
1732 case PINENTRY_OPEN_FAILED:
1733 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1734 N_("Invalid passphrase, please try again."));
1735 break;
1736 case PINENTRY_SAVE_CONFIRM:
1737 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1738 N_("Please type the passphrase again for confirmation."));
1739 break;
1742 error = pinentry_command(pwm, NULL, tmp);
1744 if (error) {
1745 pwmd_free(tmp);
1746 return error;
1749 snprintf(tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->prompt);
1750 error = pinentry_command(pwm, NULL, tmp);
1752 if (error) {
1753 pwmd_free(tmp);
1754 return error;
1757 snprintf(tmp, ASSUAN_LINELENGTH, "SETDESC %s", pwm->title);
1758 error = pinentry_command(pwm, NULL, tmp);
1759 pwmd_free(tmp);
1760 return error;
1762 fail_no_mem:
1763 pwmd_free(tmp);
1764 return gpg_error_from_errno(ENOMEM);
1767 static void update_pinentry_settings(pwm_t *pwm)
1769 FILE *fp;
1770 char buf[LINE_MAX];
1771 char *p;
1772 struct passwd pw;
1773 char *pwbuf = _getpwuid(&pw);
1775 if (!pwbuf)
1776 return;
1778 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw.pw_dir);
1780 if ((fp = fopen(buf, "r")) == NULL) {
1781 pwmd_free(pwbuf);
1782 return;
1785 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
1786 char name[32], val[256];
1788 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
1789 continue;
1791 if (strcasecmp(name, "TTYNAME") == 0) {
1792 pwmd_free(pwm->pinentry_tty);
1793 pwm->pinentry_tty = pwmd_strdup(val);
1795 else if (strcasecmp(name, "TTYTYPE") == 0) {
1796 pwmd_free(pwm->pinentry_term);
1797 pwm->pinentry_term = pwmd_strdup(val);
1799 else if (strcasecmp(name, "DISPLAY") == 0) {
1800 pwmd_free(pwm->pinentry_display);
1801 pwm->pinentry_display = pwmd_strdup(val);
1803 else if (strcasecmp(name, "PATH") == 0) {
1804 pwmd_free(pwm->pinentry_path);
1805 pwm->pinentry_path = expand_homedir(val, &pw);
1809 pwmd_free(pwbuf);
1810 fclose(fp);
1813 static gpg_error_t launch_pinentry(pwm_t *pwm)
1815 int rc;
1816 assuan_context_t ctx;
1817 int child_list[] = {-1};
1818 char *display = getenv("DISPLAY");
1819 const char *argv[10];
1820 const char **p = argv;
1821 int have_display = 0;
1822 char *tty = NULL;
1823 char *ttybuf = NULL;
1825 update_pinentry_settings(pwm);
1827 if (pwm->pinentry_display || display)
1828 have_display = 1;
1829 else {
1830 if (!pwm->pinentry_tty) {
1831 ttybuf = pwmd_malloc(255);
1833 if (!ttybuf)
1834 return gpg_error_from_errno(ENOMEM);
1836 rc = ttyname_r(STDOUT_FILENO, ttybuf, 255);
1838 if (rc) {
1839 pwmd_free(ttybuf);
1840 return gpg_error_from_errno(rc);
1843 tty = ttybuf;
1845 else
1846 tty = pwm->pinentry_tty;
1849 if (!have_display && !tty)
1850 return GPG_ERR_ENOTTY;
1852 *p++ = "pinentry";
1853 *p++ = have_display ? "--display" : "--ttyname";
1854 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
1856 if (pwm->lcctype) {
1857 *p++ = "--lc-ctype";
1858 *p++ = pwm->lcctype;
1861 if (pwm->lcmessages) {
1862 *p++ = "--lc-messages";
1863 *p++ = pwm->lcmessages;
1866 *p = NULL;
1868 if (!have_display) {
1869 *p++ = "--ttytype";
1870 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
1871 *p = NULL;
1874 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
1876 if (ttybuf)
1877 pwmd_free(ttybuf);
1879 if (rc)
1880 return rc;
1882 pwm->pid = assuan_get_pid(ctx);
1883 pwm->pctx = ctx;
1884 return set_pinentry_strings(pwm, 0);
1887 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
1889 gpg_error_t n;
1891 if (!pwm->pctx) {
1892 n = launch_pinentry(pwm);
1894 if (n)
1895 return n;
1898 return assuan_command(pwm, pwm->pctx, result, cmd);
1901 static void pinentry_disconnect(pwm_t *pwm)
1903 if (pwm->pctx)
1904 assuan_disconnect(pwm->pctx);
1906 pwm->pctx = NULL;
1907 pwm->pid = -1;
1911 * Only called from a child process.
1913 static void catchsig(int sig)
1915 switch (sig) {
1916 case SIGALRM:
1917 if (gelapsed++ >= gtimeout) {
1918 terminate_pinentry(gpwm);
1919 gerror = GPG_ERR_TIMEOUT;
1921 else
1922 alarm(1);
1923 break;
1924 default:
1925 break;
1928 #endif
1931 * Borrowed from libassuan.
1933 static char *percent_escape(const char *atext)
1935 const unsigned char *s;
1936 int len = strlen(atext) * 3 + 1;
1937 char *buf = (char *)pwmd_malloc(len), *p = buf;
1939 if (!buf)
1940 return NULL;
1942 for (s=(const unsigned char *)atext; *s; s++) {
1943 if (*s < ' ') {
1944 sprintf (p, "%%%02X", *s);
1945 p += 3;
1947 else
1948 *p++ = *s;
1951 *p = 0;
1952 return buf;
1955 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
1957 if (!cmd)
1958 return GPG_ERR_INV_ARG;
1960 return assuan_command(pwm, pwm->ctx, result, cmd);
1963 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1964 va_list ap)
1966 char *buf;
1967 size_t len;
1968 gpg_error_t error;
1969 va_list ap2;
1971 if (!pwm || !cmd)
1972 return GPG_ERR_INV_ARG;
1974 if (!pwm->ctx)
1975 return GPG_ERR_INV_STATE;
1978 * C99 allows the dst pointer to be null which will calculate the length
1979 * of the would-be result and return it.
1981 va_copy(ap2, ap);
1982 len = vsnprintf(NULL, 0, cmd, ap)+1;
1983 buf = (char *)pwmd_malloc(len);
1985 if (!buf) {
1986 va_end(ap2);
1987 return gpg_error_from_errno(ENOMEM);
1990 len = vsnprintf(buf, len, cmd, ap2);
1991 va_end(ap2);
1993 if (buf[strlen(buf)-1] == '\n')
1994 buf[strlen(buf)-1] = 0;
1996 if (buf[strlen(buf)-1] == '\r')
1997 buf[strlen(buf)-1] = 0;
1999 error = send_command(pwm, result, buf);
2000 pwmd_free(buf);
2001 return error;
2004 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
2006 va_list ap;
2007 gpg_error_t error;
2009 if (!pwm || !cmd)
2010 return GPG_ERR_INV_ARG;
2012 if (!pwm->ctx)
2013 return GPG_ERR_INV_STATE;
2015 if (result)
2016 *result = NULL;
2018 va_start(ap, cmd);
2019 error = pwmd_command_ap(pwm, result, cmd, ap);
2020 va_end(ap);
2021 return error;
2024 #ifdef WITH_PINENTRY
2025 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
2027 if (gtimeout) {
2028 signal(SIGALRM, catchsig);
2029 alarm(1);
2032 *result = NULL;
2033 return pinentry_command(pwm, result, "GETPIN");
2036 static gpg_error_t getpin(pwm_t *pwm, char **result, int which)
2038 gpg_error_t rc;
2040 gerror = 0;
2041 rc = set_pinentry_strings(pwm, which);
2043 if (rc) {
2044 pinentry_disconnect(pwm);
2045 return rc;
2048 rc = do_getpin(pwm, result);
2051 * Since there was input cancel any timeout setting.
2053 alarm(0);
2054 signal(SIGALRM, SIG_DFL);
2056 if (rc) {
2057 if (pwm->pctx)
2058 pinentry_disconnect(pwm);
2060 /* This lets pwmd_open2() with PWMD_OPTION_PINENTRY_TIMEOUT work. */
2061 if (rc== GPG_ERR_EOF && gerror == GPG_ERR_TIMEOUT)
2062 return gerror;
2064 return rc;
2067 return 0;
2069 #endif
2071 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
2073 char *buf;
2074 gpg_error_t error;
2075 char *result = NULL;
2077 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2079 if (!buf)
2080 return gpg_error_from_errno(ENOMEM);
2082 snprintf(buf, ASSUAN_LINELENGTH, "OPEN %s %s", filename,
2083 password ? password : "");
2084 error = send_command(pwm, &result, buf);
2085 pwmd_free(buf);
2087 if (error && result)
2088 pwmd_free(result);
2090 return error;
2093 static gpg_error_t send_pinentry_options(pwm_t *pwm)
2095 gpg_error_t rc;
2097 if (pwm->pinentry_path) {
2098 rc = pwmd_command(pwm, NULL, "OPTION PATH=%s", pwm->pinentry_path);
2100 if (rc)
2101 return rc;
2104 if (pwm->pinentry_tty) {
2105 rc = pwmd_command(pwm, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
2107 if (rc)
2108 return rc;
2111 if (pwm->pinentry_term) {
2112 rc = pwmd_command(pwm, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
2114 if (rc)
2115 return rc;
2118 if (pwm->pinentry_display) {
2119 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->pinentry_display);
2121 if (rc)
2122 return rc;
2125 if (pwm->title) {
2126 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->title);
2128 if (rc)
2129 return rc;
2132 if (pwm->desc) {
2133 rc = pwmd_command(pwm, NULL, "OPTION DESC=%s", pwm->desc);
2135 if (rc)
2136 return rc;
2139 if (pwm->prompt) {
2140 rc = pwmd_command(pwm, NULL, "OPTION PROMPT=%s", pwm->prompt);
2142 if (rc)
2143 return rc;
2146 if (pwm->lcctype) {
2147 rc = pwmd_command(pwm, NULL, "OPTION LC_CTYPE=%s", pwm->lcctype);
2149 if (rc)
2150 return rc;
2153 if (pwm->lcmessages) {
2154 rc = pwmd_command(pwm, NULL, "OPTION LC_MESSAGES=%s", pwm->lcmessages);
2156 if (rc)
2157 return rc;
2160 if (pwm->pinentry_timeout >= 0) {
2161 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=%i", pwm->pinentry_timeout);
2163 if (rc)
2164 return rc;
2167 return 0;
2170 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
2172 if (!pwm)
2173 return GPG_ERR_INV_ARG;
2175 #ifdef WITH_TCP
2176 if (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1)
2177 #else
2178 if (pwm->fd == -1)
2179 #endif
2180 return GPG_ERR_INV_STATE;
2182 #ifdef WITH_TCP
2183 *result = pwm->tcp_conn ? PWMD_SOCKET_SSH : PWMD_SOCKET_UDS;
2184 #else
2185 *result = PWMD_SOCKET_UDS;
2186 #endif
2187 return 0;
2190 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb,
2191 int local_pinentry)
2193 char *result = NULL;
2194 char *password = NULL;
2195 char *path;
2196 int pin_try;
2197 gpg_error_t rc;
2199 if (!pwm || !filename || !*filename)
2200 return GPG_ERR_INV_ARG;
2202 if (!pwm->ctx)
2203 return GPG_ERR_INV_STATE;
2205 pin_try = pwm->pinentry_tries - 1;
2208 * Avoid calling pinentry if the password is cached on the server or if
2209 * this is a new file. pwmd version 2 adds a VERSION command which is
2210 * determined in _socket_connect_finalize(). If the server is version 2,
2211 * ISCACHED can determine if a file exists.
2213 #ifdef WITH_TCP
2214 if (!pwm->tcp_conn && pwm->version == PWMD_V1) {
2215 #else
2216 if (pwm->version == PWMD_V1) {
2217 #endif
2218 rc = pwmd_command(pwm, &result, "GETCONFIG data_directory");
2220 if (rc)
2221 return rc;
2223 path = pwmd_strdup_printf("%s/%s", result, filename);
2224 pwmd_free(result);
2226 if (!path)
2227 return gpg_error_from_errno(ENOMEM);
2229 if (access(path, R_OK) == -1) {
2230 if (errno == ENOENT) {
2231 pwmd_free(path);
2232 goto gotpassword;
2236 pwmd_free(path);
2239 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
2241 if (gpg_err_code(rc) == GPG_ERR_ENOENT)
2242 goto gotpassword;
2244 if (rc && rc != GPG_ERR_NOT_FOUND)
2245 return rc;
2247 if (rc == GPG_ERR_NOT_FOUND) {
2248 if (pwm->password) {
2249 password = pwm->password;
2250 goto gotpassword;
2253 if (pwm->passfunc) {
2254 rc = pwm->passfunc(pwm->passdata, &password);
2256 if (rc)
2257 return rc;
2259 goto gotpassword;
2263 #ifdef WITH_PINENTRY
2264 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2265 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
2267 if (rc)
2268 return rc;
2270 if (!pwm->filename)
2271 pwm->filename = pwmd_strdup(filename);
2273 if (!pwm->filename)
2274 return gpg_error_from_errno(ENOMEM);
2276 /* Get the passphrase using the LOCAL pinentry. */
2277 if (nb) {
2278 int p[2];
2279 pid_t pid;
2280 pwmd_nb_status_t pw;
2282 if (pipe(p) == -1)
2283 return gpg_error_from_syserror();
2285 #ifdef WITH_LIBPTH
2286 pid = pth_fork();
2287 #else
2288 pid = fork();
2289 #endif
2291 switch (pid) {
2292 case 0:
2293 close(p[0]);
2294 pw.fd = p[0];
2296 if (pwm->pinentry_timeout != 0) {
2297 gpwm = pwm;
2298 gtimeout = abs(pwm->pinentry_timeout);
2299 gelapsed = 0;
2302 pw.error = getpin(pwm, &password, PINENTRY_OPEN);
2304 if (gtimeout && gelapsed >= gtimeout)
2305 pw.error = GPG_ERR_TIMEOUT;
2307 if (!pw.error)
2308 snprintf(pw.password, sizeof(pw.password), "%s",
2309 password);
2311 pinentry_disconnect(pwm);
2312 #ifdef WITH_LIBPTH
2313 pth_write(p[1], &pw, sizeof(pw));
2314 #else
2315 write(p[1], &pw, sizeof(pw));
2316 #endif
2317 memset(&pw, 0, sizeof(pw));
2318 close(p[1]);
2319 _exit(0);
2320 break;
2321 case -1:
2322 rc = gpg_error_from_syserror();
2323 close(p[0]);
2324 close(p[1]);
2325 return rc;
2326 default:
2327 break;
2330 close(p[1]);
2331 pwm->nb_fd = p[0];
2332 pwm->nb_pid = pid;
2333 return 0;
2336 if (pwm->pinentry_timeout != 0) {
2337 gpwm = pwm;
2338 gtimeout = abs(pwm->pinentry_timeout);
2339 gelapsed = 0;
2342 rc = getpin(pwm, &password, PINENTRY_OPEN);
2344 /* Don't timeout when an invalid passphrase was entered. */
2345 gtimeout = 0;
2347 if (rc)
2348 return rc;
2350 #endif
2352 gotpassword:
2353 pwm->state = ASYNC_DONE;
2355 #ifdef WITH_TCP
2356 if (!local_pinentry && !pwm->tcp_conn) {
2357 #else
2358 if (!local_pinentry) {
2359 #endif
2360 rc = send_pinentry_options(pwm);
2362 if (rc)
2363 return rc;
2366 rc = do_open_command(pwm, filename, password);
2369 * Keep the user defined password set with pwmd_setopt(). The password may
2370 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
2372 if (!pwm->passfunc && password && password != pwm->password)
2373 pwmd_free(password);
2375 #ifdef WITH_TCP
2376 if (rc == GPG_ERR_INV_PASSPHRASE && !pwm->tcp_conn) {
2377 #else
2378 if (rc == GPG_ERR_INV_PASSPHRASE) {
2379 #endif
2380 if (pin_try-- > 0 && !nb) {
2382 #ifdef WITH_PINENTRY
2383 if (local_pinentry)
2384 rc = getpin(pwm, &password, PINENTRY_OPEN_FAILED);
2385 else
2386 #endif
2387 rc = pwmd_command(pwm, &result, "OPTION TITLE=%s",
2388 N_("Invalid passphrase, please try again."));
2390 if (rc)
2391 return rc;
2393 goto gotpassword;
2396 #ifdef WITH_PINENTRY
2397 if (nb)
2398 pinentry_disconnect(pwm);
2399 #endif
2401 return rc;
2404 if (!rc) {
2405 if (pwm->filename)
2406 pwmd_free(pwm->filename);
2408 pwm->filename = pwmd_strdup(filename);
2411 return rc;
2414 gpg_error_t pwmd_open2(pwm_t *pwm, const char *filename)
2416 #ifndef WITH_PINENTRY
2417 return GPG_ERR_NOT_IMPLEMENTED;
2418 #else
2419 return do_pwmd_open(pwm, filename, 0, 1);
2420 #endif
2423 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
2425 return do_pwmd_open(pwm, filename, 0, 0);
2428 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
2430 #ifndef WITH_PINENTRY
2431 return GPG_ERR_NOT_IMPLEMENTED;
2432 #else
2433 gpg_error_t rc;
2435 if (!pwm || !filename)
2436 return GPG_ERR_INV_ARG;
2438 if (!pwm->ctx)
2439 return GPG_ERR_INV_STATE;
2441 if (pwm->cmd != ASYNC_CMD_OPEN2)
2442 pwm->pin_try = 0;
2444 pwm->cmd = ASYNC_CMD_OPEN2;
2445 pwm->state = ASYNC_PROCESS;
2446 rc = do_pwmd_open(pwm, filename, 1, 1);
2448 if (rc)
2449 reset_async(pwm, 1);
2451 return rc;
2452 #endif
2455 #ifdef WITH_PINENTRY
2456 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
2458 int confirm = 0;
2459 gpg_error_t error;
2460 char *result = NULL;
2462 again:
2463 error = getpin(pwm, &result, confirm ? PINENTRY_SAVE_CONFIRM : PINENTRY_SAVE);
2465 if (error) {
2466 if (pwm->pctx)
2467 pinentry_disconnect(pwm);
2469 if (*password)
2470 pwmd_free(*password);
2472 return error;
2475 if (!confirm++) {
2476 *password = result;
2477 goto again;
2480 if (strcmp(*password, result)) {
2481 pwmd_free(*password);
2482 pwmd_free(result);
2483 confirm = 0;
2484 *password = NULL;
2485 goto again;
2488 pwmd_free(result);
2489 pinentry_disconnect(pwm);
2490 return 0;
2492 #endif
2494 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
2496 char *buf;
2497 gpg_error_t error;
2498 char *result = NULL;
2500 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
2502 if (!buf)
2503 return gpg_error_from_errno(ENOMEM);
2505 snprintf(buf, ASSUAN_LINELENGTH, "SAVE %s", password ? password : "");
2506 error = send_command(pwm, &result, buf);
2507 pwmd_free(buf);
2509 if (error && result)
2510 pwmd_free(result);
2512 return error;
2515 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb, int local_pinentry)
2517 char *result = NULL;
2518 char *password = NULL;
2519 gpg_error_t rc;
2521 if (!pwm)
2522 return GPG_ERR_INV_ARG;
2524 if (!pwm->ctx)
2525 return GPG_ERR_INV_STATE;
2527 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
2529 if (rc && rc != GPG_ERR_NOT_FOUND)
2530 return rc;
2532 if (rc == GPG_ERR_NOT_FOUND) {
2533 if (pwm->password) {
2534 password = pwm->password;
2535 goto gotpassword;
2538 if (pwm->passfunc) {
2539 rc = pwm->passfunc(pwm->passdata, &password);
2541 if (rc)
2542 return rc;
2544 goto gotpassword;
2548 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
2549 #ifdef WITH_PINENTRY
2550 /* Get the password using the LOCAL pinentry. */
2551 if (nb) {
2552 int p[2];
2553 pid_t pid;
2554 pwmd_nb_status_t pw;
2556 if (pipe(p) == -1)
2557 return gpg_error_from_syserror();
2559 #ifdef WITH_LIBPTH
2560 pid = pth_fork();
2561 #else
2562 pid = fork();
2563 #endif
2565 switch (pid) {
2566 case 0:
2567 close(p[0]);
2568 pw.fd = p[0];
2569 password = NULL;
2570 pw.error = do_save_getpin(pwm, &password);
2571 pinentry_disconnect(pwm);
2572 snprintf(pw.password, sizeof(pw.password), "%s",
2573 password);
2574 #ifdef WITH_LIBPTH
2575 pth_write(p[1], &pw, sizeof(pw));
2576 #else
2577 write(p[1], &pw, sizeof(pw));
2578 #endif
2579 memset(&pw, 0, sizeof(pw));
2580 close(p[1]);
2581 _exit(0);
2582 break;
2583 case -1:
2584 rc = gpg_error_from_syserror();
2585 close(p[0]);
2586 close(p[1]);
2587 return rc;
2588 default:
2589 break;
2592 close(p[1]);
2593 pwm->nb_fd = p[0];
2594 pwm->nb_pid = pid;
2595 return 0;
2598 rc = do_save_getpin(pwm, &password);
2600 if (rc)
2601 return rc;
2602 #endif
2604 else
2605 pwm->state = ASYNC_DONE;
2607 gotpassword:
2608 #ifdef WITH_TCP
2609 if (!local_pinentry && !pwm->tcp_conn) {
2610 #else
2611 if (!local_pinentry) {
2612 #endif
2613 rc = send_pinentry_options(pwm);
2615 if (rc)
2616 return rc;
2619 rc = do_save_command(pwm, password);
2621 if (!pwm->passfunc && password && password != pwm->password)
2622 pwmd_free(password);
2624 return rc;
2627 gpg_error_t pwmd_save_async2(pwm_t *pwm)
2629 #ifndef WITH_PINENTRY
2630 return GPG_ERR_NOT_IMPLEMENTED;
2631 #else
2632 gpg_error_t rc;
2634 if (!pwm)
2635 return GPG_ERR_INV_ARG;
2637 if (!pwm->ctx)
2638 return GPG_ERR_INV_STATE;
2640 pwm->cmd = ASYNC_CMD_SAVE2;
2641 pwm->state = ASYNC_PROCESS;
2642 rc = do_pwmd_save(pwm, 1, 1);
2644 if (rc)
2645 reset_async(pwm, 0);
2647 return rc;
2648 #endif
2651 gpg_error_t pwmd_save2(pwm_t *pwm)
2653 #ifndef WITH_PINENTRY
2654 return GPG_ERR_NOT_IMPLEMENTED;
2655 #else
2656 return do_pwmd_save(pwm, 0, 1);
2657 #endif
2660 gpg_error_t pwmd_save(pwm_t *pwm)
2662 return do_pwmd_save(pwm, 0, 0);
2665 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
2667 va_list ap;
2668 int n = va_arg(ap, int);
2669 char *arg1;
2670 gpg_error_t error = 0;
2672 if (!pwm)
2673 return GPG_ERR_INV_ARG;
2675 va_start(ap, opt);
2677 switch (opt) {
2678 case PWMD_OPTION_STATUS_CB:
2679 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
2680 break;
2681 case PWMD_OPTION_STATUS_DATA:
2682 pwm->status_data = va_arg(ap, void *);
2683 break;
2684 case PWMD_OPTION_PASSPHRASE_CB:
2685 pwm->passfunc = va_arg(ap, pwmd_passphrase_cb_t);
2686 break;
2687 case PWMD_OPTION_PASSPHRASE_DATA:
2688 pwm->passdata = va_arg(ap, void *);
2689 break;
2690 case PWMD_OPTION_PASSPHRASE:
2691 arg1 = va_arg(ap, char *);
2693 if (pwm->password)
2694 pwmd_free(pwm->password);
2696 pwm->password = pwmd_strdup(arg1);
2697 break;
2698 case PWMD_OPTION_PINENTRY_TRIES:
2699 n = va_arg(ap, int);
2701 if (n <= 0) {
2702 va_end(ap);
2703 error = GPG_ERR_INV_VALUE;
2705 else
2706 pwm->pinentry_tries = n;
2707 break;
2708 case PWMD_OPTION_PINENTRY_TIMEOUT:
2709 n = va_arg(ap, int);
2711 if (n < 0) {
2712 va_end(ap);
2713 error = GPG_ERR_INV_VALUE;
2715 else
2716 pwm->pinentry_timeout = n;
2717 break;
2718 case PWMD_OPTION_PINENTRY_PATH:
2719 if (pwm->pinentry_path)
2720 pwmd_free(pwm->pinentry_path);
2722 pwm->pinentry_path = expand_homedir(va_arg(ap, char *), NULL);
2723 break;
2724 case PWMD_OPTION_PINENTRY_TTY:
2725 if (pwm->pinentry_tty)
2726 pwmd_free(pwm->pinentry_tty);
2728 pwm->pinentry_tty = pwmd_strdup(va_arg(ap, char *));
2729 break;
2730 case PWMD_OPTION_PINENTRY_DISPLAY:
2731 if (pwm->pinentry_display)
2732 pwmd_free(pwm->pinentry_display);
2734 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
2735 break;
2736 case PWMD_OPTION_PINENTRY_TERM:
2737 if (pwm->pinentry_term)
2738 pwmd_free(pwm->pinentry_term);
2740 pwm->pinentry_term = pwmd_strdup(va_arg(ap, char *));
2741 break;
2742 case PWMD_OPTION_PINENTRY_TITLE:
2743 if (pwm->title)
2744 pwmd_free(pwm->title);
2746 pwm->title = percent_escape(va_arg(ap, char *));
2747 break;
2748 case PWMD_OPTION_PINENTRY_PROMPT:
2749 if (pwm->prompt)
2750 pwmd_free(pwm->prompt);
2752 pwm->prompt = percent_escape(va_arg(ap, char *));
2753 break;
2754 case PWMD_OPTION_PINENTRY_DESC:
2755 if (pwm->desc)
2756 pwmd_free(pwm->desc);
2758 pwm->desc = percent_escape(va_arg(ap, char *));
2759 break;
2760 case PWMD_OPTION_PINENTRY_LC_CTYPE:
2761 if (pwm->lcctype)
2762 pwmd_free(pwm->lcctype);
2764 pwm->lcctype = pwmd_strdup(va_arg(ap, char *));
2765 break;
2766 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
2767 if (pwm->lcmessages)
2768 pwmd_free(pwm->lcmessages);
2770 pwm->lcmessages = pwmd_strdup(va_arg(ap, char *));
2771 break;
2772 #ifdef WITH_TCP
2773 case PWMD_OPTION_IP_VERSION:
2774 n = va_arg(ap, int);
2776 switch (n) {
2777 case PWMD_IP_ANY:
2778 case PWMD_IPV4:
2779 case PWMD_IPV6:
2780 pwm->prot = n;
2781 break;
2782 default:
2783 error = GPG_ERR_INV_VALUE;
2784 break;
2787 va_end(ap);
2788 break;
2789 #endif
2790 default:
2791 error = GPG_ERR_NOT_IMPLEMENTED;
2792 break;
2795 va_end(ap);
2796 return error;
2799 gpg_error_t pwmd_get_fds(pwm_t *pwm, pwmd_fd_t *fds, int *n_fds)
2801 int in_total;
2802 int fd = 0;
2803 #ifdef WITH_TCP
2804 int afds[ARES_GETSOCK_MAXNUM];
2805 int got_sock = 0;
2806 int n, i;
2807 #endif
2809 if (!pwm || !fds || !n_fds || *n_fds <= 0)
2810 return GPG_ERR_INV_ARG;
2812 in_total = *n_fds;
2813 #ifdef WITH_TCP
2814 memset(afds, 0, sizeof(int)*ARES_GETSOCK_MAXNUM);
2815 #endif
2816 memset(fds, 0, sizeof(pwmd_fd_t)*in_total);
2817 *n_fds = 0;
2819 switch (pwm->cmd) {
2820 default:
2821 case ASYNC_CMD_NONE:
2822 case ASYNC_CMD_OPEN:
2823 case ASYNC_CMD_SAVE:
2824 #ifdef WITH_PINENTRY
2825 async1:
2826 #endif
2827 if (pwm->fd == -1)
2828 return GPG_ERR_INV_STATE;
2830 (*n_fds)++;
2831 fds[fd].fd = pwm->fd;
2832 fds[fd++].flags = PWMD_FD_READABLE;
2833 return 0;
2834 #ifdef WITH_PINENTRY
2835 case ASYNC_CMD_OPEN2:
2836 case ASYNC_CMD_SAVE2:
2837 /* The command has already completed (cached or new). */
2838 if (pwm->state == ASYNC_DONE)
2839 return 0;
2841 if (pwm->nb_fd == -1)
2842 return GPG_ERR_INV_STATE;
2844 (*n_fds)++;
2845 fds[fd].fd = pwm->nb_fd;
2846 fds[fd++].flags = PWMD_FD_READABLE;
2847 goto async1;
2848 #endif
2849 #ifdef WITH_TCP
2850 case ASYNC_CMD_DNS:
2851 if (!pwm->tcp_conn || !pwm->tcp_conn->chan)
2852 return GPG_ERR_INV_STATE;
2854 n = ares_getsock(pwm->tcp_conn->chan, afds, ARES_GETSOCK_MAXNUM);
2856 for (i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
2857 got_sock = 0;
2859 if (fd > in_total) {
2860 *n_fds = fd;
2861 return GPG_ERR_ERANGE;
2864 if (ARES_GETSOCK_READABLE(n, i)) {
2865 got_sock++;
2866 fds[fd].flags |= PWMD_FD_READABLE;
2869 if (ARES_GETSOCK_WRITABLE(n, i)) {
2870 got_sock++;
2871 fds[fd].flags |= PWMD_FD_WRITABLE;
2874 if (got_sock)
2875 fds[fd++].fd = afds[i];
2878 *n_fds = fd;
2879 return 0;
2880 case ASYNC_CMD_CONNECT:
2881 case ASYNC_CMD_HOSTKEY:
2882 if (!pwm->tcp_conn || pwm->tcp_conn->fd == -1)
2883 return GPG_ERR_INV_STATE;
2885 (*n_fds)++;
2886 fds[fd].fd = pwm->tcp_conn->fd;
2887 fds[fd++].flags = PWMD_FD_READABLE;
2888 return 0;
2889 #endif
2892 return GPG_ERR_INV_STATE;
2895 pwm_t *pwmd_new(const char *name)
2897 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
2899 if (!h)
2900 return NULL;
2902 if (name) {
2903 h->name = pwmd_strdup(name);
2905 if (!h->name) {
2906 pwmd_free(h);
2907 return NULL;
2911 h->fd = -1;
2912 #ifdef WITH_PINENTRY
2913 h->nb_fd = -1;
2914 #endif
2915 h->pinentry_timeout = -30;
2916 h->pinentry_tries = 3;
2917 #ifdef WITH_TCP
2918 h->prot = PWMD_IP_ANY;
2919 #endif
2920 return h;
2923 void pwmd_free(void *ptr)
2925 xfree(ptr);
2928 void *pwmd_malloc(size_t size)
2930 return xmalloc(size);
2933 void *pwmd_calloc(size_t nmemb, size_t size)
2935 return xcalloc(nmemb, size);
2938 void *pwmd_realloc(void *ptr, size_t size)
2940 return xrealloc(ptr, size);
2943 char *pwmd_strdup(const char *str)
2945 return xstrdup(str);
2948 char *pwmd_strdup_printf(const char *fmt, ...)
2950 va_list ap, ap2;
2951 int len;
2952 char *buf;
2954 if (!fmt)
2955 return NULL;
2957 va_start(ap, fmt);
2958 va_copy(ap2, ap);
2959 len = vsnprintf(NULL, 0, fmt, ap);
2960 va_end(ap);
2961 buf = pwmd_malloc(++len);
2963 if (buf)
2964 vsnprintf(buf, len, fmt, ap2);
2966 va_end(ap2);
2967 return buf;