Fix GnuTLS and SSH conditionals.
[libpwmd.git] / src / libpwmd.c
blob18f81cb71afa5913b12e358dc6337857a1d008cf
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of libpwmd.
8 Libpwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Libpwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <err.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <signal.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <sys/wait.h>
38 #include <fcntl.h>
39 #include <pwd.h>
40 #include <time.h>
41 #include <sys/types.h>
42 #include <limits.h>
43 #include <sys/select.h>
44 #include <termios.h>
45 #include <libpwmd.h>
47 #ifdef HAVE_ASSUAN_H
48 #include <assuan.h>
49 #endif
51 #ifndef LINE_MAX
52 #define LINE_MAX 2048
53 #endif
55 #include "mem.h"
56 #include "misc.h"
57 #include "types.h"
59 #ifdef WITH_PINENTRY
60 #include "pinentry.h"
61 #endif
63 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
64 #include <sys/types.h>
65 #include <sys/socket.h>
66 #include <netdb.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 if (initialized)
136 return 0;
138 #ifndef MEM_DEBUG
139 _xmem_init();
140 #endif
141 #ifdef ENABLE_NLS
142 bindtextdomain("libpwmd", LOCALEDIR);
143 #endif
144 #ifdef WITH_SSH
145 libssh2_init(0);
146 #endif
147 #ifdef WITH_GNUTLS
148 gnutls_global_init();
149 #endif
150 gpg_err_init();
151 initialized = 1;
152 return 0;
155 gpg_error_t _connect_finalize(pwm_t *pwm)
157 gpg_error_t rc = 0;
158 int active[2];
159 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
161 if (n <= 0)
162 return GPG_ERR_EBADFD;
164 pwm->fd = active[0];
165 #ifdef WITH_PINENTRY
166 pwm->pinentry_pid = -1;
167 #endif
169 if (pwm->name)
170 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION NAME=%s", pwm->name);
172 return rc;
175 static gpg_error_t connect_uds(pwm_t *pwm, const char *path)
177 char *socketpath = NULL;
178 struct passwd pw;
179 char *pwbuf;
180 gpg_error_t rc;
182 if (!pwm)
183 return GPG_ERR_INV_ARG;
185 pwbuf = _getpwuid(&pw);
186 if (!pwbuf)
187 return gpg_error_from_syserror();
189 if (!path || !*path)
190 socketpath = pwmd_strdup_printf("%s/.pwmd/socket", pw.pw_dir);
191 else
192 socketpath = _expand_homedir((char *)path, &pw);
194 pwmd_free(pwbuf);
195 if (!socketpath)
196 return GPG_ERR_ENOMEM;
198 rc = assuan_socket_connect(pwm->ctx, socketpath, ASSUAN_INVALID_FD, 0);
199 pwmd_free(socketpath);
200 return rc ? rc : _connect_finalize(pwm);
203 static gpg_error_t init_handle(pwm_t *pwm)
205 gpg_error_t rc;
206 static struct assuan_malloc_hooks mhooks = {
207 pwmd_malloc, pwmd_realloc, pwmd_free
209 static struct assuan_system_hooks shooks = {
210 ASSUAN_SYSTEM_HOOKS_VERSION,
211 __assuan_usleep,
212 __assuan_pipe,
213 __assuan_close,
214 hook_read,
215 hook_write,
216 //FIXME
217 NULL, //recvmsg
218 NULL, //sendmsg both are used for FD passing
219 __assuan_spawn,
220 hook_waitpid,
221 __assuan_socketpair,
222 __assuan_socket,
223 __assuan_connect
226 rc = assuan_new_ext(&pwm->ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, NULL, NULL);
227 if (rc)
228 return rc;
230 assuan_set_pointer(pwm->ctx, pwm);
231 assuan_ctx_set_system_hooks(pwm->ctx, &shooks);
232 return 0;
235 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
236 void free_tcp(struct tcp_s *tcp)
238 if (!tcp)
239 return;
241 #ifdef WITH_SSH
242 _free_ssh_conn(tcp->ssh);
243 #endif
244 #ifdef WITH_GNUTLS
245 tls_free(tcp->tls);
246 #endif
248 pwmd_free(tcp->host);
249 if (tcp->addrs) {
250 freeaddrinfo(tcp->addrs);
251 tcp->addrs = NULL;
254 if (tcp->fd != -1)
255 close(tcp->fd);
257 pwmd_free(tcp);
259 #endif
261 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
262 gpg_error_t tcp_connect_common(pwm_t *pwm)
264 struct addrinfo hints = {0};
265 int n;
266 char portstr[6];
267 gpg_error_t rc = 0;
269 switch (pwm->prot) {
270 case PWMD_IP_ANY:
271 hints.ai_family = AF_UNSPEC;
272 break;
273 case PWMD_IPV4:
274 hints.ai_family = AF_INET;
275 break;
276 case PWMD_IPV6:
277 hints.ai_family = AF_INET6;
278 break;
281 hints.ai_socktype = SOCK_STREAM;
282 snprintf(portstr, sizeof(portstr), "%i", pwm->tcp->port);
283 n = getaddrinfo(pwm->tcp->host, portstr, &hints, &pwm->tcp->addrs);
284 if (n) {
285 fprintf(stderr, "%s\n", gai_strerror(n));
286 return GPG_ERR_UNKNOWN_HOST; //FIXME
289 for (pwm->tcp->addr = pwm->tcp->addrs; pwm->tcp->addr;
290 pwm->tcp->addr = pwm->tcp->addrs->ai_next) {
291 pwm->tcp->fd = socket(pwm->tcp->addr->ai_family, SOCK_STREAM, 0);
292 if (pwm->tcp->fd == -1) {
293 rc = gpg_error_from_syserror();
294 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
295 break;
296 continue;
299 if (connect(pwm->tcp->fd, pwm->tcp->addr->ai_addr,
300 pwm->tcp->addr->ai_family == AF_INET6
301 ? sizeof(struct sockaddr_in6)
302 : sizeof(struct sockaddr)) == -1) {
303 rc = gpg_error_from_syserror();
304 close(pwm->tcp->fd);
305 pwm->tcp->fd = -1;
306 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
307 break;
308 continue;
311 rc = 0;
312 break;
315 return rc;
317 #endif
319 gpg_error_t pwmd_connect(pwm_t *pwm, const char *url, ...)
321 const char *p = url;
322 gpg_error_t rc;
324 if (!pwm)
325 return FINISH(GPG_ERR_INV_ARG);
326 else if (!pwm->ctx) {
327 rc = init_handle(pwm);
328 if (rc)
329 return rc;
332 rc = GPG_ERR_UNSUPPORTED_PROTOCOL;
334 if (p && *p == '/')
335 rc = connect_uds(pwm, p);
336 else if (!p || !strncmp(p, "file://", 7)) {
337 if (p)
338 p += 7;
339 rc = connect_uds(pwm, p);
341 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
342 !strncmp(p, "ssh4://", 7)) {
343 #ifndef WITH_SSH
344 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
345 #else
346 char *host = NULL;
347 int port;
348 char *username = NULL;
350 if (!strncmp(p, "ssh6://", 7)) {
351 pwm->prot = PWMD_IPV6;
352 p += 7;
354 else if (!strncmp(p, "ssh4://", 7)) {
355 pwm->prot = PWMD_IPV4;
356 p += 7;
358 else {
359 pwm->prot = PWMD_IP_ANY;
360 p += 6;
363 /* X11 forwarding is not supported. */
364 pwmd_setopt(pwm, PWMD_OPTION_NO_PINENTRY, 1);
365 rc = _parse_ssh_url(p, &host, &port, &username);
366 if (!rc) {
367 va_list ap;
368 char *identity = NULL;
369 char *knownhosts = NULL;
371 va_start(ap, url);
372 identity = va_arg(ap, char *);
374 if (!identity && !pwm->use_agent)
375 rc = GPG_ERR_INV_ARG;
376 else
377 knownhosts = va_arg(ap, char *);
379 va_end(ap);
381 if (!rc)
382 rc = _do_ssh_connect(pwm, host, port, identity, username,
383 knownhosts);
384 if (!rc) {
385 rc = _connect_finalize(pwm);
386 if (rc) {
387 free_tcp(pwm->tcp);
388 pwm->tcp = NULL;
393 pwmd_free(host);
394 pwmd_free(username);
395 return FINISH(rc);
396 #endif
398 else if (!strncmp(p, "tls://", 6) || !strncmp(p, "tls6://", 7) ||
399 !strncmp(p, "tls4://", 7)) {
400 #ifndef WITH_GNUTLS
401 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
402 #else
403 char *host = NULL;
404 int port;
406 if (!strncmp(p, "tls6://", 7)) {
407 pwm->prot = PWMD_IPV6;
408 p += 7;
410 else if (!strncmp(p, "tls4://", 7)) {
411 pwm->prot = PWMD_IPV4;
412 p += 7;
414 else {
415 pwm->prot = PWMD_IP_ANY;
416 p += 6;
419 /* X11 forwarding is not supported. */
420 pwmd_setopt(pwm, PWMD_OPTION_NO_PINENTRY, 1);
421 rc = _parse_tls_url(p, &host, &port);
422 if (!rc) {
423 va_list ap;
424 char *clientcert = NULL;
425 char *clientkey = NULL;
426 char *cacert = NULL;
427 char *prio = NULL;
429 va_start(ap, url);
430 clientcert = va_arg(ap, char *);
432 if (!clientcert)
433 rc = GPG_ERR_INV_ARG;
434 else {
435 clientkey = va_arg(ap, char *);
436 if (!clientkey)
437 rc = GPG_ERR_INV_ARG;
438 else {
439 cacert = va_arg(ap, char *);
440 if (!cacert)
441 rc = GPG_ERR_INV_ARG;
442 else
443 prio = va_arg(ap, char *);
447 va_end(ap);
449 if (!rc)
450 rc = _do_tls_connect(pwm, host, port, clientcert, clientkey,
451 cacert, prio, pwm->tls_verify);
452 if (!rc) {
453 rc = _connect_finalize(pwm);
454 if (rc) {
455 free_tcp(pwm->tcp);
456 pwm->tcp = NULL;
461 pwmd_free(host);
462 return FINISH(rc);
463 #endif
466 return FINISH(rc);
469 static void disconnect(pwm_t *pwm)
471 if (!pwm)
472 return;
474 if (pwm->ctx)
475 assuan_release(pwm->ctx);
477 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
478 free_tcp(pwm->tcp);
479 pwm->tcp = NULL;
480 #endif
481 pwm->ctx = NULL;
482 pwm->fd = -1;
485 void pwmd_close(pwm_t *pwm)
487 if (!pwm)
488 return;
490 disconnect(pwm);
491 pwmd_free(pwm->pinentry_error);
492 pwmd_free(pwm->pinentry_desc);
493 pwmd_free(pwm->pinentry_prompt);
494 pwmd_free(pwm->pinentry_tty);
495 pwmd_free(pwm->pinentry_display);
496 pwmd_free(pwm->pinentry_term);
497 pwmd_free(pwm->pinentry_lcctype);
498 pwmd_free(pwm->pinentry_lcmessages);
499 pwmd_free(pwm->filename);
500 pwmd_free(pwm->name);
502 #ifdef WITH_PINENTRY
503 if (pwm->pctx)
504 _pinentry_disconnect(pwm);
505 #endif
507 pwmd_free(pwm);
510 static gpg_error_t inquire_realloc_cb(void *data, const void *buffer,
511 size_t len)
513 membuf_t *mem = (membuf_t *)data;
514 void *p;
516 if (!buffer)
517 return 0;
519 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
520 return gpg_error(GPG_ERR_ENOMEM);
522 mem->buf = p;
523 memcpy((char *)mem->buf + mem->len, buffer, len);
524 mem->len += len;
525 return 0;
528 static gpg_error_t get_password(pwm_t *pwm, char **result, pwmd_pinentry_t w,
529 int echo)
531 char buf[LINE_MAX] = {0}, *p;
532 struct termios told, tnew;
533 char *key = NULL;
535 *result = NULL;
537 if (!isatty(STDIN_FILENO)) {
538 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
539 return GPG_ERR_ENOTTY;
542 if (!echo) {
543 if (tcgetattr(STDIN_FILENO, &told) == -1)
544 return gpg_error_from_syserror();
546 memcpy(&tnew, &told, sizeof(struct termios));
547 tnew.c_lflag &= ~(ECHO);
548 tnew.c_lflag |= ICANON|ECHONL;
550 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
551 int n = errno;
553 tcsetattr(STDIN_FILENO, TCSANOW, &told);
554 return gpg_error_from_errno(n);
558 switch (w) {
559 case PWMD_PINENTRY_OPEN:
560 fprintf(stderr, N_("Password for %s: "), pwm->filename);
561 break;
562 case PWMD_PINENTRY_OPEN_FAILED:
563 fprintf(stderr, N_("Invalid password. Password for %s: "),
564 pwm->filename);
565 break;
566 case PWMD_PINENTRY_SAVE:
567 fprintf(stderr, N_("New password for %s: "), pwm->filename);
568 break;
569 case PWMD_PINENTRY_SAVE_CONFIRM:
570 fprintf(stderr, N_("Confirm password: "));
571 break;
572 default:
573 break;
576 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
577 tcsetattr(STDIN_FILENO, TCSANOW, &told);
578 return 0;
581 if (!echo)
582 tcsetattr(STDIN_FILENO, TCSANOW, &told);
584 if (feof(stdin)) {
585 clearerr(stdin);
586 return GPG_ERR_CANCELED;
589 p[strlen(p) - 1] = 0;
591 if (buf[0]) {
592 key = pwmd_strdup_printf("%s", p);
593 memset(&buf, 0, sizeof(buf));
595 if (!key)
596 return GPG_ERR_ENOMEM;
599 *result = key;
600 return 0;
603 gpg_error_t pwmd_password(pwm_t *pwm, const char *keyword, char **data,
604 size_t *size)
606 gpg_error_t rc;
607 int new_password = 0;
608 size_t len;
609 char *password = NULL, *newpass = NULL;
610 int error = 0;
612 if (!strcmp(keyword, "NEW_PASSPHRASE"))
613 new_password = 1;
615 if (!new_password && pwm->pinentry_try)
616 error = 1;
618 again:
619 if (pwm->disable_pinentry && !pwm->local_pinentry) {
620 rc = get_password(pwm, &password,
621 new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN, 0);
622 if (!rc && new_password)
623 rc = get_password(pwm, &newpass, PWMD_PINENTRY_SAVE_CONFIRM, 0);
625 else {
626 pwmd_pinentry_t which;
628 if (error)
629 which = new_password ? PWMD_PINENTRY_SAVE_FAILED : PWMD_PINENTRY_OPEN_FAILED;
630 else
631 which = new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN;
633 rc = pwmd_getpin(pwm, pwm->filename, &password, &len, which);
634 if (!rc && new_password)
635 rc = pwmd_getpin(pwm, pwm->filename, &newpass, &len,
636 PWMD_PINENTRY_SAVE_CONFIRM);
639 if (!rc && new_password) {
640 if ((!password && newpass) || (!newpass && password)
641 || strcmp(newpass, password)) {
642 if (pwm->disable_pinentry)
643 fprintf(stderr, N_("Passphrases do not match.\n"));
645 pwmd_free(password);
646 pwmd_free(newpass);
647 password = newpass = NULL;
648 error = 1;
649 goto again;
653 (void)pwmd_getpin(pwm, pwm->filename, NULL, NULL, PWMD_PINENTRY_CLOSE);
654 pwmd_free(newpass);
655 if (!rc) {
656 // An empty passphrase on a protected key is not allowed by gpg-agent.
657 if (!password && !new_password)
658 rc = GPG_ERR_CANCELED;
659 else {
660 *data = password;
661 *size = password ? strlen(password) : 0;
665 return rc;
668 static gpg_error_t inquire_cb(void *data, const char *keyword)
670 pwm_t *pwm = (pwm_t *)data;
671 gpg_error_t rc = 0;
672 int free_result = 0;
673 char *result = NULL;
675 /* Shouldn't get this far without a callback. */
676 if (!pwm->override_inquire && !pwm->inquire_func)
677 return gpg_error(GPG_ERR_ASS_NO_INQUIRE_CB);
679 for (;;) {
680 size_t len = 0;
681 gpg_error_t arc;
682 int is_password = 0;
683 int new_password = 0;
685 result = NULL;
687 if (!strcmp(keyword, "PASSPHRASE"))
688 is_password = 1;
689 else if (!strcmp(keyword, "NEW_PASSPHRASE"))
690 new_password = 1;
692 if (!pwm->override_inquire && (is_password || new_password)) {
693 free_result = 1;
694 rc = pwmd_password(data, keyword, &result, &len);
695 if (!rc)
696 rc = GPG_ERR_EOF;
698 else
699 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result,
700 &len);
702 cancel:
703 if (rc && gpg_err_code(rc) != GPG_ERR_EOF) {
704 gpg_error_t trc = rc;
706 /* Cancel this inquire. */
707 rc = assuan_send_data(pwm->ctx, NULL, 1);
708 if (!rc) {
709 char *line;
710 size_t len;
712 /* There is a bug (or feature?) in assuan_send_data() that
713 * when cancelling an inquire the next read from the server is
714 * not done until the next command making the next command
715 * fail with GPG_ERR_ASS_UNEXPECTED_CMD.
717 rc = assuan_read_line(pwm->ctx, &line, &len);
719 /* Restore the original error. This differs from the error
720 * returned from the pwmd command (GPG_ERR_CANCELED). This
721 * error is returned to the calling function.
723 if (!rc)
724 rc = trc;
727 break;
730 if (gpg_err_code(rc) == GPG_ERR_EOF || !rc) {
731 if (len <= 0 && !result) {
732 rc = 0;
733 break;
735 else if ((len <= 0 && result) || (len && !result)) {
736 rc = gpg_error(GPG_ERR_INV_ARG);
737 break;
740 if (pwm->inquire_maxlen
741 && pwm->inquire_sent+len > pwm->inquire_maxlen) {
742 rc = gpg_error(GPG_ERR_TOO_LARGE);
743 if (!free_result)
744 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc,
745 &result, &len);
746 goto cancel;
749 arc = assuan_send_data(pwm->ctx, result, len);
750 if (gpg_err_code(rc) == GPG_ERR_EOF) {
751 rc = arc;
752 break;
755 rc = arc;
757 else if (rc)
758 break;
760 if (!rc) {
761 pwm->inquire_sent += len;
763 if (pwm->status_func) {
764 char buf[ASSUAN_LINELENGTH];
766 snprintf(buf, sizeof(buf), "XFER %lu %lu", pwm->inquire_sent,
767 pwm->inquire_total);
768 rc = pwm->status_func(pwm->status_data, buf);
769 if (rc)
770 continue;
775 if (free_result)
776 pwmd_free(result);
778 return rc;
781 static gpg_error_t parse_assuan_line(pwm_t *pwm)
783 gpg_error_t rc;
784 char *line;
785 size_t len;
787 rc = assuan_read_line(pwm->ctx, &line, &len);
788 if (!rc) {
789 if (line[0] == 'O' && line[1] == 'K' &&
790 (line[2] == 0 || line[2] == ' ')) {
792 else if (line[0] == '#') {
794 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
795 if (pwm->status_func) {
796 rc = pwm->status_func(pwm->status_data,
797 line[1] == 0 ? line+1 : line+2);
800 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
801 (line[3] == 0 || line[3] == ' ')) {
802 line += 4;
803 rc = atoi(line);
807 return rc;
810 static void reset_handle_state(pwm_t *pwm, int done)
812 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
813 if (pwm->tcp)
814 pwm->tcp->rc = 0;
816 if (done) {
817 free_tcp(pwm->tcp);
818 pwm->tcp = NULL;
820 #endif
823 static void reset_handle(pwm_t *h)
825 h->fd = -1;
826 #ifdef WITH_PINENTRY
827 if (h->pctx)
828 _pinentry_disconnect(h);
829 #endif
830 reset_handle_state(h, 0);
833 gpg_error_t pwmd_disconnect(pwm_t *pwm)
835 if (!pwm)
836 return FINISH(GPG_ERR_INV_ARG);
838 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
839 if (pwm->fd == -1 && pwm->tcp && pwm->tcp->fd == -1)
840 #else
841 if (pwm->fd == -1)
842 #endif
843 return FINISH(GPG_ERR_INV_STATE);
845 if (pwm->fd != 1)
846 disconnect(pwm);
848 reset_handle(pwm);
849 return 0;
852 /* Note that this should only be called when not in a command. */
853 gpg_error_t pwmd_process(pwm_t *pwm)
855 gpg_error_t rc = 0;
856 fd_set fds;
857 struct timeval tv = {0, 0};
858 int n;
860 if (!pwm)
861 return FINISH(GPG_ERR_INV_ARG);
862 else if (!pwm->ctx)
863 return FINISH(GPG_ERR_INV_STATE);
865 #if defined(WITH_SSH)
866 if (pwm->tcp && pwm->tcp->ssh && pwm->keepalive_interval) {
867 int to;
868 int n = libssh2_keepalive_send(pwm->tcp->ssh->session, &to);
870 if (n)
871 return FINISH(GPG_ERR_ETIMEDOUT);
873 if (pwm->tcp->ssh->keepalive_prev &&
874 to > pwm->tcp->ssh->keepalive_prev) {
875 pwm->tcp->ssh->keepalive_prev = to;
876 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "NOP");
877 if (rc)
878 return FINISH(rc);
880 else
881 pwm->tcp->ssh->keepalive_prev = to;
883 #endif
885 FD_ZERO(&fds);
886 FD_SET(pwm->fd, &fds);
887 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
889 if (n == -1)
890 return FINISH(gpg_error_from_syserror());
892 if (n > 0) {
893 if (FD_ISSET(pwm->fd, &fds))
894 rc = parse_assuan_line(pwm);
897 while (!rc && assuan_pending_line(pwm->ctx))
898 rc = parse_assuan_line(pwm);
900 return FINISH(rc);
903 static gpg_error_t status_cb(void *data, const char *line)
905 pwm_t *pwm = data;
907 if (!strncmp(line, "INQUIRE_MAXLEN ", 15))
908 pwm->inquire_maxlen = atoi(line+15);
910 if (pwm->status_func)
911 return pwm->status_func(pwm->status_data, line);
913 return 0;
916 gpg_error_t _assuan_command(pwm_t *pwm, assuan_context_t ctx,
917 char **result, size_t *len, const char *cmd)
919 membuf_t data;
920 gpg_error_t rc;
922 if (!cmd || !*cmd)
923 return FINISH(GPG_ERR_INV_ARG);
925 if (strlen(cmd) >= ASSUAN_LINELENGTH+1)
926 return FINISH(GPG_ERR_LINE_TOO_LONG);
928 data.len = 0;
929 data.buf = NULL;
930 rc = assuan_transact(ctx, cmd, inquire_realloc_cb, &data,
931 #ifdef WITH_QUALITY
932 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
933 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
934 #else
935 inquire_cb, pwm,
936 #endif
937 status_cb, pwm);
939 if (rc) {
940 if (data.buf) {
941 pwmd_free(data.buf);
942 data.buf = NULL;
945 else {
946 if (data.buf) {
947 inquire_realloc_cb(&data, "", 1);
949 if (result)
950 *result = (char *)data.buf;
951 else
952 pwmd_free(data.buf);
954 if (len)
955 *len = data.len;
959 pwm->inquire_maxlen = 0;
960 return rc;
963 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, size_t *rlen,
964 pwmd_inquire_cb_t func, void *user, const char *cmd, va_list ap)
966 char *buf;
967 size_t len;
968 va_list ap2;
970 if (!pwm || !cmd)
971 return FINISH(GPG_ERR_INV_ARG);
972 if (!pwm->ctx)
973 return FINISH(GPG_ERR_INV_STATE);
976 * C99 allows the dst pointer to be null which will calculate the length
977 * of the would-be result and return it.
979 va_copy(ap2, ap);
980 len = vsnprintf(NULL, 0, cmd, ap)+1;
981 buf = (char *)pwmd_malloc(len);
982 if (!buf) {
983 va_end(ap2);
984 return FINISH(GPG_ERR_ENOMEM);
987 len = vsnprintf(buf, len, cmd, ap2);
988 va_end(ap2);
990 if (buf[strlen(buf)-1] == '\n')
991 buf[strlen(buf)-1] = 0;
992 if (buf[strlen(buf)-1] == '\r')
993 buf[strlen(buf)-1] = 0;
995 pwm->inquire_func = func;
996 pwm->inquire_data = user;
997 pwm->inquire_sent = 0;
998 gpg_error_t rc = _assuan_command(pwm, pwm->ctx, result, rlen, buf);
999 pwmd_free(buf);
1000 return rc;
1003 gpg_error_t pwmd_command(pwm_t *pwm, char **result, size_t *len,
1004 pwmd_inquire_cb_t func, void *user, const char *cmd, ...)
1006 va_list ap;
1008 if (!pwm || !cmd)
1009 return FINISH(GPG_ERR_INV_ARG);
1010 if (!pwm->ctx)
1011 return FINISH(GPG_ERR_INV_STATE);
1013 if (result)
1014 *result = NULL;
1016 va_start(ap, cmd);
1017 gpg_error_t rc = pwmd_command_ap(pwm, result, len, func, user, cmd, ap);
1018 va_end(ap);
1019 return rc;
1022 static gpg_error_t send_pinentry_options(pwm_t *pwm)
1024 gpg_error_t rc;
1026 if (pwm->pinentry_tty) {
1027 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
1028 if (rc)
1029 return rc;
1032 if (pwm->pinentry_term) {
1033 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
1034 if (rc)
1035 return rc;
1038 if (pwm->pinentry_display) {
1039 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DISPLAY=%s",
1040 pwm->pinentry_display);
1041 if (rc)
1042 return rc;
1045 if (pwm->pinentry_desc) {
1046 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DESC=%s",
1047 pwm->pinentry_desc);
1048 if (rc)
1049 return rc;
1052 if (pwm->pinentry_lcctype) {
1053 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_CTYPE=%s",
1054 pwm->pinentry_lcctype);
1055 if (rc)
1056 return rc;
1059 if (pwm->pinentry_lcmessages) {
1060 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_MESSAGES=%s",
1061 pwm->pinentry_lcmessages);
1062 if (rc)
1063 return rc;
1066 return 0;
1069 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
1071 if (!pwm || !result)
1072 return FINISH(GPG_ERR_INV_ARG);
1074 *result = PWMD_SOCKET_LOCAL;
1076 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1077 if ((pwm->fd == -1 && (!pwm->tcp
1078 #ifdef WITH_SSH
1079 || !pwm->tcp->ssh
1080 #endif
1081 #ifdef WITH_GNUTLS
1082 || !pwm->tcp->tls
1083 #endif
1084 )) ||
1085 (pwm->fd == -1 && pwm->tcp && pwm->tcp->fd == -1))
1086 return FINISH(GPG_ERR_INV_STATE);
1087 #else
1088 if (pwm->fd == -1)
1089 return FINISH(GPG_ERR_INV_STATE);
1090 #endif
1092 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1093 #ifdef WITH_SSH
1094 if (pwm->tcp && pwm->tcp->ssh)
1095 *result = PWMD_SOCKET_SSH;
1096 #endif
1097 #ifdef WITH_GNUTLS
1098 if (pwm->tcp && pwm->tcp->tls)
1099 *result = PWMD_SOCKET_TLS;
1100 #endif
1101 #endif
1102 return 0;
1105 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename, pwmd_inquire_cb_t cb,
1106 void *data)
1108 gpg_error_t rc = 0;
1109 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1110 int no_pinentry = pwm->disable_pinentry || pwm->tcp || pwm->local_pinentry;
1111 #else
1112 int no_pinentry = pwm->disable_pinentry || pwm->local_pinentry;
1113 #endif
1115 if (!pwm || !filename || !*filename)
1116 return FINISH(GPG_ERR_INV_ARG);
1118 if (!pwm->ctx)
1119 return FINISH(GPG_ERR_INV_STATE);
1121 if (!no_pinentry)
1122 rc = send_pinentry_options(pwm);
1124 if (!rc) {
1125 pwm->pinentry_try = 0;
1126 pwmd_free(pwm->filename);
1127 pwm->filename = pwmd_strdup(filename);
1129 do {
1130 rc = pwmd_command(pwm, NULL, NULL, cb, data, "OPEN %s%s%s",
1131 (pwm->opts & OPT_LOCK_ON_OPEN) ? "--lock " : "",
1132 no_pinentry ? "--no-pinentry " : "", filename);
1133 } while (gpg_err_code(rc) == GPG_ERR_BAD_PASSPHRASE
1134 && no_pinentry && ++pwm->pinentry_try < pwm->pinentry_tries);
1136 pwm->pinentry_try = 0;
1138 if (rc) {
1139 pwmd_free(pwm->filename);
1140 pwm->filename = NULL;
1144 return FINISH(rc);
1147 gpg_error_t pwmd_save(pwm_t *pwm, const char *args, pwmd_inquire_cb_t cb,
1148 void *data)
1150 gpg_error_t rc;
1152 if (!pwm)
1153 return FINISH(GPG_ERR_INV_ARG);
1154 if (!pwm->ctx)
1155 return FINISH(GPG_ERR_INV_STATE);
1157 rc = pwmd_command(pwm, NULL, NULL, cb, data, "SAVE %s",
1158 args ? args : "");
1159 return FINISH(rc);
1162 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1164 va_list ap;
1165 int n;
1166 char *arg1;
1167 gpg_error_t rc = 0;
1169 if (!pwm)
1170 return FINISH(GPG_ERR_INV_ARG);
1172 va_start(ap, opt);
1174 switch (opt) {
1175 case PWMD_OPTION_LOCK_ON_OPEN:
1176 n = va_arg(ap, int);
1178 if (n < 0 || n > 1)
1179 rc = GPG_ERR_INV_VALUE;
1181 if (n)
1182 pwm->opts |= OPT_LOCK_ON_OPEN;
1183 else
1184 pwm->opts &= ~OPT_LOCK_ON_OPEN;
1186 break;
1187 case PWMD_OPTION_INQUIRE_TOTAL:
1188 pwm->inquire_total = va_arg(ap, size_t);
1189 break;
1190 case PWMD_OPTION_STATUS_CB:
1191 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
1192 break;
1193 case PWMD_OPTION_STATUS_DATA:
1194 pwm->status_data = va_arg(ap, void *);
1195 break;
1196 case PWMD_OPTION_NO_PINENTRY:
1197 n = va_arg(ap, int);
1199 if (n < 0 || n > 1)
1200 rc = GPG_ERR_INV_VALUE;
1201 else
1202 pwm->disable_pinentry = n;
1204 break;
1205 case PWMD_OPTION_LOCAL_PINENTRY:
1206 n = va_arg(ap, int);
1208 if (n < 0 || n > 1)
1209 rc = GPG_ERR_INV_VALUE;
1210 else
1211 pwm->local_pinentry = n;
1213 break;
1214 case PWMD_OPTION_PINENTRY_TIMEOUT:
1215 n = va_arg(ap, int);
1217 if (n < 0)
1218 rc = GPG_ERR_INV_VALUE;
1219 else
1220 pwm->pinentry_timeout = n;
1222 break;
1223 case PWMD_OPTION_PINENTRY_TRIES:
1224 n = va_arg(ap, int);
1225 pwm->pinentry_tries = n;
1226 break;
1227 case PWMD_OPTION_PINENTRY_PATH:
1228 arg1 = va_arg(ap, char *);
1229 pwmd_free(pwm->pinentry_path);
1230 pwm->pinentry_path = arg1 ? _expand_homedir(arg1, NULL) : NULL;
1231 break;
1232 case PWMD_OPTION_PINENTRY_TTY:
1233 arg1 = va_arg(ap, char *);
1234 pwmd_free(pwm->pinentry_tty);
1235 pwm->pinentry_tty = arg1 ? pwmd_strdup(arg1) : NULL;
1236 break;
1237 case PWMD_OPTION_PINENTRY_DISPLAY:
1238 arg1 = va_arg(ap, char *);
1239 pwmd_free(pwm->pinentry_display);
1240 pwm->pinentry_display = arg1 ? pwmd_strdup(arg1) : NULL;
1241 break;
1242 case PWMD_OPTION_PINENTRY_TERM:
1243 arg1 = va_arg(ap, char *);
1244 pwmd_free(pwm->pinentry_term);
1245 pwm->pinentry_term = arg1 ? pwmd_strdup(arg1) : NULL;
1246 break;
1247 case PWMD_OPTION_PINENTRY_ERROR:
1248 arg1 = va_arg(ap, char *);
1249 pwmd_free(pwm->pinentry_error);
1250 pwm->pinentry_error = arg1 ? _percent_escape(arg1) : NULL;
1251 break;
1252 case PWMD_OPTION_PINENTRY_PROMPT:
1253 arg1 = va_arg(ap, char *);
1254 pwmd_free(pwm->pinentry_prompt);
1255 pwm->pinentry_prompt = arg1 ? _percent_escape(arg1) : NULL;
1256 break;
1257 case PWMD_OPTION_PINENTRY_DESC:
1258 arg1 = va_arg(ap, char *);
1259 pwmd_free(pwm->pinentry_desc);
1260 pwm->pinentry_desc = arg1 ? _percent_escape(arg1) : NULL;
1261 break;
1262 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1263 arg1 = va_arg(ap, char *);
1264 pwmd_free(pwm->pinentry_lcctype);
1265 pwm->pinentry_lcctype = arg1 ? pwmd_strdup(arg1) : NULL;
1266 break;
1267 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1268 arg1 = va_arg(ap, char *);
1269 pwmd_free(pwm->pinentry_lcmessages);
1270 pwm->pinentry_lcmessages = arg1 ? pwmd_strdup(arg1) : NULL;
1271 break;
1272 #ifdef WITH_SSH
1273 case PWMD_OPTION_KNOWNHOST_CB:
1274 pwm->kh_cb = va_arg(ap, pwmd_knownhost_cb_t);
1275 break;
1276 case PWMD_OPTION_KNOWNHOST_DATA:
1277 pwm->kh_data = va_arg(ap, void *);
1278 break;
1279 case PWMD_OPTION_SSH_AGENT:
1280 pwm->use_agent = va_arg(ap, int);
1282 if (pwm->use_agent < 0 || pwm->use_agent > 1) {
1283 pwm->use_agent = 0;
1284 rc = GPG_ERR_INV_VALUE;
1287 break;
1288 case PWMD_OPTION_SSH_TIMEOUT:
1289 pwm->ssh_timeout = va_arg(ap, int);
1291 if (pwm->ssh_timeout < 0) {
1292 pwm->ssh_timeout = 0;
1293 rc = GPG_ERR_INV_VALUE;
1295 else if (pwm->tcp && pwm->tcp->ssh && pwm->tcp->ssh->session)
1296 libssh2_session_set_timeout(pwm->tcp->ssh->session,
1297 pwm->ssh_timeout*1000);
1299 break;
1300 case PWMD_OPTION_SSH_KEEPALIVE:
1301 pwm->keepalive_interval = va_arg(ap, int);
1303 if (pwm->keepalive_interval < 0) {
1304 pwm->keepalive_interval = 0;
1305 rc = GPG_ERR_INV_VALUE;
1307 else if (pwm->tcp && pwm->tcp->ssh && pwm->tcp->ssh->session)
1308 libssh2_keepalive_config(pwm->tcp->ssh->session, 1,
1309 pwm->keepalive_interval);
1311 break;
1312 #else
1313 case PWMD_OPTION_KNOWNHOST_CB:
1314 case PWMD_OPTION_KNOWNHOST_DATA:
1315 case PWMD_OPTION_SSH_AGENT:
1316 case PWMD_OPTION_SSH_TIMEOUT:
1317 rc = GPG_ERR_NOT_IMPLEMENTED;
1318 break;
1319 #endif
1320 #ifdef WITH_GNUTLS
1321 case PWMD_OPTION_TLS_VERIFY:
1322 pwm->tls_verify = va_arg(ap, int);
1324 if (pwm->tls_verify < 0 || pwm->tls_verify > 1) {
1325 pwm->tls_verify = 0;
1326 rc = GPG_ERR_INV_VALUE;
1328 break;
1329 #endif
1330 case PWMD_OPTION_OVERRIDE_INQUIRE:
1331 pwm->override_inquire = va_arg(ap, int);
1333 if (pwm->override_inquire < 0 || pwm->override_inquire > 1) {
1334 pwm->override_inquire = 0;
1335 rc = GPG_ERR_INV_VALUE;
1337 break;
1338 default:
1339 rc = GPG_ERR_UNKNOWN_OPTION;
1340 break;
1343 va_end(ap);
1344 return FINISH(rc);
1347 gpg_error_t pwmd_new(const char *name, pwm_t **pwm)
1349 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
1350 gpg_error_t rc;
1352 if (!h)
1353 return FINISH(GPG_ERR_ENOMEM);
1355 if (name) {
1356 h->name = pwmd_strdup(name);
1357 if (!h->name) {
1358 pwmd_free(h);
1359 return FINISH(GPG_ERR_ENOMEM);
1363 reset_handle(h);
1364 h->pinentry_timeout = -30;
1365 h->pinentry_tries = 3;
1366 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1367 h->prot = PWMD_IP_ANY;
1368 #endif
1370 if (ttyname(STDOUT_FILENO)) {
1371 char buf[256];
1373 ttyname_r(STDOUT_FILENO, buf, sizeof(buf));
1374 h->pinentry_tty = pwmd_strdup(buf);
1375 if (!h->pinentry_tty) {
1376 rc = GPG_ERR_ENOMEM;
1377 goto fail;
1381 if (getenv("TERM") && h->pinentry_tty) {
1382 h->pinentry_term = pwmd_strdup(getenv("TERM"));
1383 if (!h->pinentry_term) {
1384 rc = GPG_ERR_ENOMEM;
1385 goto fail;
1389 if (getenv("DISPLAY")) {
1390 h->pinentry_display = pwmd_strdup(getenv("DISPLAY"));
1391 if (!h->pinentry_display) {
1392 rc = GPG_ERR_ENOMEM;
1393 goto fail;
1397 update_pinentry_settings(h);
1398 *pwm = h;
1399 return 0;
1401 fail:
1402 pwmd_close(h);
1403 return FINISH(rc);
1406 void pwmd_free(void *ptr)
1408 _xfree(ptr);
1411 void *pwmd_malloc(size_t size)
1413 return _xmalloc(size);
1416 void *pwmd_calloc(size_t nmemb, size_t size)
1418 return _xcalloc(nmemb, size);
1421 void *pwmd_realloc(void *ptr, size_t size)
1423 return _xrealloc(ptr, size);
1426 char *pwmd_strdup(const char *str)
1428 return _xstrdup(str);
1431 char *pwmd_strdup_printf(const char *fmt, ...)
1433 va_list ap, ap2;
1434 int len;
1435 char *buf;
1437 if (!fmt)
1438 return NULL;
1440 va_start(ap, fmt);
1441 va_copy(ap2, ap);
1442 len = vsnprintf(NULL, 0, fmt, ap);
1443 va_end(ap);
1444 buf = pwmd_malloc(++len);
1445 if (buf)
1446 vsnprintf(buf, len, fmt, ap2);
1448 va_end(ap2);
1449 return buf;
1452 gpg_error_t pwmd_getpin(pwm_t *pwm, const char *filename, char **result,
1453 size_t *len, pwmd_pinentry_t which)
1455 #ifndef WITH_PINENTRY
1456 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
1457 #else
1458 gpg_error_t rc = _pwmd_getpin(pwm, filename, result, len, which);
1460 return FINISH(rc);
1461 #endif
1464 const char *pwmd_version()
1466 return LIBPWMD_VERSION_STR;
1469 unsigned int pwmd_features()
1471 unsigned int n = 0;
1473 #ifdef WITH_PINENTRY
1474 n |= PWMD_FEATURE_PINENTRY;
1475 #endif
1476 #ifdef WITH_SSH
1477 n |= PWMD_FEATURE_SSH;
1478 #endif
1479 #ifdef WITH_QUALITY
1480 n |= PWMD_FEATURE_CRACK;
1481 #endif
1482 #ifdef WITH_GNUTLS
1483 n |= PWMD_FEATURE_GNUTLS;
1484 #endif
1485 return n;
1488 gpg_error_t pwmd_fd(pwm_t *pwm, int *fd)
1490 if (!pwm || !fd)
1491 return FINISH(GPG_ERR_INV_ARG);
1493 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1494 if (pwm->tcp && pwm->tcp->fd == -1 && pwm->fd == -1)
1495 return GPG_ERR_INV_STATE;
1497 *fd = pwm->tcp && pwm->tcp->fd != -1 ? pwm->tcp->fd : pwm->fd;
1498 #else
1499 if (pwm->fd == -1)
1500 return FINISH(GPG_ERR_INV_STATE);
1502 *fd = pwm->fd;
1503 #endif
1505 return 0;