diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 185 |
1 files changed, 28 insertions, 157 deletions
diff --git a/sshconnect.c b/sshconnect.c index 5c56f3178..013a896b7 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -13,7 +13,7 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include "includes.h" | 15 | #include "includes.h" |
16 | RCSID("$OpenBSD: sshconnect.c,v 1.148 2003/09/18 07:52:54 markus Exp $"); | 16 | RCSID("$OpenBSD: sshconnect.c,v 1.137 2002/11/21 23:03:51 deraadt Exp $"); |
17 | 17 | ||
18 | #include <openssl/bn.h> | 18 | #include <openssl/bn.h> |
19 | 19 | ||
@@ -33,17 +33,9 @@ RCSID("$OpenBSD: sshconnect.c,v 1.148 2003/09/18 07:52:54 markus Exp $"); | |||
33 | #include "misc.h" | 33 | #include "misc.h" |
34 | #include "readpass.h" | 34 | #include "readpass.h" |
35 | 35 | ||
36 | #ifdef DNS | ||
37 | #include "dns.h" | ||
38 | #endif | ||
39 | |||
40 | char *client_version_string = NULL; | 36 | char *client_version_string = NULL; |
41 | char *server_version_string = NULL; | 37 | char *server_version_string = NULL; |
42 | 38 | ||
43 | #ifdef DNS | ||
44 | int verified_host_key_dns = 0; | ||
45 | #endif | ||
46 | |||
47 | /* import */ | 39 | /* import */ |
48 | extern Options options; | 40 | extern Options options; |
49 | extern char *__progname; | 41 | extern char *__progname; |
@@ -178,7 +170,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
178 | * Creates a (possibly privileged) socket for use as the ssh connection. | 170 | * Creates a (possibly privileged) socket for use as the ssh connection. |
179 | */ | 171 | */ |
180 | static int | 172 | static int |
181 | ssh_create_socket(int privileged, struct addrinfo *ai) | 173 | ssh_create_socket(int privileged, int family) |
182 | { | 174 | { |
183 | int sock, gaierr; | 175 | int sock, gaierr; |
184 | struct addrinfo hints, *res; | 176 | struct addrinfo hints, *res; |
@@ -190,16 +182,15 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
190 | if (privileged) { | 182 | if (privileged) { |
191 | int p = IPPORT_RESERVED - 1; | 183 | int p = IPPORT_RESERVED - 1; |
192 | PRIV_START; | 184 | PRIV_START; |
193 | sock = rresvport_af(&p, ai->ai_family); | 185 | sock = rresvport_af(&p, family); |
194 | PRIV_END; | 186 | PRIV_END; |
195 | if (sock < 0) | 187 | if (sock < 0) |
196 | error("rresvport: af=%d %.100s", ai->ai_family, | 188 | error("rresvport: af=%d %.100s", family, strerror(errno)); |
197 | strerror(errno)); | ||
198 | else | 189 | else |
199 | debug("Allocated local port %d.", p); | 190 | debug("Allocated local port %d.", p); |
200 | return sock; | 191 | return sock; |
201 | } | 192 | } |
202 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | 193 | sock = socket(family, SOCK_STREAM, 0); |
203 | if (sock < 0) | 194 | if (sock < 0) |
204 | error("socket: %.100s", strerror(errno)); | 195 | error("socket: %.100s", strerror(errno)); |
205 | 196 | ||
@@ -208,9 +199,8 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
208 | return sock; | 199 | return sock; |
209 | 200 | ||
210 | memset(&hints, 0, sizeof(hints)); | 201 | memset(&hints, 0, sizeof(hints)); |
211 | hints.ai_family = ai->ai_family; | 202 | hints.ai_family = family; |
212 | hints.ai_socktype = ai->ai_socktype; | 203 | hints.ai_socktype = SOCK_STREAM; |
213 | hints.ai_protocol = ai->ai_protocol; | ||
214 | hints.ai_flags = AI_PASSIVE; | 204 | hints.ai_flags = AI_PASSIVE; |
215 | gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); | 205 | gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); |
216 | if (gaierr) { | 206 | if (gaierr) { |
@@ -229,74 +219,6 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
229 | return sock; | 219 | return sock; |
230 | } | 220 | } |
231 | 221 | ||
232 | static int | ||
233 | timeout_connect(int sockfd, const struct sockaddr *serv_addr, | ||
234 | socklen_t addrlen, int timeout) | ||
235 | { | ||
236 | fd_set *fdset; | ||
237 | struct timeval tv; | ||
238 | socklen_t optlen; | ||
239 | int fdsetsz, optval, rc, result = -1; | ||
240 | |||
241 | if (timeout <= 0) | ||
242 | return (connect(sockfd, serv_addr, addrlen)); | ||
243 | |||
244 | if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) | ||
245 | return (-1); | ||
246 | |||
247 | rc = connect(sockfd, serv_addr, addrlen); | ||
248 | if (rc == 0) | ||
249 | return (0); | ||
250 | if (errno != EINPROGRESS) | ||
251 | return (-1); | ||
252 | |||
253 | fdsetsz = howmany(sockfd + 1, NFDBITS) * sizeof(fd_mask); | ||
254 | fdset = (fd_set *)xmalloc(fdsetsz); | ||
255 | |||
256 | memset(fdset, 0, fdsetsz); | ||
257 | FD_SET(sockfd, fdset); | ||
258 | tv.tv_sec = timeout; | ||
259 | tv.tv_usec = 0; | ||
260 | |||
261 | for(;;) { | ||
262 | rc = select(sockfd + 1, NULL, fdset, NULL, &tv); | ||
263 | if (rc != -1 || errno != EINTR) | ||
264 | break; | ||
265 | } | ||
266 | |||
267 | switch(rc) { | ||
268 | case 0: | ||
269 | /* Timed out */ | ||
270 | errno = ETIMEDOUT; | ||
271 | break; | ||
272 | case -1: | ||
273 | /* Select error */ | ||
274 | debug("select: %s", strerror(errno)); | ||
275 | break; | ||
276 | case 1: | ||
277 | /* Completed or failed */ | ||
278 | optval = 0; | ||
279 | optlen = sizeof(optval); | ||
280 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, | ||
281 | &optlen) == -1) { | ||
282 | debug("getsockopt: %s", strerror(errno)); | ||
283 | break; | ||
284 | } | ||
285 | if (optval != 0) { | ||
286 | errno = optval; | ||
287 | break; | ||
288 | } | ||
289 | result = 0; | ||
290 | break; | ||
291 | default: | ||
292 | /* Should not occur */ | ||
293 | fatal("Bogus return (%d) from select()", rc); | ||
294 | } | ||
295 | |||
296 | xfree(fdset); | ||
297 | return (result); | ||
298 | } | ||
299 | |||
300 | /* | 222 | /* |
301 | * Opens a TCP/IP connection to the remote server on the given host. | 223 | * Opens a TCP/IP connection to the remote server on the given host. |
302 | * The address of the remote host will be returned in hostaddr. | 224 | * The address of the remote host will be returned in hostaddr. |
@@ -380,13 +302,12 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
380 | host, ntop, strport); | 302 | host, ntop, strport); |
381 | 303 | ||
382 | /* Create a socket for connecting. */ | 304 | /* Create a socket for connecting. */ |
383 | sock = ssh_create_socket(needpriv, ai); | 305 | sock = ssh_create_socket(needpriv, ai->ai_family); |
384 | if (sock < 0) | 306 | if (sock < 0) |
385 | /* Any error is already output */ | 307 | /* Any error is already output */ |
386 | continue; | 308 | continue; |
387 | 309 | ||
388 | if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, | 310 | if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { |
389 | options.connection_timeout) >= 0) { | ||
390 | /* Successful connection. */ | 311 | /* Successful connection. */ |
391 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); | 312 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); |
392 | break; | 313 | break; |
@@ -418,7 +339,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
418 | 339 | ||
419 | /* Return failure if we didn't get a successful connection. */ | 340 | /* Return failure if we didn't get a successful connection. */ |
420 | if (attempt >= connection_attempts) { | 341 | if (attempt >= connection_attempts) { |
421 | logit("ssh: connect to host %s port %s: %s", | 342 | log("ssh: connect to host %s port %s: %s", |
422 | host, strport, strerror(errno)); | 343 | host, strport, strerror(errno)); |
423 | return full_failure ? ECONNABORTED : ECONNREFUSED; | 344 | return full_failure ? ECONNABORTED : ECONNREFUSED; |
424 | } | 345 | } |
@@ -532,7 +453,7 @@ ssh_exchange_identification(void) | |||
532 | enable_compat13(); | 453 | enable_compat13(); |
533 | minor1 = 3; | 454 | minor1 = 3; |
534 | if (options.forward_agent) { | 455 | if (options.forward_agent) { |
535 | logit("Agent forwarding disabled for protocol 1.3"); | 456 | log("Agent forwarding disabled for protocol 1.3"); |
536 | options.forward_agent = 0; | 457 | options.forward_agent = 0; |
537 | } | 458 | } |
538 | } | 459 | } |
@@ -556,7 +477,7 @@ ssh_exchange_identification(void) | |||
556 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | 477 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, |
557 | compat20 ? PROTOCOL_MINOR_2 : minor1, | 478 | compat20 ? PROTOCOL_MINOR_2 : minor1, |
558 | SSH_VERSION); | 479 | SSH_VERSION); |
559 | if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) | 480 | if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) |
560 | fatal("write: %.100s", strerror(errno)); | 481 | fatal("write: %.100s", strerror(errno)); |
561 | client_version_string = xstrdup(buf); | 482 | client_version_string = xstrdup(buf); |
562 | chop(client_version_string); | 483 | chop(client_version_string); |
@@ -607,7 +528,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
607 | int salen; | 528 | int salen; |
608 | char ntop[NI_MAXHOST]; | 529 | char ntop[NI_MAXHOST]; |
609 | char msg[1024]; | 530 | char msg[1024]; |
610 | int len, host_line, ip_line; | 531 | int len, host_line, ip_line, has_keys; |
611 | const char *host_file = NULL, *ip_file = NULL; | 532 | const char *host_file = NULL, *ip_file = NULL; |
612 | 533 | ||
613 | /* | 534 | /* |
@@ -723,16 +644,16 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
723 | debug("Found key in %s:%d", host_file, host_line); | 644 | debug("Found key in %s:%d", host_file, host_line); |
724 | if (options.check_host_ip && ip_status == HOST_NEW) { | 645 | if (options.check_host_ip && ip_status == HOST_NEW) { |
725 | if (readonly) | 646 | if (readonly) |
726 | logit("%s host key for IP address " | 647 | log("%s host key for IP address " |
727 | "'%.128s' not in list of known hosts.", | 648 | "'%.128s' not in list of known hosts.", |
728 | type, ip); | 649 | type, ip); |
729 | else if (!add_host_to_hostfile(user_hostfile, ip, | 650 | else if (!add_host_to_hostfile(user_hostfile, ip, |
730 | host_key)) | 651 | host_key)) |
731 | logit("Failed to add the %s host key for IP " | 652 | log("Failed to add the %s host key for IP " |
732 | "address '%.128s' to the list of known " | 653 | "address '%.128s' to the list of known " |
733 | "hosts (%.30s).", type, ip, user_hostfile); | 654 | "hosts (%.30s).", type, ip, user_hostfile); |
734 | else | 655 | else |
735 | logit("Warning: Permanently added the %s host " | 656 | log("Warning: Permanently added the %s host " |
736 | "key for IP address '%.128s' to the list " | 657 | "key for IP address '%.128s' to the list " |
737 | "of known hosts.", type, ip); | 658 | "of known hosts.", type, ip); |
738 | } | 659 | } |
@@ -751,36 +672,19 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
751 | "have requested strict checking.", type, host); | 672 | "have requested strict checking.", type, host); |
752 | goto fail; | 673 | goto fail; |
753 | } else if (options.strict_host_key_checking == 2) { | 674 | } else if (options.strict_host_key_checking == 2) { |
754 | char msg1[1024], msg2[1024]; | 675 | has_keys = show_other_keys(host, host_key); |
755 | |||
756 | if (show_other_keys(host, host_key)) | ||
757 | snprintf(msg1, sizeof(msg1), | ||
758 | "\nbut keys of different type are already" | ||
759 | " known for this host."); | ||
760 | else | ||
761 | snprintf(msg1, sizeof(msg1), "."); | ||
762 | /* The default */ | 676 | /* The default */ |
763 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); | 677 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); |
764 | msg2[0] = '\0'; | ||
765 | #ifdef DNS | ||
766 | if (options.verify_host_key_dns) { | ||
767 | if (verified_host_key_dns) | ||
768 | snprintf(msg2, sizeof(msg2), | ||
769 | "Matching host key fingerprint" | ||
770 | " found in DNS.\n"); | ||
771 | else | ||
772 | snprintf(msg2, sizeof(msg2), | ||
773 | "No matching host key fingerprint" | ||
774 | " found in DNS.\n"); | ||
775 | } | ||
776 | #endif | ||
777 | snprintf(msg, sizeof(msg), | 678 | snprintf(msg, sizeof(msg), |
778 | "The authenticity of host '%.200s (%s)' can't be " | 679 | "The authenticity of host '%.200s (%s)' can't be " |
779 | "established%s\n" | 680 | "established%s\n" |
780 | "%s key fingerprint is %s.\n%s" | 681 | "%s key fingerprint is %s.\n" |
781 | "Are you sure you want to continue connecting " | 682 | "Are you sure you want to continue connecting " |
782 | "(yes/no)? ", | 683 | "(yes/no)? ", |
783 | host, ip, msg1, type, fp, msg2); | 684 | host, ip, |
685 | has_keys ? ",\nbut keys of different type are already " | ||
686 | "known for this host." : ".", | ||
687 | type, fp); | ||
784 | xfree(fp); | 688 | xfree(fp); |
785 | if (!confirm(msg)) | 689 | if (!confirm(msg)) |
786 | goto fail; | 690 | goto fail; |
@@ -796,10 +700,10 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
796 | * local known_hosts file. | 700 | * local known_hosts file. |
797 | */ | 701 | */ |
798 | if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) | 702 | if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) |
799 | logit("Failed to add the host to the list of known " | 703 | log("Failed to add the host to the list of known " |
800 | "hosts (%.500s).", user_hostfile); | 704 | "hosts (%.500s).", user_hostfile); |
801 | else | 705 | else |
802 | logit("Warning: Permanently added '%.200s' (%s) to the " | 706 | log("Warning: Permanently added '%.200s' (%s) to the " |
803 | "list of known hosts.", hostp, type); | 707 | "list of known hosts.", hostp, type); |
804 | break; | 708 | break; |
805 | case HOST_CHANGED: | 709 | case HOST_CHANGED: |
@@ -850,7 +754,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
850 | 754 | ||
851 | /* | 755 | /* |
852 | * If strict host key checking has not been requested, allow | 756 | * If strict host key checking has not been requested, allow |
853 | * the connection but without MITM-able authentication or | 757 | * the connection but without password authentication or |
854 | * agent forwarding. | 758 | * agent forwarding. |
855 | */ | 759 | */ |
856 | if (options.password_authentication) { | 760 | if (options.password_authentication) { |
@@ -858,17 +762,6 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
858 | "man-in-the-middle attacks."); | 762 | "man-in-the-middle attacks."); |
859 | options.password_authentication = 0; | 763 | options.password_authentication = 0; |
860 | } | 764 | } |
861 | if (options.kbd_interactive_authentication) { | ||
862 | error("Keyboard-interactive authentication is disabled" | ||
863 | " to avoid man-in-the-middle attacks."); | ||
864 | options.kbd_interactive_authentication = 0; | ||
865 | options.challenge_response_authentication = 0; | ||
866 | } | ||
867 | if (options.challenge_response_authentication) { | ||
868 | error("Challenge/response authentication is disabled" | ||
869 | " to avoid man-in-the-middle attacks."); | ||
870 | options.challenge_response_authentication = 0; | ||
871 | } | ||
872 | if (options.forward_agent) { | 765 | if (options.forward_agent) { |
873 | error("Agent forwarding is disabled to avoid " | 766 | error("Agent forwarding is disabled to avoid " |
874 | "man-in-the-middle attacks."); | 767 | "man-in-the-middle attacks."); |
@@ -913,7 +806,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
913 | host_file, host_line); | 806 | host_file, host_line); |
914 | } | 807 | } |
915 | if (options.strict_host_key_checking == 1) { | 808 | if (options.strict_host_key_checking == 1) { |
916 | logit("%s", msg); | 809 | log(msg); |
917 | error("Exiting, you have requested strict checking."); | 810 | error("Exiting, you have requested strict checking."); |
918 | goto fail; | 811 | goto fail; |
919 | } else if (options.strict_host_key_checking == 2) { | 812 | } else if (options.strict_host_key_checking == 2) { |
@@ -922,7 +815,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
922 | if (!confirm(msg)) | 815 | if (!confirm(msg)) |
923 | goto fail; | 816 | goto fail; |
924 | } else { | 817 | } else { |
925 | logit("%s", msg); | 818 | log(msg); |
926 | } | 819 | } |
927 | } | 820 | } |
928 | 821 | ||
@@ -934,33 +827,11 @@ fail: | |||
934 | return -1; | 827 | return -1; |
935 | } | 828 | } |
936 | 829 | ||
937 | /* returns 0 if key verifies or -1 if key does NOT verify */ | ||
938 | int | 830 | int |
939 | verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | 831 | verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) |
940 | { | 832 | { |
941 | struct stat st; | 833 | struct stat st; |
942 | 834 | ||
943 | #ifdef DNS | ||
944 | if (options.verify_host_key_dns) { | ||
945 | switch(verify_host_key_dns(host, hostaddr, host_key)) { | ||
946 | case DNS_VERIFY_OK: | ||
947 | #ifdef DNSSEC | ||
948 | return 0; | ||
949 | #else | ||
950 | verified_host_key_dns = 1; | ||
951 | break; | ||
952 | #endif | ||
953 | case DNS_VERIFY_FAILED: | ||
954 | return -1; | ||
955 | case DNS_VERIFY_ERROR: | ||
956 | break; | ||
957 | default: | ||
958 | debug3("bad return value from verify_host_key_dns"); | ||
959 | break; | ||
960 | } | ||
961 | } | ||
962 | #endif /* DNS */ | ||
963 | |||
964 | /* return ok if the key can be found in an old keyfile */ | 835 | /* return ok if the key can be found in an old keyfile */ |
965 | if (stat(options.system_hostfile2, &st) == 0 || | 836 | if (stat(options.system_hostfile2, &st) == 0 || |
966 | stat(options.user_hostfile2, &st) == 0) { | 837 | stat(options.user_hostfile2, &st) == 0) { |
@@ -1042,7 +913,7 @@ show_key_from_file(const char *file, const char *host, int keytype) | |||
1042 | if ((ret = lookup_key_in_hostfile_by_type(file, host, | 913 | if ((ret = lookup_key_in_hostfile_by_type(file, host, |
1043 | keytype, found, &line))) { | 914 | keytype, found, &line))) { |
1044 | fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); | 915 | fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); |
1045 | logit("WARNING: %s key found for host %s\n" | 916 | log("WARNING: %s key found for host %s\n" |
1046 | "in %s:%d\n" | 917 | "in %s:%d\n" |
1047 | "%s key fingerprint %s.", | 918 | "%s key fingerprint %s.", |
1048 | key_type(found), host, file, line, | 919 | key_type(found), host, file, line, |