Use ttyname_r().
[libpwmd.git] / src / libpwmd.c
blob23924df317b01a064b8dcce397ddfcbfa800dd8f
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 static gpg_error_t global_error;
76 #endif
78 static struct passwd *_getpwuid(char **outbuf)
80 size_t size = sysconf(_SC_GETPW_R_SIZE_MAX);
81 struct passwd pwd, *result;
82 char *buf;
83 int n;
85 if (size == -1)
86 size = 16384;
88 buf = xmalloc(size);
90 if (!buf)
91 return NULL;
93 n = getpwuid_r(getuid(), &pwd, buf, size, &result);
95 if (n) {
96 xfree(buf);
97 errno = n;
98 return NULL;
101 if (!result) {
102 xfree(buf);
103 return NULL;
106 errno = n;
107 *outbuf = buf;
108 return result;
111 const char *_pwmd_strerror(gpg_error_t e)
113 gpg_err_code_t code = gpg_err_code(e);
115 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
116 switch (code) {
117 default:
118 return NULL;
119 case GPG_ERR_USER_1:
120 return N_("Unknown error");
121 case GPG_ERR_USER_2:
122 return N_("No cache slots available");
123 case GPG_ERR_USER_3:
124 return N_("Recursion loop");
125 case GPG_ERR_USER_4:
126 return N_("No file is open");
127 case GPG_ERR_USER_5:
128 return N_("General LibXML error");
129 case GPG_ERR_USER_6:
130 return N_("File modified");
131 case GPG_ERR_USER_7:
132 return N_("Access denied");
136 return NULL;
139 const char *pwmd_strerror(gpg_error_t e)
141 const char *p = _pwmd_strerror(e);
143 return p ? p : gpg_strerror(e);
146 int pwmd_strerror_r(gpg_error_t e, char *buf, size_t size)
148 const char *p = _pwmd_strerror(e);
150 if (p) {
151 snprintf(buf, size, "%s", p);
153 if (strlen(p) > size)
154 return ERANGE;
156 return 0;
159 return gpg_strerror_r(e, buf, size);
162 gpg_error_t pwmd_init()
164 static int initialized;
166 if (initialized)
167 return 0;
169 #ifndef MEM_DEBUG
170 xmem_init();
171 #endif
172 #ifdef ENABLE_NLS
173 bindtextdomain("libpwmd", LOCALEDIR);
174 #endif
175 gpg_err_init();
176 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
177 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
178 initialized = 1;
179 return 0;
182 static pwm_t *_socket_connect_finalize(pwm_t *pwm, assuan_context_t ctx)
184 int active[2];
185 int n = assuan_get_active_fds(ctx, 0, active, sizeof(active));
187 pwm->fd = n <= 0 ? -1 : dup(active[0]);
188 pwm->ctx = ctx;
189 #ifdef WITH_PINENTRY
190 pwm->pid = -1;
191 pwm->pinentry_tries = 3;
192 #endif
193 assuan_set_pointer(ctx, pwm);
194 return pwm;
197 #ifdef WITH_TCP
198 static int read_hook(assuan_context_t ctx, assuan_fd_t fd, void *data,
199 size_t len, ssize_t *ret)
201 pwm_t *pwm = assuan_get_pointer(ctx);
203 if (!pwm || !pwm->tcp_conn)
204 #ifdef WITH_LIBPTH
205 *ret = pth_read((int)fd, data, len);
206 #else
207 *ret = read((int)fd, data, len);
208 #endif
209 else {
210 do {
211 *ret = libssh2_channel_read(pwm->tcp_conn->channel, data, len);
212 } while (*ret == LIBSSH2_ERROR_EAGAIN);
215 return *ret <= 0 ? 0 : 1;
218 static int write_hook(assuan_context_t ctx, assuan_fd_t fd, const void *data,
219 size_t len, ssize_t *ret)
221 pwm_t *pwm = assuan_get_pointer(ctx);
223 if (!pwm || !pwm->tcp_conn)
224 #ifdef WITH_LIBPTH
225 *ret = pth_write((int)fd, data, len);
226 #else
227 *ret = write((int)fd, data, len);
228 #endif
229 else {
230 do {
231 *ret = libssh2_channel_write(pwm->tcp_conn->channel, data, len);
232 } while (*ret == LIBSSH2_ERROR_EAGAIN);
235 return *ret <= 0 ? 0 : 1;
238 static void _ssh_deinit(pwmd_tcp_conn_t *conn);
239 static void free_tcp_conn(pwmd_tcp_conn_t *conn)
241 if (!conn)
242 return;
244 if (conn->username) {
245 xfree(conn->username);
246 conn->username = NULL;
249 if (conn->known_hosts) {
250 xfree(conn->known_hosts);
251 conn->known_hosts = NULL;
254 if (conn->identity) {
255 xfree(conn->identity);
256 conn->identity = NULL;
259 if (conn->identity_pub) {
260 xfree(conn->identity_pub);
261 conn->identity_pub = NULL;
264 if (conn->host) {
265 xfree(conn->host);
266 conn->host = NULL;
269 if (conn->hostkey) {
270 xfree(conn->hostkey);
271 conn->hostkey = NULL;
274 if (conn->chan) {
275 ares_destroy(conn->chan);
276 conn->chan = NULL;
279 if (conn->he) {
280 ares_free_hostent(conn->he);
281 conn->he = NULL;
284 if (conn->fd >= 0) {
285 close(conn->fd);
286 conn->fd = -1;
289 if (conn->session)
290 _ssh_deinit(conn);
291 else
292 xfree(conn);
295 static void _ssh_deinit(pwmd_tcp_conn_t *conn)
297 if (!conn)
298 return;
300 if (conn->channel)
301 libssh2_channel_free(conn->channel);
303 if (conn->session) {
304 libssh2_session_disconnect(conn->session, "Bye!");
305 libssh2_session_free(conn->session);
308 conn->session = NULL;
309 conn->channel = NULL;
310 free_tcp_conn(conn);
313 static void _ssh_assuan_deinit(assuan_context_t ctx)
315 pwm_t *pwm = assuan_get_pointer(ctx);
317 _ssh_deinit(pwm->tcp_conn);
318 pwm->tcp_conn = NULL;
322 * Sets common options from both pwmd_tcp_connect() and
323 * pwmd_tcp_connect_async().
325 static gpg_error_t init_tcp_conn(pwmd_tcp_conn_t **dst, const char *host,
326 int port, const char *identity, const char *user, const char *hosts,
327 int get)
329 pwmd_tcp_conn_t *conn;
330 gpg_error_t rc = 0;
332 if (get) {
333 if (!host)
334 return GPG_ERR_INV_ARG;
336 else {
337 if (!host || !identity || !hosts)
338 return GPG_ERR_INV_ARG;
341 conn = xcalloc(1, sizeof(pwmd_tcp_conn_t));
343 if (!conn)
344 return gpg_error_from_errno(ENOMEM);
346 conn->port = port == -1 ? 22 : port;
347 conn->host = xstrdup(host);
349 if (!conn->host) {
350 rc = gpg_error_from_errno(ENOMEM);
351 goto fail;
354 if (!get) {
355 char *pwbuf;
356 struct passwd *pw = _getpwuid(&pwbuf);
358 if (!pw) {
359 rc = gpg_error_from_errno(errno);
360 goto fail;
363 conn->username = xstrdup(user ? user : pw->pw_name);
364 xfree(pwbuf);
366 if (!conn->username) {
367 rc = gpg_error_from_errno(ENOMEM);
368 goto fail;
371 conn->identity = xstrdup(identity);
373 if (!conn->identity) {
374 rc = gpg_error_from_errno(ENOMEM);
375 goto fail;
378 conn->identity_pub = xmalloc(strlen(conn->identity)+5);
380 if (!conn->identity_pub) {
381 rc = gpg_error_from_errno(ENOMEM);
382 goto fail;
385 sprintf(conn->identity_pub, "%s.pub", conn->identity);
386 conn->known_hosts = xstrdup(hosts);
388 if (!conn->known_hosts) {
389 rc = gpg_error_from_errno(ENOMEM);
390 goto fail;
394 *dst = conn;
395 return 0;
397 fail:
398 free_tcp_conn(conn);
399 return rc;
402 static gpg_error_t do_connect(pwm_t *pwm, int prot, void *addr)
404 struct sockaddr_in their_addr;
406 pwm->tcp_conn->fd = socket(prot, SOCK_STREAM, 0);
408 if (pwm->tcp_conn->fd == -1)
409 return gpg_error_from_syserror();
411 if (pwm->tcp_conn->async)
412 fcntl(pwm->tcp_conn->fd, F_SETFL, O_NONBLOCK);
414 pwm->cmd = ASYNC_CMD_CONNECT;
415 their_addr.sin_family = prot;
416 their_addr.sin_port = htons(pwm->tcp_conn->port);
417 their_addr.sin_addr = *((struct in_addr *)addr);
418 memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
420 #ifdef WITH_LIBPTH
421 if (pth_connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
422 sizeof(their_addr)) == -1)
423 #else
424 if (connect(pwm->tcp_conn->fd, (struct sockaddr *)&their_addr,
425 sizeof(their_addr)) == -1)
426 #endif
427 return gpg_error_from_syserror();
429 return 0;
432 static gpg_error_t ares_error_to_pwmd(int status)
434 if (status != ARES_SUCCESS)
435 warnx("%s", ares_strerror(status));
437 switch (status) {
438 case ARES_ENODATA:
439 case ARES_EFORMERR:
440 case ARES_ENOTFOUND:
441 return GPG_ERR_UNKNOWN_HOST;
442 case ARES_ESERVFAIL:
443 return GPG_ERR_EHOSTDOWN;
444 case ARES_ETIMEOUT:
445 return GPG_ERR_TIMEOUT;
446 case ARES_ENOMEM:
447 return gpg_error_from_errno(ENOMEM);
448 case ARES_ECONNREFUSED:
449 return GPG_ERR_ECONNREFUSED;
450 default:
451 /* FIXME ??? */
452 return GPG_ERR_EHOSTUNREACH;
455 return ARES_SUCCESS;
458 static void dns_resolve_cb(void *arg, int status, int timeouts,
459 unsigned char *abuf, int alen)
461 pwm_t *pwm = arg;
462 int rc;
463 struct hostent *he;
465 if (status == ARES_EDESTRUCTION)
466 return;
468 if (status != ARES_SUCCESS) {
469 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
470 return;
473 //FIXME localhost. works with ipv4. maybe local system config error
474 /* Check for an IPv6 address first. */
475 rc = ares_parse_a_reply(abuf, alen, &he, NULL, NULL);
477 if (rc != ARES_SUCCESS) {
478 if (rc != ARES_ENODATA) {
479 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
480 return;
483 rc = ares_parse_aaaa_reply(abuf, alen, &he, NULL, NULL);
485 if (rc != ARES_SUCCESS) {
486 pwm->tcp_conn->rc = ares_error_to_pwmd(status);
487 return;
491 pwm->tcp_conn->he = he;
492 pwm->tcp_conn->rc = do_connect(pwm, he->h_addrtype, he->h_addr);
495 pwm_t *_do_pwmd_tcp_connect_async(const char *host, int port,
496 const char *identity, const char *user, const char *known_hosts,
497 gpg_error_t *rc, pwmd_async_cmd_t which)
499 pwmd_tcp_conn_t *conn;
500 pwm_t *pwm;
502 *rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts,
503 which == ASYNC_CMD_HOSTKEY ? 1 : 0);
505 if (*rc)
506 return NULL;
508 if ((pwm = (pwm_t *)xcalloc(1, sizeof(pwm_t))) == NULL) {
509 *rc = gpg_error_from_syserror();
510 free_tcp_conn(conn);
511 return NULL;
514 conn->async = 1;
515 pwm->tcp_conn = conn;
516 pwm->tcp_conn->cmd = which;
518 if (pwm->tcp_conn->cmd == ASYNC_CMD_HOSTKEY)
519 pwm->tcp_conn->get_only = 1;
521 pwm->cmd = ASYNC_CMD_DNS;
522 pwm->state = ASYNC_PROCESS;
523 ares_init(&pwm->tcp_conn->chan);
524 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
525 dns_resolve_cb, pwm);
526 return pwm;
529 pwm_t *pwmd_tcp_connect_async(const char *host, int port, const char *identity,
530 const char *user, const char *known_hosts, gpg_error_t *rc)
532 return _do_pwmd_tcp_connect_async(host, port, identity, user, known_hosts,
533 rc, ASYNC_CMD_CONNECT);
536 void *_ssh_malloc(size_t size, void **data)
538 return xmalloc(size);
541 void _ssh_free(void *ptr, void **data)
543 xfree(ptr);
546 void *_ssh_realloc(void *ptr, size_t size, void **data)
548 return xrealloc(ptr, size);
551 static char *to_hex(const char *str, size_t slen)
553 int i;
554 char *buf = xmalloc(slen*2+1);
556 if (!buf)
557 return NULL;
559 for (i = 0, buf[0] = 0; i < slen; i++) {
560 char tmp[3];
562 sprintf(tmp, "%02x", (unsigned char)str[i]);
563 strcat(buf, tmp);
566 return buf;
569 static int verify_host_key(pwm_t *pwm)
571 FILE *fp = fopen(pwm->tcp_conn->known_hosts, "r");
572 char *buf, *p;
574 if (!fp)
575 return 1;
577 buf = xmalloc(LINE_MAX);
579 if (!buf)
580 goto fail;
582 while ((p = fgets(buf, LINE_MAX, fp))) {
583 if (*p == '#' || isspace(*p))
584 continue;
586 if (p[strlen(p)-1] == '\n')
587 p[strlen(p)-1] = 0;
589 if (!strcmp(buf, pwm->tcp_conn->hostkey))
590 goto done;
593 fail:
594 if (buf)
595 xfree(buf);
597 fclose(fp);
598 return 1;
600 done:
601 xfree(buf);
602 fclose(fp);
603 return 0;
606 static gpg_error_t authenticate_ssh(pwm_t *pwm)
608 const char *fp = libssh2_hostkey_hash(pwm->tcp_conn->session,
609 LIBSSH2_HOSTKEY_HASH_SHA1);
610 char *userauth;
612 pwm->tcp_conn->hostkey = to_hex(fp, 20);
614 if (!pwm->tcp_conn->hostkey)
615 return gpg_error_from_errno(ENOMEM);
617 if (pwm->tcp_conn->get_only)
618 return 0;
620 if (!fp || verify_host_key(pwm))
621 return GPG_ERR_CHECKSUM;
623 userauth = libssh2_userauth_list(pwm->tcp_conn->session,
624 pwm->tcp_conn->username, strlen(pwm->tcp_conn->username));
626 if (!userauth || !strstr(userauth, "publickey"))
627 return GPG_ERR_BAD_PIN_METHOD;
629 if (libssh2_userauth_publickey_fromfile(pwm->tcp_conn->session,
630 pwm->tcp_conn->username, pwm->tcp_conn->identity_pub,
631 pwm->tcp_conn->identity, NULL))
632 return GPG_ERR_BAD_SECKEY;
634 return 0;
637 static pwm_t *setup_tcp_session(pwm_t *pwm, gpg_error_t *rc)
639 assuan_context_t ctx;
640 struct assuan_io_hooks io_hooks = {read_hook, write_hook};
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 = xstrdup(pwm->tcp_conn->hostkey);
664 if (!pwm->result) {
665 *rc = gpg_error_from_errno(ENOMEM);
666 goto fail;
669 return pwm;
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 return _socket_connect_finalize(pwm, ctx);
693 fail:
694 if (!pwm->tcp_conn->async)
695 pwmd_close(pwm);
697 return NULL;
700 static pwm_t *_do_pwmd_tcp_connect(const char *host, int port,
701 const char *identity, const char *user, const char *known_hosts,
702 gpg_error_t *rc, int get)
704 pwm_t *pwm = NULL;
705 pwmd_tcp_conn_t *conn;
707 *rc = init_tcp_conn(&conn, host, port, identity, user, known_hosts, get);
709 if (*rc)
710 return NULL;
712 if ((pwm = (pwm_t *)xcalloc(1, sizeof(pwm_t))) == NULL) {
713 *rc = gpg_error_from_errno(ENOMEM);
714 goto fail;
717 pwm->tcp_conn = conn;
718 pwm->tcp_conn->get_only = get;
719 pwm->cmd = ASYNC_CMD_DNS;
720 ares_init(&pwm->tcp_conn->chan);
721 ares_query(pwm->tcp_conn->chan, pwm->tcp_conn->host, ns_c_any, ns_t_any,
722 dns_resolve_cb, pwm);
724 /* dns_resolve_cb() may have already been called. */
725 if (pwm->tcp_conn->rc) {
726 *rc = pwm->tcp_conn->rc;
727 goto fail;
731 * Fake a blocking DNS lookup. libcares does a better job than
732 * getaddrinfo().
734 do {
735 fd_set rfds, wfds;
736 int n;
737 struct timeval tv;
739 FD_ZERO(&rfds);
740 FD_ZERO(&wfds);
741 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
742 ares_timeout(pwm->tcp_conn->chan, NULL, &tv);
743 #ifdef WITH_LIBPTH
744 n = pth_select(n, &rfds, &wfds, NULL, &tv);
745 #else
746 n = select(n, &rfds, &wfds, NULL, &tv);
747 #endif
749 if (n == -1) {
750 *rc = gpg_error_from_syserror();
751 goto fail;
753 else if (n == 0) {
754 *rc = GPG_ERR_TIMEOUT;
755 goto fail;
758 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
760 if (pwm->tcp_conn->rc)
761 break;
762 } while (pwm->cmd == ASYNC_CMD_DNS);
764 if (pwm->tcp_conn->rc) {
765 *rc = pwm->tcp_conn->rc;
766 goto fail;
769 return setup_tcp_session(pwm, rc);
771 fail:
772 pwmd_close(pwm);
773 return NULL;
776 pwm_t *pwmd_tcp_connect(const char *host, int port, const char *identity,
777 const char *user, const char *known_hosts, gpg_error_t *rc)
779 return _do_pwmd_tcp_connect(host, port, identity, user, known_hosts, rc, 0);
782 /* Must free the result with pwmd_free_result(). */
783 char *pwmd_get_hostkey(const char *host, int port, gpg_error_t *rc)
785 char *hostkey;
786 pwm_t *pwm;
788 pwm = _do_pwmd_tcp_connect(host, port, NULL, NULL, NULL, rc, 1);
790 if (!pwm)
791 return NULL;
793 hostkey = xstrdup(pwm->tcp_conn->hostkey);
795 if (!hostkey)
796 *rc = gpg_error_from_errno(ENOMEM);
798 pwmd_close(pwm);
799 return hostkey;
802 pwm_t *pwmd_get_hostkey_async(const char *host, int port, gpg_error_t *rc)
804 return _do_pwmd_tcp_connect_async(host, port, NULL, NULL, NULL, rc,
805 ASYNC_CMD_HOSTKEY);
807 #endif
809 pwm_t *pwmd_connect(const char *path, gpg_error_t *rc)
811 pwm_t *pwm = NULL;
812 char *socketpath = NULL;
813 assuan_context_t ctx;
814 char *pwbuf;
815 struct passwd *pw = _getpwuid(&pwbuf);
817 if (!pw) {
818 *rc = gpg_error_from_errno(errno);
819 return NULL;
822 if (!path) {
823 socketpath = (char *)xmalloc(strlen(pw->pw_dir) + strlen("/.pwmd/socket") + 1);
824 sprintf(socketpath, "%s/.pwmd/socket", pw->pw_dir);
826 else
827 socketpath = xstrdup(path);
829 xfree(pwbuf);
830 *rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
831 xfree(socketpath);
833 if (*rc)
834 return NULL;
836 if ((pwm = (pwm_t *)xcalloc(1, sizeof(pwm_t))) == NULL) {
837 *rc = gpg_error_from_syserror();
838 return NULL;
841 return _socket_connect_finalize(pwm, ctx);
844 gpg_error_t pwmd_pending_line(pwm_t *pwm, char **line, size_t *len)
846 if (!pwm)
847 return GPG_ERR_INV_ARG;
849 if (assuan_pending_line(pwm->ctx))
850 return assuan_read_line(pwm->ctx, line, len);
852 return GPG_ERR_NO_DATA;
855 void pwmd_close(pwm_t *pwm)
857 if (!pwm)
858 return;
860 if (pwm->ctx)
861 assuan_disconnect(pwm->ctx);
863 if (pwm->password)
864 xfree(pwm->password);
866 if (pwm->title)
867 xfree(pwm->title);
869 if (pwm->desc)
870 xfree(pwm->desc);
872 if (pwm->prompt)
873 xfree(pwm->prompt);
875 if (pwm->pinentry_tty)
876 xfree(pwm->pinentry_tty);
878 if (pwm->pinentry_display)
879 xfree(pwm->pinentry_display);
881 if (pwm->pinentry_term)
882 xfree(pwm->pinentry_term);
884 if (pwm->lcctype)
885 xfree(pwm->lcctype);
887 if (pwm->lcmessages)
888 xfree(pwm->lcmessages);
890 if (pwm->filename)
891 xfree(pwm->filename);
893 if (pwm->result)
894 xfree(pwm->result);
896 #ifdef WITH_TCP
897 if (pwm->tcp_conn)
898 free_tcp_conn(pwm->tcp_conn);
899 #endif
901 xfree(pwm);
904 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
906 membuf_t *mem = (membuf_t *)data;
907 void *p;
909 if (!buffer)
910 return 0;
912 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
913 return 1;
915 mem->buf = p;
916 memcpy((char *)mem->buf + mem->len, buffer, len);
917 mem->len += len;
918 return 0;
921 void pwmd_free_result(void *data)
923 xfree(data);
926 static int _inquire_cb(void *data, const char *keyword)
928 pwm_t *pwm = (pwm_t *)data;
929 gpg_error_t rc = 0;
930 int flags = fcntl(pwm->fd, F_GETFL);
932 /* Shouldn't get this far without a callback. */
933 if (!pwm->inquire_func)
934 return GPG_ERR_INV_ARG;
937 * Since the socket file descriptor is probably set to non-blocking, set to
938 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixes when
939 * asynchronous INQUIRE is supported by either libassuan or a later
940 * libpwmd.
942 fcntl(pwm->fd, F_SETFL, 0);
944 for (;;) {
945 char *result = NULL;
946 size_t len;
947 gpg_error_t arc;
949 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
950 rc = gpg_err_code(rc);
952 if (rc == GPG_ERR_EOF || !rc) {
953 if (len <= 0 || !result || !*result) {
954 rc = 0;
955 break;
958 arc = assuan_send_data(pwm->ctx, result, len);
960 if (rc == GPG_ERR_EOF) {
961 rc = arc;
962 break;
965 rc = arc;
967 else if (rc)
968 break;
971 fcntl(pwm->fd, F_SETFL, flags);
972 return rc;
975 gpg_error_t pwmd_finalize(pwm_t *pwm)
977 if (!pwm)
978 return GPG_ERR_INV_ARG;
980 if (pwm->cmd == ASYNC_CMD_NONE || pwm->state != ASYNC_DONE)
981 return GPG_ERR_INV_STATE;
983 pwm->state = ASYNC_INIT;
984 pwm->cmd = ASYNC_CMD_NONE;
986 if (pwm->result)
987 xfree(pwm->result);
989 pwm->result = NULL;
991 #ifdef WITH_TCP
992 if (pwm->cmd == ASYNC_CMD_CONNECT || pwm->cmd == ASYNC_CMD_DNS) {
993 gpg_error_t rc = pwm->tcp_conn->rc;
995 /* pwm is no longer a valid handle. */
996 if (rc)
997 pwmd_close(pwm);
999 return 0;
1001 #endif
1003 if (pwm->fd < 0)
1004 return GPG_ERR_INV_ARG;
1006 pwm->ntries = 0;
1007 #ifdef WITH_PINENTRY
1008 pwm->is_open_cmd = 0;
1009 #endif
1010 return 0;
1013 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, const char *arg)
1015 char *buf;
1016 gpg_error_t rc;
1017 size_t len = strlen(cmd) + 2;
1019 len += arg ? strlen(arg) : 0;
1021 if (pwm->state != ASYNC_INIT)
1022 return GPG_ERR_INV_STATE;
1024 buf = (char *)xmalloc(len);
1026 if (!buf) {
1027 rc = gpg_error_from_errno(ENOMEM);
1028 goto fail;
1031 snprintf(buf, len, "%s %s", cmd, arg ? arg : "");
1032 rc = assuan_write_line(pwm->ctx, buf);
1033 xfree(buf);
1035 if (!rc)
1036 pwm->state = ASYNC_PROCESS;
1038 fail:
1039 return rc;
1042 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
1044 if (!pwm || !filename)
1045 return GPG_ERR_INV_ARG;
1047 /* For pinentry retries. */
1048 if (!pwm->is_open_cmd) {
1049 if (pwm->filename)
1050 xfree(pwm->filename);
1052 pwm->filename = xstrdup(filename);
1055 pwm->is_open_cmd = 1;
1056 pwm->cmd = ASYNC_CMD_OPEN;
1057 return do_nb_command(pwm, "OPEN", filename);
1060 gpg_error_t pwmd_save_async(pwm_t *pwm)
1062 if (!pwm)
1063 return GPG_ERR_INV_ARG;
1065 pwm->cmd = ASYNC_CMD_SAVE;
1066 return do_nb_command(pwm, "SAVE", NULL);
1069 static gpg_error_t parse_assuan_line(pwm_t *pwm)
1071 gpg_error_t rc;
1072 char *line;
1073 size_t len;
1075 rc = assuan_read_line(pwm->ctx, &line, &len);
1077 if (!rc) {
1078 if (line[0] == 'O' && line[1] == 'K' &&
1079 (line[2] == 0 || line[2] == ' ')) {
1080 pwm->state = ASYNC_DONE;
1082 else if (line[0] == '#') {
1084 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
1085 if (pwm->status_func) {
1086 pwm->status_func(pwm->status_data,
1087 line[1] == 0 ? line+1 : line+2);
1090 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
1091 (line[3] == 0 || line[3] == ' ')) {
1092 line += 4;
1093 rc = atoi(line);
1094 pwm->state = ASYNC_DONE;
1098 return rc;
1101 gpg_error_t pwmd_get_result(pwm_t *pwm, const char **result)
1103 if (!pwm || !result)
1104 return GPG_ERR_INV_ARG;
1106 if (pwm->state != ASYNC_DONE)
1107 return GPG_ERR_INV_STATE;
1109 if (!pwm->result)
1110 return GPG_ERR_NO_DATA;
1112 *result = pwm->result;
1113 return 0;
1116 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
1117 char **result, const char *cmd);
1118 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc)
1120 fd_set fds;
1121 int n;
1122 struct timeval tv = {0, 0};
1124 *rc = 0;
1126 if (!pwm) {
1127 *rc = GPG_ERR_INV_ARG;
1128 return ASYNC_DONE;
1131 #ifdef WITH_TCP
1132 if (pwm->cmd == ASYNC_CMD_DNS) {
1133 fd_set rfds, wfds;
1135 if (pwm->tcp_conn->rc) {
1136 *rc = pwm->tcp_conn->rc;
1137 close(pwm->tcp_conn->fd);
1138 pwm->state = ASYNC_DONE;
1139 return pwm->state;
1142 FD_ZERO(&rfds);
1143 FD_ZERO(&wfds);
1144 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
1146 /* Shouldn't happen. */
1147 if (!n)
1148 return pwm->state;
1150 #ifdef WITH_LIBPTH
1151 n = pth_select(n, &rfds, &wfds, NULL, &tv);
1152 #else
1153 n = select(n, &rfds, &wfds, NULL, &tv);
1154 #endif
1156 if (n > 0)
1157 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
1159 return pwm->state;
1161 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
1162 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
1163 int ret;
1164 socklen_t len = sizeof(int);
1166 FD_ZERO(&fds);
1167 FD_SET(pwm->tcp_conn->fd, &fds);
1168 #ifdef WITH_LIBPTH
1169 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1170 #else
1171 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
1172 #endif
1174 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
1175 return pwm->state;
1176 else if (n == -1) {
1177 *rc = gpg_error_from_syserror();
1178 close(pwm->tcp_conn->fd);
1179 pwm->state = ASYNC_DONE;
1180 return pwm->state;
1183 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
1184 if (ret || n) {
1185 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
1186 close(pwm->tcp_conn->fd);
1187 pwm->state = ASYNC_DONE;
1188 return pwm->state;
1191 else if (pwm->tcp_conn->rc) {
1192 *rc = pwm->tcp_conn->rc;
1193 close(pwm->tcp_conn->fd);
1194 pwm->state = ASYNC_DONE;
1195 return pwm->state;
1198 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
1199 setup_tcp_session(pwm, rc);
1200 pwm->state = ASYNC_DONE;
1201 return pwm->state;
1203 #endif
1205 if (pwm->fd < 0) {
1206 *rc = GPG_ERR_INV_ARG;
1207 return ASYNC_DONE;
1210 if (pwm->state == ASYNC_DONE)
1211 return pwm->state;
1213 /* When not in a command, this will let libassuan process status messages
1214 * by calling PWMD_OPTION_STATUS_FUNC. */
1215 if (pwm->cmd == ASYNC_CMD_NONE) {
1216 *rc = assuan_command(pwm, pwm->ctx, NULL, "NOP");
1217 return pwm->state;
1220 /* This is for the non-blocking OPEN and SAVE commands. */
1221 FD_ZERO(&fds);
1222 FD_SET(pwm->fd, &fds);
1223 #ifdef WITH_LIBPTH
1224 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
1225 #else
1226 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
1227 #endif
1229 if (n > 0) {
1230 if (FD_ISSET(pwm->fd, &fds))
1231 *rc = parse_assuan_line(pwm);
1234 while (!*rc && assuan_pending_line(pwm->ctx))
1235 *rc = parse_assuan_line(pwm);
1237 /* For pinentry retries. */
1238 if (pwm->is_open_cmd && gpg_err_code(*rc) == EPWMD_BADKEY &&
1239 ++pwm->ntries < pwm->pinentry_tries) {
1240 pwm->state = ASYNC_INIT;
1241 *rc = pwmd_open_async(pwm, pwm->filename);
1244 return pwm->state;
1247 static gpg_error_t assuan_command(pwm_t *pwm, assuan_context_t ctx,
1248 char **result, const char *cmd)
1250 membuf_t data;
1251 gpg_error_t rc;
1253 data.len = 0;
1254 data.buf = NULL;
1256 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, _inquire_cb, pwm,
1257 pwm->status_func, pwm->status_data);
1259 if (rc) {
1260 if (data.buf) {
1261 xfree(data.buf);
1262 data.buf = NULL;
1265 else {
1266 if (data.buf) {
1267 mem_realloc_cb(&data, "", 1);
1269 if (!result) {
1270 xfree(data.buf);
1271 rc = GPG_ERR_INV_ARG;
1273 else
1274 *result = (char *)data.buf;
1278 return gpg_err_code(rc);
1281 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_fn fn,
1282 void *data)
1284 if (!pwm || !cmd || !fn)
1285 return GPG_ERR_INV_ARG;
1287 pwm->inquire_func = fn;
1288 pwm->inquire_data = data;
1289 return assuan_command(pwm, pwm->ctx, NULL, cmd);
1292 gpg_error_t pwmd_terminate_pinentry(pwm_t *pwm)
1294 #ifndef WITH_PINENTRY
1295 return GPG_ERR_NOT_IMPLEMENTED;
1296 #else
1297 pid_t pid = pwm->pid;
1299 pwm->pid = -1;
1301 if (!pwm || pid == -1)
1302 return GPG_ERR_INV_ARG;
1304 if (kill(pid, 0) == 0) {
1305 if (kill(pid, SIGTERM) == -1) {
1306 if (kill(pid, SIGKILL) == -1)
1307 return gpg_error_from_errno(errno);
1310 pwm->pin_error = GPG_ERR_TIMEOUT;
1312 else
1313 return gpg_error_from_errno(errno);
1315 return 0;
1316 #endif
1319 #ifdef WITH_PINENTRY
1320 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
1322 char *buf;
1323 char tmp[ASSUAN_LINELENGTH];
1324 gpg_error_t error;
1326 if (!pwm->title)
1327 pwm->title = xstrdup(N_("LibPWMD"));
1329 if (!pwm->prompt)
1330 pwm->prompt = xstrdup(N_("Passphrase:"));
1332 if (!pwm->desc && !which)
1333 pwm->desc = xstrdup(N_("Enter a passphrase."));
1335 if (which == 1) {
1336 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Invalid passphrase, please try again."));
1337 buf = xstrdup(tmp);
1339 else if (which == 2) {
1340 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Please type the passphrase again for confirmation."));
1341 buf = xstrdup(tmp);
1343 else {
1344 buf = (char *)xmalloc(strlen("SETERROR ") + strlen(pwm->desc) + 1);
1345 sprintf(buf, "SETERROR %s", pwm->desc);
1348 error = pinentry_command(pwm, NULL, buf);
1349 xfree(buf);
1351 if (error)
1352 return error;
1354 buf = (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm->prompt) + 1);
1355 sprintf(buf, "SETPROMPT %s", pwm->prompt);
1356 error = pinentry_command(pwm, NULL, buf);
1357 xfree(buf);
1359 if (error)
1360 return error;
1362 buf = (char *)xmalloc(strlen("SETDESC ") + strlen(pwm->title) + 1);
1363 sprintf(buf, "SETDESC %s", pwm->title);
1364 error = pinentry_command(pwm, NULL, buf);
1365 xfree(buf);
1366 return error;
1369 static void update_pinentry_settings(pwm_t *pwm)
1371 FILE *fp;
1372 char buf[LINE_MAX];
1373 char *p;
1374 char *pwbuf;
1375 struct passwd *pw = _getpwuid(&pwbuf);
1377 if (!pw)
1378 return;
1380 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw->pw_dir);
1381 xfree(pwbuf);
1383 if ((fp = fopen(buf, "r")) == NULL)
1384 return;
1386 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
1387 char name[32], val[256];
1389 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
1390 continue;
1392 if (strcasecmp(name, "TTYNAME") == 0) {
1393 xfree(pwm->pinentry_tty);
1394 pwm->pinentry_tty = xstrdup(val);
1396 else if (strcasecmp(name, "TTYTYPE") == 0) {
1397 xfree(pwm->pinentry_term);
1398 pwm->pinentry_term = xstrdup(val);
1400 else if (strcasecmp(name, "DISPLAY") == 0) {
1401 xfree(pwm->pinentry_display);
1402 pwm->pinentry_display = xstrdup(val);
1404 else if (strcasecmp(name, "PATH") == 0) {
1405 xfree(pwm->pinentry_path);
1406 pwm->pinentry_path = xstrdup(val);
1410 fclose(fp);
1413 static gpg_error_t launch_pinentry(pwm_t *pwm)
1415 int rc;
1416 assuan_context_t ctx;
1417 int child_list[] = {-1};
1418 char *display = getenv("DISPLAY");
1419 const char *argv[10];
1420 const char **p = argv;
1421 int have_display = 0;
1422 char *tty = NULL;
1423 char *ttybuf = NULL;
1425 update_pinentry_settings(pwm);
1427 if (pwm->pinentry_display || display)
1428 have_display = 1;
1429 else {
1430 if (!pwm->pinentry_tty) {
1431 ttybuf = xmalloc(255);
1433 if (!ttybuf)
1434 return gpg_error_from_errno(ENOMEM);
1436 rc = ttyname_r(STDOUT_FILENO, ttybuf, 255);
1438 if (rc) {
1439 xfree(ttybuf);
1440 return gpg_error_from_errno(rc);
1443 tty = ttybuf;
1445 else
1446 tty = pwm->pinentry_tty;
1449 if (!have_display && !tty)
1450 return GPG_ERR_ENOTTY;
1452 *p++ = "pinentry";
1453 *p++ = have_display ? "--display" : "--ttyname";
1454 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
1456 if (pwm->lcctype) {
1457 *p++ = "--lc-ctype";
1458 *p++ = pwm->lcctype;
1461 if (pwm->lcmessages) {
1462 *p++ = "--lc-messages";
1463 *p++ = pwm->lcmessages;
1466 *p = NULL;
1468 if (!have_display) {
1469 *p++ = "--ttytype";
1470 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
1471 *p = NULL;
1474 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
1476 if (ttybuf)
1477 xfree(ttybuf);
1479 if (rc)
1480 return rc;
1482 pwm->pid = assuan_get_pid(ctx);
1483 pwm->pctx = ctx;
1484 return set_pinentry_strings(pwm, 0);
1487 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
1489 gpg_error_t n;
1491 if (!pwm->pctx) {
1492 n = launch_pinentry(pwm);
1494 if (n)
1495 return n;
1498 return assuan_command(pwm, pwm->pctx, result, cmd);
1501 static void pinentry_disconnect(pwm_t *pwm)
1503 if (pwm->pctx)
1504 assuan_disconnect(pwm->pctx);
1506 pwm->pctx = NULL;
1507 pwm->pid = -1;
1511 * Only called from a child process.
1513 static void catchsig(int sig)
1515 switch (sig) {
1516 case SIGALRM:
1517 if (gelapsed++ >= gtimeout) {
1518 global_error = pwmd_terminate_pinentry(gpwm);
1520 if (!global_error)
1521 global_error = GPG_ERR_TIMEOUT;
1523 break;
1526 alarm(1);
1527 break;
1528 default:
1529 break;
1532 #endif
1535 * Borrowed from libassuan.
1537 static char *percent_escape(const char *atext)
1539 const unsigned char *s;
1540 int len = strlen(atext) * 3 + 1;
1541 char *buf = (char *)xmalloc(len), *p = buf;
1543 if (!buf)
1544 return NULL;
1546 for (s=(const unsigned char *)atext; *s; s++) {
1547 if (*s < ' ') {
1548 sprintf (p, "%%%02X", *s);
1549 p += 3;
1551 else
1552 *p++ = *s;
1555 *p = 0;
1556 return buf;
1559 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
1561 if (!cmd)
1562 return GPG_ERR_INV_ARG;
1564 return assuan_command(pwm, pwm->ctx, result, cmd);
1567 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1568 va_list ap)
1570 char *buf;
1571 size_t len;
1572 gpg_error_t error;
1574 if (!pwm || !cmd)
1575 return GPG_ERR_INV_ARG;
1578 * C99 allows the dst pointer to be null which will calculate the length
1579 * of the would-be result and return it.
1581 len = vsnprintf(NULL, 0, cmd, ap)+1;
1582 buf = (char *)xmalloc(len);
1583 len = vsnprintf(buf, len, cmd, ap);
1584 error = send_command(pwm, result, buf);
1585 xfree(buf);
1586 return error;
1590 * Avoid sending the BYE command here. libassuan will close the file
1591 * descriptor and release the assuan context. Use pwmd_close() instead.
1593 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1595 va_list ap;
1596 gpg_error_t error;
1598 if (!pwm || !cmd)
1599 return GPG_ERR_INV_ARG;
1601 *result = NULL;
1602 va_start(ap, cmd);
1603 error = pwmd_command_ap(pwm, result, cmd, ap);
1604 va_end(ap);
1605 return error;
1608 #ifdef WITH_PINENTRY
1609 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
1611 if (gtimeout) {
1612 signal(SIGALRM, catchsig);
1613 alarm(1);
1616 *result = NULL;
1617 return pinentry_command(pwm, result, "GETPIN");
1620 static gpg_error_t getpin(pwm_t *pwm, char **result, int *try_n, int which)
1622 int pin_try = *try_n;
1623 gpg_error_t error;
1625 getpin_again:
1626 *try_n = pin_try;
1628 if (pin_try == -1) {
1629 error = set_pinentry_strings(pwm, which);
1631 if (error) {
1632 pinentry_disconnect(pwm);
1633 return error;
1636 else {
1637 if (pwm->pinentry_tries-1 != pin_try) {
1638 error = set_pinentry_strings(pwm, 1);
1640 if (error) {
1641 pinentry_disconnect(pwm);
1642 return error;
1647 error = do_getpin(pwm, result);
1650 * Since there was input cancel any timeout setting.
1652 alarm(0);
1654 if (error) {
1655 if (error == GPG_ERR_CANCELED)
1656 return GPG_ERR_CANCELED;
1658 if (pin_try != -1 && pin_try--)
1659 goto getpin_again;
1661 if (pwm->pctx)
1662 pinentry_disconnect(pwm);
1664 *try_n = pin_try;
1665 return error;
1668 return 0;
1670 #endif
1672 gpg_error_t pwmd_open_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
1674 gpg_error_t error;
1676 #ifndef WITH_PINENTRY
1677 return GPG_ERR_NOT_IMPLEMENTED;
1678 #endif
1680 if (!pwm || !pw || !pw->filename[0])
1681 return GPG_ERR_INV_ARG;
1683 close(pw->fd);
1685 if (pw->error) {
1686 error = pw->error;
1687 goto fail;
1690 if (pwm->filename)
1691 xfree(pwm->filename);
1693 pwm->filename = xstrdup(pw->filename);
1694 memset(pw, 0, sizeof(pwmd_nb_status_t));
1695 return 0;
1697 fail:
1698 memset(pw, 0, sizeof(pwmd_nb_status_t));
1699 return error;
1702 static gpg_error_t do_open_command(pwm_t *pwm, const char *filename, char *password)
1704 char buf[ASSUAN_LINELENGTH];
1705 gpg_error_t error;
1706 char *result = NULL;
1708 snprintf(buf, sizeof(buf), "OPEN %s %s", filename, password ? password : "");
1709 error = send_command(pwm, &result, buf);
1710 memset(buf, 0, sizeof(buf));
1712 if (error && result)
1713 xfree(result);
1715 return error;
1718 static int do_pwmd_open(pwm_t *pwm, gpg_error_t *error, const char *filename,
1719 int nb, int timeout)
1721 char *result = NULL;
1722 char *password = NULL;
1723 char path[PATH_MAX];
1724 #ifdef WITH_PINENTRY
1725 int pin_try;
1726 #endif
1728 if (!pwm || !filename || !*filename) {
1729 *error = GPG_ERR_INV_ARG;
1730 return nb ? -1 : 1;
1733 #ifdef WITH_PINENTRY
1734 pin_try = pwm->pinentry_tries - 1;
1735 #endif
1738 * Avoid calling pinentry if the password is cached on the server or if
1739 * this is a new file.
1741 *error = pwmd_command(pwm, &result, "GETCONFIG data_directory");
1743 if (*error)
1744 return nb ? -1 : 1;
1746 snprintf(path, sizeof(path), "%s/%s", result, filename);
1747 pwmd_free_result(result);
1749 if (access(path, R_OK) == -1) {
1750 if (errno == ENOENT)
1751 goto gotpassword;
1754 *error = pwmd_command(pwm, &result, "ISCACHED %s", filename);
1756 if (*error == EPWMD_CACHE_NOT_FOUND) {
1757 if (pwm->passfunc) {
1758 password = pwm->passfunc(pwm, pwm->passdata);
1759 goto gotpassword;
1762 #ifdef WITH_PINENTRY
1764 * Get the password from pinentry.
1766 if (pwm->use_pinentry) {
1768 * Nonblocking is wanted. fork() then return a file descriptor
1769 * that the client can use to read() from.
1771 if (nb) {
1772 int p[2];
1773 pid_t pid;
1774 pwmd_nb_status_t pw;
1776 if (pipe(p) == -1) {
1777 *error = gpg_error_from_syserror();
1778 return -1;
1781 #ifdef WITH_LIBPTH
1782 pid = pth_fork();
1783 #else
1784 pid = fork();
1785 #endif
1787 switch (pid) {
1788 case 0:
1789 close(p[0]);
1790 strncpy(pw.filename, filename, sizeof(pw.filename));
1791 pw.filename[sizeof(pw.filename)-1] = 0;
1792 pw.fd = p[0];
1794 if (timeout > 0) {
1795 gpwm = pwm;
1796 gtimeout = timeout;
1797 gelapsed = 0;
1800 getpin_nb_again:
1801 *error = getpin(pwm, &password, &pin_try, 0);
1803 if (*error) {
1804 getpin_nb_fail:
1805 if (pwm->pctx)
1806 pinentry_disconnect(pwm);
1808 if (gtimeout && gelapsed >= gtimeout)
1809 *error = GPG_ERR_TIMEOUT;
1811 pw.error = *error;
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 *error = do_open_command(pwm, filename, password);
1828 if (timeout)
1829 signal(SIGALRM, catchsig);
1831 if (pwm->pctx && *error == 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 *error = gpg_error_from_syserror();
1850 close(p[0]);
1851 close(p[1]);
1852 return -1;
1853 default:
1854 break;
1857 close(p[1]);
1858 *error = 0;
1859 return p[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 (*error)
1874 return nb ? -1 : 1;
1876 gotpassword:
1877 *error = do_open_command(pwm, filename, password);
1880 * Keep the user defined password set with pwmd_setopt(). The password may
1881 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1883 if (!pwm->passfunc && password && password != pwm->password)
1884 xfree(password);
1886 #ifdef WITH_PINENTRY
1887 if (*error == EPWMD_BADKEY) {
1888 if (pin_try-- > 0 && !nb) {
1889 *error = pwmd_command(pwm, &result, "OPTION TITLE=%s",
1890 N_("Invalid passphrase, please try again."));
1892 if (*error)
1893 return 1;
1895 goto gotpassword;
1898 if (nb)
1899 pinentry_disconnect(pwm);
1901 return nb ? -1 : 1;
1903 #endif
1905 if (!*error) {
1906 if (pwm->filename)
1907 xfree(pwm->filename);
1909 pwm->filename = xstrdup(filename);
1913 * The file is cached or the file is a new file.
1915 if (nb)
1916 return *error ? -1 : -2;
1918 return *error ? 1 : 0;
1921 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
1923 gpg_error_t error;
1925 do_pwmd_open(pwm, &error, filename, 0, 0);
1926 return error;
1929 int pwmd_open_nb(pwm_t *pwm, gpg_error_t *error, const char *filename,
1930 int timeout)
1932 #ifndef WITH_PINENTRY
1933 *error = GPG_ERR_NOT_IMPLEMENTED;
1934 return -1;
1935 #else
1936 return do_pwmd_open(pwm, error, filename, 1, timeout);
1937 #endif
1940 #ifdef WITH_PINENTRY
1941 static gpg_error_t do_save_getpin(pwm_t *pwm, char **password)
1943 int confirm = 0;
1944 gpg_error_t error;
1945 char *result = NULL;
1946 int pin_try = -1;
1948 again:
1949 error = getpin(pwm, &result, &pin_try, confirm ? 2 : 0);
1951 if (error) {
1952 if (pwm->pctx)
1953 pinentry_disconnect(pwm);
1955 if (*password)
1956 xfree(*password);
1958 return error;
1961 if (!confirm++) {
1962 *password = result;
1963 goto again;
1966 if (strcmp(*password, result)) {
1967 xfree(*password);
1968 xfree(result);
1969 pinentry_disconnect(pwm);
1970 error = EPWMD_BADKEY;
1971 return error;
1974 xfree(result);
1975 pinentry_disconnect(pwm);
1976 return 0;
1978 #endif
1980 static gpg_error_t do_save_command(pwm_t *pwm, char *password)
1982 char buf[ASSUAN_LINELENGTH];
1983 gpg_error_t error;
1984 char *result = NULL;
1986 snprintf(buf, sizeof(buf), "SAVE %s", password ? password : "");
1987 error = send_command(pwm, &result, buf);
1988 memset(&buf, 0, sizeof(buf));
1990 if (error && result)
1991 xfree(result);
1993 return error;
1996 gpg_error_t pwmd_save_nb_finalize(pwm_t *pwm, pwmd_nb_status_t *pw)
1998 gpg_error_t rc;
2000 #ifndef WITH_PINENTRY
2001 return GPG_ERR_NOT_IMPLEMENTED;
2002 #endif
2004 if (!pwm || !pw || !pw->filename[0])
2005 return GPG_ERR_INV_ARG;
2007 close(pw->fd);
2008 rc = pw->error;
2009 memset(pw, 0, sizeof(pwmd_nb_status_t));
2010 return rc;
2013 static int do_pwmd_save(pwm_t *pwm, gpg_error_t *error, int nb)
2015 char *result = NULL;
2016 char *password = NULL;
2018 if (!pwm) {
2019 *error = GPG_ERR_INV_ARG;
2020 return nb ? -1 : 1;
2023 if (pwm->use_pinentry || pwm->passfunc) {
2024 *error = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
2026 if (*error == EPWMD_CACHE_NOT_FOUND) {
2027 if (pwm->passfunc)
2028 password = (*pwm->passfunc)(pwm, pwm->passdata);
2029 #ifdef WITH_PINENTRY
2030 else if (pwm->use_pinentry) {
2031 if (nb) {
2032 int p[2];
2033 pid_t pid;
2034 pwmd_nb_status_t pw;
2036 if (pipe(p) == -1) {
2037 *error = gpg_error_from_syserror();
2038 return -1;
2041 #ifdef WITH_LIBPTH
2042 pid = pth_fork();
2043 #else
2044 pid = fork();
2045 #endif
2047 switch (pid) {
2048 case 0:
2049 close(p[0]);
2050 strncpy(pw.filename, pwm->filename, sizeof(pw.filename));
2051 pw.filename[sizeof(pw.filename)-1] = 0;
2052 pw.fd = p[0];
2054 do {
2055 password = NULL;
2056 *error = do_save_getpin(pwm, &password);
2057 } while (*error == EPWMD_BADKEY);
2059 if (*error) {
2060 if (pwm->pctx)
2061 pinentry_disconnect(pwm);
2063 pw.error = *error;
2064 #ifdef WITH_LIBPTH
2065 pth_write(p[1], &pw, sizeof(pw));
2066 #else
2067 write(p[1], &pw, sizeof(pw));
2068 #endif
2069 close(p[1]);
2070 _exit(1);
2073 *error = do_save_command(pwm, password);
2074 pinentry_disconnect(pwm);
2075 pw.error = *error;
2076 #ifdef WITH_LIBPTH
2077 pth_write(p[1], &pw, sizeof(pw));
2078 #else
2079 write(p[1], &pw, sizeof(pw));
2080 #endif
2081 close(p[1]);
2082 _exit(0);
2083 break;
2084 case -1:
2085 *error = gpg_error_from_syserror();
2086 close(p[0]);
2087 close(p[1]);
2088 return -1;
2089 default:
2090 break;
2093 close(p[1]);
2094 *error = 0;
2095 return p[0];
2098 *error = do_save_getpin(pwm, &password);
2100 if (*error)
2101 return 1;
2103 #endif
2105 else {
2106 if (*error)
2107 return nb ? -1 : 1;
2110 else
2111 password = pwm->password;
2113 *error = do_save_command(pwm, password);
2115 if (!pwm->passfunc && password && password != pwm->password)
2116 xfree(password);
2118 if (nb)
2119 return *error ? -1 : -2;
2121 return *error ? 1 : 0;
2124 int pwmd_save_nb(pwm_t *pwm, gpg_error_t *error)
2126 #ifndef WITH_PINENTRY
2127 *error = GPG_ERR_NOT_IMPLEMENTED;
2128 return -1;
2129 #else
2130 return do_pwmd_save(pwm, error, 1);
2131 #endif
2134 gpg_error_t pwmd_save(pwm_t *pwm)
2136 gpg_error_t error;
2138 do_pwmd_save(pwm, &error, 0);
2139 return error;
2142 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
2144 va_list ap;
2145 int n = va_arg(ap, int);
2146 char *result;
2147 char *arg1;
2148 gpg_error_t error = 0;
2150 if (!pwm)
2151 return GPG_ERR_INV_ARG;
2153 va_start(ap, opt);
2155 switch (opt) {
2156 case PWMD_OPTION_STATUS_FUNC:
2157 pwm->status_func = va_arg(ap, pwmd_status_fn);
2158 break;
2159 case PWMD_OPTION_STATUS_DATA:
2160 pwm->status_data = va_arg(ap, void *);
2161 break;
2162 case PWMD_OPTION_PASSWORD_FUNC:
2163 pwm->passfunc = va_arg(ap, pwmd_password_fn);
2164 break;
2165 case PWMD_OPTION_PASSWORD_DATA:
2166 pwm->passdata = va_arg(ap, void *);
2167 break;
2168 case PWMD_OPTION_PASSWORD:
2169 arg1 = va_arg(ap, char *);
2171 if (pwm->password)
2172 xfree(pwm->password);
2174 pwm->password = xstrdup(arg1);
2175 break;
2176 case PWMD_OPTION_PINENTRY:
2177 n = va_arg(ap, int);
2179 if (n != 0 && n != 1) {
2180 va_end(ap);
2181 error = GPG_ERR_INV_VALUE;
2183 else {
2184 pwm->use_pinentry = n;
2185 error = pwmd_command(pwm, &result, "OPTION PINENTRY=%i",
2186 !pwm->use_pinentry);
2188 break;
2189 #ifdef WITH_PINENTRY
2190 case PWMD_OPTION_PINENTRY_TRIES:
2191 n = va_arg(ap, int);
2193 if (n <= 0) {
2194 va_end(ap);
2195 error = GPG_ERR_INV_VALUE;
2197 else
2198 pwm->pinentry_tries = n;
2199 break;
2200 #endif
2201 case PWMD_OPTION_PINENTRY_TIMEOUT:
2202 n = va_arg(ap, int);
2204 if (n < 0) {
2205 va_end(ap);
2206 error = GPG_ERR_INV_VALUE;
2208 else
2209 pwm->pinentry_timeout = n;
2211 if (!pwm->use_pinentry)
2212 error = pwmd_command(pwm, &result, "OPTION TIMEOUT=%i",
2213 pwm->pinentry_timeout);
2214 break;
2215 case PWMD_OPTION_PINENTRY_PATH:
2216 if (pwm->pinentry_path)
2217 xfree(pwm->pinentry_path);
2219 pwm->pinentry_path = xstrdup(va_arg(ap, char *));
2221 if (!pwm->use_pinentry)
2222 error = pwmd_command(pwm, &result, "OPTION PATH=%s",
2223 pwm->pinentry_path);
2224 break;
2225 case PWMD_OPTION_PINENTRY_TTY:
2226 if (pwm->pinentry_tty)
2227 xfree(pwm->pinentry_tty);
2229 pwm->pinentry_tty = xstrdup(va_arg(ap, char *));
2231 if (!pwm->use_pinentry)
2232 error = pwmd_command(pwm, &result, "OPTION TTY=%s",
2233 pwm->pinentry_tty);
2234 break;
2235 case PWMD_OPTION_PINENTRY_DISPLAY:
2236 if (pwm->pinentry_display)
2237 xfree(pwm->pinentry_display);
2239 pwm->pinentry_display = xstrdup(va_arg(ap, char *));
2241 if (!pwm->use_pinentry)
2242 error = pwmd_command(pwm, &result, "OPTION DISPLAY=%s",
2243 pwm->pinentry_display);
2244 break;
2245 case PWMD_OPTION_PINENTRY_TERM:
2246 if (pwm->pinentry_term)
2247 xfree(pwm->pinentry_term);
2249 pwm->pinentry_term = xstrdup(va_arg(ap, char *));
2251 if (!pwm->use_pinentry)
2252 error = pwmd_command(pwm, &result, "OPTION TTYTYPE=%s",
2253 pwm->pinentry_term);
2254 break;
2255 case PWMD_OPTION_PINENTRY_TITLE:
2256 if (pwm->title)
2257 xfree(pwm->title);
2259 pwm->title = percent_escape(va_arg(ap, char *));
2261 if (!pwm->use_pinentry)
2262 error = pwmd_command(pwm, &result, "OPTION TITLE=%s",
2263 pwm->title);
2264 break;
2265 case PWMD_OPTION_PINENTRY_PROMPT:
2266 if (pwm->prompt)
2267 xfree(pwm->prompt);
2269 pwm->prompt = percent_escape(va_arg(ap, char *));
2271 if (!pwm->use_pinentry)
2272 error = pwmd_command(pwm, &result, "OPTION PROMPT=%s",
2273 pwm->prompt);
2274 break;
2275 case PWMD_OPTION_PINENTRY_DESC:
2276 if (pwm->desc)
2277 xfree(pwm->desc);
2279 pwm->desc = percent_escape(va_arg(ap, char *));
2281 if (!pwm->use_pinentry)
2282 error = pwmd_command(pwm, &result, "OPTION DESC=%s",
2283 pwm->desc);
2284 break;
2285 case PWMD_OPTION_PINENTRY_LC_CTYPE:
2286 if (pwm->lcctype)
2287 xfree(pwm->lcctype);
2289 pwm->lcctype = xstrdup(va_arg(ap, char *));
2291 if (!pwm->use_pinentry)
2292 error = pwmd_command(pwm, &result, "OPTION LC_CTYPE=%s",
2293 pwm->lcctype);
2294 break;
2295 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
2296 if (pwm->lcmessages)
2297 xfree(pwm->lcmessages);
2299 pwm->lcmessages = xstrdup(va_arg(ap, char *));
2301 if (!pwm->use_pinentry)
2302 error = pwmd_command(pwm, &result, "OPTION LC_MESSAGES=%s",
2303 pwm->lcmessages);
2304 break;
2305 default:
2306 error = GPG_ERR_NOT_IMPLEMENTED;
2307 break;
2310 va_end(ap);
2311 return error;
2315 * Prevent requiring assuan.h when setting ctx. The ctx is really an
2316 * assuan_context_t *.
2318 gpg_error_t pwmd_assuan_ctx(pwm_t *pwm, void *ctx, int *fd)
2320 if (!pwm)
2321 return GPG_ERR_INV_ARG;
2323 ctx = pwm->ctx;
2324 *fd = pwm->fd;
2325 return 0;