diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 106 |
1 files changed, 91 insertions, 15 deletions
diff --git a/sshconnect.c b/sshconnect.c index 96f823f93..b6fea4d7e 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.214 2009/05/28 16:50:16 andreas Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.220 2010/03/04 10:36:03 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 |
@@ -28,6 +28,7 @@ | |||
28 | 28 | ||
29 | #include <ctype.h> | 29 | #include <ctype.h> |
30 | #include <errno.h> | 30 | #include <errno.h> |
31 | #include <fcntl.h> | ||
31 | #include <netdb.h> | 32 | #include <netdb.h> |
32 | #ifdef HAVE_PATHS_H | 33 | #ifdef HAVE_PATHS_H |
33 | #include <paths.h> | 34 | #include <paths.h> |
@@ -57,6 +58,7 @@ | |||
57 | #include "misc.h" | 58 | #include "misc.h" |
58 | #include "dns.h" | 59 | #include "dns.h" |
59 | #include "roaming.h" | 60 | #include "roaming.h" |
61 | #include "ssh2.h" | ||
60 | #include "version.h" | 62 | #include "version.h" |
61 | 63 | ||
62 | char *client_version_string = NULL; | 64 | char *client_version_string = NULL; |
@@ -191,8 +193,11 @@ ssh_create_socket(int privileged, struct addrinfo *ai) | |||
191 | return sock; | 193 | return sock; |
192 | } | 194 | } |
193 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | 195 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
194 | if (sock < 0) | 196 | if (sock < 0) { |
195 | error("socket: %.100s", strerror(errno)); | 197 | error("socket: %.100s", strerror(errno)); |
198 | return -1; | ||
199 | } | ||
200 | fcntl(sock, F_SETFD, FD_CLOEXEC); | ||
196 | 201 | ||
197 | /* Bind the socket to an alternative local IP address */ | 202 | /* Bind the socket to an alternative local IP address */ |
198 | if (options.bind_address == NULL) | 203 | if (options.bind_address == NULL) |
@@ -572,6 +577,23 @@ confirm(const char *prompt) | |||
572 | } | 577 | } |
573 | } | 578 | } |
574 | 579 | ||
580 | static int | ||
581 | check_host_cert(const char *host, const Key *host_key) | ||
582 | { | ||
583 | const char *reason; | ||
584 | |||
585 | if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { | ||
586 | error("%s", reason); | ||
587 | return 0; | ||
588 | } | ||
589 | if (buffer_len(&host_key->cert->constraints) != 0) { | ||
590 | error("Certificate for %s contains unsupported constraint(s)", | ||
591 | host); | ||
592 | return 0; | ||
593 | } | ||
594 | return 1; | ||
595 | } | ||
596 | |||
575 | /* | 597 | /* |
576 | * check whether the supplied host key is valid, return -1 if the key | 598 | * check whether the supplied host key is valid, return -1 if the key |
577 | * is not valid. the user_hostfile will not be updated if 'readonly' is true. | 599 | * is not valid. the user_hostfile will not be updated if 'readonly' is true. |
@@ -584,13 +606,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
584 | Key *host_key, int readonly, const char *user_hostfile, | 606 | Key *host_key, int readonly, const char *user_hostfile, |
585 | const char *system_hostfile) | 607 | const char *system_hostfile) |
586 | { | 608 | { |
587 | Key *file_key; | 609 | Key *file_key, *raw_key = NULL; |
588 | const char *type = key_type(host_key); | 610 | const char *type; |
589 | char *ip = NULL, *host = NULL; | 611 | char *ip = NULL, *host = NULL; |
590 | char hostline[1000], *hostp, *fp, *ra; | 612 | char hostline[1000], *hostp, *fp, *ra; |
591 | HostStatus host_status; | 613 | HostStatus host_status; |
592 | HostStatus ip_status; | 614 | HostStatus ip_status; |
593 | int r, local = 0, host_ip_differ = 0; | 615 | int r, want_cert, local = 0, host_ip_differ = 0; |
594 | int salen; | 616 | int salen; |
595 | char ntop[NI_MAXHOST]; | 617 | char ntop[NI_MAXHOST]; |
596 | char msg[1024]; | 618 | char msg[1024]; |
@@ -663,11 +685,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
663 | host = put_host_port(hostname, port); | 685 | host = put_host_port(hostname, port); |
664 | } | 686 | } |
665 | 687 | ||
688 | retry: | ||
689 | want_cert = key_is_cert(host_key); | ||
690 | type = key_type(host_key); | ||
691 | |||
666 | /* | 692 | /* |
667 | * Store the host key from the known host file in here so that we can | 693 | * Store the host key from the known host file in here so that we can |
668 | * compare it with the key for the IP address. | 694 | * compare it with the key for the IP address. |
669 | */ | 695 | */ |
670 | file_key = key_new(host_key->type); | 696 | file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type); |
671 | 697 | ||
672 | /* | 698 | /* |
673 | * Check if the host key is present in the user's list of known | 699 | * Check if the host key is present in the user's list of known |
@@ -683,9 +709,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
683 | } | 709 | } |
684 | /* | 710 | /* |
685 | * Also perform check for the ip address, skip the check if we are | 711 | * Also perform check for the ip address, skip the check if we are |
686 | * localhost or the hostname was an ip address to begin with | 712 | * localhost, looking for a certificate, or the hostname was an ip |
713 | * address to begin with. | ||
687 | */ | 714 | */ |
688 | if (options.check_host_ip) { | 715 | if (!want_cert && options.check_host_ip) { |
689 | Key *ip_key = key_new(host_key->type); | 716 | Key *ip_key = key_new(host_key->type); |
690 | 717 | ||
691 | ip_file = user_hostfile; | 718 | ip_file = user_hostfile; |
@@ -709,11 +736,14 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
709 | switch (host_status) { | 736 | switch (host_status) { |
710 | case HOST_OK: | 737 | case HOST_OK: |
711 | /* The host is known and the key matches. */ | 738 | /* The host is known and the key matches. */ |
712 | debug("Host '%.200s' is known and matches the %s host key.", | 739 | debug("Host '%.200s' is known and matches the %s host %s.", |
713 | host, type); | 740 | host, type, want_cert ? "certificate" : "key"); |
714 | debug("Found key in %s:%d", host_file, host_line); | 741 | debug("Found %s in %s:%d", |
742 | want_cert ? "certificate" : "key", host_file, host_line); | ||
743 | if (want_cert && !check_host_cert(hostname, host_key)) | ||
744 | goto fail; | ||
715 | if (options.check_host_ip && ip_status == HOST_NEW) { | 745 | if (options.check_host_ip && ip_status == HOST_NEW) { |
716 | if (readonly) | 746 | if (readonly || want_cert) |
717 | logit("%s host key for IP address " | 747 | logit("%s host key for IP address " |
718 | "'%.128s' not in list of known hosts.", | 748 | "'%.128s' not in list of known hosts.", |
719 | type, ip); | 749 | type, ip); |
@@ -745,7 +775,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
745 | break; | 775 | break; |
746 | } | 776 | } |
747 | } | 777 | } |
748 | if (readonly) | 778 | if (readonly || want_cert) |
749 | goto fail; | 779 | goto fail; |
750 | /* The host is new. */ | 780 | /* The host is new. */ |
751 | if (options.strict_host_key_checking == 1) { | 781 | if (options.strict_host_key_checking == 1) { |
@@ -829,7 +859,37 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
829 | logit("Warning: Permanently added '%.200s' (%s) to the " | 859 | logit("Warning: Permanently added '%.200s' (%s) to the " |
830 | "list of known hosts.", hostp, type); | 860 | "list of known hosts.", hostp, type); |
831 | break; | 861 | break; |
862 | case HOST_REVOKED: | ||
863 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
864 | error("@ WARNING: REVOKED HOST KEY DETECTED! @"); | ||
865 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
866 | error("The %s host key for %s is marked as revoked.", type, host); | ||
867 | error("This could mean that a stolen key is being used to"); | ||
868 | error("impersonate this host."); | ||
869 | |||
870 | /* | ||
871 | * If strict host key checking is in use, the user will have | ||
872 | * to edit the key manually and we can only abort. | ||
873 | */ | ||
874 | if (options.strict_host_key_checking) { | ||
875 | error("%s host key for %.200s was revoked and you have " | ||
876 | "requested strict checking.", type, host); | ||
877 | goto fail; | ||
878 | } | ||
879 | goto continue_unsafe; | ||
880 | |||
832 | case HOST_CHANGED: | 881 | case HOST_CHANGED: |
882 | if (want_cert) { | ||
883 | /* | ||
884 | * This is only a debug() since it is valid to have | ||
885 | * CAs with wildcard DNS matches that don't match | ||
886 | * all hosts that one might visit. | ||
887 | */ | ||
888 | debug("Host certificate authority does not " | ||
889 | "match %s in %s:%d", CA_MARKER, | ||
890 | host_file, host_line); | ||
891 | goto fail; | ||
892 | } | ||
833 | if (readonly == ROQUIET) | 893 | if (readonly == ROQUIET) |
834 | goto fail; | 894 | goto fail; |
835 | if (options.check_host_ip && host_ip_differ) { | 895 | if (options.check_host_ip && host_ip_differ) { |
@@ -867,6 +927,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
867 | goto fail; | 927 | goto fail; |
868 | } | 928 | } |
869 | 929 | ||
930 | continue_unsafe: | ||
870 | /* | 931 | /* |
871 | * If strict host key checking has not been requested, allow | 932 | * If strict host key checking has not been requested, allow |
872 | * the connection but without MITM-able authentication or | 933 | * the connection but without MITM-able authentication or |
@@ -925,7 +986,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
925 | * XXX Should permit the user to change to use the new id. | 986 | * XXX Should permit the user to change to use the new id. |
926 | * This could be done by converting the host key to an | 987 | * This could be done by converting the host key to an |
927 | * identifying sentence, tell that the host identifies itself | 988 | * identifying sentence, tell that the host identifies itself |
928 | * by that sentence, and ask the user if he/she whishes to | 989 | * by that sentence, and ask the user if he/she wishes to |
929 | * accept the authentication. | 990 | * accept the authentication. |
930 | */ | 991 | */ |
931 | break; | 992 | break; |
@@ -966,6 +1027,20 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
966 | return 0; | 1027 | return 0; |
967 | 1028 | ||
968 | fail: | 1029 | fail: |
1030 | if (want_cert && host_status != HOST_REVOKED) { | ||
1031 | /* | ||
1032 | * No matching certificate. Downgrade cert to raw key and | ||
1033 | * search normally. | ||
1034 | */ | ||
1035 | debug("No matching CA found. Retry with plain key"); | ||
1036 | raw_key = key_from_private(host_key); | ||
1037 | if (key_drop_cert(raw_key) != 0) | ||
1038 | fatal("Couldn't drop certificate"); | ||
1039 | host_key = raw_key; | ||
1040 | goto retry; | ||
1041 | } | ||
1042 | if (raw_key != NULL) | ||
1043 | key_free(raw_key); | ||
969 | xfree(ip); | 1044 | xfree(ip); |
970 | xfree(host); | 1045 | xfree(host); |
971 | return -1; | 1046 | return -1; |
@@ -978,7 +1053,8 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
978 | struct stat st; | 1053 | struct stat st; |
979 | int flags = 0; | 1054 | int flags = 0; |
980 | 1055 | ||
981 | if (options.verify_host_key_dns && | 1056 | /* XXX certs are not yet supported for DNS */ |
1057 | if (!key_is_cert(host_key) && options.verify_host_key_dns && | ||
982 | verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { | 1058 | verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { |
983 | 1059 | ||
984 | if (flags & DNS_VERIFY_FOUND) { | 1060 | if (flags & DNS_VERIFY_FOUND) { |