From 37876e913a069036501086a247ed2ea430cea206 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 15 May 2003 10:19:46 +1000 Subject: [PATCH] - jakob@cvs.openbsd.org 2003/05/14 18:16:20 [key.c key.h readconf.c readconf.h ssh_config.5 sshconnect.c] [dns.c dns.h README.dns ssh-keygen.1 ssh-keygen.c] add experimental support for verifying hos keys using DNS as described in draft-ietf-secsh-dns-xx.txt. more information in README.dns. ok markus@ and henning@ --- ChangeLog | 8 +- Makefile.in | 8 +- README.dns | 55 ++++++++++ dns.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++ dns.h | 57 ++++++++++ key.c | 4 +- key.h | 3 +- readconf.c | 12 ++- readconf.h | 3 +- ssh-keygen.1 | 11 +- ssh-keygen.c | 55 +++++++++- ssh_config.5 | 7 +- sshconnect.c | 23 +++- 13 files changed, 523 insertions(+), 16 deletions(-) create mode 100644 README.dns create mode 100644 dns.c create mode 100644 dns.h diff --git a/ChangeLog b/ChangeLog index 8ad7108a6..8feae8b23 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,12 @@ [ssh-agent.1] setup -> set up; from wiz@netbsd + - jakob@cvs.openbsd.org 2003/05/14 18:16:20 + [key.c key.h readconf.c readconf.h ssh_config.5 sshconnect.c] + [dns.c dns.h README.dns ssh-keygen.1 ssh-keygen.c] + add experimental support for verifying hos keys using DNS as described + in draft-ietf-secsh-dns-xx.txt. more information in README.dns. + ok markus@ and henning@ 20030514 - (djm) Bug #117: Don't lie to PAM about username @@ -1479,4 +1485,4 @@ save auth method before monitor_reset_key_state(); bugzilla bug #284; ok provos@ -$Id: ChangeLog,v 1.2701 2003/05/15 00:16:21 djm Exp $ +$Id: ChangeLog,v 1.2702 2003/05/15 00:19:46 djm Exp $ diff --git a/Makefile.in b/Makefile.in index ba898db40..f25fe0ae5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,4 +1,4 @@ -# $Id: Makefile.in,v 1.232 2003/05/14 04:31:11 djm Exp $ +# $Id: Makefile.in,v 1.233 2003/05/15 00:19:46 djm Exp $ # uncomment if you run a non bourne compatable shell. Ie. csh #SHELL = @SH@ @@ -62,11 +62,11 @@ TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keys LIBSSH_OBJS=authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o \ cipher.o compat.o compress.o crc32.o deattack.o fatal.o \ - hostfile.o log.o match.o mpaux.o nchan.o packet.o radix.o readpass.o \ - rsa.o tildexpand.o ttymodes.o xmalloc.o atomicio.o \ + hostfile.o log.o match.o mpaux.o nchan.o packet.o radix.o \ + readpass.o rsa.o tildexpand.o ttymodes.o xmalloc.o atomicio.o \ key.o dispatch.o kex.o mac.o uuencode.o misc.o \ rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o kexgex.o \ - kexdhc.o kexgexc.o scard.o msg.o progressmeter.o \ + kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ entropy.o SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ diff --git a/README.dns b/README.dns new file mode 100644 index 000000000..d6889b9a5 --- /dev/null +++ b/README.dns @@ -0,0 +1,55 @@ +How to verify host keys using OpenSSH and DNS +--------------------------------------------- + +OpenSSH contains experimental support for verifying host keys using DNS +as described in draft-ietf-secsh-dns-xx.txt. The document contains +very brief instructions on how to test this feature. Configuring DNS +and DNSSEC is out of the scope of this document. + + +(1) Enable DNS fingerprint support in OpenSSH + +Edit /usr/src/usr.bin/ssh/Makefile.inc and uncomment the line containing + + CFLAGS+= -DDNS + + +(2) Generate and publish the DNS RR + +To create a DNS resource record (RR) containing a fingerprint of the +public host key, use the following command: + + ssh-keygen -r hostname -f keyfile -g + +where "hostname" is your fully qualified hostname and "keyfile" is the +file containing the public host key file. If you have multiple keys, +you should generate one RR for each key. + +In the example above, ssh-keygen will print the fingerprint in a +generic DNS RR format parsable by most modern name server +implementations. If your nameserver has support for the SSHFP RR, as +defined by the draft, you can omit the -g flag and ssh-keygen will +print a standard RR. + +To publish the fingerprint using the DNS you must add the generated RR +to your DNS zone file and sign your zone. + + +(3) Enable the ssh client to verify host keys using DNS + +To enable the ssh client to verify host keys using DNS, you have to +add the following option to the ssh configuration file +($HOME/.ssh/config or /etc/ssh/ssh_config): + + VerifyHostKeyDNS yes + +Upon connection the client will try to look up the fingerprint RR +using DNS. If the fingerprint received from the DNS server matches +the remote host key, the user will be notified. + + + Jakob Schlyter + Wesley Griffin + + +$OpenBSD: README.dns,v 1.1 2003/05/14 18:16:20 jakob Exp $ diff --git a/dns.c b/dns.c new file mode 100644 index 000000000..9b7a0e7a0 --- /dev/null +++ b/dns.c @@ -0,0 +1,293 @@ +/* $OpenBSD: dns.c,v 1.4 2003/05/14 23:29:22 jakob Exp $ */ + +/* + * Copyright (c) 2003 Wesley Griffin. All rights reserved. + * Copyright (c) 2003 Jakob Schlyter. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "includes.h" + +#ifdef DNS +#include +#ifdef LWRES +#include +#include +#else /* LWRES */ +#include +#endif /* LWRES */ + +#include "xmalloc.h" +#include "key.h" +#include "dns.h" +#include "log.h" +#include "uuencode.h" + +extern char *__progname; +RCSID("$OpenBSD: dns.c,v 1.4 2003/05/14 23:29:22 jakob Exp $"); + +#ifndef LWRES +static const char *errset_text[] = { + "success", /* 0 ERRSET_SUCCESS */ + "out of memory", /* 1 ERRSET_NOMEMORY */ + "general failure", /* 2 ERRSET_FAIL */ + "invalid parameter", /* 3 ERRSET_INVAL */ + "name does not exist", /* 4 ERRSET_NONAME */ + "data does not exist", /* 5 ERRSET_NODATA */ +}; + +static const char * +dns_result_totext(unsigned int error) +{ + switch (error) { + case ERRSET_SUCCESS: + return errset_text[ERRSET_SUCCESS]; + case ERRSET_NOMEMORY: + return errset_text[ERRSET_NOMEMORY]; + case ERRSET_FAIL: + return errset_text[ERRSET_FAIL]; + case ERRSET_INVAL: + return errset_text[ERRSET_INVAL]; + case ERRSET_NONAME: + return errset_text[ERRSET_NONAME]; + case ERRSET_NODATA: + return errset_text[ERRSET_NODATA]; + default: + return "unknown error"; + } +} +#endif /* LWRES */ + + +/* + * Read SSHFP parameters from key buffer. + */ +static int +dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type, + u_char **digest, u_int *digest_len, Key *key) +{ + int success = 0; + + switch (key->type) { + case KEY_RSA: + *algorithm = SSHFP_KEY_RSA; + break; + case KEY_DSA: + *algorithm = SSHFP_KEY_DSA; + break; + default: + *algorithm = SSHFP_KEY_RESERVED; + } + + if (*algorithm) { + *digest_type = SSHFP_HASH_SHA1; + *digest = key_fingerprint_raw(key, SSH_FP_SHA1, digest_len); + success = 1; + } else { + *digest_type = SSHFP_HASH_RESERVED; + *digest = NULL; + *digest_len = 0; + success = 0; + } + + return success; +} + +/* + * Read SSHFP parameters from rdata buffer. + */ +static int +dns_read_rdata(u_int8_t *algorithm, u_int8_t *digest_type, + u_char **digest, u_int *digest_len, u_char *rdata, int rdata_len) +{ + int success = 0; + + *algorithm = SSHFP_KEY_RESERVED; + *digest_type = SSHFP_HASH_RESERVED; + + if (rdata_len >= 2) { + *algorithm = rdata[0]; + *digest_type = rdata[1]; + *digest_len = rdata_len - 2; + + if (*digest_len > 0) { + *digest = (u_char *) xmalloc(*digest_len); + memcpy(*digest, rdata + 2, *digest_len); + } else { + *digest = NULL; + } + + success = 1; + } + + return success; +} + + +/* + * Verify the given hostname, address and host key using DNS. + * Returns 0 if key verifies or -1 if key does NOT verify + */ +int +verify_host_key_dns(const char *hostname, struct sockaddr *address, + Key *hostkey) +{ + int counter; + int result; + struct rrsetinfo *fingerprints = NULL; + int failures = 0; + + u_int8_t hostkey_algorithm; + u_int8_t hostkey_digest_type; + u_char *hostkey_digest; + u_int hostkey_digest_len; + + u_int8_t dnskey_algorithm; + u_int8_t dnskey_digest_type; + u_char *dnskey_digest; + u_int dnskey_digest_len; + + + debug3("verify_hostkey_dns"); + if (hostkey == NULL) + fatal("No key to look up!"); + + result = getrrsetbyname(hostname, DNS_RDATACLASS_IN, + DNS_RDATATYPE_SSHFP, 0, &fingerprints); + if (result) { + verbose("DNS lookup error: %s", dns_result_totext(result)); + return DNS_VERIFY_ERROR; + } + +#ifdef DNSSEC + /* Only accept validated answers */ + if (!fingerprints->rri_flags & RRSET_VALIDATED) { + error("Ignored unvalidated fingerprint from DNS."); + return DNS_VERIFY_ERROR; + } +#endif + + debug("found %d fingerprints in DNS", fingerprints->rri_nrdatas); + + /* Initialize host key parameters */ + if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type, + &hostkey_digest, &hostkey_digest_len, hostkey)) { + error("Error calculating host key fingerprint."); + return DNS_VERIFY_ERROR; + } + + for (counter = 0 ; counter < fingerprints->rri_nrdatas ; counter++) { + /* + * Extract the key from the answer. Ignore any badly + * formatted fingerprints. + */ + if (!dns_read_rdata(&dnskey_algorithm, &dnskey_digest_type, + &dnskey_digest, &dnskey_digest_len, + fingerprints->rri_rdatas[counter].rdi_data, + fingerprints->rri_rdatas[counter].rdi_length)) { + verbose("Error parsing fingerprint from DNS."); + continue; + } + + /* Check if the current key is the same as the given key */ + if (hostkey_algorithm == dnskey_algorithm && + hostkey_digest_type == dnskey_digest_type) { + + if (hostkey_digest_len == dnskey_digest_len && + memcmp(hostkey_digest, dnskey_digest, + hostkey_digest_len) == 0) { + + /* Matching algoritm and digest. */ + freerrset(fingerprints); +#ifdef DNSSEC + debug("matching host key fingerprint found in DNS"); + return DNS_VERIFY_OK; +#else + logit("Matching host key fingerprint found in DNS."); + return DNS_VERIFY_ERROR; +#endif + } else { + /* Correct algorithm but bad digest */ + debug("verify_hostkey_dns: failed"); + failures++; + } + } + } + + freerrset(fingerprints); + + if (failures) { + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); + error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); + error("It is also possible that the %s host key has just been changed.", + key_type(hostkey)); + error("Please contact your system administrator."); + return DNS_VERIFY_FAILED; + } + + debug("fingerprints found in DNS, but none of them matched"); + + return DNS_VERIFY_ERROR; +} + + +/* + * Export the fingerprint of a key as a DNS resource record + */ +int +export_dns_rr(const char *hostname, Key *key, FILE *f, int generic) +{ + u_int8_t rdata_pubkey_algorithm = 0; + u_int8_t rdata_digest_type = SSHFP_HASH_SHA1; + u_char *rdata_digest; + u_int rdata_digest_len; + + int i; + int success = 0; + + if (dns_read_key(&rdata_pubkey_algorithm, &rdata_digest_type, + &rdata_digest, &rdata_digest_len, key)) { + + if (generic) + fprintf(f, "%s IN TYPE%d \\# %d %02x %02x ", hostname, + DNS_RDATATYPE_SSHFP, 2 + rdata_digest_len, + rdata_pubkey_algorithm, rdata_digest_type); + else + fprintf(f, "%s IN SSHFP %d %d ", hostname, + rdata_pubkey_algorithm, rdata_digest_type); + + for (i = 0; i < rdata_digest_len; i++) + fprintf(f, "%02x", rdata_digest[i]); + fprintf(f, "\n"); + success = 1; + } else { + error("dns_export_rr: unsupported algorithm"); + } + + return success; +} + +#endif /* DNS */ diff --git a/dns.h b/dns.h new file mode 100644 index 000000000..ba0ea9fb4 --- /dev/null +++ b/dns.h @@ -0,0 +1,57 @@ +/* $OpenBSD: dns.h,v 1.3 2003/05/14 22:56:51 jakob Exp $ */ + +/* + * Copyright (c) 2003 Wesley Griffin. All rights reserved. + * Copyright (c) 2003 Jakob Schlyter. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "includes.h" + +#ifdef DNS +#ifndef DNS_H +#define DNS_H + +enum sshfp_types { + SSHFP_KEY_RESERVED, + SSHFP_KEY_RSA, + SSHFP_KEY_DSA +}; + +enum sshfp_hashes { + SSHFP_HASH_RESERVED, + SSHFP_HASH_SHA1 +}; + +#define DNS_RDATACLASS_IN 1 +#define DNS_RDATATYPE_SSHFP 44 + +#define DNS_VERIFY_FAILED -1 +#define DNS_VERIFY_OK 0 +#define DNS_VERIFY_ERROR 1 + +int verify_host_key_dns(const char *, struct sockaddr *, Key *); +int export_dns_rr(const char *, Key *, FILE *, int); + +#endif /* DNS_H */ +#endif /* DNS */ diff --git a/key.c b/key.c index 060b63745..d918cfd0a 100644 --- a/key.c +++ b/key.c @@ -32,7 +32,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$OpenBSD: key.c,v 1.51 2003/02/12 09:33:04 markus Exp $"); +RCSID("$OpenBSD: key.c,v 1.52 2003/05/14 18:16:20 jakob Exp $"); #include @@ -169,7 +169,7 @@ key_equal(Key *a, Key *b) return 0; } -static u_char * +u_char* key_fingerprint_raw(Key *k, enum fp_type dgst_type, u_int *dgst_raw_length) { const EVP_MD *md = NULL; diff --git a/key.h b/key.h index 725c7a04a..a7b6afe86 100644 --- a/key.h +++ b/key.h @@ -1,4 +1,4 @@ -/* $OpenBSD: key.h,v 1.20 2003/02/12 09:33:04 markus Exp $ */ +/* $OpenBSD: key.h,v 1.21 2003/05/14 18:16:20 jakob Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -61,6 +61,7 @@ void key_free(Key *); Key *key_demote(Key *); int key_equal(Key *, Key *); char *key_fingerprint(Key *, enum fp_type, enum fp_rep); +u_char *key_fingerprint_raw(Key *, enum fp_type, u_int *); char *key_type(Key *); int key_write(Key *, FILE *); int key_read(Key *, char **); diff --git a/readconf.c b/readconf.c index acdf128f6..c9c463b29 100644 --- a/readconf.c +++ b/readconf.c @@ -12,7 +12,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: readconf.c,v 1.106 2003/04/09 12:00:37 djm Exp $"); +RCSID("$OpenBSD: readconf.c,v 1.107 2003/05/14 18:16:20 jakob Exp $"); #include "ssh.h" #include "xmalloc.h" @@ -114,7 +114,7 @@ typedef enum { oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oSmartcardDevice, oClearAllForwardings, oNoHostAuthenticationForLocalhost, - oEnableSSHKeysign, oRekeyLimit, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oDeprecated } OpCodes; @@ -187,6 +187,7 @@ static struct { { "smartcarddevice", oSmartcardDevice }, { "clearallforwardings", oClearAllForwardings }, { "enablesshkeysign", oEnableSSHKeysign }, + { "verifyhostkeydns", oVerifyHostKeyDNS }, { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, { "rekeylimit", oRekeyLimit }, { NULL, oBadOption } @@ -392,6 +393,10 @@ parse_flag: intptr = &options->check_host_ip; goto parse_flag; + case oVerifyHostKeyDNS: + intptr = &options->verify_host_key_dns; + goto parse_flag; + case oStrictHostKeyChecking: intptr = &options->strict_host_key_checking; arg = strdelim(&s); @@ -829,6 +834,7 @@ initialize_options(Options * options) options->enable_ssh_keysign = - 1; options->no_host_authentication_for_localhost = - 1; options->rekey_limit = - 1; + options->verify_host_key_dns = -1; } /* @@ -947,6 +953,8 @@ fill_default_options(Options * options) options->enable_ssh_keysign = 0; if (options->rekey_limit == -1) options->rekey_limit = 0; + if (options->verify_host_key_dns == -1) + options->verify_host_key_dns = 0; /* options->proxy_command should not be set by default */ /* options->user will be set in the main program if appropriate */ /* options->hostname will be set in the main program if appropriate */ diff --git a/readconf.h b/readconf.h index d35472117..d141b8c00 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.47 2003/04/02 09:48:07 markus Exp $ */ +/* $OpenBSD: readconf.h,v 1.48 2003/05/14 18:16:20 jakob Exp $ */ /* * Author: Tatu Ylonen @@ -86,6 +86,7 @@ typedef struct { char *preferred_authentications; char *bind_address; /* local socket address for connection to sshd */ char *smartcard_device; /* Smartcard reader device */ + int verify_host_key_dns; /* Verify host key using DNS */ int num_identity_files; /* Number of files for RSA/DSA identities. */ char *identity_files[SSH_MAX_IDENTITY_FILES]; diff --git a/ssh-keygen.1 b/ssh-keygen.1 index 000e8ff2a..613d71a07 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.56 2003/03/28 10:11:43 jmc Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.57 2003/05/14 18:16:20 jakob Exp $ .\" .\" -*- nroff -*- .\" @@ -83,6 +83,10 @@ .Nm ssh-keygen .Fl U Ar reader .Op Fl f Ar input_keyfile +.Nm ssh-keygen +.Fl r Ar hostname +.Op Fl f Ar input_keyfile +.Op Fl g .Sh DESCRIPTION .Nm generates, manages and converts authentication keys for @@ -163,6 +167,8 @@ print the key in a to stdout. This option allows exporting keys for use by several commercial SSH implementations. +.It Fl g +Use generic DNS resource record format. .It Fl f Ar filename Specifies the filename of the key file. .It Fl i @@ -218,6 +224,9 @@ Provides the (old) passphrase. .It Fl U Ar reader Upload an existing RSA private key into the smartcard in .Ar reader . +.It Fl r Ar hostname +Print DNS resource record with the specified +.Ar hostname . .El .Sh FILES .Bl -tag -width Ds diff --git a/ssh-keygen.c b/ssh-keygen.c index 1d08c7cec..f3ea4f1fd 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -12,7 +12,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: ssh-keygen.c,v 1.104 2003/05/11 16:56:48 markus Exp $"); +RCSID("$OpenBSD: ssh-keygen.c,v 1.105 2003/05/14 18:16:20 jakob Exp $"); #include #include @@ -70,6 +70,7 @@ char *identity_comment = NULL; int convert_to_ssh2 = 0; int convert_from_ssh2 = 0; int print_public = 0; +int print_generic = 0; char *key_type_name = NULL; @@ -620,6 +621,38 @@ do_change_passphrase(struct passwd *pw) exit(0); } +#ifdef DNS +/* + * Print the SSHFP RR. + */ +static void +do_print_resource_record(struct passwd *pw, char *hostname) +{ + Key *public; + char *comment = NULL; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + public = key_load_public(identity_file, &comment); + if (public != NULL) { + export_dns_rr(hostname, public, stdout, print_generic); + key_free(public); + xfree(comment); + exit(0); + } + if (comment) + xfree(comment); + + printf("failed to read v2 public key from %s.\n", identity_file); + exit(1); +} +#endif /* DNS */ + /* * Change the comment of a private key file. */ @@ -726,6 +759,7 @@ usage(void) fprintf(stderr, " -c Change comment in private and public key files.\n"); fprintf(stderr, " -e Convert OpenSSH to IETF SECSH key file.\n"); fprintf(stderr, " -f filename Filename of the key file.\n"); + fprintf(stderr, " -g Use generic DNS resource record format.\n"); fprintf(stderr, " -i Convert IETF SECSH to OpenSSH key file.\n"); fprintf(stderr, " -l Show fingerprint of key file.\n"); fprintf(stderr, " -p Change passphrase of private key file.\n"); @@ -736,6 +770,9 @@ usage(void) fprintf(stderr, " -C comment Provide new comment.\n"); fprintf(stderr, " -N phrase Provide new passphrase.\n"); fprintf(stderr, " -P phrase Provide old passphrase.\n"); +#ifdef DNS + fprintf(stderr, " -r hostname Print DNS resource record.\n"); +#endif /* DNS */ #ifdef SMARTCARD fprintf(stderr, " -D reader Download public key from smartcard.\n"); fprintf(stderr, " -U reader Upload private key to smartcard.\n"); @@ -752,6 +789,7 @@ main(int ac, char **av) { char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; char *reader_id = NULL; + char *resource_record_hostname = NULL; Key *private, *public; struct passwd *pw; struct stat st; @@ -778,7 +816,7 @@ main(int ac, char **av) exit(1); } - while ((opt = getopt(ac, av, "deiqpclBRxXyb:f:t:U:D:P:N:C:")) != -1) { + while ((opt = getopt(ac, av, "degiqpclBRxXyb:f:t:U:D:P:N:C:r:")) != -1) { switch (opt) { case 'b': bits = atoi(optarg); @@ -803,6 +841,9 @@ main(int ac, char **av) strlcpy(identity_file, optarg, sizeof(identity_file)); have_identity = 1; break; + case 'g': + print_generic = 1; + break; case 'P': identity_passphrase = optarg; break; @@ -843,6 +884,9 @@ main(int ac, char **av) case 'U': reader_id = optarg; break; + case 'r': + resource_record_hostname = optarg; + break; case '?': default: usage(); @@ -868,6 +912,13 @@ main(int ac, char **av) do_convert_from_ssh2(pw); if (print_public) do_print_public(pw); + if (resource_record_hostname != NULL) { +#ifdef DNS + do_print_resource_record(pw, resource_record_hostname); +#else /* DNS */ + fatal("no DNS support."); +#endif /* DNS */ + } if (reader_id != NULL) { #ifdef SMARTCARD if (download) diff --git a/ssh_config.5 b/ssh_config.5 index 44208b431..2f33aa3f3 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -34,7 +34,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.7 2003/03/28 10:11:43 jmc Exp $ +.\" $OpenBSD: ssh_config.5,v 1.8 2003/05/14 18:16:20 jakob Exp $ .Dd September 25, 1999 .Dt SSH_CONFIG 5 .Os @@ -618,6 +618,11 @@ having to remember to give the user name on the command line. Specifies a file to use for the user host key database instead of .Pa $HOME/.ssh/known_hosts . +.It Cm VerifyHostKeyDNS +Specifies whether to verify the remote key using DNS and SSHFP resource +records. +The default is +.Dq no . .It Cm XAuthLocation Specifies the full pathname of the .Xr xauth 1 diff --git a/sshconnect.c b/sshconnect.c index 33d9c727f..32bef7d07 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -13,7 +13,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect.c,v 1.139 2003/04/14 14:17:50 markus Exp $"); +RCSID("$OpenBSD: sshconnect.c,v 1.140 2003/05/14 18:16:21 jakob Exp $"); #include @@ -33,6 +33,10 @@ RCSID("$OpenBSD: sshconnect.c,v 1.139 2003/04/14 14:17:50 markus Exp $"); #include "misc.h" #include "readpass.h" +#ifdef DNS +#include "dns.h" +#endif + char *client_version_string = NULL; char *server_version_string = NULL; @@ -797,11 +801,28 @@ fail: return -1; } +/* returns 0 if key verifies or -1 if key does NOT verify */ int verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) { struct stat st; +#ifdef DNS + if (options.verify_host_key_dns) { + switch(verify_host_key_dns(host, hostaddr, host_key)) { + case DNS_VERIFY_OK: + return 0; + case DNS_VERIFY_FAILED: + return -1; + case DNS_VERIFY_ERROR: + break; + default: + debug3("bad return value from verify_host_key_dns"); + break; + } + } +#endif /* DNS */ + /* return ok if the key can be found in an old keyfile */ if (stat(options.system_hostfile2, &st) == 0 || stat(options.user_hostfile2, &st) == 0) {