diff options
author | Damien Miller <djm@mindrot.org> | 2007-09-17 12:06:57 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2007-09-17 12:06:57 +1000 |
commit | 67bd062b27f87047ec48e9a0dd9a47eec2891973 (patch) | |
tree | 1d2a2ca058f75e068707dfd82e093a7b80e57d3e | |
parent | 54fd7cf2db5327f304825e0f9aaf9af5a490a75f (diff) |
- djm@cvs.openbsd.org 2007/09/04 11:15:56
[ssh.c sshconnect.c sshconnect.h]
make ssh(1)'s ConnectTimeout option apply to both the TCP connection and
SSH banner exchange (previously it just covered the TCP connection).
This allows callers of ssh(1) to better detect and deal with stuck servers
that accept a TCP connection but don't progress the protocol, and also
makes ConnectTimeout useful for connections via a ProxyCommand;
feedback and "looks ok" markus@
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | ssh.c | 15 | ||||
-rw-r--r-- | sshconnect.c | 105 | ||||
-rw-r--r-- | sshconnect.h | 6 |
4 files changed, 107 insertions, 29 deletions
@@ -25,6 +25,14 @@ | |||
25 | make file descriptor passing code return an error rather than call fatal() | 25 | make file descriptor passing code return an error rather than call fatal() |
26 | when it encounters problems, and use this to make session multiplexing | 26 | when it encounters problems, and use this to make session multiplexing |
27 | masters survive slaves failing to pass all stdio FDs; ok markus@ | 27 | masters survive slaves failing to pass all stdio FDs; ok markus@ |
28 | - djm@cvs.openbsd.org 2007/09/04 11:15:56 | ||
29 | [ssh.c sshconnect.c sshconnect.h] | ||
30 | make ssh(1)'s ConnectTimeout option apply to both the TCP connection and | ||
31 | SSH banner exchange (previously it just covered the TCP connection). | ||
32 | This allows callers of ssh(1) to better detect and deal with stuck servers | ||
33 | that accept a TCP connection but don't progress the protocol, and also | ||
34 | makes ConnectTimeout useful for connections via a ProxyCommand; | ||
35 | feedback and "looks ok" markus@ | ||
28 | 36 | ||
29 | 20070914 | 37 | 20070914 |
30 | - (dtucker) [openbsd-compat/bsd-asprintf.c] Plug mem leak in error path. | 38 | - (dtucker) [openbsd-compat/bsd-asprintf.c] Plug mem leak in error path. |
@@ -3222,4 +3230,4 @@ | |||
3222 | OpenServer 6 and add osr5bigcrypt support so when someone migrates | 3230 | OpenServer 6 and add osr5bigcrypt support so when someone migrates |
3223 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ | 3231 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ |
3224 | 3232 | ||
3225 | $Id: ChangeLog,v 1.4748 2007/09/17 02:04:08 djm Exp $ | 3233 | $Id: ChangeLog,v 1.4749 2007/09/17 02:06:57 djm Exp $ |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh.c,v 1.302 2007/09/04 03:21:03 djm Exp $ */ | 1 | /* $OpenBSD: ssh.c,v 1.303 2007/09/04 11:15:55 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -210,7 +210,7 @@ main(int ac, char **av) | |||
210 | char *p, *cp, *line, buf[256]; | 210 | char *p, *cp, *line, buf[256]; |
211 | struct stat st; | 211 | struct stat st; |
212 | struct passwd *pw; | 212 | struct passwd *pw; |
213 | int dummy; | 213 | int dummy, timeout_ms; |
214 | extern int optind, optreset; | 214 | extern int optind, optreset; |
215 | extern char *optarg; | 215 | extern char *optarg; |
216 | struct servent *sp; | 216 | struct servent *sp; |
@@ -681,9 +681,12 @@ main(int ac, char **av) | |||
681 | if (options.control_path != NULL) | 681 | if (options.control_path != NULL) |
682 | control_client(options.control_path); | 682 | control_client(options.control_path); |
683 | 683 | ||
684 | timeout_ms = options.connection_timeout * 1000; | ||
685 | |||
684 | /* Open a connection to the remote host. */ | 686 | /* Open a connection to the remote host. */ |
685 | if (ssh_connect(host, &hostaddr, options.port, | 687 | if (ssh_connect(host, &hostaddr, options.port, |
686 | options.address_family, options.connection_attempts, | 688 | options.address_family, options.connection_attempts, &timeout_ms, |
689 | options.tcp_keep_alive, | ||
687 | #ifdef HAVE_CYGWIN | 690 | #ifdef HAVE_CYGWIN |
688 | options.use_privileged_port, | 691 | options.use_privileged_port, |
689 | #else | 692 | #else |
@@ -692,6 +695,9 @@ main(int ac, char **av) | |||
692 | options.proxy_command) != 0) | 695 | options.proxy_command) != 0) |
693 | exit(255); | 696 | exit(255); |
694 | 697 | ||
698 | if (timeout_ms > 0) | ||
699 | debug3("timeout: %d ms remain after connect", timeout_ms); | ||
700 | |||
695 | /* | 701 | /* |
696 | * If we successfully made the connection, load the host private key | 702 | * If we successfully made the connection, load the host private key |
697 | * in case we will need it later for combined rsa-rhosts | 703 | * in case we will need it later for combined rsa-rhosts |
@@ -767,7 +773,8 @@ main(int ac, char **av) | |||
767 | signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ | 773 | signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ |
768 | 774 | ||
769 | /* Log into the remote system. This never returns if the login fails. */ | 775 | /* Log into the remote system. This never returns if the login fails. */ |
770 | ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, pw); | 776 | ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, |
777 | pw, timeout_ms); | ||
771 | 778 | ||
772 | /* We no longer need the private host keys. Clear them now. */ | 779 | /* We no longer need the private host keys. Clear them now. */ |
773 | if (sensitive_data.nkeys != 0) { | 780 | if (sensitive_data.nkeys != 0) { |
diff --git a/sshconnect.c b/sshconnect.c index 7e3c9fff4..933df39fa 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.201 2007/08/23 03:23:26 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.202 2007/09/04 11:15:55 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -77,6 +77,23 @@ extern pid_t proxy_command_pid; | |||
77 | static int show_other_keys(const char *, Key *); | 77 | static int show_other_keys(const char *, Key *); |
78 | static void warn_changed_key(Key *); | 78 | static void warn_changed_key(Key *); |
79 | 79 | ||
80 | static void | ||
81 | ms_subtract_diff(struct timeval *start, int *ms) | ||
82 | { | ||
83 | struct timeval diff, finish; | ||
84 | |||
85 | gettimeofday(&finish, NULL); | ||
86 | timersub(&finish, start, &diff); | ||
87 | *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); | ||
88 | } | ||
89 | |||
90 | static void | ||
91 | ms_to_timeval(struct timeval *tv, int ms) | ||
92 | { | ||
93 | tv->tv_sec = ms / 1000; | ||
94 | tv->tv_usec = (ms % 1000) * 1000; | ||
95 | } | ||
96 | |||
80 | /* | 97 | /* |
81 | * Connect to the given ssh server using a proxy command. | 98 | * Connect to the given ssh server using a proxy command. |
82 | */ | 99 | */ |
@@ -223,30 +240,36 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
223 | 240 | ||
224 | static int | 241 | static int |
225 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, | 242 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, |
226 | socklen_t addrlen, int timeout) | 243 | socklen_t addrlen, int *timeoutp) |
227 | { | 244 | { |
228 | fd_set *fdset; | 245 | fd_set *fdset; |
229 | struct timeval tv; | 246 | struct timeval tv, t_start; |
230 | socklen_t optlen; | 247 | socklen_t optlen; |
231 | int optval, rc, result = -1; | 248 | int optval, rc, result = -1; |
232 | 249 | ||
233 | if (timeout <= 0) | 250 | gettimeofday(&t_start, NULL); |
234 | return (connect(sockfd, serv_addr, addrlen)); | 251 | |
252 | if (*timeoutp <= 0) { | ||
253 | result = connect(sockfd, serv_addr, addrlen); | ||
254 | goto done; | ||
255 | } | ||
235 | 256 | ||
236 | set_nonblock(sockfd); | 257 | set_nonblock(sockfd); |
237 | rc = connect(sockfd, serv_addr, addrlen); | 258 | rc = connect(sockfd, serv_addr, addrlen); |
238 | if (rc == 0) { | 259 | if (rc == 0) { |
239 | unset_nonblock(sockfd); | 260 | unset_nonblock(sockfd); |
240 | return (0); | 261 | result = 0; |
262 | goto done; | ||
263 | } | ||
264 | if (errno != EINPROGRESS) { | ||
265 | result = -1; | ||
266 | goto done; | ||
241 | } | 267 | } |
242 | if (errno != EINPROGRESS) | ||
243 | return (-1); | ||
244 | 268 | ||
245 | fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), | 269 | fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), |
246 | sizeof(fd_mask)); | 270 | sizeof(fd_mask)); |
247 | FD_SET(sockfd, fdset); | 271 | FD_SET(sockfd, fdset); |
248 | tv.tv_sec = timeout; | 272 | ms_to_timeval(&tv, *timeoutp); |
249 | tv.tv_usec = 0; | ||
250 | 273 | ||
251 | for (;;) { | 274 | for (;;) { |
252 | rc = select(sockfd + 1, NULL, fdset, NULL, &tv); | 275 | rc = select(sockfd + 1, NULL, fdset, NULL, &tv); |
@@ -285,6 +308,16 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, | |||
285 | } | 308 | } |
286 | 309 | ||
287 | xfree(fdset); | 310 | xfree(fdset); |
311 | |||
312 | done: | ||
313 | if (result == 0 && *timeoutp > 0) { | ||
314 | ms_subtract_diff(&t_start, timeoutp); | ||
315 | if (*timeoutp <= 0) { | ||
316 | errno = ETIMEDOUT; | ||
317 | result = -1; | ||
318 | } | ||
319 | } | ||
320 | |||
288 | return (result); | 321 | return (result); |
289 | } | 322 | } |
290 | 323 | ||
@@ -301,8 +334,8 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, | |||
301 | */ | 334 | */ |
302 | int | 335 | int |
303 | ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | 336 | ssh_connect(const char *host, struct sockaddr_storage * hostaddr, |
304 | u_short port, int family, int connection_attempts, | 337 | u_short port, int family, int connection_attempts, int *timeout_ms, |
305 | int needpriv, const char *proxy_command) | 338 | int want_keepalive, int needpriv, const char *proxy_command) |
306 | { | 339 | { |
307 | int gaierr; | 340 | int gaierr; |
308 | int on = 1; | 341 | int on = 1; |
@@ -355,7 +388,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
355 | continue; | 388 | continue; |
356 | 389 | ||
357 | if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, | 390 | if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, |
358 | options.connection_timeout) >= 0) { | 391 | timeout_ms) >= 0) { |
359 | /* Successful connection. */ | 392 | /* Successful connection. */ |
360 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); | 393 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); |
361 | break; | 394 | break; |
@@ -382,7 +415,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
382 | debug("Connection established."); | 415 | debug("Connection established."); |
383 | 416 | ||
384 | /* Set SO_KEEPALIVE if requested. */ | 417 | /* Set SO_KEEPALIVE if requested. */ |
385 | if (options.tcp_keep_alive && | 418 | if (want_keepalive && |
386 | setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, | 419 | setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, |
387 | sizeof(on)) < 0) | 420 | sizeof(on)) < 0) |
388 | error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); | 421 | error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); |
@@ -398,7 +431,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
398 | * identification string. | 431 | * identification string. |
399 | */ | 432 | */ |
400 | static void | 433 | static void |
401 | ssh_exchange_identification(void) | 434 | ssh_exchange_identification(int timeout_ms) |
402 | { | 435 | { |
403 | char buf[256], remote_version[256]; /* must be same size! */ | 436 | char buf[256], remote_version[256]; /* must be same size! */ |
404 | int remote_major, remote_minor, mismatch; | 437 | int remote_major, remote_minor, mismatch; |
@@ -406,16 +439,44 @@ ssh_exchange_identification(void) | |||
406 | int connection_out = packet_get_connection_out(); | 439 | int connection_out = packet_get_connection_out(); |
407 | int minor1 = PROTOCOL_MINOR_1; | 440 | int minor1 = PROTOCOL_MINOR_1; |
408 | u_int i, n; | 441 | u_int i, n; |
442 | size_t len; | ||
443 | int fdsetsz, remaining, rc; | ||
444 | struct timeval t_start, t_remaining; | ||
445 | fd_set *fdset; | ||
446 | |||
447 | fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); | ||
448 | fdset = xcalloc(1, fdsetsz); | ||
409 | 449 | ||
410 | /* Read other side's version identification. */ | 450 | /* Read other side's version identification. */ |
451 | remaining = timeout_ms; | ||
411 | for (n = 0;;) { | 452 | for (n = 0;;) { |
412 | for (i = 0; i < sizeof(buf) - 1; i++) { | 453 | for (i = 0; i < sizeof(buf) - 1; i++) { |
413 | size_t len = atomicio(read, connection_in, &buf[i], 1); | 454 | if (timeout_ms > 0) { |
455 | gettimeofday(&t_start, NULL); | ||
456 | ms_to_timeval(&t_remaining, remaining); | ||
457 | FD_SET(connection_in, fdset); | ||
458 | rc = select(connection_in + 1, fdset, NULL, | ||
459 | fdset, &t_remaining); | ||
460 | ms_subtract_diff(&t_start, &remaining); | ||
461 | if (rc == 0 || remaining <= 0) | ||
462 | fatal("Connection timed out during " | ||
463 | "banner exchange"); | ||
464 | if (rc == -1) { | ||
465 | if (errno == EINTR) | ||
466 | continue; | ||
467 | fatal("ssh_exchange_identification: " | ||
468 | "select: %s", strerror(errno)); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | len = atomicio(read, connection_in, &buf[i], 1); | ||
414 | 473 | ||
415 | if (len != 1 && errno == EPIPE) | 474 | if (len != 1 && errno == EPIPE) |
416 | fatal("ssh_exchange_identification: Connection closed by remote host"); | 475 | fatal("ssh_exchange_identification: " |
476 | "Connection closed by remote host"); | ||
417 | else if (len != 1) | 477 | else if (len != 1) |
418 | fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); | 478 | fatal("ssh_exchange_identification: " |
479 | "read: %.100s", strerror(errno)); | ||
419 | if (buf[i] == '\r') { | 480 | if (buf[i] == '\r') { |
420 | buf[i] = '\n'; | 481 | buf[i] = '\n'; |
421 | buf[i + 1] = 0; | 482 | buf[i + 1] = 0; |
@@ -426,7 +487,8 @@ ssh_exchange_identification(void) | |||
426 | break; | 487 | break; |
427 | } | 488 | } |
428 | if (++n > 65536) | 489 | if (++n > 65536) |
429 | fatal("ssh_exchange_identification: No banner received"); | 490 | fatal("ssh_exchange_identification: " |
491 | "No banner received"); | ||
430 | } | 492 | } |
431 | buf[sizeof(buf) - 1] = 0; | 493 | buf[sizeof(buf) - 1] = 0; |
432 | if (strncmp(buf, "SSH-", 4) == 0) | 494 | if (strncmp(buf, "SSH-", 4) == 0) |
@@ -434,6 +496,7 @@ ssh_exchange_identification(void) | |||
434 | debug("ssh_exchange_identification: %s", buf); | 496 | debug("ssh_exchange_identification: %s", buf); |
435 | } | 497 | } |
436 | server_version_string = xstrdup(buf); | 498 | server_version_string = xstrdup(buf); |
499 | xfree(fdset); | ||
437 | 500 | ||
438 | /* | 501 | /* |
439 | * Check that the versions match. In future this might accept | 502 | * Check that the versions match. In future this might accept |
@@ -946,7 +1009,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
946 | */ | 1009 | */ |
947 | void | 1010 | void |
948 | ssh_login(Sensitive *sensitive, const char *orighost, | 1011 | ssh_login(Sensitive *sensitive, const char *orighost, |
949 | struct sockaddr *hostaddr, struct passwd *pw) | 1012 | struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms) |
950 | { | 1013 | { |
951 | char *host, *cp; | 1014 | char *host, *cp; |
952 | char *server_user, *local_user; | 1015 | char *server_user, *local_user; |
@@ -961,7 +1024,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, | |||
961 | *cp = (char)tolower(*cp); | 1024 | *cp = (char)tolower(*cp); |
962 | 1025 | ||
963 | /* Exchange protocol version identification strings with the server. */ | 1026 | /* Exchange protocol version identification strings with the server. */ |
964 | ssh_exchange_identification(); | 1027 | ssh_exchange_identification(timeout_ms); |
965 | 1028 | ||
966 | /* Put the connection into non-blocking mode. */ | 1029 | /* Put the connection into non-blocking mode. */ |
967 | packet_set_nonblocking(); | 1030 | packet_set_nonblocking(); |
diff --git a/sshconnect.h b/sshconnect.h index 4e66bbffc..75bde1a4d 100644 --- a/sshconnect.h +++ b/sshconnect.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.h,v 1.23 2006/08/03 03:34:42 deraadt Exp $ */ | 1 | /* $OpenBSD: sshconnect.h,v 1.24 2007/09/04 11:15:56 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
@@ -33,10 +33,10 @@ struct Sensitive { | |||
33 | 33 | ||
34 | int | 34 | int |
35 | ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, | 35 | ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, |
36 | int, const char *); | 36 | int *, int, int, const char *); |
37 | 37 | ||
38 | void | 38 | void |
39 | ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *); | 39 | ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *, int); |
40 | 40 | ||
41 | int verify_host_key(char *, struct sockaddr *, Key *); | 41 | int verify_host_key(char *, struct sockaddr *, Key *); |
42 | 42 | ||