diff options
author | Colin Watson <cjwatson@debian.org> | 2011-01-24 12:43:25 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2011-01-24 12:43:25 +0000 |
commit | 626f1d986ff72aa514da63e34744e1de9cf21b9a (patch) | |
tree | d215a5280bc2e57251e4a9e08bfd3674ad824a94 /sshconnect.c | |
parent | 6ed622cb6fe8f71bbe0d998cdd12280410bfb420 (diff) | |
parent | 0970072c89b079b022538e3c366fbfa2c53fc821 (diff) |
* New upstream release (http://www.openssh.org/txt/release-5.7):
- Implement Elliptic Curve Cryptography modes for key exchange (ECDH)
and host/user keys (ECDSA) as specified by RFC5656. ECDH and ECDSA
offer better performance than plain DH and DSA at the same equivalent
symmetric key length, as well as much shorter keys.
- sftp(1)/sftp-server(8): add a protocol extension to support a hard
link operation. It is available through the "ln" command in the
client. The old "ln" behaviour of creating a symlink is available
using its "-s" option or through the preexisting "symlink" command.
- scp(1): Add a new -3 option to scp: Copies between two remote hosts
are transferred through the local host (closes: #508613).
- ssh(1): "atomically" create the listening mux socket by binding it on
a temporary name and then linking it into position after listen() has
succeeded. This allows the mux clients to determine that the server
socket is either ready or stale without races (closes: #454784).
Stale server sockets are now automatically removed (closes: #523250).
- ssh(1): install a SIGCHLD handler to reap expired child process
(closes: #594687).
- ssh(1)/ssh-agent(1): honour $TMPDIR for client xauth and ssh-agent
temporary directories (closes: #357469, although only if you arrange
for ssh-agent to actually see $TMPDIR since the setgid bit will cause
it to be stripped off).
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 322 |
1 files changed, 177 insertions, 145 deletions
diff --git a/sshconnect.c b/sshconnect.c index 5b2da9136..1c066b641 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.224 2010/04/16 21:14:27 djm Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.232 2011/01/16 11:50:36 djm 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 |
@@ -34,6 +34,7 @@ | |||
34 | #include <paths.h> | 34 | #include <paths.h> |
35 | #endif | 35 | #endif |
36 | #include <pwd.h> | 36 | #include <pwd.h> |
37 | #include <signal.h> | ||
37 | #include <stdarg.h> | 38 | #include <stdarg.h> |
38 | #include <stdio.h> | 39 | #include <stdio.h> |
39 | #include <stdlib.h> | 40 | #include <stdlib.h> |
@@ -66,14 +67,15 @@ char *server_version_string = NULL; | |||
66 | 67 | ||
67 | static int matching_host_key_dns = 0; | 68 | static int matching_host_key_dns = 0; |
68 | 69 | ||
70 | static pid_t proxy_command_pid = 0; | ||
71 | |||
69 | /* import */ | 72 | /* import */ |
70 | extern Options options; | 73 | extern Options options; |
71 | extern char *__progname; | 74 | extern char *__progname; |
72 | extern uid_t original_real_uid; | 75 | extern uid_t original_real_uid; |
73 | extern uid_t original_effective_uid; | 76 | extern uid_t original_effective_uid; |
74 | extern pid_t proxy_command_pid; | ||
75 | 77 | ||
76 | static int show_other_keys(const char *, Key *); | 78 | static int show_other_keys(struct hostkeys *, Key *); |
77 | static void warn_changed_key(Key *); | 79 | static void warn_changed_key(Key *); |
78 | 80 | ||
79 | /* | 81 | /* |
@@ -87,7 +89,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
87 | pid_t pid; | 89 | pid_t pid; |
88 | char *shell, strport[NI_MAXSERV]; | 90 | char *shell, strport[NI_MAXSERV]; |
89 | 91 | ||
90 | if ((shell = getenv("SHELL")) == NULL) | 92 | if ((shell = getenv("SHELL")) == NULL || *shell == '\0') |
91 | shell = _PATH_BSHELL; | 93 | shell = _PATH_BSHELL; |
92 | 94 | ||
93 | /* Convert the port number into a string. */ | 95 | /* Convert the port number into a string. */ |
@@ -141,6 +143,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
141 | 143 | ||
142 | /* Execute the proxy command. Note that we gave up any | 144 | /* Execute the proxy command. Note that we gave up any |
143 | extra privileges above. */ | 145 | extra privileges above. */ |
146 | signal(SIGPIPE, SIG_DFL); | ||
144 | execvp(argv[0], argv); | 147 | execvp(argv[0], argv); |
145 | perror(argv[0]); | 148 | perror(argv[0]); |
146 | exit(1); | 149 | exit(1); |
@@ -167,6 +170,17 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) | |||
167 | return 0; | 170 | return 0; |
168 | } | 171 | } |
169 | 172 | ||
173 | void | ||
174 | ssh_kill_proxy_command(void) | ||
175 | { | ||
176 | /* | ||
177 | * Send SIGHUP to proxy command if used. We don't wait() in | ||
178 | * case it hangs and instead rely on init to reap the child | ||
179 | */ | ||
180 | if (proxy_command_pid > 1) | ||
181 | kill(proxy_command_pid, SIGHUP); | ||
182 | } | ||
183 | |||
170 | /* | 184 | /* |
171 | * Creates a (possibly privileged) socket for use as the ssh connection. | 185 | * Creates a (possibly privileged) socket for use as the ssh connection. |
172 | */ | 186 | */ |
@@ -594,6 +608,79 @@ check_host_cert(const char *host, const Key *host_key) | |||
594 | return 1; | 608 | return 1; |
595 | } | 609 | } |
596 | 610 | ||
611 | static int | ||
612 | sockaddr_is_local(struct sockaddr *hostaddr) | ||
613 | { | ||
614 | switch (hostaddr->sa_family) { | ||
615 | case AF_INET: | ||
616 | return (ntohl(((struct sockaddr_in *)hostaddr)-> | ||
617 | sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; | ||
618 | case AF_INET6: | ||
619 | return IN6_IS_ADDR_LOOPBACK( | ||
620 | &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); | ||
621 | default: | ||
622 | return 0; | ||
623 | } | ||
624 | } | ||
625 | |||
626 | /* | ||
627 | * Prepare the hostname and ip address strings that are used to lookup | ||
628 | * host keys in known_hosts files. These may have a port number appended. | ||
629 | */ | ||
630 | void | ||
631 | get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, | ||
632 | u_short port, char **hostfile_hostname, char **hostfile_ipaddr) | ||
633 | { | ||
634 | char ntop[NI_MAXHOST]; | ||
635 | socklen_t addrlen; | ||
636 | |||
637 | switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { | ||
638 | case -1: | ||
639 | addrlen = 0; | ||
640 | break; | ||
641 | case AF_INET: | ||
642 | addrlen = sizeof(struct sockaddr_in); | ||
643 | break; | ||
644 | case AF_INET6: | ||
645 | addrlen = sizeof(struct sockaddr_in6); | ||
646 | break; | ||
647 | default: | ||
648 | addrlen = sizeof(struct sockaddr); | ||
649 | break; | ||
650 | } | ||
651 | |||
652 | /* | ||
653 | * We don't have the remote ip-address for connections | ||
654 | * using a proxy command | ||
655 | */ | ||
656 | if (hostfile_ipaddr != NULL) { | ||
657 | if (options.proxy_command == NULL) { | ||
658 | if (getnameinfo(hostaddr, addrlen, | ||
659 | ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) | ||
660 | fatal("check_host_key: getnameinfo failed"); | ||
661 | *hostfile_ipaddr = put_host_port(ntop, port); | ||
662 | } else { | ||
663 | *hostfile_ipaddr = xstrdup("<no hostip for proxy " | ||
664 | "command>"); | ||
665 | } | ||
666 | } | ||
667 | |||
668 | /* | ||
669 | * Allow the user to record the key under a different name or | ||
670 | * differentiate a non-standard port. This is useful for ssh | ||
671 | * tunneling over forwarded connections or if you run multiple | ||
672 | * sshd's on different ports on the same machine. | ||
673 | */ | ||
674 | if (hostfile_hostname != NULL) { | ||
675 | if (options.host_key_alias != NULL) { | ||
676 | *hostfile_hostname = xstrdup(options.host_key_alias); | ||
677 | debug("using hostkeyalias: %s", *hostfile_hostname); | ||
678 | } else { | ||
679 | *hostfile_hostname = put_host_port(hostname, port); | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | |||
597 | /* | 684 | /* |
598 | * check whether the supplied host key is valid, return -1 if the key | 685 | * check whether the supplied host key is valid, return -1 if the key |
599 | * is not valid. the user_hostfile will not be updated if 'readonly' is true. | 686 | * is not valid. the user_hostfile will not be updated if 'readonly' is true. |
@@ -603,21 +690,21 @@ check_host_cert(const char *host, const Key *host_key) | |||
603 | #define ROQUIET 2 | 690 | #define ROQUIET 2 |
604 | static int | 691 | static int |
605 | check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | 692 | check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, |
606 | Key *host_key, int readonly, const char *user_hostfile, | 693 | Key *host_key, int readonly, char *user_hostfile, |
607 | const char *system_hostfile) | 694 | char *system_hostfile) |
608 | { | 695 | { |
609 | Key *file_key, *raw_key = NULL; | 696 | Key *raw_key = NULL; |
610 | const char *type; | 697 | const char *type; |
611 | char *ip = NULL, *host = NULL; | 698 | char *ip = NULL, *host = NULL; |
612 | char hostline[1000], *hostp, *fp, *ra; | 699 | char hostline[1000], *hostp, *fp, *ra; |
613 | HostStatus host_status; | 700 | HostStatus host_status; |
614 | HostStatus ip_status; | 701 | HostStatus ip_status; |
615 | int r, want_cert, local = 0, host_ip_differ = 0; | 702 | int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; |
616 | int salen; | 703 | int local = sockaddr_is_local(hostaddr); |
617 | char ntop[NI_MAXHOST]; | ||
618 | char msg[1024]; | 704 | char msg[1024]; |
619 | int len, host_line, ip_line, cancelled_forwarding = 0; | 705 | int len, cancelled_forwarding = 0; |
620 | const char *host_file = NULL, *ip_file = NULL; | 706 | struct hostkeys *host_hostkeys, *ip_hostkeys; |
707 | const struct hostkey_entry *host_found, *ip_found; | ||
621 | 708 | ||
622 | /* | 709 | /* |
623 | * Force accepting of the host key for loopback/localhost. The | 710 | * Force accepting of the host key for loopback/localhost. The |
@@ -627,23 +714,6 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
627 | * essentially disables host authentication for localhost; however, | 714 | * essentially disables host authentication for localhost; however, |
628 | * this is probably not a real problem. | 715 | * this is probably not a real problem. |
629 | */ | 716 | */ |
630 | /** hostaddr == 0! */ | ||
631 | switch (hostaddr->sa_family) { | ||
632 | case AF_INET: | ||
633 | local = (ntohl(((struct sockaddr_in *)hostaddr)-> | ||
634 | sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; | ||
635 | salen = sizeof(struct sockaddr_in); | ||
636 | break; | ||
637 | case AF_INET6: | ||
638 | local = IN6_IS_ADDR_LOOPBACK( | ||
639 | &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); | ||
640 | salen = sizeof(struct sockaddr_in6); | ||
641 | break; | ||
642 | default: | ||
643 | local = 0; | ||
644 | salen = sizeof(struct sockaddr_storage); | ||
645 | break; | ||
646 | } | ||
647 | if (options.no_host_authentication_for_localhost == 1 && local && | 717 | if (options.no_host_authentication_for_localhost == 1 && local && |
648 | options.host_key_alias == NULL) { | 718 | options.host_key_alias == NULL) { |
649 | debug("Forcing accepting of host key for " | 719 | debug("Forcing accepting of host key for " |
@@ -652,17 +722,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
652 | } | 722 | } |
653 | 723 | ||
654 | /* | 724 | /* |
655 | * We don't have the remote ip-address for connections | 725 | * Prepare the hostname and address strings used for hostkey lookup. |
656 | * using a proxy command | 726 | * In some cases, these will have a port number appended. |
657 | */ | 727 | */ |
658 | if (options.proxy_command == NULL) { | 728 | get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); |
659 | if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), | ||
660 | NULL, 0, NI_NUMERICHOST) != 0) | ||
661 | fatal("check_host_key: getnameinfo failed"); | ||
662 | ip = put_host_port(ntop, port); | ||
663 | } else { | ||
664 | ip = xstrdup("<no hostip for proxy command>"); | ||
665 | } | ||
666 | 729 | ||
667 | /* | 730 | /* |
668 | * Turn off check_host_ip if the connection is to localhost, via proxy | 731 | * Turn off check_host_ip if the connection is to localhost, via proxy |
@@ -672,74 +735,52 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
672 | strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) | 735 | strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) |
673 | options.check_host_ip = 0; | 736 | options.check_host_ip = 0; |
674 | 737 | ||
675 | /* | 738 | host_hostkeys = init_hostkeys(); |
676 | * Allow the user to record the key under a different name or | 739 | load_hostkeys(host_hostkeys, host, user_hostfile); |
677 | * differentiate a non-standard port. This is useful for ssh | 740 | load_hostkeys(host_hostkeys, host, system_hostfile); |
678 | * tunneling over forwarded connections or if you run multiple | 741 | |
679 | * sshd's on different ports on the same machine. | 742 | ip_hostkeys = NULL; |
680 | */ | 743 | if (!want_cert && options.check_host_ip) { |
681 | if (options.host_key_alias != NULL) { | 744 | ip_hostkeys = init_hostkeys(); |
682 | host = xstrdup(options.host_key_alias); | 745 | load_hostkeys(ip_hostkeys, ip, user_hostfile); |
683 | debug("using hostkeyalias: %s", host); | 746 | load_hostkeys(ip_hostkeys, ip, system_hostfile); |
684 | } else { | ||
685 | host = put_host_port(hostname, port); | ||
686 | } | 747 | } |
687 | 748 | ||
688 | retry: | 749 | retry: |
750 | /* Reload these as they may have changed on cert->key downgrade */ | ||
689 | want_cert = key_is_cert(host_key); | 751 | want_cert = key_is_cert(host_key); |
690 | type = key_type(host_key); | 752 | type = key_type(host_key); |
691 | 753 | ||
692 | /* | 754 | /* |
693 | * Store the host key from the known host file in here so that we can | ||
694 | * compare it with the key for the IP address. | ||
695 | */ | ||
696 | file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type); | ||
697 | |||
698 | /* | ||
699 | * Check if the host key is present in the user's list of known | 755 | * Check if the host key is present in the user's list of known |
700 | * hosts or in the systemwide list. | 756 | * hosts or in the systemwide list. |
701 | */ | 757 | */ |
702 | host_file = user_hostfile; | 758 | host_status = check_key_in_hostkeys(host_hostkeys, host_key, |
703 | host_status = check_host_in_hostfile(host_file, host, host_key, | 759 | &host_found); |
704 | file_key, &host_line); | 760 | |
705 | if (host_status == HOST_NEW) { | ||
706 | host_file = system_hostfile; | ||
707 | host_status = check_host_in_hostfile(host_file, host, host_key, | ||
708 | file_key, &host_line); | ||
709 | } | ||
710 | /* | 761 | /* |
711 | * Also perform check for the ip address, skip the check if we are | 762 | * Also perform check for the ip address, skip the check if we are |
712 | * localhost, looking for a certificate, or the hostname was an ip | 763 | * localhost, looking for a certificate, or the hostname was an ip |
713 | * address to begin with. | 764 | * address to begin with. |
714 | */ | 765 | */ |
715 | if (!want_cert && options.check_host_ip) { | 766 | if (!want_cert && ip_hostkeys != NULL) { |
716 | Key *ip_key = key_new(host_key->type); | 767 | ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, |
717 | 768 | &ip_found); | |
718 | ip_file = user_hostfile; | ||
719 | ip_status = check_host_in_hostfile(ip_file, ip, host_key, | ||
720 | ip_key, &ip_line); | ||
721 | if (ip_status == HOST_NEW) { | ||
722 | ip_file = system_hostfile; | ||
723 | ip_status = check_host_in_hostfile(ip_file, ip, | ||
724 | host_key, ip_key, &ip_line); | ||
725 | } | ||
726 | if (host_status == HOST_CHANGED && | 769 | if (host_status == HOST_CHANGED && |
727 | (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) | 770 | (ip_status != HOST_CHANGED || |
771 | (ip_found != NULL && | ||
772 | !key_equal(ip_found->key, host_found->key)))) | ||
728 | host_ip_differ = 1; | 773 | host_ip_differ = 1; |
729 | |||
730 | key_free(ip_key); | ||
731 | } else | 774 | } else |
732 | ip_status = host_status; | 775 | ip_status = host_status; |
733 | 776 | ||
734 | key_free(file_key); | ||
735 | |||
736 | switch (host_status) { | 777 | switch (host_status) { |
737 | case HOST_OK: | 778 | case HOST_OK: |
738 | /* The host is known and the key matches. */ | 779 | /* The host is known and the key matches. */ |
739 | debug("Host '%.200s' is known and matches the %s host %s.", | 780 | debug("Host '%.200s' is known and matches the %s host %s.", |
740 | host, type, want_cert ? "certificate" : "key"); | 781 | host, type, want_cert ? "certificate" : "key"); |
741 | debug("Found %s in %s:%d", | 782 | debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", |
742 | want_cert ? "CA key" : "key", host_file, host_line); | 783 | host_found->file, host_found->line); |
743 | if (want_cert && !check_host_cert(hostname, host_key)) | 784 | if (want_cert && !check_host_cert(hostname, host_key)) |
744 | goto fail; | 785 | goto fail; |
745 | if (options.check_host_ip && ip_status == HOST_NEW) { | 786 | if (options.check_host_ip && ip_status == HOST_NEW) { |
@@ -790,7 +831,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
790 | } else if (options.strict_host_key_checking == 2) { | 831 | } else if (options.strict_host_key_checking == 2) { |
791 | char msg1[1024], msg2[1024]; | 832 | char msg1[1024], msg2[1024]; |
792 | 833 | ||
793 | if (show_other_keys(host, host_key)) | 834 | if (show_other_keys(host_hostkeys, host_key)) |
794 | snprintf(msg1, sizeof(msg1), | 835 | snprintf(msg1, sizeof(msg1), |
795 | "\nbut keys of different type are already" | 836 | "\nbut keys of different type are already" |
796 | " known for this host."); | 837 | " known for this host."); |
@@ -831,8 +872,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
831 | * local known_hosts file. | 872 | * local known_hosts file. |
832 | */ | 873 | */ |
833 | if (options.check_host_ip && ip_status == HOST_NEW) { | 874 | if (options.check_host_ip && ip_status == HOST_NEW) { |
834 | snprintf(hostline, sizeof(hostline), "%s,%s", | 875 | snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); |
835 | host, ip); | ||
836 | hostp = hostline; | 876 | hostp = hostline; |
837 | if (options.hash_known_hosts) { | 877 | if (options.hash_known_hosts) { |
838 | /* Add hash of host and IP separately */ | 878 | /* Add hash of host and IP separately */ |
@@ -886,8 +926,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
886 | * all hosts that one might visit. | 926 | * all hosts that one might visit. |
887 | */ | 927 | */ |
888 | debug("Host certificate authority does not " | 928 | debug("Host certificate authority does not " |
889 | "match %s in %s:%d", CA_MARKER, | 929 | "match %s in %s:%lu", CA_MARKER, |
890 | host_file, host_line); | 930 | host_found->file, host_found->line); |
891 | goto fail; | 931 | goto fail; |
892 | } | 932 | } |
893 | if (readonly == ROQUIET) | 933 | if (readonly == ROQUIET) |
@@ -909,13 +949,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
909 | error("DNS SPOOFING is happening or the IP address for the host"); | 949 | error("DNS SPOOFING is happening or the IP address for the host"); |
910 | error("and its host key have changed at the same time."); | 950 | error("and its host key have changed at the same time."); |
911 | if (ip_status != HOST_NEW) | 951 | if (ip_status != HOST_NEW) |
912 | error("Offending key for IP in %s:%d", ip_file, ip_line); | 952 | error("Offending key for IP in %s:%lu", |
953 | ip_found->file, ip_found->line); | ||
913 | } | 954 | } |
914 | /* The host key has changed. */ | 955 | /* The host key has changed. */ |
915 | warn_changed_key(host_key); | 956 | warn_changed_key(host_key); |
916 | error("Add correct host key in %.100s to get rid of this message.", | 957 | error("Add correct host key in %.100s to get rid of this message.", |
917 | user_hostfile); | 958 | user_hostfile); |
918 | error("Offending key in %s:%d", host_file, host_line); | 959 | error("Offending %s key in %s:%lu", key_type(host_found->key), |
960 | host_found->file, host_found->line); | ||
919 | 961 | ||
920 | /* | 962 | /* |
921 | * If strict host key checking is in use, the user will have | 963 | * If strict host key checking is in use, the user will have |
@@ -1000,13 +1042,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
1000 | snprintf(msg, sizeof(msg), | 1042 | snprintf(msg, sizeof(msg), |
1001 | "Warning: the %s host key for '%.200s' " | 1043 | "Warning: the %s host key for '%.200s' " |
1002 | "differs from the key for the IP address '%.128s'" | 1044 | "differs from the key for the IP address '%.128s'" |
1003 | "\nOffending key for IP in %s:%d", | 1045 | "\nOffending key for IP in %s:%lu", |
1004 | type, host, ip, ip_file, ip_line); | 1046 | type, host, ip, ip_found->file, ip_found->line); |
1005 | if (host_status == HOST_OK) { | 1047 | if (host_status == HOST_OK) { |
1006 | len = strlen(msg); | 1048 | len = strlen(msg); |
1007 | snprintf(msg + len, sizeof(msg) - len, | 1049 | snprintf(msg + len, sizeof(msg) - len, |
1008 | "\nMatching host key in %s:%d", | 1050 | "\nMatching host key in %s:%lu", |
1009 | host_file, host_line); | 1051 | host_found->file, host_found->line); |
1010 | } | 1052 | } |
1011 | if (options.strict_host_key_checking == 1) { | 1053 | if (options.strict_host_key_checking == 1) { |
1012 | logit("%s", msg); | 1054 | logit("%s", msg); |
@@ -1024,6 +1066,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
1024 | 1066 | ||
1025 | xfree(ip); | 1067 | xfree(ip); |
1026 | xfree(host); | 1068 | xfree(host); |
1069 | if (host_hostkeys != NULL) | ||
1070 | free_hostkeys(host_hostkeys); | ||
1071 | if (ip_hostkeys != NULL) | ||
1072 | free_hostkeys(ip_hostkeys); | ||
1027 | return 0; | 1073 | return 0; |
1028 | 1074 | ||
1029 | fail: | 1075 | fail: |
@@ -1043,6 +1089,10 @@ fail: | |||
1043 | key_free(raw_key); | 1089 | key_free(raw_key); |
1044 | xfree(ip); | 1090 | xfree(ip); |
1045 | xfree(host); | 1091 | xfree(host); |
1092 | if (host_hostkeys != NULL) | ||
1093 | free_hostkeys(host_hostkeys); | ||
1094 | if (ip_hostkeys != NULL) | ||
1095 | free_hostkeys(ip_hostkeys); | ||
1046 | return -1; | 1096 | return -1; |
1047 | } | 1097 | } |
1048 | 1098 | ||
@@ -1052,6 +1102,11 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
1052 | { | 1102 | { |
1053 | struct stat st; | 1103 | struct stat st; |
1054 | int flags = 0; | 1104 | int flags = 0; |
1105 | char *fp; | ||
1106 | |||
1107 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); | ||
1108 | debug("Server host key: %s %s", key_type(host_key), fp); | ||
1109 | xfree(fp); | ||
1055 | 1110 | ||
1056 | /* XXX certs are not yet supported for DNS */ | 1111 | /* XXX certs are not yet supported for DNS */ |
1057 | if (!key_is_cert(host_key) && options.verify_host_key_dns && | 1112 | if (!key_is_cert(host_key) && options.verify_host_key_dns && |
@@ -1095,7 +1150,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
1095 | */ | 1150 | */ |
1096 | void | 1151 | void |
1097 | ssh_login(Sensitive *sensitive, const char *orighost, | 1152 | ssh_login(Sensitive *sensitive, const char *orighost, |
1098 | struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms) | 1153 | struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) |
1099 | { | 1154 | { |
1100 | char *host, *cp; | 1155 | char *host, *cp; |
1101 | char *server_user, *local_user; | 1156 | char *server_user, *local_user; |
@@ -1118,7 +1173,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, | |||
1118 | /* key exchange */ | 1173 | /* key exchange */ |
1119 | /* authenticate user */ | 1174 | /* authenticate user */ |
1120 | if (compat20) { | 1175 | if (compat20) { |
1121 | ssh_kex2(host, hostaddr); | 1176 | ssh_kex2(host, hostaddr, port); |
1122 | ssh_userauth2(local_user, server_user, host, sensitive); | 1177 | ssh_userauth2(local_user, server_user, host, sensitive); |
1123 | } else { | 1178 | } else { |
1124 | ssh_kex(host, hostaddr); | 1179 | ssh_kex(host, hostaddr); |
@@ -1145,68 +1200,41 @@ ssh_put_password(char *password) | |||
1145 | xfree(padded); | 1200 | xfree(padded); |
1146 | } | 1201 | } |
1147 | 1202 | ||
1148 | static int | ||
1149 | show_key_from_file(const char *file, const char *host, int keytype) | ||
1150 | { | ||
1151 | Key *found; | ||
1152 | char *fp, *ra; | ||
1153 | int line, ret; | ||
1154 | |||
1155 | found = key_new(keytype); | ||
1156 | if ((ret = lookup_key_in_hostfile_by_type(file, host, | ||
1157 | keytype, found, &line))) { | ||
1158 | fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); | ||
1159 | ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART); | ||
1160 | logit("WARNING: %s key found for host %s\n" | ||
1161 | "in %s:%d\n" | ||
1162 | "%s key fingerprint %s.\n%s\n", | ||
1163 | key_type(found), host, file, line, | ||
1164 | key_type(found), fp, ra); | ||
1165 | xfree(ra); | ||
1166 | xfree(fp); | ||
1167 | } | ||
1168 | key_free(found); | ||
1169 | return (ret); | ||
1170 | } | ||
1171 | |||
1172 | /* print all known host keys for a given host, but skip keys of given type */ | 1203 | /* print all known host keys for a given host, but skip keys of given type */ |
1173 | static int | 1204 | static int |
1174 | show_other_keys(const char *host, Key *key) | 1205 | show_other_keys(struct hostkeys *hostkeys, Key *key) |
1175 | { | 1206 | { |
1176 | int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1}; | 1207 | int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; |
1177 | int i, found = 0; | 1208 | int i, ret = 0; |
1209 | char *fp, *ra; | ||
1210 | const struct hostkey_entry *found; | ||
1178 | 1211 | ||
1179 | for (i = 0; type[i] != -1; i++) { | 1212 | for (i = 0; type[i] != -1; i++) { |
1180 | if (type[i] == key->type) | 1213 | if (type[i] == key->type) |
1181 | continue; | 1214 | continue; |
1182 | if (type[i] != KEY_RSA1 && | 1215 | if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) |
1183 | show_key_from_file(options.user_hostfile2, host, type[i])) { | ||
1184 | found = 1; | ||
1185 | continue; | ||
1186 | } | ||
1187 | if (type[i] != KEY_RSA1 && | ||
1188 | show_key_from_file(options.system_hostfile2, host, type[i])) { | ||
1189 | found = 1; | ||
1190 | continue; | ||
1191 | } | ||
1192 | if (show_key_from_file(options.user_hostfile, host, type[i])) { | ||
1193 | found = 1; | ||
1194 | continue; | ||
1195 | } | ||
1196 | if (show_key_from_file(options.system_hostfile, host, type[i])) { | ||
1197 | found = 1; | ||
1198 | continue; | 1216 | continue; |
1199 | } | 1217 | fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); |
1200 | debug2("no key of type %d for host %s", type[i], host); | 1218 | ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); |
1219 | logit("WARNING: %s key found for host %s\n" | ||
1220 | "in %s:%lu\n" | ||
1221 | "%s key fingerprint %s.", | ||
1222 | key_type(found->key), | ||
1223 | found->host, found->file, found->line, | ||
1224 | key_type(found->key), fp); | ||
1225 | if (options.visual_host_key) | ||
1226 | logit("%s", ra); | ||
1227 | xfree(ra); | ||
1228 | xfree(fp); | ||
1229 | ret = 1; | ||
1201 | } | 1230 | } |
1202 | return (found); | 1231 | return ret; |
1203 | } | 1232 | } |
1204 | 1233 | ||
1205 | static void | 1234 | static void |
1206 | warn_changed_key(Key *host_key) | 1235 | warn_changed_key(Key *host_key) |
1207 | { | 1236 | { |
1208 | char *fp; | 1237 | char *fp; |
1209 | const char *type = key_type(host_key); | ||
1210 | 1238 | ||
1211 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); | 1239 | fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); |
1212 | 1240 | ||
@@ -1215,9 +1243,9 @@ warn_changed_key(Key *host_key) | |||
1215 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 1243 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
1216 | error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); | 1244 | error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); |
1217 | error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); | 1245 | error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); |
1218 | error("It is also possible that the %s host key has just been changed.", type); | 1246 | error("It is also possible that a host key has just been changed."); |
1219 | error("The fingerprint for the %s key sent by the remote host is\n%s.", | 1247 | error("The fingerprint for the %s key sent by the remote host is\n%s.", |
1220 | type, fp); | 1248 | key_type(host_key), fp); |
1221 | error("Please contact your system administrator."); | 1249 | error("Please contact your system administrator."); |
1222 | 1250 | ||
1223 | xfree(fp); | 1251 | xfree(fp); |
@@ -1232,16 +1260,19 @@ ssh_local_cmd(const char *args) | |||
1232 | char *shell; | 1260 | char *shell; |
1233 | pid_t pid; | 1261 | pid_t pid; |
1234 | int status; | 1262 | int status; |
1263 | void (*osighand)(int); | ||
1235 | 1264 | ||
1236 | if (!options.permit_local_command || | 1265 | if (!options.permit_local_command || |
1237 | args == NULL || !*args) | 1266 | args == NULL || !*args) |
1238 | return (1); | 1267 | return (1); |
1239 | 1268 | ||
1240 | if ((shell = getenv("SHELL")) == NULL) | 1269 | if ((shell = getenv("SHELL")) == NULL || *shell == '\0') |
1241 | shell = _PATH_BSHELL; | 1270 | shell = _PATH_BSHELL; |
1242 | 1271 | ||
1272 | osighand = signal(SIGCHLD, SIG_DFL); | ||
1243 | pid = fork(); | 1273 | pid = fork(); |
1244 | if (pid == 0) { | 1274 | if (pid == 0) { |
1275 | signal(SIGPIPE, SIG_DFL); | ||
1245 | debug3("Executing %s -c \"%s\"", shell, args); | 1276 | debug3("Executing %s -c \"%s\"", shell, args); |
1246 | execlp(shell, shell, "-c", args, (char *)NULL); | 1277 | execlp(shell, shell, "-c", args, (char *)NULL); |
1247 | error("Couldn't execute %s -c \"%s\": %s", | 1278 | error("Couldn't execute %s -c \"%s\": %s", |
@@ -1252,6 +1283,7 @@ ssh_local_cmd(const char *args) | |||
1252 | while (waitpid(pid, &status, 0) == -1) | 1283 | while (waitpid(pid, &status, 0) == -1) |
1253 | if (errno != EINTR) | 1284 | if (errno != EINTR) |
1254 | fatal("Couldn't wait for child: %s", strerror(errno)); | 1285 | fatal("Couldn't wait for child: %s", strerror(errno)); |
1286 | signal(SIGCHLD, osighand); | ||
1255 | 1287 | ||
1256 | if (!WIFEXITED(status)) | 1288 | if (!WIFEXITED(status)) |
1257 | return (1); | 1289 | return (1); |