1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006-2008 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
26 #include <sys/socket.h>
35 #include <sys/types.h>
37 #include <sys/select.h>
39 #include <netinet/in.h>
40 #include <sys/socket.h>
48 #include <gnutls/gnutls.h>
49 #include <gnutls/x509.h>
50 #define DNS_USE_GETTIMEOFDAY_FOR_ID 1
64 #define N_(msgid) dgettext("libpwmd", msgid)
70 #define xrealloc realloc
71 #define xmalloc malloc
72 #define xstrdup strdup
73 #define xcalloc calloc
80 static int gelapsed
, gtimeout
;
81 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
);
82 static gpg_error_t global_error
;
85 const char *pwmd_strerror(gpg_error_t e
)
87 gpg_err_code_t code
= gpg_err_code(e
);
89 if (code
>= GPG_ERR_USER_1
&& code
< gpg_err_code(EPWMD_MAX
)) {
93 return N_("Unknown error");
95 return N_("No cache slots available");
97 return N_("Recursion loop");
99 return N_("No file is open");
101 return N_("General LibXML error");
103 return N_("File modified");
107 return gpg_strerror(e
);
110 gpg_error_t
pwmd_init()
112 #ifdef HAVE_LIBGNUTLS
113 gnutls_global_set_mem_functions(xmalloc
, xmalloc
, NULL
, xrealloc
, xfree
);
114 gnutls_global_init ();
119 bindtextdomain("libpwmd", LOCALEDIR
);
122 assuan_set_malloc_hooks(xmalloc
, xrealloc
, xfree
);
123 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT
);
127 static pwm_t
*_socket_connect_finalize(pwm_t
*pwm
, assuan_context_t ctx
)
130 int n
= assuan_get_active_fds(ctx
, 0, active
, sizeof(active
));
132 pwm
->fd
= n
<= 0 ? -1 : dup(active
[0]);
136 pwm
->pinentry_tries
= 3;
138 assuan_set_pointer(ctx
, pwm
);
142 #ifdef HAVE_LIBGNUTLS
143 static int read_hook(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
144 size_t len
, ssize_t
*ret
)
146 pwm_t
*pwm
= assuan_get_pointer(ctx
);
148 if (!pwm
|| !pwm
->session
)
149 *ret
= read((int)fd
, data
, len
);
152 *ret
= gnutls_record_recv(pwm
->session
, data
, len
);
154 if (*ret
== GNUTLS_E_REHANDSHAKE
) {
155 *ret
= gnutls_rehandshake(pwm
->session
);
157 if (*ret
== GNUTLS_E_WARNING_ALERT_RECEIVED
||
158 *ret
== GNUTLS_A_NO_RENEGOTIATION
) {
159 fprintf(stderr
, "%s", gnutls_strerror(*ret
));
163 if (*ret
!= GNUTLS_E_SUCCESS
) {
164 fprintf(stderr
, "%s", gnutls_strerror(*ret
));
169 *ret
= gnutls_handshake(pwm
->session
);
171 if (*ret
!= GNUTLS_E_SUCCESS
) {
172 fprintf(stderr
, "%s", gnutls_strerror(*ret
));
179 } while (*ret
== GNUTLS_E_INTERRUPTED
|| *ret
== GNUTLS_E_AGAIN
);
182 return *ret
<= 0 ? 0 : 1;
185 static int write_hook(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
186 size_t len
, ssize_t
*ret
)
188 pwm_t
*pwm
= assuan_get_pointer(ctx
);
190 if (!pwm
|| !pwm
->session
)
191 *ret
= write((int)fd
, data
, len
);
194 *ret
= gnutls_record_send(pwm
->session
, data
, len
);
195 } while (*ret
== GNUTLS_E_INTERRUPTED
|| *ret
== GNUTLS_E_AGAIN
);
198 return *ret
<= 0 ? 0 : 1;
201 static void _tls_deinit(pwm_t
*pwm
)
204 //FIXME hangs when missing cert
205 gnutls_bye(pwm
->session
, GNUTLS_SHUT_RDWR
);
206 gnutls_deinit(pwm
->session
);
210 gnutls_certificate_free_credentials(pwm
->x509
);
213 static gpg_error_t
verify_certificate(pwm_t
*pwm
)
216 const gnutls_datum_t
*cert_list
;
217 unsigned int cert_list_size
;
219 gnutls_x509_crt_t cert
;
222 rc
= gnutls_certificate_verify_peers2(pwm
->session
, &status
);
225 return GPG_ERR_UNKNOWN_ERRNO
;
227 if (status
& GNUTLS_CERT_INVALID
)
228 return GPG_ERR_MISSING_CERT
;
230 if (status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
231 return GPG_ERR_BAD_SIGNATURE
;
233 if (status
& GNUTLS_CERT_REVOKED
)
234 return GPG_ERR_CERT_REVOKED
;
236 if (gnutls_certificate_type_get(pwm
->session
) != GNUTLS_CRT_X509
)
237 return GPG_ERR_UNSUPPORTED_CERT
;
239 if (gnutls_x509_crt_init(&cert
) < 0)
240 return gpg_error_from_errno(ENOMEM
);
242 cert_list
= gnutls_certificate_get_peers (pwm
->session
, &cert_list_size
);
245 rc
= GPG_ERR_MISSING_CERT
;
249 for (i
= 0; i
< cert_list_size
; i
++) {
250 if (gnutls_x509_crt_import(cert
, &cert_list
[i
], GNUTLS_X509_FMT_DER
) < 0) {
251 rc
= GPG_ERR_BAD_CERT_CHAIN
;
256 /* Beware here we do not check for errors.
258 if (gnutls_x509_crt_get_expiration_time(cert
) < time (0)) {
259 rc
= GPG_ERR_CERT_EXPIRED
;
263 if (gnutls_x509_crt_get_activation_time(cert
) > time (0)) {
264 rc
= GPG_ERR_CERT_TOO_YOUNG
;
269 if (!gnutls_x509_crt_check_hostname (cert
, hostname
))
271 printf ("The certificate's owner does not match hostname '%s'\n",
278 gnutls_x509_crt_deinit(cert
);
283 static gpg_error_t
_tls_init(pwm_t
*pwm
)
288 rc
= gnutls_certificate_allocate_credentials(&pwm
->x509
);
291 return gpg_error_from_errno(ENOMEM
);
293 /* The client certificate must be signed by the CA of the pwmd server
294 * certificate in order for the client to authenticate successfully. Man in
295 * the middle attacks are still possible if the attacker is running a pwmd
296 * that doesn't require client certificate authentication. So require the
297 * client to verify the server certificate.
299 gnutls_certificate_set_x509_trust_file (pwm
->x509
, pwm
->tcp_conn
->ca
,
300 GNUTLS_X509_FMT_PEM
);
302 rc
= gnutls_certificate_set_x509_key_file(pwm
->x509
, pwm
->tcp_conn
->cert
,
303 pwm
->tcp_conn
->key
, GNUTLS_X509_FMT_PEM
);
304 rc
= gnutls_init(&pwm
->session
, GNUTLS_CLIENT
);
305 rc
= gnutls_priority_set_direct(pwm
->session
, "SECURE256", &errstr
);
306 rc
= gnutls_credentials_set(pwm
->session
, GNUTLS_CRD_CERTIFICATE
, pwm
->x509
);
307 gnutls_transport_set_ptr(pwm
->session
,
308 (gnutls_transport_ptr_t
)pwm
->tcp_conn
->fd
);
309 rc
= gnutls_handshake(pwm
->session
);
313 rc
= GPG_ERR_INV_CIPHER_MODE
;
318 rc
= verify_certificate(pwm
);
324 static void _tls_assuan_deinit(assuan_context_t ctx
)
326 pwm_t
*pwm
= assuan_get_pointer(ctx
);
331 static void free_tcp_conn(pwmd_tcp_conn_t
*conn
)
352 * Sets common options from both pwmd_tcp_connect() and
353 * pwmd_tcp_connect_async().
355 static gpg_error_t
init_tcp_conn(pwmd_tcp_conn_t
**dst
, char *host
, int port
,
356 const char *cert
, const char *key
, const char *ca
)
359 struct passwd
*pw
= getpwuid(getuid());
360 pwmd_tcp_conn_t
*conn
;
364 return GPG_ERR_INV_ARG
;
366 conn
= xcalloc(1, sizeof(pwmd_tcp_conn_t
));
368 return gpg_error_from_errno(ENOMEM
);
370 conn
->port
= port
== -1 ? 6466 : port
;
371 conn
->host
= xstrdup(host
);
374 rc
= gpg_error_from_syserror();
379 snprintf(buf
, sizeof(buf
), "%s/.pwmd/client-cert.pem", pw
->pw_dir
);
380 conn
->cert
= xstrdup(buf
);
383 conn
->cert
= xstrdup(cert
);
386 rc
= gpg_error_from_syserror();
391 snprintf(buf
, sizeof(buf
), "%s/.pwmd/client-key.pem", pw
->pw_dir
);
392 conn
->key
= xstrdup(buf
);
395 conn
->key
= xstrdup(key
);
398 rc
= gpg_error_from_syserror();
403 snprintf(buf
, sizeof(buf
), "%s/.pwmd/ca-cert.pem", pw
->pw_dir
);
404 conn
->ca
= xstrdup(buf
);
407 conn
->ca
= xstrdup(ca
);
410 rc
= gpg_error_from_syserror();
414 conn
->fd
= socket(PF_INET
, SOCK_STREAM
, 0);
416 if (conn
->fd
== -1) {
417 rc
= gpg_error_from_syserror();
429 static gpg_error_t
do_connect(pwm_t
*pwm
, void *addr
)
431 struct sockaddr_in their_addr
;
433 pwm
->cmd
= ASYNC_CMD_CONNECT
;
434 their_addr
.sin_family
= AF_INET
;
435 their_addr
.sin_port
= htons(pwm
->tcp_conn
->port
);
436 their_addr
.sin_addr
= *((struct in_addr
*)addr
);
437 memset(their_addr
.sin_zero
, '\0', sizeof their_addr
.sin_zero
);
439 if (connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
440 sizeof(their_addr
)) == -1)
441 return gpg_error_from_syserror();
446 static void dns_resolve_cb(int result
, char type
, int count
, int ttl
,
447 void *addrs
, void *arg
)
451 if (result
!= DNS_ERR_NONE
) {
453 case DNS_ERR_NOTEXIST
:
454 pwm
->tcp_conn
->rc
= GPG_ERR_UNKNOWN_HOST
;
456 case DNS_ERR_SERVERFAILED
:
457 pwm
->tcp_conn
->rc
= GPG_ERR_EHOSTDOWN
;
459 case DNS_ERR_TIMEOUT
:
460 pwm
->tcp_conn
->rc
= GPG_ERR_TIMEOUT
;
464 pwm
->tcp_conn
->rc
= GPG_ERR_EHOSTUNREACH
;
471 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, O_NONBLOCK
);
472 pwm
->tcp_conn
->rc
= do_connect(pwm
, &addrs
[0]);
475 pwm_t
*pwmd_tcp_connect_async(char *host
, int port
, gpg_error_t
*rc
,
476 const char *cert
, const char *key
, const char *ca
)
478 #ifndef HAVE_LIBGNUTLS
479 *rc
= GPG_ERR_NOT_IMPLEMENTED
;
482 pwmd_tcp_conn_t
*conn
;
485 *rc
= init_tcp_conn(&conn
, host
, port
, cert
, key
, ca
);
490 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
491 *rc
= gpg_error_from_syserror();
496 pwm
->tcp_conn
= conn
;
497 pwm
->cmd
= ASYNC_CMD_DNS
;
498 pwm
->state
= ASYNC_PROCESS
;
499 evdns_resolv_conf_parse(DNS_OPTIONS_ALL
, "/etc/resolv.conf");
500 evdns_resolve_ipv4(pwm
->tcp_conn
->host
, 0, dns_resolve_cb
, pwm
);
505 static pwm_t
*setup_context(pwm_t
*pwm
, gpg_error_t
*rc
)
507 assuan_context_t ctx
;
508 struct assuan_io_hooks io_hooks
= {read_hook
, write_hook
};
509 int fd
= pwm
->tcp_conn
->fd
;
511 *rc
= _tls_init(pwm
);
512 free_tcp_conn(pwm
->tcp_conn
);
513 pwm
->tcp_conn
= NULL
;
518 assuan_set_io_hooks(&io_hooks
);
519 *rc
= assuan_socket_connect_fd(&ctx
, fd
, 0, pwm
);
524 assuan_set_finish_handler(ctx
, _tls_assuan_deinit
);
525 return _socket_connect_finalize(pwm
, ctx
);
533 pwm_t
*pwmd_tcp_connect(char *host
, int port
, gpg_error_t
*rc
, const char *cert
,
534 const char *key
, const char *ca
)
538 pwmd_tcp_conn_t
*conn
;
540 *rc
= init_tcp_conn(&conn
, host
, port
, cert
, key
, ca
);
545 if ((he
= gethostbyname(conn
->host
)) == NULL
) {
547 *rc
= gpg_error_from_syserror();
552 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
553 *rc
= gpg_error_from_syserror();
557 pwm
->tcp_conn
= conn
;
558 *rc
= do_connect(pwm
, he
->h_addr
);
563 return setup_context(pwm
, rc
);
576 pwm_t
*pwmd_connect(const char *path
, gpg_error_t
*rc
)
579 char *socketpath
= NULL
;
581 assuan_context_t ctx
;
584 pw
= getpwuid(getuid());
585 socketpath
= (char *)xmalloc(strlen(pw
->pw_dir
) + strlen("/.pwmd/socket") + 1);
586 sprintf(socketpath
, "%s/.pwmd/socket", pw
->pw_dir
);
589 socketpath
= xstrdup(path
);
591 *rc
= assuan_socket_connect_ext(&ctx
, socketpath
, -1, 0);
597 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
598 *rc
= gpg_error_from_syserror();
602 return _socket_connect_finalize(pwm
, ctx
);
605 gpg_error_t
pwmd_pending_line(pwm_t
*pwm
, char **line
, size_t *len
)
608 return GPG_ERR_INV_ARG
;
610 if (assuan_pending_line(pwm
->ctx
))
611 return assuan_read_line(pwm
->ctx
, line
, len
);
613 return GPG_ERR_NO_DATA
;
616 void pwmd_close(pwm_t
*pwm
)
622 assuan_disconnect(pwm
->ctx
);
625 xfree(pwm
->password
);
636 if (pwm
->pinentry_tty
)
637 xfree(pwm
->pinentry_tty
);
639 if (pwm
->pinentry_display
)
640 xfree(pwm
->pinentry_display
);
642 if (pwm
->pinentry_term
)
643 xfree(pwm
->pinentry_term
);
646 xfree(pwm
->filename
);
651 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
653 membuf_t
*mem
= (membuf_t
*)data
;
659 if ((p
= xrealloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
663 memcpy((char *)mem
->buf
+ mem
->len
, buffer
, len
);
668 void pwmd_free_result(void *data
)
673 static int _inquire_cb(void *data
, const char *keyword
)
675 pwm_t
*pwm
= (pwm_t
*)data
;
677 int flags
= fcntl(pwm
->fd
, F_GETFL
);
679 /* Shouldn't get this far without a callback. */
680 if (!pwm
->inquire_func
)
681 return GPG_ERR_INV_ARG
;
684 * Since the socket file descriptor is probably set to non-blocking, set to
685 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixes when
686 * asynchronous INQUIRE is supported by either libassuan or a later
689 fcntl(pwm
->fd
, F_SETFL
, 0);
696 rc
= pwm
->inquire_func(pwm
->inquire_data
, keyword
, rc
, &result
, &len
);
697 rc
= gpg_err_code(rc
);
699 if (rc
== GPG_ERR_EOF
|| !rc
) {
700 if (len
<= 0 || !result
|| !*result
) {
705 arc
= assuan_send_data(pwm
->ctx
, result
, len
);
707 if (rc
== GPG_ERR_EOF
) {
718 fcntl(pwm
->fd
, F_SETFL
, flags
);
722 gpg_error_t
pwmd_finalize(pwm_t
*pwm
)
724 if (!pwm
|| pwm
->cmd
== ASYNC_CMD_NONE
|| pwm
->state
!= ASYNC_DONE
)
725 return GPG_ERR_INV_ARG
;
727 pwm
->state
= ASYNC_INIT
;
728 pwm
->cmd
= ASYNC_CMD_NONE
;
730 #ifdef HAVE_LIBGNUTLS
731 if (pwm
->cmd
== ASYNC_CMD_CONNECT
|| pwm
->cmd
== ASYNC_CMD_DNS
) {
732 /* pwm is no longer a valid handle. */
733 if (pwm
->tcp_conn
->rc
) {
734 free_tcp_conn(pwm
->tcp_conn
);
738 free_tcp_conn(pwm
->tcp_conn
);
739 pwm
->tcp_conn
= NULL
;
745 return GPG_ERR_INV_ARG
;
747 pwm
->state
= ASYNC_INIT
;
750 pwm
->is_open_cmd
= 0;
755 static gpg_error_t
do_nb_command(pwm_t
*pwm
, const char *cmd
, const char *arg
)
759 size_t len
= strlen(cmd
) + 2;
761 len
+= arg
? strlen(arg
) : 0;
763 if (pwm
->state
!= ASYNC_INIT
)
764 return GPG_ERR_UNEXPECTED
;
766 buf
= (char *)xmalloc(len
);
769 rc
= gpg_error_from_errno(ENOMEM
);
773 snprintf(buf
, len
, "%s %s", cmd
, arg
? arg
: "");
774 rc
= assuan_write_line(pwm
->ctx
, buf
);
778 pwm
->state
= ASYNC_PROCESS
;
784 gpg_error_t
pwmd_open_async(pwm_t
*pwm
, const char *filename
)
786 if (!pwm
|| !filename
)
787 return GPG_ERR_INV_ARG
;
789 /* For pinentry retries. */
790 if (!pwm
->is_open_cmd
) {
792 xfree(pwm
->filename
);
794 pwm
->filename
= xstrdup(filename
);
797 pwm
->is_open_cmd
= 1;
798 return do_nb_command(pwm
, "OPEN", filename
);
801 gpg_error_t
pwmd_save_async(pwm_t
*pwm
)
804 return GPG_ERR_INV_ARG
;
806 return do_nb_command(pwm
, "SAVE", NULL
);
809 static gpg_error_t
parse_assuan_line(pwm_t
*pwm
)
815 rc
= assuan_read_line(pwm
->ctx
, &line
, &len
);
818 if (line
[0] == 'O' && line
[1] == 'K' &&
819 (line
[2] == 0 || line
[2] == ' ')) {
820 pwm
->state
= ASYNC_DONE
;
822 else if (line
[0] == '#') {
824 else if (line
[0] == 'S' && (line
[1] == 0 || line
[1] == ' ')) {
825 if (pwm
->status_func
) {
826 pwm
->status_func(pwm
->status_data
,
827 line
[1] == 0 ? line
+1 : line
+2);
830 else if (line
[0] == 'E' && line
[1] == 'R' && line
[2] == 'R' &&
831 (line
[3] == 0 || line
[3] == ' ')) {
834 pwm
->state
= ASYNC_DONE
;
841 pwmd_async_t
pwmd_process(pwm_t
*pwm
, gpg_error_t
*rc
)
845 struct timeval tv
= {0, 0};
850 *rc
= GPG_ERR_INV_ARG
;
854 #ifdef HAVE_LIBGNUTLS
855 if (pwm
->cmd
== ASYNC_CMD_DNS
) {
856 if (pwm
->tcp_conn
->rc
) {
857 *rc
= pwm
->tcp_conn
->rc
;
858 close(pwm
->tcp_conn
->fd
);
859 pwm
->state
= ASYNC_DONE
;
862 event_loop(EVLOOP_NONBLOCK
);
865 else if (pwm
->cmd
== ASYNC_CMD_CONNECT
) {
866 if (pwm
->tcp_conn
->rc
== GPG_ERR_EINPROGRESS
) {
868 socklen_t len
= sizeof(int);
871 FD_SET(pwm
->tcp_conn
->fd
, &fds
);
872 n
= select(pwm
->tcp_conn
->fd
+1, NULL
, &fds
, NULL
, &tv
);
874 if (!n
|| !FD_ISSET(pwm
->tcp_conn
->fd
, &fds
))
877 *rc
= gpg_error_from_syserror();
878 close(pwm
->tcp_conn
->fd
);
879 pwm
->state
= ASYNC_DONE
;
883 ret
= getsockopt(pwm
->tcp_conn
->fd
, SOL_SOCKET
, SO_ERROR
, &n
, &len
);
885 *rc
= ret
? gpg_error_from_syserror() : gpg_error_from_errno(n
);
886 close(pwm
->tcp_conn
->fd
);
887 pwm
->state
= ASYNC_DONE
;
891 else if (pwm
->tcp_conn
->rc
) {
892 *rc
= pwm
->tcp_conn
->rc
;
893 close(pwm
->tcp_conn
->fd
);
894 pwm
->state
= ASYNC_DONE
;
898 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, 0);
899 setup_context(pwm
, rc
);
900 pwm
->state
= ASYNC_DONE
;
906 *rc
= GPG_ERR_INV_ARG
;
909 else if (pwm
->state
== ASYNC_DONE
)
911 else if (pwm
->state
== ASYNC_INIT
) {
912 *rc
= GPG_ERR_UNEXPECTED
;
917 FD_SET(pwm
->fd
, &fds
);
919 n
= pth_select(pwm
->fd
+1, &fds
, NULL
, NULL
, &tv
);
921 n
= select(pwm
->fd
+1, &fds
, NULL
, NULL
, &tv
);
925 if (FD_ISSET(pwm
->fd
, &fds
))
926 *rc
= parse_assuan_line(pwm
);
929 while (!*rc
&& assuan_pending_line(pwm
->ctx
))
930 *rc
= parse_assuan_line(pwm
);
932 if (pwm
->is_open_cmd
&& gpg_err_code(*rc
) == EPWMD_BADKEY
&&
933 ++pwm
->ntries
< pwm
->pinentry_tries
) {
934 pwm
->state
= ASYNC_INIT
;
935 *rc
= pwmd_open_async(pwm
, pwm
->filename
);
941 static gpg_error_t
assuan_command(pwm_t
*pwm
, assuan_context_t ctx
,
942 char **result
, const char *cmd
)
950 rc
= assuan_transact(ctx
, cmd
, mem_realloc_cb
, &data
, _inquire_cb
, pwm
,
951 pwm
->status_func
, pwm
->status_data
);
961 mem_realloc_cb(&data
, "", 1);
962 *result
= (char *)data
.buf
;
966 return gpg_err_code(rc
);
969 gpg_error_t
pwmd_inquire(pwm_t
*pwm
, const char *cmd
, pwmd_inquire_fn fn
,
972 if (!pwm
|| !cmd
|| !fn
)
973 return GPG_ERR_INV_ARG
;
975 pwm
->inquire_func
= fn
;
976 pwm
->inquire_data
= data
;
977 return assuan_command(pwm
, pwm
->ctx
, NULL
, cmd
);
980 gpg_error_t
pwmd_terminate_pinentry(pwm_t
*pwm
)
982 #ifndef WITH_PINENTRY
983 return GPG_ERR_NOT_IMPLEMENTED
;
985 if (!pwm
|| pwm
->pid
== -1)
986 return GPG_ERR_INV_ARG
;
988 if (kill(pwm
->pid
, 0) == 0) {
989 if (kill(pwm
->pid
, SIGTERM
) == -1) {
990 if (kill(pwm
->pid
, SIGKILL
) == -1)
991 return gpg_error_from_errno(errno
);
994 pwm
->pin_error
= GPG_ERR_TIMEOUT
;
997 return gpg_error_from_errno(errno
);
1003 #ifdef WITH_PINENTRY
1004 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, int which
)
1007 char tmp
[ASSUAN_LINELENGTH
];
1011 pwm
->title
= xstrdup(N_("LibPWMD"));
1014 pwm
->prompt
= xstrdup(N_("Password:"));
1016 if (!pwm
->desc
&& !which
)
1017 pwm
->desc
= xstrdup(N_("Enter a password."));
1020 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Invalid password, please try again."));
1023 else if (which
== 2) {
1024 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Please type the password again for confirmation."));
1028 buf
= (char *)xmalloc(strlen("SETERROR ") + strlen(pwm
->desc
) + 1);
1029 sprintf(buf
, "SETERROR %s", pwm
->desc
);
1032 error
= pinentry_command(pwm
, NULL
, buf
);
1038 buf
= (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm
->prompt
) + 1);
1039 sprintf(buf
, "SETPROMPT %s", pwm
->prompt
);
1040 error
= pinentry_command(pwm
, NULL
, buf
);
1046 buf
= (char *)xmalloc(strlen("SETDESC ") + strlen(pwm
->title
) + 1);
1047 sprintf(buf
, "SETDESC %s", pwm
->title
);
1048 error
= pinentry_command(pwm
, NULL
, buf
);
1053 static void update_pinentry_settings(pwm_t
*pwm
)
1057 struct passwd
*pw
= getpwuid(getuid());
1060 snprintf(buf
, sizeof(buf
), "%s/.pwmd/pinentry.conf", pw
->pw_dir
);
1062 if ((fp
= fopen(buf
, "r")) == NULL
)
1065 while ((p
= fgets(buf
, sizeof(buf
), fp
)) != NULL
) {
1066 char name
[32], val
[256];
1068 if (sscanf(p
, " %31[a-zA-Z] = %255s", name
, val
) != 2)
1071 if (strcasecmp(name
, "TTYNAME") == 0) {
1072 xfree(pwm
->pinentry_tty
);
1073 pwm
->pinentry_tty
= xstrdup(val
);
1075 else if (strcasecmp(name
, "TTYTYPE") == 0) {
1076 xfree(pwm
->pinentry_term
);
1077 pwm
->pinentry_term
= xstrdup(val
);
1079 else if (strcasecmp(name
, "DISPLAY") == 0) {
1080 xfree(pwm
->pinentry_display
);
1081 pwm
->pinentry_display
= xstrdup(val
);
1083 else if (strcasecmp(name
, "PATH") == 0) {
1084 xfree(pwm
->pinentry_path
);
1085 pwm
->pinentry_path
= xstrdup(val
);
1092 static gpg_error_t
launch_pinentry(pwm_t
*pwm
)
1095 assuan_context_t ctx
;
1096 int child_list
[] = {-1};
1097 char *display
= getenv("DISPLAY");
1098 const char *argv
[6];
1099 int have_display
= 0;
1102 update_pinentry_settings(pwm
);
1104 if (pwm
->pinentry_display
|| display
)
1107 tty
= pwm
->pinentry_tty
? pwm
->pinentry_tty
: ttyname(STDOUT_FILENO
);
1110 return gpg_error_from_errno(errno
);
1113 if (!have_display
&& !tty
)
1114 return GPG_ERR_ENOTTY
;
1116 argv
[0] = "pinentry";
1117 argv
[1] = have_display
? "--display" : "--ttyname";
1118 argv
[2] = have_display
? pwm
->pinentry_display
? pwm
->pinentry_display
: display
: tty
;
1121 if (!have_display
) {
1122 argv
[3] = "--ttytype";
1123 argv
[4] = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv("TERM");
1127 rc
= assuan_pipe_connect(&ctx
, pwm
->pinentry_path
? pwm
->pinentry_path
: PINENTRY_PATH
, argv
, child_list
);
1132 pwm
->pid
= assuan_get_pid(ctx
);
1134 return set_pinentry_strings(pwm
, 0);
1137 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1142 n
= launch_pinentry(pwm
);
1148 return assuan_command(pwm
, pwm
->pctx
, result
, cmd
);
1151 static void pinentry_disconnect(pwm_t
*pwm
)
1154 assuan_disconnect(pwm
->pctx
);
1161 * Only called from a child process.
1163 static void catchsig(int sig
)
1167 if (gelapsed
++ >= gtimeout
) {
1168 global_error
= pwmd_terminate_pinentry(gpwm
);
1171 global_error
= GPG_ERR_TIMEOUT
;
1184 * Borrowed from libassuan.
1186 static char *percent_escape(const char *atext
)
1188 const unsigned char *s
;
1189 int len
= strlen(atext
) * 3 + 1;
1190 char *buf
= (char *)xmalloc(len
), *p
= buf
;
1195 for (s
=(const unsigned char *)atext
; *s
; s
++) {
1197 sprintf (p
, "%%%02X", *s
);
1209 static gpg_error_t
send_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1212 return GPG_ERR_INV_ARG
;
1214 return assuan_command(pwm
, pwm
->ctx
, result
, cmd
);
1218 * Avoid sending the BYE command here. libassuan will close the file
1219 * descriptor and release the assuan context. Use pwmd_close() instead.
1221 gpg_error_t
pwmd_command(pwm_t
*pwm
, char **result
, const char *cmd
, ...)
1229 return GPG_ERR_INV_ARG
;
1234 * C99 allows the dst pointer to be null which will calculate the length
1235 * of the result and return it.
1237 len
= vsnprintf(NULL
, 0, cmd
, ap
);
1238 buf
= (char *)xmalloc(len
+ 1);
1239 len
= vsnprintf(buf
, len
+ 1, cmd
, ap
);
1241 error
= send_command(pwm
, result
, buf
);
1246 #ifdef WITH_PINENTRY
1247 static gpg_error_t
do_getpin(pwm_t
*pwm
, char **result
)
1250 signal(SIGALRM
, catchsig
);
1255 return pinentry_command(pwm
, result
, "GETPIN");
1258 static gpg_error_t
getpin(pwm_t
*pwm
, char **result
, int *try_n
, int which
)
1260 int pin_try
= *try_n
;
1266 if (pin_try
== -1) {
1267 error
= set_pinentry_strings(pwm
, which
);
1270 pinentry_disconnect(pwm
);
1275 if (pwm
->pinentry_tries
-1 != pin_try
) {
1276 error
= set_pinentry_strings(pwm
, 1);
1279 pinentry_disconnect(pwm
);
1285 error
= do_getpin(pwm
, result
);
1288 * Since there was input cancel any timeout setting.
1293 if (error
== GPG_ERR_CANCELED
)
1294 return GPG_ERR_CANCELED
;
1296 if (pin_try
!= -1 && pin_try
--)
1300 pinentry_disconnect(pwm
);
1310 gpg_error_t
pwmd_open_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1314 #ifndef WITH_PINENTRY
1315 return GPG_ERR_NOT_IMPLEMENTED
;
1318 if (!pwm
|| !pw
|| !pw
->filename
[0])
1319 return GPG_ERR_INV_ARG
;
1329 xfree(pwm
->filename
);
1331 pwm
->filename
= xstrdup(pw
->filename
);
1332 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1336 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1340 static gpg_error_t
do_open_command(pwm_t
*pwm
, const char *filename
, char *password
)
1342 char buf
[ASSUAN_LINELENGTH
];
1344 char *result
= NULL
;
1346 snprintf(buf
, sizeof(buf
), "OPEN %s %s", filename
, password
? password
: "");
1347 error
= send_command(pwm
, &result
, buf
);
1348 memset(buf
, 0, sizeof(buf
));
1350 if (error
&& result
)
1356 static int do_pwmd_open(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1357 int nb
, int timeout
)
1359 char *result
= NULL
;
1360 char *password
= NULL
;
1361 char path
[PATH_MAX
];
1362 #ifdef WITH_PINENTRY
1366 if (!pwm
|| !filename
|| !*filename
) {
1367 *error
= GPG_ERR_INV_ARG
;
1371 #ifdef WITH_PINENTRY
1372 pin_try
= pwm
->pinentry_tries
- 1;
1376 * Avoid calling pinentry if the password is cached on the server or if
1377 * this is a new file.
1379 *error
= pwmd_command(pwm
, &result
, "GETCONFIG data_directory");
1384 snprintf(path
, sizeof(path
), "%s/%s", result
, filename
);
1385 pwmd_free_result(result
);
1387 if (access(path
, R_OK
) == -1) {
1388 if (errno
== ENOENT
)
1392 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", filename
);
1394 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1395 if (pwm
->passfunc
) {
1396 password
= pwm
->passfunc(pwm
, pwm
->passdata
);
1400 #ifdef WITH_PINENTRY
1402 * Get the password from pinentry.
1404 if (pwm
->use_pinentry
) {
1406 * Nonblocking is wanted. fork() then return a file descriptor
1407 * that the client can use to read() from.
1412 pwmd_nb_status_t pw
;
1414 if (pipe(p
) == -1) {
1415 *error
= gpg_error_from_syserror();
1428 strncpy(pw
.filename
, filename
, sizeof(pw
.filename
));
1429 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1439 *error
= getpin(pwm
, &password
, &pin_try
, 0);
1444 pinentry_disconnect(pwm
);
1446 if (gtimeout
&& gelapsed
>= gtimeout
)
1447 *error
= GPG_ERR_TIMEOUT
;
1451 pth_write(p
[1], &pw
, sizeof(pw
));
1453 write(p
[1], &pw
, sizeof(pw
));
1460 * Don't count the time it takes to open the file
1461 * which may have many iterations.
1463 signal(SIGALRM
, SIG_DFL
);
1464 *error
= do_open_command(pwm
, filename
, password
);
1467 signal(SIGALRM
, catchsig
);
1469 if (pwm
->pctx
&& *error
== EPWMD_BADKEY
) {
1471 goto getpin_nb_again
;
1473 goto getpin_nb_fail
;
1476 pinentry_disconnect(pwm
);
1479 pth_write(p
[1], &pw
, sizeof(pw
));
1481 write(p
[1], &pw
, sizeof(pw
));
1487 *error
= gpg_error_from_syserror();
1502 * Not using pinentry and the file was not found
1505 password
= pwm
->password
;
1506 #ifdef WITH_PINENTRY
1514 *error
= do_open_command(pwm
, filename
, password
);
1517 * Keep the user defined password set with pwmd_setopt(). The password may
1518 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1520 if (password
&& password
!= pwm
->password
)
1523 #ifdef WITH_PINENTRY
1524 if (*error
== EPWMD_BADKEY
) {
1525 if (pin_try
-- > 0 && !nb
) {
1526 *error
= pwmd_command(pwm
, &result
, "OPTION TITLE=%s",
1527 N_("Invalid password, please try again."));
1536 pinentry_disconnect(pwm
);
1544 xfree(pwm
->filename
);
1546 pwm
->filename
= xstrdup(filename
);
1550 * The file is cached or the file is a new file.
1553 return *error
? -1 : -2;
1555 return *error
? 1 : 0;
1558 gpg_error_t
pwmd_open(pwm_t
*pwm
, const char *filename
)
1562 do_pwmd_open(pwm
, &error
, filename
, 0, 0);
1566 int pwmd_open_nb(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1569 #ifndef WITH_PINENTRY
1570 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1573 return do_pwmd_open(pwm
, error
, filename
, 1, timeout
);
1577 #ifdef WITH_PINENTRY
1578 static gpg_error_t
do_save_getpin(pwm_t
*pwm
, char **password
)
1582 char *result
= NULL
;
1586 error
= getpin(pwm
, &result
, &pin_try
, confirm
? 2 : 0);
1590 pinentry_disconnect(pwm
);
1603 if (strcmp(*password
, result
)) {
1606 pinentry_disconnect(pwm
);
1607 error
= EPWMD_BADKEY
;
1612 pinentry_disconnect(pwm
);
1617 static gpg_error_t
do_save_command(pwm_t
*pwm
, char *password
)
1619 char buf
[ASSUAN_LINELENGTH
];
1621 char *result
= NULL
;
1623 snprintf(buf
, sizeof(buf
), "SAVE %s", password
? password
: "");
1624 error
= send_command(pwm
, &result
, buf
);
1625 memset(&buf
, 0, sizeof(buf
));
1627 if (error
&& result
)
1633 gpg_error_t
pwmd_save_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1637 #ifndef WITH_PINENTRY
1638 return GPG_ERR_NOT_IMPLEMENTED
;
1641 if (!pwm
|| !pw
|| !pw
->filename
[0])
1642 return GPG_ERR_INV_ARG
;
1646 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1650 static int do_pwmd_save(pwm_t
*pwm
, gpg_error_t
*error
, int nb
)
1652 char *result
= NULL
;
1653 char *password
= NULL
;
1656 *error
= GPG_ERR_INV_ARG
;
1660 if (pwm
->use_pinentry
|| pwm
->passfunc
) {
1661 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", pwm
->filename
);
1663 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1664 #ifdef WITH_PINENTRY
1665 if (pwm
->use_pinentry
) {
1669 pwmd_nb_status_t pw
;
1671 if (pipe(p
) == -1) {
1672 *error
= gpg_error_from_syserror();
1685 strncpy(pw
.filename
, pwm
->filename
, sizeof(pw
.filename
));
1686 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1691 *error
= do_save_getpin(pwm
, &password
);
1692 } while (*error
== EPWMD_BADKEY
);
1696 pinentry_disconnect(pwm
);
1700 pth_write(p
[1], &pw
, sizeof(pw
));
1702 write(p
[1], &pw
, sizeof(pw
));
1708 *error
= do_save_command(pwm
, password
);
1709 pinentry_disconnect(pwm
);
1712 pth_write(p
[1], &pw
, sizeof(pw
));
1714 write(p
[1], &pw
, sizeof(pw
));
1720 *error
= gpg_error_from_syserror();
1732 *error
= do_save_getpin(pwm
, &password
);
1740 password
= (*pwm
->passfunc
)(pwm
, pwm
->passdata
);
1741 #ifdef WITH_PINENTRY
1751 password
= pwm
->password
;
1753 *error
= do_save_command(pwm
, password
);
1755 if (password
&& password
!= pwm
->password
)
1759 return *error
? -1 : -2;
1761 return *error
? 1 : 0;
1764 int pwmd_save_nb(pwm_t
*pwm
, gpg_error_t
*error
)
1766 #ifndef WITH_PINENTRY
1767 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1770 return do_pwmd_save(pwm
, error
, 1);
1774 gpg_error_t
pwmd_save(pwm_t
*pwm
)
1778 do_pwmd_save(pwm
, &error
, 0);
1782 gpg_error_t
pwmd_setopt(pwm_t
*pwm
, pwmd_option_t opt
, ...)
1785 #ifdef WITH_PINENTRY
1786 int n
= va_arg(ap
, int);
1790 gpg_error_t error
= 0;
1793 return GPG_ERR_INV_ARG
;
1798 case PWMD_OPTION_STATUS_FUNC
:
1799 pwm
->status_func
= va_arg(ap
, pwmd_status_fn
);
1801 case PWMD_OPTION_STATUS_DATA
:
1802 pwm
->status_data
= va_arg(ap
, void *);
1804 case PWMD_OPTION_PASSWORD_FUNC
:
1805 pwm
->passfunc
= va_arg(ap
, pwmd_password_fn
);
1807 case PWMD_OPTION_PASSWORD_DATA
:
1808 pwm
->passdata
= va_arg(ap
, void *);
1810 case PWMD_OPTION_PASSWORD
:
1811 arg1
= va_arg(ap
, char *);
1814 xfree(pwm
->password
);
1816 pwm
->password
= xstrdup(arg1
);
1818 #ifdef WITH_PINENTRY
1819 case PWMD_OPTION_PINENTRY
:
1820 n
= va_arg(ap
, int);
1822 if (n
!= 0 && n
!= 1) {
1824 error
= GPG_ERR_INV_VALUE
;
1827 pwm
->use_pinentry
= n
;
1828 error
= pwmd_command(pwm
, &result
, "OPTION PINENTRY=%i",
1829 !pwm
->use_pinentry
);
1832 case PWMD_OPTION_PINENTRY_TRIES
:
1833 n
= va_arg(ap
, int);
1837 error
= GPG_ERR_INV_VALUE
;
1840 pwm
->pinentry_tries
= n
;
1842 case PWMD_OPTION_PINENTRY_PATH
:
1843 if (pwm
->pinentry_path
)
1844 xfree(pwm
->pinentry_path
);
1846 pwm
->pinentry_path
= xstrdup(va_arg(ap
, char *));
1848 case PWMD_OPTION_PINENTRY_TTY
:
1849 if (pwm
->pinentry_tty
)
1850 xfree(pwm
->pinentry_tty
);
1852 pwm
->pinentry_tty
= xstrdup(va_arg(ap
, char *));
1854 case PWMD_OPTION_PINENTRY_DISPLAY
:
1855 if (pwm
->pinentry_display
)
1856 xfree(pwm
->pinentry_display
);
1858 pwm
->pinentry_display
= xstrdup(va_arg(ap
, char *));
1860 case PWMD_OPTION_PINENTRY_TERM
:
1861 if (pwm
->pinentry_term
)
1862 xfree(pwm
->pinentry_term
);
1864 pwm
->pinentry_term
= xstrdup(va_arg(ap
, char *));
1866 case PWMD_OPTION_PINENTRY_TITLE
:
1869 pwm
->title
= percent_escape(va_arg(ap
, char *));
1871 case PWMD_OPTION_PINENTRY_PROMPT
:
1874 pwm
->prompt
= percent_escape(va_arg(ap
, char *));
1876 case PWMD_OPTION_PINENTRY_DESC
:
1879 pwm
->desc
= percent_escape(va_arg(ap
, char *));
1882 case PWMD_OPTION_PINENTRY
:
1883 case PWMD_OPTION_PINENTRY_TRIES
:
1884 case PWMD_OPTION_PINENTRY_PATH
:
1885 case PWMD_OPTION_PINENTRY_TTY
:
1886 case PWMD_OPTION_PINENTRY_DISPLAY
:
1887 case PWMD_OPTION_PINENTRY_TERM
:
1888 case PWMD_OPTION_PINENTRY_TITLE
:
1889 case PWMD_OPTION_PINENTRY_PROMPT
:
1890 case PWMD_OPTION_PINENTRY_DESC
:
1891 error
= GPG_ERR_NOT_IMPLEMENTED
;
1895 error
= GPG_ERR_NOT_IMPLEMENTED
;
1903 gpg_error_t
pwmd_assuan_ctx(pwm_t
*pwm
, assuan_context_t
*ctx
, int *fd
)
1906 return GPG_ERR_INV_ARG
;