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 WITH_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
);
918 n
= select(pwm
->fd
+1, &fds
, NULL
, NULL
, &tv
);
921 if (FD_ISSET(pwm
->fd
, &fds
))
922 *rc
= parse_assuan_line(pwm
);
925 while (!*rc
&& assuan_pending_line(pwm
->ctx
))
926 *rc
= parse_assuan_line(pwm
);
928 if (pwm
->is_open_cmd
&& gpg_err_code(*rc
) == EPWMD_BADKEY
&&
929 ++pwm
->ntries
< pwm
->pinentry_tries
) {
930 pwm
->state
= ASYNC_INIT
;
931 *rc
= pwmd_open_async(pwm
, pwm
->filename
);
937 static gpg_error_t
assuan_command(pwm_t
*pwm
, assuan_context_t ctx
,
938 char **result
, const char *cmd
)
946 rc
= assuan_transact(ctx
, cmd
, mem_realloc_cb
, &data
, _inquire_cb
, pwm
,
947 pwm
->status_func
, pwm
->status_data
);
957 mem_realloc_cb(&data
, "", 1);
958 *result
= (char *)data
.buf
;
962 return gpg_err_code(rc
);
965 gpg_error_t
pwmd_inquire(pwm_t
*pwm
, const char *cmd
, pwmd_inquire_fn fn
,
968 if (!pwm
|| !cmd
|| !fn
)
969 return GPG_ERR_INV_ARG
;
971 pwm
->inquire_func
= fn
;
972 pwm
->inquire_data
= data
;
973 return assuan_command(pwm
, pwm
->ctx
, NULL
, cmd
);
976 gpg_error_t
pwmd_terminate_pinentry(pwm_t
*pwm
)
978 #ifndef WITH_PINENTRY
979 return GPG_ERR_NOT_IMPLEMENTED
;
981 if (!pwm
|| pwm
->pid
== -1)
982 return GPG_ERR_INV_ARG
;
984 if (kill(pwm
->pid
, 0) == 0) {
985 if (kill(pwm
->pid
, SIGTERM
) == -1) {
986 if (kill(pwm
->pid
, SIGKILL
) == -1)
987 return gpg_error_from_errno(errno
);
990 pwm
->pin_error
= GPG_ERR_TIMEOUT
;
993 return gpg_error_from_errno(errno
);
1000 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, int which
)
1003 char tmp
[ASSUAN_LINELENGTH
];
1007 pwm
->title
= xstrdup(N_("LibPWMD"));
1010 pwm
->prompt
= xstrdup(N_("Password:"));
1012 if (!pwm
->desc
&& !which
)
1013 pwm
->desc
= xstrdup(N_("Enter a password."));
1016 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Invalid password, please try again."));
1019 else if (which
== 2) {
1020 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Please type the password again for confirmation."));
1024 buf
= (char *)xmalloc(strlen("SETERROR ") + strlen(pwm
->desc
) + 1);
1025 sprintf(buf
, "SETERROR %s", pwm
->desc
);
1028 error
= pinentry_command(pwm
, NULL
, buf
);
1034 buf
= (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm
->prompt
) + 1);
1035 sprintf(buf
, "SETPROMPT %s", pwm
->prompt
);
1036 error
= pinentry_command(pwm
, NULL
, buf
);
1042 buf
= (char *)xmalloc(strlen("SETDESC ") + strlen(pwm
->title
) + 1);
1043 sprintf(buf
, "SETDESC %s", pwm
->title
);
1044 error
= pinentry_command(pwm
, NULL
, buf
);
1049 static void update_pinentry_settings(pwm_t
*pwm
)
1053 struct passwd
*pw
= getpwuid(getuid());
1056 snprintf(buf
, sizeof(buf
), "%s/.pwmd/pinentry.conf", pw
->pw_dir
);
1058 if ((fp
= fopen(buf
, "r")) == NULL
)
1061 while ((p
= fgets(buf
, sizeof(buf
), fp
)) != NULL
) {
1062 char name
[32], val
[256];
1064 if (sscanf(p
, " %31[a-zA-Z] = %255s", name
, val
) != 2)
1067 if (strcasecmp(name
, "TTYNAME") == 0) {
1068 xfree(pwm
->pinentry_tty
);
1069 pwm
->pinentry_tty
= xstrdup(val
);
1071 else if (strcasecmp(name
, "TTYTYPE") == 0) {
1072 xfree(pwm
->pinentry_term
);
1073 pwm
->pinentry_term
= xstrdup(val
);
1075 else if (strcasecmp(name
, "DISPLAY") == 0) {
1076 xfree(pwm
->pinentry_display
);
1077 pwm
->pinentry_display
= xstrdup(val
);
1079 else if (strcasecmp(name
, "PATH") == 0) {
1080 xfree(pwm
->pinentry_path
);
1081 pwm
->pinentry_path
= xstrdup(val
);
1088 static gpg_error_t
launch_pinentry(pwm_t
*pwm
)
1091 assuan_context_t ctx
;
1092 int child_list
[] = {-1};
1093 char *display
= getenv("DISPLAY");
1094 const char *argv
[6];
1095 int have_display
= 0;
1098 update_pinentry_settings(pwm
);
1100 if (pwm
->pinentry_display
|| display
)
1103 tty
= pwm
->pinentry_tty
? pwm
->pinentry_tty
: ttyname(STDOUT_FILENO
);
1106 return gpg_error_from_errno(errno
);
1109 if (!have_display
&& !tty
)
1110 return GPG_ERR_ENOTTY
;
1112 argv
[0] = "pinentry";
1113 argv
[1] = have_display
? "--display" : "--ttyname";
1114 argv
[2] = have_display
? pwm
->pinentry_display
? pwm
->pinentry_display
: display
: tty
;
1117 if (!have_display
) {
1118 argv
[3] = "--ttytype";
1119 argv
[4] = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv("TERM");
1123 rc
= assuan_pipe_connect(&ctx
, pwm
->pinentry_path
? pwm
->pinentry_path
: PINENTRY_PATH
, argv
, child_list
);
1128 pwm
->pid
= assuan_get_pid(ctx
);
1130 return set_pinentry_strings(pwm
, 0);
1133 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1138 n
= launch_pinentry(pwm
);
1144 return assuan_command(pwm
, pwm
->pctx
, result
, cmd
);
1147 static void pinentry_disconnect(pwm_t
*pwm
)
1150 assuan_disconnect(pwm
->pctx
);
1157 * Only called from a child process.
1159 static void catchsig(int sig
)
1163 if (gelapsed
++ >= gtimeout
) {
1164 global_error
= pwmd_terminate_pinentry(gpwm
);
1167 global_error
= GPG_ERR_TIMEOUT
;
1180 * Borrowed from libassuan.
1182 static char *percent_escape(const char *atext
)
1184 const unsigned char *s
;
1185 int len
= strlen(atext
) * 3 + 1;
1186 char *buf
= (char *)xmalloc(len
), *p
= buf
;
1191 for (s
=(const unsigned char *)atext
; *s
; s
++) {
1193 sprintf (p
, "%%%02X", *s
);
1205 static gpg_error_t
send_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1208 return GPG_ERR_INV_ARG
;
1210 return assuan_command(pwm
, pwm
->ctx
, result
, cmd
);
1214 * Avoid sending the BYE command here. libassuan will close the file
1215 * descriptor and release the assuan context. Use pwmd_close() instead.
1217 gpg_error_t
pwmd_command(pwm_t
*pwm
, char **result
, const char *cmd
, ...)
1225 return GPG_ERR_INV_ARG
;
1230 * C99 allows the dst pointer to be null which will calculate the length
1231 * of the result and return it.
1233 len
= vsnprintf(NULL
, 0, cmd
, ap
);
1234 buf
= (char *)xmalloc(len
+ 1);
1235 len
= vsnprintf(buf
, len
+ 1, cmd
, ap
);
1237 error
= send_command(pwm
, result
, buf
);
1242 #ifdef WITH_PINENTRY
1243 static gpg_error_t
do_getpin(pwm_t
*pwm
, char **result
)
1246 signal(SIGALRM
, catchsig
);
1251 return pinentry_command(pwm
, result
, "GETPIN");
1254 static gpg_error_t
getpin(pwm_t
*pwm
, char **result
, int *try_n
, int which
)
1256 int pin_try
= *try_n
;
1262 if (pin_try
== -1) {
1263 error
= set_pinentry_strings(pwm
, which
);
1266 pinentry_disconnect(pwm
);
1271 if (pwm
->pinentry_tries
-1 != pin_try
) {
1272 error
= set_pinentry_strings(pwm
, 1);
1275 pinentry_disconnect(pwm
);
1281 error
= do_getpin(pwm
, result
);
1284 * Since there was input cancel any timeout setting.
1289 if (error
== GPG_ERR_CANCELED
)
1290 return GPG_ERR_CANCELED
;
1292 if (pin_try
!= -1 && pin_try
--)
1296 pinentry_disconnect(pwm
);
1306 gpg_error_t
pwmd_open_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1310 #ifndef WITH_PINENTRY
1311 return GPG_ERR_NOT_IMPLEMENTED
;
1314 if (!pwm
|| !pw
|| !pw
->filename
[0])
1315 return GPG_ERR_INV_ARG
;
1325 xfree(pwm
->filename
);
1327 pwm
->filename
= xstrdup(pw
->filename
);
1328 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1332 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1336 static gpg_error_t
do_open_command(pwm_t
*pwm
, const char *filename
, char *password
)
1338 char buf
[ASSUAN_LINELENGTH
];
1340 char *result
= NULL
;
1342 snprintf(buf
, sizeof(buf
), "OPEN %s %s", filename
, password
? password
: "");
1343 error
= send_command(pwm
, &result
, buf
);
1344 memset(buf
, 0, sizeof(buf
));
1346 if (error
&& result
)
1352 static int do_pwmd_open(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1353 int nb
, int timeout
)
1355 char *result
= NULL
;
1356 char *password
= NULL
;
1357 char path
[PATH_MAX
];
1358 #ifdef WITH_PINENTRY
1362 if (!pwm
|| !filename
|| !*filename
) {
1363 *error
= GPG_ERR_INV_ARG
;
1367 #ifdef WITH_PINENTRY
1368 pin_try
= pwm
->pinentry_tries
- 1;
1372 * Avoid calling pinentry if the password is cached on the server or if
1373 * this is a new file.
1375 *error
= pwmd_command(pwm
, &result
, "GETCONFIG data_directory");
1380 snprintf(path
, sizeof(path
), "%s/%s", result
, filename
);
1381 pwmd_free_result(result
);
1383 if (access(path
, R_OK
) == -1) {
1384 if (errno
== ENOENT
)
1388 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", filename
);
1390 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1391 if (pwm
->passfunc
) {
1392 password
= pwm
->passfunc(pwm
, pwm
->passdata
);
1396 #ifdef WITH_PINENTRY
1398 * Get the password from pinentry.
1400 if (pwm
->use_pinentry
) {
1402 * Nonblocking is wanted. fork() then return a file descriptor
1403 * that the client can use to read() from.
1408 pwmd_nb_status_t pw
;
1410 if (pipe(p
) == -1) {
1411 *error
= gpg_error_from_syserror();
1420 strncpy(pw
.filename
, filename
, sizeof(pw
.filename
));
1421 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1431 *error
= getpin(pwm
, &password
, &pin_try
, 0);
1436 pinentry_disconnect(pwm
);
1438 if (gtimeout
&& gelapsed
>= gtimeout
)
1439 *error
= GPG_ERR_TIMEOUT
;
1442 write(p
[1], &pw
, sizeof(pw
));
1448 * Don't count the time it takes to open the file
1449 * which may have many iterations.
1451 signal(SIGALRM
, SIG_DFL
);
1452 *error
= do_open_command(pwm
, filename
, password
);
1455 signal(SIGALRM
, catchsig
);
1457 if (pwm
->pctx
&& *error
== EPWMD_BADKEY
) {
1459 goto getpin_nb_again
;
1461 goto getpin_nb_fail
;
1464 pinentry_disconnect(pwm
);
1466 write(p
[1], &pw
, sizeof(pw
));
1471 *error
= gpg_error_from_syserror();
1486 * Not using pinentry and the file was not found
1489 password
= pwm
->password
;
1490 #ifdef WITH_PINENTRY
1498 *error
= do_open_command(pwm
, filename
, password
);
1501 * Keep the user defined password set with pwmd_setopt(). The password may
1502 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1504 if (password
&& password
!= pwm
->password
)
1507 #ifdef WITH_PINENTRY
1508 if (*error
== EPWMD_BADKEY
) {
1509 if (pin_try
-- > 0 && !nb
) {
1510 *error
= pwmd_command(pwm
, &result
, "OPTION TITLE=%s",
1511 N_("Invalid password, please try again."));
1520 pinentry_disconnect(pwm
);
1528 xfree(pwm
->filename
);
1530 pwm
->filename
= xstrdup(filename
);
1534 * The file is cached or the file is a new file.
1537 return *error
? -1 : -2;
1539 return *error
? 1 : 0;
1542 gpg_error_t
pwmd_open(pwm_t
*pwm
, const char *filename
)
1546 do_pwmd_open(pwm
, &error
, filename
, 0, 0);
1550 int pwmd_open_nb(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1553 #ifndef WITH_PINENTRY
1554 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1557 return do_pwmd_open(pwm
, error
, filename
, 1, timeout
);
1561 #ifdef WITH_PINENTRY
1562 static gpg_error_t
do_save_getpin(pwm_t
*pwm
, char **password
)
1566 char *result
= NULL
;
1570 error
= getpin(pwm
, &result
, &pin_try
, confirm
? 2 : 0);
1574 pinentry_disconnect(pwm
);
1587 if (strcmp(*password
, result
)) {
1590 pinentry_disconnect(pwm
);
1591 error
= EPWMD_BADKEY
;
1596 pinentry_disconnect(pwm
);
1601 static gpg_error_t
do_save_command(pwm_t
*pwm
, char *password
)
1603 char buf
[ASSUAN_LINELENGTH
];
1605 char *result
= NULL
;
1607 snprintf(buf
, sizeof(buf
), "SAVE %s", password
? password
: "");
1608 error
= send_command(pwm
, &result
, buf
);
1609 memset(&buf
, 0, sizeof(buf
));
1611 if (error
&& result
)
1617 gpg_error_t
pwmd_save_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1621 #ifndef WITH_PINENTRY
1622 return GPG_ERR_NOT_IMPLEMENTED
;
1625 if (!pwm
|| !pw
|| !pw
->filename
[0])
1626 return GPG_ERR_INV_ARG
;
1630 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1634 static int do_pwmd_save(pwm_t
*pwm
, gpg_error_t
*error
, int nb
)
1636 char *result
= NULL
;
1637 char *password
= NULL
;
1640 *error
= GPG_ERR_INV_ARG
;
1644 if (pwm
->use_pinentry
|| pwm
->passfunc
) {
1645 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", pwm
->filename
);
1647 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1648 #ifdef WITH_PINENTRY
1649 if (pwm
->use_pinentry
) {
1653 pwmd_nb_status_t pw
;
1655 if (pipe(p
) == -1) {
1656 *error
= gpg_error_from_syserror();
1665 strncpy(pw
.filename
, pwm
->filename
, sizeof(pw
.filename
));
1666 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1671 *error
= do_save_getpin(pwm
, &password
);
1672 } while (*error
== EPWMD_BADKEY
);
1676 pinentry_disconnect(pwm
);
1679 write(p
[1], &pw
, sizeof(pw
));
1684 *error
= do_save_command(pwm
, password
);
1685 pinentry_disconnect(pwm
);
1687 write(p
[1], &pw
, sizeof(pw
));
1692 *error
= gpg_error_from_syserror();
1704 *error
= do_save_getpin(pwm
, &password
);
1712 password
= (*pwm
->passfunc
)(pwm
, pwm
->passdata
);
1713 #ifdef WITH_PINENTRY
1723 password
= pwm
->password
;
1725 *error
= do_save_command(pwm
, password
);
1727 if (password
&& password
!= pwm
->password
)
1731 return *error
? -1 : -2;
1733 return *error
? 1 : 0;
1736 int pwmd_save_nb(pwm_t
*pwm
, gpg_error_t
*error
)
1738 #ifndef WITH_PINENTRY
1739 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1742 return do_pwmd_save(pwm
, error
, 1);
1746 gpg_error_t
pwmd_save(pwm_t
*pwm
)
1750 do_pwmd_save(pwm
, &error
, 0);
1754 gpg_error_t
pwmd_setopt(pwm_t
*pwm
, pwmd_option_t opt
, ...)
1757 #ifdef WITH_PINENTRY
1758 int n
= va_arg(ap
, int);
1762 gpg_error_t error
= 0;
1765 return GPG_ERR_INV_ARG
;
1770 case PWMD_OPTION_STATUS_FUNC
:
1771 pwm
->status_func
= va_arg(ap
, pwmd_status_fn
);
1773 case PWMD_OPTION_STATUS_DATA
:
1774 pwm
->status_data
= va_arg(ap
, void *);
1776 case PWMD_OPTION_PASSWORD_FUNC
:
1777 pwm
->passfunc
= va_arg(ap
, pwmd_password_fn
);
1779 case PWMD_OPTION_PASSWORD_DATA
:
1780 pwm
->passdata
= va_arg(ap
, void *);
1782 case PWMD_OPTION_PASSWORD
:
1783 arg1
= va_arg(ap
, char *);
1786 xfree(pwm
->password
);
1788 pwm
->password
= xstrdup(arg1
);
1790 #ifdef WITH_PINENTRY
1791 case PWMD_OPTION_PINENTRY
:
1792 n
= va_arg(ap
, int);
1794 if (n
!= 0 && n
!= 1) {
1796 error
= GPG_ERR_INV_VALUE
;
1799 pwm
->use_pinentry
= n
;
1800 error
= pwmd_command(pwm
, &result
, "OPTION PINENTRY=%i",
1801 !pwm
->use_pinentry
);
1804 case PWMD_OPTION_PINENTRY_TRIES
:
1805 n
= va_arg(ap
, int);
1809 error
= GPG_ERR_INV_VALUE
;
1812 pwm
->pinentry_tries
= n
;
1814 case PWMD_OPTION_PINENTRY_PATH
:
1815 if (pwm
->pinentry_path
)
1816 xfree(pwm
->pinentry_path
);
1818 pwm
->pinentry_path
= xstrdup(va_arg(ap
, char *));
1820 case PWMD_OPTION_PINENTRY_TTY
:
1821 if (pwm
->pinentry_tty
)
1822 xfree(pwm
->pinentry_tty
);
1824 pwm
->pinentry_tty
= xstrdup(va_arg(ap
, char *));
1826 case PWMD_OPTION_PINENTRY_DISPLAY
:
1827 if (pwm
->pinentry_display
)
1828 xfree(pwm
->pinentry_display
);
1830 pwm
->pinentry_display
= xstrdup(va_arg(ap
, char *));
1832 case PWMD_OPTION_PINENTRY_TERM
:
1833 if (pwm
->pinentry_term
)
1834 xfree(pwm
->pinentry_term
);
1836 pwm
->pinentry_term
= xstrdup(va_arg(ap
, char *));
1838 case PWMD_OPTION_PINENTRY_TITLE
:
1841 pwm
->title
= percent_escape(va_arg(ap
, char *));
1843 case PWMD_OPTION_PINENTRY_PROMPT
:
1846 pwm
->prompt
= percent_escape(va_arg(ap
, char *));
1848 case PWMD_OPTION_PINENTRY_DESC
:
1851 pwm
->desc
= percent_escape(va_arg(ap
, char *));
1854 case PWMD_OPTION_PINENTRY
:
1855 case PWMD_OPTION_PINENTRY_TRIES
:
1856 case PWMD_OPTION_PINENTRY_PATH
:
1857 case PWMD_OPTION_PINENTRY_TTY
:
1858 case PWMD_OPTION_PINENTRY_DISPLAY
:
1859 case PWMD_OPTION_PINENTRY_TERM
:
1860 case PWMD_OPTION_PINENTRY_TITLE
:
1861 case PWMD_OPTION_PINENTRY_PROMPT
:
1862 case PWMD_OPTION_PINENTRY_DESC
:
1863 error
= GPG_ERR_NOT_IMPLEMENTED
;
1867 error
= GPG_ERR_NOT_IMPLEMENTED
;
1875 gpg_error_t
pwmd_assuan_ctx(pwm_t
*pwm
, assuan_context_t
*ctx
, int *fd
)
1878 return GPG_ERR_INV_ARG
;