I’ve written before about how the failure of source routing created the need for NAT, but that post didn’t address the basic security problem with source routing that caused ISPs to disable it. It allows a man-in-the-middle attack where a machine can totally fabricate a packet that claims to come from a trusted source. There’s no way that the destination machine can distinguish between such a rogue packet and a genuine packet that the source actually requested be source routed through the fabricating machine. At the time, Internet security was heavily host-based (think rsh), so this loophole became perceived as a fatal security flaw that led to source routing being derogated and abandoned.
A quarter century later, I think we can offer a more balanced perspective. Host-based authentication in general is now viewed as suspect and has largely been abandoned in favor of cryptographic techniques, particularly public-key cryptosystems (think ssh) which didn’t exist when TCP/IP was first designed. We are better able to offer answers to key questions concerning the separation of identity, address, and route. In particular, we are far less willing (at least in principle) to confuse identity with address, if for no other reason than an improved toolset, and thus perhaps better able to judge source routing, not as a fundamental security loophole, but as a design feature that became exploitable only after we began using addresses to establish identity.
Can we “fix” source routing? Perhaps, if we will completely abandon any pretext of address-based authentication. What, then, should replace it? I suggest that we already have our address-less identifiers, and they are DNS names. Furthermore, we already have a basic scheme for attaching cryptographic keys to DNS names (DNSSEC), so can we put all this together and form a largely automated DNS-based authentication system?
The basic idea is that we authenticate using DNS names (not IP addresses), so it stands to reason that a basic operation has to be obtaining a connection’s remote DNS name in a secure manner. Or perhaps I should say, obtain a connection’s remote DNS names, because it will become clear that we want a connection endpoint to have multiple ones. How can we do this? Several options suggest themselves:
- Modify the transport protocols to communicate the endpoint’s DNS names
- Modify the IDENT protocol so that it can identify remote DNS names
- Use IPSEC (somehow)
Let’s assume for the moment that we’ve solved this problem. The remote endpoint claims to be one or more DNS names and has communicated that information to us. Now we need to authenticate it. The obvious way is a DNS lookup on the name to obtain a public key. The remote endpoint now has to back up its claim by using the matching private key in some way. We want the entire session to be authenticated, not just one little piece of it, so “some way” seems to imply signing all the IP packets. We don’t want to use the private key from the DNS lookup to sign all the packets, because that would expose too much information about a fairly long-lived key, so probably something like using the key pair from the DNS lookup to establish some per-session keys and then using them to sign the packets would be more agreeable.
Sounds a lot like IPSEC.
The problem, as always, lies in key management. IPSEC was designed largely to protect host-to-host communications, not application-to-application, and the structure of its key management reflects this. IKEv2, probably the most widely used IPSEC key management protocol, operates using privileged daemons on reserved port numbers (500 and 4500). This seems to preclude the possibility of having multiple distinct identities associated with different processes on the same machine.
So maybe simpler would be better. Just cryptographically sign each packet using an AH header that matches a DNS-published public key. Use DNS’s existing mechanisms to periodically time out that public key and replace it (maybe once an hour), thwarting attempts to take advantage of the exposure of the key to crack it. The need for a transition period seems to dictate that multiple public keys can be associated with a single DNS name, and that signing with one of them is sufficient to establish identity.
Of course, using any kind of public key whose exchange hasn’t been protected by encryption exposes it, so it would seem that this simple scheme is about the best we can do without some kind of handshake exchange. In particular, the simple scheme can naturally support multicast. Also, the use of multiple public keys suggests the possibility of a transition that starts using a “low security” key as described in the previous paragraph, then transitions to a “high security” key that is used to authenticate and encrypt the exchange of a session key.
In short, while IPSEC’s basic packet headers seem quite suitable for this purpose, its key management scheme seems cumbersome and overblown. I’ll use the simplified variant just described for the rest of this essay.
Returning now to the question of communicating DNS identity, it doesn’t matter (all that much) since any such communication isn’t trusted until it is verified. Probably the most theoretically rigorous approach would be to specify the DNS name as part of the IP packet header, so your packet headers now contain the source location (IP address), source identifier (DNS name), and authentication (AH header). Other, less elegant but more palatable approaches (from an efficiency standpoint), would be to use an enhanced IDENT protocol, or an extra TCP header during TCP initialization, or some other out-of-band mechanism.
In short, our identifiers are DNS names. We have signed public keys in DNS. Hosts or applications possessing the matching private keys are able to act as the entity named by the matching DNS name. The application uses some kind of API to install a private key that will be used to sign all of its packets. It also announces its DNS name (in IP header options, or TCP header options, or via an IDENT daemon). All of this is enough to allow the recipient to validate the identity of its remote peer, independent of its IP address.
Package all of this up into a library, and authentication takes a relatively simple form – applications need only to check the remote DNS name against a list of valid names. Regular expressions seem a natural choice. The kind of DNS wildcard syntax used by NFS exports files or X Windows xauth now look a lot more attractive as a generic authentication framework.
Since DNS is hierarchal, fine-grained authentication seems to fit naturally into this model. For example, I don’t want my login sessions authenticated on a per-host basis, but rather on a per-user basis. This can be accomodated simply by adding DNS names for individual users. Rather than having a single DNS name for a single host, a host can manage an entire zone underneath its name. Each machine can run its own DNS server and manage its own DNS space, so “bigsky.freesoft.org” can create a DNS name “baccala.bigsky.freesoft.org” for one of its users. Attach a public key to that DNS name, and remote applications can specify “baccala.bigsky.freesoft.org” in their authentication list to allow access to this user account. Since DNS is not tied specifically to any one naming scheme, organizations have a great deal of flexibility in managing their zones. Specifically, the same authentication scheme, unmodified, could also be used to create a single domain-wide user account “baccala.freesoft.org”, or “baccala.users.freesoft.org”, and it would all be transparent to remote applications, so long as local applications possessed the correct matching private keys.
Applications would naturally possess multiple identities – one for the user account they run under, one for the machine they run on, maybe other for user groups or workstation clusters. How does an application know which one to use? This could be negotiated using some kind of key exchange protocol, but that wouldn’t be realistic in a multicast environment. So probably the best thing is to allow multiple AH headers to stamp multiple identities on a single packet. Then, it’s up to the application to decide which set of identities to use for a particular transaction, subject always of course to the proviso that it must possess the correct set of private keys. And this implies that a remote entity may have multiple identities, any one of which, presumably, can be used for an authentication process.
More can be done. If we want to encrypt without pre-shared keys, a similar scheme can be used – obtain a public key from DNS and encrypt packets so that only the matching private key can decrypt. Again, when you think about a multicast environment, it starts to look a lot more attractive that running a key exchange protocol between every pair of hosts.
But I’ll stop here. My point is this: we’re now able to assemble an authentication scheme based on cryptographically secure DNS identifiers, and we’ve have enough problems with address-based authentication that we should. If we finally ditch this idea that you can convert an IP address into a DNS name, and instead insist that that translation only really works in the other direction, we can overcome the real design flaw that doomed source routing.