diff options
-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. |