Fix PWMD_OPTION_NO_PINENTRY in pwmd_password().
[libpwmd.git] / src / libpwmd.c
blob0bee7a59702c181f2f709f8f39a1b82974184762
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 void pwmd_deinit()
157 #ifdef WITH_GNUTLS
158 gnutls_global_deinit();
159 #endif
162 gpg_error_t _connect_finalize(pwm_t *pwm)
164 gpg_error_t rc = 0;
165 int active[2];
166 int n = assuan_get_active_fds(pwm->ctx, 0, active, N_ARRAY(active));
168 if (n <= 0)
169 return GPG_ERR_EBADFD;
171 pwm->fd = active[0];
172 #ifdef WITH_PINENTRY
173 pwm->pinentry_pid = -1;
174 #endif
176 if (pwm->name)
177 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION NAME=%s", pwm->name);
179 return rc;
182 static gpg_error_t connect_uds(pwm_t *pwm, const char *path)
184 char *socketpath = NULL;
185 struct passwd pw;
186 char *pwbuf;
187 gpg_error_t rc;
189 if (!pwm)
190 return GPG_ERR_INV_ARG;
192 pwbuf = _getpwuid(&pw);
193 if (!pwbuf)
194 return gpg_error_from_syserror();
196 if (!path || !*path)
197 socketpath = pwmd_strdup_printf("%s/.pwmd/socket", pw.pw_dir);
198 else
199 socketpath = _expand_homedir((char *)path, &pw);
201 pwmd_free(pwbuf);
202 if (!socketpath)
203 return GPG_ERR_ENOMEM;
205 rc = assuan_socket_connect(pwm->ctx, socketpath, ASSUAN_INVALID_FD, 0);
206 pwmd_free(socketpath);
207 return rc ? rc : _connect_finalize(pwm);
210 static gpg_error_t init_handle(pwm_t *pwm)
212 gpg_error_t rc;
213 static struct assuan_malloc_hooks mhooks = {
214 pwmd_malloc, pwmd_realloc, pwmd_free
216 static struct assuan_system_hooks shooks = {
217 ASSUAN_SYSTEM_HOOKS_VERSION,
218 __assuan_usleep,
219 __assuan_pipe,
220 __assuan_close,
221 hook_read,
222 hook_write,
223 //FIXME
224 NULL, //recvmsg
225 NULL, //sendmsg both are used for FD passing
226 __assuan_spawn,
227 hook_waitpid,
228 __assuan_socketpair,
229 __assuan_socket,
230 __assuan_connect
233 rc = assuan_new_ext(&pwm->ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, NULL, NULL);
234 if (rc)
235 return rc;
237 assuan_set_pointer(pwm->ctx, pwm);
238 assuan_ctx_set_system_hooks(pwm->ctx, &shooks);
239 return 0;
242 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
243 void free_tcp(struct tcp_s *tcp)
245 if (!tcp)
246 return;
248 #ifdef WITH_SSH
249 _free_ssh_conn(tcp->ssh);
250 #endif
251 #ifdef WITH_GNUTLS
252 tls_free(tcp->tls);
253 #endif
255 pwmd_free(tcp->host);
256 if (tcp->addrs) {
257 freeaddrinfo(tcp->addrs);
258 tcp->addrs = NULL;
261 if (tcp->fd != -1)
262 close(tcp->fd);
264 pwmd_free(tcp);
266 #endif
268 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
269 gpg_error_t tcp_connect_common(pwm_t *pwm)
271 struct addrinfo hints = {0};
272 int n;
273 char portstr[6];
274 gpg_error_t rc = 0;
276 switch (pwm->prot) {
277 case PWMD_IP_ANY:
278 hints.ai_family = AF_UNSPEC;
279 break;
280 case PWMD_IPV4:
281 hints.ai_family = AF_INET;
282 break;
283 case PWMD_IPV6:
284 hints.ai_family = AF_INET6;
285 break;
288 hints.ai_socktype = SOCK_STREAM;
289 snprintf(portstr, sizeof(portstr), "%i", pwm->tcp->port);
290 n = getaddrinfo(pwm->tcp->host, portstr, &hints, &pwm->tcp->addrs);
291 if (n) {
292 fprintf(stderr, "%s\n", gai_strerror(n));
293 return GPG_ERR_UNKNOWN_HOST; //FIXME
296 for (pwm->tcp->addr = pwm->tcp->addrs; pwm->tcp->addr;
297 pwm->tcp->addr = pwm->tcp->addrs->ai_next) {
298 pwm->tcp->fd = socket(pwm->tcp->addr->ai_family, SOCK_STREAM, 0);
299 if (pwm->tcp->fd == -1) {
300 rc = gpg_error_from_syserror();
301 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
302 break;
303 continue;
306 if (connect(pwm->tcp->fd, pwm->tcp->addr->ai_addr,
307 pwm->tcp->addr->ai_family == AF_INET6
308 ? sizeof(struct sockaddr_in6)
309 : sizeof(struct sockaddr)) == -1) {
310 rc = gpg_error_from_syserror();
311 close(pwm->tcp->fd);
312 pwm->tcp->fd = -1;
313 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
314 break;
315 continue;
318 rc = 0;
319 break;
322 return rc;
324 #endif
326 gpg_error_t pwmd_connect(pwm_t *pwm, const char *url, ...)
328 const char *p = url;
329 gpg_error_t rc;
331 if (!pwm)
332 return FINISH(GPG_ERR_INV_ARG);
333 else if (!pwm->ctx) {
334 rc = init_handle(pwm);
335 if (rc)
336 return rc;
339 rc = GPG_ERR_UNSUPPORTED_PROTOCOL;
341 if (p && *p == '/')
342 rc = connect_uds(pwm, p);
343 else if (!p || !strncmp(p, "file://", 7)) {
344 if (p)
345 p += 7;
346 rc = connect_uds(pwm, p);
348 else if (!strncmp(p, "ssh://", 6) || !strncmp(p, "ssh6://", 7) ||
349 !strncmp(p, "ssh4://", 7)) {
350 #ifndef WITH_SSH
351 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
352 #else
353 char *host = NULL;
354 int port;
355 char *username = NULL;
357 if (!strncmp(p, "ssh6://", 7)) {
358 pwm->prot = PWMD_IPV6;
359 p += 7;
361 else if (!strncmp(p, "ssh4://", 7)) {
362 pwm->prot = PWMD_IPV4;
363 p += 7;
365 else {
366 pwm->prot = PWMD_IP_ANY;
367 p += 6;
370 /* X11 forwarding is not supported. */
371 pwmd_setopt(pwm, PWMD_OPTION_NO_PINENTRY, 1);
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 /* X11 forwarding is not supported. */
427 pwmd_setopt(pwm, PWMD_OPTION_NO_PINENTRY, 1);
428 rc = _parse_tls_url(p, &host, &port);
429 if (!rc) {
430 va_list ap;
431 char *clientcert = NULL;
432 char *clientkey = NULL;
433 char *cacert = NULL;
434 char *prio = NULL;
436 va_start(ap, url);
437 clientcert = va_arg(ap, char *);
439 if (!clientcert)
440 rc = GPG_ERR_INV_ARG;
441 else {
442 clientkey = va_arg(ap, char *);
443 if (!clientkey)
444 rc = GPG_ERR_INV_ARG;
445 else {
446 cacert = va_arg(ap, char *);
447 if (!cacert)
448 rc = GPG_ERR_INV_ARG;
449 else
450 prio = va_arg(ap, char *);
454 va_end(ap);
456 if (!rc)
457 rc = _do_tls_connect(pwm, host, port, clientcert, clientkey,
458 cacert, prio, pwm->tls_verify);
459 if (!rc) {
460 rc = _connect_finalize(pwm);
461 if (rc) {
462 free_tcp(pwm->tcp);
463 pwm->tcp = NULL;
468 pwmd_free(host);
469 return FINISH(rc);
470 #endif
473 return FINISH(rc);
476 static void disconnect(pwm_t *pwm)
478 if (!pwm)
479 return;
481 if (pwm->ctx)
482 assuan_release(pwm->ctx);
484 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
485 free_tcp(pwm->tcp);
486 pwm->tcp = NULL;
487 #endif
488 pwm->ctx = NULL;
489 pwm->fd = -1;
492 void pwmd_close(pwm_t *pwm)
494 if (!pwm)
495 return;
497 disconnect(pwm);
498 pwmd_free(pwm->pinentry_error);
499 pwmd_free(pwm->pinentry_desc);
500 pwmd_free(pwm->pinentry_prompt);
501 pwmd_free(pwm->pinentry_tty);
502 pwmd_free(pwm->pinentry_display);
503 pwmd_free(pwm->pinentry_term);
504 pwmd_free(pwm->pinentry_lcctype);
505 pwmd_free(pwm->pinentry_lcmessages);
506 pwmd_free(pwm->filename);
507 pwmd_free(pwm->name);
509 #ifdef WITH_PINENTRY
510 if (pwm->pctx)
511 _pinentry_disconnect(pwm);
512 #endif
514 pwmd_free(pwm);
517 static gpg_error_t inquire_realloc_cb(void *data, const void *buffer,
518 size_t len)
520 membuf_t *mem = (membuf_t *)data;
521 void *p;
523 if (!buffer)
524 return 0;
526 if ((p = pwmd_realloc(mem->buf, mem->len + len)) == NULL)
527 return gpg_error(GPG_ERR_ENOMEM);
529 mem->buf = p;
530 memcpy((char *)mem->buf + mem->len, buffer, len);
531 mem->len += len;
532 return 0;
535 static gpg_error_t get_password(pwm_t *pwm, char **result, pwmd_pinentry_t w,
536 int echo)
538 char buf[LINE_MAX] = {0}, *p;
539 struct termios told, tnew;
540 char *key = NULL;
542 *result = NULL;
544 if (!isatty(STDIN_FILENO)) {
545 fprintf(stderr, N_("Input is not from a terminal! Failing.\n"));
546 return GPG_ERR_ENOTTY;
549 if (!echo) {
550 if (tcgetattr(STDIN_FILENO, &told) == -1)
551 return gpg_error_from_syserror();
553 memcpy(&tnew, &told, sizeof(struct termios));
554 tnew.c_lflag &= ~(ECHO);
555 tnew.c_lflag |= ICANON|ECHONL;
557 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
558 int n = errno;
560 tcsetattr(STDIN_FILENO, TCSANOW, &told);
561 return gpg_error_from_errno(n);
565 switch (w) {
566 case PWMD_PINENTRY_OPEN:
567 fprintf(stderr, N_("Password for %s: "), pwm->filename);
568 break;
569 case PWMD_PINENTRY_OPEN_FAILED:
570 fprintf(stderr, N_("Invalid password. Password for %s: "),
571 pwm->filename);
572 break;
573 case PWMD_PINENTRY_SAVE:
574 fprintf(stderr, N_("New password for %s: "), pwm->filename);
575 break;
576 case PWMD_PINENTRY_SAVE_CONFIRM:
577 fprintf(stderr, N_("Confirm password: "));
578 break;
579 default:
580 break;
583 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
584 tcsetattr(STDIN_FILENO, TCSANOW, &told);
585 return 0;
588 if (!echo)
589 tcsetattr(STDIN_FILENO, TCSANOW, &told);
591 if (feof(stdin)) {
592 clearerr(stdin);
593 return GPG_ERR_CANCELED;
596 p[strlen(p) - 1] = 0;
598 if (buf[0]) {
599 key = pwmd_strdup_printf("%s", p);
600 memset(&buf, 0, sizeof(buf));
602 if (!key)
603 return GPG_ERR_ENOMEM;
606 *result = key;
607 return 0;
610 gpg_error_t pwmd_password(pwm_t *pwm, const char *keyword, char **data,
611 size_t *size)
613 gpg_error_t rc;
614 int new_password = 0;
615 size_t len;
616 char *password = NULL, *newpass = NULL;
617 int error = 0;
619 if (!strcmp(keyword, "NEW_PASSPHRASE"))
620 new_password = 1;
622 if (!new_password && pwm->pinentry_try)
623 error = 1;
625 again:
626 if (pwm->disable_pinentry) {
627 rc = get_password(pwm, &password,
628 new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN, 0);
629 if (!rc && new_password)
630 rc = get_password(pwm, &newpass, PWMD_PINENTRY_SAVE_CONFIRM, 0);
632 else {
633 pwmd_pinentry_t which;
635 if (error)
636 which = new_password ? PWMD_PINENTRY_SAVE_FAILED : PWMD_PINENTRY_OPEN_FAILED;
637 else
638 which = new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN;
640 rc = pwmd_getpin(pwm, pwm->filename, &password, &len, which);
641 if (!rc && new_password)
642 rc = pwmd_getpin(pwm, pwm->filename, &newpass, &len,
643 PWMD_PINENTRY_SAVE_CONFIRM);
646 if (!rc && new_password) {
647 if ((!password && newpass) || (!newpass && password)
648 || strcmp(newpass, password)) {
649 if (pwm->disable_pinentry)
650 fprintf(stderr, N_("Passphrases do not match.\n"));
652 pwmd_free(password);
653 pwmd_free(newpass);
654 password = newpass = NULL;
655 error = 1;
656 goto again;
660 (void)pwmd_getpin(pwm, pwm->filename, NULL, NULL, PWMD_PINENTRY_CLOSE);
661 pwmd_free(newpass);
662 if (!rc) {
663 // An empty passphrase on a protected key is not allowed by gpg-agent.
664 if (!password && !new_password)
665 rc = GPG_ERR_CANCELED;
666 else {
667 *data = password;
668 *size = password ? strlen(password) : 0;
672 return rc;
675 static gpg_error_t inquire_cb(void *data, const char *keyword)
677 pwm_t *pwm = (pwm_t *)data;
678 gpg_error_t rc = 0;
679 int free_result = 0;
680 char *result = NULL;
682 /* Shouldn't get this far without a callback. */
683 if (!pwm->override_inquire && !pwm->inquire_func)
684 return gpg_error(GPG_ERR_ASS_NO_INQUIRE_CB);
686 for (;;) {
687 size_t len = 0;
688 gpg_error_t arc;
689 int is_password = 0;
690 int new_password = 0;
692 result = NULL;
694 if (!strcmp(keyword, "PASSPHRASE"))
695 is_password = 1;
696 else if (!strcmp(keyword, "NEW_PASSPHRASE"))
697 new_password = 1;
699 if (!pwm->override_inquire && (is_password || new_password)) {
700 free_result = 1;
701 rc = pwmd_password(data, keyword, &result, &len);
702 if (!rc)
703 rc = GPG_ERR_EOF;
705 else
706 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc, &result,
707 &len);
709 cancel:
710 if (rc && gpg_err_code(rc) != GPG_ERR_EOF) {
711 gpg_error_t trc = rc;
713 /* Cancel this inquire. */
714 rc = assuan_send_data(pwm->ctx, NULL, 1);
715 if (!rc) {
716 char *line;
717 size_t len;
719 /* There is a bug (or feature?) in assuan_send_data() that
720 * when cancelling an inquire the next read from the server is
721 * not done until the next command making the next command
722 * fail with GPG_ERR_ASS_UNEXPECTED_CMD.
724 rc = assuan_read_line(pwm->ctx, &line, &len);
726 /* Restore the original error. This differs from the error
727 * returned from the pwmd command (GPG_ERR_CANCELED). This
728 * error is returned to the calling function.
730 if (!rc)
731 rc = trc;
734 break;
737 if (gpg_err_code(rc) == GPG_ERR_EOF || !rc) {
738 if (len <= 0 && !result) {
739 rc = 0;
740 break;
742 else if ((len <= 0 && result) || (len && !result)) {
743 rc = gpg_error(GPG_ERR_INV_ARG);
744 break;
747 if (pwm->inquire_maxlen
748 && pwm->inquire_sent+len > pwm->inquire_maxlen) {
749 rc = gpg_error(GPG_ERR_TOO_LARGE);
750 if (!free_result)
751 rc = pwm->inquire_func(pwm->inquire_data, keyword, rc,
752 &result, &len);
753 goto cancel;
756 arc = assuan_send_data(pwm->ctx, result, len);
757 if (gpg_err_code(rc) == GPG_ERR_EOF) {
758 rc = arc;
759 break;
762 rc = arc;
764 else if (rc)
765 break;
767 if (!rc) {
768 pwm->inquire_sent += len;
770 if (pwm->status_func) {
771 char buf[ASSUAN_LINELENGTH];
773 snprintf(buf, sizeof(buf), "XFER %lu %lu", pwm->inquire_sent,
774 pwm->inquire_total);
775 rc = pwm->status_func(pwm->status_data, buf);
776 if (rc)
777 continue;
782 if (free_result)
783 pwmd_free(result);
785 return rc;
788 static gpg_error_t parse_assuan_line(pwm_t *pwm)
790 gpg_error_t rc;
791 char *line;
792 size_t len;
794 rc = assuan_read_line(pwm->ctx, &line, &len);
795 if (!rc) {
796 if (line[0] == 'O' && line[1] == 'K' &&
797 (line[2] == 0 || line[2] == ' ')) {
799 else if (line[0] == '#') {
801 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' ')) {
802 if (pwm->status_func) {
803 rc = pwm->status_func(pwm->status_data,
804 line[1] == 0 ? line+1 : line+2);
807 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
808 (line[3] == 0 || line[3] == ' ')) {
809 line += 4;
810 rc = strtol(line, NULL, 10);
814 return rc;
817 static void reset_handle_state(pwm_t *pwm, int done)
819 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
820 if (pwm->tcp)
821 pwm->tcp->rc = 0;
823 if (done) {
824 free_tcp(pwm->tcp);
825 pwm->tcp = NULL;
827 #endif
830 static void reset_handle(pwm_t *h)
832 h->fd = -1;
833 #ifdef WITH_PINENTRY
834 if (h->pctx)
835 _pinentry_disconnect(h);
836 #endif
837 reset_handle_state(h, 0);
840 gpg_error_t pwmd_disconnect(pwm_t *pwm)
842 if (!pwm)
843 return FINISH(GPG_ERR_INV_ARG);
845 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
846 if (pwm->fd == -1 && pwm->tcp && pwm->tcp->fd == -1)
847 #else
848 if (pwm->fd == -1)
849 #endif
850 return FINISH(GPG_ERR_INV_STATE);
852 if (pwm->fd != 1)
853 disconnect(pwm);
855 reset_handle(pwm);
856 return 0;
859 /* Note that this should only be called when not in a command. */
860 gpg_error_t pwmd_process(pwm_t *pwm)
862 gpg_error_t rc = 0;
863 fd_set fds;
864 struct timeval tv = {0, 0};
865 int n;
867 if (!pwm)
868 return FINISH(GPG_ERR_INV_ARG);
869 else if (!pwm->ctx)
870 return FINISH(GPG_ERR_INV_STATE);
872 #if defined(WITH_SSH)
873 if (pwm->tcp && pwm->tcp->ssh && pwm->keepalive_interval) {
874 int to;
875 int n = libssh2_keepalive_send(pwm->tcp->ssh->session, &to);
877 if (n)
878 return FINISH(GPG_ERR_ETIMEDOUT);
880 if (pwm->tcp->ssh->keepalive_prev &&
881 to > pwm->tcp->ssh->keepalive_prev) {
882 pwm->tcp->ssh->keepalive_prev = to;
883 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "NOP");
884 if (rc)
885 return FINISH(rc);
887 else
888 pwm->tcp->ssh->keepalive_prev = to;
890 #endif
892 FD_ZERO(&fds);
893 FD_SET(pwm->fd, &fds);
894 n = select(pwm->fd+1, &fds, NULL, NULL, &tv);
896 if (n == -1)
897 return FINISH(gpg_error_from_syserror());
899 if (n > 0) {
900 if (FD_ISSET(pwm->fd, &fds))
901 rc = parse_assuan_line(pwm);
904 while (!rc && assuan_pending_line(pwm->ctx))
905 rc = parse_assuan_line(pwm);
907 return FINISH(rc);
910 static gpg_error_t status_cb(void *data, const char *line)
912 pwm_t *pwm = data;
914 if (!strncmp(line, "INQUIRE_MAXLEN ", 15))
915 pwm->inquire_maxlen = strtol(line+15, NULL, 10);
917 if (pwm->status_func)
918 return pwm->status_func(pwm->status_data, line);
920 return 0;
923 gpg_error_t _assuan_command(pwm_t *pwm, assuan_context_t ctx,
924 char **result, size_t *len, const char *cmd)
926 membuf_t data;
927 gpg_error_t rc;
929 if (!cmd || !*cmd)
930 return FINISH(GPG_ERR_INV_ARG);
932 if (strlen(cmd) >= ASSUAN_LINELENGTH+1)
933 return FINISH(GPG_ERR_LINE_TOO_LONG);
935 data.len = 0;
936 data.buf = NULL;
937 rc = assuan_transact(ctx, cmd, inquire_realloc_cb, &data,
938 #ifdef WITH_QUALITY
939 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
940 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
941 #else
942 inquire_cb, pwm,
943 #endif
944 status_cb, pwm);
946 if (rc) {
947 if (data.buf) {
948 pwmd_free(data.buf);
949 data.buf = NULL;
952 else {
953 if (data.buf) {
954 inquire_realloc_cb(&data, "", 1);
956 if (result)
957 *result = (char *)data.buf;
958 else
959 pwmd_free(data.buf);
961 if (len)
962 *len = data.len;
966 pwm->inquire_maxlen = 0;
967 return rc;
970 gpg_error_t pwmd_command_ap(pwm_t *pwm, char **result, size_t *rlen,
971 pwmd_inquire_cb_t func, void *user, const char *cmd, va_list ap)
973 char *buf;
974 size_t len;
975 va_list ap2;
977 if (!pwm || !cmd)
978 return FINISH(GPG_ERR_INV_ARG);
979 if (!pwm->ctx)
980 return FINISH(GPG_ERR_INV_STATE);
983 * C99 allows the dst pointer to be null which will calculate the length
984 * of the would-be result and return it.
986 va_copy(ap2, ap);
987 len = vsnprintf(NULL, 0, cmd, ap)+1;
988 buf = (char *)pwmd_malloc(len);
989 if (!buf) {
990 va_end(ap2);
991 return FINISH(GPG_ERR_ENOMEM);
994 len = vsnprintf(buf, len, cmd, ap2);
995 va_end(ap2);
997 if (buf[strlen(buf)-1] == '\n')
998 buf[strlen(buf)-1] = 0;
999 if (buf[strlen(buf)-1] == '\r')
1000 buf[strlen(buf)-1] = 0;
1002 pwm->inquire_func = func;
1003 pwm->inquire_data = user;
1004 pwm->inquire_sent = 0;
1005 gpg_error_t rc = _assuan_command(pwm, pwm->ctx, result, rlen, buf);
1006 pwmd_free(buf);
1007 return rc;
1010 gpg_error_t pwmd_command(pwm_t *pwm, char **result, size_t *len,
1011 pwmd_inquire_cb_t func, void *user, const char *cmd, ...)
1013 va_list ap;
1015 if (!pwm || !cmd)
1016 return FINISH(GPG_ERR_INV_ARG);
1017 if (!pwm->ctx)
1018 return FINISH(GPG_ERR_INV_STATE);
1020 if (result)
1021 *result = NULL;
1023 va_start(ap, cmd);
1024 gpg_error_t rc = pwmd_command_ap(pwm, result, len, func, user, cmd, ap);
1025 va_end(ap);
1026 return rc;
1029 static gpg_error_t send_pinentry_options(pwm_t *pwm)
1031 gpg_error_t rc;
1033 if (pwm->pinentry_tty) {
1034 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYNAME=%s", pwm->pinentry_tty);
1035 if (rc)
1036 return rc;
1039 if (pwm->pinentry_term) {
1040 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION TTYTYPE=%s", pwm->pinentry_term);
1041 if (rc)
1042 return rc;
1045 if (pwm->pinentry_display) {
1046 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DISPLAY=%s",
1047 pwm->pinentry_display);
1048 if (rc)
1049 return rc;
1052 if (pwm->pinentry_desc) {
1053 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION DESC=%s",
1054 pwm->pinentry_desc);
1055 if (rc)
1056 return rc;
1059 if (pwm->pinentry_lcctype) {
1060 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_CTYPE=%s",
1061 pwm->pinentry_lcctype);
1062 if (rc)
1063 return rc;
1066 if (pwm->pinentry_lcmessages) {
1067 rc = pwmd_command(pwm, NULL, NULL, NULL, NULL, "OPTION LC_MESSAGES=%s",
1068 pwm->pinentry_lcmessages);
1069 if (rc)
1070 return rc;
1073 return 0;
1076 gpg_error_t pwmd_socket_type(pwm_t *pwm, pwmd_socket_t *result)
1078 if (!pwm || !result)
1079 return FINISH(GPG_ERR_INV_ARG);
1081 *result = PWMD_SOCKET_LOCAL;
1083 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1084 if ((pwm->fd == -1 && (!pwm->tcp
1085 #ifdef WITH_SSH
1086 || !pwm->tcp->ssh
1087 #endif
1088 #ifdef WITH_GNUTLS
1089 || !pwm->tcp->tls
1090 #endif
1091 )) ||
1092 (pwm->fd == -1 && pwm->tcp && pwm->tcp->fd == -1))
1093 return FINISH(GPG_ERR_INV_STATE);
1094 #else
1095 if (pwm->fd == -1)
1096 return FINISH(GPG_ERR_INV_STATE);
1097 #endif
1099 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1100 #ifdef WITH_SSH
1101 if (pwm->tcp && pwm->tcp->ssh)
1102 *result = PWMD_SOCKET_SSH;
1103 #endif
1104 #ifdef WITH_GNUTLS
1105 if (pwm->tcp && pwm->tcp->tls)
1106 *result = PWMD_SOCKET_TLS;
1107 #endif
1108 #endif
1109 return 0;
1112 gpg_error_t pwmd_open(pwm_t *pwm, const char *filename, pwmd_inquire_cb_t cb,
1113 void *data)
1115 gpg_error_t rc = 0;
1116 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1117 int no_pinentry = pwm->disable_pinentry || pwm->tcp || pwm->local_pinentry;
1118 #else
1119 int no_pinentry = pwm->disable_pinentry || pwm->local_pinentry;
1120 #endif
1122 if (!pwm || !filename || !*filename)
1123 return FINISH(GPG_ERR_INV_ARG);
1125 if (!pwm->ctx)
1126 return FINISH(GPG_ERR_INV_STATE);
1128 if (!no_pinentry)
1129 rc = send_pinentry_options(pwm);
1131 if (!rc) {
1132 pwm->pinentry_try = 0;
1133 pwmd_free(pwm->filename);
1134 pwm->filename = pwmd_strdup(filename);
1136 do {
1137 rc = pwmd_command(pwm, NULL, NULL, cb, data, "OPEN %s%s%s",
1138 (pwm->opts & OPT_LOCK_ON_OPEN) ? "--lock " : "",
1139 no_pinentry ? "--no-pinentry " : "", filename);
1140 } while (gpg_err_code(rc) == GPG_ERR_BAD_PASSPHRASE
1141 && no_pinentry && ++pwm->pinentry_try < pwm->pinentry_tries);
1143 pwm->pinentry_try = 0;
1145 if (rc) {
1146 pwmd_free(pwm->filename);
1147 pwm->filename = NULL;
1151 return FINISH(rc);
1154 gpg_error_t pwmd_save(pwm_t *pwm, const char *args, pwmd_inquire_cb_t cb,
1155 void *data)
1157 gpg_error_t rc;
1159 if (!pwm)
1160 return FINISH(GPG_ERR_INV_ARG);
1161 if (!pwm->ctx)
1162 return FINISH(GPG_ERR_INV_STATE);
1164 rc = pwmd_command(pwm, NULL, NULL, cb, data, "SAVE %s",
1165 args ? args : "");
1166 return FINISH(rc);
1169 gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...)
1171 va_list ap;
1172 int n;
1173 char *arg1;
1174 gpg_error_t rc = 0;
1176 if (!pwm)
1177 return FINISH(GPG_ERR_INV_ARG);
1179 va_start(ap, opt);
1181 switch (opt) {
1182 case PWMD_OPTION_LOCK_ON_OPEN:
1183 n = va_arg(ap, int);
1185 if (n < 0 || n > 1)
1186 rc = GPG_ERR_INV_VALUE;
1188 if (n)
1189 pwm->opts |= OPT_LOCK_ON_OPEN;
1190 else
1191 pwm->opts &= ~OPT_LOCK_ON_OPEN;
1193 break;
1194 case PWMD_OPTION_INQUIRE_TOTAL:
1195 pwm->inquire_total = va_arg(ap, size_t);
1196 break;
1197 case PWMD_OPTION_STATUS_CB:
1198 pwm->status_func = va_arg(ap, pwmd_status_cb_t);
1199 break;
1200 case PWMD_OPTION_STATUS_DATA:
1201 pwm->status_data = va_arg(ap, void *);
1202 break;
1203 case PWMD_OPTION_NO_PINENTRY:
1204 n = va_arg(ap, int);
1206 if (n < 0 || n > 1)
1207 rc = GPG_ERR_INV_VALUE;
1208 else
1209 pwm->disable_pinentry = n;
1211 break;
1212 case PWMD_OPTION_LOCAL_PINENTRY:
1213 n = va_arg(ap, int);
1215 if (n < 0 || n > 1)
1216 rc = GPG_ERR_INV_VALUE;
1217 else
1218 pwm->local_pinentry = n;
1220 break;
1221 case PWMD_OPTION_PINENTRY_TIMEOUT:
1222 n = va_arg(ap, int);
1224 if (n < 0)
1225 rc = GPG_ERR_INV_VALUE;
1226 else
1227 pwm->pinentry_timeout = n;
1229 break;
1230 case PWMD_OPTION_PINENTRY_TRIES:
1231 n = va_arg(ap, int);
1232 pwm->pinentry_tries = n;
1233 break;
1234 case PWMD_OPTION_PINENTRY_PATH:
1235 arg1 = va_arg(ap, char *);
1236 pwmd_free(pwm->pinentry_path);
1237 pwm->pinentry_path = arg1 ? _expand_homedir(arg1, NULL) : NULL;
1238 break;
1239 case PWMD_OPTION_PINENTRY_TTY:
1240 arg1 = va_arg(ap, char *);
1241 pwmd_free(pwm->pinentry_tty);
1242 pwm->pinentry_tty = arg1 ? pwmd_strdup(arg1) : NULL;
1243 break;
1244 case PWMD_OPTION_PINENTRY_DISPLAY:
1245 arg1 = va_arg(ap, char *);
1246 pwmd_free(pwm->pinentry_display);
1247 pwm->pinentry_display = arg1 ? pwmd_strdup(arg1) : NULL;
1248 break;
1249 case PWMD_OPTION_PINENTRY_TERM:
1250 arg1 = va_arg(ap, char *);
1251 pwmd_free(pwm->pinentry_term);
1252 pwm->pinentry_term = arg1 ? pwmd_strdup(arg1) : NULL;
1253 break;
1254 case PWMD_OPTION_PINENTRY_ERROR:
1255 arg1 = va_arg(ap, char *);
1256 pwmd_free(pwm->pinentry_error);
1257 pwm->pinentry_error = arg1 ? _percent_escape(arg1) : NULL;
1258 break;
1259 case PWMD_OPTION_PINENTRY_PROMPT:
1260 arg1 = va_arg(ap, char *);
1261 pwmd_free(pwm->pinentry_prompt);
1262 pwm->pinentry_prompt = arg1 ? _percent_escape(arg1) : NULL;
1263 break;
1264 case PWMD_OPTION_PINENTRY_DESC:
1265 arg1 = va_arg(ap, char *);
1266 pwmd_free(pwm->pinentry_desc);
1267 pwm->pinentry_desc = arg1 ? _percent_escape(arg1) : NULL;
1268 break;
1269 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1270 arg1 = va_arg(ap, char *);
1271 pwmd_free(pwm->pinentry_lcctype);
1272 pwm->pinentry_lcctype = arg1 ? pwmd_strdup(arg1) : NULL;
1273 break;
1274 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1275 arg1 = va_arg(ap, char *);
1276 pwmd_free(pwm->pinentry_lcmessages);
1277 pwm->pinentry_lcmessages = arg1 ? pwmd_strdup(arg1) : NULL;
1278 break;
1279 #ifdef WITH_SSH
1280 case PWMD_OPTION_KNOWNHOST_CB:
1281 pwm->kh_cb = va_arg(ap, pwmd_knownhost_cb_t);
1282 break;
1283 case PWMD_OPTION_KNOWNHOST_DATA:
1284 pwm->kh_data = va_arg(ap, void *);
1285 break;
1286 case PWMD_OPTION_SSH_AGENT:
1287 pwm->use_agent = va_arg(ap, int);
1289 if (pwm->use_agent < 0 || pwm->use_agent > 1) {
1290 pwm->use_agent = 0;
1291 rc = GPG_ERR_INV_VALUE;
1294 break;
1295 case PWMD_OPTION_SSH_TIMEOUT:
1296 pwm->ssh_timeout = va_arg(ap, int);
1298 if (pwm->ssh_timeout < 0) {
1299 pwm->ssh_timeout = 0;
1300 rc = GPG_ERR_INV_VALUE;
1302 else if (pwm->tcp && pwm->tcp->ssh && pwm->tcp->ssh->session)
1303 libssh2_session_set_timeout(pwm->tcp->ssh->session,
1304 pwm->ssh_timeout*1000);
1306 break;
1307 case PWMD_OPTION_SSH_KEEPALIVE:
1308 pwm->keepalive_interval = va_arg(ap, int);
1310 if (pwm->keepalive_interval < 0) {
1311 pwm->keepalive_interval = 0;
1312 rc = GPG_ERR_INV_VALUE;
1314 else if (pwm->tcp && pwm->tcp->ssh && pwm->tcp->ssh->session)
1315 libssh2_keepalive_config(pwm->tcp->ssh->session, 1,
1316 pwm->keepalive_interval);
1318 break;
1319 #else
1320 case PWMD_OPTION_KNOWNHOST_CB:
1321 case PWMD_OPTION_KNOWNHOST_DATA:
1322 case PWMD_OPTION_SSH_AGENT:
1323 case PWMD_OPTION_SSH_TIMEOUT:
1324 rc = GPG_ERR_NOT_IMPLEMENTED;
1325 break;
1326 #endif
1327 #ifdef WITH_GNUTLS
1328 case PWMD_OPTION_TLS_VERIFY:
1329 pwm->tls_verify = va_arg(ap, int);
1331 if (pwm->tls_verify < 0 || pwm->tls_verify > 1) {
1332 pwm->tls_verify = 0;
1333 rc = GPG_ERR_INV_VALUE;
1335 break;
1336 #endif
1337 case PWMD_OPTION_OVERRIDE_INQUIRE:
1338 pwm->override_inquire = va_arg(ap, int);
1340 if (pwm->override_inquire < 0 || pwm->override_inquire > 1) {
1341 pwm->override_inquire = 0;
1342 rc = GPG_ERR_INV_VALUE;
1344 break;
1345 default:
1346 rc = GPG_ERR_UNKNOWN_OPTION;
1347 break;
1350 va_end(ap);
1351 return FINISH(rc);
1354 gpg_error_t pwmd_new(const char *name, pwm_t **pwm)
1356 pwm_t *h = pwmd_calloc(1, sizeof(pwm_t));
1357 gpg_error_t rc;
1359 if (!h)
1360 return FINISH(GPG_ERR_ENOMEM);
1362 if (name) {
1363 h->name = pwmd_strdup(name);
1364 if (!h->name) {
1365 pwmd_free(h);
1366 return FINISH(GPG_ERR_ENOMEM);
1370 reset_handle(h);
1371 h->pinentry_timeout = -30;
1372 h->pinentry_tries = 3;
1373 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1374 h->prot = PWMD_IP_ANY;
1375 #endif
1377 if (ttyname(STDOUT_FILENO)) {
1378 char buf[256];
1380 ttyname_r(STDOUT_FILENO, buf, sizeof(buf));
1381 h->pinentry_tty = pwmd_strdup(buf);
1382 if (!h->pinentry_tty) {
1383 rc = GPG_ERR_ENOMEM;
1384 goto fail;
1388 if (getenv("TERM") && h->pinentry_tty) {
1389 h->pinentry_term = pwmd_strdup(getenv("TERM"));
1390 if (!h->pinentry_term) {
1391 rc = GPG_ERR_ENOMEM;
1392 goto fail;
1396 if (getenv("DISPLAY")) {
1397 h->pinentry_display = pwmd_strdup(getenv("DISPLAY"));
1398 if (!h->pinentry_display) {
1399 rc = GPG_ERR_ENOMEM;
1400 goto fail;
1404 update_pinentry_settings(h);
1405 *pwm = h;
1406 return 0;
1408 fail:
1409 pwmd_close(h);
1410 return FINISH(rc);
1413 void pwmd_free(void *ptr)
1415 _xfree(ptr);
1418 void *pwmd_malloc(size_t size)
1420 return _xmalloc(size);
1423 void *pwmd_calloc(size_t nmemb, size_t size)
1425 return _xcalloc(nmemb, size);
1428 void *pwmd_realloc(void *ptr, size_t size)
1430 return _xrealloc(ptr, size);
1433 char *pwmd_strdup(const char *str)
1435 return _xstrdup(str);
1438 char *pwmd_strdup_printf(const char *fmt, ...)
1440 va_list ap, ap2;
1441 int len;
1442 char *buf;
1444 if (!fmt)
1445 return NULL;
1447 va_start(ap, fmt);
1448 va_copy(ap2, ap);
1449 len = vsnprintf(NULL, 0, fmt, ap);
1450 va_end(ap);
1451 buf = pwmd_malloc(++len);
1452 if (buf)
1453 vsnprintf(buf, len, fmt, ap2);
1455 va_end(ap2);
1456 return buf;
1459 gpg_error_t pwmd_getpin(pwm_t *pwm, const char *filename, char **result,
1460 size_t *len, pwmd_pinentry_t which)
1462 #ifndef WITH_PINENTRY
1463 return FINISH(GPG_ERR_NOT_IMPLEMENTED);
1464 #else
1465 gpg_error_t rc = _pwmd_getpin(pwm, filename, result, len, which);
1467 return FINISH(rc);
1468 #endif
1471 const char *pwmd_version()
1473 return LIBPWMD_VERSION_STR;
1476 unsigned int pwmd_features()
1478 unsigned int n = 0;
1480 #ifdef WITH_PINENTRY
1481 n |= PWMD_FEATURE_PINENTRY;
1482 #endif
1483 #ifdef WITH_SSH
1484 n |= PWMD_FEATURE_SSH;
1485 #endif
1486 #ifdef WITH_QUALITY
1487 n |= PWMD_FEATURE_CRACK;
1488 #endif
1489 #ifdef WITH_GNUTLS
1490 n |= PWMD_FEATURE_GNUTLS;
1491 #endif
1492 return n;
1495 gpg_error_t pwmd_fd(pwm_t *pwm, int *fd)
1497 if (!pwm || !fd)
1498 return FINISH(GPG_ERR_INV_ARG);
1500 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1501 if (pwm->tcp && pwm->tcp->fd == -1 && pwm->fd == -1)
1502 return GPG_ERR_INV_STATE;
1504 *fd = pwm->tcp && pwm->tcp->fd != -1 ? pwm->tcp->fd : pwm->fd;
1505 #else
1506 if (pwm->fd == -1)
1507 return FINISH(GPG_ERR_INV_STATE);
1509 *fd = pwm->fd;
1510 #endif
1512 return 0;