Always use the non-blocking portion of libssh2. This changes libpwmd
[libpwmd.git] / src / libpwmd.c
blob243dd0c1cb2ab44176f79baaebce958eb6aed064
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 <libpwmd.h>
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
44 #ifdef HAVE_ASSUAN_H
45 #include <assuan.h>
46 #endif
48 #include "mem.h"
49 #include "misc.h"
50 #include "types.h"
52 #ifdef WITH_PINENTRY
53 #include "pinentry.h"
54 #endif
56 #ifdef WITH_TCP
57 #include "ssh.h"
58 #endif
60 static gpg_error_t send_pinentry_options(pwm_t *pwm);
61 static gpg_error_t _pwmd_process(pwm_t *pwm);
62 static gpg_error_t set_pinentry_retry(pwm_t *pwm);
63 static gpg_error_t get_custom_passphrase(pwm_t *pwm, char **result);
65 static const char *_pwmd_strerror(gpg_error_t e)
67 gpg_err_code_t code = gpg_err_code(e);
69 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
70 switch (code) {
71 case GPG_ERR_USER_1:
72 return N_("No file is open");
73 case GPG_ERR_USER_2:
74 return N_("General LibXML error");
75 case GPG_ERR_USER_3:
76 return N_("File modified");
77 default:
78 return N_("Unknown error");
82 return NULL;
85 const char *pwmd_strerror(gpg_error_t code)
87 const char *p = _pwmd_strerror(code);
89 return p ? p : gpg_strerror(code);
92 int pwmd_strerror_r(gpg_error_t code, char *buf, size_t size)
94 const char *p = _pwmd_strerror(code);
96 if (p) {
97 snprintf(buf, size, "%s", p);
99 if (strlen(p) > size)
100 return ERANGE;
102 return 0;
105 return gpg_strerror_r(code, buf, size);
108 gpg_error_t pwmd_init()
110 static int initialized;
112 if (initialized)
113 return 0;
115 #ifndef MEM_DEBUG
116 _xmem_init();
117 #endif
118 #ifdef ENABLE_NLS
119 bindtextdomain("libpwmd", LOCALEDIR);
120 #endif
121 gpg_err_init();
122 assuan_set_malloc_hooks(pwmd_malloc, pwmd_realloc, pwmd_free);
123 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
124 initialized = 1;
125 return 0;
128 gpg_error_t _connect_finalize(pwm_t *pwm)
130 int active[2];
131 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
132 gpg_error_t rc;
134 if (n <= 0)
135 return GPG_ERR_EBADFD;
137 pwm->fd = active[0];
138 #ifdef WITH_PINENTRY
139 pwm->pid = -1;
140 #endif
141 assuan_set_pointer(pwm->ctx, pwm);
143 #ifdef WITH_TCP
144 // Until X11 forwarding is supported, disable the remote pwmd pinentry.
145 if (pwm->tcp_conn) {
146 pwm->tcp_conn->state = SSH_NONE;
147 rc = pwmd_command(pwm, NULL, "SET ENABLE_PINENTRY=0");
149 if (rc)
150 return rc;
152 #endif
154 if (pwm->name) {
155 rc = pwmd_command(pwm, NULL, "SET NAME=%s", pwm->name);
157 if (rc)
158 return rc;
161 return 0;
164 static gpg_error_t _pwmd_connect_url(pwm_t *pwm, const char *url, int async)
166 char *p = (char *)url;
167 gpg_error_t rc;
169 if (!pwm)
170 return GPG_ERR_INV_ARG;
172 if (!p || !strncmp(p, "file://", 7) || !strncmp(p, "local://", 8)) {
173 if (p) {
174 if (!strncmp(p, "file://", 7))
175 p += 7;
176 else
177 p += 8;
180 goto connect_uds;
182 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
183 !strncmp(p, "ssh4://", 7)) {
184 #ifndef WITH_TCP
185 return GPG_ERR_NOT_IMPLEMENTED;
186 #else
187 char *host = NULL;
188 int port = -1;
189 char *identity = NULL;
190 char *known_hosts = NULL;
191 char *username = NULL;
193 if (!strncmp(p, "ssh6://", 7)) {
194 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV6);
195 p += 7;
197 else if (!strncmp(p, "ssh4://", 7)) {
198 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV4);
199 p += 7;
201 else {
202 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IP_ANY);
203 p += 6;
206 if (rc)
207 return rc;
209 rc = _parse_ssh_url(p, &host, &port, &username, &identity,
210 &known_hosts);
212 if (rc)
213 goto fail;
215 if (async)
216 rc = pwmd_ssh_connect_async(pwm, host, port, identity, username,
217 known_hosts);
218 else
219 rc = pwmd_ssh_connect(pwm, host, port, identity, username,
220 known_hosts);
222 fail:
223 if (host)
224 pwmd_free(host);
226 if (username)
227 pwmd_free(username);
229 if (identity)
230 pwmd_free(identity);
232 if (known_hosts)
233 pwmd_free(known_hosts);
235 return rc;
236 #endif
239 connect_uds:
240 rc = pwmd_connect(pwm, p);
241 pwm->state = ASYNC_DONE;
242 return rc;
245 gpg_error_t pwmd_connect_url(pwm_t *pwm, const char *url)
247 return _pwmd_connect_url(pwm, url, 0);
250 gpg_error_t pwmd_connect_url_async(pwm_t *pwm, const char *url)
252 return _pwmd_connect_url(pwm, url, 1);
255 gpg_error_t pwmd_connect(pwm_t *pwm, const char *path)
257 char *socketpath = NULL;
258 assuan_context_t ctx;
259 struct passwd pw;
260 char *pwbuf;
261 gpg_error_t rc;
263 if (!pwm)
264 return GPG_ERR_INV_ARG;
266 pwbuf = _getpwuid(&pw);
268 if (!pwbuf)
269 return gpg_error_from_errno(errno);
271 if (!path || !*path)
272 socketpath = pwmd_strdup_printf("%s/.pwmd/socket", pw.pw_dir);
273 else
274 socketpath = _expand_homedir((char *)path, &pw);
276 pwmd_free(pwbuf);
278 if (!socketpath)
279 return gpg_error_from_errno(ENOMEM);
281 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
282 pwmd_free(socketpath);
284 if (rc)
285 return gpg_err_code(rc);
287 pwm->ctx = ctx;
288 return _connect_finalize(pwm);
291 static void disconnect(pwm_t *pwm)
293 if (!pwm || !pwm->ctx)
294 return;
296 assuan_disconnect(pwm->ctx);
297 pwm->ctx = NULL;
298 pwm->fd = -1;
301 void pwmd_close(pwm_t *pwm)
303 if (!pwm)
304 return;
306 disconnect(pwm);
308 if (pwm->password)
309 pwmd_free(pwm->password);
311 if (pwm->title)
312 pwmd_free(pwm->title);
314 if (pwm->desc)
315 pwmd_free(pwm->desc);
317 if (pwm->prompt)
318 pwmd_free(pwm->prompt);
320 if (pwm->pinentry_tty)
321 pwmd_free(pwm->pinentry_tty);
323 if (pwm->pinentry_display)
324 pwmd_free(pwm->pinentry_display);
326 if (pwm->pinentry_term)
327 pwmd_free(pwm->pinentry_term);
329 if (pwm->lcctype)
330 pwmd_free(pwm->lcctype);
332 if (pwm->lcmessages)
333 pwmd_free(pwm->lcmessages);
335 if (pwm->filename)
336 pwmd_free(pwm->filename);
338 if (pwm->name)
339 pwmd_free(pwm->name);
341 #ifdef WITH_TCP
342 if (pwm->tcp_conn)
343 _free_ssh_conn(pwm->tcp_conn);
344 #endif
346 #ifdef WITH_PINENTRY
347 if (pwm->pctx)
348 _pinentry_disconnect(pwm);
349 #endif
351 pwmd_free(pwm);
354 static gpg_error_t do_async_command(pwm_t *pwm, char **result)
356 pwmd_async_t s;
357 gpg_error_t rc;
359 do {
360 s = pwmd_process(pwm, &rc, result);
362 if (s != ASYNC_DONE) {
363 #ifdef WITH_LIBPTH
364 pth_usleep(50000);
365 #else
366 usleep(50000);
367 #endif
369 } while (s != ASYNC_DONE);
371 return rc;
374 gpg_error_t pwmd_ssh_connect_async(pwm_t *pwm, const char *host, int port,
375 const char *identity, const char *user, const char *known_hosts)
377 #ifndef WITH_TCP
378 return GPG_ERR_NOT_IMPLEMENTED;
379 #else
380 return _do_pwmd_ssh_connect_async(pwm, host, port, identity, user,
381 known_hosts, ASYNC_CMD_CONNECT);
382 #endif
385 gpg_error_t pwmd_ssh_connect(pwm_t *pwm, const char *host, int port,
386 const char *identity, const char *user, const char *known_hosts)
388 #ifndef WITH_TCP
389 return GPG_ERR_NOT_IMPLEMENTED;
390 #else
391 gpg_error_t rc;
393 rc = _do_pwmd_ssh_connect_async(pwm, host, port, identity, user,
394 known_hosts, ASYNC_CMD_CONNECT);
395 return rc || do_async_command(pwm, NULL);
396 #endif
399 gpg_error_t pwmd_get_hostkey(pwm_t *pwm, const char *host, int port,
400 char **result)
402 #ifndef WITH_TCP
403 return GPG_ERR_NOT_IMPLEMENTED;
404 #else
405 gpg_error_t rc;
407 rc = _do_pwmd_ssh_connect_async(pwm, host, port, NULL, NULL, NULL,
408 ASYNC_CMD_HOSTKEY);
410 return rc || do_async_command(pwm, result);
411 #endif
414 gpg_error_t pwmd_get_hostkey_async(pwm_t *pwm, const char *host, int port)
416 #ifndef WITH_TCP
417 return GPG_ERR_NOT_IMPLEMENTED;
418 #else
419 return _do_pwmd_ssh_connect_async(pwm, host, port, NULL, NULL, NULL,
420 ASYNC_CMD_HOSTKEY);
421 #endif
424 static int inquire_realloc_cb(void *data, const void *buffer, size_t len)
426 membuf_t *mem = (membuf_t *)data;
427 void *p;
429 if (!buffer)
430 return 0;
432 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
433 return gpg_error_from_errno(ENOMEM);
435 mem->buf = p;
436 memcpy((char *)mem->buf + mem->len, buffer, len);
437 mem->len += len;
438 return 0;
441 static int inquire_cb(void *data, const char *keyword)
443 pwm_t *pwm = (pwm_t *)data;
444 gpg_error_t rc = 0;
446 /* Shouldn't get this far without a callback. */
447 if (!pwm->inquire_func)
448 return GPG_ERR_INV_ARG;
450 for (;;) {
451 char *result = NULL;
452 size_t len;
453 gpg_error_t arc;
455 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
456 rc = gpg_err_code(rc);
458 if (rc == GPG_ERR_EOF || !rc) {
459 if (len <= 0 || !result) {
460 rc = 0;
461 break;
464 arc = assuan_send_data(pwm->ctx, result, len);
465 arc = gpg_err_code(arc);
467 if (rc == GPG_ERR_EOF) {
468 rc = arc;
469 break;
472 rc = arc;
474 else if (rc)
475 break;
477 if (!rc) {
478 pwm->inquire_sent += len;
480 if (pwm->status_func) {
481 char buf[ASSUAN_LINELENGTH];
483 snprintf(buf, sizeof(buf), "XFER %u %u", pwm->inquire_sent,
484 pwm->inquire_total);
485 rc = pwm->status_func(pwm->status_data, buf);
487 if (rc)
488 continue;
491 rc = _pwmd_process(pwm);
493 if (rc == GPG_ERR_EAGAIN) {
494 rc = 0;
495 #ifdef WITH_LIBPTH
496 pth_usleep(50000);
497 #else
498 usleep(50000);
499 #endif
504 return gpg_err_code(rc);
507 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, ...)
509 char *buf;
510 gpg_error_t rc;
511 va_list ap;
512 int len;
514 if (pwm->state == ASYNC_DONE)
515 pwm->state = ASYNC_INIT;
517 if (pwm->state != ASYNC_INIT)
518 return GPG_ERR_INV_STATE;
520 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
522 if (!buf)
523 return gpg_error_from_errno(ENOMEM);
525 va_start(ap, cmd);
526 len = vsnprintf(buf, ASSUAN_LINELENGTH+1, cmd, ap);
527 va_end(ap);
529 if (len >= ASSUAN_LINELENGTH+1) {
530 pwmd_free(buf);
531 return GPG_ERR_LINE_TOO_LONG;
534 rc = assuan_write_line(pwm->ctx, buf);
535 pwmd_free(buf);
537 if (!rc)
538 pwm->state = ASYNC_PROCESS;
540 return gpg_err_code(rc);
543 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
545 char *p = NULL;
546 const char *f = NULL;
547 gpg_error_t rc;
549 if (!pwm || !filename)
550 return GPG_ERR_INV_ARG;
552 if (!pwm->ctx)
553 return GPG_ERR_INV_STATE;
555 if (pwm->cmd != ASYNC_CMD_NONE)
556 return GPG_ERR_ASS_NESTED_COMMANDS;
558 if (pwm->lastcmd == ASYNC_CMD_NONE) {
559 pwm->pin_try = 0;
561 if (pwm->filename)
562 pwmd_free(pwm->filename);
564 pwm->filename = pwmd_strdup(filename);
566 if (!pwm->filename)
567 return gpg_error_from_errno(ENOMEM);
569 gpg_error_t rc = send_pinentry_options(pwm);
571 if (rc)
572 return rc;
574 rc = get_custom_passphrase(pwm, &p);
576 if (rc && rc != GPG_ERR_NO_DATA)
577 return rc;
579 f = filename;
581 #ifdef WITH_PINENTRY
582 else if (pwm->lastcmd == ASYNC_CMD_OPEN2) {
583 p = pwm->_password;
584 f = pwm->filename;
586 #endif
587 else if (pwm->lastcmd == ASYNC_CMD_OPEN) {
588 rc = set_pinentry_retry(pwm);
590 if (rc)
591 return rc;
593 p = pwm->password;
594 f = filename;
596 else
597 return GPG_ERR_INV_STATE;
599 pwm->cmd = ASYNC_CMD_OPEN;
600 return do_nb_command(pwm, "OPEN %s %s", f, p ? p : "");
603 gpg_error_t pwmd_save_async(pwm_t *pwm)
605 char *p = NULL;
607 if (!pwm)
608 return GPG_ERR_INV_ARG;
610 if (!pwm->ctx)
611 return GPG_ERR_INV_STATE;
613 if (pwm->cmd != ASYNC_CMD_NONE)
614 return GPG_ERR_ASS_NESTED_COMMANDS;
616 if (pwm->lastcmd != ASYNC_CMD_SAVE2) {
617 gpg_error_t rc = send_pinentry_options(pwm);
619 if (rc)
620 return rc;
622 rc = get_custom_passphrase(pwm, &p);
624 if (rc && rc != GPG_ERR_NO_DATA)
625 return rc;
627 #ifdef WITH_PINENTRY
628 else
629 p = pwm->_password;
630 #endif
632 pwm->cmd = ASYNC_CMD_SAVE;
633 return do_nb_command(pwm, "SAVE %s", p ? p : "");
636 static gpg_error_t parse_assuan_line(pwm_t *pwm)
638 gpg_error_t rc;
639 char *line;
640 size_t len;
642 rc = assuan_read_line(pwm->ctx, &line, &len);
644 if (!rc) {
645 if (line[0] == 'O' && line[1] == 'K' &&
646 (line[2] == 0 || line[2] == ' ')) {
647 pwm->state = ASYNC_DONE;
649 else if (line[0] == '#') {
651 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
652 if (pwm->status_func) {
653 rc = pwm->status_func(pwm->status_data,
654 line[1] == 0 ? line+1 : line+2);
657 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
658 (line[3] == 0 || line[3] == ' ')) {
659 line += 4;
660 rc = atoi(line);
661 pwm->state = ASYNC_DONE;
665 return gpg_err_code(rc);
668 gpg_error_t pwmd_pending_line(pwm_t *pwm)
670 if (!pwm)
671 return GPG_ERR_INV_ARG;
673 if (!pwm->ctx)
674 return GPG_ERR_INV_STATE;
676 return assuan_pending_line(pwm->ctx) ? 0 : GPG_ERR_NO_DATA;
679 static pwmd_async_t reset_async(pwm_t *pwm, int done)
681 pwm->state = ASYNC_INIT;
682 pwm->cmd = pwm->lastcmd = ASYNC_CMD_NONE;
684 #ifdef WITH_PINENTRY
685 if (pwm->nb_fd != -1) {
686 close(pwm->nb_fd);
687 pwm->nb_fd = -1;
690 if (pwm->_password) {
691 pwmd_free(pwm->_password);
692 pwm->_password = NULL;
694 #endif
695 #ifdef WITH_TCP
696 if (pwm->tcp_conn)
697 pwm->tcp_conn->rc = 0;
699 if (done && pwm->tcp_conn) {
700 _free_ssh_conn(pwm->tcp_conn);
701 pwm->tcp_conn = NULL;
703 #endif
705 return ASYNC_DONE;
709 * Used for processing status messages when not in an async command and for
710 * waiting for the result from pwmd_open_async() and pwmd_save_async().
712 static gpg_error_t _pwmd_process(pwm_t *pwm)
714 gpg_error_t rc = 0;
715 fd_set fds;
716 struct timeval tv = {0, 0};
717 int n;
719 FD_ZERO(&fds);
720 FD_SET(pwm->fd, &fds);
721 #ifdef WITH_LIBPTH
722 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
723 #else
724 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
725 #endif
727 if (n == -1)
728 return gpg_error_from_syserror();
730 if (n > 0) {
731 if (FD_ISSET(pwm->fd, &fds))
732 rc = parse_assuan_line(pwm);
735 while (!rc && assuan_pending_line(pwm->ctx))
736 rc = parse_assuan_line(pwm);
738 return gpg_err_code(rc);
741 static void reset_handle(pwm_t *h)
743 h->fd = -1;
744 #ifdef WITH_PINENTRY
745 if (h->pctx)
746 _pinentry_disconnect(h);
748 h->nb_fd = -1;
749 #endif
750 h->pin_try = 0;
751 reset_async(h, 0);
754 gpg_error_t pwmd_disconnect(pwm_t *pwm)
756 if (!pwm)
757 return GPG_ERR_INV_ARG;
759 #ifdef WITH_TCP
760 if (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1)
761 #else
762 if (pwm->fd == -1)
763 #endif
764 return GPG_ERR_INV_STATE;
766 if (pwm->fd != 1)
767 disconnect(pwm);
768 #ifdef WITH_TCP
769 else
770 _ssh_disconnect(pwm);
771 #endif
773 reset_handle(pwm);
774 return 0;
777 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
779 #if defined(WITH_PINENTRY) || defined(WITH_TCP)
780 fd_set fds;
781 int n;
782 struct timeval tv = {0, 0};
783 #endif
785 if (result)
786 *result = NULL;
788 if (!rc)
789 return GPG_ERR_INV_ARG;
791 *rc = 0;
793 if (!pwm) {
794 *rc = GPG_ERR_INV_ARG;
795 return ASYNC_DONE;
797 else if (!pwm->ctx) {
798 switch (pwm->cmd) {
799 default:
800 *rc = GPG_ERR_INV_STATE;
801 return ASYNC_DONE;
802 #ifdef WITH_TCP
803 case ASYNC_CMD_DNS:
804 case ASYNC_CMD_CONNECT:
805 case ASYNC_CMD_HOSTKEY:
806 break;
807 #endif
811 /* When not in a command, this will let libassuan process status messages
812 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
813 * descriptor returned by pwmd_get_fd() to determine when this should be
814 * called or call pwmd_pending_line() to determine whether a buffered line
815 * needs to be processed. */
816 if (pwm->cmd == ASYNC_CMD_NONE) {
817 *rc = _pwmd_process(pwm);
818 return ASYNC_DONE;
821 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
822 if (pwm->state == ASYNC_DONE) {
823 *rc = _pwmd_process(pwm);
824 return reset_async(pwm, 0);
827 if (pwm->state != ASYNC_PROCESS) {
828 *rc = GPG_ERR_INV_STATE;
829 return ASYNC_DONE;
832 #ifdef WITH_TCP
833 if (pwm->cmd == ASYNC_CMD_DNS) {
834 fd_set rfds, wfds;
836 if (pwm->tcp_conn->rc) {
837 *rc = pwm->tcp_conn->rc;
838 return reset_async(pwm, 1);
841 FD_ZERO(&rfds);
842 FD_ZERO(&wfds);
843 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
845 /* Shouldn't happen. */
846 if (!n)
847 return pwm->state;
849 #ifdef WITH_LIBPTH
850 n = pth_select(n, &rfds, &wfds, NULL, &tv);
851 #else
852 n = select(n, &rfds, &wfds, NULL, &tv);
853 #endif
855 if (n < 0) {
856 *rc = gpg_error_from_syserror();
857 return reset_async(pwm, 1);
860 if (n > 0)
861 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
863 return pwm->state;
865 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
866 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
867 int ret;
868 socklen_t len = sizeof(int);
870 FD_ZERO(&fds);
871 FD_SET(pwm->tcp_conn->fd, &fds);
872 #ifdef WITH_LIBPTH
873 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
874 #else
875 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
876 #endif
878 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
879 return pwm->state;
880 else if (n == -1) {
881 *rc = gpg_error_from_syserror();
882 return reset_async(pwm, 1);
885 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
887 if (ret || n) {
888 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
889 return reset_async(pwm, 1);
892 pwm->tcp_conn->state = SSH_NONE;
893 pwm->tcp_conn->rc = 0;
894 *rc = _setup_ssh_session(pwm);
896 if (*rc && *rc != GPG_ERR_EAGAIN)
897 return reset_async(pwm, 1);
899 else if (pwm->tcp_conn->rc) {
900 *rc = pwm->tcp_conn->rc;
901 return reset_async(pwm, 1);
904 switch (pwm->tcp_conn->state) {
905 case SSH_INIT:
906 *rc = _setup_ssh_init(pwm);
907 break;
908 case SSH_AUTHLIST:
909 *rc = _setup_ssh_authlist(pwm);
910 break;
911 case SSH_AUTH:
912 *rc = _setup_ssh_auth(pwm);
913 break;
914 case SSH_CHANNEL:
915 *rc = _setup_ssh_channel(pwm);
916 break;
917 case SSH_SHELL:
918 *rc = _setup_ssh_shell(pwm);
919 break;
920 default:
921 break;
924 if (*rc == GPG_ERR_EAGAIN) {
925 *rc = 0;
926 return ASYNC_PROCESS;
929 if (!*rc) {
930 switch (pwm->tcp_conn->cmd) {
931 case ASYNC_CMD_HOSTKEY:
932 *result = pwmd_strdup(pwm->tcp_conn->hostkey);
934 if (!*result)
935 *rc = GPG_ERR_ENOMEM;
936 break;
937 default:
938 break;
942 return reset_async(pwm, *rc ? 1 : 0);
944 #endif
946 #ifdef WITH_PINENTRY
947 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
948 int status;
950 if (pwm->nb_fd == -1) {
951 *rc = GPG_ERR_INV_STATE;
952 return reset_async(pwm, 0);
955 FD_ZERO(&fds);
956 FD_SET(pwm->nb_fd, &fds);
957 FD_SET(pwm->fd, &fds);
958 #ifdef WITH_LIBPTH
959 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
960 #else
961 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
962 #endif
963 if (n < 0) {
964 *rc = gpg_error_from_syserror();
965 return reset_async(pwm, 0);
968 if (n > 0 && FD_ISSET(pwm->nb_fd, &fds)) {
969 pwmd_nb_status_t nb;
970 #ifdef WITH_LIBPTH
971 size_t len = pth_read(pwm->nb_fd, &nb, sizeof(nb));
972 #else
973 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
974 #endif
975 waitpid(pwm->nb_pid, &status, WNOHANG);
977 if (len != sizeof(nb)) {
978 memset(&nb, 0, sizeof(pwmd_nb_status_t));
979 *rc = gpg_error_from_syserror();
980 return reset_async(pwm, 0);
983 *rc = nb.error;
985 if (*rc)
986 return reset_async(pwm, 0);
988 /* Since the non-blocking pinentry returned a success, do a
989 * non-blocking OPEN or SAVE. */
990 pwmd_async_cmd_t c = pwm->cmd;
991 reset_async(pwm, 0);
992 pwm->_password = pwmd_strdup(nb.password);
993 memset(&nb, 0, sizeof(pwmd_nb_status_t));
994 pwm->lastcmd = c;
996 if (!pwm->_password) {
997 *rc = gpg_error_from_errno(ENOMEM);
998 return reset_async(pwm, 0);
1001 if (c == ASYNC_CMD_SAVE2)
1002 *rc = pwmd_save_async(pwm);
1003 else
1004 *rc = pwmd_open_async(pwm, pwm->filename);
1006 if (*rc) {
1007 reset_async(pwm, 0);
1008 return ASYNC_DONE;
1011 return ASYNC_PROCESS;
1014 /* Fall through so status messages can be processed during the
1015 * pinentry. */
1017 #endif
1019 if (pwm->fd < 0) {
1020 *rc = GPG_ERR_INV_STATE;
1021 return reset_async(pwm, 0);
1024 /* This is for pwmd_open_async() and pwmd_save_async(). For pinentry
1025 * retries. */
1026 *rc = _pwmd_process(pwm);
1028 if (*rc && *rc != GPG_ERR_INV_PASSPHRASE)
1029 return reset_async(pwm, 0);
1031 if (pwm->cmd == ASYNC_CMD_OPEN &&
1032 *rc == GPG_ERR_INV_PASSPHRASE &&
1033 #ifdef WITH_TCP
1034 (!pwm->tcp_conn || (pwm->tcp_conn && pwm->lastcmd == ASYNC_CMD_OPEN2)) &&
1035 #endif
1036 ++pwm->pin_try < pwm->pinentry_tries) {
1037 if (!get_custom_passphrase(pwm, NULL))
1038 goto done;
1040 #ifdef WITH_PINENTRY
1041 if (pwm->_password) {
1042 reset_async(pwm, 0);
1043 pwm->lastcmd = ASYNC_CMD_OPEN2;
1044 *rc = pwmd_open_async2(pwm, pwm->filename);
1046 else {
1047 #endif
1048 reset_async(pwm, 0);
1049 pwm->lastcmd = ASYNC_CMD_OPEN;
1050 *rc = pwmd_open_async(pwm, pwm->filename);
1051 #ifdef WITH_PINENTRY
1053 #endif
1056 done:
1057 if (*rc || pwm->state == ASYNC_DONE)
1058 return reset_async(pwm, 0);
1060 return pwm->state;
1063 gpg_error_t _assuan_command(pwm_t *pwm, assuan_context_t ctx,
1064 char **result, const char *cmd)
1066 membuf_t data;
1067 gpg_error_t rc;
1069 if (!cmd || !*cmd)
1070 return GPG_ERR_INV_ARG;
1072 if (strlen(cmd) >= ASSUAN_LINELENGTH+1)
1073 return GPG_ERR_LINE_TOO_LONG;
1075 data.len = 0;
1076 data.buf = NULL;
1077 rc = assuan_transact(ctx, cmd, inquire_realloc_cb, &data,
1078 #ifdef WITH_QUALITY
1079 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
1080 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
1081 #else
1082 inquire_cb, pwm,
1083 #endif
1084 pwm->status_func, pwm->status_data);
1086 if (rc) {
1087 if (data.buf) {
1088 pwmd_free(data.buf);
1089 data.buf = NULL;
1092 else {
1093 if (data.buf) {
1094 inquire_realloc_cb(&data, "", 1);
1096 if (!result) {
1097 pwmd_free(data.buf);
1098 rc = GPG_ERR_INV_ARG;
1100 else
1101 *result = (char *)data.buf;
1105 return gpg_err_code(rc);
1108 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_cb_t fn,
1109 void *data)
1111 if (!pwm || !cmd || !fn)
1112 return GPG_ERR_INV_ARG;
1114 if (!pwm->ctx)
1115 return GPG_ERR_INV_STATE;
1117 pwm->inquire_func = fn;
1118 pwm->inquire_data = data;
1119 pwm->inquire_sent = 0;
1120 return _assuan_command(pwm, pwm->ctx, NULL, cmd);
1123 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1124 va_list ap)
1126 char *buf;
1127 size_t len;
1128 va_list ap2;
1130 if (!pwm || !cmd)
1131 return GPG_ERR_INV_ARG;
1133 if (!pwm->ctx)
1134 return GPG_ERR_INV_STATE;
1137 * C99 allows the dst pointer to be null which will calculate the length
1138 * of the would-be result and return it.
1140 va_copy(ap2, ap);
1141 len = vsnprintf(NULL, 0, cmd, ap)+1;
1142 buf = (char *)pwmd_malloc(len);
1144 if (!buf) {
1145 va_end(ap2);
1146 return gpg_error_from_errno(ENOMEM);
1149 len = vsnprintf(buf, len, cmd, ap2);
1150 va_end(ap2);
1152 if (buf[strlen(buf)-1] == '\n')
1153 buf[strlen(buf)-1] = 0;
1155 if (buf[strlen(buf)-1] == '\r')
1156 buf[strlen(buf)-1] = 0;
1158 gpg_error_t rc = _assuan_command(pwm, pwm->ctx, result, buf);
1159 pwmd_free(buf);
1160 return rc;
1163 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1165 va_list ap;
1167 if (!pwm || !cmd)
1168 return GPG_ERR_INV_ARG;
1170 if (!pwm->ctx)
1171 return GPG_ERR_INV_STATE;
1173 if (result)
1174 *result = NULL;
1176 va_start(ap, cmd);
1177 gpg_error_t rc = pwmd_command_ap(pwm, result, cmd, ap);
1178 va_end(ap);
1179 return rc;
1182 static gpg_error_t send_pinentry_options(pwm_t *pwm)
1184 gpg_error_t rc;
1186 if (pwm->pinentry_path) {
1187 rc = pwmd_command(pwm, NULL, "SET PINENTRY_PATH=%s",
1188 pwm->pinentry_path);
1190 if (rc)
1191 return rc;
1194 if (pwm->pinentry_tty) {
1195 rc = pwmd_command(pwm, NULL, "SET TTYNAME=%s", pwm->pinentry_tty);
1197 if (rc)
1198 return rc;
1201 if (pwm->pinentry_term) {
1202 rc = pwmd_command(pwm, NULL, "SET TTYTYPE=%s", pwm->pinentry_term);
1204 if (rc)
1205 return rc;
1208 if (pwm->pinentry_display) {
1209 rc = pwmd_command(pwm, NULL, "SET DISPLAY=%s",
1210 pwm->pinentry_display);
1212 if (rc)
1213 return rc;
1216 if (pwm->title) {
1217 rc = pwmd_command(pwm, NULL, "SET TITLE=%s", pwm->title);
1219 if (rc)
1220 return rc;
1223 if (pwm->desc) {
1224 rc = pwmd_command(pwm, NULL, "SET DESC=%s", pwm->desc);
1226 if (rc)
1227 return rc;
1230 if (pwm->prompt) {
1231 rc = pwmd_command(pwm, NULL, "SET PROMPT=%s", pwm->prompt);
1233 if (rc)
1234 return rc;
1237 if (pwm->lcctype) {
1238 rc = pwmd_command(pwm, NULL, "SET LC_CTYPE=%s", pwm->lcctype);
1240 if (rc)
1241 return rc;
1244 if (pwm->lcmessages) {
1245 rc = pwmd_command(pwm, NULL, "SET LC_MESSAGES=%s", pwm->lcmessages);
1247 if (rc)
1248 return rc;
1251 if (pwm->pinentry_timeout >= 0 && !pwm->pin_try) {
1252 rc = pwmd_command(pwm, NULL, "SET PINENTRY_TIMEOUT=%i",
1253 pwm->pinentry_timeout);
1255 if (rc)
1256 return rc;
1259 return 0;
1262 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
1264 if (!pwm || !result)
1265 return GPG_ERR_INV_ARG;
1267 #ifdef WITH_TCP
1268 if ((pwm->fd == -1 && !pwm->tcp_conn) ||
1269 (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1))
1270 #else
1271 if (pwm->fd == -1)
1272 #endif
1273 return GPG_ERR_INV_STATE;
1275 #ifdef WITH_TCP
1276 *result = pwm->tcp_conn ? PWMD_SOCKET_SSH : PWMD_SOCKET_LOCAL;
1277 #else
1278 *result = PWMD_SOCKET_LOCAL;
1279 #endif
1280 return 0;
1283 static gpg_error_t set_pinentry_retry(pwm_t *pwm)
1285 gpg_error_t rc = 0;
1287 if (pwm->pin_try == 1) {
1288 rc = pwmd_command(pwm, NULL, "SET TITLE=%s",
1289 N_("Invalid passphrase, please try again."));
1291 if (rc)
1292 return rc;
1294 rc = pwmd_command(pwm, NULL, "SET PINENTRY_TIMEOUT=0");
1297 return rc;
1300 static gpg_error_t get_custom_passphrase(pwm_t *pwm, char **result)
1302 gpg_error_t rc = GPG_ERR_NO_DATA;
1304 if (result)
1305 *result = NULL;
1306 else {
1307 if (pwm->password || pwm->passfunc)
1308 return 0;
1311 if (pwm->password) {
1312 rc = 0;
1313 *result = pwm->password;
1315 else if (pwm->passfunc)
1316 rc = pwm->passfunc(pwm->passdata, result);
1318 return rc;
1321 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb,
1322 int local_pinentry)
1324 char *result = NULL;
1325 char *password = NULL;
1326 gpg_error_t rc;
1328 if (pwm->lastcmd != ASYNC_CMD_OPEN2)
1329 pwm->pin_try = 0;
1331 if (!pwm || !filename || !*filename)
1332 return GPG_ERR_INV_ARG;
1334 if (!pwm->ctx)
1335 return GPG_ERR_INV_STATE;
1338 * Avoid calling pinentry if the password is cached on the server or if
1339 * this is a new file.
1341 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
1343 if (rc == GPG_ERR_ENOENT)
1344 goto gotpassword;
1346 if (rc && rc != GPG_ERR_NOT_FOUND)
1347 return rc;
1349 if (rc == GPG_ERR_NOT_FOUND) {
1350 rc = get_custom_passphrase(pwm, &password);
1352 if (rc && rc != GPG_ERR_NO_DATA)
1353 return rc;
1354 else if (rc == GPG_ERR_NO_DATA)
1355 rc = GPG_ERR_NOT_FOUND;
1356 else
1357 goto gotpassword;
1360 #ifdef WITH_PINENTRY
1361 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
1362 /* Prevent pwmd from using it's pinentry if the passphrase fails. */
1363 if (!pwm->pin_try) {
1364 rc = pwmd_command(pwm, NULL, "SET ENABLE_PINENTRY=0");
1366 if (rc)
1367 return rc;
1370 rc = _pinentry_open(pwm, filename, &password, nb);
1372 /* pwmd_process() should be called if using a non-blocking local
1373 * pinentry. */
1374 if (rc || (!rc && nb))
1375 return rc;
1377 #endif
1379 gotpassword:
1380 reset_async(pwm, 0);
1382 #ifdef WITH_TCP
1383 if (!local_pinentry && !pwm->tcp_conn && !pwm->pin_try) {
1384 #else
1385 if (!local_pinentry && !pwm->pin_try) {
1386 #endif
1387 rc = send_pinentry_options(pwm);
1389 if (rc)
1390 return rc;
1393 rc = pwmd_command(pwm, NULL, "OPEN %s %s", filename,
1394 password ? password : "");
1397 * Keep the user defined password set with pwmd_setopt(). The password may
1398 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1400 if (!pwm->passfunc && password && password != pwm->password)
1401 pwmd_free(password);
1403 if (rc == GPG_ERR_INV_PASSPHRASE) {
1404 if (++pwm->pin_try < pwm->pinentry_tries) {
1405 if (!get_custom_passphrase(pwm, NULL))
1406 goto done;
1408 #ifdef WITH_PINENTRY
1409 #ifdef WITH_TCP
1410 if (pwm->tcp_conn && !local_pinentry)
1411 return rc;
1412 else if (local_pinentry)
1413 rc = _getpin(pwm, &password, PWMD_PINENTRY_OPEN_FAILED);
1414 else
1415 #else
1416 if (local_pinentry)
1417 rc = _getpin(pwm, &password, PWMD_PINENTRY_OPEN_FAILED);
1418 else
1419 #endif
1420 #else
1421 #ifdef WITH_TCP
1422 if (pwm->tcp_conn)
1423 return rc;
1424 else
1425 #endif
1426 #endif
1427 rc = set_pinentry_retry(pwm);
1429 if (rc)
1430 return rc;
1432 goto gotpassword;
1434 #ifdef WITH_PINENTRY
1435 else if (local_pinentry)
1436 _pinentry_disconnect(pwm);
1437 #endif
1439 done:
1440 return rc;
1442 #ifdef WITH_PINENTRY
1443 else if (rc && local_pinentry)
1444 _pinentry_disconnect(pwm);
1445 #endif
1447 if (!rc) {
1448 if (pwm->filename)
1449 pwmd_free(pwm->filename);
1451 pwm->filename = pwmd_strdup(filename);
1454 return rc;
1457 gpg_error_t pwmd_open2(pwm_t *pwm, const char *filename)
1459 #ifndef WITH_PINENTRY
1460 return GPG_ERR_NOT_IMPLEMENTED;
1461 #else
1462 return do_pwmd_open(pwm, filename, 0, 1);
1463 #endif
1466 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
1468 return do_pwmd_open(pwm, filename, 0, 0);
1471 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
1473 #ifndef WITH_PINENTRY
1474 return GPG_ERR_NOT_IMPLEMENTED;
1475 #else
1476 if (!pwm || !filename)
1477 return GPG_ERR_INV_ARG;
1479 if (!pwm->ctx)
1480 return GPG_ERR_INV_STATE;
1482 if (pwm->cmd != ASYNC_CMD_NONE)
1483 return GPG_ERR_ASS_NESTED_COMMANDS;
1485 /* Initialize a new command since this is not a pinentry retry. */
1486 if (pwm->lastcmd != ASYNC_CMD_OPEN2)
1487 pwm->pin_try = 0;
1489 pwm->cmd = ASYNC_CMD_OPEN2;
1490 pwm->state = ASYNC_PROCESS;
1491 gpg_error_t rc = do_pwmd_open(pwm, filename, 1, 1);
1493 if (rc)
1494 reset_async(pwm, 0);
1496 return rc;
1497 #endif
1500 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb, int local_pinentry)
1502 char *result = NULL;
1503 char *password = NULL;
1504 gpg_error_t rc;
1506 if (!pwm)
1507 return GPG_ERR_INV_ARG;
1509 if (!pwm->ctx)
1510 return GPG_ERR_INV_STATE;
1512 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
1514 if (rc == GPG_ERR_ENOENT)
1515 rc = GPG_ERR_NOT_FOUND;
1517 if (rc && rc != GPG_ERR_NOT_FOUND)
1518 return rc;
1520 if (rc == GPG_ERR_NOT_FOUND) {
1521 rc = get_custom_passphrase(pwm, &password);
1523 if (rc && rc != GPG_ERR_NO_DATA)
1524 return rc;
1525 else if (rc == GPG_ERR_NO_DATA)
1526 rc = GPG_ERR_NOT_FOUND;
1527 else
1528 goto gotpassword;
1531 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
1532 #ifdef WITH_PINENTRY
1533 /* Get the password using the LOCAL pinentry. */
1534 if (nb) {
1535 int p[2];
1536 pid_t pid;
1537 pwmd_nb_status_t pw;
1539 if (pipe(p) == -1)
1540 return gpg_error_from_syserror();
1542 #ifdef WITH_LIBPTH
1543 pid = pth_fork();
1544 #else
1545 pid = fork();
1546 #endif
1548 switch (pid) {
1549 case 0:
1550 close(p[0]);
1551 pw.fd = p[0];
1552 password = NULL;
1553 pw.error = _do_save_getpin(pwm, &password);
1555 if (!pw.error) {
1556 snprintf(pw.password, sizeof(pw.password), "%s",
1557 password);
1558 pwmd_free(password);
1560 #ifdef WITH_LIBPTH
1561 pth_write(p[1], &pw, sizeof(pw));
1562 #else
1563 write(p[1], &pw, sizeof(pw));
1564 #endif
1565 memset(&pw, 0, sizeof(pw));
1566 close(p[1]);
1567 _exit(0);
1568 break;
1569 case -1:
1570 rc = gpg_error_from_syserror();
1571 close(p[0]);
1572 close(p[1]);
1573 return rc;
1574 default:
1575 break;
1578 close(p[1]);
1579 pwm->nb_fd = p[0];
1580 pwm->nb_pid = pid;
1581 return 0;
1584 rc = _do_save_getpin(pwm, &password);
1586 if (rc)
1587 return rc;
1588 #endif
1590 else
1591 pwm->state = ASYNC_DONE;
1593 gotpassword:
1594 reset_async(pwm, 0);
1596 #ifdef WITH_TCP
1597 if (!local_pinentry && !pwm->tcp_conn) {
1598 #else
1599 if (!local_pinentry) {
1600 #endif
1601 rc = send_pinentry_options(pwm);
1603 if (rc)
1604 return rc;
1607 rc = pwmd_command(pwm, NULL, "SAVE %s", password ? password : "");
1609 if (!pwm->passfunc && password && password != pwm->password)
1610 pwmd_free(password);
1612 return rc;
1615 gpg_error_t pwmd_save_async2(pwm_t *pwm)
1617 #ifndef WITH_PINENTRY
1618 return GPG_ERR_NOT_IMPLEMENTED;
1619 #else
1620 if (!pwm)
1621 return GPG_ERR_INV_ARG;
1623 if (!pwm->ctx)
1624 return GPG_ERR_INV_STATE;
1626 if (pwm->cmd != ASYNC_CMD_NONE)
1627 return GPG_ERR_ASS_NESTED_COMMANDS;
1629 pwm->cmd = ASYNC_CMD_SAVE2;
1630 pwm->state = ASYNC_PROCESS;
1631 gpg_error_t rc = do_pwmd_save(pwm, 1, 1);
1633 if (rc)
1634 reset_async(pwm, 0);
1636 return rc;
1637 #endif
1640 gpg_error_t pwmd_save2(pwm_t *pwm)
1642 #ifndef WITH_PINENTRY
1643 return GPG_ERR_NOT_IMPLEMENTED;
1644 #else
1645 return do_pwmd_save(pwm, 0, 1);
1646 #endif
1649 gpg_error_t pwmd_save(pwm_t *pwm)
1651 return do_pwmd_save(pwm, 0, 0);
1654 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1656 va_list ap;
1657 int n = va_arg(ap, int);
1658 char *arg1;
1659 gpg_error_t rc = 0;
1661 if (!pwm)
1662 return GPG_ERR_INV_ARG;
1664 va_start(ap, opt);
1666 switch (opt) {
1667 case PWMD_OPTION_INQUIRE_TOTAL:
1668 n = va_arg(ap, size_t);
1669 pwm->inquire_total = n;
1670 break;
1671 case PWMD_OPTION_STATUS_CB:
1672 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
1673 break;
1674 case PWMD_OPTION_STATUS_DATA:
1675 pwm->status_data = va_arg(ap, void *);
1676 break;
1677 case PWMD_OPTION_PASSPHRASE_CB:
1678 pwm->passfunc = va_arg(ap, pwmd_passphrase_cb_t);
1679 break;
1680 case PWMD_OPTION_PASSPHRASE_DATA:
1681 pwm->passdata = va_arg(ap, void *);
1682 break;
1683 case PWMD_OPTION_PASSPHRASE:
1684 arg1 = va_arg(ap, char *);
1686 if (pwm->password)
1687 pwmd_free(pwm->password);
1689 pwm->password = arg1 ? pwmd_strdup(arg1) : NULL;
1690 break;
1691 case PWMD_OPTION_PINENTRY_TRIES:
1692 n = va_arg(ap, int);
1694 if (n <= 0) {
1695 va_end(ap);
1696 rc = GPG_ERR_INV_VALUE;
1698 else
1699 pwm->pinentry_tries = n;
1700 break;
1701 case PWMD_OPTION_PINENTRY_TIMEOUT:
1702 n = va_arg(ap, int);
1704 if (n < 0) {
1705 va_end(ap);
1706 rc = GPG_ERR_INV_VALUE;
1708 else
1709 pwm->pinentry_timeout = n;
1710 break;
1711 case PWMD_OPTION_PINENTRY_PATH:
1712 if (pwm->pinentry_path)
1713 pwmd_free(pwm->pinentry_path);
1715 pwm->pinentry_path = _expand_homedir(va_arg(ap, char *), NULL);
1716 break;
1717 case PWMD_OPTION_PINENTRY_TTY:
1718 arg1 = va_arg(ap, char *);
1720 if (pwm->pinentry_tty)
1721 pwmd_free(pwm->pinentry_tty);
1723 pwm->pinentry_tty = arg1 ? pwmd_strdup(arg1) : NULL;
1724 break;
1725 case PWMD_OPTION_PINENTRY_DISPLAY:
1726 if (pwm->pinentry_display)
1727 pwmd_free(pwm->pinentry_display);
1729 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
1730 break;
1731 case PWMD_OPTION_PINENTRY_TERM:
1732 arg1 = va_arg(ap, char *);
1734 if (pwm->pinentry_term)
1735 pwmd_free(pwm->pinentry_term);
1737 pwm->pinentry_term = arg1 ? pwmd_strdup(arg1) : NULL;
1738 break;
1739 case PWMD_OPTION_PINENTRY_TITLE:
1740 if (pwm->title)
1741 pwmd_free(pwm->title);
1743 pwm->title = _percent_escape(va_arg(ap, char *));
1744 break;
1745 case PWMD_OPTION_PINENTRY_PROMPT:
1746 if (pwm->prompt)
1747 pwmd_free(pwm->prompt);
1749 pwm->prompt = _percent_escape(va_arg(ap, char *));
1750 break;
1751 case PWMD_OPTION_PINENTRY_DESC:
1752 if (pwm->desc)
1753 pwmd_free(pwm->desc);
1755 pwm->desc = _percent_escape(va_arg(ap, char *));
1756 break;
1757 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1758 arg1 = va_arg(ap, char *);
1760 if (pwm->lcctype)
1761 pwmd_free(pwm->lcctype);
1763 pwm->lcctype = arg1 ? pwmd_strdup(arg1) : NULL;
1764 break;
1765 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1766 arg1 = va_arg(ap, char *);
1768 if (pwm->lcmessages)
1769 pwmd_free(pwm->lcmessages);
1771 pwm->lcmessages = arg1 ? pwmd_strdup(arg1) : NULL;
1772 break;
1773 case PWMD_OPTION_IP_VERSION:
1774 #ifdef WITH_TCP
1775 n = va_arg(ap, int);
1777 switch (n) {
1778 case PWMD_IP_ANY:
1779 case PWMD_IPV4:
1780 case PWMD_IPV6:
1781 pwm->prot = n;
1782 break;
1783 default:
1784 rc = GPG_ERR_INV_VALUE;
1785 break;
1788 va_end(ap);
1789 #else
1790 rc = GPG_ERR_NOT_IMPLEMENTED;
1791 #endif
1792 break;
1793 case PWMD_OPTION_KNOWNHOST_CB:
1794 #ifdef WITH_TCP
1795 pwm->kh_cb = va_arg(ap, pwmd_knownhost_cb_t);
1796 #else
1797 rc = GPG_ERR_NOT_IMPLEMENTED;
1798 #endif
1799 break;
1800 case PWMD_OPTION_KNOWNHOST_DATA:
1801 #ifdef WITH_TCP
1802 pwm->kh_data = va_arg(ap, void *);
1803 #else
1804 rc = GPG_ERR_NOT_IMPLEMENTED;
1805 #endif
1806 break;
1807 default:
1808 rc = GPG_ERR_UNKNOWN_OPTION;
1809 break;
1812 va_end(ap);
1813 return rc;
1816 gpg_error_t pwmd_get_fds(pwm_t *pwm, pwmd_fd_t *fds, int *n_fds)
1818 int in_total;
1819 int fd = 0;
1820 #ifdef WITH_TCP
1821 int afds[ARES_GETSOCK_MAXNUM];
1822 int got_sock = 0;
1823 int n, i;
1824 #endif
1826 if (!pwm || !fds || !n_fds || *n_fds <= 0)
1827 return GPG_ERR_INV_ARG;
1829 in_total = *n_fds;
1830 #ifdef WITH_TCP
1831 memset(afds, 0, sizeof(int)*ARES_GETSOCK_MAXNUM);
1832 #endif
1833 memset(fds, 0, sizeof(pwmd_fd_t)*in_total);
1834 *n_fds = 0;
1836 switch (pwm->cmd) {
1837 default:
1838 case ASYNC_CMD_NONE:
1839 case ASYNC_CMD_OPEN:
1840 case ASYNC_CMD_SAVE:
1841 #ifdef WITH_PINENTRY
1842 async1:
1843 #endif
1844 if (pwm->fd == -1)
1845 return GPG_ERR_INV_STATE;
1847 (*n_fds)++;
1848 fds[fd].fd = pwm->fd;
1849 fds[fd++].flags = PWMD_FD_READABLE;
1850 return 0;
1851 #ifdef WITH_PINENTRY
1852 case ASYNC_CMD_OPEN2:
1853 case ASYNC_CMD_SAVE2:
1854 /* The command has already completed (cached or new). */
1855 if (pwm->state == ASYNC_DONE)
1856 return 0;
1858 if (pwm->nb_fd == -1)
1859 return GPG_ERR_INV_STATE;
1861 (*n_fds)++;
1862 fds[fd].fd = pwm->nb_fd;
1863 fds[fd++].flags = PWMD_FD_READABLE;
1864 goto async1;
1865 #endif
1866 #ifdef WITH_TCP
1867 case ASYNC_CMD_DNS:
1868 if (!pwm->tcp_conn || !pwm->tcp_conn->chan)
1869 return GPG_ERR_INV_STATE;
1871 n = ares_getsock(pwm->tcp_conn->chan, afds, ARES_GETSOCK_MAXNUM);
1873 for (i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
1874 got_sock = 0;
1876 if (fd > in_total) {
1877 *n_fds = fd;
1878 return GPG_ERR_ERANGE;
1881 if (ARES_GETSOCK_READABLE(n, i)) {
1882 got_sock++;
1883 fds[fd].flags |= PWMD_FD_READABLE;
1886 if (ARES_GETSOCK_WRITABLE(n, i)) {
1887 got_sock++;
1888 fds[fd].flags |= PWMD_FD_WRITABLE;
1891 if (got_sock)
1892 fds[fd++].fd = afds[i];
1895 *n_fds = fd;
1896 return 0;
1897 case ASYNC_CMD_CONNECT:
1898 case ASYNC_CMD_HOSTKEY:
1899 if (!pwm->tcp_conn || pwm->tcp_conn->fd == -1)
1900 return GPG_ERR_INV_STATE;
1902 (*n_fds)++;
1903 fds[fd].fd = pwm->tcp_conn->fd;
1904 fds[fd++].flags = PWMD_FD_READABLE;
1905 return 0;
1906 #endif
1909 return GPG_ERR_INV_STATE;
1912 pwm_t *pwmd_new(const char *name)
1914 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
1916 if (!h)
1917 return NULL;
1919 if (name) {
1920 h->name = pwmd_strdup(name);
1922 if (!h->name) {
1923 pwmd_free(h);
1924 return NULL;
1928 reset_handle(h);
1929 h->pinentry_timeout = -30;
1930 h->pinentry_tries = 3;
1931 #ifdef WITH_TCP
1932 h->prot = PWMD_IP_ANY;
1933 #endif
1935 if (ttyname(STDOUT_FILENO)) {
1936 char buf[256];
1938 ttyname_r(STDOUT_FILENO, buf, sizeof(buf));
1939 h->pinentry_tty = pwmd_strdup(buf);
1941 if (!h->pinentry_tty)
1942 goto fail;
1945 if (getenv("TERM") && h->pinentry_tty) {
1946 h->pinentry_term = pwmd_strdup(getenv("TERM"));
1948 if (!h->pinentry_term)
1949 goto fail;
1952 if (getenv("DISPLAY")) {
1953 h->pinentry_display = pwmd_strdup(getenv("DISPLAY"));
1955 if (!h->pinentry_display)
1956 goto fail;
1959 return h;
1961 fail:
1962 pwmd_close(h);
1963 return NULL;
1966 void pwmd_free(void *ptr)
1968 _xfree(ptr);
1971 void *pwmd_malloc(size_t size)
1973 return _xmalloc(size);
1976 void *pwmd_calloc(size_t nmemb, size_t size)
1978 return _xcalloc(nmemb, size);
1981 void *pwmd_realloc(void *ptr, size_t size)
1983 return _xrealloc(ptr, size);
1986 char *pwmd_strdup(const char *str)
1988 return _xstrdup(str);
1991 char *pwmd_strdup_printf(const char *fmt, ...)
1993 va_list ap, ap2;
1994 int len;
1995 char *buf;
1997 if (!fmt)
1998 return NULL;
2000 va_start(ap, fmt);
2001 va_copy(ap2, ap);
2002 len = vsnprintf(NULL, 0, fmt, ap);
2003 va_end(ap);
2004 buf = pwmd_malloc(++len);
2006 if (buf)
2007 vsnprintf(buf, len, fmt, ap2);
2009 va_end(ap2);
2010 return buf;
2013 gpg_error_t pwmd_getpin(pwm_t *pwm, const char *filename, char **result,
2014 pwmd_pinentry_t which)
2016 #ifndef WITH_PINENTRY
2017 return GPG_ERR_NOT_IMPLEMENTED;
2018 #else
2019 return _pwmd_getpin(pwm, filename, result, which);
2020 #endif