From 3bdcb513c719d56088f29f7916a5cf794e5a8a5a Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Mon, 28 Sep 2015 21:47:04 -0400 Subject: [PATCH] Handle cancellation in pwmd_connect (). --- src/libpwmd.c | 59 +++++++++++++++++++++++++++++++++---------- src/ssh.c | 81 +++++++++++++++++++++++++++++++++++++++++------------------ src/ssh.h | 3 +++ src/tls.c | 16 ++++++------ src/types.h | 1 + 5 files changed, 114 insertions(+), 46 deletions(-) diff --git a/src/libpwmd.c b/src/libpwmd.c index 8967430f..393ac2cb 100644 --- a/src/libpwmd.c +++ b/src/libpwmd.c @@ -330,13 +330,10 @@ tcp_connect_common (pwm_t * pwm) continue; } - if (pwm->socket_timeout) + if (fcntl (pwm->fd, F_SETFL, O_NONBLOCK) == -1) { - if (fcntl (pwm->fd, F_SETFL, O_NONBLOCK) == -1) - { - rc = gpg_error_from_syserror (); - break; - } + rc = gpg_error_from_syserror (); + break; } if (connect (pwm->fd, pwm->tcp->addr->ai_addr, @@ -345,8 +342,9 @@ tcp_connect_common (pwm_t * pwm) : sizeof (struct sockaddr)) == -1) { int n; - struct timeval tv = { pwm->socket_timeout, 0 }; + struct timeval tv; fd_set wfds; + unsigned elapsed = 0; rc = gpg_error_from_syserror (); if (gpg_err_code (rc) != GPG_ERR_EINPROGRESS) @@ -358,12 +356,22 @@ tcp_connect_common (pwm_t * pwm) continue; } +again: + tv.tv_sec = 1; + tv.tv_usec = 0; FD_ZERO (&wfds); FD_SET (pwm->fd, &wfds); n = select (pwm->fd+1, NULL, &wfds, NULL, &tv); rc = 0; - if (!n) - rc = gpg_error (GPG_ERR_ETIMEDOUT); + if (!n || pwm->cancel) + { + if (pwm->cancel) + rc = gpg_error (GPG_ERR_CANCELED); + else if (++elapsed >= pwm->socket_timeout) + rc = gpg_error (GPG_ERR_ETIMEDOUT); + else + goto again; + } else if (n != -1) { socklen_t len = sizeof(int); @@ -380,7 +388,8 @@ tcp_connect_common (pwm_t * pwm) close (pwm->fd); pwm->fd = -1; if (pwm->tcp->addr == pwm->tcp->addrs->ai_next - || gpg_err_code (rc) == GPG_ERR_ETIMEDOUT) + || gpg_err_code (rc) == GPG_ERR_ETIMEDOUT + || pwm->cancel) return rc; } else @@ -390,7 +399,7 @@ tcp_connect_common (pwm_t * pwm) break; } - if (!rc && pwm->socket_timeout) + if (!rc) if (fcntl (pwm->fd, F_SETFL, 0) == -1) rc = gpg_error_from_syserror (); @@ -398,6 +407,12 @@ tcp_connect_common (pwm_t * pwm) } #endif +static void +command_start (pwm_t *pwm) +{ + pwm->cancel = 0; +} + gpg_error_t pwmd_connect (pwm_t * pwm, const char *url, ...) { @@ -413,6 +428,8 @@ pwmd_connect (pwm_t * pwm, const char *url, ...) return rc; } + command_start (pwm); + if (!(pwm->opts & OPT_SIGPIPE)) signal (SIGPIPE, SIG_IGN); @@ -490,7 +507,6 @@ pwmd_connect (pwm_t * pwm, const char *url, ...) pwmd_free (host); pwmd_free (username); pwm->local_pinentry = 1; - return FINISH (rc); #endif } else if (!strncmp (p, "tls://", 6) || !strncmp (p, "tls6://", 7) || @@ -566,10 +582,12 @@ pwmd_connect (pwm_t * pwm, const char *url, ...) pwmd_free (host); pwm->local_pinentry = 1; - return FINISH (rc); #endif } + if (!rc) + pwm->connected = 1; + return FINISH (rc); } @@ -587,6 +605,7 @@ disconnect (pwm_t * pwm) #endif pwm->ctx = NULL; pwm->fd = -1; + pwm->connected = 0; } void @@ -744,6 +763,8 @@ pwmd_password (pwm_t * pwm, const char *keyword, char **data, size_t * size) char *password = NULL, *newpass = NULL; int error = 0; + command_start (pwm); + if (data) *data = NULL; @@ -1011,6 +1032,8 @@ pwmd_disconnect (pwm_t * pwm) if (!pwm) return FINISH (GPG_ERR_INV_ARG); + command_start (pwm); + if (pwm->fd == -1) return FINISH (GPG_ERR_INV_STATE); @@ -1145,6 +1168,8 @@ pwmd_command_ap (pwm_t * pwm, char **result, size_t * rlen, size_t len; va_list ap2; + command_start (pwm); + if (result) *result = NULL; @@ -1332,6 +1357,7 @@ pwmd_open (pwm_t * pwm, const char *filename, pwmd_inquire_cb_t cb, if (!pwm->ctx) return FINISH (GPG_ERR_INV_STATE); + command_start (pwm); rc = disable_pinentry (pwm, &no_pinentry); if (!rc && !no_pinentry) rc = send_pinentry_options (pwm); @@ -1375,6 +1401,7 @@ do_pwmd_save_passwd (pwm_t * pwm, const char *args, pwmd_inquire_cb_t cb, if (!pwm->ctx) return FINISH (GPG_ERR_INV_STATE); + command_start (pwm); rc = disable_pinentry (pwm, NULL); if (!rc) rc = pwmd_command (pwm, NULL, NULL, cb, data, @@ -1406,6 +1433,7 @@ pwmd_get_set_opt (pwm_t *pwm, pwmd_option_t opt, int get, va_list ap) if (!pwm) return GPG_ERR_INV_ARG; + command_start (pwm); switch (opt) { case PWMD_OPTION_SERVER_VERSION: @@ -1928,6 +1956,7 @@ pwmd_getpin (pwm_t * pwm, const char *filename, char **result, #ifndef WITH_PINENTRY return FINISH (GPG_ERR_NOT_IMPLEMENTED); #else + command_start (pwm); gpg_error_t rc = _pwmd_getpin (pwm, filename, result, len, which); return FINISH (rc); @@ -2004,6 +2033,10 @@ pwmd_cancel (pwm_t *pwm) if (pwm->fd == -1) return FINISH (GPG_ERR_INV_STATE); + /* Can only cancel the connection for the time being. */ + if (pwm->connected) + return FINISH (GPG_ERR_INV_STATE); + pwm->cancel = 1; return 0; } diff --git a/src/ssh.c b/src/ssh.c index 82f6bfc4..f58f5336 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -44,6 +44,28 @@ #include "misc.h" #include "ssh.h" +#define INTERVAL 50000 +#define TEST_TIMEOUT(pwm, ssh, n) do { \ + if (pwm->cancel) \ + n = LIBSSH2_ERROR_TIMEOUT; \ + else if (n == LIBSSH2_ERROR_EAGAIN) \ + { \ + if (ssh->elapsed.tv_usec + INTERVAL >= 1000000L) \ + { \ + ssh->elapsed.tv_sec++; \ + ssh->elapsed.tv_usec = 0; \ + } \ + if (ssh->elapsed.tv_sec >= pwm->socket_timeout) \ + n = LIBSSH2_ERROR_TIMEOUT; \ + else \ + { \ + struct timeval tv = { 0, INTERVAL }; \ + select (0, NULL, NULL, NULL, &tv); \ + ssh->elapsed.tv_usec += INTERVAL; \ + } \ + } \ +} while (0); + static gpg_error_t ssh_connect_finalize (pwm_t * pwm); static gpg_error_t _setup_ssh_init (pwm_t * pwm); static gpg_error_t _setup_ssh_authlist (pwm_t * pwm); @@ -124,11 +146,11 @@ write_hook_ssh (struct ssh_s * ssh, assuan_fd_t fd, const void *data, /* libassuan cannot handle EAGAIN when doing writes. */ do { + struct timeval tv = { 0, INTERVAL }; + ret = libssh2_channel_write (ssh->channel, data, len); if (ret == LIBSSH2_ERROR_EAGAIN) - { - usleep (50000); - } + select (0, NULL, NULL, NULL, &tv); } while (ret == LIBSSH2_ERROR_EAGAIN); @@ -307,15 +329,16 @@ _setup_ssh_agent (pwm_t * pwm) { n = libssh2_agent_userauth (pwm->tcp->ssh->agent, pwm->tcp->ssh->username, identity); - if (n == LIBSSH2_ERROR_EAGAIN) - usleep (50000); + TEST_TIMEOUT (pwm, pwm->tcp->ssh, n); } while (n == LIBSSH2_ERROR_EAGAIN); if (!n) break; - if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED) + if (n == LIBSSH2_ERROR_TIMEOUT) + return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT; + else if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED) return GPG_ERR_ASS_SERVER_START; identity_prev = identity; @@ -345,8 +368,7 @@ _setup_ssh_auth (pwm_t * pwm) pwm->tcp->ssh->username, pwm->tcp->ssh->identity_pub, pwm->tcp->ssh->identity, NULL); - if (n == LIBSSH2_ERROR_EAGAIN) - usleep (50000); + TEST_TIMEOUT (pwm, pwm->tcp->ssh, n); } while (n == LIBSSH2_ERROR_EAGAIN); @@ -359,7 +381,7 @@ _setup_ssh_auth (pwm_t * pwm) case LIBSSH2_ERROR_FILE: return GPG_ERR_UNUSABLE_SECKEY; case LIBSSH2_ERROR_TIMEOUT: - return GPG_ERR_TIMEOUT; + return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT; case LIBSSH2_ERROR_AUTHENTICATION_FAILED: return GPG_ERR_BAD_SECKEY; default: @@ -382,14 +404,17 @@ _setup_ssh_authlist (pwm_t * pwm) pwm->tcp->ssh->username, strlen (pwm->tcp->ssh->username)); n = libssh2_session_last_errno (pwm->tcp->ssh->session); - if (n == LIBSSH2_ERROR_EAGAIN) - usleep (50000); + TEST_TIMEOUT (pwm, pwm->tcp->ssh, n); } while (!userauth && n == LIBSSH2_ERROR_EAGAIN); - if (n) - return n == - LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT; + if (n && n != LIBSSH2_ERROR_EAGAIN) + { + if (pwm->cancel) + return GPG_ERR_CANCELED; + return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT + : GPG_ERR_ASSUAN_SERVER_FAULT; + } if (!userauth) return GPG_ERR_BAD_PIN_METHOD; @@ -603,8 +628,7 @@ _setup_ssh_channel (pwm_t * pwm) libssh2_channel_open_session (pwm->tcp->ssh->session); n = libssh2_session_last_errno (pwm->tcp->ssh->session); - if (n == LIBSSH2_ERROR_EAGAIN) - usleep (50000); + TEST_TIMEOUT (pwm, pwm->tcp->ssh, n); } while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN); @@ -612,7 +636,9 @@ _setup_ssh_channel (pwm_t * pwm) { rc = GPG_ERR_ASS_SERVER_START; free_tcp (pwm); - return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc; + if (pwm->cancel) + return GPG_ERR_CANCELED; + return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc; } return _setup_ssh_shell (pwm); @@ -627,15 +653,16 @@ _setup_ssh_shell (pwm_t * pwm) do { n = libssh2_channel_shell (pwm->tcp->ssh->channel); - if (n == LIBSSH2_ERROR_EAGAIN) - usleep (50000); + TEST_TIMEOUT (pwm, pwm->tcp->ssh, n); } while (n == LIBSSH2_ERROR_EAGAIN); if (n) { rc = GPG_ERR_ASS_SERVER_START; - return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc; + if (pwm->cancel) + return GPG_ERR_CANCELED; + return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc; } return ssh_connect_finalize (pwm); @@ -644,6 +671,7 @@ _setup_ssh_shell (pwm_t * pwm) static gpg_error_t ssh_connect_finalize (pwm_t * pwm) { + libssh2_session_set_blocking (pwm->tcp->ssh->session, 1); return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0); } @@ -655,14 +683,17 @@ _setup_ssh_init (pwm_t * pwm) do { n = libssh2_session_handshake (pwm->tcp->ssh->session, pwm->fd); - if (n == LIBSSH2_ERROR_EAGAIN) - usleep (50000); + TEST_TIMEOUT (pwm, pwm->tcp->ssh, n); } while (n == LIBSSH2_ERROR_EAGAIN); if (n) - return n == - LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT; + { + if (pwm->cancel) + return GPG_ERR_CANCELED; + return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT + : GPG_ERR_ASSUAN_SERVER_FAULT; + } return verify_hostkey (pwm); } @@ -690,7 +721,7 @@ _setup_ssh_session (pwm_t * pwm) if (!pwm->tcp->ssh->session) return GPG_ERR_ENOMEM; - libssh2_session_set_blocking (pwm->tcp->ssh->session, 1); + libssh2_session_set_blocking (pwm->tcp->ssh->session, 0); rc = _setup_ssh_init (pwm); if (!rc) return rc; diff --git a/src/ssh.h b/src/ssh.h index 138ee2d6..ef3fc45b 100644 --- a/src/ssh.h +++ b/src/ssh.h @@ -21,6 +21,8 @@ #define SSH_H #include +#include +#include struct ssh_s { @@ -36,6 +38,7 @@ struct ssh_s char *hostkey; int timeout; int *fd; + struct timeval elapsed; }; void _free_ssh_conn (struct ssh_s *conn); diff --git a/src/tls.c b/src/tls.c index 2624a24d..2f8948c3 100644 --- a/src/tls.c +++ b/src/tls.c @@ -39,12 +39,12 @@ #define DEFAULT_TLS_PRIORITY "SECURE256:SECURE192:SECURE128:-VERS-SSL3.0" #define WAIT_INTERVAL 50000 -#define TEST_TIMEOUT(timeout, ret, start, rc) \ +#define TEST_TIMEOUT(pwm, timeout, ret, start, rc) \ do { \ - if (ret == GNUTLS_E_AGAIN) \ + if (ret == GNUTLS_E_AGAIN || pwm->cancel) \ { \ time_t now = time (NULL); \ - if (timeout && now - start >= timeout) \ + if (pwm->cancel || (timeout && now - start >= timeout)) \ { \ rc = gpg_error (GPG_ERR_ETIMEDOUT); \ break; \ @@ -81,7 +81,7 @@ tls_free (pwm_t *pwm) gpg_error_t rc = 0; ret = gnutls_bye (tls->session, GNUTLS_SHUT_WR); - TEST_TIMEOUT (tls->timeout, ret, start, rc); + TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc); if (rc) break; } @@ -137,7 +137,7 @@ read_hook_tls (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len) { ret = gnutls_handshake (tls->session); rc = 0; - TEST_TIMEOUT (tls->timeout, ret, start, rc); + TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc); if (rc) { close (*tls->fd); @@ -152,7 +152,7 @@ read_hook_tls (pwm_t *pwm, assuan_fd_t fd, void *data, size_t len) continue; } - TEST_TIMEOUT (tls->timeout, ret, start, rc); + TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc); if (rc) { close (*tls->fd); @@ -192,7 +192,7 @@ write_hook_tls (pwm_t *pwm, assuan_fd_t fd, const void *data, size_t len) gpg_error_t rc = 0; ret = gnutls_record_send (tls->session, data, len); - TEST_TIMEOUT (tls->timeout, ret, start, rc); + TEST_TIMEOUT (pwm, tls->timeout, ret, start, rc); if (rc) { close (*tls->fd); @@ -468,7 +468,7 @@ tls_init (pwm_t * pwm) { ret = gnutls_handshake (pwm->tcp->tls->session); rc = 0; - TEST_TIMEOUT (pwm->tcp->tls->timeout, ret, start, rc); + TEST_TIMEOUT (pwm, pwm->tcp->tls->timeout, ret, start, rc); if (rc) goto fail; } diff --git a/src/types.h b/src/types.h index 3c61e601..3a2a35d8 100644 --- a/src/types.h +++ b/src/types.h @@ -91,6 +91,7 @@ struct pwm_s int socket_timeout; int fd; int cancel; + int connected; #ifdef WITH_PINENTRY pid_t pinentry_pid; // for local pinentry timeouts assuan_context_t pctx; -- 2.11.4.GIT