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>
52 #include <gnutls/gnutls.h>
53 #include <gnutls/x509.h>
54 #define DNS_USE_GETTIMEOFDAY_FOR_ID 1
68 #define N_(msgid) dgettext("libpwmd", msgid)
74 #define xrealloc realloc
75 #define xmalloc malloc
76 #define xstrdup strdup
77 #define xcalloc calloc
84 static int gelapsed
, gtimeout
;
85 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
);
86 static gpg_error_t global_error
;
89 const char *pwmd_strerror(gpg_error_t e
)
91 gpg_err_code_t code
= gpg_err_code(e
);
93 if (code
>= GPG_ERR_USER_1
&& code
< gpg_err_code(EPWMD_MAX
)) {
97 return N_("Unknown error");
99 return N_("No cache slots available");
101 return N_("Recursion loop");
103 return N_("No file is open");
105 return N_("General LibXML error");
107 return N_("File modified");
109 return N_("Access denied");
113 return gpg_strerror(e
);
116 gpg_error_t
pwmd_init()
118 static int initialized
;
123 #ifdef HAVE_LIBGNUTLS
124 gnutls_global_set_mem_functions(xmalloc
, xmalloc
, NULL
, xrealloc
, xfree
);
125 gnutls_global_init ();
130 bindtextdomain("libpwmd", LOCALEDIR
);
133 assuan_set_malloc_hooks(xmalloc
, xrealloc
, xfree
);
134 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT
);
139 static pwm_t
*_socket_connect_finalize(pwm_t
*pwm
, assuan_context_t ctx
)
142 int n
= assuan_get_active_fds(ctx
, 0, active
, sizeof(active
));
144 pwm
->fd
= n
<= 0 ? -1 : dup(active
[0]);
148 pwm
->pinentry_tries
= 3;
150 assuan_set_pointer(ctx
, pwm
);
154 #ifdef HAVE_LIBGNUTLS
155 static int read_hook(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
156 size_t len
, ssize_t
*ret
)
158 pwm_t
*pwm
= assuan_get_pointer(ctx
);
160 if (!pwm
|| !pwm
->session
)
161 *ret
= read((int)fd
, data
, len
);
164 *ret
= gnutls_record_recv(pwm
->session
, data
, len
);
166 if (*ret
== GNUTLS_E_REHANDSHAKE
) {
167 *ret
= gnutls_rehandshake(pwm
->session
);
169 if (*ret
== GNUTLS_E_WARNING_ALERT_RECEIVED
||
170 *ret
== GNUTLS_A_NO_RENEGOTIATION
) {
175 if (*ret
!= GNUTLS_E_SUCCESS
) {
181 *ret
= gnutls_handshake(pwm
->session
);
183 if (*ret
!= GNUTLS_E_SUCCESS
) {
191 } while (*ret
== GNUTLS_E_INTERRUPTED
|| *ret
== GNUTLS_E_AGAIN
);
194 return *ret
<= 0 ? 0 : 1;
197 static int write_hook(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
198 size_t len
, ssize_t
*ret
)
200 pwm_t
*pwm
= assuan_get_pointer(ctx
);
202 if (!pwm
|| !pwm
->session
)
203 *ret
= write((int)fd
, data
, len
);
206 *ret
= gnutls_record_send(pwm
->session
, data
, len
);
207 } while (*ret
== GNUTLS_E_INTERRUPTED
|| *ret
== GNUTLS_E_AGAIN
);
210 return *ret
<= 0 ? 0 : 1;
213 static void _tls_deinit(pwm_t
*pwm
)
216 gnutls_bye(pwm
->session
, GNUTLS_SHUT_WR
);
217 gnutls_deinit(pwm
->session
);
221 gnutls_certificate_free_credentials(pwm
->x509
);
227 static gpg_error_t
verify_certificate(pwm_t
*pwm
)
230 const gnutls_datum_t
*cert_list
;
231 unsigned int cert_list_size
;
233 gnutls_x509_crt_t cert
;
236 rc
= gnutls_certificate_verify_peers2(pwm
->session
, &status
);
240 return GPG_ERR_BAD_CERT
;
243 if (status
& GNUTLS_CERT_INVALID
)
244 return GPG_ERR_BAD_CERT
;
246 if (status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
247 return GPG_ERR_BAD_SIGNATURE
;
249 if (status
& GNUTLS_CERT_REVOKED
)
250 return GPG_ERR_CERT_REVOKED
;
252 if (gnutls_certificate_type_get(pwm
->session
) != GNUTLS_CRT_X509
)
253 return GPG_ERR_UNSUPPORTED_CERT
;
255 if (gnutls_x509_crt_init(&cert
) < 0)
256 return gpg_error_from_errno(ENOMEM
);
258 cert_list
= gnutls_certificate_get_peers(pwm
->session
, &cert_list_size
);
261 rc
= GPG_ERR_MISSING_CERT
;
265 for (i
= 0; i
< cert_list_size
; i
++) {
266 if (gnutls_x509_crt_import(cert
, &cert_list
[i
], GNUTLS_X509_FMT_DER
) < 0) {
267 rc
= GPG_ERR_BAD_CERT_CHAIN
;
271 if (gnutls_x509_crt_get_expiration_time(cert
) < time(0)) {
272 rc
= GPG_ERR_CERT_EXPIRED
;
276 if (gnutls_x509_crt_get_activation_time(cert
) > time(0)) {
277 rc
= GPG_ERR_CERT_TOO_YOUNG
;
283 gnutls_x509_crt_deinit(cert
);
287 static gpg_error_t
_tls_init(pwm_t
*pwm
)
292 rc
= gnutls_certificate_allocate_credentials(&pwm
->x509
);
295 return gpg_error_from_errno(ENOMEM
);
297 /* The client certificate must be signed by the CA of the pwmd server
298 * certificate in order for the client to authenticate successfully. Man in
299 * the middle attacks are still possible if the attacker is running a pwmd
300 * that doesn't require client certificate authentication. So require the
301 * client to verify the server certificate.
303 rc
= gnutls_certificate_set_x509_trust_file (pwm
->x509
, pwm
->tcp_conn
->ca
,
304 GNUTLS_X509_FMT_PEM
);
308 rc
= GPG_ERR_INV_CERT_OBJ
;
312 rc
= gnutls_certificate_set_x509_key_file(pwm
->x509
, pwm
->tcp_conn
->cert
,
313 pwm
->tcp_conn
->key
, GNUTLS_X509_FMT_PEM
);
315 if (rc
!= GNUTLS_E_SUCCESS
) {
317 rc
= GPG_ERR_INV_CERT_OBJ
;
321 rc
= gnutls_init(&pwm
->session
, GNUTLS_CLIENT
);
323 if (rc
!= GNUTLS_E_SUCCESS
) {
325 rc
= GPG_ERR_INV_CERT_OBJ
;
329 rc
= gnutls_priority_set_direct(pwm
->session
, "SECURE256", &errstr
);
331 if (rc
!= GNUTLS_E_SUCCESS
) {
333 rc
= GPG_ERR_INV_CERT_OBJ
;
337 rc
= gnutls_credentials_set(pwm
->session
, GNUTLS_CRD_CERTIFICATE
, pwm
->x509
);
339 if (rc
!= GNUTLS_E_SUCCESS
) {
341 rc
= GPG_ERR_INV_CERT_OBJ
;
345 gnutls_transport_set_ptr(pwm
->session
,
346 (gnutls_transport_ptr_t
)pwm
->tcp_conn
->fd
);
347 rc
= gnutls_handshake(pwm
->session
);
349 if (rc
!= GNUTLS_E_SUCCESS
) {
351 rc
= GPG_ERR_INV_CERT_OBJ
;
356 rc
= verify_certificate(pwm
);
365 static void _tls_assuan_deinit(assuan_context_t ctx
)
367 pwm_t
*pwm
= assuan_get_pointer(ctx
);
372 static void free_tcp_conn(pwmd_tcp_conn_t
*conn
)
393 * Sets common options from both pwmd_tcp_connect() and
394 * pwmd_tcp_connect_async().
396 static gpg_error_t
init_tcp_conn(pwmd_tcp_conn_t
**dst
, char *host
, int port
,
397 const char *cert
, const char *key
, const char *ca
)
400 struct passwd
*pw
= getpwuid(getuid());
401 pwmd_tcp_conn_t
*conn
;
405 return GPG_ERR_INV_ARG
;
407 conn
= xcalloc(1, sizeof(pwmd_tcp_conn_t
));
409 return gpg_error_from_errno(ENOMEM
);
411 conn
->port
= port
== -1 ? 6466 : port
;
412 conn
->host
= xstrdup(host
);
415 rc
= gpg_error_from_errno(ENOMEM
);
420 snprintf(buf
, sizeof(buf
), "%s/.pwmd/client-cert.pem", pw
->pw_dir
);
421 conn
->cert
= xstrdup(buf
);
424 conn
->cert
= xstrdup(cert
);
427 rc
= gpg_error_from_errno(ENOMEM
);
432 snprintf(buf
, sizeof(buf
), "%s/.pwmd/client-key.pem", pw
->pw_dir
);
433 conn
->key
= xstrdup(buf
);
436 conn
->key
= xstrdup(key
);
439 rc
= gpg_error_from_errno(ENOMEM
);
444 snprintf(buf
, sizeof(buf
), "%s/.pwmd/ca-cert.pem", pw
->pw_dir
);
445 conn
->ca
= xstrdup(buf
);
448 conn
->ca
= xstrdup(ca
);
451 rc
= gpg_error_from_errno(ENOMEM
);
455 conn
->fd
= socket(PF_INET
, SOCK_STREAM
, 0);
457 if (conn
->fd
== -1) {
458 rc
= gpg_error_from_syserror();
470 static gpg_error_t
do_connect(pwm_t
*pwm
, void *addr
)
472 struct sockaddr_in their_addr
;
474 pwm
->cmd
= ASYNC_CMD_CONNECT
;
475 their_addr
.sin_family
= AF_INET
;
476 their_addr
.sin_port
= htons(pwm
->tcp_conn
->port
);
477 their_addr
.sin_addr
= *((struct in_addr
*)addr
);
478 memset(their_addr
.sin_zero
, '\0', sizeof their_addr
.sin_zero
);
480 if (connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
481 sizeof(their_addr
)) == -1)
482 return gpg_error_from_syserror();
487 static void dns_resolve_cb(int result
, char type
, int count
, int ttl
,
488 void *addrs
, void *arg
)
492 if (result
!= DNS_ERR_NONE
) {
494 case DNS_ERR_NOTEXIST
:
495 pwm
->tcp_conn
->rc
= GPG_ERR_UNKNOWN_HOST
;
497 case DNS_ERR_SERVERFAILED
:
498 pwm
->tcp_conn
->rc
= GPG_ERR_EHOSTDOWN
;
500 case DNS_ERR_TIMEOUT
:
501 pwm
->tcp_conn
->rc
= GPG_ERR_TIMEOUT
;
505 pwm
->tcp_conn
->rc
= GPG_ERR_EHOSTUNREACH
;
512 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, O_NONBLOCK
);
513 pwm
->tcp_conn
->rc
= do_connect(pwm
, &addrs
[0]);
516 pwm_t
*pwmd_tcp_connect_async(char *host
, int port
, gpg_error_t
*rc
,
517 const char *cert
, const char *key
, const char *ca
)
519 #ifndef HAVE_LIBGNUTLS
520 *rc
= GPG_ERR_NOT_IMPLEMENTED
;
523 pwmd_tcp_conn_t
*conn
;
526 *rc
= init_tcp_conn(&conn
, host
, port
, cert
, key
, ca
);
531 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
532 *rc
= gpg_error_from_syserror();
537 pwm
->tcp_conn
= conn
;
538 pwm
->cmd
= ASYNC_CMD_DNS
;
539 pwm
->state
= ASYNC_PROCESS
;
540 evdns_resolv_conf_parse(DNS_OPTIONS_ALL
, "/etc/resolv.conf");
541 evdns_resolve_ipv4(pwm
->tcp_conn
->host
, 0, dns_resolve_cb
, pwm
);
546 static pwm_t
*setup_context(pwm_t
*pwm
, gpg_error_t
*rc
)
548 assuan_context_t ctx
;
549 struct assuan_io_hooks io_hooks
= {read_hook
, write_hook
};
550 int fd
= pwm
->tcp_conn
->fd
;
552 *rc
= _tls_init(pwm
);
553 free_tcp_conn(pwm
->tcp_conn
);
554 pwm
->tcp_conn
= NULL
;
559 assuan_set_io_hooks(&io_hooks
);
560 *rc
= assuan_socket_connect_fd(&ctx
, fd
, 0, pwm
);
565 assuan_set_finish_handler(ctx
, _tls_assuan_deinit
);
566 return _socket_connect_finalize(pwm
, ctx
);
574 pwm_t
*pwmd_tcp_connect(char *host
, int port
, gpg_error_t
*rc
, const char *cert
,
575 const char *key
, const char *ca
)
579 pwmd_tcp_conn_t
*conn
;
581 *rc
= init_tcp_conn(&conn
, host
, port
, cert
, key
, ca
);
586 if ((he
= gethostbyname(conn
->host
)) == NULL
) {
588 *rc
= gpg_error_from_syserror();
593 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
594 *rc
= gpg_error_from_syserror();
598 pwm
->tcp_conn
= conn
;
599 *rc
= do_connect(pwm
, he
->h_addr
);
604 return setup_context(pwm
, rc
);
617 pwm_t
*pwmd_connect(const char *path
, gpg_error_t
*rc
)
620 char *socketpath
= NULL
;
622 assuan_context_t ctx
;
625 pw
= getpwuid(getuid());
626 socketpath
= (char *)xmalloc(strlen(pw
->pw_dir
) + strlen("/.pwmd/socket") + 1);
627 sprintf(socketpath
, "%s/.pwmd/socket", pw
->pw_dir
);
630 socketpath
= xstrdup(path
);
632 *rc
= assuan_socket_connect_ext(&ctx
, socketpath
, -1, 0);
638 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
639 *rc
= gpg_error_from_syserror();
643 return _socket_connect_finalize(pwm
, ctx
);
646 gpg_error_t
pwmd_pending_line(pwm_t
*pwm
, char **line
, size_t *len
)
649 return GPG_ERR_INV_ARG
;
651 if (assuan_pending_line(pwm
->ctx
))
652 return assuan_read_line(pwm
->ctx
, line
, len
);
654 return GPG_ERR_NO_DATA
;
657 void pwmd_close(pwm_t
*pwm
)
663 assuan_disconnect(pwm
->ctx
);
666 xfree(pwm
->password
);
677 if (pwm
->pinentry_tty
)
678 xfree(pwm
->pinentry_tty
);
680 if (pwm
->pinentry_display
)
681 xfree(pwm
->pinentry_display
);
683 if (pwm
->pinentry_term
)
684 xfree(pwm
->pinentry_term
);
687 xfree(pwm
->filename
);
692 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
694 membuf_t
*mem
= (membuf_t
*)data
;
700 if ((p
= xrealloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
704 memcpy((char *)mem
->buf
+ mem
->len
, buffer
, len
);
709 void pwmd_free_result(void *data
)
714 static int _inquire_cb(void *data
, const char *keyword
)
716 pwm_t
*pwm
= (pwm_t
*)data
;
718 int flags
= fcntl(pwm
->fd
, F_GETFL
);
720 /* Shouldn't get this far without a callback. */
721 if (!pwm
->inquire_func
)
722 return GPG_ERR_INV_ARG
;
725 * Since the socket file descriptor is probably set to non-blocking, set to
726 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixes when
727 * asynchronous INQUIRE is supported by either libassuan or a later
730 fcntl(pwm
->fd
, F_SETFL
, 0);
737 rc
= pwm
->inquire_func(pwm
->inquire_data
, keyword
, rc
, &result
, &len
);
738 rc
= gpg_err_code(rc
);
740 if (rc
== GPG_ERR_EOF
|| !rc
) {
741 if (len
<= 0 || !result
|| !*result
) {
746 arc
= assuan_send_data(pwm
->ctx
, result
, len
);
748 if (rc
== GPG_ERR_EOF
) {
759 fcntl(pwm
->fd
, F_SETFL
, flags
);
763 gpg_error_t
pwmd_finalize(pwm_t
*pwm
)
765 if (!pwm
|| pwm
->cmd
== ASYNC_CMD_NONE
|| pwm
->state
!= ASYNC_DONE
)
766 return GPG_ERR_INV_ARG
;
768 pwm
->state
= ASYNC_INIT
;
769 pwm
->cmd
= ASYNC_CMD_NONE
;
771 #ifdef HAVE_LIBGNUTLS
772 if (pwm
->cmd
== ASYNC_CMD_CONNECT
|| pwm
->cmd
== ASYNC_CMD_DNS
) {
773 /* pwm is no longer a valid handle. */
774 if (pwm
->tcp_conn
->rc
) {
775 free_tcp_conn(pwm
->tcp_conn
);
779 free_tcp_conn(pwm
->tcp_conn
);
780 pwm
->tcp_conn
= NULL
;
786 return GPG_ERR_INV_ARG
;
788 pwm
->state
= ASYNC_INIT
;
791 pwm
->is_open_cmd
= 0;
796 static gpg_error_t
do_nb_command(pwm_t
*pwm
, const char *cmd
, const char *arg
)
800 size_t len
= strlen(cmd
) + 2;
802 len
+= arg
? strlen(arg
) : 0;
804 if (pwm
->state
!= ASYNC_INIT
)
805 return GPG_ERR_UNEXPECTED
;
807 buf
= (char *)xmalloc(len
);
810 rc
= gpg_error_from_errno(ENOMEM
);
814 snprintf(buf
, len
, "%s %s", cmd
, arg
? arg
: "");
815 rc
= assuan_write_line(pwm
->ctx
, buf
);
819 pwm
->state
= ASYNC_PROCESS
;
825 gpg_error_t
pwmd_open_async(pwm_t
*pwm
, const char *filename
)
827 if (!pwm
|| !filename
)
828 return GPG_ERR_INV_ARG
;
830 /* For pinentry retries. */
831 if (!pwm
->is_open_cmd
) {
833 xfree(pwm
->filename
);
835 pwm
->filename
= xstrdup(filename
);
838 pwm
->is_open_cmd
= 1;
839 return do_nb_command(pwm
, "OPEN", filename
);
842 gpg_error_t
pwmd_save_async(pwm_t
*pwm
)
845 return GPG_ERR_INV_ARG
;
847 return do_nb_command(pwm
, "SAVE", NULL
);
850 static gpg_error_t
parse_assuan_line(pwm_t
*pwm
)
856 rc
= assuan_read_line(pwm
->ctx
, &line
, &len
);
859 if (line
[0] == 'O' && line
[1] == 'K' &&
860 (line
[2] == 0 || line
[2] == ' ')) {
861 pwm
->state
= ASYNC_DONE
;
863 else if (line
[0] == '#') {
865 else if (line
[0] == 'S' && (line
[1] == 0 || line
[1] == ' ')) {
866 if (pwm
->status_func
) {
867 pwm
->status_func(pwm
->status_data
,
868 line
[1] == 0 ? line
+1 : line
+2);
871 else if (line
[0] == 'E' && line
[1] == 'R' && line
[2] == 'R' &&
872 (line
[3] == 0 || line
[3] == ' ')) {
875 pwm
->state
= ASYNC_DONE
;
882 pwmd_async_t
pwmd_process(pwm_t
*pwm
, gpg_error_t
*rc
)
886 struct timeval tv
= {0, 0};
891 *rc
= GPG_ERR_INV_ARG
;
895 #ifdef HAVE_LIBGNUTLS
896 if (pwm
->cmd
== ASYNC_CMD_DNS
) {
897 if (pwm
->tcp_conn
->rc
) {
898 *rc
= pwm
->tcp_conn
->rc
;
899 close(pwm
->tcp_conn
->fd
);
900 pwm
->state
= ASYNC_DONE
;
903 event_loop(EVLOOP_NONBLOCK
);
906 else if (pwm
->cmd
== ASYNC_CMD_CONNECT
) {
907 if (pwm
->tcp_conn
->rc
== GPG_ERR_EINPROGRESS
) {
909 socklen_t len
= sizeof(int);
912 FD_SET(pwm
->tcp_conn
->fd
, &fds
);
913 n
= select(pwm
->tcp_conn
->fd
+1, NULL
, &fds
, NULL
, &tv
);
915 if (!n
|| !FD_ISSET(pwm
->tcp_conn
->fd
, &fds
))
918 *rc
= gpg_error_from_syserror();
919 close(pwm
->tcp_conn
->fd
);
920 pwm
->state
= ASYNC_DONE
;
924 ret
= getsockopt(pwm
->tcp_conn
->fd
, SOL_SOCKET
, SO_ERROR
, &n
, &len
);
926 *rc
= ret
? gpg_error_from_syserror() : gpg_error_from_errno(n
);
927 close(pwm
->tcp_conn
->fd
);
928 pwm
->state
= ASYNC_DONE
;
932 else if (pwm
->tcp_conn
->rc
) {
933 *rc
= pwm
->tcp_conn
->rc
;
934 close(pwm
->tcp_conn
->fd
);
935 pwm
->state
= ASYNC_DONE
;
939 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, 0);
940 setup_context(pwm
, rc
);
941 pwm
->state
= ASYNC_DONE
;
947 *rc
= GPG_ERR_INV_ARG
;
950 else if (pwm
->state
== ASYNC_DONE
)
952 else if (pwm
->state
== ASYNC_INIT
) {
953 *rc
= GPG_ERR_UNEXPECTED
;
958 FD_SET(pwm
->fd
, &fds
);
960 n
= pth_select(pwm
->fd
+1, &fds
, NULL
, NULL
, &tv
);
962 n
= select(pwm
->fd
+1, &fds
, NULL
, NULL
, &tv
);
966 if (FD_ISSET(pwm
->fd
, &fds
))
967 *rc
= parse_assuan_line(pwm
);
970 while (!*rc
&& assuan_pending_line(pwm
->ctx
))
971 *rc
= parse_assuan_line(pwm
);
973 if (pwm
->is_open_cmd
&& gpg_err_code(*rc
) == EPWMD_BADKEY
&&
974 ++pwm
->ntries
< pwm
->pinentry_tries
) {
975 pwm
->state
= ASYNC_INIT
;
976 *rc
= pwmd_open_async(pwm
, pwm
->filename
);
982 static gpg_error_t
assuan_command(pwm_t
*pwm
, assuan_context_t ctx
,
983 char **result
, const char *cmd
)
991 rc
= assuan_transact(ctx
, cmd
, mem_realloc_cb
, &data
, _inquire_cb
, pwm
,
992 pwm
->status_func
, pwm
->status_data
);
1002 mem_realloc_cb(&data
, "", 1);
1003 *result
= (char *)data
.buf
;
1007 return gpg_err_code(rc
);
1010 gpg_error_t
pwmd_inquire(pwm_t
*pwm
, const char *cmd
, pwmd_inquire_fn fn
,
1013 if (!pwm
|| !cmd
|| !fn
)
1014 return GPG_ERR_INV_ARG
;
1016 pwm
->inquire_func
= fn
;
1017 pwm
->inquire_data
= data
;
1018 return assuan_command(pwm
, pwm
->ctx
, NULL
, cmd
);
1021 gpg_error_t
pwmd_terminate_pinentry(pwm_t
*pwm
)
1023 #ifndef WITH_PINENTRY
1024 return GPG_ERR_NOT_IMPLEMENTED
;
1026 if (!pwm
|| pwm
->pid
== -1)
1027 return GPG_ERR_INV_ARG
;
1029 if (kill(pwm
->pid
, 0) == 0) {
1030 if (kill(pwm
->pid
, SIGTERM
) == -1) {
1031 if (kill(pwm
->pid
, SIGKILL
) == -1)
1032 return gpg_error_from_errno(errno
);
1035 pwm
->pin_error
= GPG_ERR_TIMEOUT
;
1038 return gpg_error_from_errno(errno
);
1044 #ifdef WITH_PINENTRY
1045 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, int which
)
1048 char tmp
[ASSUAN_LINELENGTH
];
1052 pwm
->title
= xstrdup(N_("LibPWMD"));
1055 pwm
->prompt
= xstrdup(N_("Passphrase:"));
1057 if (!pwm
->desc
&& !which
)
1058 pwm
->desc
= xstrdup(N_("Enter a passphrase."));
1061 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Invalid passphrase, please try again."));
1064 else if (which
== 2) {
1065 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Please type the passphrase again for confirmation."));
1069 buf
= (char *)xmalloc(strlen("SETERROR ") + strlen(pwm
->desc
) + 1);
1070 sprintf(buf
, "SETERROR %s", pwm
->desc
);
1073 error
= pinentry_command(pwm
, NULL
, buf
);
1079 buf
= (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm
->prompt
) + 1);
1080 sprintf(buf
, "SETPROMPT %s", pwm
->prompt
);
1081 error
= pinentry_command(pwm
, NULL
, buf
);
1087 buf
= (char *)xmalloc(strlen("SETDESC ") + strlen(pwm
->title
) + 1);
1088 sprintf(buf
, "SETDESC %s", pwm
->title
);
1089 error
= pinentry_command(pwm
, NULL
, buf
);
1094 static void update_pinentry_settings(pwm_t
*pwm
)
1098 struct passwd
*pw
= getpwuid(getuid());
1101 snprintf(buf
, sizeof(buf
), "%s/.pwmd/pinentry.conf", pw
->pw_dir
);
1103 if ((fp
= fopen(buf
, "r")) == NULL
)
1106 while ((p
= fgets(buf
, sizeof(buf
), fp
)) != NULL
) {
1107 char name
[32], val
[256];
1109 if (sscanf(p
, " %31[a-zA-Z] = %255s", name
, val
) != 2)
1112 if (strcasecmp(name
, "TTYNAME") == 0) {
1113 xfree(pwm
->pinentry_tty
);
1114 pwm
->pinentry_tty
= xstrdup(val
);
1116 else if (strcasecmp(name
, "TTYTYPE") == 0) {
1117 xfree(pwm
->pinentry_term
);
1118 pwm
->pinentry_term
= xstrdup(val
);
1120 else if (strcasecmp(name
, "DISPLAY") == 0) {
1121 xfree(pwm
->pinentry_display
);
1122 pwm
->pinentry_display
= xstrdup(val
);
1124 else if (strcasecmp(name
, "PATH") == 0) {
1125 xfree(pwm
->pinentry_path
);
1126 pwm
->pinentry_path
= xstrdup(val
);
1133 static gpg_error_t
launch_pinentry(pwm_t
*pwm
)
1136 assuan_context_t ctx
;
1137 int child_list
[] = {-1};
1138 char *display
= getenv("DISPLAY");
1139 const char *argv
[6];
1140 int have_display
= 0;
1143 update_pinentry_settings(pwm
);
1145 if (pwm
->pinentry_display
|| display
)
1148 tty
= pwm
->pinentry_tty
? pwm
->pinentry_tty
: ttyname(STDOUT_FILENO
);
1151 return gpg_error_from_errno(errno
);
1154 if (!have_display
&& !tty
)
1155 return GPG_ERR_ENOTTY
;
1157 argv
[0] = "pinentry";
1158 argv
[1] = have_display
? "--display" : "--ttyname";
1159 argv
[2] = have_display
? pwm
->pinentry_display
? pwm
->pinentry_display
: display
: tty
;
1162 if (!have_display
) {
1163 argv
[3] = "--ttytype";
1164 argv
[4] = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv("TERM");
1168 rc
= assuan_pipe_connect(&ctx
, pwm
->pinentry_path
? pwm
->pinentry_path
: PINENTRY_PATH
, argv
, child_list
);
1173 pwm
->pid
= assuan_get_pid(ctx
);
1175 return set_pinentry_strings(pwm
, 0);
1178 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1183 n
= launch_pinentry(pwm
);
1189 return assuan_command(pwm
, pwm
->pctx
, result
, cmd
);
1192 static void pinentry_disconnect(pwm_t
*pwm
)
1195 assuan_disconnect(pwm
->pctx
);
1202 * Only called from a child process.
1204 static void catchsig(int sig
)
1208 if (gelapsed
++ >= gtimeout
) {
1209 global_error
= pwmd_terminate_pinentry(gpwm
);
1212 global_error
= GPG_ERR_TIMEOUT
;
1225 * Borrowed from libassuan.
1227 static char *percent_escape(const char *atext
)
1229 const unsigned char *s
;
1230 int len
= strlen(atext
) * 3 + 1;
1231 char *buf
= (char *)xmalloc(len
), *p
= buf
;
1236 for (s
=(const unsigned char *)atext
; *s
; s
++) {
1238 sprintf (p
, "%%%02X", *s
);
1250 static gpg_error_t
send_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1253 return GPG_ERR_INV_ARG
;
1255 return assuan_command(pwm
, pwm
->ctx
, result
, cmd
);
1259 * Avoid sending the BYE command here. libassuan will close the file
1260 * descriptor and release the assuan context. Use pwmd_close() instead.
1262 gpg_error_t
pwmd_command(pwm_t
*pwm
, char **result
, const char *cmd
, ...)
1270 return GPG_ERR_INV_ARG
;
1275 * C99 allows the dst pointer to be null which will calculate the length
1276 * of the result and return it.
1278 len
= vsnprintf(NULL
, 0, cmd
, ap
);
1279 buf
= (char *)xmalloc(len
+ 1);
1280 len
= vsnprintf(buf
, len
+ 1, cmd
, ap
);
1282 error
= send_command(pwm
, result
, buf
);
1287 #ifdef WITH_PINENTRY
1288 static gpg_error_t
do_getpin(pwm_t
*pwm
, char **result
)
1291 signal(SIGALRM
, catchsig
);
1296 return pinentry_command(pwm
, result
, "GETPIN");
1299 static gpg_error_t
getpin(pwm_t
*pwm
, char **result
, int *try_n
, int which
)
1301 int pin_try
= *try_n
;
1307 if (pin_try
== -1) {
1308 error
= set_pinentry_strings(pwm
, which
);
1311 pinentry_disconnect(pwm
);
1316 if (pwm
->pinentry_tries
-1 != pin_try
) {
1317 error
= set_pinentry_strings(pwm
, 1);
1320 pinentry_disconnect(pwm
);
1326 error
= do_getpin(pwm
, result
);
1329 * Since there was input cancel any timeout setting.
1334 if (error
== GPG_ERR_CANCELED
)
1335 return GPG_ERR_CANCELED
;
1337 if (pin_try
!= -1 && pin_try
--)
1341 pinentry_disconnect(pwm
);
1351 gpg_error_t
pwmd_open_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1355 #ifndef WITH_PINENTRY
1356 return GPG_ERR_NOT_IMPLEMENTED
;
1359 if (!pwm
|| !pw
|| !pw
->filename
[0])
1360 return GPG_ERR_INV_ARG
;
1370 xfree(pwm
->filename
);
1372 pwm
->filename
= xstrdup(pw
->filename
);
1373 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1377 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1381 static gpg_error_t
do_open_command(pwm_t
*pwm
, const char *filename
, char *password
)
1383 char buf
[ASSUAN_LINELENGTH
];
1385 char *result
= NULL
;
1387 snprintf(buf
, sizeof(buf
), "OPEN %s %s", filename
, password
? password
: "");
1388 error
= send_command(pwm
, &result
, buf
);
1389 memset(buf
, 0, sizeof(buf
));
1391 if (error
&& result
)
1397 static int do_pwmd_open(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1398 int nb
, int timeout
)
1400 char *result
= NULL
;
1401 char *password
= NULL
;
1402 char path
[PATH_MAX
];
1403 #ifdef WITH_PINENTRY
1407 if (!pwm
|| !filename
|| !*filename
) {
1408 *error
= GPG_ERR_INV_ARG
;
1412 #ifdef WITH_PINENTRY
1413 pin_try
= pwm
->pinentry_tries
- 1;
1417 * Avoid calling pinentry if the password is cached on the server or if
1418 * this is a new file.
1420 *error
= pwmd_command(pwm
, &result
, "GETCONFIG data_directory");
1425 snprintf(path
, sizeof(path
), "%s/%s", result
, filename
);
1426 pwmd_free_result(result
);
1428 if (access(path
, R_OK
) == -1) {
1429 if (errno
== ENOENT
)
1433 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", filename
);
1435 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1436 if (pwm
->passfunc
) {
1437 password
= pwm
->passfunc(pwm
, pwm
->passdata
);
1441 #ifdef WITH_PINENTRY
1443 * Get the password from pinentry.
1445 if (pwm
->use_pinentry
) {
1447 * Nonblocking is wanted. fork() then return a file descriptor
1448 * that the client can use to read() from.
1453 pwmd_nb_status_t pw
;
1455 if (pipe(p
) == -1) {
1456 *error
= gpg_error_from_syserror();
1469 strncpy(pw
.filename
, filename
, sizeof(pw
.filename
));
1470 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1480 *error
= getpin(pwm
, &password
, &pin_try
, 0);
1485 pinentry_disconnect(pwm
);
1487 if (gtimeout
&& gelapsed
>= gtimeout
)
1488 *error
= GPG_ERR_TIMEOUT
;
1492 pth_write(p
[1], &pw
, sizeof(pw
));
1494 write(p
[1], &pw
, sizeof(pw
));
1501 * Don't count the time it takes to open the file
1502 * which may have many iterations.
1504 signal(SIGALRM
, SIG_DFL
);
1505 *error
= do_open_command(pwm
, filename
, password
);
1508 signal(SIGALRM
, catchsig
);
1510 if (pwm
->pctx
&& *error
== EPWMD_BADKEY
) {
1512 goto getpin_nb_again
;
1514 goto getpin_nb_fail
;
1517 pinentry_disconnect(pwm
);
1520 pth_write(p
[1], &pw
, sizeof(pw
));
1522 write(p
[1], &pw
, sizeof(pw
));
1528 *error
= gpg_error_from_syserror();
1544 * Not using pinentry and the file was not found
1547 password
= pwm
->password
;
1548 #ifdef WITH_PINENTRY
1556 *error
= do_open_command(pwm
, filename
, password
);
1559 * Keep the user defined password set with pwmd_setopt(). The password may
1560 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1562 if (password
&& password
!= pwm
->password
)
1565 #ifdef WITH_PINENTRY
1566 if (*error
== EPWMD_BADKEY
) {
1567 if (pin_try
-- > 0 && !nb
) {
1568 *error
= pwmd_command(pwm
, &result
, "OPTION TITLE=%s",
1569 N_("Invalid passphrase, please try again."));
1578 pinentry_disconnect(pwm
);
1586 xfree(pwm
->filename
);
1588 pwm
->filename
= xstrdup(filename
);
1592 * The file is cached or the file is a new file.
1595 return *error
? -1 : -2;
1597 return *error
? 1 : 0;
1600 gpg_error_t
pwmd_open(pwm_t
*pwm
, const char *filename
)
1604 do_pwmd_open(pwm
, &error
, filename
, 0, 0);
1608 int pwmd_open_nb(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1611 #ifndef WITH_PINENTRY
1612 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1615 return do_pwmd_open(pwm
, error
, filename
, 1, timeout
);
1619 #ifdef WITH_PINENTRY
1620 static gpg_error_t
do_save_getpin(pwm_t
*pwm
, char **password
)
1624 char *result
= NULL
;
1628 error
= getpin(pwm
, &result
, &pin_try
, confirm
? 2 : 0);
1632 pinentry_disconnect(pwm
);
1645 if (strcmp(*password
, result
)) {
1648 pinentry_disconnect(pwm
);
1649 error
= EPWMD_BADKEY
;
1654 pinentry_disconnect(pwm
);
1659 static gpg_error_t
do_save_command(pwm_t
*pwm
, char *password
)
1661 char buf
[ASSUAN_LINELENGTH
];
1663 char *result
= NULL
;
1665 snprintf(buf
, sizeof(buf
), "SAVE %s", password
? password
: "");
1666 error
= send_command(pwm
, &result
, buf
);
1667 memset(&buf
, 0, sizeof(buf
));
1669 if (error
&& result
)
1675 gpg_error_t
pwmd_save_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1679 #ifndef WITH_PINENTRY
1680 return GPG_ERR_NOT_IMPLEMENTED
;
1683 if (!pwm
|| !pw
|| !pw
->filename
[0])
1684 return GPG_ERR_INV_ARG
;
1688 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1692 static int do_pwmd_save(pwm_t
*pwm
, gpg_error_t
*error
, int nb
)
1694 char *result
= NULL
;
1695 char *password
= NULL
;
1698 *error
= GPG_ERR_INV_ARG
;
1702 if (pwm
->use_pinentry
|| pwm
->passfunc
) {
1703 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", pwm
->filename
);
1705 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1706 #ifdef WITH_PINENTRY
1707 if (pwm
->use_pinentry
) {
1711 pwmd_nb_status_t pw
;
1713 if (pipe(p
) == -1) {
1714 *error
= gpg_error_from_syserror();
1727 strncpy(pw
.filename
, pwm
->filename
, sizeof(pw
.filename
));
1728 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1733 *error
= do_save_getpin(pwm
, &password
);
1734 } while (*error
== EPWMD_BADKEY
);
1738 pinentry_disconnect(pwm
);
1742 pth_write(p
[1], &pw
, sizeof(pw
));
1744 write(p
[1], &pw
, sizeof(pw
));
1750 *error
= do_save_command(pwm
, password
);
1751 pinentry_disconnect(pwm
);
1754 pth_write(p
[1], &pw
, sizeof(pw
));
1756 write(p
[1], &pw
, sizeof(pw
));
1762 *error
= gpg_error_from_syserror();
1775 *error
= do_save_getpin(pwm
, &password
);
1783 password
= (*pwm
->passfunc
)(pwm
, pwm
->passdata
);
1784 #ifdef WITH_PINENTRY
1794 password
= pwm
->password
;
1796 *error
= do_save_command(pwm
, password
);
1798 if (password
&& password
!= pwm
->password
)
1802 return *error
? -1 : -2;
1804 return *error
? 1 : 0;
1807 int pwmd_save_nb(pwm_t
*pwm
, gpg_error_t
*error
)
1809 #ifndef WITH_PINENTRY
1810 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1813 return do_pwmd_save(pwm
, error
, 1);
1817 gpg_error_t
pwmd_save(pwm_t
*pwm
)
1821 do_pwmd_save(pwm
, &error
, 0);
1825 gpg_error_t
pwmd_setopt(pwm_t
*pwm
, pwmd_option_t opt
, ...)
1828 #ifdef WITH_PINENTRY
1829 int n
= va_arg(ap
, int);
1833 gpg_error_t error
= 0;
1836 return GPG_ERR_INV_ARG
;
1841 case PWMD_OPTION_STATUS_FUNC
:
1842 pwm
->status_func
= va_arg(ap
, pwmd_status_fn
);
1844 case PWMD_OPTION_STATUS_DATA
:
1845 pwm
->status_data
= va_arg(ap
, void *);
1847 case PWMD_OPTION_PASSWORD_FUNC
:
1848 pwm
->passfunc
= va_arg(ap
, pwmd_password_fn
);
1850 case PWMD_OPTION_PASSWORD_DATA
:
1851 pwm
->passdata
= va_arg(ap
, void *);
1853 case PWMD_OPTION_PASSWORD
:
1854 arg1
= va_arg(ap
, char *);
1857 xfree(pwm
->password
);
1859 pwm
->password
= xstrdup(arg1
);
1861 #ifdef WITH_PINENTRY
1862 case PWMD_OPTION_PINENTRY
:
1863 n
= va_arg(ap
, int);
1865 if (n
!= 0 && n
!= 1) {
1867 error
= GPG_ERR_INV_VALUE
;
1870 pwm
->use_pinentry
= n
;
1871 error
= pwmd_command(pwm
, &result
, "OPTION PINENTRY=%i",
1872 !pwm
->use_pinentry
);
1875 case PWMD_OPTION_PINENTRY_TRIES
:
1876 n
= va_arg(ap
, int);
1880 error
= GPG_ERR_INV_VALUE
;
1883 pwm
->pinentry_tries
= n
;
1885 case PWMD_OPTION_PINENTRY_PATH
:
1886 if (pwm
->pinentry_path
)
1887 xfree(pwm
->pinentry_path
);
1889 pwm
->pinentry_path
= xstrdup(va_arg(ap
, char *));
1891 case PWMD_OPTION_PINENTRY_TTY
:
1892 if (pwm
->pinentry_tty
)
1893 xfree(pwm
->pinentry_tty
);
1895 pwm
->pinentry_tty
= xstrdup(va_arg(ap
, char *));
1897 case PWMD_OPTION_PINENTRY_DISPLAY
:
1898 if (pwm
->pinentry_display
)
1899 xfree(pwm
->pinentry_display
);
1901 pwm
->pinentry_display
= xstrdup(va_arg(ap
, char *));
1903 case PWMD_OPTION_PINENTRY_TERM
:
1904 if (pwm
->pinentry_term
)
1905 xfree(pwm
->pinentry_term
);
1907 pwm
->pinentry_term
= xstrdup(va_arg(ap
, char *));
1909 case PWMD_OPTION_PINENTRY_TITLE
:
1912 pwm
->title
= percent_escape(va_arg(ap
, char *));
1914 case PWMD_OPTION_PINENTRY_PROMPT
:
1917 pwm
->prompt
= percent_escape(va_arg(ap
, char *));
1919 case PWMD_OPTION_PINENTRY_DESC
:
1922 pwm
->desc
= percent_escape(va_arg(ap
, char *));
1925 case PWMD_OPTION_PINENTRY
:
1926 case PWMD_OPTION_PINENTRY_TRIES
:
1927 case PWMD_OPTION_PINENTRY_PATH
:
1928 case PWMD_OPTION_PINENTRY_TTY
:
1929 case PWMD_OPTION_PINENTRY_DISPLAY
:
1930 case PWMD_OPTION_PINENTRY_TERM
:
1931 case PWMD_OPTION_PINENTRY_TITLE
:
1932 case PWMD_OPTION_PINENTRY_PROMPT
:
1933 case PWMD_OPTION_PINENTRY_DESC
:
1934 error
= GPG_ERR_NOT_IMPLEMENTED
;
1938 error
= GPG_ERR_NOT_IMPLEMENTED
;
1946 gpg_error_t
pwmd_assuan_ctx(pwm_t
*pwm
, assuan_context_t
*ctx
, int *fd
)
1949 return GPG_ERR_INV_ARG
;