summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2007-09-17 12:06:57 +1000
committerDamien Miller <djm@mindrot.org>2007-09-17 12:06:57 +1000
commit67bd062b27f87047ec48e9a0dd9a47eec2891973 (patch)
tree1d2a2ca058f75e068707dfd82e093a7b80e57d3e
parent54fd7cf2db5327f304825e0f9aaf9af5a490a75f (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--ChangeLog10
-rw-r--r--ssh.c15
-rw-r--r--sshconnect.c105
-rw-r--r--sshconnect.h6
4 files changed, 107 insertions, 29 deletions
diff --git a/ChangeLog b/ChangeLog
index c0a927051..29f77d870 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
2920070914 3720070914
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 $
diff --git a/ssh.c b/ssh.c
index 7f8ea0d17..df3fc51ef 100644
--- a/ssh.c
+++ b/ssh.c
@@ -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;
77static int show_other_keys(const char *, Key *); 77static int show_other_keys(const char *, Key *);
78static void warn_changed_key(Key *); 78static void warn_changed_key(Key *);
79 79
80static void
81ms_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
90static void
91ms_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
224static int 241static int
225timeout_connect(int sockfd, const struct sockaddr *serv_addr, 242timeout_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 */
302int 335int
303ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 336ssh_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 */
400static void 433static void
401ssh_exchange_identification(void) 434ssh_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 */
947void 1010void
948ssh_login(Sensitive *sensitive, const char *orighost, 1011ssh_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
34int 34int
35ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int, 35ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int,
36 int, const char *); 36 int *, int, int, const char *);
37 37
38void 38void
39ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *); 39ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *, int);
40 40
41int verify_host_key(char *, struct sockaddr *, Key *); 41int verify_host_key(char *, struct sockaddr *, Key *);
42 42