diff options
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); |