Fixed pinentry retries when used with pwmd_open_async().
[libpwmd.git] / src / libpwmd.c
blobcbe10c4d1d8d234875ede6e899e62e479ea4a16f
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 #ifdef WITH_PINENTRY
72 static pwm_t *gpwm;
73 static int gelapsed, gtimeout;
74 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd);
75 #endif
77 static char *_getpwuid(struct passwd *pwd)
79 size_t size = sysconf(_SC_GETPW_R_SIZE_MAX);
80 struct passwd *result;
81 char *buf;
82 int n;
84 if (size == -1)
85 size = 16384;
87 buf = pwmd_malloc(size);
89 if (!buf)
90 return NULL;
92 n = getpwuid_r(getuid(), pwd, buf, size, &result);
94 if (n) {
95 pwmd_free(buf);
96 errno = n;
97 return NULL;
100 if (!result) {
101 pwmd_free(buf);
102 return NULL;
105 errno = n;
106 return buf;
109 const char *_pwmd_strerror(gpg_error_t e)
111 gpg_err_code_t code = gpg_err_code(e);
113 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
114 switch (code) {
115 default:
116 return NULL;
117 case GPG_ERR_USER_1:
118 return N_("Unknown error");
119 case GPG_ERR_USER_2:
120 return N_("No cache slots available");
121 case GPG_ERR_USER_3:
122 return N_("Recursion loop");
123 case GPG_ERR_USER_4:
124 return N_("No file is open");
125 case GPG_ERR_USER_5:
126 return N_("General LibXML error");
127 case GPG_ERR_USER_6:
128 return N_("File modified");
129 case GPG_ERR_USER_7:
130 return N_("Access denied");
134 return NULL;
137 const char *pwmd_strerror(gpg_error_t e)
139 const char *p = _pwmd_strerror(e);
141 return p ? p : gpg_strerror(e);
144 int pwmd_strerror_r(gpg_error_t e, char *buf, size_t size)
146 const char *p = _pwmd_strerror(e);
148 if (p) {
149 snprintf(buf, size, "%s", p);
151 if (strlen(p) > size)
152 return ERANGE;
154 return 0;
157 return gpg_strerror_r(e, buf, size);
160 gpg_error_t pwmd_init()
162 static int initialized;
164 if (initialized)
165 return 0;
167 #ifndef MEM_DEBUG
168 xmem_init();
169 #endif
170 #ifdef ENABLE_NLS
171 bindtextdomain("libpwmd", LOCALEDIR);
172 #endif
173 gpg_err_init();
174 assuan_set_malloc_hooks(pwmd_malloc, pwmd_realloc, pwmd_free);
175 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
176 initialized = 1;
177 return 0;
180 static gpg_error_t _socket_connect_finalize(pwm_t *pwm)
182 int active[2];
183 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
185 if (n <= 0)
186 return GPG_ERR_EBADFD;
188 pwm->fd = active[0];
189 #ifdef WITH_PINENTRY
190 pwm->pid = -1;
191 pwm->pinentry_tries = 3;
192 #endif
193 assuan_set_pointer(pwm->ctx, pwm);
195 if (pwm->name)
196 return pwmd_command(pwm, NULL, "OPTION CLIENT NAME=%s", pwm->name);
198 return 0;
201 #ifdef WITH_TCP
202 static int read_hook(assuan_context_t ctx, assuan_fd_t fd, void *data,
203 size_t len, ssize_t *ret)
205 pwm_t *pwm = assuan_get_pointer(ctx);
207 if (!pwm || !pwm->tcp_conn)
208 #ifdef WITH_LIBPTH
209 *ret = pth_read((int)fd, data, len);
210 #else
211 *ret = read((int)fd, data, len);
212 #endif
213 else {
214 do {
215 *ret = libssh2_channel_read(pwm->tcp_conn->channel, data, len);
216 } while (*ret == LIBSSH2_ERROR_EAGAIN);
219 return *ret <= 0 ? 0 : 1;
222 static int write_hook(assuan_context_t ctx, assuan_fd_t fd, const void *data,
223 size_t len, ssize_t *ret)
225 pwm_t *pwm = assuan_get_pointer(ctx);
227 if (!pwm || !pwm->tcp_conn)
228 #ifdef WITH_LIBPTH
229 *ret = pth_write((int)fd, data, len);
230 #else
231 *ret = write((int)fd, data, len);
232 #endif
233 else {
234 do {
235 *ret = libssh2_channel_write(pwm->tcp_conn->channel, data, len);
236 } while (*ret == LIBSSH2_ERROR_EAGAIN);
239 return *ret <= 0 ? 0 : 1;
242 static void _ssh_deinit(pwmd_tcp_conn_t *conn);
243 static void free_tcp_conn(pwmd_tcp_conn_t *conn)
245 if (!conn)
246 return;
248 if (conn->username) {
249 pwmd_free(conn->username);
250 conn->username = NULL;
253 if (conn->known_hosts) {
254 pwmd_free(conn->known_hosts);
255 conn->known_hosts = NULL;
258 if (conn->identity) {
259 pwmd_free(conn->identity);
260 conn->identity = NULL;
263 if (conn->identity_pub) {
264 pwmd_free(conn->identity_pub);
265 conn->identity_pub = NULL;
268 if (conn->host) {
269 pwmd_free(conn->host);
270 conn->host = NULL;
273 if (conn->hostkey) {
274 pwmd_free(conn->hostkey);
275 conn->hostkey = NULL;
278 if (conn->chan) {
279 ares_destroy(conn->chan);
280 conn->chan = NULL;
283 if (conn->he) {
284 ares_free_hostent(conn->he);
285 conn->he = NULL;
288 if (conn->fd >= 0) {
289 close(conn->fd);
290 conn->fd = -1;
293 if (conn->session)
294 _ssh_deinit(conn);
295 else
296 pwmd_free(conn);
299 static void _ssh_deinit(pwmd_tcp_conn_t *conn)
301 if (!conn)
302 return;
304 if (conn->channel)
305 libssh2_channel_free(conn->channel);
307 if (conn->session) {
308 libssh2_session_disconnect(conn->session, "Bye!");
309 libssh2_session_free(conn->session);
312 conn->session = NULL;
313 conn->channel = NULL;
314 free_tcp_conn(conn);
317 static void _ssh_assuan_deinit(assuan_context_t ctx)
319 pwm_t *pwm = assuan_get_pointer(ctx);
321 pwm->tcp_conn->fd = -1;
322 _ssh_deinit(pwm->tcp_conn);
323 pwm->tcp_conn = NULL;
327 * Sets common options from both pwmd_tcp_connect() and
328 * pwmd_tcp_connect_async().
330 static gpg_error_t init_tcp_conn(pwmd_tcp_conn_t **dst, const char *host,
331 int port, const char *identity, const char *user, const char *hosts,
332 int get)
334 pwmd_tcp_conn_t *conn;
335 gpg_error_t rc = 0;
337 if (get) {
338 if (!host)
339 return GPG_ERR_INV_ARG;
341 else {
342 if (!host || !identity || !hosts)
343 return GPG_ERR_INV_ARG;
346 conn = pwmd_calloc(1, sizeof(pwmd_tcp_conn_t));
348 if (!conn)
349 return gpg_error_from_errno(ENOMEM);
351 conn->port = port == -1 ? 22 : port;
352 conn->host = pwmd_strdup(host);
354 if (!conn->host) {
355 rc = gpg_error_from_errno(ENOMEM);
356 goto fail;
359 if (!get) {
360 struct passwd pw;
361 char *pwbuf = _getpwuid(&pw);
363 if (!pwbuf) {
364 rc = gpg_error_from_errno(errno);
365 goto fail;
368 conn->username = pwmd_strdup(user ? user : pw.pw_name);
369 pwmd_free(pwbuf);
371 if (!conn->username) {
372 rc = gpg_error_from_errno(ENOMEM);
373 goto fail;
376 conn->identity = pwmd_strdup(identity);
378 if (!conn->identity) {
379 rc = gpg_error_from_errno(ENOMEM);
380 goto fail;
383 conn->identity_pub = pwmd_malloc(strlen(conn->identity)+5);
385 if (!conn->identity_pub) {
386 rc = gpg_error_from_errno(ENOMEM);
387 goto fail;
390 sprintf(conn->identity_pub, "%s.pub", conn->identity);
391 conn->known_hosts = pwmd_strdup(hosts);
393 if (!conn->known_hosts) {
394 rc = gpg_error_from_errno(ENOMEM);
395 goto fail;
399 *dst = conn;
400 return 0;
402 fail:
403 free_tcp_conn(conn);
404 return rc;
407 static gpg_error_t do_connect(pwm_t *pwm, int prot, void *addr)
409 struct sockaddr_in their_addr;
411 pwm->tcp_conn->fd = socket(prot, SOCK_STREAM, 0);
413 if (pwm->tcp_conn->fd == -1)
414 return gpg_error_from_syserror();
416 if (pwm->tcp_conn->async)
417 fcntl(pwm->tcp_conn->fd, F_SETFL, O_NONBLOCK);
419 pwm->cmd = ASYNC_CMD_CONNECT;
420 their_addr.sin_family = prot;
421 their_addr.sin_port = htons(pwm->tcp_conn->port);
422 their_addr.sin_addr = *((struct in_addr *)addr);
423 memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
425 #ifdef WITH_LIBPTH
426 if (pth_connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
427 sizeof(their_addr)) == -1)
428 #else
429 if (connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
430 sizeof(their_addr)) == -1)
431 #endif
432 return gpg_error_from_syserror();
434 return 0;
437 static gpg_error_t ares_error_to_pwmd(int status)
439 if (status != ARES_SUCCESS)
440 warnx("%s", ares_strerror(status));
442 switch (status) {
443 case ARES_ENODATA:
444 case ARES_EFORMERR:
445 case ARES_ENOTFOUND:
446 return GPG_ERR_UNKNOWN_HOST;
447 case ARES_ESERVFAIL:
448 return GPG_ERR_EHOSTDOWN;
449 case ARES_ETIMEOUT:
450 return GPG_ERR_TIMEOUT;
451 case ARES_ENOMEM:
452 return gpg_error_from_errno(ENOMEM);
453 case ARES_ECONNREFUSED:
454 return GPG_ERR_ECONNREFUSED;
455 default:
456 /* FIXME ??? */
457 return GPG_ERR_EHOSTUNREACH;
460 return ARES_SUCCESS;
463 static void dns_resolve_cb(void *arg, int status, int timeouts,
464 unsigned char *abuf, int alen)
466 pwm_t *pwm = arg;
467 int rc;
468 struct hostent *he;
470 if (status == ARES_EDESTRUCTION)
471 return;
473 if (status != ARES_SUCCESS) {
474 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
475 return;
478 //FIXME localhost. works with ipv4. maybe local system config error
479 /* Check for an IPv6 address first. */
480 rc = ares_parse_a_reply(abuf, alen, &he, NULL, NULL);
482 if (rc != ARES_SUCCESS) {
483 if (rc != ARES_ENODATA) {
484 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
485 return;
488 rc = ares_parse_aaaa_reply(abuf, alen, &he, NULL, NULL);
490 if (rc != ARES_SUCCESS) {
491 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
492 return;
496 pwm->tcp_conn->he = he;
497 pwm->tcp_conn->rc = do_connect(pwm, he->h_addrtype, he->h_addr);
500 static gpg_error_t _do_pwmd_tcp_connect_async(pwm_t *pwm, const char *host,
501 int port, const char *identity, const char *user,
502 const char *known_hosts, pwmd_async_cmd_t which)
504 pwmd_tcp_conn_t *conn;
505 gpg_error_t rc;
507 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts,
508 which == ASYNC_CMD_HOSTKEY ? 1 : 0);
510 if (rc)
511 return rc;
513 conn->async = 1;
514 pwm->tcp_conn = conn;
515 pwm->tcp_conn->cmd = which;
517 if (pwm->tcp_conn->cmd == ASYNC_CMD_HOSTKEY)
518 pwm->tcp_conn->get_only = 1;
520 pwm->cmd = ASYNC_CMD_DNS;
521 pwm->state = ASYNC_PROCESS;
522 ares_init(&pwm->tcp_conn->chan);
523 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
524 dns_resolve_cb, pwm);
525 return 0;
528 gpg_error_t pwmd_ssh_connect_async(pwm_t *pwm, const char *host, int port,
529 const char *identity, const char *user, const char *known_hosts)
531 return _do_pwmd_tcp_connect_async(pwm, host, port, identity, user,
532 known_hosts, ASYNC_CMD_CONNECT);
535 static void *_ssh_malloc(size_t size, void **data)
537 return pwmd_malloc(size);
540 static void _ssh_free(void *ptr, void **data)
542 pwmd_free(ptr);
545 static void *_ssh_realloc(void *ptr, size_t size, void **data)
547 return pwmd_realloc(ptr, size);
550 static char *to_hex(const char *str, size_t slen)
552 int i;
553 char *buf = pwmd_malloc(slen*2+1);
555 if (!buf)
556 return NULL;
558 for (i = 0, buf[0] = 0; i < slen; i++) {
559 char tmp[3];
561 sprintf(tmp, "%02x", (unsigned char)str[i]);
562 strcat(buf, tmp);
565 return buf;
568 static int verify_host_key(pwm_t *pwm)
570 FILE *fp = fopen(pwm->tcp_conn->known_hosts, "r");
571 char *buf, *p;
573 if (!fp)
574 return 1;
576 buf = pwmd_malloc(LINE_MAX);
578 if (!buf)
579 goto fail;
581 while ((p = fgets(buf, LINE_MAX, fp))) {
582 if (*p == '#' || isspace(*p))
583 continue;
585 if (p[strlen(p)-1] == '\n')
586 p[strlen(p)-1] = 0;
588 if (!strcmp(buf, pwm->tcp_conn->hostkey))
589 goto done;
592 fail:
593 if (buf)
594 pwmd_free(buf);
596 fclose(fp);
597 return 1;
599 done:
600 pwmd_free(buf);
601 fclose(fp);
602 return 0;
605 static gpg_error_t authenticate_ssh(pwm_t *pwm)
607 const char *fp = libssh2_hostkey_hash(pwm->tcp_conn->session,
608 LIBSSH2_HOSTKEY_HASH_SHA1);
609 char *userauth;
611 pwm->tcp_conn->hostkey = to_hex(fp, 20);
613 if (!pwm->tcp_conn->hostkey)
614 return gpg_error_from_errno(ENOMEM);
616 if (pwm->tcp_conn->get_only)
617 return 0;
619 if (!fp || verify_host_key(pwm))
620 return GPG_ERR_CHECKSUM;
622 userauth = libssh2_userauth_list(pwm->tcp_conn->session,
623 pwm->tcp_conn->username, strlen(pwm->tcp_conn->username));
625 if (!userauth || !strstr(userauth, "publickey"))
626 return GPG_ERR_BAD_PIN_METHOD;
628 if (libssh2_userauth_publickey_fromfile(pwm->tcp_conn->session,
629 pwm->tcp_conn->username, pwm->tcp_conn->identity_pub,
630 pwm->tcp_conn->identity, NULL))
631 return GPG_ERR_BAD_SECKEY;
633 return 0;
636 static gpg_error_t setup_tcp_session(pwm_t *pwm)
638 assuan_context_t ctx;
639 struct assuan_io_hooks io_hooks = {read_hook, write_hook};
640 gpg_error_t rc;
642 pwm->tcp_conn->session = libssh2_session_init_ex(_ssh_malloc, _ssh_free,
643 _ssh_realloc, NULL);
645 if (!pwm->tcp_conn->session) {
646 rc = gpg_error_from_errno(ENOMEM);
647 goto fail;
650 if (libssh2_session_startup(pwm->tcp_conn->session, pwm->tcp_conn->fd)) {
651 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
652 goto fail;
655 rc = authenticate_ssh(pwm);
657 if (rc)
658 goto fail;
660 /* pwmd_get_hostkey(). */
661 if (pwm->tcp_conn->get_only) {
662 pwm->result = pwmd_strdup(pwm->tcp_conn->hostkey);
664 if (!pwm->result) {
665 rc = gpg_error_from_errno(ENOMEM);
666 goto fail;
669 return 0;
672 pwm->tcp_conn->channel = libssh2_channel_open_session(pwm->tcp_conn->session);
674 if (!pwm->tcp_conn->channel) {
675 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
676 goto fail;
679 if (libssh2_channel_shell(pwm->tcp_conn->channel)) {
680 rc = GPG_ERR_ASSUAN_SERVER_FAULT;
681 goto fail;
684 assuan_set_io_hooks(&io_hooks);
685 rc = assuan_socket_connect_fd(&ctx, pwm->tcp_conn->fd, 0, pwm);
687 if (rc)
688 goto fail;
690 assuan_set_finish_handler(ctx, _ssh_assuan_deinit);
691 pwm->ctx = ctx;
692 return _socket_connect_finalize(pwm);
694 fail:
695 free_tcp_conn(pwm->tcp_conn);
696 pwm->tcp_conn = NULL;
697 return rc;
700 static gpg_error_t _do_pwmd_tcp_connect(pwm_t *pwm, const char *host, int port,
701 const char *identity, const char *user, const char *known_hosts, int get)
703 pwmd_tcp_conn_t *conn;
704 gpg_error_t rc;
706 rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts, get);
708 if (rc)
709 return rc;
711 pwm->tcp_conn = conn;
712 pwm->tcp_conn->get_only = get;
713 pwm->cmd = ASYNC_CMD_DNS;
714 ares_init(&pwm->tcp_conn->chan);
715 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
716 dns_resolve_cb, pwm);
718 /* dns_resolve_cb() may have already been called. */
719 if (pwm->tcp_conn->rc) {
720 rc = pwm->tcp_conn->rc;
721 goto fail;
725 * Fake a blocking DNS lookup. libcares does a better job than
726 * getaddrinfo().
728 do {
729 fd_set rfds, wfds;
730 int n;
731 struct timeval tv;
733 FD_ZERO(&rfds);
734 FD_ZERO(&wfds);
735 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
736 ares_timeout(pwm->tcp_conn->chan, NULL, &tv);
737 #ifdef WITH_LIBPTH
738 n = pth_select(n, &rfds, &wfds, NULL, &tv);
739 #else
740 n = select(n, &rfds, &wfds, NULL, &tv);
741 #endif
743 if (n == -1) {
744 rc = gpg_error_from_syserror();
745 goto fail;
747 else if (n == 0) {
748 rc = GPG_ERR_TIMEOUT;
749 goto fail;
752 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
754 if (pwm->tcp_conn->rc)
755 break;
756 } while (pwm->cmd == ASYNC_CMD_DNS);
758 if (pwm->tcp_conn->rc) {
759 rc = pwm->tcp_conn->rc;
760 goto fail;
763 return setup_tcp_session(pwm);
765 fail:
766 return rc;
769 gpg_error_t pwmd_ssh_connect(pwm_t *pwm, const char *host, int port,
770 const char *identity, const char *user, const char *known_hosts)
772 return _do_pwmd_tcp_connect(pwm, host, port, identity, user, known_hosts, 0);
775 /* Must free the result with pwmd_free(). */
776 gpg_error_t pwmd_get_hostkey(const char *host, int port, char **result)
778 char *hostkey;
779 pwm_t *pwm = pwmd_new(NULL);
780 gpg_error_t rc;
782 if (!pwm)
783 return gpg_error_from_errno(ENOMEM);
785 rc = _do_pwmd_tcp_connect(pwm, host, port, NULL, NULL, NULL, 1);
787 if (rc) {
788 pwmd_close(pwm);
789 return rc;
792 hostkey = pwmd_strdup(pwm->tcp_conn->hostkey);
794 if (!hostkey)
795 rc = gpg_error_from_errno(ENOMEM);
797 pwmd_close(pwm);
798 *result = hostkey;
799 return rc;
802 gpg_error_t pwmd_get_hostkey_async(pwm_t *pwm, const char *host, int port)
804 return _do_pwmd_tcp_connect_async(pwm, host, port, NULL, NULL, NULL,
805 ASYNC_CMD_HOSTKEY);
807 #endif
809 gpg_error_t pwmd_connect(pwm_t *pwm, const char *path)
811 char *socketpath = NULL;
812 assuan_context_t ctx;
813 struct passwd pw;
814 char *pwbuf = _getpwuid(&pw);
815 gpg_error_t rc;
817 if (!pwbuf)
818 return gpg_error_from_errno(errno);
820 if (!path) {
821 socketpath = (char *)pwmd_malloc(strlen(pw.pw_dir) + strlen("/.pwmd/socket") + 1);
822 sprintf(socketpath, "%s/.pwmd/socket", pw.pw_dir);
824 else
825 socketpath = pwmd_strdup(path);
827 pwmd_free(pwbuf);
828 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
829 pwmd_free(socketpath);
831 if (rc)
832 return rc;
834 pwm->ctx = ctx;
835 return _socket_connect_finalize(pwm);
838 void pwmd_close(pwm_t *pwm)
840 if (!pwm)
841 return;
843 if (pwm->ctx)
844 assuan_disconnect(pwm->ctx);
846 if (pwm->password)
847 pwmd_free(pwm->password);
849 if (pwm->title)
850 pwmd_free(pwm->title);
852 if (pwm->desc)
853 pwmd_free(pwm->desc);
855 if (pwm->prompt)
856 pwmd_free(pwm->prompt);
858 if (pwm->pinentry_tty)
859 pwmd_free(pwm->pinentry_tty);
861 if (pwm->pinentry_display)
862 pwmd_free(pwm->pinentry_display);
864 if (pwm->pinentry_term)
865 pwmd_free(pwm->pinentry_term);
867 if (pwm->lcctype)
868 pwmd_free(pwm->lcctype);
870 if (pwm->lcmessages)
871 pwmd_free(pwm->lcmessages);
873 if (pwm->filename)
874 pwmd_free(pwm->filename);
876 if (pwm->name)
877 pwmd_free(pwm->name);
879 #ifdef WITH_TCP
880 if (pwm->tcp_conn)
881 free_tcp_conn(pwm->tcp_conn);
882 #endif
884 pwmd_free(pwm);
887 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
889 membuf_t *mem = (membuf_t *)data;
890 void *p;
892 if (!buffer)
893 return 0;
895 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
896 return 1;
898 mem->buf = p;
899 memcpy((char *)mem->buf + mem->len, buffer, len);
900 mem->len += len;
901 return 0;
904 static int _inquire_cb(void *data, const char *keyword)
906 pwm_t *pwm = (pwm_t *)data;
907 gpg_error_t rc = 0;
908 int flags = fcntl(pwm->fd, F_GETFL);
910 /* Shouldn't get this far without a callback. */
911 if (!pwm->inquire_func)
912 return GPG_ERR_INV_ARG;
915 * Since the socket file descriptor is probably set to non-blocking, set to
916 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixes when
917 * asynchronous INQUIRE is supported by either libassuan or a later
918 * libpwmd.
920 fcntl(pwm->fd, F_SETFL, 0);
922 for (;;) {
923 char *result = NULL;
924 size_t len;
925 gpg_error_t arc;
927 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
928 rc = gpg_err_code(rc);
930 if (rc == GPG_ERR_EOF || !rc) {
931 if (len <= 0 || !result || !*result) {
932 rc = 0;
933 break;
936 arc = assuan_send_data(pwm->ctx, result, len);
938 if (rc == GPG_ERR_EOF) {
939 rc = arc;
940 break;
943 rc = arc;
945 else if (rc)
946 break;
949 fcntl(pwm->fd, F_SETFL, flags);
950 return rc;
953 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, ...)
955 char *buf;
956 gpg_error_t rc;
957 va_list ap;
959 if (pwm->state == ASYNC_DONE)
960 pwm->state = ASYNC_INIT;
962 if (pwm->state != ASYNC_INIT)
963 return GPG_ERR_INV_STATE;
965 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
967 if (!buf)
968 return gpg_error_from_errno(ENOMEM);
970 va_start(ap, cmd);
971 vsnprintf(buf, ASSUAN_LINELENGTH, cmd, ap);
972 va_end(ap);
973 rc = assuan_write_line(pwm->ctx, buf);
974 pwmd_free(buf);
976 if (!rc)
977 pwm->state = ASYNC_PROCESS;
979 fail:
980 return rc;
983 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
985 if (!pwm || !filename)
986 return GPG_ERR_INV_ARG;
988 /* For pinentry retries. */
989 if (!pwm->is_open_cmd) {
990 if (pwm->filename)
991 pwmd_free(pwm->filename);
993 pwm->filename = pwmd_strdup(filename);
996 pwm->is_open_cmd = 1;
997 pwm->cmd = ASYNC_CMD_OPEN;
998 return do_nb_command(pwm, "OPEN %s %s", filename,
999 pwm->password ? pwm->password : "");
1002 gpg_error_t pwmd_save_async(pwm_t *pwm)
1004 if (!pwm)
1005 return GPG_ERR_INV_ARG;
1007 pwm->cmd = ASYNC_CMD_SAVE;
1008 return do_nb_command(pwm, "SAVE %s", pwm->password ? pwm->password : "");
1011 static gpg_error_t parse_assuan_line(pwm_t *pwm)
1013 gpg_error_t rc;
1014 char *line;
1015 size_t len;
1017 rc = assuan_read_line(pwm->ctx, &line, &len);
1019 if (!rc) {
1020 if (line[0] == 'O' && line[1] == 'K' &&
1021 (line[2] == 0 || line[2] == ' ')) {
1022 pwm->state = ASYNC_DONE;
1024 else if (line[0] == '#') {
1026 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
1027 if (pwm->status_func) {
1028 pwm->status_func(pwm->status_data,
1029 line[1] == 0 ? line+1 : line+2);
1032 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
1033 (line[3] == 0 || line[3] == ' ')) {
1034 line += 4;
1035 rc = atoi(line);
1036 pwm->state = ASYNC_DONE;
1040 return rc;
1043 gpg_error_t pwmd_pending_line(pwm_t *pwm)
1045 if (!pwm)
1046 return GPG_ERR_INV_ARG;
1048 if (!pwm->ctx)
1049 return GPG_ERR_INV_STATE;
1051 return assuan_pending_line(pwm->ctx) ? 0 : GPG_ERR_NO_DATA;
1054 static pwmd_async_t reset_async(pwm_t *pwm, int done)
1056 pwm->state = ASYNC_INIT;
1057 pwm->cmd = ASYNC_CMD_NONE;
1058 pwm->ntries = 0;
1059 #ifdef WITH_PINENTRY
1060 pwm->is_open_cmd = 0;
1062 if (pwm->nb_fd != -1) {
1063 close(pwm->nb_fd);
1064 pwm->nb_fd = -1;
1066 #endif
1067 #ifdef WITH_TCP
1068 if (done && pwm->tcp_conn && pwm->tcp_conn->fd != -1) {
1069 close(pwm->tcp_conn->fd);
1070 pwm->tcp_conn->fd = -1;
1072 #endif
1074 return ASYNC_DONE;
1077 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
1079 fd_set fds;
1080 int n;
1081 struct timeval tv = {0, 0};
1083 *rc = 0;
1085 if (!pwm) {
1086 *rc = GPG_ERR_INV_ARG;
1087 return ASYNC_DONE;
1090 /* When not in a command, this will let libassuan process status messages
1091 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
1092 * descriptor returned by pwmd_get_fd() to determine when this should be
1093 * called or call pwmd_pending_line() to determine whether a buffered line
1094 * needs to be processed. */
1095 if (pwm->cmd == ASYNC_CMD_NONE) {
1096 *rc = assuan_command(pwm, pwm->ctx, NULL, "NOP");
1097 return pwm->state;
1100 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
1101 if (pwm->state == ASYNC_DONE) {
1102 reset_async(pwm, 0);
1103 return ASYNC_DONE;
1106 if (pwm->state != ASYNC_PROCESS) {
1107 *rc = GPG_ERR_INV_STATE;
1108 return ASYNC_DONE;
1111 #ifdef WITH_TCP
1112 if (pwm->cmd == ASYNC_CMD_DNS) {
1113 fd_set rfds, wfds;
1115 if (pwm->tcp_conn->rc) {
1116 *rc = pwm->tcp_conn->rc;
1117 reset_async(pwm, 1);
1118 return ASYNC_DONE;
1121 FD_ZERO(&rfds);
1122 FD_ZERO(&wfds);
1123 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
1125 /* Shouldn't happen. */
1126 if (!n)
1127 return pwm->state;
1129 #ifdef WITH_LIBPTH
1130 n = pth_select(n, &rfds, &wfds, NULL, &tv);
1131 #else
1132 n = select(n, &rfds, &wfds, NULL, &tv);
1133 #endif
1135 if (n > 0)
1136 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
1138 return pwm->state;
1140 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
1141 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
1142 int ret;
1143 socklen_t len = sizeof(int);
1145 FD_ZERO(&fds);
1146 FD_SET(pwm->tcp_conn->fd, &fds);
1147 #ifdef WITH_LIBPTH
1148 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1149 #else
1150 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1151 #endif
1153 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
1154 return pwm->state;
1155 else if (n == -1) {
1156 *rc = gpg_error_from_syserror();
1157 reset_async(pwm, 1);
1158 return ASYNC_DONE;
1161 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
1163 if (ret || n) {
1164 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
1165 reset_async(pwm, 1);
1166 return ASYNC_DONE;
1169 else if (pwm->tcp_conn->rc) {
1170 *rc = pwm->tcp_conn->rc;
1171 reset_async(pwm, 1);
1172 return ASYNC_DONE;
1175 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
1176 *rc = setup_tcp_session(pwm);
1178 if (!*rc) {
1179 switch (pwm->tcp_conn->cmd) {
1180 case ASYNC_CMD_HOSTKEY:
1181 if (!*rc)
1182 *result = pwm->result;
1183 break;
1184 default:
1185 break;
1189 return reset_async(pwm, *rc ? 1 : 0);
1191 #endif
1193 #ifdef WITH_PINENTRY
1194 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
1195 if (pwm->nb_fd == -1) {
1196 *rc = GPG_ERR_INV_STATE;
1197 return reset_async(pwm, 0);
1200 FD_ZERO(&fds);
1201 FD_SET(pwm->nb_fd, &fds);
1202 #ifdef WITH_LIBPTH
1203 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1204 #else
1205 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
1206 #endif
1207 if (n == -1) {
1208 *rc = gpg_error_from_syserror();
1209 return reset_async(pwm, 0);
1212 if (n > 0) {
1213 pwmd_nb_status_t nb;
1214 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
1216 *rc = nb.error;
1217 return reset_async(pwm, 0);
1220 return pwm->state;
1222 #endif
1224 if (pwm->fd < 0) {
1225 *rc = GPG_ERR_INV_STATE;
1226 return reset_async(pwm, 0);
1229 /* This is for the non-blocking OPEN and SAVE commands. */
1230 FD_ZERO(&fds);
1231 FD_SET(pwm->fd, &fds);
1232 #ifdef WITH_LIBPTH
1233 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
1234 #else
1235 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
1236 #endif
1238 if (n == -1) {
1239 *rc = gpg_error_from_syserror();
1240 return reset_async(pwm, 0);
1243 if (n > 0) {
1244 if (FD_ISSET(pwm->fd, &fds))
1245 *rc = parse_assuan_line(pwm);
1248 while (!*rc && assuan_pending_line(pwm->ctx))
1249 *rc = parse_assuan_line(pwm);
1251 /* For pinentry retries. */
1252 if (pwm->is_open_cmd && gpg_err_code(*rc) == EPWMD_BADKEY &&
1253 ++pwm->ntries < pwm->pinentry_tries) {
1254 pwm->state = ASYNC_INIT;
1255 *rc = pwmd_open_async(pwm, pwm->filename);
1258 if (*rc)
1259 return reset_async(pwm, pwm->cmd == ASYNC_CMD_OPEN ? 1 : 0);
1261 if (pwm->state == ASYNC_DONE) {
1262 reset_async(pwm, 0);
1263 return ASYNC_DONE;
1266 return pwm->state;
1269 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
1270 char **result, const char *cmd)
1272 membuf_t data;
1273 gpg_error_t rc;
1275 data.len = 0;
1276 data.buf = NULL;
1278 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
1279 pwm->status_func, pwm->status_data);
1281 if (rc) {
1282 if (data.buf) {
1283 pwmd_free(data.buf);
1284 data.buf = NULL;
1287 else {
1288 if (data.buf) {
1289 mem_realloc_cb(&data, "", 1);
1291 if (!result) {
1292 pwmd_free(data.buf);
1293 rc = GPG_ERR_INV_ARG;
1295 else
1296 *result = (char *)data.buf;
1300 return gpg_err_code(rc);
1303 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_fn fn,
1304 void *data)
1306 if (!pwm || !cmd || !fn)
1307 return GPG_ERR_INV_ARG;
1309 pwm->inquire_func = fn;
1310 pwm->inquire_data = data;
1311 return assuan_command(pwm, pwm->ctx, NULL, cmd);
1314 static gpg_error_t terminate_pinentry(pwm_t *pwm)
1316 #ifndef WITH_PINENTRY
1317 return GPG_ERR_NOT_IMPLEMENTED;
1318 #else
1319 pid_t pid = pwm->pid;
1321 pwm->pid = -1;
1323 if (!pwm || pid == -1)
1324 return GPG_ERR_INV_ARG;
1326 if (kill(pid, 0) == 0) {
1327 if (kill(pid, SIGTERM) == -1) {
1328 if (kill(pid, SIGKILL) == -1)
1329 return gpg_error_from_errno(errno);
1332 else
1333 return gpg_error_from_errno(errno);
1335 return 0;
1336 #endif
1339 #ifdef WITH_PINENTRY
1340 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
1342 char *tmp;
1343 gpg_error_t error;
1345 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
1347 if (!tmp)
1348 return gpg_error_from_errno(ENOMEM);
1350 if (!pwm->title)
1351 pwm->title = pwmd_strdup(N_("LibPWMD"));
1353 if (!pwm->title)
1354 goto fail_no_mem;
1356 if (!pwm->prompt)
1357 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
1359 if (!pwm->prompt)
1360 goto fail_no_mem;
1362 if (!pwm->desc && !which) {
1363 pwm->desc = pwmd_strdup(N_("Enter a passphrase."));
1365 if (!pwm->desc)
1366 goto fail_no_mem;
1369 if (which == 1)
1370 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1371 N_("Invalid passphrase, please try again."));
1372 else if (which == 2)
1373 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
1374 N_("Please type the passphrase again for confirmation."));
1375 else
1376 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s", pwm->desc);
1378 error = pinentry_command(pwm, NULL, tmp);
1380 if (error) {
1381 pwmd_free(tmp);
1382 return error;
1385 snprintf(tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->prompt);
1386 error = pinentry_command(pwm, NULL, tmp);
1388 if (error) {
1389 pwmd_free(tmp);
1390 return error;
1393 snprintf(tmp, ASSUAN_LINELENGTH, "SETDESC %s", pwm->title);
1394 error = pinentry_command(pwm, NULL, tmp);
1395 pwmd_free(tmp);
1396 return error;
1398 fail_no_mem:
1399 pwmd_free(tmp);
1400 return gpg_error_from_errno(ENOMEM);
1403 static void update_pinentry_settings(pwm_t *pwm)
1405 FILE *fp;
1406 char buf[LINE_MAX];
1407 char *p;
1408 struct passwd pw;
1409 char *pwbuf = _getpwuid(&pw);
1411 if (!pwbuf)
1412 return;
1414 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw.pw_dir);
1415 pwmd_free(pwbuf);
1417 if ((fp = fopen(buf, "r")) == NULL)
1418 return;
1420 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
1421 char name[32], val[256];
1423 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
1424 continue;
1426 if (strcasecmp(name, "TTYNAME") == 0) {
1427 pwmd_free(pwm->pinentry_tty);
1428 pwm->pinentry_tty = pwmd_strdup(val);
1430 else if (strcasecmp(name, "TTYTYPE") == 0) {
1431 pwmd_free(pwm->pinentry_term);
1432 pwm->pinentry_term = pwmd_strdup(val);
1434 else if (strcasecmp(name, "DISPLAY") == 0) {
1435 pwmd_free(pwm->pinentry_display);
1436 pwm->pinentry_display = pwmd_strdup(val);
1438 else if (strcasecmp(name, "PATH") == 0) {
1439 pwmd_free(pwm->pinentry_path);
1440 pwm->pinentry_path = pwmd_strdup(val);
1444 fclose(fp);
1447 static gpg_error_t launch_pinentry(pwm_t *pwm)
1449 int rc;
1450 assuan_context_t ctx;
1451 int child_list[] = {-1};
1452 char *display = getenv("DISPLAY");
1453 const char *argv[10];
1454 const char **p = argv;
1455 int have_display = 0;
1456 char *tty = NULL;
1457 char *ttybuf = NULL;
1459 update_pinentry_settings(pwm);
1461 if (pwm->pinentry_display || display)
1462 have_display = 1;
1463 else {
1464 if (!pwm->pinentry_tty) {
1465 ttybuf = pwmd_malloc(255);
1467 if (!ttybuf)
1468 return gpg_error_from_errno(ENOMEM);
1470 rc = ttyname_r(STDOUT_FILENO, ttybuf, 255);
1472 if (rc) {
1473 pwmd_free(ttybuf);
1474 return gpg_error_from_errno(rc);
1477 tty = ttybuf;
1479 else
1480 tty = pwm->pinentry_tty;
1483 if (!have_display && !tty)
1484 return GPG_ERR_ENOTTY;
1486 *p++ = "pinentry";
1487 *p++ = have_display ? "--display" : "--ttyname";
1488 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
1490 if (pwm->lcctype) {
1491 *p++ = "--lc-ctype";
1492 *p++ = pwm->lcctype;
1495 if (pwm->lcmessages) {
1496 *p++ = "--lc-messages";
1497 *p++ = pwm->lcmessages;
1500 *p = NULL;
1502 if (!have_display) {
1503 *p++ = "--ttytype";
1504 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
1505 *p = NULL;
1508 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
1510 if (ttybuf)
1511 pwmd_free(ttybuf);
1513 if (rc)
1514 return rc;
1516 pwm->pid = assuan_get_pid(ctx);
1517 pwm->pctx = ctx;
1518 return set_pinentry_strings(pwm, 0);
1521 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
1523 gpg_error_t n;
1525 if (!pwm->pctx) {
1526 n = launch_pinentry(pwm);
1528 if (n)
1529 return n;
1532 return assuan_command(pwm, pwm->pctx, result, cmd);
1535 static void pinentry_disconnect(pwm_t *pwm)
1537 if (pwm->pctx)
1538 assuan_disconnect(pwm->pctx);
1540 pwm->pctx = NULL;
1541 pwm->pid = -1;
1545 * Only called from a child process.
1547 static void catchsig(int sig)
1549 switch (sig) {
1550 case SIGALRM:
1551 if (gelapsed++ >= gtimeout)
1552 terminate_pinentry(gpwm);
1553 else
1554 alarm(1);
1555 break;
1556 default:
1557 break;
1560 #endif
1563 * Borrowed from libassuan.
1565 static char *percent_escape(const char *atext)
1567 const unsigned char *s;
1568 int len = strlen(atext) * 3 + 1;
1569 char *buf = (char *)pwmd_malloc(len), *p = buf;
1571 if (!buf)
1572 return NULL;
1574 for (s=(const unsigned char *)atext; *s; s++) {
1575 if (*s < ' ') {
1576 sprintf (p, "%%%02X", *s);
1577 p += 3;
1579 else
1580 *p++ = *s;
1583 *p = 0;
1584 return buf;
1587 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
1589 if (!cmd)
1590 return GPG_ERR_INV_ARG;
1592 return assuan_command(pwm, pwm->ctx, result, cmd);
1595 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1596 va_list ap)
1598 char *buf;
1599 size_t len;
1600 gpg_error_t error;
1602 if (!pwm || !cmd)
1603 return GPG_ERR_INV_ARG;
1606 * C99 allows the dst pointer to be null which will calculate the length
1607 * of the would-be result and return it.
1609 len = vsnprintf(NULL, 0, cmd, ap)+1;
1610 buf = (char *)pwmd_malloc(len);
1611 len = vsnprintf(buf, len, cmd, ap);
1612 error = send_command(pwm, result, buf);
1613 pwmd_free(buf);
1614 return error;
1618 * Avoid sending the BYE command here. libassuan will close the file
1619 * descriptor and release the assuan context. Use pwmd_close() instead.
1621 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1623 va_list ap;
1624 gpg_error_t error;
1626 if (!pwm || !cmd)
1627 return GPG_ERR_INV_ARG;
1629 if (result)
1630 *result = NULL;
1632 va_start(ap, cmd);
1633 error = pwmd_command_ap(pwm, result, cmd, ap);
1634 va_end(ap);
1635 return error;
1638 #ifdef WITH_PINENTRY
1639 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
1641 if (gtimeout) {
1642 signal(SIGALRM, catchsig);
1643 alarm(1);
1646 *result = NULL;
1647 return pinentry_command(pwm, result, "GETPIN");
1650 static gpg_error_t getpin(pwm_t *pwm, char **result, int *try_n, int which)
1652 int pin_try = *try_n;
1653 gpg_error_t error;
1655 getpin_again:
1656 *try_n = pin_try;
1658 if (pin_try == -1) {
1659 error = set_pinentry_strings(pwm, which);
1661 if (error) {
1662 pinentry_disconnect(pwm);
1663 return error;
1666 else {
1667 if (pwm->pinentry_tries-1 != pin_try) {
1668 error = set_pinentry_strings(pwm, 1);
1670 if (error) {
1671 pinentry_disconnect(pwm);
1672 return error;
1677 error = do_getpin(pwm, result);
1680 * Since there was input cancel any timeout setting.
1682 alarm(0);
1684 if (error) {
1685 if (error == GPG_ERR_CANCELED)
1686 return GPG_ERR_CANCELED;
1688 if (pin_try != -1 && pin_try--)
1689 goto getpin_again;
1691 if (pwm->pctx)
1692 pinentry_disconnect(pwm);
1694 *try_n = pin_try;
1695 return error;
1698 return 0;
1700 #endif
1702 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
1704 char *buf;
1705 gpg_error_t error;
1706 char *result = NULL;
1708 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
1710 if (!buf)
1711 return gpg_error_from_errno(ENOMEM);
1713 snprintf(buf, ASSUAN_LINELENGTH, "OPEN %s %s", filename,
1714 password ? password : "");
1715 error = send_command(pwm, &result, buf);
1716 pwmd_free(buf);
1718 if (error && result)
1719 pwmd_free(result);
1721 return error;
1724 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb)
1726 char *result = NULL;
1727 char *password = NULL;
1728 char path[PATH_MAX];
1729 #ifdef WITH_PINENTRY
1730 int pin_try;
1731 #endif
1732 gpg_error_t rc;
1734 if (!pwm || !filename || !*filename)
1735 return GPG_ERR_INV_ARG;
1737 #ifdef WITH_PINENTRY
1738 pin_try = pwm->pinentry_tries - 1;
1739 #endif
1742 * Avoid calling pinentry if the password is cached on the server or if
1743 * this is a new file.
1745 rc = pwmd_command(pwm, &result, "GETCONFIG data_directory");
1747 if (rc)
1748 return rc;
1750 snprintf(path, sizeof(path), "%s/%s", result, filename);
1751 pwmd_free(result);
1753 if (access(path, R_OK) == -1) {
1754 if (errno == ENOENT)
1755 goto gotpassword;
1758 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
1760 if (rc == EPWMD_CACHE_NOT_FOUND) {
1761 if (pwm->passfunc) {
1762 password = (char *)pwm->passfunc(pwm->passdata);
1763 goto gotpassword;
1766 #ifdef WITH_PINENTRY
1768 * Get the password from pinentry.
1770 if (pwm->use_pinentry) {
1772 * Nonblocking is wanted. fork() then return a file descriptor
1773 * that the client can use to read() from.
1775 if (nb) {
1776 int p[2];
1777 pid_t pid;
1778 pwmd_nb_status_t pw;
1780 if (pipe(p) == -1)
1781 return gpg_error_from_syserror();
1783 #ifdef WITH_LIBPTH
1784 pid = pth_fork();
1785 #else
1786 pid = fork();
1787 #endif
1789 switch (pid) {
1790 case 0:
1791 close(p[0]);
1792 pw.fd = p[0];
1794 if (pwm->pinentry_timeout > 0) {
1795 gpwm = pwm;
1796 gtimeout = pwm->pinentry_timeout;
1797 gelapsed = 0;
1800 getpin_nb_again:
1801 rc = getpin(pwm, &password, &pin_try, 0);
1803 if (rc) {
1804 getpin_nb_fail:
1805 if (pwm->pctx)
1806 pinentry_disconnect(pwm);
1808 if (gtimeout && gelapsed >= gtimeout)
1809 rc = GPG_ERR_TIMEOUT;
1811 pw.error = rc;
1812 #ifdef WITH_LIBPTH
1813 pth_write(p[1], &pw, sizeof(pw));
1814 #else
1815 write(p[1], &pw, sizeof(pw));
1816 #endif
1817 close(p[1]);
1818 _exit(1);
1822 * Don't count the time it takes to open the file
1823 * which may have many iterations.
1825 signal(SIGALRM, SIG_DFL);
1826 rc = do_open_command(pwm, filename, password);
1828 if (pwm->pinentry_timeout)
1829 signal(SIGALRM, catchsig);
1831 if (pwm->pctx && rc == EPWMD_BADKEY) {
1832 if (pin_try-- > 0)
1833 goto getpin_nb_again;
1835 goto getpin_nb_fail;
1838 pinentry_disconnect(pwm);
1839 pw.error = 0;
1840 #ifdef WITH_LIBPTH
1841 pth_write(p[1], &pw, sizeof(pw));
1842 #else
1843 write(p[1], &pw, sizeof(pw));
1844 #endif
1845 close(p[1]);
1846 _exit(0);
1847 break;
1848 case -1:
1849 rc = gpg_error_from_syserror();
1850 close(p[0]);
1851 close(p[1]);
1852 return rc;
1853 default:
1854 break;
1857 close(p[1]);
1858 pwm->nb_fd = p[0];
1859 return 0;
1862 else {
1863 #endif
1865 * Not using pinentry and the file was not found
1866 * in the cache.
1868 password = pwm->password;
1869 #ifdef WITH_PINENTRY
1871 #endif
1873 else if (rc)
1874 return rc;
1876 gotpassword:
1877 pwm->state = ASYNC_DONE;
1878 rc = do_open_command(pwm, filename, password);
1881 * Keep the user defined password set with pwmd_setopt(). The password may
1882 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1884 if (!pwm->passfunc && password && password != pwm->password)
1885 pwmd_free(password);
1887 #ifdef WITH_PINENTRY
1888 if (rc == EPWMD_BADKEY) {
1889 if (pin_try-- > 0 && !nb) {
1890 rc = pwmd_command(pwm, &result, "OPTION TITLE=%s",
1891 N_("Invalid passphrase, please try again."));
1893 if (rc)
1894 return rc;
1896 goto gotpassword;
1899 if (nb)
1900 pinentry_disconnect(pwm);
1902 return rc;
1904 #endif
1906 if (!rc) {
1907 if (pwm->filename)
1908 pwmd_free(pwm->filename);
1910 pwm->filename = pwmd_strdup(filename);
1913 return rc;
1916 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
1918 return do_pwmd_open(pwm, filename, 0);
1921 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
1923 #ifndef WITH_PINENTRY
1924 return GPG_ERR_NOT_IMPLEMENTED;
1925 #else
1926 gpg_error_t rc;
1928 pwm->cmd = ASYNC_CMD_OPEN2;
1929 pwm->state = ASYNC_PROCESS;
1930 rc = do_pwmd_open(pwm, filename, 1);
1932 if (rc)
1933 reset_async(pwm, 1);
1935 return rc;
1936 #endif
1939 #ifdef WITH_PINENTRY
1940 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
1942 int confirm = 0;
1943 gpg_error_t error;
1944 char *result = NULL;
1945 int pin_try = -1;
1947 again:
1948 error = getpin(pwm, &result, &pin_try, confirm ? 2 : 0);
1950 if (error) {
1951 if (pwm->pctx)
1952 pinentry_disconnect(pwm);
1954 if (*password)
1955 pwmd_free(*password);
1957 return error;
1960 if (!confirm++) {
1961 *password = result;
1962 goto again;
1965 if (strcmp(*password, result)) {
1966 pwmd_free(*password);
1967 pwmd_free(result);
1968 pinentry_disconnect(pwm);
1969 error = EPWMD_BADKEY;
1970 return error;
1973 pwmd_free(result);
1974 pinentry_disconnect(pwm);
1975 return 0;
1977 #endif
1979 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
1981 char *buf;
1982 gpg_error_t error;
1983 char *result = NULL;
1985 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
1987 if (!buf)
1988 return gpg_error_from_errno(ENOMEM);
1990 snprintf(buf, ASSUAN_LINELENGTH, "SAVE %s", password ? password : "");
1991 error = send_command(pwm, &result, buf);
1992 pwmd_free(buf);
1994 if (error && result)
1995 pwmd_free(result);
1997 return error;
2000 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb)
2002 char *result = NULL;
2003 char *password = NULL;
2004 gpg_error_t rc;
2006 if (!pwm)
2007 return GPG_ERR_INV_ARG;
2009 if (pwm->use_pinentry || pwm->passfunc) {
2010 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
2012 if (rc == EPWMD_CACHE_NOT_FOUND) {
2013 if (pwm->passfunc)
2014 password = (char *)(*pwm->passfunc)(pwm->passdata);
2015 #ifdef WITH_PINENTRY
2016 else if (pwm->use_pinentry) {
2017 if (nb) {
2018 int p[2];
2019 pid_t pid;
2020 pwmd_nb_status_t pw;
2022 if (pipe(p) == -1)
2023 return gpg_error_from_syserror();
2025 #ifdef WITH_LIBPTH
2026 pid = pth_fork();
2027 #else
2028 pid = fork();
2029 #endif
2031 switch (pid) {
2032 case 0:
2033 close(p[0]);
2034 pw.fd = p[0];
2036 do {
2037 password = NULL;
2038 rc = do_save_getpin(pwm, &password);
2039 } while (rc == EPWMD_BADKEY);
2041 if (rc) {
2042 if (pwm->pctx)
2043 pinentry_disconnect(pwm);
2045 pw.error = rc;
2046 #ifdef WITH_LIBPTH
2047 pth_write(p[1], &pw, sizeof(pw));
2048 #else
2049 write(p[1], &pw, sizeof(pw));
2050 #endif
2051 close(p[1]);
2052 _exit(1);
2055 rc = do_save_command(pwm, password);
2056 pinentry_disconnect(pwm);
2057 pw.error = rc;
2058 #ifdef WITH_LIBPTH
2059 pth_write(p[1], &pw, sizeof(pw));
2060 #else
2061 write(p[1], &pw, sizeof(pw));
2062 #endif
2063 close(p[1]);
2064 _exit(0);
2065 break;
2066 case -1:
2067 rc = gpg_error_from_syserror();
2068 close(p[0]);
2069 close(p[1]);
2070 return rc;
2071 default:
2072 break;
2075 close(p[1]);
2076 pwm->nb_fd = p[0];
2077 return 0;
2080 rc = do_save_getpin(pwm, &password);
2082 if (rc)
2083 return rc;
2085 #endif
2087 else {
2088 if (rc)
2089 return rc;
2091 pwm->state = ASYNC_DONE;
2094 else
2095 password = pwm->password;
2097 rc = do_save_command(pwm, password);
2099 if (!pwm->passfunc && password && password != pwm->password)
2100 pwmd_free(password);
2102 return rc;
2105 gpg_error_t pwmd_save_async2(pwm_t *pwm)
2107 #ifndef WITH_PINENTRY
2108 return GPG_ERR_NOT_IMPLEMENTED;
2109 #else
2110 gpg_error_t rc;
2112 pwm->cmd = ASYNC_CMD_SAVE2;
2113 pwm->state = ASYNC_PROCESS;
2114 rc = do_pwmd_save(pwm, 1);
2116 if (rc)
2117 reset_async(pwm, 0);
2119 return rc;
2120 #endif
2123 gpg_error_t pwmd_save(pwm_t *pwm)
2125 return do_pwmd_save(pwm, 0);
2128 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
2130 va_list ap;
2131 int n = va_arg(ap, int);
2132 char *result;
2133 char *arg1;
2134 gpg_error_t error = 0;
2136 if (!pwm)
2137 return GPG_ERR_INV_ARG;
2139 va_start(ap, opt);
2141 switch (opt) {
2142 case PWMD_OPTION_STATUS_FUNC:
2143 pwm->status_func = va_arg(ap, pwmd_status_fn);
2144 break;
2145 case PWMD_OPTION_STATUS_DATA:
2146 pwm->status_data = va_arg(ap, void *);
2147 break;
2148 case PWMD_OPTION_PASSWORD_FUNC:
2149 pwm->passfunc = va_arg(ap, pwmd_password_fn);
2150 break;
2151 case PWMD_OPTION_PASSWORD_DATA:
2152 pwm->passdata = va_arg(ap, void *);
2153 break;
2154 case PWMD_OPTION_PASSWORD:
2155 arg1 = va_arg(ap, char *);
2157 if (pwm->password)
2158 pwmd_free(pwm->password);
2160 pwm->password = pwmd_strdup(arg1);
2161 break;
2162 case PWMD_OPTION_PINENTRY:
2163 n = va_arg(ap, int);
2165 if (n != 0 && n != 1) {
2166 va_end(ap);
2167 error = GPG_ERR_INV_VALUE;
2169 else {
2170 pwm->use_pinentry = n;
2171 error = pwmd_command(pwm, &result, "OPTION PINENTRY=%i",
2172 !pwm->use_pinentry);
2174 break;
2175 #ifdef WITH_PINENTRY
2176 case PWMD_OPTION_PINENTRY_TRIES:
2177 n = va_arg(ap, int);
2179 if (n <= 0) {
2180 va_end(ap);
2181 error = GPG_ERR_INV_VALUE;
2183 else
2184 pwm->pinentry_tries = n;
2185 break;
2186 #endif
2187 case PWMD_OPTION_PINENTRY_TIMEOUT:
2188 n = va_arg(ap, int);
2190 if (n < 0) {
2191 va_end(ap);
2192 error = GPG_ERR_INV_VALUE;
2194 else
2195 pwm->pinentry_timeout = n;
2197 if (!pwm->use_pinentry)
2198 error = pwmd_command(pwm, &result, "OPTION TIMEOUT=%i",
2199 pwm->pinentry_timeout);
2200 break;
2201 case PWMD_OPTION_PINENTRY_PATH:
2202 if (pwm->pinentry_path)
2203 pwmd_free(pwm->pinentry_path);
2205 pwm->pinentry_path = pwmd_strdup(va_arg(ap, char *));
2207 if (!pwm->use_pinentry)
2208 error = pwmd_command(pwm, &result, "OPTION PATH=%s",
2209 pwm->pinentry_path);
2210 break;
2211 case PWMD_OPTION_PINENTRY_TTY:
2212 if (pwm->pinentry_tty)
2213 pwmd_free(pwm->pinentry_tty);
2215 pwm->pinentry_tty = pwmd_strdup(va_arg(ap, char *));
2217 if (!pwm->use_pinentry)
2218 error = pwmd_command(pwm, &result, "OPTION TTY=%s",
2219 pwm->pinentry_tty);
2220 break;
2221 case PWMD_OPTION_PINENTRY_DISPLAY:
2222 if (pwm->pinentry_display)
2223 pwmd_free(pwm->pinentry_display);
2225 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
2227 if (!pwm->use_pinentry)
2228 error = pwmd_command(pwm, &result, "OPTION DISPLAY=%s",
2229 pwm->pinentry_display);
2230 break;
2231 case PWMD_OPTION_PINENTRY_TERM:
2232 if (pwm->pinentry_term)
2233 pwmd_free(pwm->pinentry_term);
2235 pwm->pinentry_term = pwmd_strdup(va_arg(ap, char *));
2237 if (!pwm->use_pinentry)
2238 error = pwmd_command(pwm, &result, "OPTION TTYTYPE=%s",
2239 pwm->pinentry_term);
2240 break;
2241 case PWMD_OPTION_PINENTRY_TITLE:
2242 if (pwm->title)
2243 pwmd_free(pwm->title);
2245 pwm->title = percent_escape(va_arg(ap, char *));
2247 if (!pwm->use_pinentry)
2248 error = pwmd_command(pwm, &result, "OPTION TITLE=%s",
2249 pwm->title);
2250 break;
2251 case PWMD_OPTION_PINENTRY_PROMPT:
2252 if (pwm->prompt)
2253 pwmd_free(pwm->prompt);
2255 pwm->prompt = percent_escape(va_arg(ap, char *));
2257 if (!pwm->use_pinentry)
2258 error = pwmd_command(pwm, &result, "OPTION PROMPT=%s",
2259 pwm->prompt);
2260 break;
2261 case PWMD_OPTION_PINENTRY_DESC:
2262 if (pwm->desc)
2263 pwmd_free(pwm->desc);
2265 pwm->desc = percent_escape(va_arg(ap, char *));
2267 if (!pwm->use_pinentry)
2268 error = pwmd_command(pwm, &result, "OPTION DESC=%s",
2269 pwm->desc);
2270 break;
2271 case PWMD_OPTION_PINENTRY_LC_CTYPE:
2272 if (pwm->lcctype)
2273 pwmd_free(pwm->lcctype);
2275 pwm->lcctype = pwmd_strdup(va_arg(ap, char *));
2277 if (!pwm->use_pinentry)
2278 error = pwmd_command(pwm, &result, "OPTION LC_CTYPE=%s",
2279 pwm->lcctype);
2280 break;
2281 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
2282 if (pwm->lcmessages)
2283 pwmd_free(pwm->lcmessages);
2285 pwm->lcmessages = pwmd_strdup(va_arg(ap, char *));
2287 if (!pwm->use_pinentry)
2288 error = pwmd_command(pwm, &result, "OPTION LC_MESSAGES=%s",
2289 pwm->lcmessages);
2290 break;
2291 default:
2292 error = GPG_ERR_NOT_IMPLEMENTED;
2293 break;
2296 va_end(ap);
2297 return error;
2300 gpg_error_t pwmd_get_fd(pwm_t *pwm, int *fd)
2302 if (!pwm)
2303 return GPG_ERR_INV_ARG;
2305 if (pwm->fd == -1)
2306 return GPG_ERR_INV_STATE;
2308 *fd = pwm->fd;
2309 return 0;
2312 gpg_error_t pwmd_get_fd2(pwm_t *pwm, int *fd)
2314 #ifndef WITH_PINENTRY
2315 return GPG_ERR_NOT_IMPLEMENTED;
2316 #else
2317 if (!pwm)
2318 return GPG_ERR_INV_ARG;
2320 if (pwm->nb_fd == -1)
2321 return GPG_ERR_INV_STATE;
2323 *fd = pwm->nb_fd;
2324 return 0;
2325 #endif
2328 pwm_t *pwmd_new(const char *name)
2330 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
2332 if (!h)
2333 return NULL;
2335 if (name) {
2336 h->name = pwmd_strdup(name);
2338 if (!h->name) {
2339 pwmd_free(h);
2340 return NULL;
2344 h->fd = -1;
2345 #ifdef WITH_PINENTRY
2346 h->nb_fd = -1;
2347 #endif
2348 return h;
2351 void pwmd_free(void *ptr)
2353 xfree(ptr);
2356 void *pwmd_malloc(size_t size)
2358 return xmalloc(size);
2361 void *pwmd_calloc(size_t nmemb, size_t size)
2363 return xcalloc(nmemb, size);
2366 void *pwmd_realloc(void *ptr, size_t size)
2368 return xrealloc(ptr, size);
2371 char *pwmd_strdup(const char *str)
2373 return xstrdup(str);