diff options
author | Ben Lindstrom <mouring@eviladmin.org> | 2001-04-13 23:28:01 +0000 |
---|---|---|
committer | Ben Lindstrom <mouring@eviladmin.org> | 2001-04-13 23:28:01 +0000 |
commit | 5744dc421d035c701b6660a58bed0d038c211375 (patch) | |
tree | a7c8df98b56a37c9be2fb3e33893e90424a85379 | |
parent | 402b3319456c1f0da0822319c3813c68e155726d (diff) |
- beck@cvs.openbsd.org 2001/04/13 22:46:54
[channels.c channels.h servconf.c servconf.h serverloop.c sshd.8]
Add options ClientAliveInterval and ClientAliveCountMax to sshd.
This gives the ability to do a "keepalive" via the encrypted channel
which can't be spoofed (unlike TCP keepalives). Useful for when you want
to use ssh connections to authenticate people for something, and know
relatively quickly when they are no longer authenticated. Disabled
by default (of course). ok markus@
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | channels.c | 37 | ||||
-rw-r--r-- | channels.h | 4 | ||||
-rw-r--r-- | servconf.c | 20 | ||||
-rw-r--r-- | servconf.h | 11 | ||||
-rw-r--r-- | serverloop.c | 64 | ||||
-rw-r--r-- | sshd.8 | 27 |
7 files changed, 162 insertions, 12 deletions
@@ -2,6 +2,15 @@ | |||
2 | - Sync with OpenBSD glob.c, strlcat.c and vis.c changes | 2 | - Sync with OpenBSD glob.c, strlcat.c and vis.c changes |
3 | - Cygwin sftp/sftp-server binary mode patch from Corinna Vinschen | 3 | - Cygwin sftp/sftp-server binary mode patch from Corinna Vinschen |
4 | <vinschen@redhat.com> | 4 | <vinschen@redhat.com> |
5 | - OpenBSD CVS Sync | ||
6 | - beck@cvs.openbsd.org 2001/04/13 22:46:54 | ||
7 | [channels.c channels.h servconf.c servconf.h serverloop.c sshd.8] | ||
8 | Add options ClientAliveInterval and ClientAliveCountMax to sshd. | ||
9 | This gives the ability to do a "keepalive" via the encrypted channel | ||
10 | which can't be spoofed (unlike TCP keepalives). Useful for when you want | ||
11 | to use ssh connections to authenticate people for something, and know | ||
12 | relatively quickly when they are no longer authenticated. Disabled | ||
13 | by default (of course). ok markus@ | ||
5 | 14 | ||
6 | 20010413 | 15 | 20010413 |
7 | - OpenBSD CVS Sync | 16 | - OpenBSD CVS Sync |
@@ -5054,4 +5063,4 @@ | |||
5054 | - Wrote replacements for strlcpy and mkdtemp | 5063 | - Wrote replacements for strlcpy and mkdtemp |
5055 | - Released 1.0pre1 | 5064 | - Released 1.0pre1 |
5056 | 5065 | ||
5057 | $Id: ChangeLog,v 1.1109 2001/04/13 14:28:42 djm Exp $ | 5066 | $Id: ChangeLog,v 1.1110 2001/04/13 23:28:01 mouring Exp $ |
diff --git a/channels.c b/channels.c index a1aa937ae..f4f2c4942 100644 --- a/channels.c +++ b/channels.c | |||
@@ -40,7 +40,7 @@ | |||
40 | */ | 40 | */ |
41 | 41 | ||
42 | #include "includes.h" | 42 | #include "includes.h" |
43 | RCSID("$OpenBSD: channels.c,v 1.106 2001/04/11 13:56:13 markus Exp $"); | 43 | RCSID("$OpenBSD: channels.c,v 1.107 2001/04/13 22:46:52 beck Exp $"); |
44 | 44 | ||
45 | #include <openssl/rsa.h> | 45 | #include <openssl/rsa.h> |
46 | #include <openssl/dsa.h> | 46 | #include <openssl/dsa.h> |
@@ -1843,6 +1843,41 @@ channel_still_open() | |||
1843 | return 0; | 1843 | return 0; |
1844 | } | 1844 | } |
1845 | 1845 | ||
1846 | /* Returns the id of an open channel suitable for keepaliving */ | ||
1847 | |||
1848 | int | ||
1849 | channel_find_open() | ||
1850 | { | ||
1851 | u_int i; | ||
1852 | for (i = 0; i < channels_alloc; i++) | ||
1853 | switch (channels[i].type) { | ||
1854 | case SSH_CHANNEL_CLOSED: | ||
1855 | continue; | ||
1856 | case SSH_CHANNEL_LARVAL: | ||
1857 | case SSH_CHANNEL_DYNAMIC: | ||
1858 | case SSH_CHANNEL_AUTH_SOCKET: | ||
1859 | case SSH_CHANNEL_CONNECTING: /* XXX ??? */ | ||
1860 | case SSH_CHANNEL_FREE: | ||
1861 | case SSH_CHANNEL_X11_LISTENER: | ||
1862 | case SSH_CHANNEL_PORT_LISTENER: | ||
1863 | case SSH_CHANNEL_RPORT_LISTENER: | ||
1864 | case SSH_CHANNEL_OPENING: | ||
1865 | case SSH_CHANNEL_OPEN: | ||
1866 | case SSH_CHANNEL_X11_OPEN: | ||
1867 | return i; | ||
1868 | case SSH_CHANNEL_INPUT_DRAINING: | ||
1869 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
1870 | if (!compat13) | ||
1871 | fatal("cannot happen: OUT_DRAIN"); | ||
1872 | return i; | ||
1873 | default: | ||
1874 | fatal("channel_find_open: bad channel type %d", channels[i].type); | ||
1875 | /* NOTREACHED */ | ||
1876 | } | ||
1877 | return -1; | ||
1878 | } | ||
1879 | |||
1880 | |||
1846 | /* | 1881 | /* |
1847 | * Returns a message describing the currently open forwarded connections, | 1882 | * Returns a message describing the currently open forwarded connections, |
1848 | * suitable for sending to the client. The message contains crlf pairs for | 1883 | * suitable for sending to the client. The message contains crlf pairs for |
diff --git a/channels.h b/channels.h index 23e6ece83..bf70a8f21 100644 --- a/channels.h +++ b/channels.h | |||
@@ -32,7 +32,7 @@ | |||
32 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 32 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
33 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 33 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | */ | 34 | */ |
35 | /* RCSID("$OpenBSD: channels.h,v 1.30 2001/04/07 08:55:17 markus Exp $"); */ | 35 | /* RCSID("$OpenBSD: channels.h,v 1.31 2001/04/13 22:46:53 beck Exp $"); */ |
36 | 36 | ||
37 | #ifndef CHANNELS_H | 37 | #ifndef CHANNELS_H |
38 | #define CHANNELS_H | 38 | #define CHANNELS_H |
@@ -307,4 +307,6 @@ int channel_connect_to(const char *host, u_short host_port); | |||
307 | int channel_connect_by_listen_adress(u_short listen_port); | 307 | int channel_connect_by_listen_adress(u_short listen_port); |
308 | int x11_connect_display(void); | 308 | int x11_connect_display(void); |
309 | 309 | ||
310 | int channel_find_open(void); | ||
311 | |||
310 | #endif | 312 | #endif |
diff --git a/servconf.c b/servconf.c index f3d5068c0..f978c632b 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -10,7 +10,7 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include "includes.h" | 12 | #include "includes.h" |
13 | RCSID("$OpenBSD: servconf.c,v 1.76 2001/04/12 20:09:37 stevesk Exp $"); | 13 | RCSID("$OpenBSD: servconf.c,v 1.77 2001/04/13 22:46:53 beck Exp $"); |
14 | 14 | ||
15 | #ifdef KRB4 | 15 | #ifdef KRB4 |
16 | #include <krb.h> | 16 | #include <krb.h> |
@@ -99,6 +99,8 @@ initialize_server_options(ServerOptions *options) | |||
99 | options->max_startups = -1; | 99 | options->max_startups = -1; |
100 | options->banner = NULL; | 100 | options->banner = NULL; |
101 | options->reverse_mapping_check = -1; | 101 | options->reverse_mapping_check = -1; |
102 | options->client_alive_interval = -1; | ||
103 | options->client_alive_count_max = -1; | ||
102 | } | 104 | } |
103 | 105 | ||
104 | void | 106 | void |
@@ -201,6 +203,10 @@ fill_default_server_options(ServerOptions *options) | |||
201 | options->max_startups_begin = options->max_startups; | 203 | options->max_startups_begin = options->max_startups; |
202 | if (options->reverse_mapping_check == -1) | 204 | if (options->reverse_mapping_check == -1) |
203 | options->reverse_mapping_check = 0; | 205 | options->reverse_mapping_check = 0; |
206 | if (options->client_alive_interval == -1) | ||
207 | options->client_alive_interval = 0; | ||
208 | if (options->client_alive_count_max == -1) | ||
209 | options->client_alive_count_max = 3; | ||
204 | } | 210 | } |
205 | 211 | ||
206 | /* Keyword tokens. */ | 212 | /* Keyword tokens. */ |
@@ -225,7 +231,8 @@ typedef enum { | |||
225 | sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, | 231 | sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, |
226 | sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups, | 232 | sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups, |
227 | sBanner, sReverseMappingCheck, sHostbasedAuthentication, | 233 | sBanner, sReverseMappingCheck, sHostbasedAuthentication, |
228 | sHostbasedUsesNameFromPacketOnly | 234 | sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, |
235 | sClientAliveCountMax | ||
229 | } ServerOpCodes; | 236 | } ServerOpCodes; |
230 | 237 | ||
231 | /* Textual representation of the tokens. */ | 238 | /* Textual representation of the tokens. */ |
@@ -289,6 +296,8 @@ static struct { | |||
289 | { "maxstartups", sMaxStartups }, | 296 | { "maxstartups", sMaxStartups }, |
290 | { "banner", sBanner }, | 297 | { "banner", sBanner }, |
291 | { "reversemappingcheck", sReverseMappingCheck }, | 298 | { "reversemappingcheck", sReverseMappingCheck }, |
299 | { "clientaliveinterval", sClientAliveInterval }, | ||
300 | { "clientalivecountmax", sClientAliveCountMax }, | ||
292 | { NULL, 0 } | 301 | { NULL, 0 } |
293 | }; | 302 | }; |
294 | 303 | ||
@@ -792,7 +801,12 @@ parse_flag: | |||
792 | case sBanner: | 801 | case sBanner: |
793 | charptr = &options->banner; | 802 | charptr = &options->banner; |
794 | goto parse_filename; | 803 | goto parse_filename; |
795 | 804 | case sClientAliveInterval: | |
805 | intptr = &options->client_alive_interval; | ||
806 | goto parse_int; | ||
807 | case sClientAliveCountMax: | ||
808 | intptr = &options->client_alive_count_max; | ||
809 | goto parse_int; | ||
796 | default: | 810 | default: |
797 | fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", | 811 | fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", |
798 | filename, linenum, arg, opcode); | 812 | filename, linenum, arg, opcode); |
diff --git a/servconf.h b/servconf.h index 9b3a60f08..4c02c0f52 100644 --- a/servconf.h +++ b/servconf.h | |||
@@ -11,7 +11,7 @@ | |||
11 | * called by a name other than "ssh" or "Secure Shell". | 11 | * called by a name other than "ssh" or "Secure Shell". |
12 | */ | 12 | */ |
13 | 13 | ||
14 | /* RCSID("$OpenBSD: servconf.h,v 1.40 2001/04/12 19:15:25 markus Exp $"); */ | 14 | /* RCSID("$OpenBSD: servconf.h,v 1.41 2001/04/13 22:46:53 beck Exp $"); */ |
15 | 15 | ||
16 | #ifndef SERVCONF_H | 16 | #ifndef SERVCONF_H |
17 | #define SERVCONF_H | 17 | #define SERVCONF_H |
@@ -115,6 +115,15 @@ typedef struct { | |||
115 | int max_startups; | 115 | int max_startups; |
116 | char *banner; /* SSH-2 banner message */ | 116 | char *banner; /* SSH-2 banner message */ |
117 | int reverse_mapping_check; /* cross-check ip and dns */ | 117 | int reverse_mapping_check; /* cross-check ip and dns */ |
118 | int client_alive_interval; /* | ||
119 | * poke the client this often to | ||
120 | * see if it's still there | ||
121 | */ | ||
122 | int client_alive_count_max; /* | ||
123 | *If the client is unresponsive | ||
124 | * for this many intervals, above | ||
125 | * diconnect the session | ||
126 | */ | ||
118 | 127 | ||
119 | } ServerOptions; | 128 | } ServerOptions; |
120 | /* | 129 | /* |
diff --git a/serverloop.c b/serverloop.c index d6b360d9a..5a5b1e37f 100644 --- a/serverloop.c +++ b/serverloop.c | |||
@@ -35,7 +35,7 @@ | |||
35 | */ | 35 | */ |
36 | 36 | ||
37 | #include "includes.h" | 37 | #include "includes.h" |
38 | RCSID("$OpenBSD: serverloop.c,v 1.60 2001/04/05 23:39:20 markus Exp $"); | 38 | RCSID("$OpenBSD: serverloop.c,v 1.61 2001/04/13 22:46:54 beck Exp $"); |
39 | 39 | ||
40 | #include "xmalloc.h" | 40 | #include "xmalloc.h" |
41 | #include "packet.h" | 41 | #include "packet.h" |
@@ -91,6 +91,8 @@ static volatile int child_wait_status; /* Status from wait(). */ | |||
91 | 91 | ||
92 | void server_init_dispatch(void); | 92 | void server_init_dispatch(void); |
93 | 93 | ||
94 | int client_alive_timeouts = 0; | ||
95 | |||
94 | void | 96 | void |
95 | sigchld_handler(int sig) | 97 | sigchld_handler(int sig) |
96 | { | 98 | { |
@@ -190,6 +192,21 @@ wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, | |||
190 | { | 192 | { |
191 | struct timeval tv, *tvp; | 193 | struct timeval tv, *tvp; |
192 | int ret; | 194 | int ret; |
195 | int client_alive_scheduled = 0; | ||
196 | |||
197 | /* | ||
198 | * if using client_alive, set the max timeout accordingly, | ||
199 | * and indicate that this particular timeout was for client | ||
200 | * alive by setting the client_alive_scheduled flag. | ||
201 | * | ||
202 | * this could be randomized somewhat to make traffic | ||
203 | * analysis more difficult, but we're not doing it yet. | ||
204 | */ | ||
205 | if (max_time_milliseconds == 0 && options.client_alive_interval) { | ||
206 | client_alive_scheduled = 1; | ||
207 | max_time_milliseconds = options.client_alive_interval * 1000; | ||
208 | } else | ||
209 | client_alive_scheduled = 0; | ||
193 | 210 | ||
194 | /* When select fails we restart from here. */ | 211 | /* When select fails we restart from here. */ |
195 | retry_select: | 212 | retry_select: |
@@ -239,7 +256,7 @@ retry_select: | |||
239 | * from it, then read as much as is available and exit. | 256 | * from it, then read as much as is available and exit. |
240 | */ | 257 | */ |
241 | if (child_terminated && packet_not_very_much_data_to_write()) | 258 | if (child_terminated && packet_not_very_much_data_to_write()) |
242 | if (max_time_milliseconds == 0) | 259 | if (max_time_milliseconds == 0 || client_alive_scheduled) |
243 | max_time_milliseconds = 100; | 260 | max_time_milliseconds = 100; |
244 | 261 | ||
245 | if (max_time_milliseconds == 0) | 262 | if (max_time_milliseconds == 0) |
@@ -255,12 +272,36 @@ retry_select: | |||
255 | /* Wait for something to happen, or the timeout to expire. */ | 272 | /* Wait for something to happen, or the timeout to expire. */ |
256 | ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); | 273 | ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); |
257 | 274 | ||
258 | if (ret < 0) { | 275 | if (ret == -1) { |
259 | if (errno != EINTR) | 276 | if (errno != EINTR) |
260 | error("select: %.100s", strerror(errno)); | 277 | error("select: %.100s", strerror(errno)); |
261 | else | 278 | else |
262 | goto retry_select; | 279 | goto retry_select; |
263 | } | 280 | } |
281 | if (ret == 0 && client_alive_scheduled) { | ||
282 | /* timeout, check to see how many we have had */ | ||
283 | client_alive_timeouts++; | ||
284 | |||
285 | if (client_alive_timeouts > options.client_alive_count_max ) { | ||
286 | packet_disconnect( | ||
287 | "Timeout, your session not responding."); | ||
288 | } else { | ||
289 | /* | ||
290 | * send a bogus channel request with "wantreply" | ||
291 | * we should get back a failure | ||
292 | */ | ||
293 | int id; | ||
294 | |||
295 | id = channel_find_open(); | ||
296 | if (id != -1) { | ||
297 | channel_request_start(id, | ||
298 | "keepalive@openssh.com", 1); | ||
299 | packet_send(); | ||
300 | } else | ||
301 | packet_disconnect( | ||
302 | "No open channels after timeout!"); | ||
303 | } | ||
304 | } | ||
264 | } | 305 | } |
265 | 306 | ||
266 | /* | 307 | /* |
@@ -701,6 +742,19 @@ server_loop2(void) | |||
701 | } | 742 | } |
702 | 743 | ||
703 | void | 744 | void |
745 | server_input_channel_failure(int type, int plen, void *ctxt) | ||
746 | { | ||
747 | debug("Got CHANNEL_FAILURE for keepalive"); | ||
748 | /* | ||
749 | * reset timeout, since we got a sane answer from the client. | ||
750 | * even if this was generated by something other than | ||
751 | * the bogus CHANNEL_REQUEST we send for keepalives. | ||
752 | */ | ||
753 | client_alive_timeouts = 0; | ||
754 | } | ||
755 | |||
756 | |||
757 | void | ||
704 | server_input_stdin_data(int type, int plen, void *ctxt) | 758 | server_input_stdin_data(int type, int plen, void *ctxt) |
705 | { | 759 | { |
706 | char *data; | 760 | char *data; |
@@ -912,7 +966,8 @@ server_init_dispatch_20(void) | |||
912 | dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); | 966 | dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); |
913 | dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); | 967 | dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); |
914 | dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); | 968 | dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); |
915 | 969 | /* client_alive */ | |
970 | dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_channel_failure); | ||
916 | /* rekeying */ | 971 | /* rekeying */ |
917 | dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); | 972 | dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); |
918 | } | 973 | } |
@@ -949,3 +1004,4 @@ server_init_dispatch(void) | |||
949 | else | 1004 | else |
950 | server_init_dispatch_15(); | 1005 | server_init_dispatch_15(); |
951 | } | 1006 | } |
1007 | |||
@@ -34,7 +34,7 @@ | |||
34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 34 | .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 35 | .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 | .\" | 36 | .\" |
37 | .\" $OpenBSD: sshd.8,v 1.114 2001/04/11 16:25:31 lebel Exp $ | 37 | .\" $OpenBSD: sshd.8,v 1.115 2001/04/13 22:46:54 beck Exp $ |
38 | .Dd September 25, 1999 | 38 | .Dd September 25, 1999 |
39 | .Dt SSHD 8 | 39 | .Dt SSHD 8 |
40 | .Os | 40 | .Os |
@@ -363,6 +363,31 @@ Specifies whether | |||
363 | should check for new mail for interactive logins. | 363 | should check for new mail for interactive logins. |
364 | The default is | 364 | The default is |
365 | .Dq no . | 365 | .Dq no . |
366 | .It Cm ClientAliveInterval | ||
367 | Sets a timeout interval in seconds after which if no data has been received | ||
368 | from the client, | ||
369 | .Nm | ||
370 | will send a message through the encrypted | ||
371 | channel to request a response from the client. This may only be | ||
372 | used on a server supporting only protocol version 2. The default | ||
373 | is 0, indicating that these messages will not be sent to the client. | ||
374 | .It Cm ClientAliveCountMax | ||
375 | Sets the number of client alive messages (see above) which may be | ||
376 | sent without | ||
377 | .Nm | ||
378 | receiving any messages back from the client. If this threshold is | ||
379 | reached while client alive messages are being sent, | ||
380 | .Nm | ||
381 | will disconnect the client, terminating the session. It is important | ||
382 | to note that the use of client alive messages is very different from | ||
383 | Keepalive (below). The client alive messages are sent through the | ||
384 | encrypted channel and therefore will not be spoofable. The TCP keepalive | ||
385 | option enable by Keepalive is spoofable. You want to use the client | ||
386 | alive mechanism when you are basing something important on | ||
387 | clients having an active connection to the server. | ||
388 | The default is value is 3. If you set ClientAliveInterval | ||
389 | (above) to 15, and leave this value at the default, unresponsive ssh clients | ||
390 | will be disconnected after approximately 45 seconds. | ||
366 | .It Cm DenyGroups | 391 | .It Cm DenyGroups |
367 | This keyword can be followed by a number of group names, separated | 392 | This keyword can be followed by a number of group names, separated |
368 | by spaces. | 393 | by spaces. |