diff options
author | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
commit | 137d76ba65883aa8143af1fcad83b57e7badef0c (patch) | |
tree | f426e804bb5248ceafedfab7bb78ae6e6752942c /sshconnect.c | |
parent | dac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff) | |
parent | ef94e5613d37bcbf880f21ee6094e4b1c7683a4c (diff) |
* New upstream release (closes: #474301). Important changes not previously
backported to 4.7p1:
- 4.9/4.9p1 (http://www.openssh.com/txt/release-4.9):
+ Added chroot(2) support for sshd(8), controlled by a new option
"ChrootDirectory" (closes: #139047, LP: #24777).
+ Linked sftp-server(8) into sshd(8). The internal sftp server is used
when the command "internal-sftp" is specified in a Subsystem or
ForceCommand declaration. When used with ChrootDirectory, the
internal sftp server requires no special configuration of files
inside the chroot environment.
+ Added a protocol extension method "posix-rename@openssh.com" for
sftp-server(8) to perform POSIX atomic rename() operations; sftp(1)
prefers this if available (closes: #308561).
+ Removed the fixed limit of 100 file handles in sftp-server(8).
+ ssh(8) will now skip generation of SSH protocol 1 ephemeral server
keys when in inetd mode and protocol 2 connections are negotiated.
This speeds up protocol 2 connections to inetd-mode servers that
also allow Protocol 1.
+ Accept the PermitRootLogin directive in a sshd_config(5) Match
block. Allows for, e.g. permitting root only from the local network.
+ Reworked sftp(1) argument splitting and escaping to be more
internally consistent (i.e. between sftp commands) and more
consistent with sh(1). Please note that this will change the
interpretation of some quoted strings, especially those with
embedded backslash escape sequences.
+ Support "Banner=none" in sshd_config(5) to disable sending of a
pre-login banner (e.g. in a Match block).
+ ssh(1) ProxyCommands are now executed with $SHELL rather than
/bin/sh.
+ ssh(1)'s ConnectTimeout option is now applied to both the TCP
connection and the 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.
+ scp(1) incorrectly reported "stalled" on slow copies (closes:
#140828).
+ scp(1) date underflow for timestamps before epoch.
+ ssh(1) used the obsolete SIG DNS RRtype for host keys in DNS,
instead of the current standard RRSIG.
+ Correctly drain ACKs when a sftp(1) upload write fails midway,
avoids a fatal() exit from what should be a recoverable condition.
+ Fixed ssh-keygen(1) selective host key hashing (i.e. "ssh-keygen -HF
hostname") to not include any IP address in the data to be hashed.
+ Make ssh(1) skip listening on the IPv6 wildcard address when a
binding address of 0.0.0.0 is used against an old SSH server that
does not support the RFC4254 syntax for wildcard bind addresses.
+ Enable IPV6_V6ONLY socket option on sshd(8) listen socket, as is
already done for X11/TCP forwarding sockets (closes: #439661).
+ Fix FD leak that could hang a ssh(1) connection multiplexing master.
+ Make ssh(1) -q option documentation consistent with reality.
+ Fixed sshd(8) PAM support not calling pam_session_close(), or
failing to call it with root privileges (closes: #372680).
+ Fix activation of OpenSSL engine support when requested in configure
(LP: #119295).
- 5.1/5.1p1 (http://www.openssh.com/txt/release-5.1):
+ Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1)
and ssh-keygen(1). Visual fingerprint display is controlled by a new
ssh_config(5) option "VisualHostKey". The intent is to render SSH
host keys in a visual form that is amenable to easy recall and
rejection of changed host keys.
+ sshd_config(5) now supports CIDR address/masklen matching in "Match
address" blocks, with a fallback to classic wildcard matching.
+ sshd(8) now supports CIDR matching in ~/.ssh/authorized_keys
from="..." restrictions, also with a fallback to classic wildcard
matching.
+ Added an extended test mode (-T) to sshd(8) to request that it write
its effective configuration to stdout and exit. Extended test mode
also supports the specification of connection parameters (username,
source address and hostname) to test the application of
sshd_config(5) Match rules.
+ ssh(1) now prints the number of bytes transferred and the overall
connection throughput for SSH protocol 2 sessions when in verbose
mode (previously these statistics were displayed for protocol 1
connections only).
+ sftp-server(8) now supports extension methods statvfs@openssh.com
and fstatvfs@openssh.com that implement statvfs(2)-like operations.
+ sftp(1) now has a "df" command to the sftp client that uses the
statvfs@openssh.com to produce a df(1)-like display of filesystem
space and inode utilisation (requires statvfs@openssh.com support on
the server).
+ Added a MaxSessions option to sshd_config(5) to allow control of the
number of multiplexed sessions supported over a single TCP
connection. This allows increasing the number of allowed sessions
above the previous default of 10, disabling connection multiplexing
(MaxSessions=1) or disallowing login/shell/subsystem sessions
entirely (MaxSessions=0).
+ Added a no-more-sessions@openssh.com global request extension that
is sent from ssh(1) to sshd(8) when the client knows that it will
never request another session (i.e. when session multiplexing is
disabled). This allows a server to disallow further session requests
and terminate the session in cases where the client has been
hijacked.
+ ssh-keygen(1) now supports the use of the -l option in combination
with -F to search for a host in ~/.ssh/known_hosts and display its
fingerprint.
+ ssh-keyscan(1) now defaults to "rsa" (protocol 2) keys, instead of
"rsa1".
+ Added an AllowAgentForwarding option to sshd_config(8) to control
whether authentication agent forwarding is permitted. Note that this
is a loose control, as a client may install their own unofficial
forwarder.
+ ssh(1) and sshd(8): avoid unnecessary malloc/copy/free when
receiving network data, resulting in a ~10% speedup.
+ ssh(1) and sshd(8) will now try additional addresses when connecting
to a port forward destination whose DNS name resolves to more than
one address. The previous behaviour was to try the only first
address and give up if that failed.
+ ssh(1) and sshd(8) now support signalling that channels are
half-closed for writing, through a channel protocol extension
notification "eow@openssh.com". This allows propagation of closed
file descriptors, so that commands such as "ssh -2 localhost od
/bin/ls | true" do not send unnecessary data over the wire.
+ sshd(8): increased the default size of ssh protocol 1 ephemeral keys
from 768 to 1024 bits.
+ When ssh(1) has been requested to fork after authentication ("ssh
-f") with ExitOnForwardFailure enabled, delay the fork until after
replies for any -R forwards have been seen. Allows for robust
detection of -R forward failure when using -f.
+ "Match group" blocks in sshd_config(5) now support negation of
groups. E.g. "Match group staff,!guests".
+ sftp(1) and sftp-server(8) now allow chmod-like operations to set
set[ug]id/sticky bits.
+ The MaxAuthTries option is now permitted in sshd_config(5) match
blocks.
+ Multiplexed ssh(1) sessions now support a subset of the ~ escapes
that are available to a primary connection.
+ ssh(1) connection multiplexing will now fall back to creating a new
connection in most error cases (closes: #352830).
+ Make ssh(1) deal more gracefully with channel requests that fail.
Previously it would optimistically assume that requests would always
succeed, which could cause hangs if they did not (e.g. when the
server runs out of file descriptors).
+ ssh(1) now reports multiplexing errors via the multiplex slave's
stderr where possible (subject to LogLevel in the mux master).
+ Prevent sshd(8) from erroneously applying public key restrictions
leaned from ~/.ssh/authorized_keys to other authentication methods
when public key authentication subsequently fails (LP: #161047).
+ Fixed an UMAC alignment problem that manifested on Itanium
platforms.
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 206 |
1 files changed, 121 insertions, 85 deletions
diff --git a/sshconnect.c b/sshconnect.c index 3c888e36a..01337fe40 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.200 2006/10/10 10:12:45 markus Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.211 2008/07/01 07:24:22 dtucker 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 |
@@ -74,13 +74,6 @@ extern pid_t proxy_command_pid; | |||
74 | #define INET6_ADDRSTRLEN 46 | 74 | #define INET6_ADDRSTRLEN 46 |
75 | #endif | 75 | #endif |
76 | 76 | ||
77 | static sig_atomic_t banner_timedout; | ||
78 | |||
79 | static void banner_alarm_catch (int signum) | ||
80 | { | ||
81 | banner_timedout = 1; | ||
82 | } | ||
83 | |||
84 | static int show_other_keys(const char *, Key *); | 77 | static int show_other_keys(const char *, Key *); |
85 | static void warn_changed_key(Key *); | 78 | static void warn_changed_key(Key *); |
86 | 79 | ||
@@ -93,7 +86,10 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
93 | char *command_string, *tmp; | 86 | char *command_string, *tmp; |
94 | int pin[2], pout[2]; | 87 | int pin[2], pout[2]; |
95 | pid_t pid; | 88 | pid_t pid; |
96 | char strport[NI_MAXSERV]; | 89 | char *shell, strport[NI_MAXSERV]; |
90 | |||
91 | if ((shell = getenv("SHELL")) == NULL) | ||
92 | shell = _PATH_BSHELL; | ||
97 | 93 | ||
98 | /* Convert the port number into a string. */ | 94 | /* Convert the port number into a string. */ |
99 | snprintf(strport, sizeof strport, "%hu", port); | 95 | snprintf(strport, sizeof strport, "%hu", port); |
@@ -139,7 +135,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
139 | 135 | ||
140 | /* Stderr is left as it is so that error messages get | 136 | /* Stderr is left as it is so that error messages get |
141 | printed on the user's terminal. */ | 137 | printed on the user's terminal. */ |
142 | argv[0] = _PATH_BSHELL; | 138 | argv[0] = shell; |
143 | argv[1] = "-c"; | 139 | argv[1] = "-c"; |
144 | argv[2] = command_string; | 140 | argv[2] = command_string; |
145 | argv[3] = NULL; | 141 | argv[3] = NULL; |
@@ -164,7 +160,9 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
164 | xfree(command_string); | 160 | xfree(command_string); |
165 | 161 | ||
166 | /* Set the connection file descriptors. */ | 162 | /* Set the connection file descriptors. */ |
167 | packet_set_connection(pout[0], pin[1], options.setuptimeout); | 163 | packet_set_connection(pout[0], pin[1]); |
164 | packet_set_timeout(options.server_alive_interval, | ||
165 | options.server_alive_count_max); | ||
168 | 166 | ||
169 | /* Indicate OK return */ | 167 | /* Indicate OK return */ |
170 | return 0; | 168 | return 0; |
@@ -208,10 +206,10 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
208 | hints.ai_socktype = ai->ai_socktype; | 206 | hints.ai_socktype = ai->ai_socktype; |
209 | hints.ai_protocol = ai->ai_protocol; | 207 | hints.ai_protocol = ai->ai_protocol; |
210 | hints.ai_flags = AI_PASSIVE; | 208 | hints.ai_flags = AI_PASSIVE; |
211 | gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); | 209 | gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); |
212 | if (gaierr) { | 210 | if (gaierr) { |
213 | error("getaddrinfo: %s: %s", options.bind_address, | 211 | error("getaddrinfo: %s: %s", options.bind_address, |
214 | gai_strerror(gaierr)); | 212 | ssh_gai_strerror(gaierr)); |
215 | close(sock); | 213 | close(sock); |
216 | return -1; | 214 | return -1; |
217 | } | 215 | } |
@@ -227,30 +225,36 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
227 | 225 | ||
228 | static int | 226 | static int |
229 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, | 227 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, |
230 | socklen_t addrlen, int timeout) | 228 | socklen_t addrlen, int *timeoutp) |
231 | { | 229 | { |
232 | fd_set *fdset; | 230 | fd_set *fdset; |
233 | struct timeval tv; | 231 | struct timeval tv, t_start; |
234 | socklen_t optlen; | 232 | socklen_t optlen; |
235 | int optval, rc, result = -1; | 233 | int optval, rc, result = -1; |
236 | 234 | ||
237 | if (timeout <= 0) | 235 | gettimeofday(&t_start, NULL); |
238 | return (connect(sockfd, serv_addr, addrlen)); | 236 | |
237 | if (*timeoutp <= 0) { | ||
238 | result = connect(sockfd, serv_addr, addrlen); | ||
239 | goto done; | ||
240 | } | ||
239 | 241 | ||
240 | set_nonblock(sockfd); | 242 | set_nonblock(sockfd); |
241 | rc = connect(sockfd, serv_addr, addrlen); | 243 | rc = connect(sockfd, serv_addr, addrlen); |
242 | if (rc == 0) { | 244 | if (rc == 0) { |
243 | unset_nonblock(sockfd); | 245 | unset_nonblock(sockfd); |
244 | return (0); | 246 | result = 0; |
247 | goto done; | ||
248 | } | ||
249 | if (errno != EINPROGRESS) { | ||
250 | result = -1; | ||
251 | goto done; | ||
245 | } | 252 | } |
246 | if (errno != EINPROGRESS) | ||
247 | return (-1); | ||
248 | 253 | ||
249 | fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), | 254 | fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), |
250 | sizeof(fd_mask)); | 255 | sizeof(fd_mask)); |
251 | FD_SET(sockfd, fdset); | 256 | FD_SET(sockfd, fdset); |
252 | tv.tv_sec = timeout; | 257 | ms_to_timeval(&tv, *timeoutp); |
253 | tv.tv_usec = 0; | ||
254 | 258 | ||
255 | for (;;) { | 259 | for (;;) { |
256 | rc = select(sockfd + 1, NULL, fdset, NULL, &tv); | 260 | rc = select(sockfd + 1, NULL, fdset, NULL, &tv); |
@@ -289,6 +293,16 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, | |||
289 | } | 293 | } |
290 | 294 | ||
291 | xfree(fdset); | 295 | xfree(fdset); |
296 | |||
297 | done: | ||
298 | if (result == 0 && *timeoutp > 0) { | ||
299 | ms_subtract_diff(&t_start, timeoutp); | ||
300 | if (*timeoutp <= 0) { | ||
301 | errno = ETIMEDOUT; | ||
302 | result = -1; | ||
303 | } | ||
304 | } | ||
305 | |||
292 | return (result); | 306 | return (result); |
293 | } | 307 | } |
294 | 308 | ||
@@ -305,8 +319,8 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, | |||
305 | */ | 319 | */ |
306 | int | 320 | int |
307 | ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | 321 | ssh_connect(const char *host, struct sockaddr_storage * hostaddr, |
308 | u_short port, int family, int connection_attempts, | 322 | u_short port, int family, int connection_attempts, int *timeout_ms, |
309 | int needpriv, const char *proxy_command) | 323 | int want_keepalive, int needpriv, const char *proxy_command) |
310 | { | 324 | { |
311 | int gaierr; | 325 | int gaierr; |
312 | int on = 1; | 326 | int on = 1; |
@@ -327,8 +341,8 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
327 | hints.ai_socktype = SOCK_STREAM; | 341 | hints.ai_socktype = SOCK_STREAM; |
328 | snprintf(strport, sizeof strport, "%u", port); | 342 | snprintf(strport, sizeof strport, "%u", port); |
329 | if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) | 343 | if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) |
330 | fatal("%s: %.100s: %s", __progname, host, | 344 | fatal("%s: Could not resolve hostname %.100s: %s", __progname, |
331 | gai_strerror(gaierr)); | 345 | host, ssh_gai_strerror(gaierr)); |
332 | 346 | ||
333 | for (attempt = 0; attempt < connection_attempts; attempt++) { | 347 | for (attempt = 0; attempt < connection_attempts; attempt++) { |
334 | if (attempt > 0) { | 348 | if (attempt > 0) { |
@@ -359,7 +373,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
359 | continue; | 373 | continue; |
360 | 374 | ||
361 | if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, | 375 | if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, |
362 | options.connection_timeout) >= 0) { | 376 | timeout_ms) >= 0) { |
363 | /* Successful connection. */ | 377 | /* Successful connection. */ |
364 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); | 378 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); |
365 | break; | 379 | break; |
@@ -386,13 +400,15 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
386 | debug("Connection established."); | 400 | debug("Connection established."); |
387 | 401 | ||
388 | /* Set SO_KEEPALIVE if requested. */ | 402 | /* Set SO_KEEPALIVE if requested. */ |
389 | if (options.tcp_keep_alive && | 403 | if (want_keepalive && |
390 | setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, | 404 | setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, |
391 | sizeof(on)) < 0) | 405 | sizeof(on)) < 0) |
392 | error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); | 406 | error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); |
393 | 407 | ||
394 | /* Set the connection. */ | 408 | /* Set the connection. */ |
395 | packet_set_connection(sock, sock, options.setuptimeout); | 409 | packet_set_connection(sock, sock); |
410 | packet_set_timeout(options.server_alive_interval, | ||
411 | options.server_alive_count_max); | ||
396 | 412 | ||
397 | return 0; | 413 | return 0; |
398 | } | 414 | } |
@@ -402,7 +418,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
402 | * identification string. | 418 | * identification string. |
403 | */ | 419 | */ |
404 | static void | 420 | static void |
405 | ssh_exchange_identification(void) | 421 | ssh_exchange_identification(int timeout_ms) |
406 | { | 422 | { |
407 | char buf[256], remote_version[256]; /* must be same size! */ | 423 | char buf[256], remote_version[256]; /* must be same size! */ |
408 | int remote_major, remote_minor, mismatch; | 424 | int remote_major, remote_minor, mismatch; |
@@ -410,40 +426,44 @@ ssh_exchange_identification(void) | |||
410 | int connection_out = packet_get_connection_out(); | 426 | int connection_out = packet_get_connection_out(); |
411 | int minor1 = PROTOCOL_MINOR_1; | 427 | int minor1 = PROTOCOL_MINOR_1; |
412 | u_int i, n; | 428 | u_int i, n; |
413 | struct sigaction sa, osa; | 429 | size_t len; |
430 | int fdsetsz, remaining, rc; | ||
431 | struct timeval t_start, t_remaining; | ||
432 | fd_set *fdset; | ||
433 | |||
434 | fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); | ||
435 | fdset = xcalloc(1, fdsetsz); | ||
414 | 436 | ||
415 | /* Read other side's version identification. | ||
416 | * If SetupTimeOut has been set, give up after the specified amount | ||
417 | * of time. | ||
418 | */ | ||
419 | if (options.setuptimeout > 0) { | ||
420 | memset(&sa, 0, sizeof(sa)); | ||
421 | sa.sa_handler = banner_alarm_catch; | ||
422 | /* throw away any pending alarms, since we'd block otherwise */ | ||
423 | alarm(0); | ||
424 | sigaction(SIGALRM, &sa, &osa); | ||
425 | alarm(options.setuptimeout); | ||
426 | } | ||
427 | /* Read other side's version identification. */ | 437 | /* Read other side's version identification. */ |
438 | remaining = timeout_ms; | ||
428 | for (n = 0;;) { | 439 | for (n = 0;;) { |
429 | for (i = 0; i < sizeof(buf) - 1; ) { | 440 | for (i = 0; i < sizeof(buf) - 1; i++) { |
430 | ssize_t len = read(connection_in, &buf[i], 1); | 441 | if (timeout_ms > 0) { |
431 | if (banner_timedout) | 442 | gettimeofday(&t_start, NULL); |
432 | fatal("ssh_exchange_identification: Timeout waiting for version information."); | 443 | ms_to_timeval(&t_remaining, remaining); |
433 | if (len == 0) | 444 | FD_SET(connection_in, fdset); |
434 | errno = EPIPE; | 445 | rc = select(connection_in + 1, fdset, NULL, |
446 | fdset, &t_remaining); | ||
447 | ms_subtract_diff(&t_start, &remaining); | ||
448 | if (rc == 0 || remaining <= 0) | ||
449 | fatal("Connection timed out during " | ||
450 | "banner exchange"); | ||
451 | if (rc == -1) { | ||
452 | if (errno == EINTR) | ||
453 | continue; | ||
454 | fatal("ssh_exchange_identification: " | ||
455 | "select: %s", strerror(errno)); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | len = atomicio(read, connection_in, &buf[i], 1); | ||
435 | 460 | ||
436 | if (len != 1 && errno == EPIPE) | 461 | if (len != 1 && errno == EPIPE) |
437 | fatal("ssh_exchange_identification: Connection closed by remote host"); | 462 | fatal("ssh_exchange_identification: " |
438 | else if (len != 1) { | 463 | "Connection closed by remote host"); |
439 | #ifdef EWOULDBLOCK | 464 | else if (len != 1) |
440 | if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) | 465 | fatal("ssh_exchange_identification: " |
441 | #else | 466 | "read: %.100s", strerror(errno)); |
442 | if (errno == EINTR || errno == EAGAIN) | ||
443 | #endif | ||
444 | continue; | ||
445 | fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); | ||
446 | } | ||
447 | if (buf[i] == '\r') { | 467 | if (buf[i] == '\r') { |
448 | buf[i] = '\n'; | 468 | buf[i] = '\n'; |
449 | buf[i + 1] = 0; | 469 | buf[i + 1] = 0; |
@@ -453,13 +473,9 @@ ssh_exchange_identification(void) | |||
453 | buf[i + 1] = 0; | 473 | buf[i + 1] = 0; |
454 | break; | 474 | break; |
455 | } | 475 | } |
456 | if (buf[i] == '\r') { | ||
457 | buf[i] = '\n'; | ||
458 | buf[i + 1] = 0; /**XXX wait for \n */ | ||
459 | } | ||
460 | if (++n > 65536) | 476 | if (++n > 65536) |
461 | fatal("ssh_exchange_identification: No banner received"); | 477 | fatal("ssh_exchange_identification: " |
462 | i++; | 478 | "No banner received"); |
463 | } | 479 | } |
464 | buf[sizeof(buf) - 1] = 0; | 480 | buf[sizeof(buf) - 1] = 0; |
465 | if (strncmp(buf, "SSH-", 4) == 0) | 481 | if (strncmp(buf, "SSH-", 4) == 0) |
@@ -467,14 +483,7 @@ ssh_exchange_identification(void) | |||
467 | debug("ssh_exchange_identification: %s", buf); | 483 | debug("ssh_exchange_identification: %s", buf); |
468 | } | 484 | } |
469 | server_version_string = xstrdup(buf); | 485 | server_version_string = xstrdup(buf); |
470 | 486 | xfree(fdset); | |
471 | /* If SetupTimeOut has been set, unset the alarm now, and | ||
472 | * put the correct handler for SIGALRM back. | ||
473 | */ | ||
474 | if (options.setuptimeout > 0) { | ||
475 | alarm(0); | ||
476 | sigaction(SIGALRM, &osa, NULL); | ||
477 | } | ||
478 | 487 | ||
479 | /* | 488 | /* |
480 | * Check that the versions match. In future this might accept | 489 | * Check that the versions match. In future this might accept |
@@ -528,10 +537,10 @@ ssh_exchange_identification(void) | |||
528 | (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | 537 | (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, |
529 | remote_major); | 538 | remote_major); |
530 | /* Send our own protocol version identification. */ | 539 | /* Send our own protocol version identification. */ |
531 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", | 540 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", |
532 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | 541 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, |
533 | compat20 ? PROTOCOL_MINOR_2 : minor1, | 542 | compat20 ? PROTOCOL_MINOR_2 : minor1, |
534 | SSH_RELEASE); | 543 | SSH_RELEASE, compat20 ? "\r\n" : "\n"); |
535 | if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) | 544 | if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) |
536 | fatal("write: %.100s", strerror(errno)); | 545 | fatal("write: %.100s", strerror(errno)); |
537 | client_version_string = xstrdup(buf); | 546 | client_version_string = xstrdup(buf); |
@@ -580,14 +589,14 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
580 | Key *file_key; | 589 | Key *file_key; |
581 | const char *type = key_type(host_key); | 590 | const char *type = key_type(host_key); |
582 | char *ip = NULL, *host = NULL; | 591 | char *ip = NULL, *host = NULL; |
583 | char hostline[1000], *hostp, *fp; | 592 | char hostline[1000], *hostp, *fp, *ra; |
584 | HostStatus host_status; | 593 | HostStatus host_status; |
585 | HostStatus ip_status; | 594 | HostStatus ip_status; |
586 | int r, local = 0, host_ip_differ = 0; | 595 | int r, local = 0, host_ip_differ = 0; |
587 | int salen; | 596 | int salen; |
588 | char ntop[NI_MAXHOST]; | 597 | char ntop[NI_MAXHOST]; |
589 | char msg[1024]; | 598 | char msg[1024]; |
590 | int len, host_line, ip_line; | 599 | int len, host_line, ip_line, cancelled_forwarding = 0; |
591 | const char *host_file = NULL, *ip_file = NULL; | 600 | const char *host_file = NULL, *ip_file = NULL; |
592 | 601 | ||
593 | /* | 602 | /* |
@@ -634,6 +643,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
634 | } else { | 643 | } else { |
635 | ip = xstrdup("<no hostip for proxy command>"); | 644 | ip = xstrdup("<no hostip for proxy command>"); |
636 | } | 645 | } |
646 | |||
637 | /* | 647 | /* |
638 | * Turn off check_host_ip if the connection is to localhost, via proxy | 648 | * Turn off check_host_ip if the connection is to localhost, via proxy |
639 | * command or if we don't have a hostname to compare with | 649 | * command or if we don't have a hostname to compare with |
@@ -718,6 +728,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
718 | logit("Warning: Permanently added the %s host " | 728 | logit("Warning: Permanently added the %s host " |
719 | "key for IP address '%.128s' to the list " | 729 | "key for IP address '%.128s' to the list " |
720 | "of known hosts.", type, ip); | 730 | "of known hosts.", type, ip); |
731 | } else if (options.visual_host_key) { | ||
732 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); | ||
733 | ra = key_fingerprint(host_key, SSH_FP_MD5, | ||
734 | SSH_FP_RANDOMART); | ||
735 | logit("Host key fingerprint is %s\n%s\n", fp, ra); | ||
736 | xfree(ra); | ||
737 | xfree(fp); | ||
721 | } | 738 | } |
722 | break; | 739 | break; |
723 | case HOST_NEW: | 740 | case HOST_NEW: |
@@ -753,6 +770,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
753 | snprintf(msg1, sizeof(msg1), "."); | 770 | snprintf(msg1, sizeof(msg1), "."); |
754 | /* The default */ | 771 | /* The default */ |
755 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); | 772 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); |
773 | ra = key_fingerprint(host_key, SSH_FP_MD5, | ||
774 | SSH_FP_RANDOMART); | ||
756 | msg2[0] = '\0'; | 775 | msg2[0] = '\0'; |
757 | if (options.verify_host_key_dns) { | 776 | if (options.verify_host_key_dns) { |
758 | if (matching_host_key_dns) | 777 | if (matching_host_key_dns) |
@@ -767,10 +786,14 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
767 | snprintf(msg, sizeof(msg), | 786 | snprintf(msg, sizeof(msg), |
768 | "The authenticity of host '%.200s (%s)' can't be " | 787 | "The authenticity of host '%.200s (%s)' can't be " |
769 | "established%s\n" | 788 | "established%s\n" |
770 | "%s key fingerprint is %s.\n%s" | 789 | "%s key fingerprint is %s.%s%s\n%s" |
771 | "Are you sure you want to continue connecting " | 790 | "Are you sure you want to continue connecting " |
772 | "(yes/no)? ", | 791 | "(yes/no)? ", |
773 | host, ip, msg1, type, fp, msg2); | 792 | host, ip, msg1, type, fp, |
793 | options.visual_host_key ? "\n" : "", | ||
794 | options.visual_host_key ? ra : "", | ||
795 | msg2); | ||
796 | xfree(ra); | ||
774 | xfree(fp); | 797 | xfree(fp); |
775 | if (!confirm(msg)) | 798 | if (!confirm(msg)) |
776 | goto fail; | 799 | goto fail; |
@@ -823,7 +846,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
823 | error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); | 846 | error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); |
824 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 847 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
825 | error("The %s host key for %s has changed,", type, host); | 848 | error("The %s host key for %s has changed,", type, host); |
826 | error("and the key for the according IP address %s", ip); | 849 | error("and the key for the corresponding IP address %s", ip); |
827 | error("%s. This could either mean that", key_msg); | 850 | error("%s. This could either mean that", key_msg); |
828 | error("DNS SPOOFING is happening or the IP address for the host"); | 851 | error("DNS SPOOFING is happening or the IP address for the host"); |
829 | error("and its host key have changed at the same time."); | 852 | error("and its host key have changed at the same time."); |
@@ -855,27 +878,32 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
855 | error("Password authentication is disabled to avoid " | 878 | error("Password authentication is disabled to avoid " |
856 | "man-in-the-middle attacks."); | 879 | "man-in-the-middle attacks."); |
857 | options.password_authentication = 0; | 880 | options.password_authentication = 0; |
881 | cancelled_forwarding = 1; | ||
858 | } | 882 | } |
859 | if (options.kbd_interactive_authentication) { | 883 | if (options.kbd_interactive_authentication) { |
860 | error("Keyboard-interactive authentication is disabled" | 884 | error("Keyboard-interactive authentication is disabled" |
861 | " to avoid man-in-the-middle attacks."); | 885 | " to avoid man-in-the-middle attacks."); |
862 | options.kbd_interactive_authentication = 0; | 886 | options.kbd_interactive_authentication = 0; |
863 | options.challenge_response_authentication = 0; | 887 | options.challenge_response_authentication = 0; |
888 | cancelled_forwarding = 1; | ||
864 | } | 889 | } |
865 | if (options.challenge_response_authentication) { | 890 | if (options.challenge_response_authentication) { |
866 | error("Challenge/response authentication is disabled" | 891 | error("Challenge/response authentication is disabled" |
867 | " to avoid man-in-the-middle attacks."); | 892 | " to avoid man-in-the-middle attacks."); |
868 | options.challenge_response_authentication = 0; | 893 | options.challenge_response_authentication = 0; |
894 | cancelled_forwarding = 1; | ||
869 | } | 895 | } |
870 | if (options.forward_agent) { | 896 | if (options.forward_agent) { |
871 | error("Agent forwarding is disabled to avoid " | 897 | error("Agent forwarding is disabled to avoid " |
872 | "man-in-the-middle attacks."); | 898 | "man-in-the-middle attacks."); |
873 | options.forward_agent = 0; | 899 | options.forward_agent = 0; |
900 | cancelled_forwarding = 1; | ||
874 | } | 901 | } |
875 | if (options.forward_x11) { | 902 | if (options.forward_x11) { |
876 | error("X11 forwarding is disabled to avoid " | 903 | error("X11 forwarding is disabled to avoid " |
877 | "man-in-the-middle attacks."); | 904 | "man-in-the-middle attacks."); |
878 | options.forward_x11 = 0; | 905 | options.forward_x11 = 0; |
906 | cancelled_forwarding = 1; | ||
879 | } | 907 | } |
880 | if (options.num_local_forwards > 0 || | 908 | if (options.num_local_forwards > 0 || |
881 | options.num_remote_forwards > 0) { | 909 | options.num_remote_forwards > 0) { |
@@ -883,12 +911,18 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
883 | "man-in-the-middle attacks."); | 911 | "man-in-the-middle attacks."); |
884 | options.num_local_forwards = | 912 | options.num_local_forwards = |
885 | options.num_remote_forwards = 0; | 913 | options.num_remote_forwards = 0; |
914 | cancelled_forwarding = 1; | ||
886 | } | 915 | } |
887 | if (options.tun_open != SSH_TUNMODE_NO) { | 916 | if (options.tun_open != SSH_TUNMODE_NO) { |
888 | error("Tunnel forwarding is disabled to avoid " | 917 | error("Tunnel forwarding is disabled to avoid " |
889 | "man-in-the-middle attacks."); | 918 | "man-in-the-middle attacks."); |
890 | options.tun_open = SSH_TUNMODE_NO; | 919 | options.tun_open = SSH_TUNMODE_NO; |
920 | cancelled_forwarding = 1; | ||
891 | } | 921 | } |
922 | if (options.exit_on_forward_failure && cancelled_forwarding) | ||
923 | fatal("Error: forwarding disabled due to host key " | ||
924 | "check failure"); | ||
925 | |||
892 | /* | 926 | /* |
893 | * XXX Should permit the user to change to use the new id. | 927 | * XXX Should permit the user to change to use the new id. |
894 | * This could be done by converting the host key to an | 928 | * This could be done by converting the host key to an |
@@ -987,7 +1021,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
987 | */ | 1021 | */ |
988 | void | 1022 | void |
989 | ssh_login(Sensitive *sensitive, const char *orighost, | 1023 | ssh_login(Sensitive *sensitive, const char *orighost, |
990 | struct sockaddr *hostaddr, struct passwd *pw) | 1024 | struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms) |
991 | { | 1025 | { |
992 | char *host, *cp; | 1026 | char *host, *cp; |
993 | char *server_user, *local_user; | 1027 | char *server_user, *local_user; |
@@ -1002,7 +1036,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, | |||
1002 | *cp = (char)tolower(*cp); | 1036 | *cp = (char)tolower(*cp); |
1003 | 1037 | ||
1004 | /* Exchange protocol version identification strings with the server. */ | 1038 | /* Exchange protocol version identification strings with the server. */ |
1005 | ssh_exchange_identification(); | 1039 | ssh_exchange_identification(timeout_ms); |
1006 | 1040 | ||
1007 | /* Put the connection into non-blocking mode. */ | 1041 | /* Put the connection into non-blocking mode. */ |
1008 | packet_set_nonblocking(); | 1042 | packet_set_nonblocking(); |
@@ -1041,18 +1075,20 @@ static int | |||
1041 | show_key_from_file(const char *file, const char *host, int keytype) | 1075 | show_key_from_file(const char *file, const char *host, int keytype) |
1042 | { | 1076 | { |
1043 | Key *found; | 1077 | Key *found; |
1044 | char *fp; | 1078 | char *fp, *ra; |
1045 | int line, ret; | 1079 | int line, ret; |
1046 | 1080 | ||
1047 | found = key_new(keytype); | 1081 | found = key_new(keytype); |
1048 | if ((ret = lookup_key_in_hostfile_by_type(file, host, | 1082 | if ((ret = lookup_key_in_hostfile_by_type(file, host, |
1049 | keytype, found, &line))) { | 1083 | keytype, found, &line))) { |
1050 | fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); | 1084 | fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); |
1085 | ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART); | ||
1051 | logit("WARNING: %s key found for host %s\n" | 1086 | logit("WARNING: %s key found for host %s\n" |
1052 | "in %s:%d\n" | 1087 | "in %s:%d\n" |
1053 | "%s key fingerprint %s.", | 1088 | "%s key fingerprint %s.\n%s\n", |
1054 | key_type(found), host, file, line, | 1089 | key_type(found), host, file, line, |
1055 | key_type(found), fp); | 1090 | key_type(found), fp, ra); |
1091 | xfree(ra); | ||
1056 | xfree(fp); | 1092 | xfree(fp); |
1057 | } | 1093 | } |
1058 | key_free(found); | 1094 | key_free(found); |