Added password quality checking for the local pinentry when
[libpwmd.git] / src / libpwmd.c
blobe66e69d56d865ec7a84dcb3930e1c7e229c70301
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);
64 static const char *_pwmd_strerror(gpg_error_t e)
66 gpg_err_code_t code = gpg_err_code(e);
68 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
69 switch (code) {
70 default:
71 return NULL;
72 case GPG_ERR_USER_1:
73 return N_("Unknown error");
74 case GPG_ERR_USER_2:
75 return N_("No cache slots available");
76 case GPG_ERR_USER_3:
77 return N_("Recursion loop");
78 case GPG_ERR_USER_4:
79 return N_("No file is open");
80 case GPG_ERR_USER_5:
81 return N_("General LibXML error");
82 case GPG_ERR_USER_6:
83 return N_("File modified");
87 return NULL;
90 const char *pwmd_strerror(gpg_error_t code)
92 const char *p = _pwmd_strerror(code);
94 return p ? p : gpg_strerror(code);
97 int pwmd_strerror_r(gpg_error_t code, char *buf, size_t size)
99 const char *p = _pwmd_strerror(code);
101 if (p) {
102 snprintf(buf, size, "%s", p);
104 if (strlen(p) > size)
105 return ERANGE;
107 return 0;
110 return gpg_strerror_r(code, buf, size);
113 gpg_error_t pwmd_init()
115 static int initialized;
117 if (initialized)
118 return 0;
120 #ifndef MEM_DEBUG
121 _xmem_init();
122 #endif
123 #ifdef ENABLE_NLS
124 bindtextdomain("libpwmd", LOCALEDIR);
125 #endif
126 gpg_err_init();
127 assuan_set_malloc_hooks(pwmd_malloc, pwmd_realloc, pwmd_free);
128 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
129 initialized = 1;
130 return 0;
133 gpg_error_t _connect_finalize(pwm_t *pwm)
135 int active[2];
136 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
137 gpg_error_t rc;
138 char *result;
140 if (n <= 0)
141 return GPG_ERR_EBADFD;
143 pwm->fd = active[0];
144 #ifdef WITH_PINENTRY
145 pwm->pid = -1;
146 #endif
147 assuan_set_pointer(pwm->ctx, pwm);
149 #ifdef WITH_TCP
150 // Until X11 forwarding is supported, disable the remote pwmd pinentry.
151 if (pwm->tcp_conn) {
152 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
154 if (rc)
155 return rc;
157 #endif
159 if (pwm->name) {
160 rc = pwmd_command(pwm, NULL, "OPTION CLIENT NAME=%s", pwm->name);
162 if (rc)
163 return rc;
166 rc = pwmd_command(pwm, &result, "VERSION");
168 if (rc && rc != GPG_ERR_ASS_UNKNOWN_CMD)
169 return rc;
171 if (rc)
172 pwm->version = PWMD_V1;
173 else
174 pwm->version = PWMD_V2;
176 pwmd_free(result);
177 return 0;
180 static gpg_error_t _pwmd_connect_url(pwm_t *pwm, const char *url, int async)
182 char *p = (char *)url;
183 gpg_error_t rc;
185 if (!pwm)
186 return GPG_ERR_INV_ARG;
188 if (!p || !strncmp(p, "socket://", 9)) {
189 if (p)
190 p += 9;
192 goto connect_uds;
194 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
195 !strncmp(p, "ssh4://", 7)) {
196 #ifndef WITH_TCP
197 return GPG_ERR_NOT_IMPLEMENTED;
198 #else
199 char *host = NULL;
200 int port = -1;
201 char *identity = NULL;
202 char *known_hosts = NULL;
203 char *username = NULL;
205 if (!strncmp(p, "ssh6://", 7)) {
206 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV6);
207 p += 7;
209 else if (!strncmp(p, "ssh4://", 7)) {
210 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IPV4);
211 p += 7;
213 else {
214 rc = pwmd_setopt(pwm, PWMD_OPTION_IP_VERSION, PWMD_IP_ANY);
215 p += 6;
218 if (rc)
219 return rc;
221 rc = _parse_ssh_url(p, &host, &port, &username, &identity,
222 &known_hosts);
224 if (rc)
225 goto fail;
227 if (async)
228 rc = pwmd_ssh_connect_async(pwm, host, port, identity, username,
229 known_hosts);
230 else
231 rc = pwmd_ssh_connect(pwm, host, port, identity, username,
232 known_hosts);
234 fail:
235 if (host)
236 pwmd_free(host);
238 if (username)
239 pwmd_free(username);
241 if (identity)
242 pwmd_free(identity);
244 if (known_hosts)
245 pwmd_free(known_hosts);
247 return rc;
248 #endif
251 connect_uds:
252 rc = pwmd_connect(pwm, p);
253 pwm->state = ASYNC_DONE;
254 return rc;
257 gpg_error_t pwmd_connect_url(pwm_t *pwm, const char *url)
259 return _pwmd_connect_url(pwm, url, 0);
262 gpg_error_t pwmd_connect_url_async(pwm_t *pwm, const char *url)
264 return _pwmd_connect_url(pwm, url, 1);
267 gpg_error_t pwmd_connect(pwm_t *pwm, const char *path)
269 char *socketpath = NULL;
270 assuan_context_t ctx;
271 struct passwd pw;
272 char *pwbuf;
273 gpg_error_t rc;
275 if (!pwm)
276 return GPG_ERR_INV_ARG;
278 pwbuf = _getpwuid(&pw);
280 if (!pwbuf)
281 return gpg_error_from_errno(errno);
283 if (!path || !*path)
284 socketpath = pwmd_strdup_printf("%s/.pwmd/socket", pw.pw_dir);
285 else
286 socketpath = _expand_homedir((char *)path, &pw);
288 pwmd_free(pwbuf);
290 if (!socketpath)
291 return gpg_error_from_errno(ENOMEM);
293 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
294 pwmd_free(socketpath);
296 if (rc)
297 return gpg_err_code(rc);
299 pwm->ctx = ctx;
300 return _connect_finalize(pwm);
303 static void disconnect(pwm_t *pwm)
305 if (!pwm || !pwm->ctx)
306 return;
308 assuan_disconnect(pwm->ctx);
309 pwm->ctx = NULL;
310 pwm->fd = -1;
313 void pwmd_close(pwm_t *pwm)
315 if (!pwm)
316 return;
318 disconnect(pwm);
320 if (pwm->password)
321 pwmd_free(pwm->password);
323 if (pwm->title)
324 pwmd_free(pwm->title);
326 if (pwm->desc)
327 pwmd_free(pwm->desc);
329 if (pwm->prompt)
330 pwmd_free(pwm->prompt);
332 if (pwm->pinentry_tty)
333 pwmd_free(pwm->pinentry_tty);
335 if (pwm->pinentry_display)
336 pwmd_free(pwm->pinentry_display);
338 if (pwm->pinentry_term)
339 pwmd_free(pwm->pinentry_term);
341 if (pwm->lcctype)
342 pwmd_free(pwm->lcctype);
344 if (pwm->lcmessages)
345 pwmd_free(pwm->lcmessages);
347 if (pwm->filename)
348 pwmd_free(pwm->filename);
350 if (pwm->name)
351 pwmd_free(pwm->name);
353 #ifdef WITH_TCP
354 if (pwm->tcp_conn)
355 _free_ssh_conn(pwm->tcp_conn);
356 #endif
358 pwmd_free(pwm);
361 gpg_error_t pwmd_ssh_connect_async(pwm_t *pwm, const char *host, int port,
362 const char *identity, const char *user, const char *known_hosts)
364 #ifndef WITH_TCP
365 return GPG_ERR_NOT_IMPLEMENTED;
366 #else
367 return _do_pwmd_ssh_connect_async(pwm, host, port, identity, user,
368 known_hosts, ASYNC_CMD_CONNECT);
369 #endif
372 gpg_error_t pwmd_ssh_connect(pwm_t *pwm, const char *host, int port,
373 const char *identity, const char *user, const char *known_hosts)
375 #ifndef WITH_TCP
376 return GPG_ERR_NOT_IMPLEMENTED;
377 #else
378 return _do_pwmd_ssh_connect(pwm, host, port, identity, user, known_hosts, 0);
379 #endif
382 gpg_error_t pwmd_get_hostkey(pwm_t *pwm, const char *host, int port,
383 char **result)
385 #ifndef WITH_TCP
386 return GPG_ERR_NOT_IMPLEMENTED;
387 #else
388 char *hostkey;
389 gpg_error_t rc;
391 rc = _do_pwmd_ssh_connect(pwm, host, port, NULL, NULL, NULL, 1);
393 if (rc)
394 return rc;
396 hostkey = pwmd_strdup(pwm->tcp_conn->hostkey);
398 if (!hostkey)
399 rc = gpg_error_from_errno(ENOMEM);
401 *result = hostkey;
402 return rc;
403 #endif
406 gpg_error_t pwmd_get_hostkey_async(pwm_t *pwm, const char *host, int port)
408 #ifndef WITH_TCP
409 return GPG_ERR_NOT_IMPLEMENTED;
410 #else
411 return _do_pwmd_ssh_connect_async(pwm, host, port, NULL, NULL, NULL,
412 ASYNC_CMD_HOSTKEY);
413 #endif
416 static int inquire_realloc_cb(void *data, const void *buffer, size_t len)
418 membuf_t *mem = (membuf_t *)data;
419 void *p;
421 if (!buffer)
422 return 0;
424 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
425 return gpg_error_from_errno(ENOMEM);
427 mem->buf = p;
428 memcpy((char *)mem->buf + mem->len, buffer, len);
429 mem->len += len;
430 return 0;
433 static int inquire_cb(void *data, const char *keyword)
435 pwm_t *pwm = (pwm_t *)data;
436 gpg_error_t rc = 0;
437 int flags = fcntl(pwm->fd, F_GETFL);
439 /* Shouldn't get this far without a callback. */
440 if (!pwm->inquire_func)
441 return GPG_ERR_INV_ARG;
443 for (;;) {
444 char *result = NULL;
445 size_t len;
446 gpg_error_t arc;
448 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result, &len);
449 rc = gpg_err_code(rc);
451 if (rc == GPG_ERR_EOF || !rc) {
452 if (len <= 0 || !result) {
453 rc = 0;
454 break;
457 arc = assuan_send_data(pwm->ctx, result, len);
458 arc = gpg_err_code(arc);
460 if (rc == GPG_ERR_EOF) {
461 rc = arc;
462 break;
465 rc = arc;
467 else if (rc)
468 break;
470 if (!rc) {
471 /* Set to non-blocking so _pwmd_process() can return. */
472 fcntl(pwm->fd, F_SETFL, O_NONBLOCK);
473 rc = _pwmd_process(pwm);
474 fcntl(pwm->fd, F_SETFL, flags);
478 fcntl(pwm->fd, F_SETFL, flags);
479 return gpg_err_code(rc);
482 static gpg_error_t do_nb_command(pwm_t *pwm, const char *cmd, ...)
484 char *buf;
485 gpg_error_t rc;
486 va_list ap;
487 int len;
489 if (pwm->state == ASYNC_DONE)
490 pwm->state = ASYNC_INIT;
492 if (pwm->state != ASYNC_INIT)
493 return GPG_ERR_INV_STATE;
495 buf = pwmd_malloc(ASSUAN_LINELENGTH+1);
497 if (!buf)
498 return gpg_error_from_errno(ENOMEM);
500 va_start(ap, cmd);
501 len = vsnprintf(buf, ASSUAN_LINELENGTH+1, cmd, ap);
502 va_end(ap);
504 if (len >= ASSUAN_LINELENGTH+1) {
505 pwmd_free(buf);
506 return GPG_ERR_LINE_TOO_LONG;
509 rc = assuan_write_line(pwm->ctx, buf);
510 pwmd_free(buf);
512 if (!rc)
513 pwm->state = ASYNC_PROCESS;
515 return gpg_err_code(rc);
518 gpg_error_t pwmd_open_async(pwm_t *pwm, const char *filename)
520 char *p = NULL;
521 const char *f = NULL;
522 gpg_error_t rc;
524 if (!pwm || !filename)
525 return GPG_ERR_INV_ARG;
527 if (!pwm->ctx)
528 return GPG_ERR_INV_STATE;
530 if (pwm->cmd != ASYNC_CMD_NONE)
531 return GPG_ERR_ASS_NESTED_COMMANDS;
533 if (pwm->lastcmd == ASYNC_CMD_NONE) {
534 pwm->pin_try = 0;
536 if (pwm->filename)
537 pwmd_free(pwm->filename);
539 pwm->filename = pwmd_strdup(filename);
541 if (!pwm->filename)
542 return gpg_error_from_errno(ENOMEM);
544 gpg_error_t rc = send_pinentry_options(pwm);
546 if (rc)
547 return rc;
549 p = pwm->password;
550 f = filename;
552 else if (pwm->lastcmd == ASYNC_CMD_OPEN2) {
553 p = pwm->_password;
554 f = pwm->filename;
556 else if (pwm->lastcmd == ASYNC_CMD_OPEN) {
557 rc = set_pinentry_retry(pwm);
559 if (rc)
560 return rc;
562 p = pwm->password;
563 f = filename;
565 else
566 return GPG_ERR_INV_STATE;
568 pwm->cmd = ASYNC_CMD_OPEN;
569 return do_nb_command(pwm, "OPEN %s %s", f, p ? p : "");
572 gpg_error_t pwmd_save_async(pwm_t *pwm)
574 char *p = NULL;
576 if (!pwm)
577 return GPG_ERR_INV_ARG;
579 if (!pwm->ctx)
580 return GPG_ERR_INV_STATE;
582 if (pwm->cmd != ASYNC_CMD_NONE)
583 return GPG_ERR_ASS_NESTED_COMMANDS;
585 if (pwm->lastcmd != ASYNC_CMD_SAVE2) {
586 gpg_error_t rc = send_pinentry_options(pwm);
588 if (rc)
589 return rc;
591 p = pwm->password;
593 else
594 p = pwm->_password;
596 pwm->cmd = ASYNC_CMD_SAVE;
597 return do_nb_command(pwm, "SAVE %s", p ? p : "");
600 static gpg_error_t parse_assuan_line(pwm_t *pwm)
602 gpg_error_t rc;
603 char *line;
604 size_t len;
606 rc = assuan_read_line(pwm->ctx, &line, &len);
608 if (!rc) {
609 if (line[0] == 'O' && line[1] == 'K' &&
610 (line[2] == 0 || line[2] == ' ')) {
611 pwm->state = ASYNC_DONE;
613 else if (line[0] == '#') {
615 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
616 if (pwm->status_func) {
617 rc = pwm->status_func(pwm->status_data,
618 line[1] == 0 ? line+1 : line+2);
621 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
622 (line[3] == 0 || line[3] == ' ')) {
623 line += 4;
624 rc = atoi(line);
625 pwm->state = ASYNC_DONE;
629 return gpg_err_code(rc);
632 gpg_error_t pwmd_pending_line(pwm_t *pwm)
634 if (!pwm)
635 return GPG_ERR_INV_ARG;
637 if (!pwm->ctx)
638 return GPG_ERR_INV_STATE;
640 return assuan_pending_line(pwm->ctx) ? 0 : GPG_ERR_NO_DATA;
643 static pwmd_async_t reset_async(pwm_t *pwm, int done)
645 pwm->state = ASYNC_INIT;
646 pwm->cmd = pwm->lastcmd = ASYNC_CMD_NONE;
648 #ifdef WITH_PINENTRY
649 if (pwm->nb_fd != -1) {
650 close(pwm->nb_fd);
651 pwm->nb_fd = -1;
654 if (pwm->_password) {
655 pwmd_free(pwm->_password);
656 pwm->_password = NULL;
658 #endif
659 #ifdef WITH_TCP
660 if (done && pwm->tcp_conn) {
661 _free_ssh_conn(pwm->tcp_conn);
662 pwm->tcp_conn = NULL;
664 #endif
666 return ASYNC_DONE;
670 * Used for processing status messages when not in an async command and for
671 * waiting for the result from pwmd_open_async() and pwmd_save_async().
673 static gpg_error_t _pwmd_process(pwm_t *pwm)
675 gpg_error_t rc = 0;
676 fd_set fds;
677 struct timeval tv = {0, 0};
678 int n;
680 FD_ZERO(&fds);
681 FD_SET(pwm->fd, &fds);
682 #ifdef WITH_LIBPTH
683 n = pth_select(pwm->fd+1, &fds, NULL, NULL, &tv);
684 #else
685 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
686 #endif
688 if (n == -1)
689 return gpg_error_from_syserror();
691 if (n > 0) {
692 if (FD_ISSET(pwm->fd, &fds))
693 rc = parse_assuan_line(pwm);
696 while (!rc && assuan_pending_line(pwm->ctx))
697 rc = parse_assuan_line(pwm);
699 return gpg_err_code(rc);
702 static void reset_handle(pwm_t *h)
704 h->fd = -1;
705 #ifdef WITH_PINENTRY
706 if (h->pctx)
707 _pinentry_disconnect(h);
709 h->nb_fd = -1;
710 #endif
711 h->pin_try = 0;
712 reset_async(h, 0);
715 gpg_error_t pwmd_disconnect(pwm_t *pwm)
717 if (!pwm)
718 return GPG_ERR_INV_ARG;
720 #ifdef WITH_TCP
721 if (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1)
722 #else
723 if (pwm->fd == -1)
724 #endif
725 return GPG_ERR_INV_STATE;
727 if (pwm->fd != 1)
728 disconnect(pwm);
729 #ifdef WITH_TCP
730 else
731 _ssh_disconnect(pwm);
732 #endif
734 reset_handle(pwm);
735 return 0;
738 pwmd_async_t pwmd_process(pwm_t *pwm, gpg_error_t *rc, char **result)
740 #if defined(WITH_PINENTRY) || defined(WITH_TCP)
741 fd_set fds;
742 int n;
743 struct timeval tv = {0, 0};
744 #endif
746 if (result)
747 *result = NULL;
749 if (!rc)
750 return GPG_ERR_INV_ARG;
752 *rc = 0;
754 if (!pwm) {
755 *rc = GPG_ERR_INV_ARG;
756 return ASYNC_DONE;
758 else if (!pwm->ctx) {
759 switch (pwm->cmd) {
760 default:
761 *rc = GPG_ERR_INV_STATE;
762 return ASYNC_DONE;
763 #ifdef WITH_TCP
764 case ASYNC_CMD_DNS:
765 case ASYNC_CMD_CONNECT:
766 case ASYNC_CMD_HOSTKEY:
767 break;
768 #endif
772 /* When not in a command, this will let libassuan process status messages
773 * by calling PWMD_OPTION_STATUS_FUNC. The client can poll the file
774 * descriptor returned by pwmd_get_fd() to determine when this should be
775 * called or call pwmd_pending_line() to determine whether a buffered line
776 * needs to be processed. */
777 if (pwm->cmd == ASYNC_CMD_NONE) {
778 *rc = _pwmd_process(pwm);
779 return ASYNC_DONE;
782 /* Fixes pwmd_open/save_async2() when there is a cached or new file. */
783 if (pwm->state == ASYNC_DONE) {
784 *rc = _pwmd_process(pwm);
785 return reset_async(pwm, 0);
788 if (pwm->state != ASYNC_PROCESS) {
789 *rc = GPG_ERR_INV_STATE;
790 return ASYNC_DONE;
793 #ifdef WITH_TCP
794 if (pwm->cmd == ASYNC_CMD_DNS) {
795 fd_set rfds, wfds;
797 if (pwm->tcp_conn->rc) {
798 *rc = pwm->tcp_conn->rc;
799 return reset_async(pwm, 1);
802 FD_ZERO(&rfds);
803 FD_ZERO(&wfds);
804 n = ares_fds(pwm->tcp_conn->chan, &rfds, &wfds);
806 /* Shouldn't happen. */
807 if (!n)
808 return pwm->state;
810 #ifdef WITH_LIBPTH
811 n = pth_select(n, &rfds, &wfds, NULL, &tv);
812 #else
813 n = select(n, &rfds, &wfds, NULL, &tv);
814 #endif
816 if (n < 0) {
817 *rc = gpg_error_from_syserror();
818 return reset_async(pwm, 1);
821 if (n > 0)
822 ares_process(pwm->tcp_conn->chan, &rfds, &wfds);
824 return pwm->state;
826 else if (pwm->cmd == ASYNC_CMD_CONNECT) {
827 if (pwm->tcp_conn->rc == GPG_ERR_EINPROGRESS) {
828 int ret;
829 socklen_t len = sizeof(int);
831 FD_ZERO(&fds);
832 FD_SET(pwm->tcp_conn->fd, &fds);
833 #ifdef WITH_LIBPTH
834 n = pth_select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
835 #else
836 n = select(pwm->tcp_conn->fd+1, NULL, &fds, NULL, &tv);
837 #endif
839 if (!n || !FD_ISSET(pwm->tcp_conn->fd, &fds))
840 return pwm->state;
841 else if (n == -1) {
842 *rc = gpg_error_from_syserror();
843 return reset_async(pwm, 1);
846 ret = getsockopt(pwm->tcp_conn->fd, SOL_SOCKET, SO_ERROR, &n, &len);
848 if (ret || n) {
849 *rc = ret ? gpg_error_from_syserror() : gpg_error_from_errno(n);
850 return reset_async(pwm, 1);
853 else if (pwm->tcp_conn->rc) {
854 *rc = pwm->tcp_conn->rc;
855 return reset_async(pwm, 1);
858 fcntl(pwm->tcp_conn->fd, F_SETFL, 0);
859 *rc = _setup_ssh_session(pwm);
861 if (!*rc) {
862 switch (pwm->tcp_conn->cmd) {
863 case ASYNC_CMD_HOSTKEY:
864 *result = pwm->result;
865 break;
866 default:
867 break;
871 return reset_async(pwm, *rc ? 1 : 0);
873 #endif
875 #ifdef WITH_PINENTRY
876 if (pwm->cmd == ASYNC_CMD_OPEN2 || pwm->cmd == ASYNC_CMD_SAVE2) {
877 int status;
879 if (pwm->nb_fd == -1) {
880 *rc = GPG_ERR_INV_STATE;
881 return reset_async(pwm, 0);
884 FD_ZERO(&fds);
885 FD_SET(pwm->nb_fd, &fds);
886 FD_SET(pwm->fd, &fds);
887 #ifdef WITH_LIBPTH
888 n = pth_select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
889 #else
890 n = select(pwm->nb_fd+1, &fds, NULL, NULL, &tv);
891 #endif
892 if (n < 0) {
893 *rc = gpg_error_from_syserror();
894 return reset_async(pwm, 0);
897 if (n > 0 && FD_ISSET(pwm->nb_fd, &fds)) {
898 pwmd_nb_status_t nb;
899 #ifdef WITH_LIBPTH
900 size_t len = pth_read(pwm->nb_fd, &nb, sizeof(nb));
901 #else
902 size_t len = read(pwm->nb_fd, &nb, sizeof(nb));
903 #endif
904 waitpid(pwm->nb_pid, &status, WNOHANG);
906 if (len != sizeof(nb)) {
907 memset(&nb, 0, sizeof(pwmd_nb_status_t));
908 *rc = gpg_error_from_syserror();
909 return reset_async(pwm, 0);
912 *rc = nb.error;
914 if (*rc)
915 return reset_async(pwm, 0);
917 /* Since the non-blocking pinentry returned a success, do a
918 * non-blocking OPEN or SAVE. */
919 pwmd_async_cmd_t c = pwm->cmd;
920 reset_async(pwm, 0);
921 pwm->_password = pwmd_strdup(nb.password);
922 memset(&nb, 0, sizeof(pwmd_nb_status_t));
923 pwm->lastcmd = c;
925 if (!pwm->_password) {
926 *rc = gpg_error_from_errno(ENOMEM);
927 return reset_async(pwm, 0);
930 if (c == ASYNC_CMD_SAVE2)
931 *rc = pwmd_save_async(pwm);
932 else
933 *rc = pwmd_open_async(pwm, pwm->filename);
935 if (*rc) {
936 reset_async(pwm, 0);
937 return ASYNC_DONE;
940 return ASYNC_PROCESS;
943 /* Fall through so status messages can be processed during the
944 * pinentry. */
946 #endif
948 if (pwm->fd < 0) {
949 *rc = GPG_ERR_INV_STATE;
950 return reset_async(pwm, 0);
953 /* This is for pwmd_open_async() and pwmd_save_async(). For pinentry
954 * retries. */
955 *rc = _pwmd_process(pwm);
957 if (*rc && *rc != GPG_ERR_INV_PASSPHRASE)
958 return reset_async(pwm, 0);
960 #ifdef WITH_TCP
961 if (!pwm->tcp_conn && pwm->cmd == ASYNC_CMD_OPEN &&
962 #else
963 if (pwm->cmd == ASYNC_CMD_OPEN &&
964 #endif
965 *rc == GPG_ERR_INV_PASSPHRASE &&
966 ++pwm->pin_try < pwm->pinentry_tries) {
967 if (pwm->_password) {
968 reset_async(pwm, 0);
969 pwm->lastcmd = ASYNC_CMD_OPEN2;
970 *rc = pwmd_open_async2(pwm, pwm->filename);
972 else {
973 reset_async(pwm, 0);
974 pwm->lastcmd = ASYNC_CMD_OPEN;
975 *rc = pwmd_open_async(pwm, pwm->filename);
979 if (*rc || pwm->state == ASYNC_DONE)
980 return reset_async(pwm, 0);
982 return pwm->state;
985 gpg_error_t _assuan_command(pwm_t *pwm, assuan_context_t ctx,
986 char **result, const char *cmd)
988 membuf_t data;
989 gpg_error_t rc;
991 if (!cmd || !*cmd)
992 return GPG_ERR_INV_ARG;
994 if (strlen(cmd) >= ASSUAN_LINELENGTH+1)
995 return GPG_ERR_LINE_TOO_LONG;
997 data.len = 0;
998 data.buf = NULL;
999 rc = assuan_transact(ctx, cmd, inquire_realloc_cb, &data,
1000 #ifdef WITH_QUALITY
1001 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
1002 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
1003 #else
1004 inquire_cb, pwm,
1005 #endif
1006 pwm->status_func, pwm->status_data);
1008 if (rc) {
1009 if (data.buf) {
1010 pwmd_free(data.buf);
1011 data.buf = NULL;
1014 else {
1015 if (data.buf) {
1016 inquire_realloc_cb(&data, "", 1);
1018 if (!result) {
1019 pwmd_free(data.buf);
1020 rc = GPG_ERR_INV_ARG;
1022 else
1023 *result = (char *)data.buf;
1027 return gpg_err_code(rc);
1030 gpg_error_t pwmd_inquire(pwm_t *pwm, const char *cmd, pwmd_inquire_cb_t fn,
1031 void *data)
1033 if (!pwm || !cmd || !fn)
1034 return GPG_ERR_INV_ARG;
1036 if (!pwm->ctx)
1037 return GPG_ERR_INV_STATE;
1039 pwm->inquire_func = fn;
1040 pwm->inquire_data = data;
1041 return _assuan_command(pwm, pwm->ctx, NULL, cmd);
1044 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, const char *cmd,
1045 va_list ap)
1047 char *buf;
1048 size_t len;
1049 va_list ap2;
1051 if (!pwm || !cmd)
1052 return GPG_ERR_INV_ARG;
1054 if (!pwm->ctx)
1055 return GPG_ERR_INV_STATE;
1058 * C99 allows the dst pointer to be null which will calculate the length
1059 * of the would-be result and return it.
1061 va_copy(ap2, ap);
1062 len = vsnprintf(NULL, 0, cmd, ap)+1;
1063 buf = (char *)pwmd_malloc(len);
1065 if (!buf) {
1066 va_end(ap2);
1067 return gpg_error_from_errno(ENOMEM);
1070 len = vsnprintf(buf, len, cmd, ap2);
1071 va_end(ap2);
1073 if (buf[strlen(buf)-1] == '\n')
1074 buf[strlen(buf)-1] = 0;
1076 if (buf[strlen(buf)-1] == '\r')
1077 buf[strlen(buf)-1] = 0;
1079 gpg_error_t rc = _assuan_command(pwm, pwm->ctx, result, buf);
1080 pwmd_free(buf);
1081 return rc;
1084 gpg_error_t pwmd_command(pwm_t *pwm, char **result, const char *cmd, ...)
1086 va_list ap;
1088 if (!pwm || !cmd)
1089 return GPG_ERR_INV_ARG;
1091 if (!pwm->ctx)
1092 return GPG_ERR_INV_STATE;
1094 if (result)
1095 *result = NULL;
1097 va_start(ap, cmd);
1098 gpg_error_t rc = pwmd_command_ap(pwm, result, cmd, ap);
1099 va_end(ap);
1100 return rc;
1103 static gpg_error_t send_pinentry_options(pwm_t *pwm)
1105 gpg_error_t rc;
1107 if (pwm->pinentry_path) {
1108 rc = pwmd_command(pwm, NULL, "OPTION PATH=%s", pwm->pinentry_path);
1110 if (rc)
1111 return rc;
1114 if (pwm->pinentry_tty) {
1115 rc = pwmd_command(pwm, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
1117 if (rc)
1118 return rc;
1121 if (pwm->pinentry_term) {
1122 rc = pwmd_command(pwm, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
1124 if (rc)
1125 return rc;
1128 if (pwm->pinentry_display) {
1129 rc = pwmd_command(pwm, NULL, "OPTION DISPLAY=%s",
1130 pwm->pinentry_display);
1132 if (rc)
1133 return rc;
1136 if (pwm->title) {
1137 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s", pwm->title);
1139 if (rc)
1140 return rc;
1143 if (pwm->desc) {
1144 rc = pwmd_command(pwm, NULL, "OPTION DESC=%s", pwm->desc);
1146 if (rc)
1147 return rc;
1150 if (pwm->prompt) {
1151 rc = pwmd_command(pwm, NULL, "OPTION PROMPT=%s", pwm->prompt);
1153 if (rc)
1154 return rc;
1157 if (pwm->lcctype) {
1158 rc = pwmd_command(pwm, NULL, "OPTION LC_CTYPE=%s", pwm->lcctype);
1160 if (rc)
1161 return rc;
1164 if (pwm->lcmessages) {
1165 rc = pwmd_command(pwm, NULL, "OPTION LC_MESSAGES=%s", pwm->lcmessages);
1167 if (rc)
1168 return rc;
1171 if (pwm->pinentry_timeout >= 0 && !pwm->pin_try) {
1172 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=%i", pwm->pinentry_timeout);
1174 if (rc)
1175 return rc;
1178 return 0;
1181 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
1183 if (!pwm || !result)
1184 return GPG_ERR_INV_ARG;
1186 #ifdef WITH_TCP
1187 if (pwm->fd == -1 && pwm->tcp_conn && pwm->tcp_conn->fd == -1)
1188 #else
1189 if (pwm->fd == -1)
1190 #endif
1191 return GPG_ERR_INV_STATE;
1193 #ifdef WITH_TCP
1194 *result = pwm->tcp_conn ? PWMD_SOCKET_SSH : PWMD_SOCKET_UDS;
1195 #else
1196 *result = PWMD_SOCKET_UDS;
1197 #endif
1198 return 0;
1201 static gpg_error_t set_pinentry_retry(pwm_t *pwm)
1203 gpg_error_t rc = 0;
1205 if (pwm->pin_try == 1) {
1206 rc = pwmd_command(pwm, NULL, "OPTION TITLE=%s",
1207 N_("Invalid passphrase, please try again."));
1209 if (rc)
1210 return rc;
1212 rc = pwmd_command(pwm, NULL, "OPTION TIMEOUT=0");
1215 return rc;
1218 static gpg_error_t do_pwmd_open(pwm_t *pwm, const char *filename, int nb,
1219 int local_pinentry)
1221 char *result = NULL;
1222 char *password = NULL;
1223 char *path;
1224 gpg_error_t rc;
1226 if (pwm->lastcmd != ASYNC_CMD_OPEN2)
1227 pwm->pin_try = 0;
1229 if (!pwm || !filename || !*filename)
1230 return GPG_ERR_INV_ARG;
1232 if (!pwm->ctx)
1233 return GPG_ERR_INV_STATE;
1236 * Avoid calling pinentry if the password is cached on the server or if
1237 * this is a new file. pwmd version 2 adds a VERSION command which is
1238 * determined in _connect_finalize(). If the server is version 2,
1239 * ISCACHED will return GPG_ERR_ENOENT if it doesn't exist.
1241 #ifdef WITH_TCP
1242 /* Don't try a local filesystem lookup of the data file over a remote
1243 * connection. */
1244 if (!pwm->tcp_conn && pwm->version == PWMD_V1) {
1245 #else
1246 if (pwm->version == PWMD_V1) {
1247 #endif
1248 rc = pwmd_command(pwm, &result, "GETCONFIG data_directory");
1250 if (rc)
1251 return rc;
1253 path = pwmd_strdup_printf("%s/%s", result, filename);
1254 pwmd_free(result);
1256 if (!path)
1257 return gpg_error_from_errno(ENOMEM);
1259 if (access(path, R_OK) == -1) {
1260 if (errno == ENOENT) {
1261 pwmd_free(path);
1262 goto gotpassword;
1266 pwmd_free(path);
1269 rc = pwmd_command(pwm, &result, "ISCACHED %s", filename);
1271 /* pwmd >= 2.0 specific. This is a new file which doesn't require a
1272 * passphrase. */
1273 if (rc == GPG_ERR_ENOENT)
1274 goto gotpassword;
1276 if (rc && rc != GPG_ERR_NOT_FOUND)
1277 return rc;
1279 if (rc == GPG_ERR_NOT_FOUND) {
1280 if (pwm->password) {
1281 password = pwm->password;
1282 goto gotpassword;
1285 if (pwm->passfunc) {
1286 rc = pwm->passfunc(pwm->passdata, &password);
1288 if (rc)
1289 return rc;
1291 goto gotpassword;
1295 #ifdef WITH_PINENTRY
1296 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
1297 /* Prevent pwmd from using it's pinentry if the passphrase fails. */
1298 if (!pwm->pin_try) {
1299 rc = pwmd_command(pwm, NULL, "OPTION PINENTRY=0");
1301 if (rc)
1302 return rc;
1305 rc = _pinentry_open(pwm, filename, &password, nb);
1307 /* pwmd_process() should be called if using a non-blocking local
1308 * pinentry. */
1309 if (rc || (!rc && nb))
1310 return rc;
1312 #endif
1314 gotpassword:
1315 pwm->state = ASYNC_DONE;
1317 #ifdef WITH_TCP
1318 if (!local_pinentry && !pwm->tcp_conn && !pwm->pin_try) {
1319 #else
1320 if (!local_pinentry && !pwm->pin_try) {
1321 #endif
1322 rc = send_pinentry_options(pwm);
1324 if (rc)
1325 return rc;
1328 rc = pwmd_command(pwm, NULL, "OPEN %s %s", filename,
1329 password ? password : "");
1332 * Keep the user defined password set with pwmd_setopt(). The password may
1333 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1335 if (!pwm->passfunc && password && password != pwm->password)
1336 pwmd_free(password);
1338 if (rc == GPG_ERR_INV_PASSPHRASE) {
1339 if (++pwm->pin_try < pwm->pinentry_tries && !nb) {
1340 rc = 0;
1342 #ifdef WITH_PINENTRY
1343 if (local_pinentry)
1344 rc = _getpin(pwm, &password, PINENTRY_OPEN_FAILED);
1345 #ifdef WITH_TCP
1346 else if (!pwm->tcp_conn) {
1347 #else
1348 else {
1349 #endif
1350 #else
1351 #ifdef WITH_TCP
1352 if (!pwm->tcp_conn) {
1353 #endif
1354 #endif
1355 rc = set_pinentry_retry(pwm);
1358 if (rc)
1359 return rc;
1361 goto gotpassword;
1363 #ifdef WITH_PINENTRY
1364 else if (local_pinentry && !nb)
1365 _pinentry_disconnect(pwm);
1366 #endif
1368 return rc;
1370 #ifdef WITH_PINENTRY
1371 else if (rc && local_pinentry && !nb)
1372 _pinentry_disconnect(pwm);
1373 #endif
1375 if (!rc) {
1376 if (pwm->filename)
1377 pwmd_free(pwm->filename);
1379 pwm->filename = pwmd_strdup(filename);
1382 return rc;
1385 gpg_error_t pwmd_open2(pwm_t *pwm, const char *filename)
1387 #ifndef WITH_PINENTRY
1388 return GPG_ERR_NOT_IMPLEMENTED;
1389 #else
1390 return do_pwmd_open(pwm, filename, 0, 1);
1391 #endif
1394 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename)
1396 return do_pwmd_open(pwm, filename, 0, 0);
1399 gpg_error_t pwmd_open_async2(pwm_t *pwm, const char *filename)
1401 #ifndef WITH_PINENTRY
1402 return GPG_ERR_NOT_IMPLEMENTED;
1403 #else
1404 if (!pwm || !filename)
1405 return GPG_ERR_INV_ARG;
1407 if (!pwm->ctx)
1408 return GPG_ERR_INV_STATE;
1410 if (pwm->cmd != ASYNC_CMD_NONE)
1411 return GPG_ERR_ASS_NESTED_COMMANDS;
1413 /* Initialize a new command since this is not a pinentry retry. */
1414 if (pwm->lastcmd != ASYNC_CMD_OPEN2)
1415 pwm->pin_try = 0;
1417 pwm->cmd = ASYNC_CMD_OPEN2;
1418 pwm->state = ASYNC_PROCESS;
1419 gpg_error_t rc = do_pwmd_open(pwm, filename, 1, 1);
1421 if (rc)
1422 reset_async(pwm, 0);
1424 return rc;
1425 #endif
1428 static gpg_error_t do_pwmd_save(pwm_t *pwm, int nb, int local_pinentry)
1430 char *result = NULL;
1431 char *password = NULL;
1432 gpg_error_t rc;
1434 if (!pwm)
1435 return GPG_ERR_INV_ARG;
1437 if (!pwm->ctx)
1438 return GPG_ERR_INV_STATE;
1440 rc = pwmd_command(pwm, &result, "ISCACHED %s", pwm->filename);
1442 if (rc == GPG_ERR_ENOENT)
1443 rc = GPG_ERR_NOT_FOUND;
1445 if (rc && rc != GPG_ERR_NOT_FOUND)
1446 return rc;
1448 if (rc == GPG_ERR_NOT_FOUND) {
1449 if (pwm->password) {
1450 password = pwm->password;
1451 goto gotpassword;
1454 if (pwm->passfunc) {
1455 rc = pwm->passfunc(pwm->passdata, &password);
1457 if (rc)
1458 return rc;
1460 goto gotpassword;
1464 if (rc == GPG_ERR_NOT_FOUND && local_pinentry) {
1465 #ifdef WITH_PINENTRY
1466 /* Get the password using the LOCAL pinentry. */
1467 if (nb) {
1468 int p[2];
1469 pid_t pid;
1470 pwmd_nb_status_t pw;
1472 if (pipe(p) == -1)
1473 return gpg_error_from_syserror();
1475 #ifdef WITH_LIBPTH
1476 pid = pth_fork();
1477 #else
1478 pid = fork();
1479 #endif
1481 switch (pid) {
1482 case 0:
1483 close(p[0]);
1484 pw.fd = p[0];
1485 password = NULL;
1486 pw.error = _do_save_getpin(pwm, &password);
1488 if (!pw.error) {
1489 snprintf(pw.password, sizeof(pw.password), "%s",
1490 password);
1491 pwmd_free(password);
1493 #ifdef WITH_LIBPTH
1494 pth_write(p[1], &pw, sizeof(pw));
1495 #else
1496 write(p[1], &pw, sizeof(pw));
1497 #endif
1498 memset(&pw, 0, sizeof(pw));
1499 close(p[1]);
1500 _exit(0);
1501 break;
1502 case -1:
1503 rc = gpg_error_from_syserror();
1504 close(p[0]);
1505 close(p[1]);
1506 return rc;
1507 default:
1508 break;
1511 close(p[1]);
1512 pwm->nb_fd = p[0];
1513 pwm->nb_pid = pid;
1514 return 0;
1517 rc = _do_save_getpin(pwm, &password);
1519 if (rc)
1520 return rc;
1521 #endif
1523 else
1524 pwm->state = ASYNC_DONE;
1526 gotpassword:
1527 #ifdef WITH_TCP
1528 if (!local_pinentry && !pwm->tcp_conn) {
1529 #else
1530 if (!local_pinentry) {
1531 #endif
1532 rc = send_pinentry_options(pwm);
1534 if (rc)
1535 return rc;
1538 rc = pwmd_command(pwm, NULL, "SAVE %s", password ? password : "");
1540 if (!pwm->passfunc && password && password != pwm->password)
1541 pwmd_free(password);
1543 return rc;
1546 gpg_error_t pwmd_save_async2(pwm_t *pwm)
1548 #ifndef WITH_PINENTRY
1549 return GPG_ERR_NOT_IMPLEMENTED;
1550 #else
1551 if (!pwm)
1552 return GPG_ERR_INV_ARG;
1554 if (!pwm->ctx)
1555 return GPG_ERR_INV_STATE;
1557 if (pwm->cmd != ASYNC_CMD_NONE)
1558 return GPG_ERR_ASS_NESTED_COMMANDS;
1560 pwm->cmd = ASYNC_CMD_SAVE2;
1561 pwm->state = ASYNC_PROCESS;
1562 gpg_error_t rc = do_pwmd_save(pwm, 1, 1);
1564 if (rc)
1565 reset_async(pwm, 0);
1567 return rc;
1568 #endif
1571 gpg_error_t pwmd_save2(pwm_t *pwm)
1573 #ifndef WITH_PINENTRY
1574 return GPG_ERR_NOT_IMPLEMENTED;
1575 #else
1576 return do_pwmd_save(pwm, 0, 1);
1577 #endif
1580 gpg_error_t pwmd_save(pwm_t *pwm)
1582 return do_pwmd_save(pwm, 0, 0);
1585 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1587 va_list ap;
1588 int n = va_arg(ap, int);
1589 char *arg1;
1590 gpg_error_t rc = 0;
1592 if (!pwm)
1593 return GPG_ERR_INV_ARG;
1595 va_start(ap, opt);
1597 switch (opt) {
1598 case PWMD_OPTION_STATUS_CB:
1599 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
1600 break;
1601 case PWMD_OPTION_STATUS_DATA:
1602 pwm->status_data = va_arg(ap, void *);
1603 break;
1604 case PWMD_OPTION_PASSPHRASE_CB:
1605 pwm->passfunc = va_arg(ap, pwmd_passphrase_cb_t);
1606 break;
1607 case PWMD_OPTION_PASSPHRASE_DATA:
1608 pwm->passdata = va_arg(ap, void *);
1609 break;
1610 case PWMD_OPTION_PASSPHRASE:
1611 arg1 = va_arg(ap, char *);
1613 if (pwm->password)
1614 pwmd_free(pwm->password);
1616 pwm->password = pwmd_strdup(arg1);
1617 break;
1618 case PWMD_OPTION_PINENTRY_TRIES:
1619 n = va_arg(ap, int);
1621 if (n <= 0) {
1622 va_end(ap);
1623 rc = GPG_ERR_INV_VALUE;
1625 else
1626 pwm->pinentry_tries = n;
1627 break;
1628 case PWMD_OPTION_PINENTRY_TIMEOUT:
1629 n = va_arg(ap, int);
1631 if (n < 0) {
1632 va_end(ap);
1633 rc = GPG_ERR_INV_VALUE;
1635 else
1636 pwm->pinentry_timeout = n;
1637 break;
1638 case PWMD_OPTION_PINENTRY_PATH:
1639 if (pwm->pinentry_path)
1640 pwmd_free(pwm->pinentry_path);
1642 pwm->pinentry_path = _expand_homedir(va_arg(ap, char *), NULL);
1643 break;
1644 case PWMD_OPTION_PINENTRY_TTY:
1645 if (pwm->pinentry_tty)
1646 pwmd_free(pwm->pinentry_tty);
1648 pwm->pinentry_tty = pwmd_strdup(va_arg(ap, char *));
1649 break;
1650 case PWMD_OPTION_PINENTRY_DISPLAY:
1651 if (pwm->pinentry_display)
1652 pwmd_free(pwm->pinentry_display);
1654 pwm->pinentry_display = pwmd_strdup(va_arg(ap, char *));
1655 break;
1656 case PWMD_OPTION_PINENTRY_TERM:
1657 if (pwm->pinentry_term)
1658 pwmd_free(pwm->pinentry_term);
1660 pwm->pinentry_term = pwmd_strdup(va_arg(ap, char *));
1661 break;
1662 case PWMD_OPTION_PINENTRY_TITLE:
1663 if (pwm->title)
1664 pwmd_free(pwm->title);
1666 pwm->title = _percent_escape(va_arg(ap, char *));
1667 break;
1668 case PWMD_OPTION_PINENTRY_PROMPT:
1669 if (pwm->prompt)
1670 pwmd_free(pwm->prompt);
1672 pwm->prompt = _percent_escape(va_arg(ap, char *));
1673 break;
1674 case PWMD_OPTION_PINENTRY_DESC:
1675 if (pwm->desc)
1676 pwmd_free(pwm->desc);
1678 pwm->desc = _percent_escape(va_arg(ap, char *));
1679 break;
1680 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1681 if (pwm->lcctype)
1682 pwmd_free(pwm->lcctype);
1684 pwm->lcctype = pwmd_strdup(va_arg(ap, char *));
1685 break;
1686 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1687 if (pwm->lcmessages)
1688 pwmd_free(pwm->lcmessages);
1690 pwm->lcmessages = pwmd_strdup(va_arg(ap, char *));
1691 break;
1692 case PWMD_OPTION_IP_VERSION:
1693 #ifdef WITH_TCP
1694 n = va_arg(ap, int);
1696 switch (n) {
1697 case PWMD_IP_ANY:
1698 case PWMD_IPV4:
1699 case PWMD_IPV6:
1700 pwm->prot = n;
1701 break;
1702 default:
1703 rc = GPG_ERR_INV_VALUE;
1704 break;
1707 va_end(ap);
1708 #else
1709 rc = GPG_ERR_NOT_IMPLEMENTED;
1710 #endif
1711 break;
1712 default:
1713 rc = GPG_ERR_UNKNOWN_OPTION;
1714 break;
1717 va_end(ap);
1718 return rc;
1721 gpg_error_t pwmd_get_fds(pwm_t *pwm, pwmd_fd_t *fds, int *n_fds)
1723 int in_total;
1724 int fd = 0;
1725 #ifdef WITH_TCP
1726 int afds[ARES_GETSOCK_MAXNUM];
1727 int got_sock = 0;
1728 int n, i;
1729 #endif
1731 if (!pwm || !fds || !n_fds || *n_fds <= 0)
1732 return GPG_ERR_INV_ARG;
1734 in_total = *n_fds;
1735 #ifdef WITH_TCP
1736 memset(afds, 0, sizeof(int)*ARES_GETSOCK_MAXNUM);
1737 #endif
1738 memset(fds, 0, sizeof(pwmd_fd_t)*in_total);
1739 *n_fds = 0;
1741 switch (pwm->cmd) {
1742 default:
1743 case ASYNC_CMD_NONE:
1744 case ASYNC_CMD_OPEN:
1745 case ASYNC_CMD_SAVE:
1746 #ifdef WITH_PINENTRY
1747 async1:
1748 #endif
1749 if (pwm->fd == -1)
1750 return GPG_ERR_INV_STATE;
1752 (*n_fds)++;
1753 fds[fd].fd = pwm->fd;
1754 fds[fd++].flags = PWMD_FD_READABLE;
1755 return 0;
1756 #ifdef WITH_PINENTRY
1757 case ASYNC_CMD_OPEN2:
1758 case ASYNC_CMD_SAVE2:
1759 /* The command has already completed (cached or new). */
1760 if (pwm->state == ASYNC_DONE)
1761 return 0;
1763 if (pwm->nb_fd == -1)
1764 return GPG_ERR_INV_STATE;
1766 (*n_fds)++;
1767 fds[fd].fd = pwm->nb_fd;
1768 fds[fd++].flags = PWMD_FD_READABLE;
1769 goto async1;
1770 #endif
1771 #ifdef WITH_TCP
1772 case ASYNC_CMD_DNS:
1773 if (!pwm->tcp_conn || !pwm->tcp_conn->chan)
1774 return GPG_ERR_INV_STATE;
1776 n = ares_getsock(pwm->tcp_conn->chan, afds, ARES_GETSOCK_MAXNUM);
1778 for (i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
1779 got_sock = 0;
1781 if (fd > in_total) {
1782 *n_fds = fd;
1783 return GPG_ERR_ERANGE;
1786 if (ARES_GETSOCK_READABLE(n, i)) {
1787 got_sock++;
1788 fds[fd].flags |= PWMD_FD_READABLE;
1791 if (ARES_GETSOCK_WRITABLE(n, i)) {
1792 got_sock++;
1793 fds[fd].flags |= PWMD_FD_WRITABLE;
1796 if (got_sock)
1797 fds[fd++].fd = afds[i];
1800 *n_fds = fd;
1801 return 0;
1802 case ASYNC_CMD_CONNECT:
1803 case ASYNC_CMD_HOSTKEY:
1804 if (!pwm->tcp_conn || pwm->tcp_conn->fd == -1)
1805 return GPG_ERR_INV_STATE;
1807 (*n_fds)++;
1808 fds[fd].fd = pwm->tcp_conn->fd;
1809 fds[fd++].flags = PWMD_FD_READABLE;
1810 return 0;
1811 #endif
1814 return GPG_ERR_INV_STATE;
1817 pwm_t *pwmd_new(const char *name)
1819 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
1821 if (!h)
1822 return NULL;
1824 if (name) {
1825 h->name = pwmd_strdup(name);
1827 if (!h->name) {
1828 pwmd_free(h);
1829 return NULL;
1833 reset_handle(h);
1834 h->pinentry_timeout = -30;
1835 h->pinentry_tries = 3;
1836 #ifdef WITH_TCP
1837 h->prot = PWMD_IP_ANY;
1838 #endif
1840 if (ttyname(STDOUT_FILENO)) {
1841 char buf[256];
1843 ttyname_r(STDOUT_FILENO, buf, sizeof(buf));
1844 h->pinentry_tty = pwmd_strdup(buf);
1846 if (!h->pinentry_tty)
1847 goto fail;
1850 if (getenv("TERM") && h->pinentry_tty) {
1851 h->pinentry_term = pwmd_strdup(getenv("TERM"));
1853 if (!h->pinentry_term)
1854 goto fail;
1857 if (getenv("DISPLAY")) {
1858 h->pinentry_display = pwmd_strdup(getenv("DISPLAY"));
1860 if (!h->pinentry_display)
1861 goto fail;
1864 return h;
1866 fail:
1867 pwmd_close(h);
1868 return NULL;
1871 void pwmd_free(void *ptr)
1873 _xfree(ptr);
1876 void *pwmd_malloc(size_t size)
1878 return _xmalloc(size);
1881 void *pwmd_calloc(size_t nmemb, size_t size)
1883 return _xcalloc(nmemb, size);
1886 void *pwmd_realloc(void *ptr, size_t size)
1888 return _xrealloc(ptr, size);
1891 char *pwmd_strdup(const char *str)
1893 return _xstrdup(str);
1896 char *pwmd_strdup_printf(const char *fmt, ...)
1898 va_list ap, ap2;
1899 int len;
1900 char *buf;
1902 if (!fmt)
1903 return NULL;
1905 va_start(ap, fmt);
1906 va_copy(ap2, ap);
1907 len = vsnprintf(NULL, 0, fmt, ap);
1908 va_end(ap);
1909 buf = pwmd_malloc(++len);
1911 if (buf)
1912 vsnprintf(buf, len, fmt, ap2);
1914 va_end(ap2);
1915 return buf;