Fix pwmd_process() to handle GPG_ERR_EOF.
[libpwmd.git] / src / libpwmd.c
blob621e303034c1f5f110c94b616ad96759cba80123
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
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
74 size_t len;
75 void *buf;
76 } membuf_t;
78 ssize_t
79 hook_read (assuan_context_t ctx, assuan_fd_t fd, void *data, size_t len)
81 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
82 pwm_t *pwm = assuan_get_pointer (ctx);
84 #ifdef WITH_SSH
85 if (pwm && pwm->tcp && pwm->tcp->ssh)
86 return read_hook_ssh (pwm->tcp->ssh, fd, data, len);
87 #endif
88 #ifdef WITH_GNUTLS
89 if (pwm && pwm->tcp && pwm->tcp->tls)
90 return read_hook_tls (pwm->tcp->tls, fd, data, len);
91 #endif
92 #endif
94 return read ((int) fd, data, len);
97 ssize_t
98 hook_write (assuan_context_t ctx, assuan_fd_t fd, const void *data,
99 size_t len)
101 ssize_t wrote;
102 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
103 pwm_t *pwm = assuan_get_pointer (ctx);
105 #ifdef WITH_SSH
106 if (pwm && pwm->tcp && pwm->tcp->ssh)
107 return write_hook_ssh (pwm->tcp->ssh, fd, data, len);
108 #endif
109 #ifdef WITH_GNUTLS
110 if (pwm && pwm->tcp && pwm->tcp->tls)
111 return write_hook_tls (pwm->tcp->tls, fd, data, len);
112 #endif
113 #endif
115 /* libassuan cannot handle EAGAIN when doing writes. */
118 wrote = write ((int) fd, data, len);
119 if (wrote == -1 && errno == EAGAIN)
120 usleep (50000);
122 while (wrote == -1 && errno == EAGAIN);
124 return wrote;
127 pid_t
128 hook_waitpid (assuan_context_t ctx, pid_t pid, int action, int *status,
129 int options)
131 return waitpid (pid, status, options);
134 gpg_error_t
135 pwmd_init ()
137 static int initialized;
139 #ifdef WITH_GNUTLS
140 // May be called more than once.
141 gnutls_global_init ();
142 #endif
144 if (initialized)
145 return 0;
147 #ifndef MEM_DEBUG
148 _xmem_init ();
149 #endif
150 #ifdef ENABLE_NLS
151 bindtextdomain ("libpwmd", LOCALEDIR);
152 #endif
153 #ifdef WITH_SSH
154 libssh2_init (0);
155 #endif
156 gpg_err_init ();
157 initialized = 1;
158 return 0;
161 void
162 pwmd_deinit ()
164 #ifdef WITH_GNUTLS
165 gnutls_global_deinit ();
166 #endif
169 gpg_error_t
170 _connect_finalize (pwm_t * pwm)
172 gpg_error_t rc = 0;
173 int active[2];
174 int n = assuan_get_active_fds (pwm->ctx, 0, active, N_ARRAY (active));
176 if (n <= 0)
177 return GPG_ERR_EBADFD;
179 pwm->fd = active[0];
180 #ifdef WITH_PINENTRY
181 pwm->pinentry_pid = -1;
182 #endif
184 if (pwm->name)
185 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION NAME=%s",
186 pwm->name);
188 return rc;
191 static gpg_error_t
192 connect_uds (pwm_t * pwm, const char *path)
194 char *socketpath = NULL;
195 struct passwd pw;
196 char *pwbuf;
197 gpg_error_t rc;
199 if (!pwm)
200 return GPG_ERR_INV_ARG;
202 pwbuf = _getpwuid (&pw);
203 if (!pwbuf)
204 return gpg_error_from_syserror ();
206 if (!path || !*path)
207 socketpath = pwmd_strdup_printf ("%s/.pwmd/socket", pw.pw_dir);
208 else
209 socketpath = _expand_homedir ((char *) path, &pw);
211 pwmd_free (pwbuf);
212 if (!socketpath)
213 return GPG_ERR_ENOMEM;
215 rc = assuan_socket_connect (pwm->ctx, socketpath, ASSUAN_INVALID_FD, 0);
216 pwmd_free (socketpath);
217 return rc ? rc : _connect_finalize (pwm);
220 static gpg_error_t
221 init_handle (pwm_t * pwm)
223 gpg_error_t rc;
224 static struct assuan_malloc_hooks mhooks = {
225 pwmd_malloc, pwmd_realloc, pwmd_free
227 static struct assuan_system_hooks shooks = {
228 ASSUAN_SYSTEM_HOOKS_VERSION,
229 __assuan_usleep,
230 __assuan_pipe,
231 __assuan_close,
232 hook_read,
233 hook_write,
234 //FIXME
235 NULL, //recvmsg
236 NULL, //sendmsg both are used for FD passing
237 __assuan_spawn,
238 hook_waitpid,
239 __assuan_socketpair,
240 __assuan_socket,
241 __assuan_connect
244 rc = assuan_new_ext (&pwm->ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, NULL,
245 NULL);
246 if (rc)
247 return rc;
249 assuan_set_pointer (pwm->ctx, pwm);
250 assuan_ctx_set_system_hooks (pwm->ctx, &shooks);
251 return 0;
254 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
255 void
256 free_tcp (struct tcp_s *tcp)
258 if (!tcp)
259 return;
261 #ifdef WITH_SSH
262 _free_ssh_conn (tcp->ssh);
263 #endif
264 #ifdef WITH_GNUTLS
265 tls_free (tcp->tls);
266 #endif
268 pwmd_free (tcp->host);
269 if (tcp->addrs)
271 freeaddrinfo (tcp->addrs);
272 tcp->addrs = NULL;
275 pwmd_free (tcp);
277 #endif
279 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
280 gpg_error_t
281 tcp_connect_common (pwm_t * pwm)
283 struct addrinfo hints = { 0 };
284 int n;
285 char portstr[6];
286 gpg_error_t rc = 0;
288 switch (pwm->prot)
290 case PWMD_IP_ANY:
291 hints.ai_family = AF_UNSPEC;
292 break;
293 case PWMD_IPV4:
294 hints.ai_family = AF_INET;
295 break;
296 case PWMD_IPV6:
297 hints.ai_family = AF_INET6;
298 break;
301 hints.ai_socktype = SOCK_STREAM;
302 snprintf (portstr, sizeof (portstr), "%i", pwm->tcp->port);
303 n = getaddrinfo (pwm->tcp->host, portstr, &hints, &pwm->tcp->addrs);
304 if (n)
306 fprintf (stderr, "%s\n", gai_strerror (n));
307 return GPG_ERR_UNKNOWN_HOST; //FIXME
310 for (pwm->tcp->addr = pwm->tcp->addrs; pwm->tcp->addr;
311 pwm->tcp->addr = pwm->tcp->addrs->ai_next)
313 pwm->fd = socket (pwm->tcp->addr->ai_family, SOCK_STREAM, 0);
314 if (pwm->fd == -1)
316 rc = gpg_error_from_syserror ();
317 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
318 break;
319 continue;
322 if (connect (pwm->fd, pwm->tcp->addr->ai_addr,
323 pwm->tcp->addr->ai_family == AF_INET6
324 ? sizeof (struct sockaddr_in6)
325 : sizeof (struct sockaddr)) == -1)
327 rc = gpg_error_from_syserror ();
328 close (pwm->fd);
329 pwm->fd = -1;
330 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
331 break;
332 continue;
335 rc = 0;
336 break;
339 return rc;
341 #endif
343 gpg_error_t
344 pwmd_connect (pwm_t * pwm, const char *url, ...)
346 const char *p = url;
347 gpg_error_t rc;
349 if (!pwm)
350 return FINISH (GPG_ERR_INV_ARG);
351 else if (!pwm->ctx)
353 rc = init_handle (pwm);
354 if (rc)
355 return rc;
358 rc = GPG_ERR_UNSUPPORTED_PROTOCOL;
360 if (p && *p == '/')
361 rc = connect_uds (pwm, p);
362 else if (!p || !strncmp (p, "file://", 7))
364 if (p)
365 p += 7;
366 rc = connect_uds (pwm, p);
368 else if (!strncmp (p, "ssh://", 6) || !strncmp (p, "ssh6://", 7) ||
369 !strncmp (p, "ssh4://", 7))
371 #ifndef WITH_SSH
372 return FINISH (GPG_ERR_NOT_IMPLEMENTED);
373 #else
374 char *host = NULL;
375 int port;
376 char *username = NULL;
378 if (!strncmp (p, "ssh6://", 7))
380 pwm->prot = PWMD_IPV6;
381 p += 7;
383 else if (!strncmp (p, "ssh4://", 7))
385 pwm->prot = PWMD_IPV4;
386 p += 7;
388 else
390 pwm->prot = PWMD_IP_ANY;
391 p += 6;
394 rc = _parse_ssh_url (p, &host, &port, &username);
395 if (!rc)
397 va_list ap;
398 char *identity = NULL;
399 char *knownhosts = NULL;
401 va_start (ap, url);
402 identity = va_arg (ap, char *);
404 if (!identity && !pwm->use_agent)
405 rc = GPG_ERR_INV_ARG;
406 else
407 knownhosts = va_arg (ap, char *);
409 va_end (ap);
411 if (!rc)
412 rc = _do_ssh_connect (pwm, host, port, identity, username,
413 knownhosts);
414 if (!rc)
416 rc = _connect_finalize (pwm);
417 if (rc)
419 free_tcp (pwm->tcp);
420 pwm->tcp = NULL;
425 pwmd_free (host);
426 pwmd_free (username);
427 return FINISH (rc);
428 #endif
430 else if (!strncmp (p, "tls://", 6) || !strncmp (p, "tls6://", 7) ||
431 !strncmp (p, "tls4://", 7))
433 #ifndef WITH_GNUTLS
434 return FINISH (GPG_ERR_NOT_IMPLEMENTED);
435 #else
436 char *host = NULL;
437 int port;
439 if (!strncmp (p, "tls6://", 7))
441 pwm->prot = PWMD_IPV6;
442 p += 7;
444 else if (!strncmp (p, "tls4://", 7))
446 pwm->prot = PWMD_IPV4;
447 p += 7;
449 else
451 pwm->prot = PWMD_IP_ANY;
452 p += 6;
455 rc = _parse_tls_url (p, &host, &port);
456 if (!rc)
458 va_list ap;
459 char *clientcert = NULL;
460 char *clientkey = NULL;
461 char *cacert = NULL;
462 char *prio = NULL;
463 char *server_fp = NULL;
465 va_start (ap, url);
466 clientcert = va_arg (ap, char *);
468 if (!clientcert)
469 rc = GPG_ERR_INV_ARG;
470 else
472 clientkey = va_arg (ap, char *);
473 if (!clientkey)
474 rc = GPG_ERR_INV_ARG;
475 else
477 cacert = va_arg (ap, char *);
478 if (!cacert)
479 rc = GPG_ERR_INV_ARG;
480 else
482 prio = va_arg (ap, char *);
483 server_fp = va_arg (ap, char *);
488 va_end (ap);
490 if (!rc)
491 rc = _do_tls_connect (pwm, host, port, clientcert, clientkey,
492 cacert, prio, server_fp, pwm->tls_verify);
493 if (!rc)
495 rc = _connect_finalize (pwm);
496 if (rc)
498 free_tcp (pwm->tcp);
499 pwm->tcp = NULL;
504 pwmd_free (host);
505 return FINISH (rc);
506 #endif
509 return FINISH (rc);
512 static void
513 disconnect (pwm_t * pwm)
515 if (!pwm)
516 return;
518 if (pwm->ctx)
519 assuan_release (pwm->ctx);
521 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
522 free_tcp (pwm->tcp);
523 pwm->tcp = NULL;
524 #endif
525 pwm->ctx = NULL;
526 pwm->fd = -1;
529 void
530 pwmd_close (pwm_t * pwm)
532 if (!pwm)
533 return;
535 disconnect (pwm);
536 pwmd_free (pwm->pinentry_error);
537 pwmd_free (pwm->pinentry_desc);
538 pwmd_free (pwm->pinentry_prompt);
539 pwmd_free (pwm->pinentry_tty);
540 pwmd_free (pwm->pinentry_display);
541 pwmd_free (pwm->pinentry_term);
542 pwmd_free (pwm->pinentry_lcctype);
543 pwmd_free (pwm->pinentry_lcmessages);
544 pwmd_free (pwm->filename);
545 pwmd_free (pwm->name);
547 #ifdef WITH_PINENTRY
548 if (pwm->pctx)
549 _pinentry_disconnect (pwm);
550 #endif
552 pwmd_free (pwm);
555 static gpg_error_t
556 inquire_realloc_cb (void *data, const void *buffer, size_t len)
558 membuf_t *mem = (membuf_t *) data;
559 void *p;
561 if (!buffer)
562 return 0;
564 if ((p = pwmd_realloc (mem->buf, mem->len + len)) == NULL)
565 return gpg_error (GPG_ERR_ENOMEM);
567 mem->buf = p;
568 memcpy ((char *) mem->buf + mem->len, buffer, len);
569 mem->len += len;
570 return 0;
573 static gpg_error_t
574 get_password (pwm_t * pwm, char **result, size_t * len,
575 pwmd_pinentry_t w, int echo)
577 char buf[LINE_MAX] = { 0 }, *p;
578 struct termios told, tnew;
579 char *key = NULL;
581 if (result)
582 *result = NULL;
584 if (len)
585 *len = 0;
587 if (!isatty (STDIN_FILENO))
589 fprintf (stderr, N_("Input is not from a terminal! Failing.\n"));
590 return GPG_ERR_ENOTTY;
593 if (!echo)
595 if (tcgetattr (STDIN_FILENO, &told) == -1)
596 return gpg_error_from_syserror ();
598 memcpy (&tnew, &told, sizeof (struct termios));
599 tnew.c_lflag &= ~(ECHO);
600 tnew.c_lflag |= ICANON | ECHONL;
602 if (tcsetattr (STDIN_FILENO, TCSANOW, &tnew) == -1)
604 int n = errno;
606 tcsetattr (STDIN_FILENO, TCSANOW, &told);
607 return gpg_error_from_errno (n);
611 switch (w)
613 case PWMD_PINENTRY_OPEN:
614 fprintf (stderr, N_("Password for %s: "), pwm->filename);
615 break;
616 case PWMD_PINENTRY_OPEN_FAILED:
617 fprintf (stderr, N_("Invalid password. Password for %s: "),
618 pwm->filename);
619 break;
620 case PWMD_PINENTRY_SAVE:
621 fprintf (stderr, N_("New password for %s: "), pwm->filename);
622 break;
623 case PWMD_PINENTRY_SAVE_CONFIRM:
624 fprintf (stderr, N_("Confirm password: "));
625 break;
626 default:
627 break;
630 if ((p = fgets (buf, sizeof (buf), stdin)) == NULL)
632 tcsetattr (STDIN_FILENO, TCSANOW, &told);
633 return 0;
636 if (!echo)
637 tcsetattr (STDIN_FILENO, TCSANOW, &told);
639 if (feof (stdin))
641 clearerr (stdin);
642 return GPG_ERR_CANCELED;
645 /* Strip the newline character. */
646 p[strlen (p) - 1] = 0;
648 if (buf[0])
650 key = pwmd_strdup_printf ("%s", p);
651 memset (buf, 0, sizeof (buf));
652 if (!key)
653 return GPG_ERR_ENOMEM;
655 if (result)
656 *result = key;
658 if (len)
659 *len = strlen (key);
661 else
663 if (result)
664 *result = pwmd_strdup ("");
666 if (len)
667 *len = 1;
670 return 0;
673 gpg_error_t
674 pwmd_password (pwm_t * pwm, const char *keyword, char **data, size_t * size)
676 gpg_error_t rc;
677 int new_password = 0;
678 char *password = NULL, *newpass = NULL;
679 int error = 0;
681 if (data)
682 *data = NULL;
684 if (size)
685 *size = 0;
687 if (!strcmp (keyword, "NEW_PASSPHRASE"))
688 new_password = 1;
690 if (!new_password && pwm->pinentry_try)
691 error = 1;
693 again:
694 if (pwm->disable_pinentry)
696 rc = get_password (pwm, &password, size,
697 new_password ? PWMD_PINENTRY_SAVE :
698 error ? PWMD_PINENTRY_OPEN_FAILED :
699 PWMD_PINENTRY_OPEN, 0);
700 if (!rc && new_password)
701 rc = get_password (pwm, &newpass, size, PWMD_PINENTRY_SAVE_CONFIRM,
704 else
706 pwmd_pinentry_t which;
708 if (error)
709 which = new_password
710 ? PWMD_PINENTRY_SAVE_FAILED : PWMD_PINENTRY_OPEN_FAILED;
711 else
712 which = new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN;
714 rc = pwmd_getpin (pwm, pwm->filename, &password, size, which);
715 if (!rc && new_password)
716 rc = pwmd_getpin (pwm, pwm->filename, &newpass, size,
717 PWMD_PINENTRY_SAVE_CONFIRM);
720 if (!rc && new_password)
722 if ((!password && newpass) || (!newpass && password)
723 || strcmp (newpass, password))
725 if (pwm->disable_pinentry)
726 fprintf (stderr, N_("Passphrases do not match.\n"));
728 pwmd_free (password);
729 pwmd_free (newpass);
730 password = newpass = NULL;
731 error = 1;
732 goto again;
736 (void) pwmd_getpin (pwm, pwm->filename, NULL, NULL, PWMD_PINENTRY_CLOSE);
737 pwmd_free (newpass);
738 if (!rc && data)
739 *data = password;
741 return rc;
744 static gpg_error_t
745 inquire_cb (void *data, const char *keyword)
747 pwm_t *pwm = (pwm_t *) data;
748 gpg_error_t rc = 0;
749 int free_result = 0;
750 char *result = NULL;
752 /* Shouldn't get this far without a callback. */
753 if (!pwm->override_inquire && !pwm->inquire_func)
754 return gpg_error (GPG_ERR_ASS_NO_INQUIRE_CB);
756 for (;;)
758 size_t len = 0;
759 gpg_error_t arc;
760 int is_password = 0;
761 int new_password = 0;
763 result = NULL;
765 if (!strcmp (keyword, "PASSPHRASE"))
766 is_password = 1;
767 else if (!strcmp (keyword, "NEW_PASSPHRASE"))
768 new_password = 1;
770 if (!pwm->override_inquire && (is_password || new_password))
772 free_result = 1;
773 rc = pwmd_password (data, keyword, &result, &len);
774 if (!rc)
775 rc = GPG_ERR_EOF;
777 else
778 rc = pwm->inquire_func (pwm->inquire_data, keyword, rc, &result,
779 &len);
781 cancel:
782 if (rc && gpg_err_code (rc) != GPG_ERR_EOF)
784 gpg_error_t trc = rc;
786 /* Cancel this inquire. */
787 rc = assuan_send_data (pwm->ctx, NULL, 1);
788 if (!rc)
790 char *line;
791 size_t len;
793 /* There is a bug (or feature?) in assuan_send_data() that
794 * when cancelling an inquire the next read from the server is
795 * not done until the next command making the next command
796 * fail with GPG_ERR_ASS_UNEXPECTED_CMD.
798 rc = assuan_read_line (pwm->ctx, &line, &len);
800 /* Restore the original error. This differs from the error
801 * returned from the pwmd command (GPG_ERR_CANCELED). This
802 * error is returned to the calling function.
804 if (!rc)
805 rc = trc;
808 break;
811 if (gpg_err_code (rc) == GPG_ERR_EOF || !rc)
813 if (len <= 0 && !result)
815 rc = 0;
816 break;
818 else if ((len <= 0 && result) || (len && !result))
820 rc = gpg_error (GPG_ERR_INV_ARG);
821 break;
824 if (pwm->inquire_maxlen
825 && pwm->inquire_sent + len > pwm->inquire_maxlen)
827 rc = gpg_error (GPG_ERR_TOO_LARGE);
828 if (!free_result)
829 rc = pwm->inquire_func (pwm->inquire_data, keyword, rc,
830 &result, &len);
831 goto cancel;
834 arc = assuan_send_data (pwm->ctx, result, len);
835 if (gpg_err_code (rc) == GPG_ERR_EOF)
837 rc = arc;
838 break;
841 rc = arc;
843 else if (rc)
844 break;
846 if (!rc)
848 pwm->inquire_sent += len;
850 if (pwm->status_func)
852 char buf[ASSUAN_LINELENGTH];
854 snprintf (buf, sizeof (buf), "XFER %lu %lu", pwm->inquire_sent,
855 pwm->inquire_total);
856 rc = pwm->status_func (pwm->status_data, buf);
857 if (rc)
858 continue;
863 if (free_result)
864 pwmd_free (result);
866 return rc;
869 static gpg_error_t
870 parse_assuan_line (pwm_t * pwm)
872 gpg_error_t rc;
873 char *line;
874 size_t len;
876 rc = assuan_read_line (pwm->ctx, &line, &len);
877 if (!rc)
879 if (line[0] == 'O' && line[1] == 'K' &&
880 (line[2] == 0 || line[2] == ' '))
883 else if (line[0] == '#')
886 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' '))
888 if (pwm->status_func)
890 rc = pwm->status_func (pwm->status_data,
891 line[1] == 0 ? line + 1 : line + 2);
894 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
895 (line[3] == 0 || line[3] == ' '))
897 line += 4;
898 rc = strtol (line, NULL, 10);
902 return rc;
905 static void
906 reset_handle_state (pwm_t * pwm, int done)
908 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
909 if (pwm->tcp)
910 pwm->tcp->rc = 0;
912 if (done)
914 free_tcp (pwm->tcp);
915 pwm->tcp = NULL;
917 #endif
920 static void
921 reset_handle (pwm_t * h)
923 h->fd = -1;
924 #ifdef WITH_PINENTRY
925 if (h->pctx)
926 _pinentry_disconnect (h);
927 #endif
928 reset_handle_state (h, 0);
931 gpg_error_t
932 pwmd_disconnect (pwm_t * pwm)
934 if (!pwm)
935 return FINISH (GPG_ERR_INV_ARG);
937 if (pwm->fd == -1)
938 return FINISH (GPG_ERR_INV_STATE);
940 disconnect (pwm);
941 reset_handle (pwm);
942 return 0;
945 /* Note that this should only be called when not in a command. */
946 gpg_error_t
947 pwmd_process (pwm_t * pwm)
949 gpg_error_t rc = 0;
950 fd_set fds;
951 struct timeval tv = { 0, 0 };
952 int n;
954 if (!pwm)
955 return FINISH (GPG_ERR_INV_ARG);
956 else if (!pwm->ctx)
957 return FINISH (GPG_ERR_INV_STATE);
959 FD_ZERO (&fds);
960 FD_SET (pwm->fd, &fds);
961 n = select (pwm->fd + 1, &fds, NULL, NULL, &tv);
963 if (n == -1)
964 return FINISH (gpg_error_from_syserror ());
966 if (n > 0)
968 if (FD_ISSET (pwm->fd, &fds))
969 rc = parse_assuan_line (pwm);
972 while (!rc && assuan_pending_line (pwm->ctx))
973 rc = parse_assuan_line (pwm);
975 #if defined (WITH_SSH) || defined (WITH_GNUTLS)
976 if (gpg_err_code (rc) == GPG_ERR_EOF && pwm->tcp)
978 close (pwm->fd);
979 pwm->fd = -1;
981 #endif
983 return FINISH (rc);
986 static gpg_error_t
987 status_cb (void *data, const char *line)
989 pwm_t *pwm = data;
991 if (!strncmp (line, "INQUIRE_MAXLEN ", 15))
992 pwm->inquire_maxlen = strtol (line + 15, NULL, 10);
994 if (pwm->status_func)
995 return pwm->status_func (pwm->status_data, line);
997 return 0;
1000 gpg_error_t
1001 _assuan_command (pwm_t * pwm, assuan_context_t ctx,
1002 char **result, size_t * len, const char *cmd)
1004 membuf_t data;
1005 gpg_error_t rc;
1007 if (!cmd || !*cmd)
1008 return FINISH (GPG_ERR_INV_ARG);
1010 if (strlen (cmd) >= ASSUAN_LINELENGTH + 1)
1011 return FINISH (GPG_ERR_LINE_TOO_LONG);
1013 data.len = 0;
1014 data.buf = NULL;
1015 rc = assuan_transact (ctx, cmd, inquire_realloc_cb, &data,
1016 #ifdef WITH_QUALITY
1017 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
1018 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
1019 #else
1020 inquire_cb, pwm,
1021 #endif
1022 status_cb, pwm);
1024 if (rc)
1026 if (data.buf)
1028 pwmd_free (data.buf);
1029 data.buf = NULL;
1032 else
1034 if (data.buf)
1036 inquire_realloc_cb (&data, "", 1);
1038 if (result)
1039 *result = (char *) data.buf;
1040 else
1041 pwmd_free (data.buf);
1043 if (len)
1044 *len = data.len;
1048 pwm->inquire_maxlen = 0;
1049 return rc;
1052 gpg_error_t
1053 pwmd_command_ap (pwm_t * pwm, char **result, size_t * rlen,
1054 pwmd_inquire_cb_t func, void *user, const char *cmd,
1055 va_list ap)
1057 char *buf;
1058 size_t len;
1059 va_list ap2;
1061 if (!pwm || !cmd)
1062 return FINISH (GPG_ERR_INV_ARG);
1063 if (!pwm->ctx)
1064 return FINISH (GPG_ERR_INV_STATE);
1067 * C99 allows the dst pointer to be null which will calculate the length
1068 * of the would-be result and return it.
1070 va_copy (ap2, ap);
1071 len = vsnprintf (NULL, 0, cmd, ap) + 1;
1072 buf = (char *) pwmd_malloc (len);
1073 if (!buf)
1075 va_end (ap2);
1076 return FINISH (GPG_ERR_ENOMEM);
1079 len = vsnprintf (buf, len, cmd, ap2);
1080 va_end (ap2);
1082 if (buf[strlen (buf) - 1] == '\n')
1083 buf[strlen (buf) - 1] = 0;
1084 if (buf[strlen (buf) - 1] == '\r')
1085 buf[strlen (buf) - 1] = 0;
1087 pwm->inquire_func = func;
1088 pwm->inquire_data = user;
1089 pwm->inquire_sent = 0;
1090 gpg_error_t rc = _assuan_command (pwm, pwm->ctx, result, rlen, buf);
1091 pwmd_free (buf);
1092 return rc;
1095 gpg_error_t
1096 pwmd_command (pwm_t * pwm, char **result, size_t * len,
1097 pwmd_inquire_cb_t func, void *user, const char *cmd, ...)
1099 va_list ap;
1101 if (!pwm || !cmd)
1102 return FINISH (GPG_ERR_INV_ARG);
1103 if (!pwm->ctx)
1104 return FINISH (GPG_ERR_INV_STATE);
1106 if (result)
1107 *result = NULL;
1109 va_start (ap, cmd);
1110 gpg_error_t rc = pwmd_command_ap (pwm, result, len, func, user, cmd, ap);
1111 va_end (ap);
1112 return rc;
1115 static gpg_error_t
1116 send_pinentry_options (pwm_t * pwm)
1118 gpg_error_t rc;
1120 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL,
1121 "OPTION disable-pinentry=0");
1122 if (rc)
1123 return rc;
1125 if (pwm->pinentry_tty)
1127 rc =
1128 pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION TTYNAME=%s",
1129 pwm->pinentry_tty);
1130 if (rc)
1131 return rc;
1134 if (pwm->pinentry_term)
1136 rc =
1137 pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION TTYTYPE=%s",
1138 pwm->pinentry_term);
1139 if (rc)
1140 return rc;
1143 if (pwm->pinentry_display)
1145 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION DISPLAY=%s",
1146 pwm->pinentry_display);
1147 if (rc)
1148 return rc;
1151 if (pwm->pinentry_desc)
1153 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION DESC=%s",
1154 pwm->pinentry_desc);
1155 if (rc)
1156 return rc;
1159 if (pwm->pinentry_lcctype)
1161 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION LC_CTYPE=%s",
1162 pwm->pinentry_lcctype);
1163 if (rc)
1164 return rc;
1167 if (pwm->pinentry_lcmessages)
1169 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION LC_MESSAGES=%s",
1170 pwm->pinentry_lcmessages);
1171 if (rc)
1172 return rc;
1175 return 0;
1178 gpg_error_t
1179 pwmd_socket_type (pwm_t * pwm, pwmd_socket_t * result)
1181 if (!pwm || !result)
1182 return FINISH (GPG_ERR_INV_ARG);
1184 *result = PWMD_SOCKET_LOCAL;
1186 if (pwm->fd == -1)
1187 return FINISH (GPG_ERR_INV_STATE);
1189 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1190 #ifdef WITH_SSH
1191 if (pwm->tcp && pwm->tcp->ssh)
1192 *result = PWMD_SOCKET_SSH;
1193 #endif
1194 #ifdef WITH_GNUTLS
1195 if (pwm->tcp && pwm->tcp->tls)
1196 *result = PWMD_SOCKET_TLS;
1197 #endif
1198 #endif
1199 return 0;
1202 gpg_error_t
1203 pwmd_open (pwm_t * pwm, const char *filename, pwmd_inquire_cb_t cb,
1204 void *data)
1206 gpg_error_t rc = 0;
1207 int no_pinentry;
1209 if (!pwm || !filename || !*filename)
1210 return FINISH (GPG_ERR_INV_ARG);
1212 if (!pwm->ctx)
1213 return FINISH (GPG_ERR_INV_STATE);
1215 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1216 no_pinentry = pwm->disable_pinentry || pwm->tcp || pwm->local_pinentry;
1217 #else
1218 no_pinentry = pwm->disable_pinentry || pwm->local_pinentry;
1219 #endif
1221 if (!no_pinentry)
1222 rc = send_pinentry_options (pwm);
1223 else
1224 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL,
1225 "OPTION disable-pinentry");
1227 if (!rc)
1229 pwm->pinentry_try = 0;
1230 pwmd_free (pwm->filename);
1231 pwm->filename = pwmd_strdup (filename);
1235 rc = pwmd_command (pwm, NULL, NULL, cb, data, "OPEN %s%s",
1236 (pwm->opts & OPT_LOCK_ON_OPEN) ? "--lock " : "",
1237 filename);
1239 while (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
1240 && no_pinentry && ++pwm->pinentry_try < pwm->pinentry_tries);
1242 pwm->pinentry_try = 0;
1244 if (rc)
1246 pwmd_free (pwm->filename);
1247 pwm->filename = NULL;
1251 return FINISH (rc);
1254 gpg_error_t
1255 pwmd_save (pwm_t * pwm, const char *args, pwmd_inquire_cb_t cb, void *data)
1257 gpg_error_t rc;
1259 if (!pwm)
1260 return FINISH (GPG_ERR_INV_ARG);
1261 if (!pwm->ctx)
1262 return FINISH (GPG_ERR_INV_STATE);
1264 rc = pwmd_command (pwm, NULL, NULL, cb, data, "SAVE %s", args ? args : "");
1265 return FINISH (rc);
1268 gpg_error_t
1269 pwmd_setopt (pwm_t * pwm, pwmd_option_t opt, ...)
1271 va_list ap;
1272 int n;
1273 char *arg1;
1274 gpg_error_t rc = 0;
1276 if (!pwm)
1277 return FINISH (GPG_ERR_INV_ARG);
1279 va_start (ap, opt);
1281 switch (opt)
1283 case PWMD_OPTION_LOCK_ON_OPEN:
1284 n = va_arg (ap, int);
1286 if (n < 0 || n > 1)
1287 rc = GPG_ERR_INV_VALUE;
1289 if (n)
1290 pwm->opts |= OPT_LOCK_ON_OPEN;
1291 else
1292 pwm->opts &= ~OPT_LOCK_ON_OPEN;
1294 break;
1295 case PWMD_OPTION_INQUIRE_TOTAL:
1296 pwm->inquire_total = va_arg (ap, size_t);
1297 break;
1298 case PWMD_OPTION_STATUS_CB:
1299 pwm->status_func = va_arg (ap, pwmd_status_cb_t);
1300 break;
1301 case PWMD_OPTION_STATUS_DATA:
1302 pwm->status_data = va_arg (ap, void *);
1303 break;
1304 case PWMD_OPTION_NO_PINENTRY:
1305 n = va_arg (ap, int);
1307 if (n < 0 || n > 1)
1308 rc = GPG_ERR_INV_VALUE;
1309 else
1310 pwm->disable_pinentry = n;
1312 break;
1313 case PWMD_OPTION_LOCAL_PINENTRY:
1314 n = va_arg (ap, int);
1316 if (n < 0 || n > 1)
1317 rc = GPG_ERR_INV_VALUE;
1318 else
1319 pwm->local_pinentry = n;
1321 break;
1322 case PWMD_OPTION_PINENTRY_TIMEOUT:
1323 n = va_arg (ap, int);
1325 if (n < 0)
1326 rc = GPG_ERR_INV_VALUE;
1327 else
1328 pwm->pinentry_timeout = n;
1330 break;
1331 case PWMD_OPTION_PINENTRY_TRIES:
1332 n = va_arg (ap, int);
1333 pwm->pinentry_tries = n;
1334 break;
1335 case PWMD_OPTION_PINENTRY_PATH:
1336 arg1 = va_arg (ap, char *);
1337 pwmd_free (pwm->pinentry_path);
1338 pwm->pinentry_path = arg1 ? _expand_homedir (arg1, NULL) : NULL;
1339 break;
1340 case PWMD_OPTION_PINENTRY_TTY:
1341 arg1 = va_arg (ap, char *);
1342 pwmd_free (pwm->pinentry_tty);
1343 pwm->pinentry_tty = arg1 ? pwmd_strdup (arg1) : NULL;
1344 break;
1345 case PWMD_OPTION_PINENTRY_DISPLAY:
1346 arg1 = va_arg (ap, char *);
1347 pwmd_free (pwm->pinentry_display);
1348 pwm->pinentry_display = arg1 ? pwmd_strdup (arg1) : NULL;
1349 break;
1350 case PWMD_OPTION_PINENTRY_TERM:
1351 arg1 = va_arg (ap, char *);
1352 pwmd_free (pwm->pinentry_term);
1353 pwm->pinentry_term = arg1 ? pwmd_strdup (arg1) : NULL;
1354 break;
1355 case PWMD_OPTION_PINENTRY_ERROR:
1356 arg1 = va_arg (ap, char *);
1357 pwmd_free (pwm->pinentry_error);
1358 pwm->pinentry_error = arg1 ? _percent_escape (arg1) : NULL;
1359 break;
1360 case PWMD_OPTION_PINENTRY_PROMPT:
1361 arg1 = va_arg (ap, char *);
1362 pwmd_free (pwm->pinentry_prompt);
1363 pwm->pinentry_prompt = arg1 ? _percent_escape (arg1) : NULL;
1364 break;
1365 case PWMD_OPTION_PINENTRY_DESC:
1366 arg1 = va_arg (ap, char *);
1367 pwmd_free (pwm->pinentry_desc);
1368 pwm->pinentry_desc = arg1 ? _percent_escape (arg1) : NULL;
1369 break;
1370 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1371 arg1 = va_arg (ap, char *);
1372 pwmd_free (pwm->pinentry_lcctype);
1373 pwm->pinentry_lcctype = arg1 ? pwmd_strdup (arg1) : NULL;
1374 break;
1375 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1376 arg1 = va_arg (ap, char *);
1377 pwmd_free (pwm->pinentry_lcmessages);
1378 pwm->pinentry_lcmessages = arg1 ? pwmd_strdup (arg1) : NULL;
1379 break;
1380 case PWMD_OPTION_KNOWNHOST_CB:
1381 pwm->kh_cb = va_arg (ap, pwmd_knownhost_cb_t);
1382 break;
1383 case PWMD_OPTION_KNOWNHOST_DATA:
1384 pwm->kh_data = va_arg (ap, void *);
1385 break;
1386 case PWMD_OPTION_SSH_AGENT:
1387 pwm->use_agent = va_arg (ap, int);
1389 if (pwm->use_agent < 0 || pwm->use_agent > 1)
1391 pwm->use_agent = 0;
1392 rc = GPG_ERR_INV_VALUE;
1394 break;
1395 case PWMD_OPTION_TLS_VERIFY:
1396 pwm->tls_verify = va_arg (ap, int);
1398 if (pwm->tls_verify < 0 || pwm->tls_verify > 1)
1400 pwm->tls_verify = 0;
1401 rc = GPG_ERR_INV_VALUE;
1403 break;
1404 case PWMD_OPTION_SOCKET_TIMEOUT:
1405 pwm->socket_timeout = va_arg (ap, int);
1406 if (pwm->socket_timeout < 0)
1408 pwm->socket_timeout = 0;
1409 rc = GPG_ERR_INV_VALUE;
1412 #ifdef WITH_SSH
1413 if (pwm->tcp && pwm->tcp->ssh && pwm->tcp->ssh->session)
1414 pwm->tcp->ssh->timeout = pwm->socket_timeout;
1415 #endif
1416 #ifdef WITH_GNUTLS
1417 if (pwm->tcp && pwm->tcp->tls && pwm->tcp->tls->session)
1418 pwm->tcp->tls->timeout = pwm->socket_timeout;
1419 #endif
1420 break;
1421 case PWMD_OPTION_OVERRIDE_INQUIRE:
1422 pwm->override_inquire = va_arg (ap, int);
1424 if (pwm->override_inquire < 0 || pwm->override_inquire > 1)
1426 pwm->override_inquire = 0;
1427 rc = GPG_ERR_INV_VALUE;
1429 break;
1430 default:
1431 rc = GPG_ERR_UNKNOWN_OPTION;
1432 break;
1435 va_end (ap);
1436 return FINISH (rc);
1439 gpg_error_t
1440 pwmd_new (const char *name, pwm_t ** pwm)
1442 pwm_t *h = pwmd_calloc (1, sizeof (pwm_t));
1443 gpg_error_t rc;
1445 if (!h)
1446 return FINISH (GPG_ERR_ENOMEM);
1448 if (name)
1450 h->name = pwmd_strdup (name);
1451 if (!h->name)
1453 pwmd_free (h);
1454 return FINISH (GPG_ERR_ENOMEM);
1458 reset_handle (h);
1459 h->pinentry_timeout = -30;
1460 h->pinentry_tries = 3;
1461 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1462 h->prot = PWMD_IP_ANY;
1463 #endif
1465 if (ttyname (STDOUT_FILENO))
1467 char buf[256];
1469 ttyname_r (STDOUT_FILENO, buf, sizeof (buf));
1470 h->pinentry_tty = pwmd_strdup (buf);
1471 if (!h->pinentry_tty)
1473 rc = GPG_ERR_ENOMEM;
1474 goto fail;
1478 if (getenv ("TERM") && h->pinentry_tty)
1480 h->pinentry_term = pwmd_strdup (getenv ("TERM"));
1481 if (!h->pinentry_term)
1483 rc = GPG_ERR_ENOMEM;
1484 goto fail;
1488 if (getenv ("DISPLAY"))
1490 h->pinentry_display = pwmd_strdup (getenv ("DISPLAY"));
1491 if (!h->pinentry_display)
1493 rc = GPG_ERR_ENOMEM;
1494 goto fail;
1498 update_pinentry_settings (h);
1499 *pwm = h;
1500 return 0;
1502 fail:
1503 pwmd_close (h);
1504 return FINISH (rc);
1507 void
1508 pwmd_free (void *ptr)
1510 _xfree (ptr);
1513 void *
1514 pwmd_malloc (size_t size)
1516 return _xmalloc (size);
1519 void *
1520 pwmd_calloc (size_t nmemb, size_t size)
1522 return _xcalloc (nmemb, size);
1525 void *
1526 pwmd_realloc (void *ptr, size_t size)
1528 return _xrealloc (ptr, size);
1531 char *
1532 pwmd_strdup (const char *str)
1534 char *t;
1535 size_t len;
1536 register size_t c;
1538 len = strlen (str);
1539 t = _xmalloc ((len + 1) * sizeof (char));
1540 if (!t)
1541 return NULL;
1543 for (c = 0; c < len; c++)
1544 t[c] = str[c];
1546 t[c] = 0;
1547 return t;
1550 char *
1551 pwmd_strdup_printf (const char *fmt, ...)
1553 va_list ap, ap2;
1554 int len;
1555 char *buf;
1557 if (!fmt)
1558 return NULL;
1560 va_start (ap, fmt);
1561 va_copy (ap2, ap);
1562 len = vsnprintf (NULL, 0, fmt, ap);
1563 va_end (ap);
1564 buf = pwmd_malloc (++len);
1565 if (buf)
1566 vsnprintf (buf, len, fmt, ap2);
1568 va_end (ap2);
1569 return buf;
1572 gpg_error_t
1573 pwmd_getpin (pwm_t * pwm, const char *filename, char **result,
1574 size_t * len, pwmd_pinentry_t which)
1576 #ifndef WITH_PINENTRY
1577 return FINISH (GPG_ERR_NOT_IMPLEMENTED);
1578 #else
1579 gpg_error_t rc = _pwmd_getpin (pwm, filename, result, len, which);
1581 return FINISH (rc);
1582 #endif
1585 const char *
1586 pwmd_version ()
1588 return LIBPWMD_VERSION_STR;
1591 unsigned int
1592 pwmd_features ()
1594 unsigned int n = 0;
1596 #ifdef WITH_PINENTRY
1597 n |= PWMD_FEATURE_PINENTRY;
1598 #endif
1599 #ifdef WITH_SSH
1600 n |= PWMD_FEATURE_SSH;
1601 #endif
1602 #ifdef WITH_QUALITY
1603 n |= PWMD_FEATURE_CRACK;
1604 #endif
1605 #ifdef WITH_GNUTLS
1606 n |= PWMD_FEATURE_GNUTLS;
1607 #endif
1608 return n;
1611 gpg_error_t
1612 pwmd_fd (pwm_t * pwm, int *fd)
1614 if (!pwm || !fd)
1615 return FINISH (GPG_ERR_INV_ARG);
1617 if (pwm->fd == -1)
1618 return FINISH (GPG_ERR_INV_STATE);
1620 *fd = pwm->fd;
1621 return 0;