diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 78 |
1 files changed, 65 insertions, 13 deletions
diff --git a/sshconnect.c b/sshconnect.c index 63c4650f7..35c2f49be 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshconnect.c,v 1.218 2010/01/13 00:19:04 dtucker Exp $ */ | 1 | /* $OpenBSD: sshconnect.c,v 1.219 2010/02/26 20:29:54 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 |
@@ -58,6 +58,7 @@ | |||
58 | #include "misc.h" | 58 | #include "misc.h" |
59 | #include "dns.h" | 59 | #include "dns.h" |
60 | #include "roaming.h" | 60 | #include "roaming.h" |
61 | #include "ssh2.h" | ||
61 | #include "version.h" | 62 | #include "version.h" |
62 | 63 | ||
63 | char *client_version_string = NULL; | 64 | char *client_version_string = NULL; |
@@ -576,6 +577,23 @@ confirm(const char *prompt) | |||
576 | } | 577 | } |
577 | } | 578 | } |
578 | 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 | |||
579 | /* | 597 | /* |
580 | * 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 |
581 | * 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. |
@@ -588,13 +606,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
588 | Key *host_key, int readonly, const char *user_hostfile, | 606 | Key *host_key, int readonly, const char *user_hostfile, |
589 | const char *system_hostfile) | 607 | const char *system_hostfile) |
590 | { | 608 | { |
591 | Key *file_key; | 609 | Key *file_key, *raw_key = NULL; |
592 | const char *type = key_type(host_key); | 610 | const char *type; |
593 | char *ip = NULL, *host = NULL; | 611 | char *ip = NULL, *host = NULL; |
594 | char hostline[1000], *hostp, *fp, *ra; | 612 | char hostline[1000], *hostp, *fp, *ra; |
595 | HostStatus host_status; | 613 | HostStatus host_status; |
596 | HostStatus ip_status; | 614 | HostStatus ip_status; |
597 | int r, local = 0, host_ip_differ = 0; | 615 | int r, want_cert, local = 0, host_ip_differ = 0; |
598 | int salen; | 616 | int salen; |
599 | char ntop[NI_MAXHOST]; | 617 | char ntop[NI_MAXHOST]; |
600 | char msg[1024]; | 618 | char msg[1024]; |
@@ -667,11 +685,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
667 | host = put_host_port(hostname, port); | 685 | host = put_host_port(hostname, port); |
668 | } | 686 | } |
669 | 687 | ||
688 | retry: | ||
689 | want_cert = key_is_cert(host_key); | ||
690 | type = key_type(host_key); | ||
691 | |||
670 | /* | 692 | /* |
671 | * 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 |
672 | * compare it with the key for the IP address. | 694 | * compare it with the key for the IP address. |
673 | */ | 695 | */ |
674 | file_key = key_new(host_key->type); | 696 | file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type); |
675 | 697 | ||
676 | /* | 698 | /* |
677 | * 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 |
@@ -687,9 +709,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
687 | } | 709 | } |
688 | /* | 710 | /* |
689 | * 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 |
690 | * 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. | ||
691 | */ | 714 | */ |
692 | if (options.check_host_ip) { | 715 | if (!want_cert && options.check_host_ip) { |
693 | Key *ip_key = key_new(host_key->type); | 716 | Key *ip_key = key_new(host_key->type); |
694 | 717 | ||
695 | ip_file = user_hostfile; | 718 | ip_file = user_hostfile; |
@@ -713,11 +736,14 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
713 | switch (host_status) { | 736 | switch (host_status) { |
714 | case HOST_OK: | 737 | case HOST_OK: |
715 | /* The host is known and the key matches. */ | 738 | /* The host is known and the key matches. */ |
716 | debug("Host '%.200s' is known and matches the %s host key.", | 739 | debug("Host '%.200s' is known and matches the %s host %s.", |
717 | host, type); | 740 | host, type, want_cert ? "certificate" : "key"); |
718 | 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; | ||
719 | if (options.check_host_ip && ip_status == HOST_NEW) { | 745 | if (options.check_host_ip && ip_status == HOST_NEW) { |
720 | if (readonly) | 746 | if (readonly || want_cert) |
721 | logit("%s host key for IP address " | 747 | logit("%s host key for IP address " |
722 | "'%.128s' not in list of known hosts.", | 748 | "'%.128s' not in list of known hosts.", |
723 | type, ip); | 749 | type, ip); |
@@ -749,7 +775,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
749 | break; | 775 | break; |
750 | } | 776 | } |
751 | } | 777 | } |
752 | if (readonly) | 778 | if (readonly || want_cert) |
753 | goto fail; | 779 | goto fail; |
754 | /* The host is new. */ | 780 | /* The host is new. */ |
755 | if (options.strict_host_key_checking == 1) { | 781 | if (options.strict_host_key_checking == 1) { |
@@ -834,6 +860,17 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
834 | "list of known hosts.", hostp, type); | 860 | "list of known hosts.", hostp, type); |
835 | break; | 861 | break; |
836 | case HOST_CHANGED: | 862 | case HOST_CHANGED: |
863 | if (want_cert) { | ||
864 | /* | ||
865 | * This is only a debug() since it is valid to have | ||
866 | * CAs with wildcard DNS matches that don't match | ||
867 | * all hosts that one might visit. | ||
868 | */ | ||
869 | debug("Host certificate authority does not " | ||
870 | "match %s in %s:%d", CA_MARKER, | ||
871 | host_file, host_line); | ||
872 | goto fail; | ||
873 | } | ||
837 | if (readonly == ROQUIET) | 874 | if (readonly == ROQUIET) |
838 | goto fail; | 875 | goto fail; |
839 | if (options.check_host_ip && host_ip_differ) { | 876 | if (options.check_host_ip && host_ip_differ) { |
@@ -970,6 +1007,20 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, | |||
970 | return 0; | 1007 | return 0; |
971 | 1008 | ||
972 | fail: | 1009 | fail: |
1010 | if (want_cert) { | ||
1011 | /* | ||
1012 | * No matching certificate. Downgrade cert to raw key and | ||
1013 | * search normally. | ||
1014 | */ | ||
1015 | debug("No matching CA found. Retry with plain key"); | ||
1016 | raw_key = key_from_private(host_key); | ||
1017 | if (key_drop_cert(raw_key) != 0) | ||
1018 | fatal("Couldn't drop certificate"); | ||
1019 | host_key = raw_key; | ||
1020 | goto retry; | ||
1021 | } | ||
1022 | if (raw_key != NULL) | ||
1023 | key_free(raw_key); | ||
973 | xfree(ip); | 1024 | xfree(ip); |
974 | xfree(host); | 1025 | xfree(host); |
975 | return -1; | 1026 | return -1; |
@@ -982,7 +1033,8 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
982 | struct stat st; | 1033 | struct stat st; |
983 | int flags = 0; | 1034 | int flags = 0; |
984 | 1035 | ||
985 | if (options.verify_host_key_dns && | 1036 | /* XXX certs are not yet supported for DNS */ |
1037 | if (!key_is_cert(host_key) && options.verify_host_key_dns && | ||
986 | verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { | 1038 | verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { |
987 | 1039 | ||
988 | if (flags & DNS_VERIFY_FOUND) { | 1040 | if (flags & DNS_VERIFY_FOUND) { |