Update to use "OPTION disable-pinentry".
[libpwmd.git] / src / libpwmd.c
blob4d5e8dc33f5c03b3a14efe55893d6e0985f3da2b
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of libpwmd.
7 Libpwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Libpwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include <signal.h>
34 #include <stdarg.h>
35 #include <string.h>
36 #include <sys/wait.h>
37 #include <fcntl.h>
38 #include <pwd.h>
39 #include <time.h>
40 #include <sys/types.h>
41 #include <limits.h>
42 #include <sys/select.h>
43 #include <termios.h>
44 #include <libpwmd.h>
46 #ifdef HAVE_STRINGS_H
47 #include <strings.h>
48 #endif
50 #ifndef LINE_MAX
51 #define LINE_MAX 2048
52 #endif
54 #include "mem.h"
55 #include "misc.h"
56 #include "types.h"
58 #ifdef WITH_PINENTRY
59 #include "pinentry.h"
60 #endif
62 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
63 #include <sys/types.h>
64 #include <sys/socket.h>
65 #include <netdb.h>
66 #include <netinet/in.h>
67 #endif
69 #define FINISH(rc) (gpg_err_source(rc) == GPG_ERR_SOURCE_UNKNOWN) \
70 ? gpg_error(rc) : rc
72 typedef struct {
73 size_t len;
74 void *buf;
75 } membuf_t;
77 ssize_t hook_read(assuan_context_t ctx, assuan_fd_t fd, void *data,
78 size_t len)
80 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
81 pwm_t *pwm = assuan_get_pointer(ctx);
83 #ifdef WITH_SSH
84 if (pwm && pwm->tcp && pwm->tcp->ssh)
85 return read_hook_ssh(pwm->tcp->ssh, fd, data, len);
86 #endif
87 #ifdef WITH_GNUTLS
88 if (pwm && pwm->tcp && pwm->tcp->tls)
89 return read_hook_tls(pwm->tcp->tls, fd, data, len);
90 #endif
91 #endif
93 return read((int)fd, data, len);
96 ssize_t hook_write(assuan_context_t ctx, assuan_fd_t fd, const void *data,
97 size_t len)
99 ssize_t wrote;
100 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
101 pwm_t *pwm = assuan_get_pointer(ctx);
103 #ifdef WITH_SSH
104 if (pwm && pwm->tcp && pwm->tcp->ssh)
105 return write_hook_ssh(pwm->tcp->ssh, fd, data, len);
106 #endif
107 #ifdef WITH_GNUTLS
108 if (pwm && pwm->tcp && pwm->tcp->tls)
109 return write_hook_tls(pwm->tcp->tls, fd, data, len);
110 #endif
111 #endif
113 /* libassuan cannot handle EAGAIN when doing writes. */
114 do {
115 wrote = write((int)fd, data, len);
117 if (wrote == -1 && errno == EAGAIN) {
118 usleep(50000);
120 } while (wrote == -1 && errno == EAGAIN);
122 return wrote;
125 pid_t hook_waitpid(assuan_context_t ctx, pid_t pid, int action, int *status,
126 int options)
128 return waitpid(pid, status, options);
131 gpg_error_t pwmd_init()
133 static int initialized;
135 #ifdef WITH_GNUTLS
136 // May be called more than once.
137 gnutls_global_init();
138 #endif
140 if (initialized)
141 return 0;
143 #ifndef MEM_DEBUG
144 _xmem_init();
145 #endif
146 #ifdef ENABLE_NLS
147 bindtextdomain("libpwmd", LOCALEDIR);
148 #endif
149 #ifdef WITH_SSH
150 libssh2_init(0);
151 #endif
152 gpg_err_init();
153 initialized = 1;
154 return 0;
157 void pwmd_deinit()
159 #ifdef WITH_GNUTLS
160 gnutls_global_deinit();
161 #endif
164 gpg_error_t _connect_finalize(pwm_t *pwm)
166 gpg_error_t rc = 0;
167 int active[2];
168 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
170 if (n <= 0)
171 return GPG_ERR_EBADFD;
173 pwm->fd = active[0];
174 #ifdef WITH_PINENTRY
175 pwm->pinentry_pid = -1;
176 #endif
178 if (pwm->name)
179 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION NAME=%s", pwm->name);
181 return rc;
184 static gpg_error_t connect_uds(pwm_t *pwm, const char *path)
186 char *socketpath = NULL;
187 struct passwd pw;
188 char *pwbuf;
189 gpg_error_t rc;
191 if (!pwm)
192 return GPG_ERR_INV_ARG;
194 pwbuf = _getpwuid(&pw);
195 if (!pwbuf)
196 return gpg_error_from_syserror();
198 if (!path || !*path)
199 socketpath = pwmd_strdup_printf("%s/.pwmd/socket", pw.pw_dir);
200 else
201 socketpath = _expand_homedir((char *)path, &pw);
203 pwmd_free(pwbuf);
204 if (!socketpath)
205 return GPG_ERR_ENOMEM;
207 rc = assuan_socket_connect(pwm->ctx, socketpath, ASSUAN_INVALID_FD, 0);
208 pwmd_free(socketpath);
209 return rc ? rc : _connect_finalize(pwm);
212 static gpg_error_t init_handle(pwm_t *pwm)
214 gpg_error_t rc;
215 static struct assuan_malloc_hooks mhooks = {
216 pwmd_malloc, pwmd_realloc, pwmd_free
218 static struct assuan_system_hooks shooks = {
219 ASSUAN_SYSTEM_HOOKS_VERSION,
220 __assuan_usleep,
221 __assuan_pipe,
222 __assuan_close,
223 hook_read,
224 hook_write,
225 //FIXME
226 NULL, //recvmsg
227 NULL, //sendmsg both are used for FD passing
228 __assuan_spawn,
229 hook_waitpid,
230 __assuan_socketpair,
231 __assuan_socket,
232 __assuan_connect
235 rc = assuan_new_ext(&pwm->ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, NULL, NULL);
236 if (rc)
237 return rc;
239 assuan_set_pointer(pwm->ctx, pwm);
240 assuan_ctx_set_system_hooks(pwm->ctx, &shooks);
241 return 0;
244 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
245 void free_tcp(struct tcp_s *tcp)
247 if (!tcp)
248 return;
250 #ifdef WITH_SSH
251 _free_ssh_conn(tcp->ssh);
252 #endif
253 #ifdef WITH_GNUTLS
254 tls_free(tcp->tls);
255 #endif
257 pwmd_free(tcp->host);
258 if (tcp->addrs) {
259 freeaddrinfo(tcp->addrs);
260 tcp->addrs = NULL;
263 if (tcp->fd != -1)
264 close(tcp->fd);
266 pwmd_free(tcp);
268 #endif
270 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
271 gpg_error_t tcp_connect_common(pwm_t *pwm)
273 struct addrinfo hints = {0};
274 int n;
275 char portstr[6];
276 gpg_error_t rc = 0;
278 switch (pwm->prot) {
279 case PWMD_IP_ANY:
280 hints.ai_family = AF_UNSPEC;
281 break;
282 case PWMD_IPV4:
283 hints.ai_family = AF_INET;
284 break;
285 case PWMD_IPV6:
286 hints.ai_family = AF_INET6;
287 break;
290 hints.ai_socktype = SOCK_STREAM;
291 snprintf(portstr, sizeof(portstr), "%i", pwm->tcp->port);
292 n = getaddrinfo(pwm->tcp->host, portstr, &hints, &pwm->tcp->addrs);
293 if (n) {
294 fprintf(stderr, "%s\n", gai_strerror(n));
295 return GPG_ERR_UNKNOWN_HOST; //FIXME
298 for (pwm->tcp->addr = pwm->tcp->addrs; pwm->tcp->addr;
299 pwm->tcp->addr = pwm->tcp->addrs->ai_next) {
300 pwm->tcp->fd = socket(pwm->tcp->addr->ai_family, SOCK_STREAM, 0);
301 if (pwm->tcp->fd == -1) {
302 rc = gpg_error_from_syserror();
303 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
304 break;
305 continue;
308 if (connect(pwm->tcp->fd, pwm->tcp->addr->ai_addr,
309 pwm->tcp->addr->ai_family == AF_INET6
310 ? sizeof(struct sockaddr_in6)
311 : sizeof(struct sockaddr)) == -1) {
312 rc = gpg_error_from_syserror();
313 close(pwm->tcp->fd);
314 pwm->tcp->fd = -1;
315 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
316 break;
317 continue;
320 rc = 0;
321 break;
324 return rc;
326 #endif
328 gpg_error_t pwmd_connect(pwm_t *pwm, const char *url, ...)
330 const char *p = url;
331 gpg_error_t rc;
333 if (!pwm)
334 return FINISH(GPG_ERR_INV_ARG);
335 else if (!pwm->ctx) {
336 rc = init_handle(pwm);
337 if (rc)
338 return rc;
341 rc = GPG_ERR_UNSUPPORTED_PROTOCOL;
343 if (p && *p == '/')
344 rc = connect_uds(pwm, p);
345 else if (!p || !strncmp(p, "file://", 7)) {
346 if (p)
347 p += 7;
348 rc = connect_uds(pwm, p);
350 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
351 !strncmp(p, "ssh4://", 7)) {
352 #ifndef WITH_SSH
353 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
354 #else
355 char *host = NULL;
356 int port;
357 char *username = NULL;
359 if (!strncmp(p, "ssh6://", 7)) {
360 pwm->prot = PWMD_IPV6;
361 p += 7;
363 else if (!strncmp(p, "ssh4://", 7)) {
364 pwm->prot = PWMD_IPV4;
365 p += 7;
367 else {
368 pwm->prot = PWMD_IP_ANY;
369 p += 6;
372 rc = _parse_ssh_url(p, &host, &port, &username);
373 if (!rc) {
374 va_list ap;
375 char *identity = NULL;
376 char *knownhosts = NULL;
378 va_start(ap, url);
379 identity = va_arg(ap, char *);
381 if (!identity && !pwm->use_agent)
382 rc = GPG_ERR_INV_ARG;
383 else
384 knownhosts = va_arg(ap, char *);
386 va_end(ap);
388 if (!rc)
389 rc = _do_ssh_connect(pwm, host, port, identity, username,
390 knownhosts);
391 if (!rc) {
392 rc = _connect_finalize(pwm);
393 if (rc) {
394 free_tcp(pwm->tcp);
395 pwm->tcp = NULL;
400 pwmd_free(host);
401 pwmd_free(username);
402 return FINISH(rc);
403 #endif
405 else if (!strncmp(p, "tls://", 6) || !strncmp(p, "tls6://", 7) ||
406 !strncmp(p, "tls4://", 7)) {
407 #ifndef WITH_GNUTLS
408 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
409 #else
410 char *host = NULL;
411 int port;
413 if (!strncmp(p, "tls6://", 7)) {
414 pwm->prot = PWMD_IPV6;
415 p += 7;
417 else if (!strncmp(p, "tls4://", 7)) {
418 pwm->prot = PWMD_IPV4;
419 p += 7;
421 else {
422 pwm->prot = PWMD_IP_ANY;
423 p += 6;
426 rc = _parse_tls_url(p, &host, &port);
427 if (!rc) {
428 va_list ap;
429 char *clientcert = NULL;
430 char *clientkey = NULL;
431 char *cacert = NULL;
432 char *prio = NULL;
433 char *server_fp = NULL;
435 va_start(ap, url);
436 clientcert = va_arg(ap, char *);
438 if (!clientcert)
439 rc = GPG_ERR_INV_ARG;
440 else {
441 clientkey = va_arg(ap, char *);
442 if (!clientkey)
443 rc = GPG_ERR_INV_ARG;
444 else {
445 cacert = va_arg(ap, char *);
446 if (!cacert)
447 rc = GPG_ERR_INV_ARG;
448 else {
449 prio = va_arg(ap, char *);
450 server_fp = va_arg(ap, char *);
455 va_end(ap);
457 if (!rc)
458 rc = _do_tls_connect(pwm, host, port, clientcert, clientkey,
459 cacert, prio, server_fp, pwm->tls_verify);
460 if (!rc) {
461 rc = _connect_finalize(pwm);
462 if (rc) {
463 free_tcp(pwm->tcp);
464 pwm->tcp = NULL;
469 pwmd_free(host);
470 return FINISH(rc);
471 #endif
474 return FINISH(rc);
477 static void disconnect(pwm_t *pwm)
479 if (!pwm)
480 return;
482 if (pwm->ctx)
483 assuan_release(pwm->ctx);
485 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
486 free_tcp(pwm->tcp);
487 pwm->tcp = NULL;
488 #endif
489 pwm->ctx = NULL;
490 pwm->fd = -1;
493 void pwmd_close(pwm_t *pwm)
495 if (!pwm)
496 return;
498 disconnect(pwm);
499 pwmd_free(pwm->pinentry_error);
500 pwmd_free(pwm->pinentry_desc);
501 pwmd_free(pwm->pinentry_prompt);
502 pwmd_free(pwm->pinentry_tty);
503 pwmd_free(pwm->pinentry_display);
504 pwmd_free(pwm->pinentry_term);
505 pwmd_free(pwm->pinentry_lcctype);
506 pwmd_free(pwm->pinentry_lcmessages);
507 pwmd_free(pwm->filename);
508 pwmd_free(pwm->name);
510 #ifdef WITH_PINENTRY
511 if (pwm->pctx)
512 _pinentry_disconnect(pwm);
513 #endif
515 pwmd_free(pwm);
518 static gpg_error_t inquire_realloc_cb(void *data, const void *buffer,
519 size_t len)
521 membuf_t *mem = (membuf_t *)data;
522 void *p;
524 if (!buffer)
525 return 0;
527 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
528 return gpg_error(GPG_ERR_ENOMEM);
530 mem->buf = p;
531 memcpy((char *)mem->buf + mem->len, buffer, len);
532 mem->len += len;
533 return 0;
536 static gpg_error_t get_password(pwm_t *pwm, char **result, pwmd_pinentry_t w,
537 int echo)
539 char buf[LINE_MAX] = {0}, *p;
540 struct termios told, tnew;
541 char *key = NULL;
543 *result = NULL;
545 if (!isatty(STDIN_FILENO)) {
546 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
547 return GPG_ERR_ENOTTY;
550 if (!echo) {
551 if (tcgetattr(STDIN_FILENO, &told) == -1)
552 return gpg_error_from_syserror();
554 memcpy(&tnew, &told, sizeof(struct termios));
555 tnew.c_lflag &= ~(ECHO);
556 tnew.c_lflag |= ICANON|ECHONL;
558 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
559 int n = errno;
561 tcsetattr(STDIN_FILENO, TCSANOW, &told);
562 return gpg_error_from_errno(n);
566 switch (w) {
567 case PWMD_PINENTRY_OPEN:
568 fprintf(stderr, N_("Password for %s: "), pwm->filename);
569 break;
570 case PWMD_PINENTRY_OPEN_FAILED:
571 fprintf(stderr, N_("Invalid password. Password for %s: "),
572 pwm->filename);
573 break;
574 case PWMD_PINENTRY_SAVE:
575 fprintf(stderr, N_("New password for %s: "), pwm->filename);
576 break;
577 case PWMD_PINENTRY_SAVE_CONFIRM:
578 fprintf(stderr, N_("Confirm password: "));
579 break;
580 default:
581 break;
584 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
585 tcsetattr(STDIN_FILENO, TCSANOW, &told);
586 return 0;
589 if (!echo)
590 tcsetattr(STDIN_FILENO, TCSANOW, &told);
592 if (feof(stdin)) {
593 clearerr(stdin);
594 return GPG_ERR_CANCELED;
597 p[strlen(p) - 1] = 0;
599 if (buf[0]) {
600 key = pwmd_strdup_printf("%s", p);
601 memset(&buf, 0, sizeof(buf));
603 if (!key)
604 return GPG_ERR_ENOMEM;
607 *result = key;
608 return 0;
611 gpg_error_t pwmd_password(pwm_t *pwm, const char *keyword, char **data,
612 size_t *size)
614 gpg_error_t rc;
615 int new_password = 0;
616 size_t len;
617 char *password = NULL, *newpass = NULL;
618 int error = 0;
620 if (!strcmp(keyword, "NEW_PASSPHRASE"))
621 new_password = 1;
623 if (!new_password && pwm->pinentry_try)
624 error = 1;
626 again:
627 if (pwm->disable_pinentry) {
628 rc = get_password(pwm, &password,
629 new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN, 0);
630 if (!rc && new_password)
631 rc = get_password(pwm, &newpass, PWMD_PINENTRY_SAVE_CONFIRM, 0);
633 else {
634 pwmd_pinentry_t which;
636 if (error)
637 which = new_password ? PWMD_PINENTRY_SAVE_FAILED : PWMD_PINENTRY_OPEN_FAILED;
638 else
639 which = new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN;
641 rc = pwmd_getpin(pwm, pwm->filename, &password, &len, which);
642 if (!rc && new_password)
643 rc = pwmd_getpin(pwm, pwm->filename, &newpass, &len,
644 PWMD_PINENTRY_SAVE_CONFIRM);
647 if (!rc && new_password) {
648 if ((!password && newpass) || (!newpass && password)
649 || strcmp(newpass, password)) {
650 if (pwm->disable_pinentry)
651 fprintf(stderr, N_("Passphrases do not match.\n"));
653 pwmd_free(password);
654 pwmd_free(newpass);
655 password = newpass = NULL;
656 error = 1;
657 goto again;
661 (void)pwmd_getpin(pwm, pwm->filename, NULL, NULL, PWMD_PINENTRY_CLOSE);
662 pwmd_free(newpass);
663 if (!rc) {
664 // An empty passphrase on a protected key is not allowed by gpg-agent.
665 if (!password && !new_password)
666 rc = GPG_ERR_CANCELED;
667 else {
668 *data = password;
669 *size = password ? strlen(password) : 0;
673 return rc;
676 static gpg_error_t inquire_cb(void *data, const char *keyword)
678 pwm_t *pwm = (pwm_t *)data;
679 gpg_error_t rc = 0;
680 int free_result = 0;
681 char *result = NULL;
683 /* Shouldn't get this far without a callback. */
684 if (!pwm->override_inquire && !pwm->inquire_func)
685 return gpg_error(GPG_ERR_ASS_NO_INQUIRE_CB);
687 for (;;) {
688 size_t len = 0;
689 gpg_error_t arc;
690 int is_password = 0;
691 int new_password = 0;
693 result = NULL;
695 if (!strcmp(keyword, "PASSPHRASE"))
696 is_password = 1;
697 else if (!strcmp(keyword, "NEW_PASSPHRASE"))
698 new_password = 1;
700 if (!pwm->override_inquire && (is_password || new_password)) {
701 free_result = 1;
702 rc = pwmd_password(data, keyword, &result, &len);
703 if (!rc)
704 rc = GPG_ERR_EOF;
706 else
707 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result,
708 &len);
710 cancel:
711 if (rc && gpg_err_code(rc) != GPG_ERR_EOF) {
712 gpg_error_t trc = rc;
714 /* Cancel this inquire. */
715 rc = assuan_send_data(pwm->ctx, NULL, 1);
716 if (!rc) {
717 char *line;
718 size_t len;
720 /* There is a bug (or feature?) in assuan_send_data() that
721 * when cancelling an inquire the next read from the server is
722 * not done until the next command making the next command
723 * fail with GPG_ERR_ASS_UNEXPECTED_CMD.
725 rc = assuan_read_line(pwm->ctx, &line, &len);
727 /* Restore the original error. This differs from the error
728 * returned from the pwmd command (GPG_ERR_CANCELED). This
729 * error is returned to the calling function.
731 if (!rc)
732 rc = trc;
735 break;
738 if (gpg_err_code(rc) == GPG_ERR_EOF || !rc) {
739 if (len <= 0 && !result) {
740 rc = 0;
741 break;
743 else if ((len <= 0 && result) || (len && !result)) {
744 rc = gpg_error(GPG_ERR_INV_ARG);
745 break;
748 if (pwm->inquire_maxlen
749 && pwm->inquire_sent+len > pwm->inquire_maxlen) {
750 rc = gpg_error(GPG_ERR_TOO_LARGE);
751 if (!free_result)
752 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc,
753 &result, &len);
754 goto cancel;
757 arc = assuan_send_data(pwm->ctx, result, len);
758 if (gpg_err_code(rc) == GPG_ERR_EOF) {
759 rc = arc;
760 break;
763 rc = arc;
765 else if (rc)
766 break;
768 if (!rc) {
769 pwm->inquire_sent += len;
771 if (pwm->status_func) {
772 char buf[ASSUAN_LINELENGTH];
774 snprintf(buf, sizeof(buf), "XFER %lu %lu", pwm->inquire_sent,
775 pwm->inquire_total);
776 rc = pwm->status_func(pwm->status_data, buf);
777 if (rc)
778 continue;
783 if (free_result)
784 pwmd_free(result);
786 return rc;
789 static gpg_error_t parse_assuan_line(pwm_t *pwm)
791 gpg_error_t rc;
792 char *line;
793 size_t len;
795 rc = assuan_read_line(pwm->ctx, &line, &len);
796 if (!rc) {
797 if (line[0] == 'O' && line[1] == 'K' &&
798 (line[2] == 0 || line[2] == ' ')) {
800 else if (line[0] == '#') {
802 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
803 if (pwm->status_func) {
804 rc = pwm->status_func(pwm->status_data,
805 line[1] == 0 ? line+1 : line+2);
808 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
809 (line[3] == 0 || line[3] == ' ')) {
810 line += 4;
811 rc = strtol(line, NULL, 10);
815 return rc;
818 static void reset_handle_state(pwm_t *pwm, int done)
820 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
821 if (pwm->tcp)
822 pwm->tcp->rc = 0;
824 if (done) {
825 free_tcp(pwm->tcp);
826 pwm->tcp = NULL;
828 #endif
831 static void reset_handle(pwm_t *h)
833 h->fd = -1;
834 #ifdef WITH_PINENTRY
835 if (h->pctx)
836 _pinentry_disconnect(h);
837 #endif
838 reset_handle_state(h, 0);
841 gpg_error_t pwmd_disconnect(pwm_t *pwm)
843 if (!pwm)
844 return FINISH(GPG_ERR_INV_ARG);
846 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
847 if (pwm->fd == -1 && pwm->tcp && pwm->tcp->fd == -1)
848 #else
849 if (pwm->fd == -1)
850 #endif
851 return FINISH(GPG_ERR_INV_STATE);
853 if (pwm->fd != 1)
854 disconnect(pwm);
856 reset_handle(pwm);
857 return 0;
860 /* Note that this should only be called when not in a command. */
861 gpg_error_t pwmd_process(pwm_t *pwm)
863 gpg_error_t rc = 0;
864 fd_set fds;
865 struct timeval tv = {0, 0};
866 int n;
868 if (!pwm)
869 return FINISH(GPG_ERR_INV_ARG);
870 else if (!pwm->ctx)
871 return FINISH(GPG_ERR_INV_STATE);
873 #if defined(WITH_SSH)
874 if (pwm->tcp && pwm->tcp->ssh && pwm->keepalive_interval) {
875 int to;
876 int n = libssh2_keepalive_send(pwm->tcp->ssh->session, &to);
878 if (n)
879 return FINISH(GPG_ERR_ETIMEDOUT);
881 if (pwm->tcp->ssh->keepalive_prev &&
882 to > pwm->tcp->ssh->keepalive_prev) {
883 pwm->tcp->ssh->keepalive_prev = to;
884 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "NOP");
885 if (rc)
886 return FINISH(rc);
888 else
889 pwm->tcp->ssh->keepalive_prev = to;
891 #endif
893 FD_ZERO(&fds);
894 FD_SET(pwm->fd, &fds);
895 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
897 if (n == -1)
898 return FINISH(gpg_error_from_syserror());
900 if (n > 0) {
901 if (FD_ISSET(pwm->fd, &fds))
902 rc = parse_assuan_line(pwm);
905 while (!rc && assuan_pending_line(pwm->ctx))
906 rc = parse_assuan_line(pwm);
908 return FINISH(rc);
911 static gpg_error_t status_cb(void *data, const char *line)
913 pwm_t *pwm = data;
915 if (!strncmp(line, "INQUIRE_MAXLEN ", 15))
916 pwm->inquire_maxlen = strtol(line+15, NULL, 10);
918 if (pwm->status_func)
919 return pwm->status_func(pwm->status_data, line);
921 return 0;
924 gpg_error_t _assuan_command(pwm_t *pwm, assuan_context_t ctx,
925 char **result, size_t *len, const char *cmd)
927 membuf_t data;
928 gpg_error_t rc;
930 if (!cmd || !*cmd)
931 return FINISH(GPG_ERR_INV_ARG);
933 if (strlen(cmd) >= ASSUAN_LINELENGTH+1)
934 return FINISH(GPG_ERR_LINE_TOO_LONG);
936 data.len = 0;
937 data.buf = NULL;
938 rc = assuan_transact(ctx, cmd, inquire_realloc_cb, &data,
939 #ifdef WITH_QUALITY
940 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
941 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
942 #else
943 inquire_cb, pwm,
944 #endif
945 status_cb, pwm);
947 if (rc) {
948 if (data.buf) {
949 pwmd_free(data.buf);
950 data.buf = NULL;
953 else {
954 if (data.buf) {
955 inquire_realloc_cb(&data, "", 1);
957 if (result)
958 *result = (char *)data.buf;
959 else
960 pwmd_free(data.buf);
962 if (len)
963 *len = data.len;
967 pwm->inquire_maxlen = 0;
968 return rc;
971 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, size_t *rlen,
972 pwmd_inquire_cb_t func, void *user, const char *cmd, va_list ap)
974 char *buf;
975 size_t len;
976 va_list ap2;
978 if (!pwm || !cmd)
979 return FINISH(GPG_ERR_INV_ARG);
980 if (!pwm->ctx)
981 return FINISH(GPG_ERR_INV_STATE);
984 * C99 allows the dst pointer to be null which will calculate the length
985 * of the would-be result and return it.
987 va_copy(ap2, ap);
988 len = vsnprintf(NULL, 0, cmd, ap)+1;
989 buf = (char *)pwmd_malloc(len);
990 if (!buf) {
991 va_end(ap2);
992 return FINISH(GPG_ERR_ENOMEM);
995 len = vsnprintf(buf, len, cmd, ap2);
996 va_end(ap2);
998 if (buf[strlen(buf)-1] == '\n')
999 buf[strlen(buf)-1] = 0;
1000 if (buf[strlen(buf)-1] == '\r')
1001 buf[strlen(buf)-1] = 0;
1003 pwm->inquire_func = func;
1004 pwm->inquire_data = user;
1005 pwm->inquire_sent = 0;
1006 gpg_error_t rc = _assuan_command(pwm, pwm->ctx, result, rlen, buf);
1007 pwmd_free(buf);
1008 return rc;
1011 gpg_error_t pwmd_command(pwm_t *pwm, char **result, size_t *len,
1012 pwmd_inquire_cb_t func, void *user, const char *cmd, ...)
1014 va_list ap;
1016 if (!pwm || !cmd)
1017 return FINISH(GPG_ERR_INV_ARG);
1018 if (!pwm->ctx)
1019 return FINISH(GPG_ERR_INV_STATE);
1021 if (result)
1022 *result = NULL;
1024 va_start(ap, cmd);
1025 gpg_error_t rc = pwmd_command_ap(pwm, result, len, func, user, cmd, ap);
1026 va_end(ap);
1027 return rc;
1030 static gpg_error_t send_pinentry_options(pwm_t *pwm)
1032 gpg_error_t rc;
1034 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION disable-pinentry=0");
1035 if (rc)
1036 return rc;
1038 if (pwm->pinentry_tty) {
1039 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
1040 if (rc)
1041 return rc;
1044 if (pwm->pinentry_term) {
1045 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
1046 if (rc)
1047 return rc;
1050 if (pwm->pinentry_display) {
1051 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DISPLAY=%s",
1052 pwm->pinentry_display);
1053 if (rc)
1054 return rc;
1057 if (pwm->pinentry_desc) {
1058 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DESC=%s",
1059 pwm->pinentry_desc);
1060 if (rc)
1061 return rc;
1064 if (pwm->pinentry_lcctype) {
1065 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_CTYPE=%s",
1066 pwm->pinentry_lcctype);
1067 if (rc)
1068 return rc;
1071 if (pwm->pinentry_lcmessages) {
1072 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_MESSAGES=%s",
1073 pwm->pinentry_lcmessages);
1074 if (rc)
1075 return rc;
1078 return 0;
1081 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
1083 if (!pwm || !result)
1084 return FINISH(GPG_ERR_INV_ARG);
1086 *result = PWMD_SOCKET_LOCAL;
1088 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1089 if ((pwm->fd == -1 && (!pwm->tcp
1090 #ifdef WITH_SSH
1091 || !pwm->tcp->ssh
1092 #endif
1093 #ifdef WITH_GNUTLS
1094 || !pwm->tcp->tls
1095 #endif
1096 )) ||
1097 (pwm->fd == -1 && pwm->tcp && pwm->tcp->fd == -1))
1098 return FINISH(GPG_ERR_INV_STATE);
1099 #else
1100 if (pwm->fd == -1)
1101 return FINISH(GPG_ERR_INV_STATE);
1102 #endif
1104 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1105 #ifdef WITH_SSH
1106 if (pwm->tcp && pwm->tcp->ssh)
1107 *result = PWMD_SOCKET_SSH;
1108 #endif
1109 #ifdef WITH_GNUTLS
1110 if (pwm->tcp && pwm->tcp->tls)
1111 *result = PWMD_SOCKET_TLS;
1112 #endif
1113 #endif
1114 return 0;
1117 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename, pwmd_inquire_cb_t cb,
1118 void *data)
1120 gpg_error_t rc = 0;
1121 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1122 int no_pinentry = pwm->disable_pinentry || pwm->tcp || pwm->local_pinentry;
1123 #else
1124 int no_pinentry = pwm->disable_pinentry || pwm->local_pinentry;
1125 #endif
1127 if (!pwm || !filename || !*filename)
1128 return FINISH(GPG_ERR_INV_ARG);
1130 if (!pwm->ctx)
1131 return FINISH(GPG_ERR_INV_STATE);
1133 if (!no_pinentry)
1134 rc = send_pinentry_options(pwm);
1135 else
1136 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION disable-pinentry");
1138 if (!rc) {
1139 pwm->pinentry_try = 0;
1140 pwmd_free(pwm->filename);
1141 pwm->filename = pwmd_strdup(filename);
1143 do {
1144 rc = pwmd_command(pwm, NULL, NULL, cb, data, "OPEN %s%s",
1145 (pwm->opts & OPT_LOCK_ON_OPEN) ? "--lock " : "",
1146 filename);
1147 } while (gpg_err_code(rc) == GPG_ERR_BAD_PASSPHRASE
1148 && no_pinentry && ++pwm->pinentry_try < pwm->pinentry_tries);
1150 pwm->pinentry_try = 0;
1152 if (rc) {
1153 pwmd_free(pwm->filename);
1154 pwm->filename = NULL;
1158 return FINISH(rc);
1161 gpg_error_t pwmd_save(pwm_t *pwm, const char *args, pwmd_inquire_cb_t cb,
1162 void *data)
1164 gpg_error_t rc;
1166 if (!pwm)
1167 return FINISH(GPG_ERR_INV_ARG);
1168 if (!pwm->ctx)
1169 return FINISH(GPG_ERR_INV_STATE);
1171 rc = pwmd_command(pwm, NULL, NULL, cb, data, "SAVE %s",
1172 args ? args : "");
1173 return FINISH(rc);
1176 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1178 va_list ap;
1179 int n;
1180 char *arg1;
1181 gpg_error_t rc = 0;
1183 if (!pwm)
1184 return FINISH(GPG_ERR_INV_ARG);
1186 va_start(ap, opt);
1188 switch (opt) {
1189 case PWMD_OPTION_LOCK_ON_OPEN:
1190 n = va_arg(ap, int);
1192 if (n < 0 || n > 1)
1193 rc = GPG_ERR_INV_VALUE;
1195 if (n)
1196 pwm->opts |= OPT_LOCK_ON_OPEN;
1197 else
1198 pwm->opts &= ~OPT_LOCK_ON_OPEN;
1200 break;
1201 case PWMD_OPTION_INQUIRE_TOTAL:
1202 pwm->inquire_total = va_arg(ap, size_t);
1203 break;
1204 case PWMD_OPTION_STATUS_CB:
1205 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
1206 break;
1207 case PWMD_OPTION_STATUS_DATA:
1208 pwm->status_data = va_arg(ap, void *);
1209 break;
1210 case PWMD_OPTION_NO_PINENTRY:
1211 n = va_arg(ap, int);
1213 if (n < 0 || n > 1)
1214 rc = GPG_ERR_INV_VALUE;
1215 else
1216 pwm->disable_pinentry = n;
1218 break;
1219 case PWMD_OPTION_LOCAL_PINENTRY:
1220 n = va_arg(ap, int);
1222 if (n < 0 || n > 1)
1223 rc = GPG_ERR_INV_VALUE;
1224 else
1225 pwm->local_pinentry = n;
1227 break;
1228 case PWMD_OPTION_PINENTRY_TIMEOUT:
1229 n = va_arg(ap, int);
1231 if (n < 0)
1232 rc = GPG_ERR_INV_VALUE;
1233 else
1234 pwm->pinentry_timeout = n;
1236 break;
1237 case PWMD_OPTION_PINENTRY_TRIES:
1238 n = va_arg(ap, int);
1239 pwm->pinentry_tries = n;
1240 break;
1241 case PWMD_OPTION_PINENTRY_PATH:
1242 arg1 = va_arg(ap, char *);
1243 pwmd_free(pwm->pinentry_path);
1244 pwm->pinentry_path = arg1 ? _expand_homedir(arg1, NULL) : NULL;
1245 break;
1246 case PWMD_OPTION_PINENTRY_TTY:
1247 arg1 = va_arg(ap, char *);
1248 pwmd_free(pwm->pinentry_tty);
1249 pwm->pinentry_tty = arg1 ? pwmd_strdup(arg1) : NULL;
1250 break;
1251 case PWMD_OPTION_PINENTRY_DISPLAY:
1252 arg1 = va_arg(ap, char *);
1253 pwmd_free(pwm->pinentry_display);
1254 pwm->pinentry_display = arg1 ? pwmd_strdup(arg1) : NULL;
1255 break;
1256 case PWMD_OPTION_PINENTRY_TERM:
1257 arg1 = va_arg(ap, char *);
1258 pwmd_free(pwm->pinentry_term);
1259 pwm->pinentry_term = arg1 ? pwmd_strdup(arg1) : NULL;
1260 break;
1261 case PWMD_OPTION_PINENTRY_ERROR:
1262 arg1 = va_arg(ap, char *);
1263 pwmd_free(pwm->pinentry_error);
1264 pwm->pinentry_error = arg1 ? _percent_escape(arg1) : NULL;
1265 break;
1266 case PWMD_OPTION_PINENTRY_PROMPT:
1267 arg1 = va_arg(ap, char *);
1268 pwmd_free(pwm->pinentry_prompt);
1269 pwm->pinentry_prompt = arg1 ? _percent_escape(arg1) : NULL;
1270 break;
1271 case PWMD_OPTION_PINENTRY_DESC:
1272 arg1 = va_arg(ap, char *);
1273 pwmd_free(pwm->pinentry_desc);
1274 pwm->pinentry_desc = arg1 ? _percent_escape(arg1) : NULL;
1275 break;
1276 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1277 arg1 = va_arg(ap, char *);
1278 pwmd_free(pwm->pinentry_lcctype);
1279 pwm->pinentry_lcctype = arg1 ? pwmd_strdup(arg1) : NULL;
1280 break;
1281 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1282 arg1 = va_arg(ap, char *);
1283 pwmd_free(pwm->pinentry_lcmessages);
1284 pwm->pinentry_lcmessages = arg1 ? pwmd_strdup(arg1) : NULL;
1285 break;
1286 #ifdef WITH_SSH
1287 case PWMD_OPTION_KNOWNHOST_CB:
1288 pwm->kh_cb = va_arg(ap, pwmd_knownhost_cb_t);
1289 break;
1290 case PWMD_OPTION_KNOWNHOST_DATA:
1291 pwm->kh_data = va_arg(ap, void *);
1292 break;
1293 case PWMD_OPTION_SSH_AGENT:
1294 pwm->use_agent = va_arg(ap, int);
1296 if (pwm->use_agent < 0 || pwm->use_agent > 1) {
1297 pwm->use_agent = 0;
1298 rc = GPG_ERR_INV_VALUE;
1301 break;
1302 case PWMD_OPTION_SSH_TIMEOUT:
1303 pwm->ssh_timeout = va_arg(ap, int);
1305 if (pwm->ssh_timeout < 0) {
1306 pwm->ssh_timeout = 0;
1307 rc = GPG_ERR_INV_VALUE;
1309 else if (pwm->tcp && pwm->tcp->ssh && pwm->tcp->ssh->session)
1310 libssh2_session_set_timeout(pwm->tcp->ssh->session,
1311 pwm->ssh_timeout*1000);
1313 break;
1314 case PWMD_OPTION_SSH_KEEPALIVE:
1315 pwm->keepalive_interval = va_arg(ap, int);
1317 if (pwm->keepalive_interval < 0) {
1318 pwm->keepalive_interval = 0;
1319 rc = GPG_ERR_INV_VALUE;
1321 else if (pwm->tcp && pwm->tcp->ssh && pwm->tcp->ssh->session)
1322 libssh2_keepalive_config(pwm->tcp->ssh->session, 1,
1323 pwm->keepalive_interval);
1325 break;
1326 #else
1327 case PWMD_OPTION_KNOWNHOST_CB:
1328 case PWMD_OPTION_KNOWNHOST_DATA:
1329 case PWMD_OPTION_SSH_AGENT:
1330 case PWMD_OPTION_SSH_TIMEOUT:
1331 rc = GPG_ERR_NOT_IMPLEMENTED;
1332 break;
1333 #endif
1334 #ifdef WITH_GNUTLS
1335 case PWMD_OPTION_TLS_VERIFY:
1336 pwm->tls_verify = va_arg(ap, int);
1338 if (pwm->tls_verify < 0 || pwm->tls_verify > 1) {
1339 pwm->tls_verify = 0;
1340 rc = GPG_ERR_INV_VALUE;
1342 break;
1343 #endif
1344 case PWMD_OPTION_OVERRIDE_INQUIRE:
1345 pwm->override_inquire = va_arg(ap, int);
1347 if (pwm->override_inquire < 0 || pwm->override_inquire > 1) {
1348 pwm->override_inquire = 0;
1349 rc = GPG_ERR_INV_VALUE;
1351 break;
1352 default:
1353 rc = GPG_ERR_UNKNOWN_OPTION;
1354 break;
1357 va_end(ap);
1358 return FINISH(rc);
1361 gpg_error_t pwmd_new(const char *name, pwm_t **pwm)
1363 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
1364 gpg_error_t rc;
1366 if (!h)
1367 return FINISH(GPG_ERR_ENOMEM);
1369 if (name) {
1370 h->name = pwmd_strdup(name);
1371 if (!h->name) {
1372 pwmd_free(h);
1373 return FINISH(GPG_ERR_ENOMEM);
1377 reset_handle(h);
1378 h->pinentry_timeout = -30;
1379 h->pinentry_tries = 3;
1380 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1381 h->prot = PWMD_IP_ANY;
1382 #endif
1384 if (ttyname(STDOUT_FILENO)) {
1385 char buf[256];
1387 ttyname_r(STDOUT_FILENO, buf, sizeof(buf));
1388 h->pinentry_tty = pwmd_strdup(buf);
1389 if (!h->pinentry_tty) {
1390 rc = GPG_ERR_ENOMEM;
1391 goto fail;
1395 if (getenv("TERM") && h->pinentry_tty) {
1396 h->pinentry_term = pwmd_strdup(getenv("TERM"));
1397 if (!h->pinentry_term) {
1398 rc = GPG_ERR_ENOMEM;
1399 goto fail;
1403 if (getenv("DISPLAY")) {
1404 h->pinentry_display = pwmd_strdup(getenv("DISPLAY"));
1405 if (!h->pinentry_display) {
1406 rc = GPG_ERR_ENOMEM;
1407 goto fail;
1411 update_pinentry_settings(h);
1412 *pwm = h;
1413 return 0;
1415 fail:
1416 pwmd_close(h);
1417 return FINISH(rc);
1420 void pwmd_free(void *ptr)
1422 _xfree(ptr);
1425 void *pwmd_malloc(size_t size)
1427 return _xmalloc(size);
1430 void *pwmd_calloc(size_t nmemb, size_t size)
1432 return _xcalloc(nmemb, size);
1435 void *pwmd_realloc(void *ptr, size_t size)
1437 return _xrealloc(ptr, size);
1440 char *pwmd_strdup(const char *str)
1442 char *t;
1443 size_t len;
1444 register size_t c;
1446 len = strlen(str);
1447 t = _xmalloc((len+1)*sizeof(char));
1448 if (!t)
1449 return NULL;
1451 for (c = 0; c < len; c++)
1452 t[c] = str[c];
1454 t[c] = 0;
1455 return t;
1458 char *pwmd_strdup_printf(const char *fmt, ...)
1460 va_list ap, ap2;
1461 int len;
1462 char *buf;
1464 if (!fmt)
1465 return NULL;
1467 va_start(ap, fmt);
1468 va_copy(ap2, ap);
1469 len = vsnprintf(NULL, 0, fmt, ap);
1470 va_end(ap);
1471 buf = pwmd_malloc(++len);
1472 if (buf)
1473 vsnprintf(buf, len, fmt, ap2);
1475 va_end(ap2);
1476 return buf;
1479 gpg_error_t pwmd_getpin(pwm_t *pwm, const char *filename, char **result,
1480 size_t *len, pwmd_pinentry_t which)
1482 #ifndef WITH_PINENTRY
1483 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
1484 #else
1485 gpg_error_t rc = _pwmd_getpin(pwm, filename, result, len, which);
1487 return FINISH(rc);
1488 #endif
1491 const char *pwmd_version()
1493 return LIBPWMD_VERSION_STR;
1496 unsigned int pwmd_features()
1498 unsigned int n = 0;
1500 #ifdef WITH_PINENTRY
1501 n |= PWMD_FEATURE_PINENTRY;
1502 #endif
1503 #ifdef WITH_SSH
1504 n |= PWMD_FEATURE_SSH;
1505 #endif
1506 #ifdef WITH_QUALITY
1507 n |= PWMD_FEATURE_CRACK;
1508 #endif
1509 #ifdef WITH_GNUTLS
1510 n |= PWMD_FEATURE_GNUTLS;
1511 #endif
1512 return n;
1515 gpg_error_t pwmd_fd(pwm_t *pwm, int *fd)
1517 if (!pwm || !fd)
1518 return FINISH(GPG_ERR_INV_ARG);
1520 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1521 if (pwm->tcp && pwm->tcp->fd == -1 && pwm->fd == -1)
1522 return GPG_ERR_INV_STATE;
1524 *fd = pwm->tcp && pwm->tcp->fd != -1 ? pwm->tcp->fd : pwm->fd;
1525 #else
1526 if (pwm->fd == -1)
1527 return FINISH(GPG_ERR_INV_STATE);
1529 *fd = pwm->fd;
1530 #endif
1532 return 0;