summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--Makefile.in8
-rw-r--r--README.dns55
-rw-r--r--dns.c293
-rw-r--r--dns.h57
-rw-r--r--key.c4
-rw-r--r--key.h3
-rw-r--r--readconf.c12
-rw-r--r--readconf.h3
-rw-r--r--ssh-keygen.111
-rw-r--r--ssh-keygen.c55
-rw-r--r--ssh_config.57
-rw-r--r--sshconnect.c23
13 files changed, 523 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 8ad7108a..8feae8b2 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 ba898db4..f25fe0ae 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 00000000..d6889b9a
--- /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 00000000..9b7a0e7a
--- /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 <openssl/bn.h>
+#ifdef LWRES
+#include <lwres/netdb.h>
+#include <dns/result.h>
+#else /* LWRES */
+#include <netdb.h>
+#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 00000000..ba0ea9fb
--- /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 060b6374..d918cfd0 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 <openssl/evp.h>
@@ -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 725c7a04..a7b6afe8 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 acdf128f..c9c463b2 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 d3547211..d141b8c0 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 <ylo@cs.hut.fi>
@@ -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 000e8ff2..613d71a0 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 1d08c7ce..f3ea4f1f 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 <openssl/evp.h>
#include <openssl/pem.h>
@@ -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 44208b43..2f33aa3f 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 33d9c727..32bef7d0 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 <openssl/bn.h>
@@ -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) {