diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 185 |
1 files changed, 157 insertions, 28 deletions
diff --git a/sshconnect.c b/sshconnect.c index 013a896b7..5c56f3178 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.137 2002/11/21 23:03:51 deraadt Exp $"); | 16 | RCSID("$OpenBSD: sshconnect.c,v 1.148 2003/09/18 07:52:54 markus Exp $"); |
17 | 17 | ||
18 | #include <openssl/bn.h> | 18 | #include <openssl/bn.h> |
19 | 19 | ||
@@ -33,9 +33,17 @@ RCSID("$OpenBSD: sshconnect.c,v 1.137 2002/11/21 23:03:51 deraadt 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 | |||
36 | char *client_version_string = NULL; | 40 | char *client_version_string = NULL; |
37 | char *server_version_string = NULL; | 41 | char *server_version_string = NULL; |
38 | 42 | ||
43 | #ifdef DNS | ||
44 | int verified_host_key_dns = 0; | ||
45 | #endif | ||
46 | |||
39 | /* import */ | 47 | /* import */ |
40 | extern Options options; | 48 | extern Options options; |
41 | extern char *__progname; | 49 | extern char *__progname; |
@@ -170,7 +178,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
170 | * Creates a (possibly privileged) socket for use as the ssh connection. | 178 | * Creates a (possibly privileged) socket for use as the ssh connection. |
171 | */ | 179 | */ |
172 | static int | 180 | static int |
173 | ssh_create_socket(int privileged, int family) | 181 | ssh_create_socket(int privileged, struct addrinfo *ai) |
174 | { | 182 | { |
175 | int sock, gaierr; | 183 | int sock, gaierr; |
176 | struct addrinfo hints, *res; | 184 | struct addrinfo hints, *res; |
@@ -182,15 +190,16 @@ ssh_create_socket(int privileged, int family) | |||
182 | if (privileged) { | 190 | if (privileged) { |
183 | int p = IPPORT_RESERVED - 1; | 191 | int p = IPPORT_RESERVED - 1; |
184 | PRIV_START; | 192 | PRIV_START; |
185 | sock = rresvport_af(&p, family); | 193 | sock = rresvport_af(&p, ai->ai_family); |
186 | PRIV_END; | 194 | PRIV_END; |
187 | if (sock < 0) | 195 | if (sock < 0) |
188 | error("rresvport: af=%d %.100s", family, strerror(errno)); | 196 | error("rresvport: af=%d %.100s", ai->ai_family, |
197 | strerror(errno)); | ||
189 | else | 198 | else |
190 | debug("Allocated local port %d.", p); | 199 | debug("Allocated local port %d.", p); |
191 | return sock; | 200 | return sock; |
192 | } | 201 | } |
193 | sock = socket(family, SOCK_STREAM, 0); | 202 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
194 | if (sock < 0) | 203 | if (sock < 0) |
195 | error("socket: %.100s", strerror(errno)); | 204 | error("socket: %.100s", strerror(errno)); |
196 | 205 | ||
@@ -199,8 +208,9 @@ ssh_create_socket(int privileged, int family) | |||
199 | return sock; | 208 | return sock; |
200 | 209 | ||
201 | memset(&hints, 0, sizeof(hints)); | 210 | memset(&hints, 0, sizeof(hints)); |
202 | hints.ai_family = family; | 211 | hints.ai_family = ai->ai_family; |
203 | hints.ai_socktype = SOCK_STREAM; | 212 | hints.ai_socktype = ai->ai_socktype; |
213 | hints.ai_protocol = ai->ai_protocol; | ||
204 | hints.ai_flags = AI_PASSIVE; | 214 | hints.ai_flags = AI_PASSIVE; |
205 | gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); | 215 | gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); |
206 | if (gaierr) { | 216 | if (gaierr) { |
@@ -219,6 +229,74 @@ ssh_create_socket(int privileged, int family) | |||
219 | return sock; | 229 | return sock; |
220 | } | 230 | } |
221 | 231 | ||
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 | |||
222 | /* | 300 | /* |
223 | * Opens a TCP/IP connection to the remote server on the given host. | 301 | * Opens a TCP/IP connection to the remote server on the given host. |
224 | * The address of the remote host will be returned in hostaddr. | 302 | * The address of the remote host will be returned in hostaddr. |
@@ -302,12 +380,13 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
302 | host, ntop, strport); | 380 | host, ntop, strport); |
303 | 381 | ||
304 | /* Create a socket for connecting. */ | 382 | /* Create a socket for connecting. */ |
305 | sock = ssh_create_socket(needpriv, ai->ai_family); | 383 | sock = ssh_create_socket(needpriv, ai); |
306 | if (sock < 0) | 384 | if (sock < 0) |
307 | /* Any error is already output */ | 385 | /* Any error is already output */ |
308 | continue; | 386 | continue; |
309 | 387 | ||
310 | if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { | 388 | if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, |
389 | options.connection_timeout) >= 0) { | ||
311 | /* Successful connection. */ | 390 | /* Successful connection. */ |
312 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); | 391 | memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); |
313 | break; | 392 | break; |
@@ -339,7 +418,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
339 | 418 | ||
340 | /* Return failure if we didn't get a successful connection. */ | 419 | /* Return failure if we didn't get a successful connection. */ |
341 | if (attempt >= connection_attempts) { | 420 | if (attempt >= connection_attempts) { |
342 | log("ssh: connect to host %s port %s: %s", | 421 | logit("ssh: connect to host %s port %s: %s", |
343 | host, strport, strerror(errno)); | 422 | host, strport, strerror(errno)); |
344 | return full_failure ? ECONNABORTED : ECONNREFUSED; | 423 | return full_failure ? ECONNABORTED : ECONNREFUSED; |
345 | } | 424 | } |
@@ -453,7 +532,7 @@ ssh_exchange_identification(void) | |||
453 | enable_compat13(); | 532 | enable_compat13(); |
454 | minor1 = 3; | 533 | minor1 = 3; |
455 | if (options.forward_agent) { | 534 | if (options.forward_agent) { |
456 | log("Agent forwarding disabled for protocol 1.3"); | 535 | logit("Agent forwarding disabled for protocol 1.3"); |
457 | options.forward_agent = 0; | 536 | options.forward_agent = 0; |
458 | } | 537 | } |
459 | } | 538 | } |
@@ -477,7 +556,7 @@ ssh_exchange_identification(void) | |||
477 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | 556 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, |
478 | compat20 ? PROTOCOL_MINOR_2 : minor1, | 557 | compat20 ? PROTOCOL_MINOR_2 : minor1, |
479 | SSH_VERSION); | 558 | SSH_VERSION); |
480 | if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) | 559 | if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) |
481 | fatal("write: %.100s", strerror(errno)); | 560 | fatal("write: %.100s", strerror(errno)); |
482 | client_version_string = xstrdup(buf); | 561 | client_version_string = xstrdup(buf); |
483 | chop(client_version_string); | 562 | chop(client_version_string); |
@@ -528,7 +607,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
528 | int salen; | 607 | int salen; |
529 | char ntop[NI_MAXHOST]; | 608 | char ntop[NI_MAXHOST]; |
530 | char msg[1024]; | 609 | char msg[1024]; |
531 | int len, host_line, ip_line, has_keys; | 610 | int len, host_line, ip_line; |
532 | const char *host_file = NULL, *ip_file = NULL; | 611 | const char *host_file = NULL, *ip_file = NULL; |
533 | 612 | ||
534 | /* | 613 | /* |
@@ -644,16 +723,16 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
644 | debug("Found key in %s:%d", host_file, host_line); | 723 | debug("Found key in %s:%d", host_file, host_line); |
645 | if (options.check_host_ip && ip_status == HOST_NEW) { | 724 | if (options.check_host_ip && ip_status == HOST_NEW) { |
646 | if (readonly) | 725 | if (readonly) |
647 | log("%s host key for IP address " | 726 | logit("%s host key for IP address " |
648 | "'%.128s' not in list of known hosts.", | 727 | "'%.128s' not in list of known hosts.", |
649 | type, ip); | 728 | type, ip); |
650 | else if (!add_host_to_hostfile(user_hostfile, ip, | 729 | else if (!add_host_to_hostfile(user_hostfile, ip, |
651 | host_key)) | 730 | host_key)) |
652 | log("Failed to add the %s host key for IP " | 731 | logit("Failed to add the %s host key for IP " |
653 | "address '%.128s' to the list of known " | 732 | "address '%.128s' to the list of known " |
654 | "hosts (%.30s).", type, ip, user_hostfile); | 733 | "hosts (%.30s).", type, ip, user_hostfile); |
655 | else | 734 | else |
656 | log("Warning: Permanently added the %s host " | 735 | logit("Warning: Permanently added the %s host " |
657 | "key for IP address '%.128s' to the list " | 736 | "key for IP address '%.128s' to the list " |
658 | "of known hosts.", type, ip); | 737 | "of known hosts.", type, ip); |
659 | } | 738 | } |
@@ -672,19 +751,36 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
672 | "have requested strict checking.", type, host); | 751 | "have requested strict checking.", type, host); |
673 | goto fail; | 752 | goto fail; |
674 | } else if (options.strict_host_key_checking == 2) { | 753 | } else if (options.strict_host_key_checking == 2) { |
675 | has_keys = show_other_keys(host, host_key); | 754 | char msg1[1024], msg2[1024]; |
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), "."); | ||
676 | /* The default */ | 762 | /* The default */ |
677 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); | 763 | 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 | ||
678 | snprintf(msg, sizeof(msg), | 777 | snprintf(msg, sizeof(msg), |
679 | "The authenticity of host '%.200s (%s)' can't be " | 778 | "The authenticity of host '%.200s (%s)' can't be " |
680 | "established%s\n" | 779 | "established%s\n" |
681 | "%s key fingerprint is %s.\n" | 780 | "%s key fingerprint is %s.\n%s" |
682 | "Are you sure you want to continue connecting " | 781 | "Are you sure you want to continue connecting " |
683 | "(yes/no)? ", | 782 | "(yes/no)? ", |
684 | host, ip, | 783 | host, ip, msg1, type, fp, msg2); |
685 | has_keys ? ",\nbut keys of different type are already " | ||
686 | "known for this host." : ".", | ||
687 | type, fp); | ||
688 | xfree(fp); | 784 | xfree(fp); |
689 | if (!confirm(msg)) | 785 | if (!confirm(msg)) |
690 | goto fail; | 786 | goto fail; |
@@ -700,10 +796,10 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
700 | * local known_hosts file. | 796 | * local known_hosts file. |
701 | */ | 797 | */ |
702 | if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) | 798 | if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) |
703 | log("Failed to add the host to the list of known " | 799 | logit("Failed to add the host to the list of known " |
704 | "hosts (%.500s).", user_hostfile); | 800 | "hosts (%.500s).", user_hostfile); |
705 | else | 801 | else |
706 | log("Warning: Permanently added '%.200s' (%s) to the " | 802 | logit("Warning: Permanently added '%.200s' (%s) to the " |
707 | "list of known hosts.", hostp, type); | 803 | "list of known hosts.", hostp, type); |
708 | break; | 804 | break; |
709 | case HOST_CHANGED: | 805 | case HOST_CHANGED: |
@@ -754,7 +850,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
754 | 850 | ||
755 | /* | 851 | /* |
756 | * If strict host key checking has not been requested, allow | 852 | * If strict host key checking has not been requested, allow |
757 | * the connection but without password authentication or | 853 | * the connection but without MITM-able authentication or |
758 | * agent forwarding. | 854 | * agent forwarding. |
759 | */ | 855 | */ |
760 | if (options.password_authentication) { | 856 | if (options.password_authentication) { |
@@ -762,6 +858,17 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
762 | "man-in-the-middle attacks."); | 858 | "man-in-the-middle attacks."); |
763 | options.password_authentication = 0; | 859 | options.password_authentication = 0; |
764 | } | 860 | } |
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 | } | ||
765 | if (options.forward_agent) { | 872 | if (options.forward_agent) { |
766 | error("Agent forwarding is disabled to avoid " | 873 | error("Agent forwarding is disabled to avoid " |
767 | "man-in-the-middle attacks."); | 874 | "man-in-the-middle attacks."); |
@@ -806,7 +913,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
806 | host_file, host_line); | 913 | host_file, host_line); |
807 | } | 914 | } |
808 | if (options.strict_host_key_checking == 1) { | 915 | if (options.strict_host_key_checking == 1) { |
809 | log(msg); | 916 | logit("%s", msg); |
810 | error("Exiting, you have requested strict checking."); | 917 | error("Exiting, you have requested strict checking."); |
811 | goto fail; | 918 | goto fail; |
812 | } else if (options.strict_host_key_checking == 2) { | 919 | } else if (options.strict_host_key_checking == 2) { |
@@ -815,7 +922,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | |||
815 | if (!confirm(msg)) | 922 | if (!confirm(msg)) |
816 | goto fail; | 923 | goto fail; |
817 | } else { | 924 | } else { |
818 | log(msg); | 925 | logit("%s", msg); |
819 | } | 926 | } |
820 | } | 927 | } |
821 | 928 | ||
@@ -827,11 +934,33 @@ fail: | |||
827 | return -1; | 934 | return -1; |
828 | } | 935 | } |
829 | 936 | ||
937 | /* returns 0 if key verifies or -1 if key does NOT verify */ | ||
830 | int | 938 | int |
831 | verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | 939 | verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) |
832 | { | 940 | { |
833 | struct stat st; | 941 | struct stat st; |
834 | 942 | ||
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 | |||
835 | /* return ok if the key can be found in an old keyfile */ | 964 | /* return ok if the key can be found in an old keyfile */ |
836 | if (stat(options.system_hostfile2, &st) == 0 || | 965 | if (stat(options.system_hostfile2, &st) == 0 || |
837 | stat(options.user_hostfile2, &st) == 0) { | 966 | stat(options.user_hostfile2, &st) == 0) { |
@@ -913,7 +1042,7 @@ show_key_from_file(const char *file, const char *host, int keytype) | |||
913 | if ((ret = lookup_key_in_hostfile_by_type(file, host, | 1042 | if ((ret = lookup_key_in_hostfile_by_type(file, host, |
914 | keytype, found, &line))) { | 1043 | keytype, found, &line))) { |
915 | fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); | 1044 | fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); |
916 | log("WARNING: %s key found for host %s\n" | 1045 | logit("WARNING: %s key found for host %s\n" |
917 | "in %s:%d\n" | 1046 | "in %s:%d\n" |
918 | "%s key fingerprint %s.", | 1047 | "%s key fingerprint %s.", |
919 | key_type(found), host, file, line, | 1048 | key_type(found), host, file, line, |