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
59 GCRY_THREAD_OPTION_PTH_IMPL
;
72 #define N_(msgid) dgettext("libpwmd", msgid)
78 #define xrealloc realloc
79 #define xmalloc malloc
80 #define xstrdup strdup
81 #define xcalloc calloc
88 static int gelapsed
, gtimeout
;
89 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
);
90 static gpg_error_t global_error
;
93 const char *pwmd_strerror(gpg_error_t e
)
95 gpg_err_code_t code
= gpg_err_code(e
);
97 if (code
>= GPG_ERR_USER_1
&& code
< gpg_err_code(EPWMD_MAX
)) {
101 return N_("Unknown error");
103 return N_("No cache slots available");
105 return N_("Recursion loop");
107 return N_("No file is open");
109 return N_("General LibXML error");
111 return N_("File modified");
113 return N_("Access denied");
117 return gpg_strerror(e
);
120 gpg_error_t
pwmd_init()
122 static int initialized
;
127 #ifdef HAVE_LIBGNUTLS
129 gcry_control (GCRYCTL_SET_THREAD_CBS
, &gcry_threads_pth
);
131 gnutls_global_set_mem_functions(xmalloc
, xmalloc
, NULL
, xrealloc
, xfree
);
132 gnutls_global_init ();
137 bindtextdomain("libpwmd", LOCALEDIR
);
140 assuan_set_malloc_hooks(xmalloc
, xrealloc
, xfree
);
141 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT
);
146 static pwm_t
*_socket_connect_finalize(pwm_t
*pwm
, assuan_context_t ctx
)
149 int n
= assuan_get_active_fds(ctx
, 0, active
, sizeof(active
));
151 pwm
->fd
= n
<= 0 ? -1 : dup(active
[0]);
155 pwm
->pinentry_tries
= 3;
157 assuan_set_pointer(ctx
, pwm
);
161 #ifdef HAVE_LIBGNUTLS
162 static int read_hook(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
163 size_t len
, ssize_t
*ret
)
165 pwm_t
*pwm
= assuan_get_pointer(ctx
);
167 if (!pwm
|| !pwm
->session
)
168 *ret
= read((int)fd
, data
, len
);
171 *ret
= gnutls_record_recv(pwm
->session
, data
, len
);
173 if (*ret
== GNUTLS_E_REHANDSHAKE
) {
174 *ret
= gnutls_rehandshake(pwm
->session
);
176 if (*ret
== GNUTLS_E_WARNING_ALERT_RECEIVED
||
177 *ret
== GNUTLS_A_NO_RENEGOTIATION
) {
182 if (*ret
!= GNUTLS_E_SUCCESS
) {
188 *ret
= gnutls_handshake(pwm
->session
);
190 if (*ret
!= GNUTLS_E_SUCCESS
) {
198 } while (*ret
== GNUTLS_E_INTERRUPTED
|| *ret
== GNUTLS_E_AGAIN
);
201 return *ret
<= 0 ? 0 : 1;
204 static int write_hook(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
205 size_t len
, ssize_t
*ret
)
207 pwm_t
*pwm
= assuan_get_pointer(ctx
);
209 if (!pwm
|| !pwm
->session
)
210 *ret
= write((int)fd
, data
, len
);
213 *ret
= gnutls_record_send(pwm
->session
, data
, len
);
214 } while (*ret
== GNUTLS_E_INTERRUPTED
|| *ret
== GNUTLS_E_AGAIN
);
217 return *ret
<= 0 ? 0 : 1;
220 static void _tls_deinit(pwm_t
*pwm
)
223 gnutls_bye(pwm
->session
, GNUTLS_SHUT_WR
);
224 gnutls_deinit(pwm
->session
);
228 gnutls_certificate_free_credentials(pwm
->x509
);
234 static gpg_error_t
verify_certificate(pwm_t
*pwm
)
237 const gnutls_datum_t
*cert_list
;
238 unsigned int cert_list_size
;
240 gnutls_x509_crt_t cert
;
243 rc
= gnutls_certificate_verify_peers2(pwm
->session
, &status
);
247 return GPG_ERR_BAD_CERT
;
250 if (status
& GNUTLS_CERT_INVALID
)
251 return GPG_ERR_BAD_CERT
;
253 if (status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
254 return GPG_ERR_BAD_SIGNATURE
;
256 if (status
& GNUTLS_CERT_REVOKED
)
257 return GPG_ERR_CERT_REVOKED
;
259 if (gnutls_certificate_type_get(pwm
->session
) != GNUTLS_CRT_X509
)
260 return GPG_ERR_UNSUPPORTED_CERT
;
262 if (gnutls_x509_crt_init(&cert
) < 0)
263 return gpg_error_from_errno(ENOMEM
);
265 cert_list
= gnutls_certificate_get_peers(pwm
->session
, &cert_list_size
);
268 rc
= GPG_ERR_MISSING_CERT
;
272 for (i
= 0; i
< cert_list_size
; i
++) {
273 if (gnutls_x509_crt_import(cert
, &cert_list
[i
], GNUTLS_X509_FMT_DER
) < 0) {
274 rc
= GPG_ERR_BAD_CERT_CHAIN
;
278 if (gnutls_x509_crt_get_expiration_time(cert
) < time(0)) {
279 rc
= GPG_ERR_CERT_EXPIRED
;
283 if (gnutls_x509_crt_get_activation_time(cert
) > time(0)) {
284 rc
= GPG_ERR_CERT_TOO_YOUNG
;
288 if (pwm
->tcp_conn
->verify
) {
289 if (!gnutls_x509_crt_check_hostname(cert
, pwm
->tcp_conn
->host
)) {
290 rc
= GPG_ERR_BAD_CERT_CHAIN
;
297 gnutls_x509_crt_deinit(cert
);
301 static gpg_error_t
_tls_init(pwm_t
*pwm
)
306 rc
= gnutls_certificate_allocate_credentials(&pwm
->x509
);
309 return gpg_error_from_errno(ENOMEM
);
311 /* The client certificate must be signed by the CA of the pwmd server
312 * certificate in order for the client to authenticate successfully. Man in
313 * the middle attacks are still possible if the attacker is running a pwmd
314 * that doesn't require client certificate authentication. So require the
315 * client to verify the server certificate.
317 rc
= gnutls_certificate_set_x509_trust_file (pwm
->x509
, pwm
->tcp_conn
->ca
,
318 GNUTLS_X509_FMT_PEM
);
322 rc
= GPG_ERR_INV_CERT_OBJ
;
326 rc
= gnutls_certificate_set_x509_key_file(pwm
->x509
, pwm
->tcp_conn
->cert
,
327 pwm
->tcp_conn
->key
, GNUTLS_X509_FMT_PEM
);
329 if (rc
!= GNUTLS_E_SUCCESS
) {
331 rc
= GPG_ERR_INV_CERT_OBJ
;
335 rc
= gnutls_init(&pwm
->session
, GNUTLS_CLIENT
);
337 if (rc
!= GNUTLS_E_SUCCESS
) {
339 rc
= GPG_ERR_INV_CERT_OBJ
;
343 rc
= gnutls_priority_set_direct(pwm
->session
,
344 pwm
->tcp_conn
->priority
? pwm
->tcp_conn
->priority
: "SECURE256",
347 if (rc
!= GNUTLS_E_SUCCESS
) {
349 rc
= GPG_ERR_INV_CERT_OBJ
;
353 rc
= gnutls_credentials_set(pwm
->session
, GNUTLS_CRD_CERTIFICATE
, pwm
->x509
);
355 if (rc
!= GNUTLS_E_SUCCESS
) {
357 rc
= GPG_ERR_INV_CERT_OBJ
;
361 gnutls_transport_set_ptr(pwm
->session
,
362 (gnutls_transport_ptr_t
)pwm
->tcp_conn
->fd
);
363 rc
= gnutls_handshake(pwm
->session
);
365 if (rc
!= GNUTLS_E_SUCCESS
) {
367 rc
= GPG_ERR_INV_CERT_OBJ
;
372 rc
= verify_certificate(pwm
);
381 static void _tls_assuan_deinit(assuan_context_t ctx
)
383 pwm_t
*pwm
= assuan_get_pointer(ctx
);
388 static void free_tcp_conn(pwmd_tcp_conn_t
*conn
)
407 xfree(conn
->priority
);
413 * Sets common options from both pwmd_tcp_connect() and
414 * pwmd_tcp_connect_async().
416 static gpg_error_t
init_tcp_conn(pwmd_tcp_conn_t
**dst
, char *host
, int port
,
417 const char *cert
, const char *key
, const char *ca
, int verify
,
418 const char *priority
)
421 struct passwd
*pw
= getpwuid(getuid());
422 pwmd_tcp_conn_t
*conn
;
426 return GPG_ERR_INV_ARG
;
428 conn
= xcalloc(1, sizeof(pwmd_tcp_conn_t
));
430 return gpg_error_from_errno(ENOMEM
);
432 conn
->port
= port
== -1 ? 6466 : port
;
433 conn
->host
= xstrdup(host
);
436 rc
= gpg_error_from_errno(ENOMEM
);
441 snprintf(buf
, sizeof(buf
), "%s/.pwmd/client-cert.pem", pw
->pw_dir
);
442 conn
->cert
= xstrdup(buf
);
445 conn
->cert
= xstrdup(cert
);
448 rc
= gpg_error_from_errno(ENOMEM
);
453 snprintf(buf
, sizeof(buf
), "%s/.pwmd/client-key.pem", pw
->pw_dir
);
454 conn
->key
= xstrdup(buf
);
457 conn
->key
= xstrdup(key
);
460 rc
= gpg_error_from_errno(ENOMEM
);
465 snprintf(buf
, sizeof(buf
), "%s/.pwmd/ca-cert.pem", pw
->pw_dir
);
466 conn
->ca
= xstrdup(buf
);
469 conn
->ca
= xstrdup(ca
);
472 rc
= gpg_error_from_errno(ENOMEM
);
476 conn
->fd
= socket(PF_INET
, SOCK_STREAM
, 0);
478 if (conn
->fd
== -1) {
479 rc
= gpg_error_from_syserror();
483 conn
->verify
= verify
;
484 conn
->priority
= xstrdup(priority
);
486 if (!conn
->priority
) {
487 rc
= gpg_error_from_errno(ENOMEM
);
499 static gpg_error_t
do_connect(pwm_t
*pwm
, void *addr
)
501 struct sockaddr_in their_addr
;
503 pwm
->cmd
= ASYNC_CMD_CONNECT
;
504 their_addr
.sin_family
= AF_INET
;
505 their_addr
.sin_port
= htons(pwm
->tcp_conn
->port
);
506 their_addr
.sin_addr
= *((struct in_addr
*)addr
);
507 memset(their_addr
.sin_zero
, '\0', sizeof their_addr
.sin_zero
);
509 if (connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
510 sizeof(their_addr
)) == -1)
511 return gpg_error_from_syserror();
516 static void dns_resolve_cb(int result
, char type
, int count
, int ttl
,
517 void *addrs
, void *arg
)
521 if (result
!= DNS_ERR_NONE
) {
523 case DNS_ERR_NOTEXIST
:
524 pwm
->tcp_conn
->rc
= GPG_ERR_UNKNOWN_HOST
;
526 case DNS_ERR_SERVERFAILED
:
527 pwm
->tcp_conn
->rc
= GPG_ERR_EHOSTDOWN
;
529 case DNS_ERR_TIMEOUT
:
530 pwm
->tcp_conn
->rc
= GPG_ERR_TIMEOUT
;
534 pwm
->tcp_conn
->rc
= GPG_ERR_EHOSTUNREACH
;
541 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, O_NONBLOCK
);
542 pwm
->tcp_conn
->rc
= do_connect(pwm
, &addrs
[0]);
545 pwm_t
*pwmd_tcp_connect_async(char *host
, int port
, gpg_error_t
*rc
,
546 const char *cert
, const char *key
, const char *ca
, int verify
,
547 const char *priority
)
549 #ifndef HAVE_LIBGNUTLS
550 *rc
= GPG_ERR_NOT_IMPLEMENTED
;
553 pwmd_tcp_conn_t
*conn
;
556 *rc
= init_tcp_conn(&conn
, host
, port
, cert
, key
, ca
, verify
, priority
);
561 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
562 *rc
= gpg_error_from_syserror();
567 pwm
->tcp_conn
= conn
;
568 pwm
->cmd
= ASYNC_CMD_DNS
;
569 pwm
->state
= ASYNC_PROCESS
;
570 evdns_resolv_conf_parse(DNS_OPTIONS_ALL
, "/etc/resolv.conf");
571 evdns_resolve_ipv4(pwm
->tcp_conn
->host
, 0, dns_resolve_cb
, pwm
);
576 static pwm_t
*setup_context(pwm_t
*pwm
, gpg_error_t
*rc
)
578 assuan_context_t ctx
;
579 struct assuan_io_hooks io_hooks
= {read_hook
, write_hook
};
580 int fd
= pwm
->tcp_conn
->fd
;
582 *rc
= _tls_init(pwm
);
583 free_tcp_conn(pwm
->tcp_conn
);
584 pwm
->tcp_conn
= NULL
;
589 assuan_set_io_hooks(&io_hooks
);
590 *rc
= assuan_socket_connect_fd(&ctx
, fd
, 0, pwm
);
595 assuan_set_finish_handler(ctx
, _tls_assuan_deinit
);
596 return _socket_connect_finalize(pwm
, ctx
);
604 pwm_t
*pwmd_tcp_connect(char *host
, int port
, gpg_error_t
*rc
, const char *cert
,
605 const char *key
, const char *ca
, int verify
, const char *priority
)
609 pwmd_tcp_conn_t
*conn
;
611 *rc
= init_tcp_conn(&conn
, host
, port
, cert
, key
, ca
, verify
, priority
);
616 if ((he
= gethostbyname(conn
->host
)) == NULL
) {
618 *rc
= gpg_error_from_syserror();
623 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
624 *rc
= gpg_error_from_syserror();
628 pwm
->tcp_conn
= conn
;
629 *rc
= do_connect(pwm
, he
->h_addr
);
634 return setup_context(pwm
, rc
);
647 pwm_t
*pwmd_connect(const char *path
, gpg_error_t
*rc
)
650 char *socketpath
= NULL
;
652 assuan_context_t ctx
;
655 pw
= getpwuid(getuid());
656 socketpath
= (char *)xmalloc(strlen(pw
->pw_dir
) + strlen("/.pwmd/socket") + 1);
657 sprintf(socketpath
, "%s/.pwmd/socket", pw
->pw_dir
);
660 socketpath
= xstrdup(path
);
662 *rc
= assuan_socket_connect_ext(&ctx
, socketpath
, -1, 0);
668 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
669 *rc
= gpg_error_from_syserror();
673 return _socket_connect_finalize(pwm
, ctx
);
676 gpg_error_t
pwmd_pending_line(pwm_t
*pwm
, char **line
, size_t *len
)
679 return GPG_ERR_INV_ARG
;
681 if (assuan_pending_line(pwm
->ctx
))
682 return assuan_read_line(pwm
->ctx
, line
, len
);
684 return GPG_ERR_NO_DATA
;
687 void pwmd_close(pwm_t
*pwm
)
693 assuan_disconnect(pwm
->ctx
);
696 xfree(pwm
->password
);
707 if (pwm
->pinentry_tty
)
708 xfree(pwm
->pinentry_tty
);
710 if (pwm
->pinentry_display
)
711 xfree(pwm
->pinentry_display
);
713 if (pwm
->pinentry_term
)
714 xfree(pwm
->pinentry_term
);
717 xfree(pwm
->filename
);
722 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
724 membuf_t
*mem
= (membuf_t
*)data
;
730 if ((p
= xrealloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
734 memcpy((char *)mem
->buf
+ mem
->len
, buffer
, len
);
739 void pwmd_free_result(void *data
)
744 static int _inquire_cb(void *data
, const char *keyword
)
746 pwm_t
*pwm
= (pwm_t
*)data
;
748 int flags
= fcntl(pwm
->fd
, F_GETFL
);
750 /* Shouldn't get this far without a callback. */
751 if (!pwm
->inquire_func
)
752 return GPG_ERR_INV_ARG
;
755 * Since the socket file descriptor is probably set to non-blocking, set to
756 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixes when
757 * asynchronous INQUIRE is supported by either libassuan or a later
760 fcntl(pwm
->fd
, F_SETFL
, 0);
767 rc
= pwm
->inquire_func(pwm
->inquire_data
, keyword
, rc
, &result
, &len
);
768 rc
= gpg_err_code(rc
);
770 if (rc
== GPG_ERR_EOF
|| !rc
) {
771 if (len
<= 0 || !result
|| !*result
) {
776 arc
= assuan_send_data(pwm
->ctx
, result
, len
);
778 if (rc
== GPG_ERR_EOF
) {
789 fcntl(pwm
->fd
, F_SETFL
, flags
);
793 gpg_error_t
pwmd_finalize(pwm_t
*pwm
)
795 if (!pwm
|| pwm
->cmd
== ASYNC_CMD_NONE
|| pwm
->state
!= ASYNC_DONE
)
796 return GPG_ERR_INV_ARG
;
798 pwm
->state
= ASYNC_INIT
;
799 pwm
->cmd
= ASYNC_CMD_NONE
;
801 #ifdef HAVE_LIBGNUTLS
802 if (pwm
->cmd
== ASYNC_CMD_CONNECT
|| pwm
->cmd
== ASYNC_CMD_DNS
) {
803 /* pwm is no longer a valid handle. */
804 if (pwm
->tcp_conn
->rc
) {
805 free_tcp_conn(pwm
->tcp_conn
);
809 free_tcp_conn(pwm
->tcp_conn
);
810 pwm
->tcp_conn
= NULL
;
816 return GPG_ERR_INV_ARG
;
818 pwm
->state
= ASYNC_INIT
;
821 pwm
->is_open_cmd
= 0;
826 static gpg_error_t
do_nb_command(pwm_t
*pwm
, const char *cmd
, const char *arg
)
830 size_t len
= strlen(cmd
) + 2;
832 len
+= arg
? strlen(arg
) : 0;
834 if (pwm
->state
!= ASYNC_INIT
)
835 return GPG_ERR_UNEXPECTED
;
837 buf
= (char *)xmalloc(len
);
840 rc
= gpg_error_from_errno(ENOMEM
);
844 snprintf(buf
, len
, "%s %s", cmd
, arg
? arg
: "");
845 rc
= assuan_write_line(pwm
->ctx
, buf
);
849 pwm
->state
= ASYNC_PROCESS
;
855 gpg_error_t
pwmd_open_async(pwm_t
*pwm
, const char *filename
)
857 if (!pwm
|| !filename
)
858 return GPG_ERR_INV_ARG
;
860 /* For pinentry retries. */
861 if (!pwm
->is_open_cmd
) {
863 xfree(pwm
->filename
);
865 pwm
->filename
= xstrdup(filename
);
868 pwm
->is_open_cmd
= 1;
869 return do_nb_command(pwm
, "OPEN", filename
);
872 gpg_error_t
pwmd_save_async(pwm_t
*pwm
)
875 return GPG_ERR_INV_ARG
;
877 return do_nb_command(pwm
, "SAVE", NULL
);
880 static gpg_error_t
parse_assuan_line(pwm_t
*pwm
)
886 rc
= assuan_read_line(pwm
->ctx
, &line
, &len
);
889 if (line
[0] == 'O' && line
[1] == 'K' &&
890 (line
[2] == 0 || line
[2] == ' ')) {
891 pwm
->state
= ASYNC_DONE
;
893 else if (line
[0] == '#') {
895 else if (line
[0] == 'S' && (line
[1] == 0 || line
[1] == ' ')) {
896 if (pwm
->status_func
) {
897 pwm
->status_func(pwm
->status_data
,
898 line
[1] == 0 ? line
+1 : line
+2);
901 else if (line
[0] == 'E' && line
[1] == 'R' && line
[2] == 'R' &&
902 (line
[3] == 0 || line
[3] == ' ')) {
905 pwm
->state
= ASYNC_DONE
;
912 pwmd_async_t
pwmd_process(pwm_t
*pwm
, gpg_error_t
*rc
)
916 struct timeval tv
= {0, 0};
921 *rc
= GPG_ERR_INV_ARG
;
925 #ifdef HAVE_LIBGNUTLS
926 if (pwm
->cmd
== ASYNC_CMD_DNS
) {
927 if (pwm
->tcp_conn
->rc
) {
928 *rc
= pwm
->tcp_conn
->rc
;
929 close(pwm
->tcp_conn
->fd
);
930 pwm
->state
= ASYNC_DONE
;
933 event_loop(EVLOOP_NONBLOCK
);
936 else if (pwm
->cmd
== ASYNC_CMD_CONNECT
) {
937 if (pwm
->tcp_conn
->rc
== GPG_ERR_EINPROGRESS
) {
939 socklen_t len
= sizeof(int);
942 FD_SET(pwm
->tcp_conn
->fd
, &fds
);
943 n
= select(pwm
->tcp_conn
->fd
+1, NULL
, &fds
, NULL
, &tv
);
945 if (!n
|| !FD_ISSET(pwm
->tcp_conn
->fd
, &fds
))
948 *rc
= gpg_error_from_syserror();
949 close(pwm
->tcp_conn
->fd
);
950 pwm
->state
= ASYNC_DONE
;
954 ret
= getsockopt(pwm
->tcp_conn
->fd
, SOL_SOCKET
, SO_ERROR
, &n
, &len
);
956 *rc
= ret
? gpg_error_from_syserror() : gpg_error_from_errno(n
);
957 close(pwm
->tcp_conn
->fd
);
958 pwm
->state
= ASYNC_DONE
;
962 else if (pwm
->tcp_conn
->rc
) {
963 *rc
= pwm
->tcp_conn
->rc
;
964 close(pwm
->tcp_conn
->fd
);
965 pwm
->state
= ASYNC_DONE
;
969 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, 0);
970 setup_context(pwm
, rc
);
971 pwm
->state
= ASYNC_DONE
;
977 *rc
= GPG_ERR_INV_ARG
;
980 else if (pwm
->state
== ASYNC_DONE
)
982 else if (pwm
->state
== ASYNC_INIT
) {
983 *rc
= GPG_ERR_UNEXPECTED
;
988 FD_SET(pwm
->fd
, &fds
);
990 n
= pth_select(pwm
->fd
+1, &fds
, NULL
, NULL
, &tv
);
992 n
= select(pwm
->fd
+1, &fds
, NULL
, NULL
, &tv
);
996 if (FD_ISSET(pwm
->fd
, &fds
))
997 *rc
= parse_assuan_line(pwm
);
1000 while (!*rc
&& assuan_pending_line(pwm
->ctx
))
1001 *rc
= parse_assuan_line(pwm
);
1003 if (pwm
->is_open_cmd
&& gpg_err_code(*rc
) == EPWMD_BADKEY
&&
1004 ++pwm
->ntries
< pwm
->pinentry_tries
) {
1005 pwm
->state
= ASYNC_INIT
;
1006 *rc
= pwmd_open_async(pwm
, pwm
->filename
);
1012 static gpg_error_t
assuan_command(pwm_t
*pwm
, assuan_context_t ctx
,
1013 char **result
, const char *cmd
)
1021 rc
= assuan_transact(ctx
, cmd
, mem_realloc_cb
, &data
, _inquire_cb
, pwm
,
1022 pwm
->status_func
, pwm
->status_data
);
1032 mem_realloc_cb(&data
, "", 1);
1033 *result
= (char *)data
.buf
;
1037 return gpg_err_code(rc
);
1040 gpg_error_t
pwmd_inquire(pwm_t
*pwm
, const char *cmd
, pwmd_inquire_fn fn
,
1043 if (!pwm
|| !cmd
|| !fn
)
1044 return GPG_ERR_INV_ARG
;
1046 pwm
->inquire_func
= fn
;
1047 pwm
->inquire_data
= data
;
1048 return assuan_command(pwm
, pwm
->ctx
, NULL
, cmd
);
1051 gpg_error_t
pwmd_terminate_pinentry(pwm_t
*pwm
)
1053 #ifndef WITH_PINENTRY
1054 return GPG_ERR_NOT_IMPLEMENTED
;
1056 if (!pwm
|| pwm
->pid
== -1)
1057 return GPG_ERR_INV_ARG
;
1059 if (kill(pwm
->pid
, 0) == 0) {
1060 if (kill(pwm
->pid
, SIGTERM
) == -1) {
1061 if (kill(pwm
->pid
, SIGKILL
) == -1)
1062 return gpg_error_from_errno(errno
);
1065 pwm
->pin_error
= GPG_ERR_TIMEOUT
;
1068 return gpg_error_from_errno(errno
);
1074 #ifdef WITH_PINENTRY
1075 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, int which
)
1078 char tmp
[ASSUAN_LINELENGTH
];
1082 pwm
->title
= xstrdup(N_("LibPWMD"));
1085 pwm
->prompt
= xstrdup(N_("Passphrase:"));
1087 if (!pwm
->desc
&& !which
)
1088 pwm
->desc
= xstrdup(N_("Enter a passphrase."));
1091 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Invalid passphrase, please try again."));
1094 else if (which
== 2) {
1095 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Please type the passphrase again for confirmation."));
1099 buf
= (char *)xmalloc(strlen("SETERROR ") + strlen(pwm
->desc
) + 1);
1100 sprintf(buf
, "SETERROR %s", pwm
->desc
);
1103 error
= pinentry_command(pwm
, NULL
, buf
);
1109 buf
= (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm
->prompt
) + 1);
1110 sprintf(buf
, "SETPROMPT %s", pwm
->prompt
);
1111 error
= pinentry_command(pwm
, NULL
, buf
);
1117 buf
= (char *)xmalloc(strlen("SETDESC ") + strlen(pwm
->title
) + 1);
1118 sprintf(buf
, "SETDESC %s", pwm
->title
);
1119 error
= pinentry_command(pwm
, NULL
, buf
);
1124 static void update_pinentry_settings(pwm_t
*pwm
)
1128 struct passwd
*pw
= getpwuid(getuid());
1131 snprintf(buf
, sizeof(buf
), "%s/.pwmd/pinentry.conf", pw
->pw_dir
);
1133 if ((fp
= fopen(buf
, "r")) == NULL
)
1136 while ((p
= fgets(buf
, sizeof(buf
), fp
)) != NULL
) {
1137 char name
[32], val
[256];
1139 if (sscanf(p
, " %31[a-zA-Z] = %255s", name
, val
) != 2)
1142 if (strcasecmp(name
, "TTYNAME") == 0) {
1143 xfree(pwm
->pinentry_tty
);
1144 pwm
->pinentry_tty
= xstrdup(val
);
1146 else if (strcasecmp(name
, "TTYTYPE") == 0) {
1147 xfree(pwm
->pinentry_term
);
1148 pwm
->pinentry_term
= xstrdup(val
);
1150 else if (strcasecmp(name
, "DISPLAY") == 0) {
1151 xfree(pwm
->pinentry_display
);
1152 pwm
->pinentry_display
= xstrdup(val
);
1154 else if (strcasecmp(name
, "PATH") == 0) {
1155 xfree(pwm
->pinentry_path
);
1156 pwm
->pinentry_path
= xstrdup(val
);
1163 static gpg_error_t
launch_pinentry(pwm_t
*pwm
)
1166 assuan_context_t ctx
;
1167 int child_list
[] = {-1};
1168 char *display
= getenv("DISPLAY");
1169 const char *argv
[6];
1170 int have_display
= 0;
1173 update_pinentry_settings(pwm
);
1175 if (pwm
->pinentry_display
|| display
)
1178 tty
= pwm
->pinentry_tty
? pwm
->pinentry_tty
: ttyname(STDOUT_FILENO
);
1181 return gpg_error_from_errno(errno
);
1184 if (!have_display
&& !tty
)
1185 return GPG_ERR_ENOTTY
;
1187 argv
[0] = "pinentry";
1188 argv
[1] = have_display
? "--display" : "--ttyname";
1189 argv
[2] = have_display
? pwm
->pinentry_display
? pwm
->pinentry_display
: display
: tty
;
1192 if (!have_display
) {
1193 argv
[3] = "--ttytype";
1194 argv
[4] = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv("TERM");
1198 rc
= assuan_pipe_connect(&ctx
, pwm
->pinentry_path
? pwm
->pinentry_path
: PINENTRY_PATH
, argv
, child_list
);
1203 pwm
->pid
= assuan_get_pid(ctx
);
1205 return set_pinentry_strings(pwm
, 0);
1208 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1213 n
= launch_pinentry(pwm
);
1219 return assuan_command(pwm
, pwm
->pctx
, result
, cmd
);
1222 static void pinentry_disconnect(pwm_t
*pwm
)
1225 assuan_disconnect(pwm
->pctx
);
1232 * Only called from a child process.
1234 static void catchsig(int sig
)
1238 if (gelapsed
++ >= gtimeout
) {
1239 global_error
= pwmd_terminate_pinentry(gpwm
);
1242 global_error
= GPG_ERR_TIMEOUT
;
1255 * Borrowed from libassuan.
1257 static char *percent_escape(const char *atext
)
1259 const unsigned char *s
;
1260 int len
= strlen(atext
) * 3 + 1;
1261 char *buf
= (char *)xmalloc(len
), *p
= buf
;
1266 for (s
=(const unsigned char *)atext
; *s
; s
++) {
1268 sprintf (p
, "%%%02X", *s
);
1280 static gpg_error_t
send_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1283 return GPG_ERR_INV_ARG
;
1285 return assuan_command(pwm
, pwm
->ctx
, result
, cmd
);
1289 * Avoid sending the BYE command here. libassuan will close the file
1290 * descriptor and release the assuan context. Use pwmd_close() instead.
1292 gpg_error_t
pwmd_command(pwm_t
*pwm
, char **result
, const char *cmd
, ...)
1300 return GPG_ERR_INV_ARG
;
1305 * C99 allows the dst pointer to be null which will calculate the length
1306 * of the result and return it.
1308 len
= vsnprintf(NULL
, 0, cmd
, ap
);
1309 buf
= (char *)xmalloc(len
+ 1);
1310 len
= vsnprintf(buf
, len
+ 1, cmd
, ap
);
1312 error
= send_command(pwm
, result
, buf
);
1317 #ifdef WITH_PINENTRY
1318 static gpg_error_t
do_getpin(pwm_t
*pwm
, char **result
)
1321 signal(SIGALRM
, catchsig
);
1326 return pinentry_command(pwm
, result
, "GETPIN");
1329 static gpg_error_t
getpin(pwm_t
*pwm
, char **result
, int *try_n
, int which
)
1331 int pin_try
= *try_n
;
1337 if (pin_try
== -1) {
1338 error
= set_pinentry_strings(pwm
, which
);
1341 pinentry_disconnect(pwm
);
1346 if (pwm
->pinentry_tries
-1 != pin_try
) {
1347 error
= set_pinentry_strings(pwm
, 1);
1350 pinentry_disconnect(pwm
);
1356 error
= do_getpin(pwm
, result
);
1359 * Since there was input cancel any timeout setting.
1364 if (error
== GPG_ERR_CANCELED
)
1365 return GPG_ERR_CANCELED
;
1367 if (pin_try
!= -1 && pin_try
--)
1371 pinentry_disconnect(pwm
);
1381 gpg_error_t
pwmd_open_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1385 #ifndef WITH_PINENTRY
1386 return GPG_ERR_NOT_IMPLEMENTED
;
1389 if (!pwm
|| !pw
|| !pw
->filename
[0])
1390 return GPG_ERR_INV_ARG
;
1400 xfree(pwm
->filename
);
1402 pwm
->filename
= xstrdup(pw
->filename
);
1403 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1407 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1411 static gpg_error_t
do_open_command(pwm_t
*pwm
, const char *filename
, char *password
)
1413 char buf
[ASSUAN_LINELENGTH
];
1415 char *result
= NULL
;
1417 snprintf(buf
, sizeof(buf
), "OPEN %s %s", filename
, password
? password
: "");
1418 error
= send_command(pwm
, &result
, buf
);
1419 memset(buf
, 0, sizeof(buf
));
1421 if (error
&& result
)
1427 static int do_pwmd_open(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1428 int nb
, int timeout
)
1430 char *result
= NULL
;
1431 char *password
= NULL
;
1432 char path
[PATH_MAX
];
1433 #ifdef WITH_PINENTRY
1437 if (!pwm
|| !filename
|| !*filename
) {
1438 *error
= GPG_ERR_INV_ARG
;
1442 #ifdef WITH_PINENTRY
1443 pin_try
= pwm
->pinentry_tries
- 1;
1447 * Avoid calling pinentry if the password is cached on the server or if
1448 * this is a new file.
1450 *error
= pwmd_command(pwm
, &result
, "GETCONFIG data_directory");
1455 snprintf(path
, sizeof(path
), "%s/%s", result
, filename
);
1456 pwmd_free_result(result
);
1458 if (access(path
, R_OK
) == -1) {
1459 if (errno
== ENOENT
)
1463 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", filename
);
1465 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1466 if (pwm
->passfunc
) {
1467 password
= pwm
->passfunc(pwm
, pwm
->passdata
);
1471 #ifdef WITH_PINENTRY
1473 * Get the password from pinentry.
1475 if (pwm
->use_pinentry
) {
1477 * Nonblocking is wanted. fork() then return a file descriptor
1478 * that the client can use to read() from.
1483 pwmd_nb_status_t pw
;
1485 if (pipe(p
) == -1) {
1486 *error
= gpg_error_from_syserror();
1499 strncpy(pw
.filename
, filename
, sizeof(pw
.filename
));
1500 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1510 *error
= getpin(pwm
, &password
, &pin_try
, 0);
1515 pinentry_disconnect(pwm
);
1517 if (gtimeout
&& gelapsed
>= gtimeout
)
1518 *error
= GPG_ERR_TIMEOUT
;
1522 pth_write(p
[1], &pw
, sizeof(pw
));
1524 write(p
[1], &pw
, sizeof(pw
));
1531 * Don't count the time it takes to open the file
1532 * which may have many iterations.
1534 signal(SIGALRM
, SIG_DFL
);
1535 *error
= do_open_command(pwm
, filename
, password
);
1538 signal(SIGALRM
, catchsig
);
1540 if (pwm
->pctx
&& *error
== EPWMD_BADKEY
) {
1542 goto getpin_nb_again
;
1544 goto getpin_nb_fail
;
1547 pinentry_disconnect(pwm
);
1550 pth_write(p
[1], &pw
, sizeof(pw
));
1552 write(p
[1], &pw
, sizeof(pw
));
1558 *error
= gpg_error_from_syserror();
1574 * Not using pinentry and the file was not found
1577 password
= pwm
->password
;
1578 #ifdef WITH_PINENTRY
1586 *error
= do_open_command(pwm
, filename
, password
);
1589 * Keep the user defined password set with pwmd_setopt(). The password may
1590 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1592 if (password
&& password
!= pwm
->password
)
1595 #ifdef WITH_PINENTRY
1596 if (*error
== EPWMD_BADKEY
) {
1597 if (pin_try
-- > 0 && !nb
) {
1598 *error
= pwmd_command(pwm
, &result
, "OPTION TITLE=%s",
1599 N_("Invalid passphrase, please try again."));
1608 pinentry_disconnect(pwm
);
1616 xfree(pwm
->filename
);
1618 pwm
->filename
= xstrdup(filename
);
1622 * The file is cached or the file is a new file.
1625 return *error
? -1 : -2;
1627 return *error
? 1 : 0;
1630 gpg_error_t
pwmd_open(pwm_t
*pwm
, const char *filename
)
1634 do_pwmd_open(pwm
, &error
, filename
, 0, 0);
1638 int pwmd_open_nb(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1641 #ifndef WITH_PINENTRY
1642 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1645 return do_pwmd_open(pwm
, error
, filename
, 1, timeout
);
1649 #ifdef WITH_PINENTRY
1650 static gpg_error_t
do_save_getpin(pwm_t
*pwm
, char **password
)
1654 char *result
= NULL
;
1658 error
= getpin(pwm
, &result
, &pin_try
, confirm
? 2 : 0);
1662 pinentry_disconnect(pwm
);
1675 if (strcmp(*password
, result
)) {
1678 pinentry_disconnect(pwm
);
1679 error
= EPWMD_BADKEY
;
1684 pinentry_disconnect(pwm
);
1689 static gpg_error_t
do_save_command(pwm_t
*pwm
, char *password
)
1691 char buf
[ASSUAN_LINELENGTH
];
1693 char *result
= NULL
;
1695 snprintf(buf
, sizeof(buf
), "SAVE %s", password
? password
: "");
1696 error
= send_command(pwm
, &result
, buf
);
1697 memset(&buf
, 0, sizeof(buf
));
1699 if (error
&& result
)
1705 gpg_error_t
pwmd_save_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1709 #ifndef WITH_PINENTRY
1710 return GPG_ERR_NOT_IMPLEMENTED
;
1713 if (!pwm
|| !pw
|| !pw
->filename
[0])
1714 return GPG_ERR_INV_ARG
;
1718 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1722 static int do_pwmd_save(pwm_t
*pwm
, gpg_error_t
*error
, int nb
)
1724 char *result
= NULL
;
1725 char *password
= NULL
;
1728 *error
= GPG_ERR_INV_ARG
;
1732 if (pwm
->use_pinentry
|| pwm
->passfunc
) {
1733 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", pwm
->filename
);
1735 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1736 #ifdef WITH_PINENTRY
1737 if (pwm
->use_pinentry
) {
1741 pwmd_nb_status_t pw
;
1743 if (pipe(p
) == -1) {
1744 *error
= gpg_error_from_syserror();
1757 strncpy(pw
.filename
, pwm
->filename
, sizeof(pw
.filename
));
1758 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1763 *error
= do_save_getpin(pwm
, &password
);
1764 } while (*error
== EPWMD_BADKEY
);
1768 pinentry_disconnect(pwm
);
1772 pth_write(p
[1], &pw
, sizeof(pw
));
1774 write(p
[1], &pw
, sizeof(pw
));
1780 *error
= do_save_command(pwm
, password
);
1781 pinentry_disconnect(pwm
);
1784 pth_write(p
[1], &pw
, sizeof(pw
));
1786 write(p
[1], &pw
, sizeof(pw
));
1792 *error
= gpg_error_from_syserror();
1805 *error
= do_save_getpin(pwm
, &password
);
1813 password
= (*pwm
->passfunc
)(pwm
, pwm
->passdata
);
1814 #ifdef WITH_PINENTRY
1824 password
= pwm
->password
;
1826 *error
= do_save_command(pwm
, password
);
1828 if (password
&& password
!= pwm
->password
)
1832 return *error
? -1 : -2;
1834 return *error
? 1 : 0;
1837 int pwmd_save_nb(pwm_t
*pwm
, gpg_error_t
*error
)
1839 #ifndef WITH_PINENTRY
1840 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1843 return do_pwmd_save(pwm
, error
, 1);
1847 gpg_error_t
pwmd_save(pwm_t
*pwm
)
1851 do_pwmd_save(pwm
, &error
, 0);
1855 gpg_error_t
pwmd_setopt(pwm_t
*pwm
, pwmd_option_t opt
, ...)
1858 #ifdef WITH_PINENTRY
1859 int n
= va_arg(ap
, int);
1863 gpg_error_t error
= 0;
1866 return GPG_ERR_INV_ARG
;
1871 case PWMD_OPTION_STATUS_FUNC
:
1872 pwm
->status_func
= va_arg(ap
, pwmd_status_fn
);
1874 case PWMD_OPTION_STATUS_DATA
:
1875 pwm
->status_data
= va_arg(ap
, void *);
1877 case PWMD_OPTION_PASSWORD_FUNC
:
1878 pwm
->passfunc
= va_arg(ap
, pwmd_password_fn
);
1880 case PWMD_OPTION_PASSWORD_DATA
:
1881 pwm
->passdata
= va_arg(ap
, void *);
1883 case PWMD_OPTION_PASSWORD
:
1884 arg1
= va_arg(ap
, char *);
1887 xfree(pwm
->password
);
1889 pwm
->password
= xstrdup(arg1
);
1891 #ifdef WITH_PINENTRY
1892 case PWMD_OPTION_PINENTRY
:
1893 n
= va_arg(ap
, int);
1895 if (n
!= 0 && n
!= 1) {
1897 error
= GPG_ERR_INV_VALUE
;
1900 pwm
->use_pinentry
= n
;
1901 error
= pwmd_command(pwm
, &result
, "OPTION PINENTRY=%i",
1902 !pwm
->use_pinentry
);
1905 case PWMD_OPTION_PINENTRY_TRIES
:
1906 n
= va_arg(ap
, int);
1910 error
= GPG_ERR_INV_VALUE
;
1913 pwm
->pinentry_tries
= n
;
1915 case PWMD_OPTION_PINENTRY_PATH
:
1916 if (pwm
->pinentry_path
)
1917 xfree(pwm
->pinentry_path
);
1919 pwm
->pinentry_path
= xstrdup(va_arg(ap
, char *));
1921 case PWMD_OPTION_PINENTRY_TTY
:
1922 if (pwm
->pinentry_tty
)
1923 xfree(pwm
->pinentry_tty
);
1925 pwm
->pinentry_tty
= xstrdup(va_arg(ap
, char *));
1927 case PWMD_OPTION_PINENTRY_DISPLAY
:
1928 if (pwm
->pinentry_display
)
1929 xfree(pwm
->pinentry_display
);
1931 pwm
->pinentry_display
= xstrdup(va_arg(ap
, char *));
1933 case PWMD_OPTION_PINENTRY_TERM
:
1934 if (pwm
->pinentry_term
)
1935 xfree(pwm
->pinentry_term
);
1937 pwm
->pinentry_term
= xstrdup(va_arg(ap
, char *));
1939 case PWMD_OPTION_PINENTRY_TITLE
:
1942 pwm
->title
= percent_escape(va_arg(ap
, char *));
1944 case PWMD_OPTION_PINENTRY_PROMPT
:
1947 pwm
->prompt
= percent_escape(va_arg(ap
, char *));
1949 case PWMD_OPTION_PINENTRY_DESC
:
1952 pwm
->desc
= percent_escape(va_arg(ap
, char *));
1955 case PWMD_OPTION_PINENTRY
:
1956 case PWMD_OPTION_PINENTRY_TRIES
:
1957 case PWMD_OPTION_PINENTRY_PATH
:
1958 case PWMD_OPTION_PINENTRY_TTY
:
1959 case PWMD_OPTION_PINENTRY_DISPLAY
:
1960 case PWMD_OPTION_PINENTRY_TERM
:
1961 case PWMD_OPTION_PINENTRY_TITLE
:
1962 case PWMD_OPTION_PINENTRY_PROMPT
:
1963 case PWMD_OPTION_PINENTRY_DESC
:
1964 error
= GPG_ERR_NOT_IMPLEMENTED
;
1968 error
= GPG_ERR_NOT_IMPLEMENTED
;
1976 gpg_error_t
pwmd_assuan_ctx(pwm_t
*pwm
, assuan_context_t
*ctx
, int *fd
)
1979 return GPG_ERR_INV_ARG
;