From 4cd789f1782cc4cf41697b160a02ccf70f62e1a4 Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Sun, 9 Oct 2011 18:38:07 -0400 Subject: [PATCH] Added PWMD_OPTION_SSH_KEEPALIVE. The keepalive message is sent in pwmd_process() and only when non-zero. libssh2 only sends the keepalive when the previous keepalive time difference is greater than the set interval; so calling pwmd_process() more than once per second will not flood the SSH channel with keepalives. --- doc/libpwmd.3 | 5 ++++- doc/pwmc.1.in | 5 +++++ src/libpwmd.c | 19 +++++++++++++++++++ src/libpwmd.h.in | 4 ++++ src/pwmc.c | 12 ++++++++++++ src/ssh.c | 5 +++++ src/types.h | 1 + 7 files changed, 50 insertions(+), 1 deletion(-) diff --git a/doc/libpwmd.3 b/doc/libpwmd.3 index b7300662..e5a59630 100644 --- a/doc/libpwmd.3 +++ b/doc/libpwmd.3 @@ -150,7 +150,7 @@ libgpg-error returns an error code as a bitmask of an error source and the error .RI "enum \fBpwmd_pinentry_t\fP { \fBPWMD_PINENTRY_OPEN\fP, \fBPWMD_PINENTRY_OPEN_FAILED\fP, \fBPWMD_PINENTRY_SAVE\fP, \fBPWMD_PINENTRY_SAVE_CONFIRM\fP, \fBPWMD_PINENTRY_SAVE_FAILED\fP, \fBPWMD_PINENTRY_CONFIRM\fP, \fBPWMD_PINENTRY_USER\fP, \fBPWMD_PINENTRY_CLOSE\fP }" .br .ti -1c -.RI "enum \fBpwmd_option_t\fP { \fBPWMD_OPTION_PINENTRY_PATH\fP, \fBPWMD_OPTION_PINENTRY_TTY\fP, \fBPWMD_OPTION_PINENTRY_TERM\fP, \fBPWMD_OPTION_PINENTRY_DISPLAY\fP, \fBPWMD_OPTION_PINENTRY_ERROR\fP, \fBPWMD_OPTION_PINENTRY_PROMPT\fP, \fBPWMD_OPTION_PINENTRY_DESC\fP, \fBPWMD_OPTION_PINENTRY_LC_CTYPE\fP, \fBPWMD_OPTION_PINENTRY_LC_MESSAGES\fP, \fBPWMD_OPTION_PINENTRY_TIMEOUT\fP, \fBPWMD_OPTION_PINENTRY_TRIES\fP, \fBPWMD_OPTION_STATUS_CB\fP, \fBPWMD_OPTION_STATUS_DATA\fP, \fBPWMD_OPTION_KNOWNHOST_CB\fP, \fBPWMD_OPTION_KNOWNHOST_DATA\fP, \fBPWMD_OPTION_INQUIRE_TOTAL\fP, \fBPWMD_OPTION_LOCK_ON_OPEN\fP, \fBPWMD_OPTION_SSH_AGENT\fP, \fBPWMD_OPTION_NO_PINENTRY\fP, \fBPWMD_OPTION_LOCAL_PINENTRY\fP, \fBPWMD_OPTION_OVERRIDE_INQUIRE\fP, \fBPWMD_OPTION_SSH_TIMEOUT\fP }" +.RI "enum \fBpwmd_option_t\fP { \fBPWMD_OPTION_PINENTRY_PATH\fP, \fBPWMD_OPTION_PINENTRY_TTY\fP, \fBPWMD_OPTION_PINENTRY_TERM\fP, \fBPWMD_OPTION_PINENTRY_DISPLAY\fP, \fBPWMD_OPTION_PINENTRY_ERROR\fP, \fBPWMD_OPTION_PINENTRY_PROMPT\fP, \fBPWMD_OPTION_PINENTRY_DESC\fP, \fBPWMD_OPTION_PINENTRY_LC_CTYPE\fP, \fBPWMD_OPTION_PINENTRY_LC_MESSAGES\fP, \fBPWMD_OPTION_PINENTRY_TIMEOUT\fP, \fBPWMD_OPTION_PINENTRY_TRIES\fP, \fBPWMD_OPTION_STATUS_CB\fP, \fBPWMD_OPTION_STATUS_DATA\fP, \fBPWMD_OPTION_KNOWNHOST_CB\fP, \fBPWMD_OPTION_KNOWNHOST_DATA\fP, \fBPWMD_OPTION_INQUIRE_TOTAL\fP, \fBPWMD_OPTION_LOCK_ON_OPEN\fP, \fBPWMD_OPTION_SSH_AGENT\fP, \fBPWMD_OPTION_NO_PINENTRY\fP, \fBPWMD_OPTION_LOCAL_PINENTRY\fP, \fBPWMD_OPTION_OVERRIDE_INQUIRE\fP, \fBPWMD_OPTION_SSH_TIMEOUT\fP, \fBPWMD_OPTION_SSH_KEEPALIVE\fP }" .br .in -1c .SS "Functions" @@ -524,6 +524,9 @@ X11 port forwarding so a remote pinentry can use the local display. .TP \fB\fIPWMD_OPTION_SSH_TIMEOUT \fP\fP An int specifying a timeout in seconds before a blocking ssh function will timeout causing a command to fail. +.TP +\fB\fIPWMD_OPTION_SSH_KEEPALIVE \fP\fP +An int specifying an interval in seconds that \fBpwmd_process()\fP will send a keepalive message to the remote SSH server. .SS "enum \fBpwmd_pinentry_t\fP" .PP Local pinentry commands and not pwmd pinentry. These determine what prompt a local or fork()'ed pinentry uses. For use with \fBpwmd_getpin()\fP. diff --git a/doc/pwmc.1.in b/doc/pwmc.1.in index d0a7c84b..835d01ef 100644 --- a/doc/pwmc.1.in +++ b/doc/pwmc.1.in @@ -74,6 +74,11 @@ option to have priority. The number of seconds before a remote command will timeout. .TP +.I "\--ssh-keepalive " +The interval in seconds to send a keepalive message to the remote SSH server. +When the keepalive fails the connection will be closed. + +.TP .I "\--name, -n " Set the client name to the specified string. This string is what shows up in the diff --git a/src/libpwmd.c b/src/libpwmd.c index 92f5266d..870672cc 100644 --- a/src/libpwmd.c +++ b/src/libpwmd.c @@ -623,6 +623,7 @@ gpg_error_t pwmd_disconnect(pwm_t *pwm) return 0; } +/* Note that this should only be called when not in a command. */ gpg_error_t pwmd_process(pwm_t *pwm) { gpg_error_t rc = 0; @@ -635,6 +636,15 @@ gpg_error_t pwmd_process(pwm_t *pwm) else if (!pwm->ctx) return FINISH(GPG_ERR_INV_STATE); +#ifdef WITH_TCP + if (pwm->tcp_conn) { + int n = libssh2_keepalive_send(pwm->tcp_conn->session, NULL); + + if (n) + return FINISH(GPG_ERR_ETIMEDOUT); + } +#endif + FD_ZERO(&fds); FD_SET(pwm->fd, &fds); n = select(pwm->fd+1, &fds, NULL, NULL, &tv); @@ -1018,6 +1028,15 @@ gpg_error_t pwmd_setopt(pwm_t *pwm, pwmd_option_t opt, ...) } break; + case PWMD_OPTION_SSH_KEEPALIVE: + pwm->keepalive_interval = va_arg(ap, int); + + if (pwm->keepalive_interval < 0) { + pwm->keepalive_interval = 0; + rc = GPG_ERR_INV_VALUE; + } + + break; #else case PWMD_OPTION_KNOWNHOST_CB: case PWMD_OPTION_KNOWNHOST_DATA: diff --git a/src/libpwmd.h.in b/src/libpwmd.h.in index 447f4fa8..1d71a638 100644 --- a/src/libpwmd.h.in +++ b/src/libpwmd.h.in @@ -475,6 +475,10 @@ typedef enum { /*! An int specifying a timeout in seconds before a blocking ssh function * will timeout causing a command to fail. */ PWMD_OPTION_SSH_TIMEOUT, + + /*! An int specifying an interval in seconds that \ref pwmd_process() will + * send a keepalive message to the remote SSH server. */ + PWMD_OPTION_SSH_KEEPALIVE, } pwmd_option_t; diff --git a/src/pwmc.c b/src/pwmc.c index f37eebb6..a96222dc 100644 --- a/src/pwmc.c +++ b/src/pwmc.c @@ -138,6 +138,8 @@ static void usage(const char *pn, int status) " the ssh knownhosts file to use (~/.ssh/knownhosts)\n" " --ssh-timeout \n" " timeout before a remote command fails (0=disabled, default)\n" + " --ssh-keepalive \n" + " interval to send a keepalive to the remote server (0=disabled, default)\n" #endif " --no-status\n" " disable showing of status messages from the server\n" @@ -1096,6 +1098,7 @@ int main(int argc, char *argv[]) #ifdef WITH_TCP int use_ssh_agent = 1; long ssh_timeout = 0; + int ssh_keepalive = 0; #endif int lock_on_open = 1; char *url = NULL; @@ -1105,6 +1108,7 @@ int main(int argc, char *argv[]) enum { #ifdef WITH_TCP OPT_USE_SSH_AGENT, OPT_IDENTITY, OPT_KNOWNHOSTS, OPT_SSH_TIMEOUT, + OPT_SSH_KEEPALIVE, #endif OPT_URL, OPT_LOCAL, OPT_FORCE_SAVE, OPT_TTYNAME, OPT_TTYTYPE, OPT_DISPLAY, OPT_LC_CTYPE, OPT_LC_MESSAGES, OPT_TIMEOUT, OPT_TRIES, @@ -1123,6 +1127,7 @@ int main(int argc, char *argv[]) { "identity", 1, 0, 'i' }, { "knownhosts", 1, 0, 'k' }, { "ssh-timeout", 1, 0, 0 }, + { "ssh-keepalive", 1, 0, 0 }, #endif { "url", 1, 0, 0 }, { "local-pinentry", 0, 0 }, @@ -1192,6 +1197,9 @@ int main(int argc, char *argv[]) case OPT_SSH_TIMEOUT: ssh_timeout = strtol(optarg, NULL, 10); break; + case OPT_SSH_KEEPALIVE: + ssh_keepalive = strtol(optarg, NULL, 10); + break; #endif case OPT_KEYPARAMS: keyparams = optarg; @@ -1396,6 +1404,10 @@ int main(int argc, char *argv[]) rc = pwmd_setopt(pwm, PWMD_OPTION_SSH_TIMEOUT, ssh_timeout); if (rc) goto done; + + rc = pwmd_setopt(pwm, PWMD_OPTION_SSH_KEEPALIVE, ssh_keepalive); + if (rc) + goto done; } #endif fprintf(stderr, N_("Connecting ...\n")); diff --git a/src/ssh.c b/src/ssh.c index d8306cbf..fabe0c34 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -627,6 +627,7 @@ static gpg_error_t _setup_ssh_shell(pwm_t *pwm) return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc; } + libssh2_keepalive_config(pwm->tcp_conn->session, 0, pwm->keepalive_interval); return ssh_connect_finalize(pwm); } @@ -662,6 +663,10 @@ gpg_error_t _setup_ssh_session(pwm_t *pwm) if (!pwm->tcp_conn->session) { pwm->tcp_conn->session = libssh2_session_init_ex(ssh_malloc, ssh_free, ssh_realloc, NULL); + if (!pwm->tcp_conn->session) { + rc = gpg_error_from_errno(ENOMEM); + goto fail; + } libssh2_session_flag(pwm->tcp_conn->session, LIBSSH2_FLAG_COMPRESS, 1); libssh2_session_set_timeout(pwm->tcp_conn->session, diff --git a/src/types.h b/src/types.h index e593e6a8..b171c3aa 100644 --- a/src/types.h +++ b/src/types.h @@ -84,6 +84,7 @@ struct pwm_s { void *kh_data; int use_agent; int ssh_timeout; + int keepalive_interval; #endif int fd; #ifdef WITH_PINENTRY -- 2.11.4.GIT