Friday May 29, 2009
End-to-end... and everything in betweenDan McDonald's Sun blog, covering IPsec, general networking goodness, and other stuff too. Hello again. Pardon any latency. This whole Oracle thing has been a bit distracting. Never mind figuring out the hard way what limitations there are on racoon2 and what to do about them. Anyway, Solaris 10 Update 7 (aka. 5/09) is now out. It contains a few new IPsec features that have been in OpenSolaris for a bit. They include:
How to tell when a performance project succeeds? The Volo project is an effort to improve the interface between sockets and any socket-driven subsystems, including the TCP/IP stack. During their testing, they panicked during some IPsec tests. See this bug for what they reported. Addendum - IKEv2 does not have this problem because its equivalent to v1's Quick Mode is a simpler request/response exchange, so the responder is ready to receive when it sends the response back to the initiator. (2008-12-09 08:42:22.0) Permalink Comments [0] Racoon2 on OpenSolaris - first tiny steps NOTE: A version of this was sent to the racoon2-users alias also. I've been spending some of my time bringing up racoon2 (an IKEv2 and IKEv1 daemon) on OpenSolaris. Because of vast differences in PF_KEY implementations between OpenSolaris and other OS kernels, I've spent my racoon2 time actually getting IKEv1 to work first, instead of IKEv2. Right now, what's working is:
That's it! IKEv1 responder needs work, as does all of IKEv2, as does work for multiple-choice of algorithms. But there's enough change in there to say something now.
ARCHITECTURAL DIFFERENCESThe most noteworthy change in the OpenSolaris work so far is that literally there's no spmd (a separate IPsec SPD daemon racoon2 uses) required for now. This is because:
If spmd serves another purpose, we will revisit it. As it stands, however, I cannot see us using it. CODE DIFFERENCESIn OpenSolaris, we use the "webrev" tool to generate easy-to-review web pages with diffs of all varieties. The webrev for what I have so far in racoon2 is available at: http://cr.opensolaris.org/~danmcd/racoon2-opensolaris/ Feel free to make comments or suggestions about what I've done. (2008-08-15 13:20:39.0) Permalink Comments [1]
Detangling IPsec NAT-Traversal, and a more stable API
And on a related note, this will be mentioned during Nicolas Droux's OpenSolaris Networking for Developers talk next week at Sun Tech Days in Boston. I'll be there too, talking about S10 and OpenSolaris security features, as well as being in the audience for Nicolas's talk. (2007-09-04 10:28:53.0) Permalink Comments [0]
Not using "ncp" on Niagara considered harmful One of our IPsec remote-access servers here is on a Niagara-powered T2000 server. It's really overkill for the job, but we get to see how IKE and the Niagara crypto accelerator (known as "ncp" by its driver name) interact.
#!/usr/sbin/dtrace -s
/*
* Responder-side Phase I setup.
*/
pid$1::ssh_policy_new_connection:entry
{
self->negstart[arg0] = timestamp;
printf("Initial packet received, pm_info = %p", arg0);
}
pid$1::ssh_policy_negotiation_done_isakmp:entry
{
/* Use 16384 value for "CONNECTED" from isakmp_doi.h */
printf("return %d - %s ", arg1,
(arg1 == 16384) ? "Success" : "Error case.");
printf("pm_info %p finished, took %d ns", arg0,
timestamp - self->negstart[arg0]);
}
With the fix for 6339802 in place, we can get pretty good phase 1 times....
dtrace: script '/space/responder-phase1.d' matched 4 probes CPU ID FUNCTION:NAME 4 48040 ssh_policy_new_connection:entry Initial packet received, pm_info = bed6c8 4 48042 ssh_policy_negotiation_done_isakmp:entry return 16384 - Success pm_info bed6c8 finished, took 165512764 nsThat's 165msec. Some of that time is packet round-trips, but let's ignore that now for this exercise. Now let's do someting drastic: cryptoadm disable provider=ncp/0 all. Suddenly those seconds come back... 4 48040 ssh_policy_new_connection:entry Initial packet received, pm_info = bed6c8 4 48042 ssh_policy_negotiation_done_isakmp:entry return 16384 - Success pm_info bed6c8 finished, took 7732419300 nsWOW! Like Darren said, that's blog-worthy, and that's why I'm here. So why is Niagara so slow without its crypto accelerator?That's a good question. Keep in mind that four big-number operations occur in an IKE Phase I exchange like I measured above: one RSA Signature, one RSA Verification, one Diffie-Hellman generate, and one Diffie-Hellman agree. I'm not 100% sure, but I believe the default software implementation of big-number operations on SPARC uses floating-point tricks to help out. Using floating-point on a Niagara kicks in a software emulation, which would definitely increase the time taken for each bignum operation.So the moral of the story is to make sure you're exploiting all of the hardware that's available to you! (2007-07-16 11:22:13.0) Permalink Comments [0]
Tunnel Reform Code Review starts now. Hey everyone!
Highlights for OpenSolaris-hackers include:
Share your comments on tref-discuss, and let us know what you think! This entry brought to you by the Technorati tags IPsec, Solaris, and OpenSolaris. (2006-10-09 12:51:53.0) Permalink Comments [0]
Tunnel Reform now open for your perusal IPsec in Solaris has one missing piece, and we're about to put it in place. This entry brought to you by the Technorati tags IPsec, Solaris, and OpenSolaris. (2006-07-13 16:22:45.0) Permalink Comments [0]
ESP without authentication considered harmful Hopefully you will read this and go "That's obvious". I'm writing this entry, however, for those who don't.
Also in Solaris 10 01/06 (aka. Update 1) Solaris 10 Update 1 has shipped. There are nifty things like new-boot in there, but here's a small, subtle, but perhaps blog-worthy entry.
Put IPsec to work in YOUR application Hello coders! Most people know that you can use ipsecconf(1m) to apply IPsec policy enforcement to an existing application. For example, if you wish to only allow inbound telnet traffic that's under IPsec protection, you'd put something like this into /etc/inet/ipsecinit.conf or other ipsecconf(1m) input:
# Inbound telnet traffic should be IPsec protected
{ lport 23 } ipsec { encr_algs any(128..) encr_auth_algs md5 sa shared}
or ipsec { encr_algs any(128..) encr_auth_algs sha1 sa shared}
Combine that with appropriate IKE configuration or manual IPsec keys, and you can secure your telnet traffic against eavesdropping, connection hijacking, etc. For existing services, using ipsecconf(1m) is the most expedient way to bring IPsec protection to bear on packets. For new services, or services that are being modified anyway, consider using per-socket policy as an alternative. Some advantages to per-socket policy are:
The newly SMF-ized inetd(1m) would be a prime candidate for per-socket policy. See RFE 6226853, and this might be something someone in the OpenSolaris community would like to tackle! Let's look at the ipsec_req_t structure that's been around since Solaris 8 in /usr/include/netinet/in.h:
/*
* Different preferences that can be requested from IPSEC protocols.
*/
#define IP_SEC_OPT 0x22 /* Used to set IPSEC options */
#define IPSEC_PREF_NEVER 0x01
#define IPSEC_PREF_REQUIRED 0x02
#define IPSEC_PREF_UNIQUE 0x04
/*
* This can be used with the setsockopt() call to set per socket security
* options. When the application uses per-socket API, we will reflect
* the request on both outbound and inbound packets.
*/
typedef struct ipsec_req {
uint_t ipsr_ah_req; /* AH request */
uint_t ipsr_esp_req; /* ESP request */
uint_t ipsr_self_encap_req; /* Self-Encap request */
uint8_t ipsr_auth_alg; /* Auth algs for AH */
uint8_t ipsr_esp_alg; /* Encr algs for ESP */
uint8_t ipsr_esp_auth_alg; /* Auth algs for ESP */
} ipsec_req_t;
The ipsec_req_t is a subset of what one can specify with ipsecconf(1m) in Solaris 9 or later, but it matched what one could do with Solaris 8's version. Algorithm values are derived from PF_KEY (see /usr/include/net/pfkeyv2.h for values), as below. One could also use getipsecalgbyname(3nsl). If I wish to set a socket to use ESP with AES and and MD5, I'd set it up as follows:
int s; /* Socket file descriptor... */
ipsec_req_t ipsr;
.....
/* NOTE: Do this BEFORE calling connect() or accept() for TCP sockets. */
ipsr.ipsr_ah_req = 0;
ipsr.ipsr_esp_req = IPSEC_PREF_REQUIRED;
ipsr.ipsr_self_encap_req = 0;
ipsr.ipsr_auth_alg = 0;
ipsr.ipsr_esp_alg = SADB_EALG_AES;
ipsr.ipsr_esp_auth_alg = SADB_AALG_MD5HMAC;
if (setsockopt(s, IPPROTO_IP, IP_SEC_OPT, &ipsr, sizeof (ipsr)) == -1) {
perror("setsockopt");
bail(); /* Ugggh, we failed. */
}
/* You now have per-socket policy set. */
Notice I mentioned setting the socket option BEFORE calling connect() or accept? This is because of a phenomenon we implement called connection latching. Basically, connection latching means that once an endpoint is connect()-ed, the IPsec policy (whether set per-socket or inherited from the state of the global SPD at the time) latches in place. We made this decision to avoid keeping policy-per-datagram state for things like TCP retransmits.
One thing per-socket policy does not address is the case of unconnected datagram services. In a perfect world, we could have IPsec policy information percolate all the way to the socket layer, where an application can make fully-informed per-datagram decisions on whether or not a particular packet was secured or not. It's a hard problem, requiring XNET sockets (to use sendmsg() and recvmsg() with ancillary data). BTW, if you want to bypass whatever global entries are in the SPD, you can zero out the structure, and set all three (ah, esp, self_encap) action indicators to IPSEC_PREF_NEVER. You need to be privileged (root or "sys_net_config") to use per-socket bypass, however. So modulo the keying problem (setting up IKE or having both ends agree on IPsec manual keys), you can put IPsec to work right in your application. In fact, if you use IKE, you can let IKE sort out permissions and access control (by using PKI-issued certificates, self-signed certificates, or preshared keys) and have policy merely determine the details of the protection required. EDITED: This entry brought to you by the Technorati tags IPsec, Solaris, and OpenSolaris. (2005-09-13 19:43:00.0) Permalink Comments [4]
Sooner than I thought! Well, it looks like our friends on the OpenSolaris source team have been working more quickly than I thought. If you look at certain files (like the ESP source), they've been mysteriously fleshed-out. While
COMING SOON: Full IPsec kernel code Pardon my channelling of Jeff Spicoli, but Mike Kupfer is working on delivering the with-kernel-crypto build of OpenSolaris. This means that OpenSolaris users will have a working implementation of ESP at their fingertips. It also means Bill and I can (time permitting... uggh) give some more details and explanations about what's going on under the covers. Watch this space for more! (2005-08-16 11:20:03.0) Permalink Comments [0]PF_KEY in Solaris, or "Dude, Where's My Spec?" An OpenSolaris IPsec HelloHi! Those of you visiting here probably know I'm one of the IPsec guys (actually, I'm the original IPsec guy) here in Solaris-land. Bill may also have some stuff to say about the IPsec source in Solaris.The kernel source for IPsec (AH, ESP, and the internal databases) lives in usr/src/uts/common/inet/ip/, because we're an integral part of our IP implementation. I should warn you now that there's a mixture of STREAMS boundaries and function calls between the different parts of the IPsec subsystem. It used to be almost all STREAMS, because of broken US Export restrictions (across all political party lines, BTW). We figured we could sell it as exportable to the powers that be more easily if we used a "general-purpose interface" which allowed for easy module perforation for moving our data around. As the restrictions loosened up, we were able to streamline things somewhat. We hope to do even more now that OpenSolaris is available. There are bits and pieces of the actual Solaris IPsec missing from OpenSolaris (especially from ESP) that will show up on OpenSolaris soon as well, now that we're officially open-source. (It's a bit of a chicken & egg problem.) This entry will be discussing the PF_KEY implementation in Solaris. I assume you know something about how IPsec works, have read RFC 2367, and have a handle on TCP/IP protocol suite principles. A Brief PF_KEY SynopsisPF_KEY is analagous to the PF_ROUTE routing socket. See Keith Sklower's Radix-Tree paper at his site for the introduction to routing sockets. Where the routing socket manipulates IP forwarding entries (or routes), the PF_KEY socket manipulates IPsec Security Associations (SAs). A user-space application sends a message to the kernel telling it to ADD, DELETE, or UPDATE SAs, and the kernel sends back a message indicating either success or failure.The paper makes mention of a message that's little-used in most PF_ROUTE implementations -- RTM_RESOLVE. RTM_RESOLVE allows a user-space application to resolve an address, e.g. a user-space ARP. This inspired PF_KEY's similar message, SADB_ACQUIRE, which is used to tell a user-space key management (KM) daemon that an outgoing IPsec SA is needed. RFC 2367 has the specification for a PF_KEY socket. Solaris Changes from RFC 2367Most, if not all, existing PF_KEY implementations either alter or add to the message types in RFC 2367. Most changes were made because:
Solaris addresses the last bullet by introducing a separate PF_POLICY socket for SPD manipulation. The other issues, however, were a problem for us. All of our changes to PF_KEY were a direct result of implementing The Internet Key Exchange (IKE) as part of our work in Solaris 9. They are summarized below:
But Dan... weren't you an author of RFC 2367?!?Yes I was. Hence the question: Dude, Where's My Spec?I wasn't allowed (yes, I'm serious; and no, it had nothing to do with any government interference) to work on IPsec or IKE when I first got to Sun, but the RFC was work that was a continuation from my previous job. In hindsight, I think we should've been paying more attention to the customers (authors of KM daemons, of which I'd be one someday). I was wrapped up in non-IPsec work at Sun when I wasn't working on what would become RFC 2367, and I split my attention in a non-optimal fashion. Enough yapping, let's see some code!The first place to look is usr/src/uts/common/net/pfkeyv2.h, which gets deposited into /usr/include/net/ on a running system. You'll notice every structure that doesn't have a field of type uint64_t will have a union in it. Here's the base PF_KEY message: Notice that every extra field that is not in RFC 2367 uses the _X_ naming convention. In the case above, we took the two uint32_ts and merged them into a union with a uint64_t so that we can force 64-bit alignment on sadb_msg_t. This makes PF_KEY message manipulations 64-bit happy.If you look at an extension that has a 64-bit type in it already, you'll see that there's no alignment-forcing union inserted into the definition: (Yes, PF_KEY is Y2038-ready, as long as the KM application is compiled as 64-bit.) For people interested in applications that use PF_KEY, I'd suggest investigating the ipseckey(1m) command. Its source can be found in usr/src/cmd/cmd-inet/usr.sbin/ipseckey.c. This program does not use command-line editing yet, but as an example of a PF_KEY consumer, it does the job. All relevant PF_KEY internal-implementation headers live in usr/src/uts/common/inet. Relevant header files are:
keysock either handles messages from a PF_KEY socket, or from the SADB and its consumers. The heavy lifting for user-generated PF_KEY messages is in keysock_parse(). The first thing keysock_parse() does is perform some reality checks. First off, the actual data length should match what's in the sadb_msg_len field. (Note the first of may SADB_nnTOmm() macros, for converting units of 64-bits to units of 8-bits, etc.) The first really interesting part of reality-checking is the "extension vector", or as it's called in the IPsec code, the extv. A PF_KEY message has a base header, followed by one or more extension headers. Let me quote this section from RFC 2367: The keysock code takes this to heart in keysock_get_ext(). The extv is a vector of sadb_ext_t pointers, where the specific extension type (SADB_EXT_foo) can be found by merely indexing into the vector by that value. Say you want the sadb_sa_t extension:
sadb_sa_t *sa;
sa = (sadb_sa_t *)extv[SADB_EXT_SA];
The above code snippet shows what you need to do. As we generate the
extv, if we see a collision, we return EINVAL (with an appropriate
diagnostic). We do not enforce extension ordering inbound or outbound. Once
keysock is done with first-pass reality-checks, the extv is sent around (as
part of the KEYSOCK_IN M_CTL that is prepended to the data) to all who need
it.Most messages are shuttled off to keysock_passdown() for sending off via STREAMS to either AH or ESP. Unusual inbound messages for keysock are the SADB_REGISTER, SADB_FLUSH, SADB_DUMP, SADB_ACQUIRE, and SADB_X_INVERSE_ACQUIRE ones. SADB_REGISTER sets socket state, as well as informs consumers about the register. If an SADB_REGISTER is sent for a specific SA type (e.g. ESP, AH), then the message is treated like a common-case message, except that when its reply arrive, keysock_t state is altered to indicate a registered socket. If the sadb_msg_satype is set to 0, then the message is an EXTENDED register, and an extended-register extension (sadb_x_ereg_t) is required. keysock converts the 0-terminated list of one-byte values into a bit vector internally. Then it sends the message to consumers like a normal REGISTER. An inbound SADB_ACQUIRE can be used to signal other KM applications. (If PF_KEY is used to keep keys in the kernel for user-space consumers.) The more common case, however, is a negative ACQUIRE, which means a KM negotiation failed and the internal ACQUIRE record (more on this in a bit) needs to be cancelled. SADB_FLUSH and SADB_DUMP messages need to lock down the keysock module until their respective operations are finished. FLUSH doesn't take as long, but DUMP needs to keep track of all consumer-originated replies until the consumer indicates it is done. SADB_X_PROMISC merely changes some keysock state. It never goes to a consumer. The SADB_X_INVERSE_ACQUIRE handling is a glimpse of things to come for keysock. It does not use the keysock_passdown() method of calling a consumer. It instead calls directly into IPsec (and if we had other in-kernel consumers, it would directly call to those) and returns a message to the user immediately. The keysock_rput() function handles all messages from consumers. The KEYSOCK_OUT portion of the switch checks for FLUSH and DUMP messages, and releases the clamps on keysock if the final message for a FLUSH or a DUMP has been received. Otherwise, the keysock_passup() does the work. keysock_passup() is conceptually much simpler than keysock_parse(). It merely has to make one of three delivery decisions:
The more interesting parts of PF_KEY are handled inside the SADB code (sadb.c) and in the consumers. Those will be the subject of one or more other entries, because of all of the interaction with the IPsec SADB. This entry was brought to you by the Technorati Tags OpenSolaris and Solaris. (2005-06-14 09:06:16.0) Permalink Comments [0] |
Calendar
RSS Feeds
All /Entertainment /IPsec /Miscellany /Networking SearchLinks
NavigationReferersToday's Page Hits: 13 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||