I had a rather hefty argument with a colleague today, regarding the particular merits of DNSSEC as far as the use of SSH fingerprints is concerned. I violently argued that, though it would be nice to have, the feature isn’t yet ready for the general public, because SSH client’s don’t correctly implement it. In 2006, RFC 4255Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints – was published. What this RFC proposes is to store the fingerprint of host SSH keys in the DNS in the form of SSHFP resource records, so that a client, upon connecting to a particular server, can be assured that the fingerprint is valid. Without DNSSEC this feature is … well, not really very useful, right? Anyhow, to prove a point, I told said colleague that whether DNSSEC is in effect or not has no effect whatsoever on the client-side handling. And here is the proof. I have the appropriate SSHFP records in my DNSSEC-secured zone

    $ dig +dnssec ubu.jpmens.org any
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27616
    ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1
    ; EDNS: version: 0, flags: do; udp: 4096
    ;ubu.jpmens.org.			IN	ANY
    ubu.jpmens.org.		120	IN	RRSIG	A 8 3 120 20110303000000 20110217000000 50853 jpmens.org. LfwN0...d Dic=
    ubu.jpmens.org.		120	IN	A
    ubu.jpmens.org.		120	IN	RRSIG	SSHFP 8 3 120 20110303000000 20110217000000 50853 jpmens.org. xk3TCDBt...gew=
    ubu.jpmens.org.		120	IN	SSHFP	1 1 7E7A55CEA3B8E15528665A6781CA7C35190CF0EB
    ubu.jpmens.org.		120	IN	SSHFP	2 1 CC17F14DA60CF38E809FE58B10D0F22680D59D08

The domain has an Address record and two SSHFP records. Now, with all SSH clients I tested on platforms available to me, the following expected result occurred: SSH does a DNS lookup, verifies that it found SSHFP fingerprint records which match the host key of the host I’m connecting to, and informs me that it has, indeed, found the information to be correct. That is it. Bob’s your uncle. Case closed. The client continues as it always has, and asks me whether I want to continue connecting, stores the key in its known_hosts file, yada, yada, yada.

I confirmed the behavior by looking at the OpenSSH source code, and verifying it on a number of platforms (FreeBSD, several Linuxes, and Mac OS X). My colleague however, didn’t believe me, and set up a validating recursor, etc. to prove me wrong. He proved me wrong. Not completely, unfortunately, because of some issues he couldn’t readily identify, but he said it “sometimes” worked for him on Fedora. I was bitten by the bug, and, it turns out Fedora have put in the necessary code, without OpenSSH upstream having done it. That is a bitch. Here goes, on Fedora 14: (what you can’t see in the screen shot is the rm known_hosts I started off with; sorry.) Just what the doctor ordered. Now, this is half a public apology to said colleague (sorry Stefan!), and half a rant that a vendor add such features quite silently. No mention in the man page, hardly a word in the changelogs, etc. I had to dig quite deeply to find it: it’s an interaction between Fedora patches and the Glibc.

Update This topic kept me awake. How does Fedora’s SSH client know that my recursing name server has the the AD (authenticated data bit) on the query? , so I sat down and looked at bit more closely at the source code. Make or break is the getrrsetbyname() function which isn’t documented on any of my Linux machines. The function was written by Jakob Schlyter, and he clearly writes:

if the EDNS0 option is activated in resolv.conf(5), getrrsetbyname() will request DNSSEC authentication using the EDNS0 DNSSEC OK (DO) bit.

Hah! Let me see if that works: It works. At least on an Ubuntu Linux as shown in above screen shot. (Note how I’ve set the resolver option in the environment before invoking ssh.) So what is different between, say, Fedora’s and Ubuntu’s ssh client? Fedora’s code explicitly sets RRSET_FORCE_EDNS0 in the flags before invoking getrrsetbyname which, although it has a flags parameter, is documented as the flags field is currently unused and must be zero. The function is defined (in Fedora’s patched source) in openbsd-compat/getrrsetbyname.c. And the moral of this story: read the bleedin’ source code properly!”.

Update: The RES_OPTIONS trick works on openSUSE 11.3 as well.

Continued in VerifyHostKeyDNS=yessssss!.

SSH, Security, CLI, BSD, and dnssec :: 18 Feb 2011 :: e-mail