Be sure ctx->fd == -1 when calling pwmd_connect*().
[libpwmd.git] / src / libpwmd.c
blob418dfdfec121a8aec22078868141b52b40b978fb
1 /*
2 Copyright (C) 2006-2016, 2017 Ben Kibbey <bjk@luxsci.net>
4 This file is part of libpwmd.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA
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/types.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35 #include <signal.h>
36 #include <stdarg.h>
37 #include <string.h>
38 #include <sys/wait.h>
39 #include <fcntl.h>
40 #include <pwd.h>
41 #include <time.h>
42 #include <limits.h>
43 #include <sys/select.h>
44 #include <termios.h>
45 #include <libpwmd.h>
47 #ifdef HAVE_STRINGS_H
48 #include <strings.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 #ifdef WITH_QUALITY
64 #include "zxcvbn.h"
65 #endif
67 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
68 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <netdb.h>
71 #include <netinet/in.h>
72 #endif
74 enum
76 PWMD_WHICH_SAVE,
77 PWMD_WHICH_PASSWD,
78 PWMD_WHICH_GENKEY
81 #define FINISH(rc) (gpg_err_source(rc) == GPG_ERR_SOURCE_UNKNOWN) \
82 ? gpg_error(rc) : rc
84 typedef struct
86 size_t len;
87 void *buf;
88 } membuf_t;
90 static gpg_error_t status_cb (void *data, const char *line);
92 ssize_t
93 hook_read (assuan_context_t ctx, assuan_fd_t fd, void *data, size_t len)
95 pwm_t *pwm = assuan_get_pointer (ctx);
97 if (pwm->read_cb)
98 return pwm->read_cb (pwm->read_cb_data, fd, data, len);
100 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
101 #ifdef WITH_SSH
102 if (pwm && pwm->tcp && pwm->tcp->ssh)
103 return read_hook_ssh (pwm->tcp->ssh, fd, data, len);
104 #endif
105 #ifdef WITH_GNUTLS
106 if (pwm && pwm->tcp && pwm->tcp->tls)
107 return tls_read_hook (pwm, fd, data, len);
108 #endif
109 #endif
111 return read ((int) fd, data, len);
114 ssize_t
115 hook_write (assuan_context_t ctx, assuan_fd_t fd, const void *data,
116 size_t len)
118 pwm_t *pwm = assuan_get_pointer (ctx);
119 ssize_t wrote;
121 if (pwm->write_cb)
122 return pwm->write_cb (pwm->write_cb_data, fd, data, len);
124 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
125 #ifdef WITH_SSH
126 if (pwm && pwm->tcp && pwm->tcp->ssh)
127 return write_hook_ssh (pwm->tcp->ssh, fd, data, len);
128 #endif
129 #ifdef WITH_GNUTLS
130 if (pwm && pwm->tcp && pwm->tcp->tls)
131 return tls_write_hook (pwm, fd, data, len);
132 #endif
133 #endif
135 /* libassuan cannot handle EAGAIN when doing writes. */
138 wrote = write ((int) fd, data, len);
139 if (wrote == -1 && errno == EAGAIN)
140 usleep (50000);
142 while (wrote == -1 && errno == EAGAIN);
144 return wrote;
147 pid_t
148 hook_waitpid (assuan_context_t ctx, pid_t pid, int action, int *status,
149 int options)
151 (void)ctx;
152 (void)action;
153 return waitpid (pid, status, options);
156 gpg_error_t
157 pwmd_init ()
159 static int initialized;
161 #ifdef WITH_GNUTLS
162 // May be called more than once.
163 gnutls_global_init ();
164 #endif
166 if (initialized)
167 return 0;
169 #ifdef ENABLE_NLS
170 bindtextdomain ("libpwmd", LOCALEDIR);
171 #endif
172 #ifdef WITH_SSH
173 libssh2_init (0);
174 #endif
175 gpgrt_init ();
176 //gpgrt_set_alloc_func (_xrealloc_gpgrt);
178 #ifdef WITH_QUALITY
179 ZxcvbnInit (NULL);
180 #endif
182 initialized = 1;
183 return 0;
186 void
187 pwmd_deinit ()
189 #ifdef WITH_GNUTLS
190 gnutls_global_deinit ();
191 #endif
192 #ifdef WITH_QUALITY
193 ZxcvbnUnInit();
194 #endif
197 gpg_error_t
198 _connect_finalize (pwm_t * pwm)
200 gpg_error_t rc = 0;
201 char *result = NULL;
202 int active[2];
203 int n = assuan_get_active_fds (pwm->ctx, 0, active, N_ARRAY (active));
205 if (n <= 0)
206 return GPG_ERR_EBADFD;
208 pwm->fd = active[0];
209 #ifdef WITH_PINENTRY
210 pwm->pinentry_pid = -1;
211 #endif
213 rc = pwmd_command (pwm, &result, NULL, NULL, NULL, "GETINFO VERSION");
214 if (!rc)
216 pwm->version = strtoul (result, NULL, 16);
217 pwmd_free (result);
220 if (!rc && pwm->name)
221 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION NAME=%s",
222 pwm->name);
224 return rc;
227 static gpg_error_t
228 connect_uds (pwm_t * pwm, const char *path)
230 char *socketpath = NULL;
231 struct passwd pw;
232 char *pwbuf;
233 gpg_error_t rc;
235 if (!pwm)
236 return GPG_ERR_INV_ARG;
238 pwbuf = _getpwuid (&pw);
239 if (!pwbuf)
240 return gpg_error_from_syserror ();
242 if (!path || !*path)
243 socketpath = pwmd_strdup_printf ("%s/.pwmd/socket", pw.pw_dir);
244 else
245 socketpath = _expand_homedir ((char *) path, &pw);
247 pwmd_free (pwbuf);
248 if (!socketpath)
249 return GPG_ERR_ENOMEM;
251 rc = assuan_socket_connect (pwm->ctx, socketpath, ASSUAN_INVALID_FD, 0);
252 pwmd_free (socketpath);
253 return rc ? rc : _connect_finalize (pwm);
256 static gpg_error_t
257 init_handle (pwm_t * pwm)
259 gpg_error_t rc;
260 static struct assuan_malloc_hooks mhooks = {
261 pwmd_malloc, pwmd_realloc, pwmd_free
263 static struct assuan_system_hooks shooks = {
264 ASSUAN_SYSTEM_HOOKS_VERSION,
265 __assuan_usleep,
266 __assuan_pipe,
267 __assuan_close,
268 hook_read,
269 hook_write,
270 //FIXME
271 NULL, //recvmsg
272 NULL, //sendmsg both are used for FD passing
273 __assuan_spawn,
274 hook_waitpid,
275 __assuan_socketpair,
276 __assuan_socket,
277 __assuan_connect
280 rc = assuan_new_ext (&pwm->ctx, GPG_ERR_SOURCE_DEFAULT, &mhooks, NULL,
281 NULL);
282 if (rc)
283 return rc;
285 assuan_set_pointer (pwm->ctx, pwm);
286 assuan_ctx_set_system_hooks (pwm->ctx, &shooks);
287 return 0;
290 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
291 void
292 free_tcp (pwm_t *pwm)
294 struct tcp_s *tcp = pwm->tcp;
296 if (!tcp)
297 return;
299 #ifdef WITH_SSH
300 _free_ssh_conn (tcp->ssh);
301 #endif
302 #ifdef WITH_GNUTLS
303 tls_free (pwm);
304 #endif
306 pwmd_free (tcp->host);
307 if (tcp->addrs)
309 freeaddrinfo (tcp->addrs);
310 tcp->addrs = NULL;
313 pthread_cond_destroy (&tcp->dns_cond);
314 pthread_mutex_destroy (&tcp->dns_mutex);
315 pwmd_free (tcp);
316 pwm->tcp = NULL;
318 #endif
320 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
321 static void *
322 resolve_host_thread (void *arg)
324 pwm_t *pwm = arg;
325 struct addrinfo hints = { 0 };
326 char portstr[6];
328 switch (pwm->prot)
330 case PWMD_IP_ANY:
331 hints.ai_family = AF_UNSPEC;
332 break;
333 case PWMD_IPV4:
334 hints.ai_family = AF_INET;
335 break;
336 case PWMD_IPV6:
337 hints.ai_family = AF_INET6;
338 break;
341 hints.ai_socktype = SOCK_STREAM;
342 snprintf (portstr, sizeof (portstr), "%i", pwm->tcp->port);
343 int *n = pwmd_malloc (sizeof (int));
344 pthread_cleanup_push (pwmd_free, n);
345 *n = getaddrinfo (pwm->tcp->host, portstr, &hints, &pwm->tcp->addrs);
346 pthread_cleanup_pop (0);
347 pthread_cond_broadcast (&pwm->tcp->dns_cond);
348 pthread_exit (n);
349 return NULL;
352 gpg_error_t
353 tcp_connect_common (pwm_t * pwm)
355 #define TS_TIMEOUT 50000000L
356 int n;
357 gpg_error_t rc = 0;
358 pthread_t tid;
359 time_t now;
361 time (&now);
362 n = pthread_create (&tid, NULL, resolve_host_thread, pwm);
363 if (n)
364 return gpg_error_from_errno (n);
366 pthread_mutex_lock (&pwm->tcp->dns_mutex);
368 for (;;)
370 struct timespec ts;
371 int *result = NULL;
373 clock_gettime (CLOCK_REALTIME, &ts);
374 if (ts.tv_nsec + TS_TIMEOUT >= 1000000000LL) {
375 ts.tv_sec++;
376 ts.tv_nsec = 0;
378 else
379 ts.tv_nsec += TS_TIMEOUT;
381 if (pwm->cancel)
383 #ifdef HAVE_PTHREAD_CANCEL
384 pthread_cancel (tid);
385 pthread_join (tid, NULL);
386 #else
387 pthread_join (tid, (void **)&result);
388 pwmd_free (result);
389 #endif
390 return GPG_ERR_CANCELED;
393 n = pthread_cond_timedwait (&pwm->tcp->dns_cond, &pwm->tcp->dns_mutex,
394 &ts);
395 if (n == ETIMEDOUT)
397 if (pwm->socket_timeout && ts.tv_sec - now >= pwm->socket_timeout)
399 #ifdef HAVE_PTHREAD_CANCEL
400 pthread_cancel (tid);
401 pthread_join (tid, NULL);
402 #else
403 pthread_join (tid, (void **)&result);
404 pwmd_free (result);
405 #endif
406 return GPG_ERR_ETIMEDOUT;
409 continue;
411 else if (n)
413 #ifdef HAVE_PTHREAD_CANCEL
414 pthread_cancel (tid);
415 pthread_join (tid, NULL);
416 #else
417 pthread_join (tid, (void **)&result);
418 pwmd_free (result);
419 #endif
420 return gpg_error_from_errno (n);
423 pthread_join (tid, (void **)&result);
424 n = *result;
425 pwmd_free (result);
426 break;
429 if (n)
430 return GPG_ERR_UNKNOWN_HOST; //FIXME
432 for (pwm->tcp->addr = pwm->tcp->addrs; pwm->tcp->addr;
433 pwm->tcp->addr = pwm->tcp->addrs->ai_next)
435 pwm->fd = socket (pwm->tcp->addr->ai_family, SOCK_STREAM, 0);
436 if (pwm->fd == -1)
438 rc = gpg_error_from_syserror ();
439 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
440 break;
441 continue;
444 if (fcntl (pwm->fd, F_SETFL, O_NONBLOCK) == -1)
446 rc = gpg_error_from_syserror ();
447 break;
450 if (connect (pwm->fd, pwm->tcp->addr->ai_addr,
451 pwm->tcp->addr->ai_family == AF_INET6
452 ? sizeof (struct sockaddr_in6)
453 : sizeof (struct sockaddr)) == -1)
455 struct timeval tv;
456 fd_set wfds;
457 unsigned elapsed = 0;
459 rc = gpg_error_from_syserror ();
460 if (gpg_err_code (rc) != GPG_ERR_EINPROGRESS)
462 close (pwm->fd);
463 pwm->fd = -1;
464 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next)
465 return rc;
466 continue;
469 again:
470 tv.tv_sec = 1;
471 tv.tv_usec = 0;
472 FD_ZERO (&wfds);
473 FD_SET (pwm->fd, &wfds);
474 n = select (pwm->fd+1, NULL, &wfds, NULL, &tv);
475 rc = 0;
476 if (!n || pwm->cancel)
478 if (pwm->cancel)
479 rc = gpg_error (GPG_ERR_CANCELED);
480 else if (++elapsed >= pwm->socket_timeout)
481 rc = gpg_error (GPG_ERR_ETIMEDOUT);
482 else
483 goto again;
485 else if (n != -1)
487 socklen_t len = sizeof(int);
489 getsockopt (pwm->fd, SOL_SOCKET, SO_ERROR, &n, &len);
490 if (n)
491 rc = gpg_error_from_errno (n);
493 else if (n == -1)
494 rc = gpg_error_from_syserror ();
496 if (rc)
498 close (pwm->fd);
499 pwm->fd = -1;
500 if (pwm->tcp->addr == pwm->tcp->addrs->ai_next
501 || gpg_err_code (rc) == GPG_ERR_ETIMEDOUT
502 || pwm->cancel)
503 return rc;
505 else
506 break;
508 else
509 break;
512 if (!rc)
513 if (fcntl (pwm->fd, F_SETFL, 0) == -1)
514 rc = gpg_error_from_syserror ();
516 return rc;
518 #endif
520 static void
521 command_start (pwm_t *pwm)
523 pwm->cancel = 0;
526 gpg_error_t
527 pwmd_connect_fd (pwm_t * pwm, int fd)
529 gpg_error_t rc = 0;
531 if (!pwm)
532 return FINISH (GPG_ERR_INV_ARG);
533 else if (!pwm->ctx)
535 rc = init_handle (pwm);
536 if (rc)
537 return FINISH (rc);
539 else if (pwm->fd != -1)
540 return FINISH (GPG_ERR_INV_STATE);
542 command_start (pwm);
544 if (!(pwm->opts & OPT_SIGPIPE))
545 signal (SIGPIPE, SIG_IGN);
547 rc = assuan_socket_connect_fd (pwm->ctx, fd, 0);
548 if (!rc)
550 pwm->fd = fd;
551 pwm->connected = pwm->user_fd = 1;
552 rc = _connect_finalize (pwm);
555 if (rc)
556 pwmd_disconnect (pwm);
558 return FINISH (rc);
561 gpg_error_t
562 pwmd_connect (pwm_t * pwm, const char *url, ...)
564 const char *p = url;
565 gpg_error_t rc;
567 if (!pwm)
568 return FINISH (GPG_ERR_INV_ARG);
569 else if (!pwm->ctx)
571 rc = init_handle (pwm);
572 if (rc)
573 return FINISH (GPG_ERR_INV_ARG);
575 else if (pwm->fd != -1)
576 return FINISH (GPG_ERR_INV_STATE);
578 command_start (pwm);
580 if (!(pwm->opts & OPT_SIGPIPE))
581 signal (SIGPIPE, SIG_IGN);
583 #ifdef WITH_GNUTLS
584 pwm->tls_error = 0;
585 #endif
586 rc = GPG_ERR_UNSUPPORTED_PROTOCOL;
588 if (p && (*p == '/' || *p == '~'))
589 rc = connect_uds (pwm, p);
590 else if (!p || !strncmp (p, "file://", 7))
592 if (p)
593 p += 7;
594 #ifdef DEFAULT_PWMD_SOCKET
595 else
596 p = DEFAULT_PWMD_SOCKET;
597 #endif
598 rc = connect_uds (pwm, p);
600 else if (!strncmp (p, "ssh://", 6) || !strncmp (p, "ssh6://", 7) ||
601 !strncmp (p, "ssh4://", 7))
603 #ifndef WITH_SSH
604 return FINISH (GPG_ERR_NOT_IMPLEMENTED);
605 #else
606 char *host = NULL;
607 int port;
608 char *username = NULL;
610 if (!strncmp (p, "ssh6://", 7))
612 pwm->prot = PWMD_IPV6;
613 p += 7;
615 else if (!strncmp (p, "ssh4://", 7))
617 pwm->prot = PWMD_IPV4;
618 p += 7;
620 else
622 pwm->prot = PWMD_IP_ANY;
623 p += 6;
626 rc = _parse_ssh_url (p, &host, &port, &username);
627 if (!rc)
629 va_list ap;
630 char *identity = NULL;
631 char *knownhosts = NULL;
633 va_start (ap, url);
634 identity = va_arg (ap, char *);
636 if (!identity && !pwm->use_agent)
637 rc = GPG_ERR_INV_ARG;
638 else
639 knownhosts = va_arg (ap, char *);
641 va_end (ap);
643 if (!rc)
644 rc = _do_ssh_connect (pwm, host, port, identity, username,
645 knownhosts);
646 if (!rc)
648 rc = _connect_finalize (pwm);
649 if (rc)
650 free_tcp (pwm);
654 pwmd_free (host);
655 pwmd_free (username);
656 pwm->local_pinentry = 1;
657 pwmd_free (pwm->ssh_passphrase);
658 pwm->ssh_passphrase = NULL;
659 #endif
661 else if (!strncmp (p, "tls://", 6) || !strncmp (p, "tls6://", 7) ||
662 !strncmp (p, "tls4://", 7))
664 #ifndef WITH_GNUTLS
665 return FINISH (GPG_ERR_NOT_IMPLEMENTED);
666 #else
667 char *host = NULL;
668 int port;
670 if (!strncmp (p, "tls6://", 7))
672 pwm->prot = PWMD_IPV6;
673 p += 7;
675 else if (!strncmp (p, "tls4://", 7))
677 pwm->prot = PWMD_IPV4;
678 p += 7;
680 else
682 pwm->prot = PWMD_IP_ANY;
683 p += 6;
686 rc = tls_parse_url (p, &host, &port);
687 if (!rc)
689 va_list ap;
690 char *clientcert = NULL;
691 char *clientkey = NULL;
692 char *cacert = NULL;
693 char *server_fp = NULL;
695 va_start (ap, url);
696 clientcert = va_arg (ap, char *);
698 if (!clientcert)
699 rc = GPG_ERR_INV_ARG;
700 else
702 clientkey = va_arg (ap, char *);
703 if (!clientkey)
704 rc = GPG_ERR_INV_ARG;
705 else
707 cacert = va_arg (ap, char *);
708 if (!cacert)
709 rc = GPG_ERR_INV_ARG;
710 else
711 server_fp = va_arg (ap, char *);
715 va_end (ap);
717 if (!rc)
718 rc = tls_connect (pwm, host, port, clientcert, clientkey, cacert,
719 pwm->tls_priority, server_fp, pwm->tls_verify);
720 if (!rc)
722 rc = _connect_finalize (pwm);
723 if (rc)
724 free_tcp (pwm);
728 pwmd_free (host);
729 pwm->local_pinentry = 1;
730 #endif
733 if (!rc)
734 pwm->connected = 1;
736 return FINISH (rc);
739 static void
740 disconnect (pwm_t * pwm)
742 if (!pwm)
743 return;
745 if (pwm->ctx)
746 assuan_release (pwm->ctx);
748 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
749 free_tcp (pwm);
750 #endif
751 pwm->ctx = NULL;
752 pwm->fd = -1;
753 pwm->connected = pwm->user_fd = 0;
756 void
757 pwmd_close (pwm_t * pwm)
759 if (!pwm)
760 return;
762 disconnect (pwm);
763 pwmd_free (pwm->pinentry_error);
764 pwmd_free (pwm->pinentry_desc);
765 pwmd_free (pwm->pinentry_prompt);
766 pwmd_free (pwm->pinentry_tty);
767 pwmd_free (pwm->pinentry_display);
768 pwmd_free (pwm->pinentry_term);
769 pwmd_free (pwm->pinentry_lcctype);
770 pwmd_free (pwm->pinentry_lcmessages);
771 pwmd_free (pwm->filename);
772 pwmd_free (pwm->name);
773 pwmd_free (pwm->passphrase_info);
774 pwmd_free (pwm->passphrase_hint);
775 pwmd_free (pwm->ssh_passphrase);
776 pwmd_free (pwm->tls_priority);
778 #ifdef WITH_PINENTRY
779 if (pwm->pctx)
780 _pinentry_disconnect (pwm);
781 #endif
783 pwmd_free (pwm);
786 static gpg_error_t
787 inquire_realloc_cb (void *data, const void *buffer, size_t len)
789 membuf_t *mem = (membuf_t *) data;
790 void *p;
792 if (!buffer)
793 return 0;
795 if ((p = pwmd_realloc (mem->buf, mem->len + len)) == NULL)
796 return gpg_error (GPG_ERR_ENOMEM);
798 mem->buf = p;
799 memcpy ((char *) mem->buf + mem->len, buffer, len);
800 mem->len += len;
801 return 0;
804 static gpg_error_t
805 get_password (pwm_t * pwm, char **result, size_t * len,
806 pwmd_pinentry_t w, int echo)
808 char buf[ASSUAN_LINELENGTH+1] = { 0 }, *p;
809 struct termios told, tnew;
810 char *key = NULL;
812 if (result)
813 *result = NULL;
815 if (len)
816 *len = 0;
818 if (!isatty (STDIN_FILENO))
820 fprintf (stderr, N_("Input is not from a terminal! Failing.\n"));
821 return GPG_ERR_ENOTTY;
824 if (!echo)
826 if (tcgetattr (STDIN_FILENO, &told) == -1)
827 return gpg_error_from_syserror ();
829 memcpy (&tnew, &told, sizeof (struct termios));
830 tnew.c_lflag &= ~(ECHO);
831 tnew.c_lflag |= ICANON | ECHONL;
833 if (tcsetattr (STDIN_FILENO, TCSANOW, &tnew) == -1)
835 int n = errno;
837 tcsetattr (STDIN_FILENO, TCSANOW, &told);
838 return gpg_error_from_errno (n);
842 if (pwm->passphrase_hint)
843 fprintf(stderr, N_("Key info: %s\n"), pwm->passphrase_hint);
845 switch (w)
847 case PWMD_PINENTRY_OPEN:
848 fprintf (stderr, N_("Passphrase for %s: "), pwm->filename);
849 break;
850 case PWMD_PINENTRY_OPEN_FAILED:
851 fprintf (stderr, N_("Invalid passphrase. Passphrase for %s: "),
852 pwm->filename);
853 break;
854 case PWMD_PINENTRY_SAVE:
855 fprintf (stderr, N_("New passphrase for %s: "), pwm->filename);
856 break;
857 case PWMD_PINENTRY_SAVE_CONFIRM:
858 fprintf (stderr, N_("Repeat passphrase: "));
859 break;
860 case PWMD_PINENTRY_CONFIRM:
861 if (pwm->pinentry_desc)
862 fprintf (stderr, "%s", pwm->pinentry_desc);
864 if (pwm->pinentry_prompt)
865 fprintf (stderr, "%s", pwm->pinentry_prompt);
866 else
867 fprintf(stderr, N_("Confirm [y/N]:"));
868 default:
869 break;
872 p = fgets (buf, sizeof (buf), stdin);
874 if (!echo)
875 tcsetattr (STDIN_FILENO, TCSANOW, &told);
877 if (!p || feof (stdin))
879 clearerr (stdin);
880 return GPG_ERR_CANCELED;
883 /* Strip the newline character. */
884 p[strlen (p) - 1] = 0;
886 if (buf[0])
888 if (w == PWMD_PINENTRY_CONFIRM)
890 if (*p != 'y' && *p != 'Y')
891 return GPG_ERR_CANCELED;
892 return 0;
895 key = pwmd_strdup_printf ("%s", p);
896 wipememory (buf, 0, sizeof (buf));
897 if (!key)
898 return GPG_ERR_ENOMEM;
900 if (result)
901 *result = key;
903 if (len)
904 *len = strlen (key);
906 else
908 if (w == PWMD_PINENTRY_CONFIRM)
909 return GPG_ERR_CANCELED;
911 /* To satisfy inquire_cb(). */
912 if (result)
913 *result = pwmd_strdup ("");
915 if (len)
916 *len = 1;
919 return 0;
922 gpg_error_t
923 pwmd_password (pwm_t * pwm, const char *keyword, char **data, size_t * size)
925 gpg_error_t rc;
926 int new_password = 0;
927 char *password = NULL, *newpass = NULL;
928 int error = 0;
930 command_start (pwm);
932 if (data)
933 *data = NULL;
935 if (size)
936 *size = 0;
938 if (!strcmp (keyword, "NEW_PASSPHRASE"))
939 new_password = 1;
941 if (!new_password && pwm->pinentry_try)
942 error = 1;
944 again:
945 if (pwm->disable_pinentry)
947 rc = get_password (pwm, &password, size,
948 new_password ? PWMD_PINENTRY_SAVE :
949 error ? PWMD_PINENTRY_OPEN_FAILED :
950 PWMD_PINENTRY_OPEN, 0);
951 if (!rc && new_password)
952 rc = get_password (pwm, &newpass, size, PWMD_PINENTRY_SAVE_CONFIRM,
955 else
957 pwmd_pinentry_t which;
959 if (error)
960 which = new_password
961 ? PWMD_PINENTRY_SAVE_FAILED : PWMD_PINENTRY_OPEN_FAILED;
962 else
963 which = new_password ? PWMD_PINENTRY_SAVE : PWMD_PINENTRY_OPEN;
965 rc = pwmd_getpin (pwm, pwm->filename, &password, size, which);
966 if (!rc && new_password)
967 rc = pwmd_getpin (pwm, pwm->filename, &newpass, size,
968 PWMD_PINENTRY_SAVE_CONFIRM);
971 if (!rc && new_password)
973 if ((!password && newpass) || (!newpass && password)
974 || (newpass && password && strcmp (newpass, password)))
976 if (pwm->disable_pinentry)
977 fprintf (stderr, N_("Passphrases do not match.\n"));
979 pwmd_free (password);
980 pwmd_free (newpass);
981 password = newpass = NULL;
982 error = 1;
983 goto again;
987 (void) pwmd_getpin (pwm, pwm->filename, NULL, NULL, PWMD_PINENTRY_CLOSE);
988 pwmd_free (newpass);
989 if (!rc && data)
990 *data = password;
991 else
992 pwmd_free (password);
994 return rc;
997 static gpg_error_t
998 inquire_cb (void *data, const char *keyword)
1000 pwm_t *pwm = (pwm_t *) data;
1001 gpg_error_t rc = 0;
1002 int free_result = 0;
1003 char *result = NULL;
1004 int is_password = 0;
1005 int new_password = 0;
1007 if (!strcmp (keyword, "PASSPHRASE") || !strcmp (keyword, "SIGN_PASSPHRASE"))
1008 is_password = 1;
1009 else if (!strcmp (keyword, "NEW_PASSPHRASE") || !strcmp (keyword, "GENKEY"))
1010 new_password = 1;
1012 /* Shouldn't get this far without a callback. */
1013 if (!pwm->override_inquire && !pwm->inquire_func
1014 && !is_password && !new_password)
1015 return gpg_error (GPG_ERR_ASS_NO_INQUIRE_CB);
1017 for (;;)
1019 size_t len = 0;
1020 gpg_error_t arc;
1022 result = NULL;
1024 if (!pwm->override_inquire && (is_password || new_password))
1026 free_result = 1;
1027 rc = pwmd_password (data, keyword, &result, &len);
1028 if (!rc)
1029 rc = GPG_ERR_EOF;
1031 else
1032 rc = pwm->inquire_func (pwm->inquire_data, keyword, rc, &result,
1033 &len);
1035 /* gpg will truncate a passphrase at the first nil byte which may be bad
1036 * for generated key files. */
1037 if ((!rc || gpg_err_code (rc) == GPG_ERR_EOF)
1038 && (is_password || new_password))
1040 if (len && result && *result)
1042 for (size_t n = 0; n < len; n++)
1044 if (result[n] == 0 && n+1 != len)
1045 rc = GPG_ERR_INV_PASSPHRASE;
1050 cancel:
1051 if (rc && gpg_err_code (rc) != GPG_ERR_EOF)
1053 #ifndef LIBASSUAN_2_1_0
1054 gpg_error_t trc = rc;
1056 /* Cancel this inquire. */
1057 rc = assuan_send_data (pwm->ctx, NULL, 1);
1058 if (!rc)
1060 char *line;
1061 size_t len;
1063 /* There is a bug (or feature?) in assuan_send_data() that
1064 * when cancelling an inquire the next read from the server is
1065 * not done until the next command making the next command
1066 * fail with GPG_ERR_ASS_UNEXPECTED_CMD.
1068 rc = assuan_read_line (pwm->ctx, &line, &len);
1070 /* Restore the original error. This differs from the error
1071 * returned from the pwmd command (GPG_ERR_CANCELED). This
1072 * error is returned to the calling function.
1074 if (!rc)
1075 rc = trc;
1077 #endif
1078 break;
1081 if (gpg_err_code (rc) == GPG_ERR_EOF || !rc)
1083 if (len <= 0 && !result)
1085 rc = 0;
1086 break;
1088 else if ((len <= 0 && result) || (len && !result))
1090 rc = gpg_error (GPG_ERR_INV_ARG);
1091 break;
1094 if (pwm->inquire_maxlen
1095 && pwm->inquire_sent + len > pwm->inquire_maxlen)
1097 rc = gpg_error (GPG_ERR_TOO_LARGE);
1098 if (!free_result)
1099 rc = pwm->inquire_func (pwm->inquire_data, keyword, rc,
1100 &result, &len);
1101 goto cancel;
1104 arc = assuan_send_data (pwm->ctx, result, len);
1105 if (gpg_err_code (rc) == GPG_ERR_EOF)
1107 rc = arc;
1108 break;
1111 rc = arc;
1113 else if (rc)
1114 break;
1116 if (!rc)
1118 pwm->inquire_sent += len;
1120 if (pwm->status_func)
1122 char buf[ASSUAN_LINELENGTH];
1124 snprintf (buf, sizeof (buf), "XFER %zu %zu", pwm->inquire_sent,
1125 pwm->inquire_total);
1126 rc = pwm->status_func (pwm->status_data, buf);
1127 if (rc)
1128 continue;
1133 if (free_result)
1134 pwmd_free (result);
1136 pwm->inquire_maxlen = pwm->inquire_sent = 0;
1137 return rc;
1140 static gpg_error_t
1141 parse_assuan_line (pwm_t * pwm)
1143 gpg_error_t rc;
1144 char *line;
1145 size_t len;
1147 rc = assuan_read_line (pwm->ctx, &line, &len);
1148 if (!rc)
1150 if (line[0] == 'O' && line[1] == 'K' &&
1151 (line[2] == 0 || line[2] == ' '))
1154 else if (line[0] == '#')
1157 else if (line[0] == 'S' && (line[1] == 0 || line[1] == ' '))
1159 rc = status_cb (pwm, line[1] == 0 ? line + 1 : line + 2);
1161 else if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R' &&
1162 (line[3] == 0 || line[3] == ' '))
1164 line += 4;
1165 rc = strtol (line, NULL, 10);
1169 return rc;
1172 static void
1173 reset_handle (pwm_t *pwm)
1175 pwm->fd = -1;
1176 pwm->user_fd = 0;
1177 pwm->cancel = 0;
1178 pwm->pinentry_disabled = 0;
1179 #ifdef WITH_PINENTRY
1180 if (pwm->pctx)
1181 _pinentry_disconnect (pwm);
1182 #endif
1183 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1184 #ifdef WITH_GNUTLS
1185 pwm->tls_error = 0;
1186 #endif
1188 if (pwm->tcp)
1189 pwm->tcp->rc = 0;
1190 #endif
1193 gpg_error_t
1194 pwmd_disconnect (pwm_t * pwm)
1196 if (!pwm)
1197 return FINISH (GPG_ERR_INV_ARG);
1199 command_start (pwm);
1201 if (pwm->fd == -1)
1202 return FINISH (GPG_ERR_INV_STATE);
1204 disconnect (pwm);
1205 reset_handle (pwm);
1206 return 0;
1209 /* Note that this should only be called when not in a command. */
1210 gpg_error_t
1211 pwmd_process (pwm_t * pwm)
1213 gpg_error_t rc = 0;
1214 fd_set fds;
1215 struct timeval tv = { 0, 0 };
1216 int n;
1218 if (!pwm || pwm->fd == -1)
1219 return FINISH (GPG_ERR_INV_ARG);
1220 else if (!pwm->ctx)
1221 return FINISH (GPG_ERR_INV_STATE);
1223 FD_ZERO (&fds);
1224 FD_SET (pwm->fd, &fds);
1225 n = select (pwm->fd + 1, &fds, NULL, NULL, &tv);
1227 if (n == -1)
1228 return FINISH (gpg_error_from_syserror ());
1230 if (n > 0)
1232 if (FD_ISSET (pwm->fd, &fds))
1233 rc = parse_assuan_line (pwm);
1236 while (!rc && assuan_pending_line (pwm->ctx))
1237 rc = parse_assuan_line (pwm);
1239 #if defined (WITH_SSH) || defined (WITH_GNUTLS)
1240 if (gpg_err_code (rc) == GPG_ERR_EOF && pwm->tcp)
1242 close (pwm->fd);
1243 pwm->fd = -1;
1245 #endif
1247 return FINISH (rc);
1250 static gpg_error_t
1251 status_cb (void *data, const char *line)
1253 pwm_t *pwm = data;
1255 if (!strncmp (line, "INQUIRE_MAXLEN ", 15))
1256 pwm->inquire_maxlen = strtol (line + 15, NULL, 10);
1257 else if (!strncmp (line, "PASSPHRASE_HINT ", 16))
1259 pwmd_free (pwm->passphrase_hint);
1260 pwm->passphrase_hint = pwmd_strdup (line+16);
1262 else if (!strncmp (line, "PASSPHRASE_INFO ", 16))
1264 pwmd_free (pwm->passphrase_info);
1265 pwm->passphrase_info = pwmd_strdup (line+16);
1267 #ifdef WITH_GNUTLS
1268 else if (!strcmp (line, "REHANDSHAKE"))
1270 char buf[32];
1271 ssize_t ret;
1273 ret = tls_read_hook (pwm, pwm->fd, buf, sizeof (buf));
1274 if (ret < 0)
1276 pwm->tls_error = ret;
1277 return GPG_ERR_GENERAL;
1280 #endif
1282 if (pwm->status_func)
1283 return pwm->status_func (pwm->status_data, line);
1285 return 0;
1288 gpg_error_t
1289 _assuan_command (pwm_t * pwm, assuan_context_t ctx,
1290 char **result, size_t * len, const char *cmd)
1292 membuf_t data;
1293 gpg_error_t rc;
1295 if (!cmd || !*cmd)
1296 return FINISH (GPG_ERR_INV_ARG);
1298 if (strlen (cmd) >= ASSUAN_LINELENGTH + 1)
1299 return FINISH (GPG_ERR_LINE_TOO_LONG);
1301 data.len = 0;
1302 data.buf = NULL;
1303 rc = assuan_transact (ctx, cmd, inquire_realloc_cb, &data,
1304 #if WITH_QUALITY && WITH_PINENTRY
1305 pwm->pctx == ctx ? pwm->_inquire_func : inquire_cb,
1306 pwm->pctx == ctx ? pwm->_inquire_data : pwm,
1307 #else
1308 inquire_cb, pwm,
1309 #endif
1310 status_cb, pwm);
1312 if (rc)
1314 if (data.buf)
1316 pwmd_free (data.buf);
1317 data.buf = NULL;
1320 else
1322 if (data.buf)
1324 inquire_realloc_cb (&data, "", 1);
1326 if (result)
1327 *result = (char *) data.buf;
1328 else
1329 pwmd_free (data.buf);
1331 if (len)
1332 *len = data.len;
1336 pwm->inquire_maxlen = 0;
1337 return rc;
1340 gpg_error_t
1341 pwmd_command_ap (pwm_t * pwm, char **result, size_t * rlen,
1342 pwmd_inquire_cb_t func, void *user, const char *cmd,
1343 va_list ap)
1345 char *buf;
1346 size_t len;
1347 va_list ap2;
1349 if (!pwm)
1350 return GPG_ERR_INV_ARG;
1352 command_start (pwm);
1354 if (result)
1355 *result = NULL;
1357 if (rlen)
1358 *rlen = 0;
1360 if (!pwm || !cmd)
1361 return FINISH (GPG_ERR_INV_ARG);
1362 if (!pwm->ctx)
1363 return FINISH (GPG_ERR_INV_STATE);
1366 * C99 allows the dst pointer to be null which will calculate the length
1367 * of the would-be result and return it.
1369 va_copy (ap2, ap);
1370 len = vsnprintf (NULL, 0, cmd, ap);
1371 buf = (char *) pwmd_malloc (len+1);
1372 if (!buf)
1374 va_end (ap2);
1375 return FINISH (GPG_ERR_ENOMEM);
1378 if (vsnprintf (buf, len+1, cmd, ap2) != len)
1380 pwmd_free (buf);
1381 va_end (ap2);
1382 return FINISH (GPG_ERR_ENOMEM);
1385 if (buf[strlen (buf) - 1] == '\n')
1386 buf[strlen (buf) - 1] = 0;
1387 if (buf[strlen (buf) - 1] == '\r')
1388 buf[strlen (buf) - 1] = 0;
1390 pwm->inquire_func = func;
1391 pwm->inquire_data = user;
1392 pwm->inquire_sent = 0;
1393 gpg_error_t rc = _assuan_command (pwm, pwm->ctx, result, rlen, buf);
1394 pwmd_free (buf);
1395 return rc;
1398 gpg_error_t
1399 pwmd_command (pwm_t * pwm, char **result, size_t * len,
1400 pwmd_inquire_cb_t func, void *user, const char *cmd, ...)
1402 va_list ap;
1404 if (result)
1405 *result = NULL;
1407 if (len)
1408 *len = 0;
1410 if (!pwm || !cmd)
1411 return FINISH (GPG_ERR_INV_ARG);
1412 if (!pwm->ctx)
1413 return FINISH (GPG_ERR_INV_STATE);
1415 va_start (ap, cmd);
1416 gpg_error_t rc = pwmd_command_ap (pwm, result, len, func, user, cmd, ap);
1417 va_end (ap);
1418 return rc;
1421 static gpg_error_t
1422 send_pinentry_timeout (pwm_t *pwm)
1424 gpg_error_t rc = 0;
1426 if ((pwm->pinentry_timeout >= 0
1427 && pwm->pinentry_timeout != pwm->current_pinentry_timeout)
1428 || (pwm->pinentry_timeout == -1
1429 && pwm->pinentry_timeout != pwm->current_pinentry_timeout))
1431 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL,
1432 "OPTION pinentry-timeout=%i",
1433 pwm->pinentry_timeout);
1434 if (!rc)
1435 pwm->current_pinentry_timeout = pwm->pinentry_timeout;
1438 return rc;
1441 static gpg_error_t
1442 send_pinentry_options (pwm_t * pwm)
1444 gpg_error_t rc;
1446 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL,
1447 "OPTION disable-pinentry=0");
1448 if (!rc && pwm->pinentry_tty)
1449 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION TTYNAME=%s",
1450 pwm->pinentry_tty);
1452 if (!rc && pwm->pinentry_term)
1453 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION TTYTYPE=%s",
1454 pwm->pinentry_term);
1456 if (!rc && pwm->pinentry_display)
1457 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION DISPLAY=%s",
1458 pwm->pinentry_display);
1460 if (!rc && pwm->pinentry_desc)
1461 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION DESC=%s",
1462 pwm->pinentry_desc);
1464 if (!rc && pwm->pinentry_lcctype)
1465 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION LC_CTYPE=%s",
1466 pwm->pinentry_lcctype);
1468 if (!rc && pwm->pinentry_lcmessages)
1469 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION LC_MESSAGES=%s",
1470 pwm->pinentry_lcmessages);
1472 if (!rc)
1473 rc = send_pinentry_timeout (pwm);
1475 return rc;
1478 gpg_error_t
1479 pwmd_socket_type (pwm_t * pwm, pwmd_socket_t * result)
1481 if (!pwm || !result)
1482 return FINISH (GPG_ERR_INV_ARG);
1484 *result = PWMD_SOCKET_LOCAL;
1486 if (pwm->fd == -1)
1487 return FINISH (GPG_ERR_INV_STATE);
1489 if (pwm->user_fd)
1491 *result = PWMD_SOCKET_USER;
1492 return 0;
1495 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1496 #ifdef WITH_SSH
1497 if (pwm->tcp && pwm->tcp->ssh)
1498 *result = PWMD_SOCKET_SSH;
1499 #endif
1500 #ifdef WITH_GNUTLS
1501 if (pwm->tcp && pwm->tcp->tls)
1502 *result = PWMD_SOCKET_TLS;
1503 #endif
1504 #endif
1505 return 0;
1508 static gpg_error_t
1509 disable_pinentry (pwm_t *pwm, int *disable)
1511 gpg_error_t rc;
1512 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
1513 int no_pinentry = pwm->disable_pinentry || pwm->tcp || pwm->local_pinentry;
1514 #else
1515 int no_pinentry = pwm->disable_pinentry || pwm->local_pinentry;
1516 #endif
1518 if (disable)
1519 *disable = no_pinentry;
1521 if (pwm->pinentry_disabled && no_pinentry)
1522 return 0;
1523 else if (!pwm->pinentry_disabled && !no_pinentry)
1524 return 0;
1526 rc = pwmd_command (pwm, NULL, NULL, NULL, NULL, "OPTION disable-pinentry=%i",
1527 no_pinentry);
1528 if (!rc)
1529 pwm->pinentry_disabled = no_pinentry;
1531 return rc;
1534 gpg_error_t
1535 pwmd_open (pwm_t * pwm, const char *filename, pwmd_inquire_cb_t cb,
1536 void *data)
1538 gpg_error_t rc = 0;
1539 int no_pinentry = 0;
1541 if (!pwm || !filename || !*filename)
1542 return FINISH (GPG_ERR_INV_ARG);
1544 if (!pwm->ctx)
1545 return FINISH (GPG_ERR_INV_STATE);
1547 command_start (pwm);
1548 rc = disable_pinentry (pwm, &no_pinentry);
1549 if (!rc && !no_pinentry)
1550 rc = send_pinentry_options (pwm);
1552 if (!rc)
1554 pwm->pinentry_try = 0;
1555 pwmd_free (pwm->filename);
1556 pwm->filename = pwmd_strdup (filename);
1560 rc = pwmd_command (pwm, NULL, NULL, cb, data, "OPEN %s%s",
1561 (pwm->opts & OPT_LOCK_ON_OPEN) ? "--lock " : "",
1562 filename);
1564 while (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
1565 && pwm->pinentry_disabled
1566 && ++pwm->pinentry_try < pwm->pinentry_tries);
1568 pwm->pinentry_try = 0;
1570 if (rc)
1572 pwmd_free (pwm->filename);
1573 pwm->filename = NULL;
1577 pwmd_free (pwm->passphrase_hint);
1578 pwmd_free (pwm->passphrase_info);
1579 pwm->passphrase_info = pwm->passphrase_hint = NULL;
1580 return FINISH (rc);
1583 static gpg_error_t
1584 do_pwmd_save_passwd (pwm_t * pwm, const char *args, pwmd_inquire_cb_t cb,
1585 void *data, int which)
1587 gpg_error_t rc = 0;
1588 int no_pinentry = 0;
1590 if (!pwm)
1591 return FINISH (GPG_ERR_INV_ARG);
1592 if (!pwm->ctx)
1593 return FINISH (GPG_ERR_INV_STATE);
1595 command_start (pwm);
1596 rc = disable_pinentry (pwm, &no_pinentry);
1597 if (!rc && !no_pinentry)
1598 rc = send_pinentry_options (pwm);
1600 if (!rc)
1602 if (which == PWMD_WHICH_SAVE)
1603 rc = pwmd_command (pwm, NULL, NULL, cb, data, "SAVE %s", args ? args : "");
1604 else if (which == PWMD_WHICH_PASSWD)
1605 rc = pwmd_command (pwm, NULL, NULL, cb, data, "PASSWD %s", args ? args : "");
1606 else if (which == PWMD_WHICH_GENKEY)
1607 rc = pwmd_command (pwm, NULL, NULL, cb, data, "GENKEY %s", args ? args : "");
1610 pwmd_free (pwm->passphrase_hint);
1611 pwmd_free (pwm->passphrase_info);
1612 pwm->passphrase_info = pwm->passphrase_hint = NULL;
1613 return FINISH (rc);
1616 gpg_error_t
1617 pwmd_passwd (pwm_t * pwm, const char *args, pwmd_inquire_cb_t cb, void *data)
1619 return do_pwmd_save_passwd (pwm, args, cb, data, PWMD_WHICH_PASSWD);
1622 gpg_error_t
1623 pwmd_save (pwm_t * pwm, const char *args, pwmd_inquire_cb_t cb, void *data)
1625 return do_pwmd_save_passwd (pwm, args, cb, data, PWMD_WHICH_SAVE);
1628 gpg_error_t
1629 pwmd_genkey (pwm_t * pwm, const char *args, pwmd_inquire_cb_t cb, void *data)
1631 return do_pwmd_save_passwd (pwm, args, cb, data, PWMD_WHICH_GENKEY);
1634 static gpg_error_t
1635 pwmd_get_set_opt (pwm_t *pwm, pwmd_option_t opt, int get, va_list ap)
1637 int n, *intp;
1638 size_t *sizetp;
1639 char *arg1, **charpp;
1640 gpg_error_t rc = 0;
1642 if (!pwm)
1643 return GPG_ERR_INV_ARG;
1645 command_start (pwm);
1646 switch (opt)
1648 case PWMD_OPTION_SERVER_VERSION:
1649 if (!get)
1650 return GPG_ERR_NOT_SUPPORTED;
1652 if (!pwm->ctx)
1653 rc = GPG_ERR_INV_STATE;
1654 else
1656 unsigned *u = va_arg (ap, unsigned *);
1657 *u = pwm->version;
1660 break;
1661 case PWMD_OPTION_SIGPIPE:
1662 if (get)
1664 intp = va_arg (ap, int *);
1665 *intp = pwm->opts & OPT_SIGPIPE ? 1 : 0;
1666 break;
1669 n = va_arg (ap, int);
1670 if (n < 0 || n > 1)
1672 rc = GPG_ERR_INV_VALUE;
1673 break;
1676 if (n)
1677 pwm->opts |= OPT_SIGPIPE;
1678 else
1679 pwm->opts &= ~OPT_SIGPIPE;
1681 break;
1682 case PWMD_OPTION_LOCK_ON_OPEN:
1683 if (get)
1685 intp = va_arg (ap, int *);
1686 *intp = pwm->opts & OPT_LOCK_ON_OPEN ? 1 : 0;
1687 break;
1690 n = va_arg (ap, int);
1691 if (n < 0 || n > 1)
1693 rc = GPG_ERR_INV_VALUE;
1694 break;
1697 if (n)
1698 pwm->opts |= OPT_LOCK_ON_OPEN;
1699 else
1700 pwm->opts &= ~OPT_LOCK_ON_OPEN;
1702 break;
1703 case PWMD_OPTION_INQUIRE_TOTAL:
1704 if (get)
1706 sizetp = va_arg (ap, size_t *);
1707 *sizetp = pwm->inquire_total;
1708 break;
1711 pwm->inquire_total = va_arg (ap, size_t);
1712 break;
1713 case PWMD_OPTION_STATUS_CB:
1714 if (get)
1716 pwmd_status_cb_t *cb = va_arg (ap, pwmd_status_cb_t *);
1718 *cb = pwm->status_func;
1719 break;
1722 pwm->status_func = va_arg (ap, pwmd_status_cb_t);
1723 break;
1724 case PWMD_OPTION_STATUS_DATA:
1725 if (get)
1727 void **data = va_arg (ap, void **);
1729 *data = pwm->status_data;
1730 break;
1733 pwm->status_data = va_arg (ap, void *);
1734 break;
1735 case PWMD_OPTION_NO_PINENTRY:
1736 if (get)
1738 intp = va_arg (ap, int *);
1739 *intp = pwm->disable_pinentry;
1740 break;
1743 n = va_arg (ap, int);
1744 if (n < 0 || n > 1)
1745 rc = GPG_ERR_INV_VALUE;
1746 else
1747 pwm->disable_pinentry = n;
1748 break;
1749 case PWMD_OPTION_LOCAL_PINENTRY:
1750 if (get)
1752 intp = va_arg (ap, int *);
1753 *intp = pwm->local_pinentry;
1754 break;
1757 n = va_arg (ap, int);
1758 if (n < 0 || n > 1)
1759 rc = GPG_ERR_INV_VALUE;
1760 else
1761 pwm->local_pinentry = n;
1763 break;
1764 case PWMD_OPTION_PINENTRY_TIMEOUT:
1765 if (get)
1767 intp = va_arg (ap, int *);
1768 *intp = pwm->pinentry_timeout;
1769 break;
1772 n = va_arg (ap, int);
1773 if (n < 0)
1774 rc = GPG_ERR_INV_VALUE;
1775 else
1776 pwm->pinentry_timeout = n;
1777 break;
1778 case PWMD_OPTION_PINENTRY_TRIES:
1779 if (get)
1781 intp = va_arg (ap, int *);
1782 *intp = pwm->pinentry_tries;
1783 break;
1786 n = va_arg (ap, int);
1787 if (n < 0)
1788 rc = GPG_ERR_INV_VALUE;
1789 else
1790 pwm->pinentry_tries = n;
1791 break;
1792 case PWMD_OPTION_PINENTRY_PATH:
1793 if (get)
1795 charpp = va_arg (ap, char **);
1796 *charpp = pwm->pinentry_path;
1797 break;
1800 arg1 = va_arg (ap, char *);
1801 pwmd_free (pwm->pinentry_path);
1802 pwm->pinentry_path = arg1 ? _expand_homedir (arg1, NULL) : NULL;
1803 break;
1804 case PWMD_OPTION_PINENTRY_TTY:
1805 if (get)
1807 charpp = va_arg (ap, char **);
1808 *charpp = pwm->pinentry_tty;
1809 break;
1812 arg1 = va_arg (ap, char *);
1813 pwmd_free (pwm->pinentry_tty);
1814 pwm->pinentry_tty = arg1 ? pwmd_strdup (arg1) : NULL;
1815 break;
1816 case PWMD_OPTION_PINENTRY_DISPLAY:
1817 if (get)
1819 charpp = va_arg (ap, char **);
1820 *charpp = pwm->pinentry_display;
1821 break;
1824 arg1 = va_arg (ap, char *);
1825 pwmd_free (pwm->pinentry_display);
1826 pwm->pinentry_display = arg1 && *arg1 ? pwmd_strdup (arg1) : NULL;
1827 if (!pwm->pinentry_display)
1828 unsetenv ("DISPLAY");
1829 break;
1830 case PWMD_OPTION_PINENTRY_TERM:
1831 if (get)
1833 charpp = va_arg (ap, char **);
1834 *charpp = pwm->pinentry_term;
1835 break;
1838 arg1 = va_arg (ap, char *);
1839 pwmd_free (pwm->pinentry_term);
1840 pwm->pinentry_term = arg1 && *arg1 ? pwmd_strdup (arg1) : NULL;
1841 if (pwm->pinentry_term)
1842 unsetenv ("TERM");
1843 break;
1844 case PWMD_OPTION_PINENTRY_ERROR:
1845 if (get)
1847 charpp = va_arg (ap, char **);
1848 *charpp = pwm->pinentry_error;
1849 break;
1852 arg1 = va_arg (ap, char *);
1853 pwmd_free (pwm->pinentry_error);
1854 if (pwm->disable_pinentry)
1855 pwm->pinentry_error = arg1 ? pwmd_strdup (arg1) : NULL;
1856 else
1857 pwm->pinentry_error = arg1 ? _percent_escape (arg1) : NULL;
1858 break;
1859 case PWMD_OPTION_PINENTRY_PROMPT:
1860 if (get)
1862 charpp = va_arg (ap, char **);
1863 *charpp = pwm->pinentry_prompt;
1864 break;
1867 arg1 = va_arg (ap, char *);
1868 pwmd_free (pwm->pinentry_prompt);
1869 if (pwm->disable_pinentry)
1870 pwm->pinentry_prompt = arg1 ? pwmd_strdup (arg1) : NULL;
1871 else
1872 pwm->pinentry_prompt = arg1 ? _percent_escape (arg1) : NULL;
1873 break;
1874 case PWMD_OPTION_PINENTRY_DESC:
1875 if (get)
1877 charpp = va_arg (ap, char **);
1878 *charpp = pwm->pinentry_desc;
1879 break;
1882 arg1 = va_arg (ap, char *);
1883 pwmd_free (pwm->pinentry_desc);
1884 if (pwm->disable_pinentry)
1885 pwm->pinentry_desc = arg1 ? pwmd_strdup (arg1) : NULL;
1886 else
1887 pwm->pinentry_desc = arg1 ? _percent_escape (arg1) : NULL;
1888 break;
1889 case PWMD_OPTION_PINENTRY_LC_CTYPE:
1890 if (get)
1892 charpp = va_arg (ap, char **);
1893 *charpp = pwm->pinentry_lcctype;
1894 break;
1897 arg1 = va_arg (ap, char *);
1898 pwmd_free (pwm->pinentry_lcctype);
1899 pwm->pinentry_lcctype = arg1 ? pwmd_strdup (arg1) : NULL;
1900 break;
1901 case PWMD_OPTION_PINENTRY_LC_MESSAGES:
1902 if (get)
1904 charpp = va_arg (ap, char **);
1905 *charpp = pwm->pinentry_lcmessages;
1906 break;
1909 arg1 = va_arg (ap, char *);
1910 pwmd_free (pwm->pinentry_lcmessages);
1911 pwm->pinentry_lcmessages = arg1 ? pwmd_strdup (arg1) : NULL;
1912 break;
1913 case PWMD_OPTION_KNOWNHOST_CB:
1914 if (get)
1916 pwmd_knownhost_cb_t *cb = va_arg (ap, pwmd_knownhost_cb_t *);
1918 *cb = pwm->kh_cb;
1919 break;
1922 pwm->kh_cb = va_arg (ap, pwmd_knownhost_cb_t);
1923 break;
1924 case PWMD_OPTION_KNOWNHOST_DATA:
1925 if (get)
1927 void **data = va_arg (ap, void **);
1929 *data = pwm->kh_data;
1930 break;
1933 pwm->kh_data = va_arg (ap, void *);
1934 break;
1935 case PWMD_OPTION_SSH_AGENT:
1936 if (get)
1938 intp = va_arg (ap, int *);
1939 *intp = pwm->use_agent;
1940 break;
1943 n = va_arg (ap, int);
1944 if (n < 0 || n > 1)
1945 rc = GPG_ERR_INV_VALUE;
1946 else
1947 pwm->use_agent = n;
1948 break;
1949 case PWMD_OPTION_SSH_PASSPHRASE:
1950 if (get)
1951 return GPG_ERR_NOT_SUPPORTED;
1953 pwmd_free (pwm->ssh_passphrase);
1954 pwm->ssh_passphrase = NULL;
1955 arg1 = va_arg (ap, char *);
1956 if (arg1)
1958 pwm->ssh_passphrase = pwmd_strdup (arg1);
1959 if (!pwm->ssh_passphrase)
1960 return GPG_ERR_ENOMEM;
1962 break;
1963 case PWMD_OPTION_SSH_NEEDS_PASSPHRASE:
1964 if (get)
1966 intp = va_arg (ap, int *);
1967 *intp = pwm->needs_passphrase;
1968 break;
1971 n = va_arg (ap, int);
1972 if (n < 0 || n > 1)
1973 rc = GPG_ERR_INV_VALUE;
1974 else
1975 pwm->needs_passphrase = n;
1976 break;
1977 case PWMD_OPTION_TLS_VERIFY:
1978 if (get)
1980 intp = va_arg (ap, int *);
1981 *intp = pwm->tls_verify;
1982 break;
1985 n = va_arg (ap, int);
1986 if (n < 0 || n > 1)
1987 rc = GPG_ERR_INV_VALUE;
1988 else
1989 pwm->tls_verify = n;
1990 break;
1991 case PWMD_OPTION_TLS_PRIORITY:
1992 if (get)
1994 charpp = va_arg (ap, char **);
1995 *charpp = pwm->tls_priority;
1996 break;
1999 pwmd_free (pwm->tls_priority);
2000 pwm->tls_priority = NULL;
2001 arg1 = va_arg (ap, char *);
2002 if (arg1)
2004 pwm->tls_priority = pwmd_strdup (arg1);
2005 if (!pwm->tls_priority)
2006 rc = GPG_ERR_ENOMEM;
2008 break;
2009 case PWMD_OPTION_SOCKET_TIMEOUT:
2010 if (get)
2012 intp = va_arg (ap, int *);
2013 *intp = pwm->socket_timeout;
2014 break;
2017 n = va_arg (ap, int);
2018 if (n < 0)
2020 rc = GPG_ERR_INV_VALUE;
2021 break;
2023 else
2024 pwm->socket_timeout = n;
2026 #ifdef WITH_SSH
2027 if (pwm->tcp && pwm->tcp->ssh && pwm->tcp->ssh->session)
2029 pwm->tcp->ssh->timeout = pwm->socket_timeout;
2030 libssh2_session_set_timeout (pwm->tcp->ssh->session,
2031 pwm->socket_timeout * 1000);
2033 #endif
2034 #ifdef WITH_GNUTLS
2035 if (pwm->tcp && pwm->tcp->tls && pwm->tcp->tls->session)
2036 pwm->tcp->tls->timeout = pwm->socket_timeout;
2037 #endif
2038 break;
2039 case PWMD_OPTION_READ_CB:
2040 if (get)
2042 pwmd_read_cb_t *cb = va_arg (ap, pwmd_read_cb_t *);
2044 *cb = pwm->read_cb;
2045 break;
2047 pwm->read_cb = va_arg (ap, pwmd_read_cb_t);
2048 break;
2049 case PWMD_OPTION_READ_CB_DATA:
2050 if (get)
2052 void **data = va_arg (ap, void **);
2054 *data = pwm->read_cb_data;
2055 break;
2058 pwm->read_cb_data = va_arg (ap, void *);
2059 break;
2060 case PWMD_OPTION_WRITE_CB:
2061 if (get)
2063 pwmd_write_cb_t *cb = va_arg (ap, pwmd_write_cb_t *);
2065 *cb = pwm->write_cb;
2066 break;
2068 pwm->write_cb = va_arg (ap, pwmd_write_cb_t);
2069 break;
2070 case PWMD_OPTION_WRITE_CB_DATA:
2071 if (get)
2073 void **data = va_arg (ap, void **);
2075 *data = pwm->write_cb_data;
2076 break;
2079 pwm->write_cb_data = va_arg (ap, void *);
2080 break;
2081 case PWMD_OPTION_OVERRIDE_INQUIRE:
2082 if (get)
2084 intp = va_arg (ap, int *);
2085 *intp = pwm->override_inquire;
2086 break;
2089 n = va_arg (ap, int);
2090 if (n < 0 || n > 1)
2091 rc = GPG_ERR_INV_VALUE;
2092 else
2093 pwm->override_inquire = n;
2094 break;
2095 default:
2096 rc = GPG_ERR_UNKNOWN_OPTION;
2097 break;
2100 return FINISH (rc);
2103 gpg_error_t
2104 pwmd_setopt (pwm_t * pwm, int opt, ...)
2106 va_list ap;
2107 gpg_error_t rc = 0;
2109 va_start (ap, opt);
2110 rc = pwmd_get_set_opt (pwm, opt, 0, ap);
2111 va_end (ap);
2112 return FINISH (rc);
2115 gpg_error_t
2116 pwmd_getopt (pwm_t *pwm, int opt, ...)
2118 va_list ap;
2119 gpg_error_t rc = 0;
2121 va_start (ap, opt);
2122 rc = pwmd_get_set_opt (pwm, opt, 1, ap);
2123 va_end (ap);
2124 return FINISH (rc);
2127 gpg_error_t
2128 set_rcdefaults (pwm_t *pwm, char *filename)
2130 char *f;
2131 FILE *fp;
2132 char *line = NULL, *p;
2133 unsigned line_n = 0;
2134 gpg_error_t rc = 0;
2136 if (!filename && isatty (STDIN_FILENO))
2138 char buf[256];
2139 int err = ttyname_r (STDOUT_FILENO, buf, sizeof (buf));
2141 if (!err)
2143 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_TTY, buf);
2144 if (rc)
2145 return rc;
2147 if (getenv ("TERM"))
2149 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_TERM, getenv ("TERM"));
2150 if (rc)
2151 return rc;
2156 if (!filename && getenv ("DISPLAY"))
2158 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_DISPLAY, getenv ("DISPLAY"));
2159 if (rc)
2160 return rc;
2163 f = _expand_homedir (filename ? filename : (char *)"~/.config/libpwmd.conf",
2164 NULL);
2165 if (f)
2167 line = pwmd_malloc (LINE_MAX);
2168 if (line)
2170 fp = fopen (f, "r");
2171 if (!fp)
2173 pwmd_free (line);
2174 pwmd_free (f);
2175 return 0;
2178 else
2179 rc = GPG_ERR_ENOMEM;
2181 else
2182 rc = GPG_ERR_ENOMEM;
2184 if (rc)
2186 pwmd_free (f);
2187 pwmd_free (line);
2188 return rc;
2191 while ((p = fgets (line, LINE_MAX, fp)))
2193 char name[32] = {0}, val[512] = {0};
2194 char *np;
2195 char *t;
2196 size_t len = 0;
2198 line_n++;
2200 while (isspace (*p))
2201 p++;
2203 if (!*p || *p == '#')
2204 continue;
2206 if (p[strlen (p)-1] == '\n')
2207 p[strlen (p)-1] = 0;
2209 t = strchr (p, '=');
2210 if (!t)
2212 fprintf(stderr, N_("%s(%u): malformed line\n"), f, line_n);
2213 continue;
2216 for (np = name; p != t; p++)
2218 if (++len == sizeof (name))
2219 break;
2221 if (isspace (*p))
2222 break;
2224 *np++ = *p;
2227 while (isspace (*t))
2228 t++;
2229 t++; // '='
2230 while (isspace (*t))
2231 t++;
2233 strncpy (val, t, sizeof (val)-1);
2235 if (!strcasecmp (name, "pinentry-path"))
2236 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_PATH, val);
2237 else if (!strcasecmp (name, "pinentry-tries"))
2238 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_TRIES, atoi (val));
2239 else if (!strcasecmp (name, "pinentry-timeout"))
2240 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_TIMEOUT, atoi (val));
2241 else if (!strcasecmp (name, "no-pinentry"))
2242 rc = pwmd_setopt (pwm, PWMD_OPTION_NO_PINENTRY, !!(atoi (val)));
2243 else if (!strcasecmp (name, "local-pinentry"))
2244 rc = pwmd_setopt (pwm, PWMD_OPTION_LOCAL_PINENTRY, !!(atoi (val)));
2245 else if (!strcasecmp (name, "pinentry-display"))
2246 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_DISPLAY, val);
2247 else if (!strcasecmp (name, "pinentry-ttyname"))
2248 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_TTY, val);
2249 else if (!strcasecmp (name, "pinentry-ttytype"))
2250 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_TERM, val);
2251 else if (!strcasecmp (name, "pinentry-lc-messages"))
2252 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_LC_MESSAGES, val);
2253 else if (!strcasecmp (name, "pinentry-lc-ctype"))
2254 rc = pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_LC_CTYPE, val);
2255 else if (!strcasecmp (name, "no-ssh-agent"))
2256 rc = pwmd_setopt (pwm, PWMD_OPTION_SSH_AGENT, !(atoi (val)));
2257 else if (!strcasecmp (name, "no-tls-verify"))
2258 rc = pwmd_setopt (pwm, PWMD_OPTION_TLS_VERIFY, !(atoi (val)));
2259 else if (!strcasecmp (name, "tls-priority"))
2260 rc = pwmd_setopt (pwm, PWMD_OPTION_TLS_PRIORITY, val);
2261 else if (!strcasecmp (name, "socket-timeout"))
2262 rc = pwmd_setopt (pwm, PWMD_OPTION_SOCKET_TIMEOUT, atoi (val));
2263 else if (!strcasecmp (name, "no-lock"))
2264 rc = pwmd_setopt (pwm, PWMD_OPTION_LOCK_ON_OPEN, !(atoi (val)));
2265 else if (!strcasecmp (name, "include"))
2266 rc = set_rcdefaults (pwm, val);
2267 else
2268 fprintf(stderr, N_("%s(%u): invalid option '%s', ignored.\n"), f,
2269 line_n, name);
2271 if (rc)
2272 break;
2275 fclose (fp);
2276 pwmd_free (line);
2277 pwmd_free (f);
2278 return rc;
2281 gpg_error_t
2282 pwmd_new (const char *name, pwm_t ** pwm)
2284 pwm_t *h = pwmd_calloc (1, sizeof (pwm_t));
2285 gpg_error_t rc;
2287 if (!h)
2288 return FINISH (GPG_ERR_ENOMEM);
2290 if (name)
2292 h->name = pwmd_strdup (name);
2293 if (!h->name)
2295 pwmd_free (h);
2296 return FINISH (GPG_ERR_ENOMEM);
2300 reset_handle (h);
2301 h->pinentry_timeout = -1;
2302 h->current_pinentry_timeout = -1;
2303 h->pinentry_tries = 3;
2304 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
2305 h->prot = PWMD_IP_ANY;
2306 h->socket_timeout = 120;
2307 h->tls_verify = 1;
2308 #endif
2309 h->opts |= OPT_LOCK_ON_OPEN;
2311 rc = set_rcdefaults (h, NULL);
2312 if (rc)
2313 goto fail;
2315 *pwm = h;
2316 return 0;
2318 fail:
2319 pwmd_close (h);
2320 return FINISH (rc);
2323 void
2324 pwmd_free (void *ptr)
2326 _xfree (ptr);
2329 void *
2330 pwmd_malloc (size_t size)
2332 return _xmalloc (size);
2335 void *
2336 pwmd_calloc (size_t nmemb, size_t size)
2338 return _xcalloc (nmemb, size);
2341 void *
2342 pwmd_realloc (void *ptr, size_t size)
2344 return _xrealloc (ptr, size);
2347 char *
2348 pwmd_strdup (const char *str)
2350 char *t;
2351 size_t len;
2352 register size_t c;
2354 len = strlen (str);
2355 t = _xmalloc ((len + 1) * sizeof (char));
2356 if (!t)
2357 return NULL;
2359 for (c = 0; c < len; c++)
2360 t[c] = str[c];
2362 t[c] = 0;
2363 return t;
2366 char *
2367 pwmd_strdup_printf (const char *fmt, ...)
2369 va_list ap, ap2;
2370 int len;
2371 char *buf;
2373 if (!fmt)
2374 return NULL;
2376 va_start (ap, fmt);
2377 va_copy (ap2, ap);
2378 len = vsnprintf (NULL, 0, fmt, ap);
2379 va_end (ap);
2380 buf = pwmd_malloc (++len);
2381 if (buf)
2382 vsnprintf (buf, len, fmt, ap2);
2384 va_end (ap2);
2385 return buf;
2388 gpg_error_t
2389 pwmd_getpin (pwm_t * pwm, const char *filename, char **result,
2390 size_t * len, pwmd_pinentry_t which)
2392 #ifndef WITH_PINENTRY
2393 return FINISH (GPG_ERR_NOT_IMPLEMENTED);
2394 #else
2395 gpg_error_t rc;
2397 command_start (pwm);
2398 if (which == PWMD_PINENTRY_CONFIRM && pwm->disable_pinentry)
2400 rc = get_password (pwm, NULL, NULL, which, 1);
2401 return FINISH (rc);
2404 rc = _pwmd_getpin (pwm, filename, result, len, which);
2405 return FINISH (rc);
2406 #endif
2409 const char *
2410 pwmd_version ()
2412 return LIBPWMD_VERSION_STR;
2415 unsigned int
2416 pwmd_features ()
2418 unsigned int n = 0;
2420 #ifdef WITH_PINENTRY
2421 n |= PWMD_FEATURE_PINENTRY;
2422 #endif
2423 #ifdef WITH_SSH
2424 n |= PWMD_FEATURE_SSH;
2425 #endif
2426 #ifdef WITH_QUALITY
2427 n |= PWMD_FEATURE_QUALITY;
2428 #endif
2429 #ifdef WITH_GNUTLS
2430 n |= PWMD_FEATURE_GNUTLS;
2431 #endif
2432 return n;
2435 gpg_error_t
2436 pwmd_fd (pwm_t * pwm, int *fd)
2438 if (!pwm || !fd)
2439 return FINISH (GPG_ERR_INV_ARG);
2441 if (pwm->fd == -1)
2442 return FINISH (GPG_ERR_INV_STATE);
2444 *fd = pwm->fd;
2445 return 0;
2448 void
2449 pwmd_set_pointer (pwm_t *pwm, void *data)
2451 pwm->user_data = data;
2454 void *
2455 pwmd_get_pointer (pwm_t *pwm)
2457 return pwm->user_data;
2461 pwmd_gnutls_error (pwm_t *pwm, const char **str)
2463 #ifndef WITH_GNUTLS
2464 (void)pwm;
2465 (void)str;
2466 return 0;
2467 #else
2468 if (str && pwm && pwm->tls_error)
2469 *str = gnutls_strerror (pwm->tls_error);
2471 return pwm ? pwm->tls_error : 0;
2472 #endif
2475 gpg_error_t
2476 pwmd_cancel (pwm_t *pwm)
2478 if (!pwm)
2479 return FINISH (GPG_ERR_INV_ARG);
2481 #if defined(WITH_SSH) || defined(WITH_GNUTLS)
2482 if (pwm->fd == -1 && !pwm->tcp)
2483 #else
2484 if (pwm->fd == -1)
2485 #endif
2486 return FINISH (GPG_ERR_INV_STATE);
2488 /* Can only cancel the connection for the time being. */
2489 if (pwm->connected)
2490 return FINISH (GPG_ERR_INV_STATE);
2492 pwm->cancel = 1;
2493 return 0;
2496 gpg_error_t
2497 pwmd_test_quality (const char *str, double *result)
2499 #ifndef WITH_QUALITY
2500 (void)str;
2501 (void)result;
2502 return GPG_ERR_NOT_IMPLEMENTED;
2503 #else
2504 if (!result)
2505 return GPG_ERR_INV_ARG;
2507 *result = ZxcvbnMatch (str, NULL, NULL);
2508 return 0;
2509 #endif