diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 152 |
1 files changed, 93 insertions, 59 deletions
diff --git a/sshconnect.c b/sshconnect.c index 64ffec240..a222233d0 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,3 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.200 2006/10/10 10:12:45 markus Exp $ */ | ||
1 | /* | 2 | /* |
2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -13,12 +14,35 @@ | |||
13 | */ | 14 | */ |
14 | 15 | ||
15 | #include "includes.h" | 16 | #include "includes.h" |
16 | RCSID("$OpenBSD: sshconnect.c,v 1.171 2005/12/06 22:38:27 reyk Exp $"); | ||
17 | 17 | ||
18 | #include <openssl/bn.h> | 18 | #include <sys/types.h> |
19 | #include <sys/wait.h> | ||
20 | #include <sys/stat.h> | ||
21 | #include <sys/socket.h> | ||
22 | #ifdef HAVE_SYS_TIME_H | ||
23 | # include <sys/time.h> | ||
24 | #endif | ||
25 | |||
26 | #include <netinet/in.h> | ||
27 | #include <arpa/inet.h> | ||
28 | |||
29 | #include <ctype.h> | ||
30 | #include <errno.h> | ||
31 | #include <netdb.h> | ||
32 | #ifdef HAVE_PATHS_H | ||
33 | #include <paths.h> | ||
34 | #endif | ||
35 | #include <pwd.h> | ||
36 | #include <stdarg.h> | ||
37 | #include <stdio.h> | ||
38 | #include <stdlib.h> | ||
39 | #include <string.h> | ||
40 | #include <unistd.h> | ||
19 | 41 | ||
20 | #include "ssh.h" | ||
21 | #include "xmalloc.h" | 42 | #include "xmalloc.h" |
43 | #include "key.h" | ||
44 | #include "hostfile.h" | ||
45 | #include "ssh.h" | ||
22 | #include "rsa.h" | 46 | #include "rsa.h" |
23 | #include "buffer.h" | 47 | #include "buffer.h" |
24 | #include "packet.h" | 48 | #include "packet.h" |
@@ -32,6 +56,7 @@ RCSID("$OpenBSD: sshconnect.c,v 1.171 2005/12/06 22:38:27 reyk Exp $"); | |||
32 | #include "atomicio.h" | 56 | #include "atomicio.h" |
33 | #include "misc.h" | 57 | #include "misc.h" |
34 | #include "dns.h" | 58 | #include "dns.h" |
59 | #include "version.h" | ||
35 | 60 | ||
36 | char *client_version_string = NULL; | 61 | char *client_version_string = NULL; |
37 | char *server_version_string = NULL; | 62 | char *server_version_string = NULL; |
@@ -62,7 +87,6 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
62 | int pin[2], pout[2]; | 87 | int pin[2], pout[2]; |
63 | pid_t pid; | 88 | pid_t pid; |
64 | char strport[NI_MAXSERV]; | 89 | char strport[NI_MAXSERV]; |
65 | size_t len; | ||
66 | 90 | ||
67 | /* Convert the port number into a string. */ | 91 | /* Convert the port number into a string. */ |
68 | snprintf(strport, sizeof strport, "%hu", port); | 92 | snprintf(strport, sizeof strport, "%hu", port); |
@@ -74,10 +98,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
74 | * Use "exec" to avoid "sh -c" processes on some platforms | 98 | * Use "exec" to avoid "sh -c" processes on some platforms |
75 | * (e.g. Solaris) | 99 | * (e.g. Solaris) |
76 | */ | 100 | */ |
77 | len = strlen(proxy_command) + 6; | 101 | xasprintf(&tmp, "exec %s", proxy_command); |
78 | tmp = xmalloc(len); | ||
79 | strlcpy(tmp, "exec ", len); | ||
80 | strlcat(tmp, proxy_command, len); | ||
81 | command_string = percent_expand(tmp, "h", host, | 102 | command_string = percent_expand(tmp, "h", host, |
82 | "p", strport, (char *)NULL); | 103 | "p", strport, (char *)NULL); |
83 | xfree(tmp); | 104 | xfree(tmp); |
@@ -94,8 +115,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
94 | char *argv[10]; | 115 | char *argv[10]; |
95 | 116 | ||
96 | /* Child. Permanently give up superuser privileges. */ | 117 | /* Child. Permanently give up superuser privileges. */ |
97 | seteuid(original_real_uid); | 118 | permanently_drop_suid(original_real_uid); |
98 | setuid(original_real_uid); | ||
99 | 119 | ||
100 | /* Redirect stdin and stdout. */ | 120 | /* Redirect stdin and stdout. */ |
101 | close(pin[1]); | 121 | close(pin[1]); |
@@ -205,7 +225,7 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, | |||
205 | fd_set *fdset; | 225 | fd_set *fdset; |
206 | struct timeval tv; | 226 | struct timeval tv; |
207 | socklen_t optlen; | 227 | socklen_t optlen; |
208 | int fdsetsz, optval, rc, result = -1; | 228 | int optval, rc, result = -1; |
209 | 229 | ||
210 | if (timeout <= 0) | 230 | if (timeout <= 0) |
211 | return (connect(sockfd, serv_addr, addrlen)); | 231 | return (connect(sockfd, serv_addr, addrlen)); |
@@ -219,10 +239,8 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, | |||
219 | if (errno != EINPROGRESS) | 239 | if (errno != EINPROGRESS) |
220 | return (-1); | 240 | return (-1); |
221 | 241 | ||
222 | fdsetsz = howmany(sockfd + 1, NFDBITS) * sizeof(fd_mask); | 242 | fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), |
223 | fdset = (fd_set *)xmalloc(fdsetsz); | 243 | sizeof(fd_mask)); |
224 | |||
225 | memset(fdset, 0, fdsetsz); | ||
226 | FD_SET(sockfd, fdset); | 244 | FD_SET(sockfd, fdset); |
227 | tv.tv_sec = timeout; | 245 | tv.tv_sec = timeout; |
228 | tv.tv_usec = 0; | 246 | tv.tv_usec = 0; |
@@ -305,17 +323,16 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
305 | fatal("%s: %.100s: %s", __progname, host, | 323 | fatal("%s: %.100s: %s", __progname, host, |
306 | gai_strerror(gaierr)); | 324 | gai_strerror(gaierr)); |
307 | 325 | ||
308 | /* | 326 | for (attempt = 0; attempt < connection_attempts; attempt++) { |
309 | * Try to connect several times. On some machines, the first time | 327 | if (attempt > 0) { |
310 | * will sometimes fail. In general socket code appears to behave | 328 | /* Sleep a moment before retrying. */ |
311 | * quite magically on many machines. | 329 | sleep(1); |
312 | */ | ||
313 | for (attempt = 0; ;) { | ||
314 | if (attempt > 0) | ||
315 | debug("Trying again..."); | 330 | debug("Trying again..."); |
316 | 331 | } | |
317 | /* Loop through addresses for this host, and try each one in | 332 | /* |
318 | sequence until the connection succeeds. */ | 333 | * Loop through addresses for this host, and try each one in |
334 | * sequence until the connection succeeds. | ||
335 | */ | ||
319 | for (ai = aitop; ai; ai = ai->ai_next) { | 336 | for (ai = aitop; ai; ai = ai->ai_next) { |
320 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) | 337 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) |
321 | continue; | 338 | continue; |
@@ -342,29 +359,18 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
342 | } else { | 359 | } else { |
343 | debug("connect to address %s port %s: %s", | 360 | debug("connect to address %s port %s: %s", |
344 | ntop, strport, strerror(errno)); | 361 | ntop, strport, strerror(errno)); |
345 | /* | ||
346 | * Close the failed socket; there appear to | ||
347 | * be some problems when reusing a socket for | ||
348 | * which connect() has already returned an | ||
349 | * error. | ||
350 | */ | ||
351 | close(sock); | 362 | close(sock); |
363 | sock = -1; | ||
352 | } | 364 | } |
353 | } | 365 | } |
354 | if (ai) | 366 | if (sock != -1) |
355 | break; /* Successful connection. */ | 367 | break; /* Successful connection. */ |
356 | |||
357 | attempt++; | ||
358 | if (attempt >= connection_attempts) | ||
359 | break; | ||
360 | /* Sleep a moment before retrying. */ | ||
361 | sleep(1); | ||
362 | } | 368 | } |
363 | 369 | ||
364 | freeaddrinfo(aitop); | 370 | freeaddrinfo(aitop); |
365 | 371 | ||
366 | /* Return failure if we didn't get a successful connection. */ | 372 | /* Return failure if we didn't get a successful connection. */ |
367 | if (attempt >= connection_attempts) { | 373 | if (sock == -1) { |
368 | error("ssh: connect to host %s port %s: %s", | 374 | error("ssh: connect to host %s port %s: %s", |
369 | host, strport, strerror(errno)); | 375 | host, strport, strerror(errno)); |
370 | return (-1); | 376 | return (-1); |
@@ -396,10 +402,10 @@ ssh_exchange_identification(void) | |||
396 | int connection_in = packet_get_connection_in(); | 402 | int connection_in = packet_get_connection_in(); |
397 | int connection_out = packet_get_connection_out(); | 403 | int connection_out = packet_get_connection_out(); |
398 | int minor1 = PROTOCOL_MINOR_1; | 404 | int minor1 = PROTOCOL_MINOR_1; |
399 | u_int i; | 405 | u_int i, n; |
400 | 406 | ||
401 | /* Read other side's version identification. */ | 407 | /* Read other side's version identification. */ |
402 | for (;;) { | 408 | for (n = 0;;) { |
403 | for (i = 0; i < sizeof(buf) - 1; i++) { | 409 | for (i = 0; i < sizeof(buf) - 1; i++) { |
404 | size_t len = atomicio(read, connection_in, &buf[i], 1); | 410 | size_t len = atomicio(read, connection_in, &buf[i], 1); |
405 | 411 | ||
@@ -416,6 +422,8 @@ ssh_exchange_identification(void) | |||
416 | buf[i + 1] = 0; | 422 | buf[i + 1] = 0; |
417 | break; | 423 | break; |
418 | } | 424 | } |
425 | if (++n > 65536) | ||
426 | fatal("ssh_exchange_identification: No banner received"); | ||
419 | } | 427 | } |
420 | buf[sizeof(buf) - 1] = 0; | 428 | buf[sizeof(buf) - 1] = 0; |
421 | if (strncmp(buf, "SSH-", 4) == 0) | 429 | if (strncmp(buf, "SSH-", 4) == 0) |
@@ -517,13 +525,17 @@ confirm(const char *prompt) | |||
517 | * check whether the supplied host key is valid, return -1 if the key | 525 | * check whether the supplied host key is valid, return -1 if the key |
518 | * is not valid. the user_hostfile will not be updated if 'readonly' is true. | 526 | * is not valid. the user_hostfile will not be updated if 'readonly' is true. |
519 | */ | 527 | */ |
528 | #define RDRW 0 | ||
529 | #define RDONLY 1 | ||
530 | #define ROQUIET 2 | ||
520 | static int | 531 | static int |
521 | check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | 532 | check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, |
522 | int readonly, const char *user_hostfile, const char *system_hostfile) | 533 | Key *host_key, int readonly, const char *user_hostfile, |
534 | const char *system_hostfile) | ||
523 | { | 535 | { |
524 | Key *file_key; | 536 | Key *file_key; |
525 | const char *type = key_type(host_key); | 537 | const char *type = key_type(host_key); |
526 | char *ip = NULL; | 538 | char *ip = NULL, *host = NULL; |
527 | char hostline[1000], *hostp, *fp; | 539 | char hostline[1000], *hostp, *fp; |
528 | HostStatus host_status; | 540 | HostStatus host_status; |
529 | HostStatus ip_status; | 541 | HostStatus ip_status; |
@@ -574,7 +586,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
574 | if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), | 586 | if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), |
575 | NULL, 0, NI_NUMERICHOST) != 0) | 587 | NULL, 0, NI_NUMERICHOST) != 0) |
576 | fatal("check_host_key: getnameinfo failed"); | 588 | fatal("check_host_key: getnameinfo failed"); |
577 | ip = xstrdup(ntop); | 589 | ip = put_host_port(ntop, port); |
578 | } else { | 590 | } else { |
579 | ip = xstrdup("<no hostip for proxy command>"); | 591 | ip = xstrdup("<no hostip for proxy command>"); |
580 | } | 592 | } |
@@ -582,18 +594,21 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
582 | * Turn off check_host_ip if the connection is to localhost, via proxy | 594 | * Turn off check_host_ip if the connection is to localhost, via proxy |
583 | * command or if we don't have a hostname to compare with | 595 | * command or if we don't have a hostname to compare with |
584 | */ | 596 | */ |
585 | if (options.check_host_ip && | 597 | if (options.check_host_ip && (local || |
586 | (local || strcmp(host, ip) == 0 || options.proxy_command != NULL)) | 598 | strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) |
587 | options.check_host_ip = 0; | 599 | options.check_host_ip = 0; |
588 | 600 | ||
589 | /* | 601 | /* |
590 | * Allow the user to record the key under a different name. This is | 602 | * Allow the user to record the key under a different name or |
591 | * useful for ssh tunneling over forwarded connections or if you run | 603 | * differentiate a non-standard port. This is useful for ssh |
592 | * multiple sshd's on different ports on the same machine. | 604 | * tunneling over forwarded connections or if you run multiple |
605 | * sshd's on different ports on the same machine. | ||
593 | */ | 606 | */ |
594 | if (options.host_key_alias != NULL) { | 607 | if (options.host_key_alias != NULL) { |
595 | host = options.host_key_alias; | 608 | host = xstrdup(options.host_key_alias); |
596 | debug("using hostkeyalias: %s", host); | 609 | debug("using hostkeyalias: %s", host); |
610 | } else { | ||
611 | host = put_host_port(hostname, port); | ||
597 | } | 612 | } |
598 | 613 | ||
599 | /* | 614 | /* |
@@ -662,6 +677,15 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
662 | } | 677 | } |
663 | break; | 678 | break; |
664 | case HOST_NEW: | 679 | case HOST_NEW: |
680 | if (options.host_key_alias == NULL && port != 0 && | ||
681 | port != SSH_DEFAULT_PORT) { | ||
682 | debug("checking without port identifier"); | ||
683 | if (check_host_key(hostname, hostaddr, 0, host_key, 2, | ||
684 | user_hostfile, system_hostfile) == 0) { | ||
685 | debug("found matching key w/out port"); | ||
686 | break; | ||
687 | } | ||
688 | } | ||
665 | if (readonly) | 689 | if (readonly) |
666 | goto fail; | 690 | goto fail; |
667 | /* The host is new. */ | 691 | /* The host is new. */ |
@@ -741,6 +765,8 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
741 | "list of known hosts.", hostp, type); | 765 | "list of known hosts.", hostp, type); |
742 | break; | 766 | break; |
743 | case HOST_CHANGED: | 767 | case HOST_CHANGED: |
768 | if (readonly == ROQUIET) | ||
769 | goto fail; | ||
744 | if (options.check_host_ip && host_ip_differ) { | 770 | if (options.check_host_ip && host_ip_differ) { |
745 | char *key_msg; | 771 | char *key_msg; |
746 | if (ip_status == HOST_NEW) | 772 | if (ip_status == HOST_NEW) |
@@ -779,7 +805,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
779 | /* | 805 | /* |
780 | * If strict host key checking has not been requested, allow | 806 | * If strict host key checking has not been requested, allow |
781 | * the connection but without MITM-able authentication or | 807 | * the connection but without MITM-able authentication or |
782 | * agent forwarding. | 808 | * forwarding. |
783 | */ | 809 | */ |
784 | if (options.password_authentication) { | 810 | if (options.password_authentication) { |
785 | error("Password authentication is disabled to avoid " | 811 | error("Password authentication is disabled to avoid " |
@@ -814,6 +840,11 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
814 | options.num_local_forwards = | 840 | options.num_local_forwards = |
815 | options.num_remote_forwards = 0; | 841 | options.num_remote_forwards = 0; |
816 | } | 842 | } |
843 | if (options.tun_open != SSH_TUNMODE_NO) { | ||
844 | error("Tunnel forwarding is disabled to avoid " | ||
845 | "man-in-the-middle attacks."); | ||
846 | options.tun_open = SSH_TUNMODE_NO; | ||
847 | } | ||
817 | /* | 848 | /* |
818 | * XXX Should permit the user to change to use the new id. | 849 | * XXX Should permit the user to change to use the new id. |
819 | * This could be done by converting the host key to an | 850 | * This could be done by converting the host key to an |
@@ -855,10 +886,12 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
855 | } | 886 | } |
856 | 887 | ||
857 | xfree(ip); | 888 | xfree(ip); |
889 | xfree(host); | ||
858 | return 0; | 890 | return 0; |
859 | 891 | ||
860 | fail: | 892 | fail: |
861 | xfree(ip); | 893 | xfree(ip); |
894 | xfree(host); | ||
862 | return -1; | 895 | return -1; |
863 | } | 896 | } |
864 | 897 | ||
@@ -892,12 +925,13 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
892 | /* return ok if the key can be found in an old keyfile */ | 925 | /* return ok if the key can be found in an old keyfile */ |
893 | if (stat(options.system_hostfile2, &st) == 0 || | 926 | if (stat(options.system_hostfile2, &st) == 0 || |
894 | stat(options.user_hostfile2, &st) == 0) { | 927 | stat(options.user_hostfile2, &st) == 0) { |
895 | if (check_host_key(host, hostaddr, host_key, /*readonly*/ 1, | 928 | if (check_host_key(host, hostaddr, options.port, host_key, |
896 | options.user_hostfile2, options.system_hostfile2) == 0) | 929 | RDONLY, options.user_hostfile2, |
930 | options.system_hostfile2) == 0) | ||
897 | return 0; | 931 | return 0; |
898 | } | 932 | } |
899 | return check_host_key(host, hostaddr, host_key, /*readonly*/ 0, | 933 | return check_host_key(host, hostaddr, options.port, host_key, |
900 | options.user_hostfile, options.system_hostfile); | 934 | RDRW, options.user_hostfile, options.system_hostfile); |
901 | } | 935 | } |
902 | 936 | ||
903 | /* | 937 | /* |
@@ -921,7 +955,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, | |||
921 | host = xstrdup(orighost); | 955 | host = xstrdup(orighost); |
922 | for (cp = host; *cp; cp++) | 956 | for (cp = host; *cp; cp++) |
923 | if (isupper(*cp)) | 957 | if (isupper(*cp)) |
924 | *cp = tolower(*cp); | 958 | *cp = (char)tolower(*cp); |
925 | 959 | ||
926 | /* Exchange protocol version identification strings with the server. */ | 960 | /* Exchange protocol version identification strings with the server. */ |
927 | ssh_exchange_identification(); | 961 | ssh_exchange_identification(); |
@@ -938,6 +972,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, | |||
938 | ssh_kex(host, hostaddr); | 972 | ssh_kex(host, hostaddr); |
939 | ssh_userauth1(local_user, server_user, host, sensitive); | 973 | ssh_userauth1(local_user, server_user, host, sensitive); |
940 | } | 974 | } |
975 | xfree(local_user); | ||
941 | } | 976 | } |
942 | 977 | ||
943 | void | 978 | void |
@@ -951,8 +986,7 @@ ssh_put_password(char *password) | |||
951 | return; | 986 | return; |
952 | } | 987 | } |
953 | size = roundup(strlen(password) + 1, 32); | 988 | size = roundup(strlen(password) + 1, 32); |
954 | padded = xmalloc(size); | 989 | padded = xcalloc(1, size); |
955 | memset(padded, 0, size); | ||
956 | strlcpy(padded, password, size); | 990 | strlcpy(padded, password, size); |
957 | packet_put_string(padded, size); | 991 | packet_put_string(padded, size); |
958 | memset(padded, 0, size); | 992 | memset(padded, 0, size); |