summaryrefslogtreecommitdiff
path: root/doc/oppimpl.txt
diff options
context:
space:
mode:
Diffstat (limited to 'doc/oppimpl.txt')
-rw-r--r--doc/oppimpl.txt514
1 files changed, 0 insertions, 514 deletions
diff --git a/doc/oppimpl.txt b/doc/oppimpl.txt
deleted file mode 100644
index fe4527d4e..000000000
--- a/doc/oppimpl.txt
+++ /dev/null
@@ -1,514 +0,0 @@
-Implementing Opportunistic Encryption
-
-Henry Spencer & D. Hugh Redelmeier
-
-Version 4+, 15 Dec 2000
-
-
-
-Updates
-
-Major changes since last version: "Negotiation Issues" section discussing
-some interoperability matters, plus some wording cleanup. Some issues
-arising from discussions at OLS are not yet resolved, so there will almost
-certainly be another version soon.
-
-xxx incoming could be opportunistic or RW. xxx any way of saving unaware
-implementations??? xxx compression needs mention.
-
-
-
-Introduction
-
-A major long-term goal of the FreeS/WAN project is opportunistic
-encryption: a security gateway intercepts an outgoing packet aimed at a
-new remote host, and quickly attempts to negotiate an IPsec tunnel to that
-host's security gateway, so that traffic can be encrypted and
-authenticated without changes to the host software. (This generalizes
-trivially to the end-to-end case where host and security gateway are one
-and the same.) If the attempt fails, the packet (or a retry thereof)
-passes through in clear or is dropped, depending on local policy.
-Prearranged tunnels bypass all this, so static VPNs can coexist with
-opportunistic encryption.
-
-xxx here Although significant intelligence about all this is necessary at the
-initiator end, it's highly desirable for little or no special machinery
-to be needed at the responder end. In particular, if none were needed,
-then a security gateway which knows nothing about opportunistic encryption
-could nevertheless participate in some opportunistic connections.
-
-IPSEC gives us the low-level mechanisms, and the key-exchange machinery,
-but there are some vague spots (to put it mildly) at higher levels.
-
-One constraint which deserves comment is that the process of tunnel setup
-should be quick. Moreover, the decision that no tunnel can be created
-should also be quick, since that will be a common case, at least in the
-beginning. People will be reluctant to use opportunistic encryption if it
-causes gross startup delays on every connection, even connections which see
-no benefit from it. Win or lose, the process must be rapid.
-
-There's nothing much we can do to speed up the key exchange itself. (The
-one thing which conceivably might be done is to use Aggressive Mode, which
-involves fewer round trips, but it has limitations and possible security
-problems, and we're reluctant to touch it.) What we can do, is to make the
-other parts of the setup process as quick as possible. This desire will
-come back to haunt us below. :-)
-
-A further note is that we must consider the processing at the responder
-end as well as the initiator end.
-
-Several pieces of new machinery are needed to make this work. Here's a
-brief list, with details considered below.
-
-+ Outgoing Packet Interception. KLIPS needs to intercept packets which
-likely would benefit from tunnel setup, and bring them to Pluto's
-attention. There needs to be enough memory in the process that the same
-tunnel doesn't get proposed too often (win or lose).
-
-+ Smart Connection Management. Not only do we need to establish tunnels
-on request, once a tunnel is set up, it needs to be torn down eventually
-if it's not in use. It's also highly desirable to detect the fact that it
-has stopped working, and do something useful. Status changes should be
-coordinated between the two security gateways unless one has crashed,
-and even then, they should get back into sync eventually.
-
-+ Security Gateway Discovery. Given a packet destination, we must decide
-who to attempt to negotiate a tunnel with. This must be done quickly, win
-or lose, and reliably even in the presence of diverse network setups.
-
-+ Authentication Without Prearrangement. We need to be sure we're really
-talking to the intended security gateway, without being able to prearrange
-any shared information. He needs the same assurance about us.
-
-+ More Flexible Policy. In particular, the responding Pluto needs a way
-to figure out whether the connection it is being asked to make is okay.
-This isn't as simple as just searching our existing conn database -- we
-probably have to specify *classes* of legitimate connections.
-
-Conveniently, we have a three-letter acronym for each of these. :-)
-
-Note on philosophy: we have deliberately avoided providing six different
-ways to do each step, in favor of specifying one good one. Choices are
-provided only when they appear to be necessary. (Or when we are not yet
-quite sure yet how best to do something...)
-
-
-
-OPI, SCM
-
-Smart Connection Management would be quite useful even by itself,
-requiring manual triggering. (Right now, we do the manual triggering, but
-not the other parts of SCM.) Outgoing Packet Interception fits together
-with SCM quite well, and improves its usefulness further. Going through a
-connection's life cycle from the start...
-
-OPI itself is relatively straightforward, aside from the nagging question
-of whether the intercepted packet is put on hold and then released, or
-dropped. Putting it on hold is preferable; the alternative is to rely on
-the application or the transport layer re-trying. The downside of packet
-hold is extra resources; the downside of packet dropping is that IPSEC
-knows *when* the packet can finally go out, and the higher layers don't.
-Either way, life gets a little tricky because a quickly-retrying
-application may try more than once before we know for sure whether a
-tunnel can be set up, and something has to detect and filter out the
-duplications. Some ARP implementations use the approach of keeping one
-packet for an as-yet-unresolved address, and throwing away any more that
-appear; that seems a reasonable choice.
-
-(Is it worth intercepting *incoming* packets, from the outside world, and
-attempting tunnel setup based on them? Perhaps... if, and only if, we
-organize AWP so that non-opportunistic SGs can do it somehow. Otherwise,
-if the other end has not initiated tunnel setup itself, it will not be
-prepared to do so at our request.)
-
-Once a tunnel is up, packets going into it naturally are not intercepted
-by OPI. However, we need to do something about the flip side of this too:
-after deciding that we *cannot* set up a tunnel, either because we don't
-have enough information or because the other security gateway is
-uncooperative, we have to remember that for a while, so we don't keep
-knocking on the same locked door. One plausible way of doing that is to
-set up a bypass "tunnel" -- the equivalent of our current %passthrough
-connection -- and have it managed like a real SCM tunnel (finite lifespan
-etc.). This sounds a bit heavyweight, but in practice, the alternatives
-all end up doing something very similar when examined closely. Note that
-we need an extra variant of this, a block rather than a bypass, to cover
-the case where local policy dictates that packets *not* be passed through;
-we still have to remember the fact that we can't set up a real tunnel.
-
-When to tear tunnels down is a bit problematic, but if we're setting up a
-potentially unbounded number of them, we have to tear them down *somehow*
-*sometime*. It seems fairly obvious that we set a tentative lifespan,
-probably fairly short (say 1min), and when it expires, we look to see if
-the tunnel is still in use (say, has had traffic in the last half of the
-lifespan). If so, we assign it a somewhat longer lifespan (say 10min),
-after which we look again. If not, we close it down. (This lifespan is
-independent of key lifetime; it is just the time when the tunnel's future
-is next considered. This should happen reasonably frequently, unlike
-rekeying, which is costly and shouldn't be too frequent.) Multi-step
-backoff algorithms probably are not worth the trouble; looking every
-10min doesn't seem onerous.
-
-For the tunnel-expiry decision, we need to know how long it has been since
-the last traffic went through. A more detailed history of the traffic
-does not seem very useful; a simple idle timer (or last-traffic timestamp)
-is both necessary and sufficient. And KLIPS already has this.
-
-As noted, default initial lifespan should be short. However, Pluto should
-keep a history of recently-closed tunnels, to detect cases where a tunnel
-is being repeatedly re-established and should be given a longer lifespan.
-(Not only is tunnel setup costly, but it adds user-visible delay, so
-keeping a tunnel alive is preferable if we have reason to suspect more
-traffic soon.) Any tunnel re-established within 10min of dying should have
-10min added to its initial lifespan. (Just leaving all tunnels open longer
-is unappealing -- adaptive lifetimes which are sensitive to the behavior
-of a particular tunnel are wanted. Tunnels are relatively cheap entities
-for us, but that is not necessarily true of all implementations, and there
-may also be administrative problems in sorting through large accumulations
-of idle tunnels.)
-
-It might be desirable to have detailed information about the initial
-packet when determining lifespans. HTTP connections in particular are
-notoriously bursty and repetitive.
-
-Arguably it would be nice to monitor TCP connection status. A still-open
-TCP connection is almost a guarantee that more traffic is coming, while
-the closing of the only TCP connection through a tunnel is a good hint
-that none is. But the monitoring is complex, and it doesn't seem worth
-the trouble.
-
-IKE connections likewise should be torn down when it appears the need has
-passed. They should linger longer than the last tunnel they administer,
-just in case they are needed again; the cost of retaining them is low. An
-SG with only a modest number of them open might want to simply retain each
-until rekeying time, with more aggressive management cutting in only when
-the number gets large. (They should be torn down eventually, if only to
-minimize the length of a status report, but rekeying is the only expensive
-event for them.)
-
-It's worth remembering that tunnels sometimes go down because the other
-end crashes, or disconnects, or has a network link break, and we don't get
-any notice of this in the general case. (Even in the event of a crash and
-successful reboot, we won't hear about it unless the other end has
-specific reason to talk IKE to us immediately.) Of course, we have to
-guard against being too quick to respond to temporary network outages,
-but it's not quite the same issue for us as for TCP, because we can tear
-down and then re-establish a tunnel without any user-visible effect except
-a pause in traffic. And if the other end does go down and come back up,
-we and it can't communicate *at all* (except via IKE) until we tear down
-our tunnel.
-
-So... we need some kind of heartbeat mechanism. Currently there is none
-in IKE, but there is discussion of changing that, and this seems like the
-best approach. Doing a heartbeat at the IP level will not tell us about a
-crash/reboot event, and sending heartbeat packets through tunnels has
-various complications (they should stop at the far mouth of the tunnel
-instead of going on to a subnet; they should not count against idle
-timers; etc.). Heartbeat exchanges obviously should be done only when
-there are tunnels established *and* there has been no recent incoming
-traffic through them. It seems reasonable to do them at lifespan ends,
-subject to appropriate rate limiting when more than one tunnel goes to the
-same other SG. When all traffic between the two ends is supposed to go
-via the tunnel, it might be reasonable to do a heartbeat -- subject to a
-rate limiter to avoid DOS attacks -- if the kernel sees a non-tunnel
-non-IKE packet from the other end.
-
-If a heartbeat gets no response, try a few (say 3) pings to check IP
-connectivity; if one comes back, try another heartbeat; if it gets no
-response, the other end has rebooted, or otherwise been re-initialized,
-and its tunnels should be torn down. If there's no response to the pings,
-note the fact and try the sequence again at the next lifespan end; if
-there's nothing then either, declare the tunnels dead.
-
-Finally... except in cases where we've decided that the other end is dead
-or has rebooted, tunnel teardown should always be coordinated with the
-other end. This means interpreting and sending Delete notifications, and
-also Initial-Contacts. Receiving a Delete for the other party's tunnel
-SAs should lead us to tear down our end too -- SAs (SA bundles, really)
-need to be considered as paired bidirectional entities, even though the
-low-level protocols don't think of them that way.
-
-
-
-SGD, AWP
-
-Given a packet destination, how do we decide who to (attempt to) negotiate
-a tunnel with? And as a related issue, how do the negotiating parties
-authenticate each other? DNSSEC obviously provides the tools for the
-latter, but how exactly do we use them?
-
-Having intercepted a packet, what we know is basically the IP addresses of
-source and destination (plus, in principle, some information about the
-desired communication, like protocol and port). We might be able to map
-the source address to more information about the source, depending on how
-well we control our local networks, but we know nothing further about the
-destination.
-
-The obvious first thing to do is a DNS reverse lookup on the destination
-address; that's about all we can do with available data. Ideally, we'd
-like to get all necessary information with this one DNS lookup, because
-DNS lookups are time-consuming -- all the more so if they involve a DNSSEC
-signature-checking treewalk by the name server -- and we've got to hurry.
-While it is unusual for a reverse lookup to yield records other than PTR
-records (or possibly CNAME records, for RFC 2317 classless delegation),
-there's no reason why it can't.
-
-(For purposes like logging, a reverse lookup is usually followed by a
-forward lookup, to verify that the reverse lookup wasn't lying about the
-host name. For our purposes, this is not vital, since we use stronger
-authentication methods anyway.)
-
-While we want to get as much data as possible (ideally all of it) from one
-lookup, it is useful to first consider how the necessary information would
-be obtained if DNS lookups were instantaneous. Two pieces of information
-are absolutely vital at this point: the IP address of the other end's
-security gateway, and the SG's public key*.
-
-(* Actually, knowledge of the key can be postponed slightly -- it's not
-needed until the second exchange of the negotiations, while we can't even
-start negotiations without knowing the IP address. The SG is not
-necessarily on the plain-IP route to the destination, especially when
-multiple SGs are present.)
-
-Given instantaneous DNS lookups, we would:
-
-+ Start with a reverse lookup to turn the address into a name.
-
-+ Look for something like RFC-2782 SRV records using the name, to find out
-who provides this particular service. If none comes back, we can abandon
-the whole process.
-
-+ Select one SRV record, which gives us the name of a target host (plus
-possibly one or more addresses, if the name server has supplied address
-records as Additional Data for the SRV records -- this is recommended
-behavior but is not required).
-
-+ Use the target name to look up a suitable KEY record, and also address
-record(s) if they are still needed.
-
-This gives us the desired address(es) and key. However, it requires three
-lookups, and we don't even find out whether there's any point in trying
-until after the second.
-
-With real DNS lookups, which are far from instantaneous, some optimization
-is needed. At the very least, typical cases should need fewer lookups.
-
-So when we do the reverse lookup on the IP address, instead of asking for
-PTR, we ask for TXT. If we get none, we abandon opportunistic
-negotiation, and set up a bypass/block with a relatively long life (say
-6hr) because it's not worth trying again soon. (Note, there needs to be a
-way to manually force an early retry -- say, by just clearing out all
-memory of a particular address -- to cover cases where a configuration
-error is discovered and fixed.)
-
-xxx need to discuss multi-string TXTs
-
-In the results, we look for at least one TXT record with content
-"X-IPsec-Server(nnn)=a.b.c.d kkk", following RFC 1464 attribute/value
-notation. (The "X-" indicates that this is tentative and experimental;
-this design will probably need modification after initial experiments.)
-Again, if there is no such record, we abandon opportunistic negotiation.
-
-"nnn" and the parentheses surrounding it are optional. If present, it
-specifies a priority (low number high priority), as for MX records, to
-control the order in which multiple servers are tried. If there are no
-priorities, or there are ties, pick one randomly.
-
-"a.b.c.d" is the dotted-decimal IP address of the SG. (Suitable extensions
-for IPv6, when the time comes, are straightforward.)
-
-"kkk" is either an RSA-MD5 public key in base-64 notation, as in the text
-form of an RFC 2535 KEY record, or "@hhh". In the latter case, hhh is a
-DNS name, under which one Host/Authentication/IPSEC/RSA-MD5 KEY record is
-present, giving the server's authentication key. (The delay of the extra
-lookup is undesirable, but practical issues of key management may make it
-advisable not to duplicate the key itself in DNS entries for many
-clients.)
-
-It unfortunately does appear that the authentication key has to be
-associated with the server, not the client behind it. At the time when
-the responder has to authenticate our SG, it does not know which of its
-clients we are interested in (i.e., which key to use), and there is no
-good way to tell it. (There are some bad ways; this decision may merit
-re-examination after experimental use.)
-
-The responder authenticates our SG by doing a reverse lookup on its IP
-address to get a Host/Authentication/IPSEC/RSA-MD5 KEY record. He can
-attempt this in parallel with the early parts of the negotiation (since he
-knows our SG IP address from the first negotiation packet), at the risk of
-having to abandon the attempt and do a different lookup if we use
-something different as our ID (see below). Unfortunately, he doesn't yet
-know what client we will claim to represent, so he'll need to do another
-lookup as part of phase 2 negotiation (unless the client *is* our SG), to
-confirm that the client has a TXT X-IPsec-Server record pointing to our
-SG. (Checking that the record specifies the same key is not important,
-since the responder already has a trustworthy key for our SG.)
-
-Also unfortunately, opportunistic tunnels can only have degenerate subnets
-(/32 subnets, containing one host) at their ends. It's superficially
-attractive to negotiate broader connections... but without prearrangement,
-you don't know whether you can trust the other end's claim to have a
-specific subnet behind it. Fixing this would require a way to do a
-reverse lookup on the *subnet* (you cannot trust information in DNS
-records for a name or a single address, which may be controlled by people
-who do not control the whole subnet) with both the address and the mask
-included in the name. Except in the special case of a subnet masked on a
-byte boundary (in which case RFC 1035's convention of an incomplete
-in-addr.arpa name could be used), this would need extensions to the
-reverse-map name space, which is awkward, especially in the presence of
-RFC 2317 delegation. (IPv6 delegation is more flexible and it might be
-easier there.)
-
-There is a question of what ID should be used in later steps of
-negotiation. However, the desire not to put more DNS lookups in the
-critical path suggests avoiding the extra complication of varied IDs,
-except in the Road Warrior case (where an extra lookup is inevitable).
-Also, figuring out what such IDs *mean* gets messy. To keep things simple,
-except in the RW case, all IDs should be IP addresses identical to those
-used in the packet headers.
-
-For Road Warrior, the RW must be the initiator, since the home-base SG has
-no idea what address the RW will appear at. Moreover, in general the RW
-does not control the DNS entries for his address. This inherently denies
-the home base any authentication of the RW's IP address; the most it can
-do is to verify an identity he provides, and perhaps decide whether it
-wishes to talk to someone with that identity, but this does not verify his
-right to use that IP address -- nothing can, really.
-
-(That may sound like it would permit some man-in-the-middle attacks, but
-the RW can still do full authentication of the home base, so a man in the
-middle cannot successfully impersonate home base. Furthermore, a man in
-the middle must impersonate both sides for the DH exchange to work. So
-either way, the IKE negotiation falls apart.)
-
-A Road Warrior provides an FQDN ID, used for a forward lookup to obtain a
-Host/Authentication/IPSEC/RSA-MD5 KEY record. (Note, an FQDN need not
-actually correspond to a host -- e.g., the DNS data for it need not
-include an A record.) This suffices, since the RW is the initiator and
-the responder knows his address from his first packet.
-
-Certain situations where a host has a more-or-less permanent IP address,
-but does not control its DNS entries, must be treated essentially like
-Road Warrior. It is unfortunate that DNS's old inverse-query feature
-cannot be used (nonrecursively) to ask the initiator's local DNS server
-whether it has a name for the address, because the address will almost
-always have been obtained from a DNS name lookup, and it might be a lookup
-of a name whose DNS entries the host *does* control. (Real examples of
-this exist: the host has a preferred name whose host-controlled entry
-includes an A record, but a reverse lookup on the address sends you to an
-ISP-controlled name whose entry has an A record but not much else.) Alas,
-inverse query is long obsolete and is not widely implemented now.
-
-There are some questions in failure cases. If we cannot acquire the info
-needed to set up a tunnel, this is the no-tunnel-possible case. If we
-reach an SG but negotiation fails, this too is the no-tunnel-possible
-case, with a relatively long bypass/block lifespan (say 1hr) since
-fruitless negotiations are expensive. (In the multiple-SG case, it seems
-unlikely to be worthwhile to try other SGs just in case one of them might
-have a configuration permitting successful negotiation.)
-
-Finally, there is a sticky problem with timeouts. If the other SG is down
-or otherwise inaccessible, in the worst case we won't hear about this
-except by not getting responses. Some other, more pathological or even
-evil, failure cases can have the same result. The problem is that in the
-case where a bypass is permitted, we want to decide whether a tunnel is
-possible quickly. It gets even worse if there are multiple SGs, in which
-case conceivably we might want to try them all (since some SGs being up
-when others are down is much more likely than SGs differing in policy).
-
-The patience setting needs to be configurable policy, with a reasonable
-default (to be determined by experiment). If it expires, we simply have
-to declare the attempt a failure, and set up a bypass/block. (Setting up
-a tentative bypass/block, and replacing it with a real tunnel if remaining
-attempts do produce one, looks attractive at first glance... but exposing
-the first few seconds of a connection is often almost as bad as exposing
-the whole thing!) Such a bypass/block should have a short lifespan, say
-10min, because the SG(s) might be only temporarily unavailable.
-
-The flip side of IKE waiting for a timeout is that all other forms of
-feedback, e.g. "host not reachable", should be *ignored*, because you
-cannot trust them! This may need kernel changes.
-
-Can AWP be done by non-opportunistic SGs? Probably not; existing SG
-implementations generally aren't prepared to do anything suitable, except
-perhaps via the messy business of certificates. There is one borderline
-exception: some implementations rely on LDAP for at least some of their
-information fetching, and it might be possible to substitute a custom LDAP
-server which does the right things for them. Feasibility of this depends
-on details, which we don't know well enough.
-
-[This could do with a full example, a complete packet by packet walkthrough
-including all DNS and IKE traffic.]
-
-
-
-MFP
-
-Our current conn database simply isn't flexible enough to cover all this
-properly. In particular, the responding Pluto needs a way to figure out
-whether the connection it is being asked to make is legitimate.
-
-This is more subtle than it sounds, given the problem noted earlier, that
-there's no clear way to authenticate claims to represent a non-degenerate
-subnet. Our database has to be able to say "a connection to any host in
-this subnet is okay" or "a connection to any subnet within this subnet is
-okay", rather than "a connection to exactly this subnet is okay". (There
-is some analogy to the Road Warrior case here, which may be relevant.)
-This will require at least a re-interpretation of ipsec.conf.
-
-Interim stages of implementation of this will require a bit of thought.
-Notably, we need some way of dealing with the lack of fully signed DNSSEC
-records. Without user interaction, probably the best we can do is to
-remember the results of old fetches, compare them to the results of new
-fetches, and complain and disbelieve all of it if there's a mismatch.
-This does mean that somebody who gets fake data into our very first fetch
-will fool us, at least for a while, but that seems an acceptable tradeoff.
-
-
-
-Negotiation Issues
-
-There are various options which are nominally open to negotiation as part
-of setup, but which have to be nailed down at least well enough that
-opportunistic SGs can reliably interoperate. Somewhat arbitrarily and
-tentatively, opportunistic SGs must support Main Mode, Oakley group 5 for
-D-H, 3DES encryption and MD5 authentication for both ISAKMP and IPsec SAs,
-RSA digital-signature authentication with keys between 2048 and 8192 bits,
-and ESP doing both encryption and authentication. They must do key PFS
-in Quick Mode, but not identity PFS.
-
-
-
-What we need from DNS
-
-Fortunately, we don't need any new record types or suchlike to make this
-all work. We do, however, need attention to a couple of areas in DNS
-implementation.
-
-First, size limits. Although the information we directly need from a
-lookup is not enormous -- the only potentially-big item is the KEY record,
-and there should be only one of those -- there is still a problem with
-DNSSEC authentication signatures. With a 2048-bit key and assorted
-supporting information, we will fill most of a 512-byte DNS UDP packet...
-and if the data is to have DNSSEC authentication, at least one quite large
-SIG record will come too. Plus maybe a TSIG signature on the whole
-response, to authenticate it to our resolver. So: DNSSEC-capable name
-servers must fix the 512-byte UDP limit. We're told there are provisions
-for this; implementation of them is mandatory.
-
-Second, interface. It is unclear how the resolver interface will let us
-ask for DNSSEC authentication. We would prefer to ask for "authentication
-where possible", and get back the data with each item flagged by whether
-authentication was available (and successful!) or not available. Having
-to ask separately for authenticated and non-authenticated data would
-probably be acceptable, *provided* both will be cached on the first
-request, so the two requests incur only one set of (non-local) network
-traffic. Either way, we want to see the name server and resolver do this
-for us; that makes sense in any case, since it's important that
-verification be done somewhere where it can be cached, the more centrally
-the better.
-
-Finally, a wistful note: the ability to do a limited form of inverse
-queries (an almost forgotten feature), to ask the local name server which
-hostname it recently mapped to a particular address, would be quite
-helpful. Note, this is *NOT* the same as a reverse lookup, and crude
-fakes like putting a dotted-decimal address in brackets do not suffice.