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