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>
61 #define N_(msgid) dgettext("libpwmd", msgid)
67 #define xrealloc realloc
68 #define xmalloc malloc
69 #define xstrdup strdup
70 #define xcalloc calloc
77 static int gelapsed
, gtimeout
;
78 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
);
79 static gpg_error_t global_error
;
82 const char *pwmd_strerror(gpg_error_t e
)
84 gpg_err_code_t code
= gpg_err_code(e
);
86 if (code
>= GPG_ERR_USER_1
&& code
< gpg_err_code(EPWMD_MAX
)) {
90 return N_("Unknown error");
92 return N_("No cache slots available");
94 return N_("Recursion loop");
96 return N_("No file is open");
98 return N_("General LibXML error");
100 return N_("File modified");
104 return gpg_strerror(e
);
107 gpg_error_t
pwmd_init()
110 gnutls_global_set_mem_functions(xmalloc
, xmalloc
, NULL
, xrealloc
, xfree
);
111 gnutls_global_init ();
114 bindtextdomain("libpwmd", LOCALEDIR
);
117 assuan_set_malloc_hooks(xmalloc
, xrealloc
, xfree
);
118 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT
);
122 static pwm_t
*_socket_connect_finalize(pwm_t
*pwm
, assuan_context_t ctx
)
125 int n
= assuan_get_active_fds(ctx
, 0, active
, sizeof(active
));
127 pwm
->fd
= n
<= 0 ? -1 : dup(active
[0]);
131 pwm
->pinentry_tries
= 3;
133 assuan_set_pointer(ctx
, pwm
);
138 static int read_hook(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
139 size_t len
, ssize_t
*ret
)
141 pwm_t
*pwm
= assuan_get_pointer(ctx
);
143 if (!pwm
|| !pwm
->session
)
144 *ret
= read((int)fd
, data
, len
);
147 *ret
= gnutls_record_recv(pwm
->session
, data
, len
);
149 if (*ret
== GNUTLS_E_REHANDSHAKE
) {
150 *ret
= gnutls_rehandshake(pwm
->session
);
152 if (*ret
== GNUTLS_E_WARNING_ALERT_RECEIVED
||
153 *ret
== GNUTLS_A_NO_RENEGOTIATION
) {
154 fprintf(stderr
, "%s", gnutls_strerror(*ret
));
158 if (*ret
!= GNUTLS_E_SUCCESS
) {
159 fprintf(stderr
, "%s", gnutls_strerror(*ret
));
164 *ret
= gnutls_handshake(pwm
->session
);
166 if (*ret
!= GNUTLS_E_SUCCESS
) {
167 fprintf(stderr
, "%s", gnutls_strerror(*ret
));
174 } while (*ret
== GNUTLS_E_INTERRUPTED
|| *ret
== GNUTLS_E_AGAIN
);
177 return *ret
<= 0 ? 0 : 1;
180 static int write_hook(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
181 size_t len
, ssize_t
*ret
)
183 pwm_t
*pwm
= assuan_get_pointer(ctx
);
185 if (!pwm
|| !pwm
->session
)
186 *ret
= write((int)fd
, data
, len
);
189 *ret
= gnutls_record_send(pwm
->session
, data
, len
);
190 } while (*ret
== GNUTLS_E_INTERRUPTED
|| *ret
== GNUTLS_E_AGAIN
);
193 return *ret
<= 0 ? 0 : 1;
196 static void _tls_deinit(pwm_t
*pwm
)
198 //FIXME hangs when missing cert
199 gnutls_bye(pwm
->session
, GNUTLS_SHUT_RDWR
);
200 gnutls_deinit(pwm
->session
);
201 gnutls_certificate_free_credentials(pwm
->x509
);
204 static gpg_error_t
verify_certificate(pwm_t
*pwm
)
207 const gnutls_datum_t
*cert_list
;
208 unsigned int cert_list_size
;
210 gnutls_x509_crt_t cert
;
213 rc
= gnutls_certificate_verify_peers2(pwm
->session
, &status
);
216 return GPG_ERR_UNKNOWN_ERRNO
;
218 if (status
& GNUTLS_CERT_INVALID
)
219 return GPG_ERR_MISSING_CERT
;
221 if (status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
222 return GPG_ERR_BAD_SIGNATURE
;
224 if (status
& GNUTLS_CERT_REVOKED
)
225 return GPG_ERR_CERT_REVOKED
;
227 if (gnutls_certificate_type_get(pwm
->session
) != GNUTLS_CRT_X509
)
228 return GPG_ERR_UNSUPPORTED_CERT
;
230 if (gnutls_x509_crt_init(&cert
) < 0)
231 return gpg_error_from_errno(ENOMEM
);
233 cert_list
= gnutls_certificate_get_peers (pwm
->session
, &cert_list_size
);
236 rc
= GPG_ERR_MISSING_CERT
;
240 for (i
= 0; i
< cert_list_size
; i
++) {
241 if (gnutls_x509_crt_import(cert
, &cert_list
[i
], GNUTLS_X509_FMT_DER
) < 0) {
242 rc
= GPG_ERR_BAD_CERT_CHAIN
;
247 /* Beware here we do not check for errors.
249 if (gnutls_x509_crt_get_expiration_time(cert
) < time (0)) {
250 rc
= GPG_ERR_CERT_EXPIRED
;
254 if (gnutls_x509_crt_get_activation_time(cert
) > time (0)) {
255 rc
= GPG_ERR_CERT_TOO_YOUNG
;
260 if (!gnutls_x509_crt_check_hostname (cert
, hostname
))
262 printf ("The certificate's owner does not match hostname '%s'\n",
269 gnutls_x509_crt_deinit(cert
);
273 static gpg_error_t
_tls_init(pwm_t
*pwm
, int fd
, const char *cert
,
274 const char *key
, const char *ca
)
279 rc
= gnutls_certificate_allocate_credentials(&pwm
->x509
);
282 return gpg_error_from_errno(ENOMEM
);
284 /* The client certificate must be signed by the CA of the pwmd server
285 * certificate in order for the client to authenticate successfully. Man in
286 * the middle attacks are still possible if the attacker is running a pwmd
287 * that doesn't require client certificate authentication. So require the
288 * client to verify the server certificate.
290 gnutls_certificate_set_x509_trust_file (pwm
->x509
, ca
, GNUTLS_X509_FMT_PEM
);
292 rc
= gnutls_certificate_set_x509_key_file(pwm
->x509
, cert
, key
,
293 GNUTLS_X509_FMT_PEM
);
294 rc
= gnutls_init(&pwm
->session
, GNUTLS_CLIENT
);
295 rc
= gnutls_priority_set_direct(pwm
->session
, "SECURE256", &errstr
);
296 rc
= gnutls_credentials_set(pwm
->session
, GNUTLS_CRD_CERTIFICATE
, pwm
->x509
);
297 gnutls_transport_set_ptr(pwm
->session
, (gnutls_transport_ptr_t
) fd
);
298 rc
= gnutls_handshake(pwm
->session
);
302 rc
= GPG_ERR_INV_CIPHER_MODE
;
307 rc
= verify_certificate(pwm
);
313 static void _tls_assuan_deinit(assuan_context_t ctx
)
315 pwm_t
*pwm
= assuan_get_pointer(ctx
);
320 pwm_t
*pwmd_tcp_connect(const char *host
, gpg_error_t
*rc
, const char *cert
,
321 const char *key
, const char *ca
)
327 struct sockaddr_in their_addr
;
328 assuan_context_t ctx
;
329 char *tcert
, *tkey
, *tca
;
331 struct passwd
*pw
= getpwuid(getuid());
333 struct assuan_io_hooks io_hooks
= {read_hook
, write_hook
};
336 *rc
= GPG_ERR_INV_ARG
;
340 p
= strchr(host
, ':');
345 port
= strtol(++p
, &pp
, 10);
348 *rc
= GPG_ERR_INV_ARG
;
353 if ((he
= gethostbyname(host
)) == NULL
) {
354 *rc
= gpg_error_from_syserror();
358 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
361 *rc
= gpg_error_from_syserror();
365 their_addr
.sin_family
= PF_INET
;
366 their_addr
.sin_port
= htons(port
);
367 their_addr
.sin_addr
= *((struct in_addr
*)he
->h_addr
);
368 memset(their_addr
.sin_zero
, '\0', sizeof their_addr
.sin_zero
);
370 if (connect(fd
, (struct sockaddr
*)&their_addr
, sizeof(their_addr
)) == -1) {
371 *rc
= gpg_error_from_syserror();
376 snprintf(buf
, sizeof(buf
), "%s/.pwmd/client-cert.pem", pw
->pw_dir
);
377 tcert
= xstrdup(buf
);
380 tcert
= xstrdup(cert
);
383 *rc
= gpg_error_from_syserror();
388 snprintf(buf
, sizeof(buf
), "%s/.pwmd/client-key.pem", pw
->pw_dir
);
395 *rc
= gpg_error_from_syserror();
401 snprintf(buf
, sizeof(buf
), "%s/.pwmd/ca-cert.pem", pw
->pw_dir
);
408 *rc
= gpg_error_from_syserror();
414 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
415 *rc
= gpg_error_from_syserror();
422 *rc
= _tls_init(pwm
, fd
, tcert
, tkey
, tca
);
433 assuan_set_io_hooks(&io_hooks
);
434 *rc
= assuan_socket_connect_fd(&ctx
, fd
, 0, pwm
);
441 assuan_set_finish_handler(ctx
, _tls_assuan_deinit
);
442 return _socket_connect_finalize(pwm
, ctx
);
446 pwm_t
*pwmd_connect(const char *path
, gpg_error_t
*rc
)
449 char *socketpath
= NULL
;
451 assuan_context_t ctx
;
454 pw
= getpwuid(getuid());
455 socketpath
= (char *)xmalloc(strlen(pw
->pw_dir
) + strlen("/.pwmd/socket") + 1);
456 sprintf(socketpath
, "%s/.pwmd/socket", pw
->pw_dir
);
459 socketpath
= xstrdup(path
);
461 *rc
= assuan_socket_connect_ext(&ctx
, socketpath
, -1, 0);
467 if ((pwm
= (pwm_t
*)xcalloc(1, sizeof(pwm_t
))) == NULL
) {
468 *rc
= gpg_error_from_syserror();
472 return _socket_connect_finalize(pwm
, ctx
);
475 gpg_error_t
pwmd_pending_line(pwm_t
*pwm
, char **line
, size_t *len
)
478 return GPG_ERR_INV_ARG
;
480 if (assuan_pending_line(pwm
->ctx
))
481 return assuan_read_line(pwm
->ctx
, line
, len
);
483 return GPG_ERR_NO_DATA
;
486 void pwmd_close(pwm_t
*pwm
)
492 assuan_disconnect(pwm
->ctx
);
495 xfree(pwm
->password
);
506 if (pwm
->pinentry_tty
)
507 xfree(pwm
->pinentry_tty
);
509 if (pwm
->pinentry_display
)
510 xfree(pwm
->pinentry_display
);
512 if (pwm
->pinentry_term
)
513 xfree(pwm
->pinentry_term
);
516 xfree(pwm
->filename
);
521 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
523 membuf_t
*mem
= (membuf_t
*)data
;
529 if ((p
= xrealloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
533 memcpy((char *)mem
->buf
+ mem
->len
, buffer
, len
);
538 void pwmd_free_result(void *data
)
543 static int _inquire_cb(void *data
, const char *keyword
)
545 pwm_t
*pwm
= (pwm_t
*)data
;
547 int flags
= fcntl(pwm
->fd
, F_GETFL
);
549 /* Shouldn't get this far without a callback. */
550 if (!pwm
->inquire_func
)
551 return GPG_ERR_INV_ARG
;
554 * Since the socket file descriptor is probably set to non-blocking, set to
555 * blocking to prevent GPG_ERR_EAGAIN errors. This should be fixes when
556 * asynchronous INQUIRE is supported by either libassuan or a later
559 fcntl(pwm
->fd
, F_SETFL
, 0);
566 rc
= pwm
->inquire_func(pwm
->inquire_data
, keyword
, rc
, &result
, &len
);
567 rc
= gpg_err_code(rc
);
569 if (rc
== GPG_ERR_EOF
|| !rc
) {
570 if (len
<= 0 || !result
|| !*result
) {
575 arc
= assuan_send_data(pwm
->ctx
, result
, len
);
577 if (rc
== GPG_ERR_EOF
) {
588 fcntl(pwm
->fd
, F_SETFL
, flags
);
592 gpg_error_t
pwmd_finalize(pwm_t
*pwm
)
594 if (!pwm
|| pwm
->fd
< 0)
595 return GPG_ERR_INV_ARG
;
597 pwm
->state
= ASYNC_INIT
;
599 pwm
->is_open_cmd
= 0;
603 static gpg_error_t
do_nb_command(pwm_t
*pwm
, const char *cmd
, const char *arg
)
607 size_t len
= strlen(cmd
) + 2;
609 len
+= arg
? strlen(arg
) : 0;
611 if (pwm
->state
!= ASYNC_INIT
)
612 return GPG_ERR_UNEXPECTED
;
614 buf
= (char *)xmalloc(len
);
617 rc
= gpg_error_from_errno(ENOMEM
);
621 snprintf(buf
, len
, "%s %s", cmd
, arg
? arg
: "");
622 rc
= assuan_write_line(pwm
->ctx
, buf
);
626 pwm
->state
= ASYNC_PROCESS
;
632 gpg_error_t
pwmd_open_async(pwm_t
*pwm
, const char *filename
)
634 if (!pwm
|| !filename
)
635 return GPG_ERR_INV_ARG
;
637 /* For pinentry retries. */
638 if (!pwm
->is_open_cmd
) {
640 xfree(pwm
->filename
);
642 pwm
->filename
= xstrdup(filename
);
645 pwm
->is_open_cmd
= 1;
646 return do_nb_command(pwm
, "OPEN", filename
);
649 gpg_error_t
pwmd_save_async(pwm_t
*pwm
)
652 return GPG_ERR_INV_ARG
;
654 return do_nb_command(pwm
, "SAVE", NULL
);
657 static gpg_error_t
parse_assuan_line(pwm_t
*pwm
)
663 rc
= assuan_read_line(pwm
->ctx
, &line
, &len
);
666 if (line
[0] == 'O' && line
[1] == 'K' &&
667 (line
[2] == 0 || line
[2] == ' ')) {
668 pwm
->state
= ASYNC_DONE
;
670 else if (line
[0] == '#') {
672 else if (line
[0] == 'S' && (line
[1] == 0 || line
[1] == ' ')) {
673 if (pwm
->status_func
) {
674 pwm
->status_func(pwm
->status_data
,
675 line
[1] == 0 ? line
+1 : line
+2);
678 else if (line
[0] == 'E' && line
[1] == 'R' && line
[2] == 'R' &&
679 (line
[3] == 0 || line
[3] == ' ')) {
682 pwm
->state
= ASYNC_DONE
;
689 pwmd_async_t
pwmd_process(pwm_t
*pwm
, gpg_error_t
*rc
)
693 struct timeval tv
= {0, 0};
697 if (!pwm
|| pwm
->fd
< 0) {
698 *rc
= GPG_ERR_INV_ARG
;
701 else if (pwm
->state
== ASYNC_DONE
)
703 else if (pwm
->state
== ASYNC_INIT
) {
704 *rc
= GPG_ERR_UNEXPECTED
;
709 FD_SET(pwm
->fd
, &rfds
);
710 n
= select(pwm
->fd
+1, &rfds
, NULL
, NULL
, &tv
);
713 if (FD_ISSET(pwm
->fd
, &rfds
))
714 *rc
= parse_assuan_line(pwm
);
717 while (!*rc
&& assuan_pending_line(pwm
->ctx
))
718 *rc
= parse_assuan_line(pwm
);
720 if (pwm
->is_open_cmd
&& gpg_err_code(*rc
) == EPWMD_BADKEY
&&
721 ++pwm
->ntries
< pwm
->pinentry_tries
) {
722 pwm
->state
= ASYNC_INIT
;
723 *rc
= pwmd_open_async(pwm
, pwm
->filename
);
729 static gpg_error_t
assuan_command(pwm_t
*pwm
, assuan_context_t ctx
,
730 char **result
, const char *cmd
)
738 rc
= assuan_transact(ctx
, cmd
, mem_realloc_cb
, &data
, _inquire_cb
, pwm
,
739 pwm
->status_func
, pwm
->status_data
);
749 mem_realloc_cb(&data
, "", 1);
750 *result
= (char *)data
.buf
;
754 return gpg_err_code(rc
);
757 gpg_error_t
pwmd_inquire(pwm_t
*pwm
, const char *cmd
, pwmd_inquire_fn fn
,
760 if (!pwm
|| !cmd
|| !fn
)
761 return GPG_ERR_INV_ARG
;
763 pwm
->inquire_func
= fn
;
764 pwm
->inquire_data
= data
;
765 return assuan_command(pwm
, pwm
->ctx
, NULL
, cmd
);
768 gpg_error_t
pwmd_terminate_pinentry(pwm_t
*pwm
)
771 return GPG_ERR_NOT_IMPLEMENTED
;
773 if (!pwm
|| pwm
->pid
== -1)
774 return GPG_ERR_INV_ARG
;
776 if (kill(pwm
->pid
, 0) == 0) {
777 if (kill(pwm
->pid
, SIGTERM
) == -1) {
778 if (kill(pwm
->pid
, SIGKILL
) == -1)
779 return gpg_error_from_errno(errno
);
782 pwm
->pin_error
= GPG_ERR_TIMEOUT
;
785 return gpg_error_from_errno(errno
);
792 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, int which
)
795 char tmp
[ASSUAN_LINELENGTH
];
799 pwm
->title
= xstrdup(N_("LibPWMD"));
802 pwm
->prompt
= xstrdup(N_("Password:"));
804 if (!pwm
->desc
&& !which
)
805 pwm
->desc
= xstrdup(N_("Enter a password."));
808 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Invalid password, please try again."));
811 else if (which
== 2) {
812 snprintf(tmp
, sizeof(tmp
), "SETERROR %s", N_("Please type the password again for confirmation."));
816 buf
= (char *)xmalloc(strlen("SETERROR ") + strlen(pwm
->desc
) + 1);
817 sprintf(buf
, "SETERROR %s", pwm
->desc
);
820 error
= pinentry_command(pwm
, NULL
, buf
);
826 buf
= (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm
->prompt
) + 1);
827 sprintf(buf
, "SETPROMPT %s", pwm
->prompt
);
828 error
= pinentry_command(pwm
, NULL
, buf
);
834 buf
= (char *)xmalloc(strlen("SETDESC ") + strlen(pwm
->title
) + 1);
835 sprintf(buf
, "SETDESC %s", pwm
->title
);
836 error
= pinentry_command(pwm
, NULL
, buf
);
841 static void update_pinentry_settings(pwm_t
*pwm
)
845 struct passwd
*pw
= getpwuid(getuid());
848 snprintf(buf
, sizeof(buf
), "%s/.pwmd/pinentry.conf", pw
->pw_dir
);
850 if ((fp
= fopen(buf
, "r")) == NULL
)
853 while ((p
= fgets(buf
, sizeof(buf
), fp
)) != NULL
) {
854 char name
[32], val
[256];
856 if (sscanf(p
, " %31[a-zA-Z] = %255s", name
, val
) != 2)
859 if (strcasecmp(name
, "TTYNAME") == 0) {
860 xfree(pwm
->pinentry_tty
);
861 pwm
->pinentry_tty
= xstrdup(val
);
863 else if (strcasecmp(name
, "TTYTYPE") == 0) {
864 xfree(pwm
->pinentry_term
);
865 pwm
->pinentry_term
= xstrdup(val
);
867 else if (strcasecmp(name
, "DISPLAY") == 0) {
868 xfree(pwm
->pinentry_display
);
869 pwm
->pinentry_display
= xstrdup(val
);
871 else if (strcasecmp(name
, "PATH") == 0) {
872 xfree(pwm
->pinentry_path
);
873 pwm
->pinentry_path
= xstrdup(val
);
880 static gpg_error_t
launch_pinentry(pwm_t
*pwm
)
883 assuan_context_t ctx
;
884 int child_list
[] = {-1};
885 char *display
= getenv("DISPLAY");
887 int have_display
= 0;
890 update_pinentry_settings(pwm
);
892 if (pwm
->pinentry_display
|| display
)
895 tty
= pwm
->pinentry_tty
? pwm
->pinentry_tty
: ttyname(STDOUT_FILENO
);
898 return gpg_error_from_errno(errno
);
901 if (!have_display
&& !tty
)
902 return GPG_ERR_ENOTTY
;
904 argv
[0] = "pinentry";
905 argv
[1] = have_display
? "--display" : "--ttyname";
906 argv
[2] = have_display
? pwm
->pinentry_display
? pwm
->pinentry_display
: display
: tty
;
910 argv
[3] = "--ttytype";
911 argv
[4] = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv("TERM");
915 rc
= assuan_pipe_connect(&ctx
, pwm
->pinentry_path
? pwm
->pinentry_path
: PINENTRY_PATH
, argv
, child_list
);
920 pwm
->pid
= assuan_get_pid(ctx
);
922 return set_pinentry_strings(pwm
, 0);
925 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
)
930 n
= launch_pinentry(pwm
);
936 return assuan_command(pwm
, pwm
->pctx
, result
, cmd
);
939 static void pinentry_disconnect(pwm_t
*pwm
)
942 assuan_disconnect(pwm
->pctx
);
949 * Only called from a child process.
951 static void catchsig(int sig
)
955 if (gelapsed
++ >= gtimeout
) {
956 global_error
= pwmd_terminate_pinentry(gpwm
);
959 global_error
= GPG_ERR_TIMEOUT
;
972 * Borrowed from libassuan.
974 static char *percent_escape(const char *atext
)
976 const unsigned char *s
;
977 int len
= strlen(atext
) * 3 + 1;
978 char *buf
= (char *)xmalloc(len
), *p
= buf
;
983 for (s
=(const unsigned char *)atext
; *s
; s
++) {
985 sprintf (p
, "%%%02X", *s
);
997 static gpg_error_t
send_command(pwm_t
*pwm
, char **result
, const char *cmd
)
1000 return GPG_ERR_INV_ARG
;
1002 return assuan_command(pwm
, pwm
->ctx
, result
, cmd
);
1006 * Avoid sending the BYE command here. libassuan will close the file
1007 * descriptor and release the assuan context. Use pwmd_close() instead.
1009 gpg_error_t
pwmd_command(pwm_t
*pwm
, char **result
, const char *cmd
, ...)
1017 return GPG_ERR_INV_ARG
;
1022 * C99 allows the dst pointer to be null which will calculate the length
1023 * of the result and return it.
1025 len
= vsnprintf(NULL
, 0, cmd
, ap
);
1026 buf
= (char *)xmalloc(len
+ 1);
1027 len
= vsnprintf(buf
, len
+ 1, cmd
, ap
);
1029 error
= send_command(pwm
, result
, buf
);
1035 static gpg_error_t
do_getpin(pwm_t
*pwm
, char **result
)
1038 signal(SIGALRM
, catchsig
);
1043 return pinentry_command(pwm
, result
, "GETPIN");
1046 static gpg_error_t
getpin(pwm_t
*pwm
, char **result
, int *try_n
, int which
)
1048 int pin_try
= *try_n
;
1054 if (pin_try
== -1) {
1055 error
= set_pinentry_strings(pwm
, which
);
1058 pinentry_disconnect(pwm
);
1063 if (pwm
->pinentry_tries
-1 != pin_try
) {
1064 error
= set_pinentry_strings(pwm
, 1);
1067 pinentry_disconnect(pwm
);
1073 error
= do_getpin(pwm
, result
);
1076 * Since there was input cancel any timeout setting.
1081 if (error
== GPG_ERR_CANCELED
)
1082 return GPG_ERR_CANCELED
;
1084 if (pin_try
!= -1 && pin_try
--)
1088 pinentry_disconnect(pwm
);
1098 gpg_error_t
pwmd_open_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1102 #ifndef USE_PINENTRY
1103 return GPG_ERR_NOT_IMPLEMENTED
;
1106 if (!pwm
|| !pw
|| !pw
->filename
[0])
1107 return GPG_ERR_INV_ARG
;
1117 xfree(pwm
->filename
);
1119 pwm
->filename
= xstrdup(pw
->filename
);
1120 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1124 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1128 static gpg_error_t
do_open_command(pwm_t
*pwm
, const char *filename
, char *password
)
1130 char buf
[ASSUAN_LINELENGTH
];
1132 char *result
= NULL
;
1134 snprintf(buf
, sizeof(buf
), "OPEN %s %s", filename
, password
? password
: "");
1135 error
= send_command(pwm
, &result
, buf
);
1136 memset(buf
, 0, sizeof(buf
));
1138 if (error
&& result
)
1144 static int do_pwmd_open(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1145 int nb
, int timeout
)
1147 char *result
= NULL
;
1148 char *password
= NULL
;
1149 char path
[PATH_MAX
];
1154 if (!pwm
|| !filename
|| !*filename
) {
1155 *error
= GPG_ERR_INV_ARG
;
1160 pin_try
= pwm
->pinentry_tries
- 1;
1164 * Avoid calling pinentry if the password is cached on the server or if
1165 * this is a new file.
1167 *error
= pwmd_command(pwm
, &result
, "GETCONFIG data_directory");
1172 snprintf(path
, sizeof(path
), "%s/%s", result
, filename
);
1173 pwmd_free_result(result
);
1175 if (access(path
, R_OK
) == -1) {
1176 if (errno
== ENOENT
)
1180 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", filename
);
1182 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1183 if (pwm
->passfunc
) {
1184 password
= pwm
->passfunc(pwm
, pwm
->passdata
);
1190 * Get the password from pinentry.
1192 if (pwm
->use_pinentry
) {
1194 * Nonblocking is wanted. fork() then return a file descriptor
1195 * that the client can use to read() from.
1200 pwmd_nb_status_t pw
;
1202 if (pipe(p
) == -1) {
1203 *error
= gpg_error_from_syserror();
1212 strncpy(pw
.filename
, filename
, sizeof(pw
.filename
));
1213 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1223 *error
= getpin(pwm
, &password
, &pin_try
, 0);
1228 pinentry_disconnect(pwm
);
1230 if (gtimeout
&& gelapsed
>= gtimeout
)
1231 *error
= GPG_ERR_TIMEOUT
;
1234 write(p
[1], &pw
, sizeof(pw
));
1240 * Don't count the time it takes to open the file
1241 * which may have many iterations.
1243 signal(SIGALRM
, SIG_DFL
);
1244 *error
= do_open_command(pwm
, filename
, password
);
1247 signal(SIGALRM
, catchsig
);
1249 if (pwm
->pctx
&& *error
== EPWMD_BADKEY
) {
1251 goto getpin_nb_again
;
1253 goto getpin_nb_fail
;
1256 pinentry_disconnect(pwm
);
1258 write(p
[1], &pw
, sizeof(pw
));
1263 *error
= gpg_error_from_syserror();
1278 * Not using pinentry and the file was not found
1281 password
= pwm
->password
;
1290 *error
= do_open_command(pwm
, filename
, password
);
1293 * Keep the user defined password set with pwmd_setopt(). The password may
1294 * be needed later (pwmd_save()) depending on the pwmd file cache settings.
1296 if (password
&& password
!= pwm
->password
)
1300 if (*error
== EPWMD_BADKEY
) {
1301 if (pin_try
-- > 0 && !nb
) {
1302 *error
= pwmd_command(pwm
, &result
, "OPTION TITLE=%s",
1303 N_("Invalid password, please try again."));
1312 pinentry_disconnect(pwm
);
1320 xfree(pwm
->filename
);
1322 pwm
->filename
= xstrdup(filename
);
1326 * The file is cached or the file is a new file.
1329 return *error
? -1 : -2;
1331 return *error
? 1 : 0;
1334 gpg_error_t
pwmd_open(pwm_t
*pwm
, const char *filename
)
1338 do_pwmd_open(pwm
, &error
, filename
, 0, 0);
1342 int pwmd_open_nb(pwm_t
*pwm
, gpg_error_t
*error
, const char *filename
,
1345 #ifndef USE_PINENTRY
1346 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1349 return do_pwmd_open(pwm
, error
, filename
, 1, timeout
);
1354 static gpg_error_t
do_save_getpin(pwm_t
*pwm
, char **password
)
1358 char *result
= NULL
;
1362 error
= getpin(pwm
, &result
, &pin_try
, confirm
? 2 : 0);
1366 pinentry_disconnect(pwm
);
1379 if (strcmp(*password
, result
)) {
1382 pinentry_disconnect(pwm
);
1383 error
= EPWMD_BADKEY
;
1388 pinentry_disconnect(pwm
);
1393 static gpg_error_t
do_save_command(pwm_t
*pwm
, char *password
)
1395 char buf
[ASSUAN_LINELENGTH
];
1397 char *result
= NULL
;
1399 snprintf(buf
, sizeof(buf
), "SAVE %s", password
? password
: "");
1400 error
= send_command(pwm
, &result
, buf
);
1401 memset(&buf
, 0, sizeof(buf
));
1403 if (error
&& result
)
1409 gpg_error_t
pwmd_save_nb_finalize(pwm_t
*pwm
, pwmd_nb_status_t
*pw
)
1413 #ifndef USE_PINENTRY
1414 return GPG_ERR_NOT_IMPLEMENTED
;
1417 if (!pwm
|| !pw
|| !pw
->filename
[0])
1418 return GPG_ERR_INV_ARG
;
1422 memset(pw
, 0, sizeof(pwmd_nb_status_t
));
1426 static int do_pwmd_save(pwm_t
*pwm
, gpg_error_t
*error
, int nb
)
1428 char *result
= NULL
;
1429 char *password
= NULL
;
1432 *error
= GPG_ERR_INV_ARG
;
1436 if (pwm
->use_pinentry
|| pwm
->passfunc
) {
1437 *error
= pwmd_command(pwm
, &result
, "ISCACHED %s", pwm
->filename
);
1439 if (*error
== EPWMD_CACHE_NOT_FOUND
) {
1441 if (pwm
->use_pinentry
) {
1445 pwmd_nb_status_t pw
;
1447 if (pipe(p
) == -1) {
1448 *error
= gpg_error_from_syserror();
1457 strncpy(pw
.filename
, pwm
->filename
, sizeof(pw
.filename
));
1458 pw
.filename
[sizeof(pw
.filename
)-1] = 0;
1463 *error
= do_save_getpin(pwm
, &password
);
1464 } while (*error
== EPWMD_BADKEY
);
1468 pinentry_disconnect(pwm
);
1471 write(p
[1], &pw
, sizeof(pw
));
1476 *error
= do_save_command(pwm
, password
);
1477 pinentry_disconnect(pwm
);
1479 write(p
[1], &pw
, sizeof(pw
));
1484 *error
= gpg_error_from_syserror();
1496 *error
= do_save_getpin(pwm
, &password
);
1504 password
= (*pwm
->passfunc
)(pwm
, pwm
->passdata
);
1515 password
= pwm
->password
;
1517 *error
= do_save_command(pwm
, password
);
1519 if (password
&& password
!= pwm
->password
)
1523 return *error
? -1 : -2;
1525 return *error
? 1 : 0;
1528 int pwmd_save_nb(pwm_t
*pwm
, gpg_error_t
*error
)
1530 #ifndef USE_PINENTRY
1531 *error
= GPG_ERR_NOT_IMPLEMENTED
;
1534 return do_pwmd_save(pwm
, error
, 1);
1538 gpg_error_t
pwmd_save(pwm_t
*pwm
)
1542 do_pwmd_save(pwm
, &error
, 0);
1546 gpg_error_t
pwmd_setopt(pwm_t
*pwm
, pwmd_option_t opt
, ...)
1550 int n
= va_arg(ap
, int);
1554 gpg_error_t error
= 0;
1557 return GPG_ERR_INV_ARG
;
1562 case PWMD_OPTION_STATUS_FUNC
:
1563 pwm
->status_func
= va_arg(ap
, pwmd_status_fn
);
1565 case PWMD_OPTION_STATUS_DATA
:
1566 pwm
->status_data
= va_arg(ap
, void *);
1568 case PWMD_OPTION_PASSWORD_FUNC
:
1569 pwm
->passfunc
= va_arg(ap
, pwmd_password_fn
);
1571 case PWMD_OPTION_PASSWORD_DATA
:
1572 pwm
->passdata
= va_arg(ap
, void *);
1574 case PWMD_OPTION_PASSWORD
:
1575 arg1
= va_arg(ap
, char *);
1578 xfree(pwm
->password
);
1580 pwm
->password
= xstrdup(arg1
);
1583 case PWMD_OPTION_PINENTRY
:
1584 n
= va_arg(ap
, int);
1586 if (n
!= 0 && n
!= 1) {
1588 error
= GPG_ERR_INV_VALUE
;
1591 pwm
->use_pinentry
= n
;
1592 error
= pwmd_command(pwm
, &result
, "OPTION PINENTRY=%i",
1593 !pwm
->use_pinentry
);
1596 case PWMD_OPTION_PINENTRY_TRIES
:
1597 n
= va_arg(ap
, int);
1601 error
= GPG_ERR_INV_VALUE
;
1604 pwm
->pinentry_tries
= n
;
1606 case PWMD_OPTION_PINENTRY_PATH
:
1607 if (pwm
->pinentry_path
)
1608 xfree(pwm
->pinentry_path
);
1610 pwm
->pinentry_path
= xstrdup(va_arg(ap
, char *));
1612 case PWMD_OPTION_PINENTRY_TTY
:
1613 if (pwm
->pinentry_tty
)
1614 xfree(pwm
->pinentry_tty
);
1616 pwm
->pinentry_tty
= xstrdup(va_arg(ap
, char *));
1618 case PWMD_OPTION_PINENTRY_DISPLAY
:
1619 if (pwm
->pinentry_display
)
1620 xfree(pwm
->pinentry_display
);
1622 pwm
->pinentry_display
= xstrdup(va_arg(ap
, char *));
1624 case PWMD_OPTION_PINENTRY_TERM
:
1625 if (pwm
->pinentry_term
)
1626 xfree(pwm
->pinentry_term
);
1628 pwm
->pinentry_term
= xstrdup(va_arg(ap
, char *));
1630 case PWMD_OPTION_PINENTRY_TITLE
:
1633 pwm
->title
= percent_escape(va_arg(ap
, char *));
1635 case PWMD_OPTION_PINENTRY_PROMPT
:
1638 pwm
->prompt
= percent_escape(va_arg(ap
, char *));
1640 case PWMD_OPTION_PINENTRY_DESC
:
1643 pwm
->desc
= percent_escape(va_arg(ap
, char *));
1646 case PWMD_OPTION_PINENTRY
:
1647 case PWMD_OPTION_PINENTRY_TRIES
:
1648 case PWMD_OPTION_PINENTRY_PATH
:
1649 case PWMD_OPTION_PINENTRY_TTY
:
1650 case PWMD_OPTION_PINENTRY_DISPLAY
:
1651 case PWMD_OPTION_PINENTRY_TERM
:
1652 case PWMD_OPTION_PINENTRY_TITLE
:
1653 case PWMD_OPTION_PINENTRY_PROMPT
:
1654 case PWMD_OPTION_PINENTRY_DESC
:
1655 error
= GPG_ERR_NOT_IMPLEMENTED
;
1659 error
= GPG_ERR_NOT_IMPLEMENTED
;
1667 gpg_error_t
pwmd_assuan_ctx(pwm_t
*pwm
, assuan_context_t
*ctx
, int *fd
)
1670 return GPG_ERR_INV_ARG
;