RFC 2367

PF_KEY Key Management API, Version 2

๐Ÿ“… published July 1998, from ietf

1 Introduction โœจ

Okay bestie, let's start at the very top ๐Ÿ’–. This is RFC 2367, titled PF_KEY Key Management API, Version 2 ๐ŸŽ€. It was written by D. McDonald, C. Metz, and B. Phan ๐Ÿซถ. It's Informational, so it's sharing info rather than setting an Internet standard ๐ŸŒธ, and it's dated July 1998 โญ. The header doesn't list anything it obsoletes or updates.

Status of this Memo ๐Ÿ“œ

This memo gives information to the Internet community ๐ŸŒˆ. It does not specify any kind of Internet standard, and anyone can distribute it as much as they want ๐Ÿ’….

Copyright (C) The Internet Society (1998). All Rights Reserved.

Abstract ๐Ÿ”‘

Okay so this doc presents a generic key management API that's not just for IP Security [Atk95a] [Atk95b] [Atk95c] but also for other network security services ๐Ÿซถ. Version 1 of this API was built into 4.4-Lite BSD as part of the U. S. Naval Research Laboratory's freely distributable and usable IPv6 and IPsec implementation[AMPMC96] ๐Ÿฆ‹. It's written down here so others can adopt and use it too, which makes key management applications way more portable ๐Ÿงš. Think a manual keying application, an ISAKMP daemon, a GKMP daemon [HM97a][HM97b], a Photuris daemon, or a SKIP certificate discovery protocol daemon ๐Ÿ’œ.

Table of Contents ๐Ÿ—‚๏ธ

   1      Introduction .............................................  3
   1.1    Terminology ..............................................  3
   1.2    Conceptual Model .........................................  4
   1.3    PF_KEY Socket Definition .................................  8
   1.4    Overview of PF_KEY Messaging Behavior ....................  8
   1.5    Common PF_KEY Operations .................................  9
   1.6    Differences Between PF_KEY and PF_ROUTE .................. 10
   1.7    Name Space ............................................... 11
   1.8    On Manual Keying ..........................................11
   2      PF_KEY Message Format .................................... 11
   2.1    Base Message Header Format ............................... 12
   2.2    Alignment of Headers and Extension Headers ............... 14
   2.3    Additional Message Fields ................................ 14
   2.3.1  Association Extension .................................... 15
   2.3.2  Lifetime Extension ....................................... 16
   2.3.3  Address Extension ........................................ 18
   2.3.4  Key Extension ............................................ 19
   2.3.5  Identity Extension ....................................... 21
   2.3.6  Sensitivity Extension .................................... 21
   2.3.7  Proposal Extension ....................................... 22
   2.3.8  Supported Algorithms Extension ........................... 25
   2.3.9  SPI Range Extension ...................................... 26
   2.4    Illustration of Message Layout ........................... 27
   3      Symbolic Names ........................................... 30
   3.1    Message Types ............................................ 31
   3.1.1  SADB_GETSPI .............................................. 32
   3.1.2  SADB_UPDATE .............................................. 33
   3.1.3  SADB_ADD ................................................. 34
   3.1.4  SADB_DELETE .............................................. 35
   3.1.5  SADB_GET ................................................. 36
   3.1.6  SADB_ACQUIRE ............................................. 36
   3.1.7  SADB_REGISTER ............................................ 38
   3.1.8  SADB_EXPIRE .............................................. 39
   3.1.9  SADB_FLUSH ............................................... 40
   3.1.10 SADB_DUMP ................................................ 40
   3.2    Security Association Flags ............................... 41
   3.3    Security Association States .............................. 41
   3.4    Security Association Types ............................... 41
   3.5    Algorithm Types .......................................... 42
   3.6    Extension Header Values .................................. 43
   3.7    Identity Extension Values ................................ 44
   3.8    Sensitivity Extension Values ............................. 45
   3.9    Proposal Extension Values ................................ 45
   4      Future Directions ........................................ 45
   5      Examples ................................................. 45
   5.1    Simple IP Security Example ............................... 46
   5.2    Proxy IP Security Example ................................ 47
   5.3    OSPF Security Example .................................... 50
   5.4    Miscellaneous ............................................ 50
   6      Security Considerations .................................. 51
          Acknowledgments ............,............................. 52
          References ............................................... 52
          Disclaimer ............................................... 54
          Authors' Addresses ....................................... 54
   A      Promiscuous Send/Receive Extension ....................... 55
   B      Passive Change Message Extension ......................... 57
   C      Key Management Private Data Extension .................... 58
   D      Sample Header File ....................................... 59
   E      Change Log ............................................... 64
   F      Full Copyright Statement ................................. 68

The Intro is where the vibe gets real ๐Ÿ”ฅ. PF_KEY is a brand-new socket protocol family ๐Ÿฆ‹. Trusted, privileged key management applications use it to talk to an operating system's key management internals, which this doc calls the "Key Engine" or the Security Association Database (SADB) ๐Ÿ’–. The Key Engine and its structures hold the required security attributes for a session, and they're instances of the "Security Association" (SA) concept from [Atk95a] โœจ. The names PF_KEY and Key Engine are about more than cryptographic keys, but they stuck around for consistency with the old-school phrase, "Key Management" ๐ŸŒธ.

PF_KEY is partly derived from the BSD routing socket, PF_ROUTE ๐Ÿงš [Skl91]. This document describes Version 2 of PF_KEY ๐Ÿ’…. Version 1 was implemented in the first five alpha test versions of the NRL IPv6+IPsec Software Distribution for 4.4-Lite BSD UNIX, and also in the Cisco ISAKMP/Oakley key management daemon ๐ŸŽ€. Version 2 extends and refines that interface โญ. In theory, the messages defined here could be used outside of a socket context (for example, between two directly communicating user-level processes), but this doc won't go into detail on those possibilities ๐Ÿ™ˆ.

Security policy is deliberately left out of this interface ๐Ÿซถ. PF_KEY isn't a mechanism for tuning systemwide security policy, and it's not meant to enforce any sort of key management policy ๐ŸŒˆ. The PF_KEY developers believe it's important to keep security mechanisms (like PF_KEY) separate from security policies ๐Ÿ‘‘. That way, one mechanism can more easily support multiple policies โœจ.

1.1 Terminology ๐Ÿ’…

Even though this document isn't meant to be an actual Internet standard, the words that define how significant particular features of this interface are usually get capitalized ๐Ÿ“. Some of these words, including MUST, MAY, and SHOULD, are detailed in [Bra97] ๐Ÿ’œ.

CONFORMANCE and COMPLIANCE ๐ŸŽ€

Conformance to this specification means the same thing as compliance to it ๐Ÿซถ. Either way, the mandatory-to-implement, or MUST, items MUST be fully implemented as specified here ๐Ÿ”ฅ. If any mandatory item isn't implemented as specified here, that implementation is not conforming and not compliant with this specification ๐Ÿ’€.

This specification also uses a lot of terms that are common in network security ๐Ÿฆ‹. Other documents give more definitions and background on these [VK83, HA94, Atk95a] ๐ŸŒธ. Two terms deserve a special shout-out โญ:

(Encryption/Authentication) Algorithm ๐Ÿ”

For PF_KEY purposes, an algorithm, whether encryption or authentication, is the set of operations performed on a packet to complete authentication or encryption as indicated by the SA type ๐Ÿงš. A PF_KEY algorithm MAY consist of more than one cryptographic algorithm ๐Ÿ’–. Another possibility is that the same basic cryptographic algorithm gets applied with different modes of operation or some other implementation difference โœจ. These differences, henceforth called algorithm differentiators, distinguish between different PF_KEY algorithms and between options to the same algorithm ๐Ÿ‘‘. Algorithm differentiators will often cause fundamentally different security properties ๐Ÿ™ˆ.

Here's the tea ๐Ÿต. Both DES and 3DES use the same cryptographic algorithm, but they're used differently and have different security properties ๐Ÿ˜ญ. The triple-application of DES counts as an algorithm differentiator, so DES and 3DES get separate PF_KEY algorithms ๐Ÿ’…. Keyed-MD5 and HMAC-MD5 use the same hash function but build their message authentication codes differently, so the use of HMAC is an algorithm differentiator ๐ŸŒˆ. DES-ECB and DES-CBC are the same cryptographic algorithm, but they use a different mode, and mode (like chaining vs. code-book) is an algorithm differentiator ๐Ÿฆ‹. Blowfish with a 128-bit key is similar to Blowfish with a 384-bit key, because the algorithm's workings are otherwise the same, so key length is not an algorithm differentiator ๐Ÿซถ.

In terms of IP Security, a general rule of thumb is that whatever might be labeled the "encryption" part of an ESP transform is probably a PF_KEY encryption algorithm ๐Ÿ”ฅ. Whatever might be labelled the "authentication" part of an AH or ESP transform is probably a PF_KEY authentication algorithm ๐Ÿ’–.

1.2 Conceptual Model ๐Ÿง โœจ

okay bestie, this section is the big picture ๐Ÿ–ผ๏ธ It explains how an OS that runs the PF_KEY key management API is set up. It's just background so the rest of the doc makes sense, and it does NOT force your implementation to copy these exact pieces ๐Ÿ’…

Key management is mostly done at the application layer ๐Ÿ“ฑ ISAKMP/Oakley, GKMP, and Photuris (all IPsec key management proposals) are application-layer protocols, and manual keying happens up there too. Even parts of the SKIP IP-layer keying proposal can live at the application layer ๐Ÿซถ Figure 1 shows how a Key Management daemon fits in. It talks to the Key Engine through PF_KEY, and it talks to a remote key management entity over the network using PF_INET (or PF_INET6 for IPv6) ๐ŸŒ

The "Key Engine" (also called the "Security Association Database (SADB)") is a logical thing in the kernel that stores, updates, and deletes Security Association data for different security protocols ๐Ÿ—„๏ธ Inside the kernel there are logical interfaces like getassocbyspi() and getassocbysocket(). In-kernel security protocols (like IP Security, aka IPsec) use those to request and grab SAs ๐Ÿ”ฅ

Here's the IPsec flow, iykyk ๐Ÿ™ˆ If policy says a particular outbound packet needs processing, the IPsec implementation asks the Key Engine for an appropriate SA through the kernel-internal interface. If the Key Engine already has one, it allocates the SA to this session (marking it as used) and hands it back ๐ŸŽ If it doesn't have one, but a key management app already said (via a PF_KEY SADB_REGISTER message) that it can get SAs, then the Key Engine asks for one to be created (via a PF_KEY SADB_ACQUIRE message). When the key management daemon creates a new SA, it drops it into the Key Engine for future use ๐Ÿ’–

                 +---------------+
                 |Key Mgmt Daemon|
                 +---------------+
                   |           |
                   |           |
                   |           |                   Applications
           ======[PF_KEY]====[PF_INET]==========================
                   |           |                   OS Kernel
           +------------+   +-----------------+
           | Key Engine |   | TCP/IP,         |
           |  or  SADB  |---| including IPsec |
           +------------+   |                 |
                            +-----------------+
                                   |
                               +-----------+
                               | Network   |
                               | Interface |
                               +-----------+

Figure 1: Relationship of Key Mgmt to PF_KEY

Some security protocols (like IP Security) are usually implemented inside the OS kernel because of performance ๐ŸŽ๏ธ Others (like OSPFv2 Cryptographic Authentication) run in trusted privileged applications outside the kernel ๐Ÿ‘‘ Figure 2 shows a trusted, privileged routing daemon that uses PF_INET to talk routing info with a remote routing daemon, and uses PF_KEY to request, obtain, and delete the SAs used with a routing protocol โœจ

                 +---------------+
                 |Routing  Daemon|
                 +---------------+
                   |           |
                   |           |
                   |           |                   Applications
           ======[PF_KEY]====[PF_INET]==========================
                   |           |                   OS Kernel
           +------------+   +---------+
           | Key Engine |   | TCP/IP  |
           |  or  SADB  |---|         |
           +------------+   +---------+
                                   |
                               +-----------+
                               | Network   |
                               | Interface |
                               +-----------+

Figure 2: Relationship of Trusted Application to PF_KEY

When a trusted privileged app uses the Key Engine but does the security protocol itself, things shift a little ๐Ÿ”„ The app sends a PF_KEY SADB_ACQUIRE message down to the Key Engine. The Key Engine then either returns an error, or sends a similar SADB_ACQUIRE message up to one or more key management apps that can create such SAs. Same as before, the key management daemon stores the SA into the Key Engine. Then the trusted privileged app uses an SADB_GET message to grab the SA from the Key Engine ๐Ÿซง

In some implementations, policy lives in user-space even though the actual crypto happens in the kernel ๐Ÿงฉ That policy communication between the kernel mechanisms and user-space policy MAY be implemented by PF_KEY extensions, or some other mechanism. This document does not specify such extensions. A PF_KEY implementation specified by this memo does NOT have to support configuring systemwide policy using PF_KEY ๐Ÿ™…โ€โ™€๏ธ

Untrusted clients, like a user's web browser or telnet client, don't need PF_KEY ๐ŸŒธ They use mechanisms that aren't specified here to request security services (e.g. IPsec) from an OS. For security reasons, only trusted, privileged apps are allowed to open a PF_KEY socket ๐Ÿ”

1.3 PF_KEY Socket Definition ๐ŸŽ€

The PF_KEY protocol family (PF_KEY) symbol is defined in <sys/socket.h> the same way other protocol families are defined ๐Ÿ’… PF_KEY doesn't use any socket addresses. Applications using PF_KEY MUST NOT depend on the availability of a symbol named AF_KEY, but kernel implementations are encouraged to define that symbol for completeness ๐Ÿงš

The key management socket is created like this:

#include <sys/types.h>
#include <sys/socket.h>
#include <net/pfkeyv2.h>

int s;
s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);

The PF_KEY domain currently supports only the SOCK_RAW socket type ๐Ÿ“ The protocol field MUST be set to PF_KEY_V2, or else EPROTONOSUPPORT will be returned. Only a trusted, privileged process can create a PF_KEY socket ๐Ÿ”’ On conventional UNIX systems, a privileged process is a process with an effective userid of zero. On non-MLS proprietary operating systems, the notion of a "privileged process" is implementation-defined. On Compartmented Mode Workstations (CMWs) or other systems that claim to provide Multi-Level Security (MLS), a process MUST have the "key management privilege" in order to open a PF_KEY socket[DIA]. MLS systems that don't currently have such a specific privilege MUST add that special privilege and enforce it with PF_KEY in order to comply and conform with this specification ๐Ÿซก Some systems, most notably some popular personal computers, don't have the concept of an unprivileged user. These systems SHOULD take steps to restrict the programs allowed to access the PF_KEY API ๐Ÿ›ก๏ธ

1.4 Overview of PF_KEY Messaging Behavior ๐Ÿ“ฌ

A process interacts with the key engine by sending and receiving messages over the PF_KEY socket ๐Ÿ’Œ SA info can be inserted into and retrieved from the kernel's security association table using a set of predefined messages. In the normal case, all properly formed messages sent to the kernel get returned to all open PF_KEY sockets, including the sender ๐Ÿ” Improperly formed messages result in errors, and an implementation MUST check for a properly formed message before returning it to the appropriate listeners. Unlike the routing socket, most errors are sent in reply messages, not the errno field when write() or send() fails ๐Ÿ™ƒ PF_KEY message delivery is not guaranteed, especially when kernel or socket buffers are exhausted and messages get dropped ๐Ÿ˜ญ

Some messages are generated by the OS to indicate that actions need to be taken, and they aren't necessarily responding to any message the user sent down ๐Ÿšจ These messages aren't received by all PF_KEY sockets. They only go to sockets that said they want kernel-originated messages. They're special because of how often they're expected to show up ๐ŸŒˆ Also, an implementation may further wish to restrict return messages from the kernel, in cases where not all PF_KEY sockets are in the same trust domain ๐Ÿฐ

Lots of the normal BSD socket calls have undefined behavior on PF_KEY sockets ๐Ÿ’€ These include bind(), connect(), socketpair(), accept(), getpeername(), getsockname(), ioctl(), and listen().

1.5 Common PF_KEY Operations ๐Ÿ› ๏ธ

There are two basic ways to add a new Security Association into the kernel โœจ The simplest is to send a single SADB_ADD message, containing all of the SA information, from the application into the kernel's Key Engine. This approach works particularly well with manual key management, which is required for IPsec and other security protocols ๐Ÿซถ

The second approach is for the application to first request a Security Parameters Index (SPI) value from the kernel using the SADB_GETSPI message, and then send an SADB_UPDATE message with the complete Security Association data ๐Ÿฆ‹ This works well with key management daemons when the SPI values need to be known before the entire SA data is known (e.g. so the SPI value can be indicated to the remote end of the key management session) ๐Ÿงš

An individual SA can be deleted using the SADB_DELETE message. Categories of SAs, or the entire kernel SA table, can be deleted using the SADB_FLUSH message ๐Ÿงน

The SADB_GET message is used by a trusted application-layer process (e.g. routed(8) or gated(8)) to retrieve an SA (e.g. RIP SA or OSPF SA) from the kernel's Key Engine ๐Ÿ“ฅ

The kernel or an application-layer can use the SADB_ACQUIRE message to request that a Security Association be created by some application-layer key management process that has registered with the kernel via an SADB_REGISTER message ๐Ÿ“ฃ This ACQUIRE message has a sequence number associated with it. This sequence number MUST be used by followup SADB_GETSPI, SADB_UPDATE, and SADB_ADD messages, so the kernel can keep track of which request gets its keying material ๐ŸŽฏ The sequence number is kind of like a transaction ID in a remote procedure call ๐Ÿ”ข

The SADB_EXPIRE message is sent from the kernel to key management applications when the "soft lifetime" or "hard lifetime" of a Security Association has expired โฐ Key management applications should use receipt of a soft lifetime SADB_EXPIRE message as a hint to negotiate a replacement SA, so the replacement SA is ready and in the kernel before it's needed ๐Ÿ’œ

A SADB_DUMP message is also defined, but it's mainly intended for PF_KEY implementor debugging and isn't used in ordinary operation of PF_KEY ๐Ÿ™ˆ

1.6. Differences Between PF_KEY and PF_ROUTE ๐Ÿฆ‹

okay so if you've ever worked with the routing socket, PF_KEY is gonna feel a little different. these are the main differences, so pay attention bestie โœจ

  • PF_KEY errors usually show up inside PF_KEY messages instead of making write() fail and dropping the number into errno. ngl this is kind of iconic ๐Ÿ’… It means other listeners on a PF_KEY socket can see that another process's request failed, which is super useful for auditing ๐Ÿ‘€ The flip side is that an app that doesn't read its PF_KEY messages can't do any error checking ๐Ÿ’€

An implementation MAY return the errors EINVAL, ENOMEM, and ENOBUFS by making write() fail and putting the error number in errno. This is a shortcut for the common error cases where it wouldn't make sense for any other process to get the error ๐Ÿงš An application MUST NOT depend on those errors being set by the write() call, but it SHOULD still check for them and handle them properly ๐Ÿซถ

  • The whole message doesn't always come back in the reply. SADB_ADD is a great example of this ๐Ÿ™ˆ
  • The kernel doesn't set the PID. The process that starts a message MUST set sadb_msg_pid to its own PID ๐ŸŽ€ If the kernel ORIGINATES a message, it MUST set sadb_msg_pid to 0. A reply to an original message SHOULD carry the pid of that original message. (like, the kernel's response to an SADB_ADD SHOULD have its pid set to the pid of the original SADB_ADD message ๐ŸŒธ)

1.7. Name Space ๐Ÿ’œ

all PF_KEYv2 preprocessor symbols and structure definitions show up once you include <net/pfkeyv2.h>. there's exactly one exception to that: the symbol "PF_KEY" (or two, if you count "AF_KEY" too), which comes from including <sys/socket.h> ๐Ÿงš all PF_KEYv2 preprocessor symbols start with the prefix "SADB_", and all structure names start with "sadb_". there are exactly two exceptions to that rule: "PF_KEY_V2" and "PFKEYV2_REVISION" โœจ

"PFKEYV2_REVISION" is a date-encoded value, kind of like certain values defined by POSIX and X/Open. the current value for "PFKEYV2_REVISION" is 199806L, where 1998 is the year and 06 is the month ๐Ÿ“…

including <net/pfkeyv2.h> MUST NOT define any symbols or structures in the PF_KEYv2 name space that aren't described in this document, unless the authors give explicit prior permission ๐Ÿ™…โ€โ™€๏ธ any symbols or structures in the PF_KEYv2 name space that aren't described here MUST start with "SADB_X_" or "sadb_x_". an implementation that doesn't follow these rules IS NOT COMPLIANT WITH THIS SPECIFICATION and MUST NOT claim to be ๐Ÿšซ these rules also apply to any files that might get included because of <net/pfkeyv2.h>. this gives implementors some peace of mind that they won't run into namespace surprises ๐Ÿฉท

1.8. On Manual Keying ๐Ÿ”‘

not unlike the 4.4-Lite BSD PF_ROUTE socket, this interface gives an application full-reign over the security associations in a kernel that implements PF_KEY ๐Ÿ‘‘ a PF_KEY implementation MUST have some kind of manual interface to PF_KEY, and it SHOULD allow all of the functionality of the programmatic interface described here ๐Ÿซถ


2. PF_KEY Message Format ๐ŸŽ€

PF_KEY messages are a base header followed by extra data fields, and some of those fields might be optional ๐ŸŒˆ the format of the extra data depends on the type of message.

PF_KEY messages don't currently require any specific ordering for non-network multi-octet fields. unless otherwise specified (like SPI values), fields MUST be in host-specific byte order ๐Ÿง 

2.1. Base Message Header Format ๐Ÿ’…

PF_KEY messages are the base message header followed by security association specific data, whose types and lengths are given by a generic type-length encoding โœจ

here's the base header, written with POSIX types. the fields are arranged mainly for alignment and, where possible, for clarity ๐Ÿ“

        struct sadb_msg {
                uint8_t sadb_msg_version;
                uint8_t sadb_msg_type;
                uint8_t sadb_msg_errno;
                uint8_t sadb_msg_satype;
                uint16_t sadb_msg_len;
                uint16_t sadb_msg_reserved;
                uint32_t sadb_msg_seq;
                uint32_t sadb_msg_pid;
        };
        /* sizeof(struct sadb_msg) == 16 */

sadb_msg_version: this is the version field of the PF_KEY message. It MUST be set to PF_KEY_V2. If it isn't set to PF_KEY_V2, the write() call MAY fail and return EINVAL. otherwise the behavior is undetermined, since the application might not understand the formatting of messages coming from the kernel ๐Ÿคทโ€โ™€๏ธ

sadb_msg_type: identifies the type of message. the valid message types are described later in this document ๐Ÿ“–

sadb_msg_errno: should be set to zero by the sender. the responder stores the error code in this field if an error happens. this includes the case where the responder is in user space (e.g. if user-space negotiation fails, an errno can be returned) ๐Ÿšจ

sadb_msg_satype: indicates the type of security association(s). valid Security Association types are declared in <net/pfkeyv2.h>. the current set of Security Association types is listed later in this document ๐Ÿ”

sadb_msg_len: contains the total length, in 64-bit words, of all the data in the PF_KEY message. that includes the base header length and any additional data after the base header ๐Ÿงฎ this length also includes any padding or extra space that might be there. unless otherwise stated, all other length fields are measured in 64-bit words too.

on user-to-kernel messages, this field MUST be verified against the length of the inbound message. EMSGSIZE MUST be returned if the verification fails ๐Ÿ™…โ€โ™€๏ธ on kernel-to-user messages, a size mismatch most likely means the user didn't provide a big enough buffer for the message. in those cases the user application SHOULD drop the message, but it MAY try to pull out whatever information it can from it ๐Ÿฅบ

sadb_msg_reserved: a reserved value. It MUST be zeroed by the sender. every field labeled reserved later in the document has the same semantics as this one ๐ŸงŠ

sadb_msg_seq: contains the sequence number of this message. this field, along with sadb_msg_pid, MUST be used to uniquely identify requests to a process. the sender is responsible for filling it in. that responsibility also includes matching the sadb_msg_seq of a request (e.g. SADB_ACQUIRE) ๐Ÿ”ข

this field is similar to a transaction ID in a remote procedure call implementation ๐Ÿซถ

sadb_msg_pid: identifies the process that originated this message, or the process a message is bound for. for example, if process id 2112 sends an SADB_UPDATE message to the kernel, the process MUST set this field to 2112, and the kernel will set this field to 2112 in its reply to that SADB_UPDATE message ๐Ÿชช this field, along with sadb_msg_seq, can be used to uniquely identify requests to a process.

it is currently assumed that a 32-bit quantity will hold an operating system's process ID space ๐Ÿ’–

2.2. Alignment of Headers and Extension Headers ๐Ÿ“

the base message header is a multiple of 64 bits, and fields after it in memory will be 64-bit aligned if the base itself is 64-bit aligned ๐Ÿงฑ some of the later extension headers have 64-bit fields in them, so they need to be 64-bit aligned in an environment where 64-bit quantities need to be 64-bit aligned too.

the basic unit of alignment and length in PF_KEY Version 2 is 64 bits. Therefore:

  • all extension headers, including the sadb_ext overlay fields, MUST be a multiple of 64 bits long ๐Ÿ“
  • all variable length data MUST be padded appropriately so its length in a message is a multiple of 64 bits ๐ŸŽ€
  • all length fields are, unless otherwise specified, in units of 64 bits ๐Ÿ”ข
  • implementations may safely access quantities of between 8 and 64 bits directly within a message without risk of alignment faults ๐Ÿซง

all PF_KEYv2 structures are packed and already have all their intended padding. implementations MUST NOT insert any extra fields, including hidden padding, into any structure in this document ๐Ÿšซ this stops implementations from "extending" or "enhancing" existing headers without changing the extension header type ๐Ÿ™ˆ as a guard against sneaky silent padding, each structure in this document is labeled with its size in bytes. the size of these structures in an implementation MUST match the size listed ๐Ÿ“

2.3. Additional Message Fields ๐ŸŒธ

the additional data after the base header is made of various length-type-value fields. the first 32 bits have a constant form:

        struct sadb_ext {
                uint16_t sadb_ext_len;
                uint16_t sadb_ext_type;
        };
        /* sizeof(struct sadb_ext) == 4 */

sadb_ext_len: the length of the extension header, in 64-bit words, inclusive ๐Ÿงฎ

sadb_ext_type: the type of extension header that follows. the values for this field are detailed later. the value zero is reserved ๐ŸงŠ

types of extension headers include: Association, Lifetime(s), Address(s), Key(s), Identity(ies), Sensitivity, Proposal, and Supported ๐ŸŒˆ there MUST be only one instance of each extension type in a message (e.g. Base, Key, Lifetime, Key is forbidden) โ˜๏ธ an EINVAL will be returned if there are duplicate extensions within a message. implementations MAY enforce ordering of extensions in the order presented in the EXTENSION HEADER VALUES section โœจ

if an unknown extension type is encountered, it MUST be ignored ๐Ÿซฅ applications using extension headers not specified in this document MUST be prepared to work around other system components that don't process those headers. likewise, if an application encounters an unknown extension from the kernel, it must be prepared to work around it. also, a kernel that generates extra extension header types MUST NOT depend on applications also understanding those extra extension header types ๐Ÿงฉ

all extension definitions include these two fields (len and exttype) because they are instances of a generic extension (not unlike how sockaddr_in and sockaddr_in6 are instances of a generic sockaddr) ๐Ÿชž the sadb_ext header MUST NOT ever be present in a message without at least four bytes of extension header data following it. so there's no problem with it being only four bytes long ๐Ÿ’œ

all extensions documented in this section MUST be implemented by a PF_KEY implementation ๐ŸŽ€

okay that's the groundwork done, bestie ๐ŸŒˆโœจ

2.3.1 Association Extension โœจ

okay bestie, the Association extension is where all the juicy details about ONE security association live ๐Ÿ’– It's basically absent in only two situations: when you're passing control messages like SADB_FLUSH or SADB_REGISTER, and on the SADB_ACQUIRE message ๐Ÿฆ‹

struct sadb_sa {
        uint16_t sadb_sa_len;
        uint16_t sadb_sa_exttype;
        uint32_t sadb_sa_spi;
        uint8_t sadb_sa_replay;
        uint8_t sadb_sa_state;
        uint8_t sadb_sa_auth;
        uint8_t sadb_sa_encrypt;
        uint32_t sadb_sa_flags;
};
/* sizeof(struct sadb_sa) == 16 */
  • sadb_sa_spi is the Security Parameters Index for the association ๐Ÿ”‘ It's 32 bits, but some security association types have an SPI or key identifier that's shorter. When that happens, the smaller value shall be stored in the least significant bits, and the unneeded bits shall be zero. This field MUST be in network byte order, no exceptions ๐Ÿ’…
  • sadb_sa_replay is the size of the replay window, if there is one. If it's zero, then no replay window is in use ๐Ÿ›ก๏ธ
  • sadb_sa_state is the state of the security association. The currently defined states get explained later in this document ๐ŸŒธ
  • sadb_sa_auth is the authentication algorithm used with this security association. The valid authentication algorithms are also covered later. A value of zero means no authentication is used for this association ๐Ÿ™ˆ
  • sadb_sa_encrypt is the encryption algorithm used with this security association. The valid encryption algorithms are described later too. A value of zero means no encryption is used for this association โœจ
  • sadb_sa_flags is a bitmap of options for the security association. The currently defined flags are described later in this document ๐ŸŽ€

The kernel MUST check these values where appropriate. For example, IPsec AH with no authentication algorithm is probably an error ๐Ÿ‘€ It's giving "wait, that's sus" energy ๐Ÿ’€

When it's used with some messages, the values in some fields in this header should be ignored ๐Ÿงš

2.3.2 Lifetime Extension ๐ŸŒˆ

The Lifetime extension specifies one or more lifetime variants for this security association. If there's no Lifetime extension present, the association has an infinite lifetime, which is lowkey iconic but also kinda scary ๐Ÿ˜ญ An association SHOULD have some sort of lifetime attached to it ๐Ÿ’œ

Lifetime variants come in three flavors. HARD means the hard-limit expiration, SOFT means the soft-limit expiration, and CURRENT tells you the current state of a given security association ๐Ÿฆ‹ The Lifetime extension looks like this:

struct sadb_lifetime {
        uint16_t sadb_lifetime_len;
        uint16_t sadb_lifetime_exttype;
        uint32_t sadb_lifetime_allocations;
        uint64_t sadb_lifetime_bytes;
        uint64_t sadb_lifetime_addtime;
        uint64_t sadb_lifetime_usetime;
};
/* sizeof(struct sadb_lifetime) == 32 */
  • sadb_lifetime_allocations: for CURRENT, this is the number of different connections, endpoints, or flows the association has been allocated towards. For HARD and SOFT, it's how many of these the association may be allocated towards before it expires. What counts as a connection, flow, or endpoint is system specific ๐ŸŒธ
  • sadb_lifetime_bytes: for CURRENT, this is how many bytes have been processed using this security association. For HARD and SOFT, it's the number of bytes that may be processed using this association before it expires ๐Ÿ“
  • sadb_lifetime_addtime: for CURRENT, this is the time, in seconds, when the association was created. For HARD and SOFT, it's the number of seconds after creation until the association expires โฐ

For time fields like this one, it's assumed that 64 bits is sufficiently large to hold the POSIX time_t value. If that assumption turns out to be wrong, this field will have to be revisited ๐Ÿ™ˆ

  • sadb_lifetime_usetime: for CURRENT, this is the time, in seconds, when the association was first used. For HARD and SOFT, it's the number of seconds after the first use of the association until it expires ๐Ÿ’ซ

The semantics of lifetimes are inclusive-OR, first-to-expire. That means if values for bytes and time, or multiple times, get passed in, the first of those values to be reached will cause a lifetime expiration. Whoever hits the finish line first wins ๐Ÿ๐Ÿ‘‘

2.3.3 Address Extension ๐Ÿซถ

The Address extension specifies one or more addresses associated with a security association. Address extensions for both source and destination MUST be present whenever an Association extension is present, so don't you dare leave one out ๐Ÿ˜ค The format of an Address extension is:

struct sadb_address {
        uint16_t sadb_address_len;
        uint16_t sadb_address_exttype;
        uint8_t sadb_address_proto;
        uint8_t sadb_address_prefixlen;
        uint16_t sadb_address_reserved;
};
/* sizeof(struct sadb_address) == 8 */

/* followed by some form of struct sockaddr */

The sockaddr structure SHOULD conform to the sockaddr structure of the system implementing PF_KEY. If the system has an sa_len field, the sockaddrs in the message SHOULD have one too. If the system has NO sa_len field, the sockaddrs SHOULD NOT have an sa_len field ๐Ÿง

All the non-address information in the sockaddrs, like sin_zero for AF_INET sockaddrs and sin6_flowinfo for AF_INET6 sockaddrs, MUST be zeroed out ๐Ÿงน Zeroing the ports (like sin_port and sin6_port) MUST be done for all messages, except for originating SADB_ACQUIRE messages. Those SHOULD fill the ports in with ports from the relevant TCP or UDP session that generated the ACQUIRE message ๐ŸŒŠ

If the ports are non-zero, then the sadb_address_proto field, which is normally zero, MUST be filled in with the transport protocol's number ๐Ÿ”ข If sadb_address_prefixlen is non-zero, the address has a prefix, often used in KM access control decisions, and its length is specified in sadb_address_prefixlen. These extra fields may be useful to KM applications, so they're not just filler ๐Ÿ’…

The SRC and DST addresses for a security association MUST be in the same protocol family, and they MUST always be present or absent together in a message. The PROXY address MAY be in a different protocol family. For most security protocols, it represents an actual originator of a packet, like the inner-packet's source address in a tunnel ๐Ÿš‡

The SRC address MUST be a unicast or unspecified (e.g., INADDR_ANY) address ๐ŸŽฏ The DST address can be any valid destination address: unicast, multicast, or even broadcast ๐Ÿ“ก The PROXY address SHOULD be a unicast address. (Experimental security protocols exist where PROXY semantics may be different than described above ๐Ÿงช)

2.3.4. Key Extension โœจ

okay bestie, the Key extension is where the actual secret sauce lives ๐Ÿ”‘ It holds one or more keys for a security association. And because keys are literally the most sensitive thing in the whole protocol, this extension is NOT always there in messages, for security reasons ๐Ÿ™ˆ

struct sadb_key {
        uint16_t sadb_key_len;
        uint16_t sadb_key_exttype;
        uint16_t sadb_key_bits;
        uint16_t sadb_key_reserved;
};
/* sizeof(struct sadb_key) == 8 */

/* followed by the key data */

sadb_key_bits is the length of the valid key data, in bits. If sadb_key_bits is zero, that MUST cause an error ๐Ÿ’€ no zero-length keys allowed, periodt.

So there are two flavours of this extension. The AUTH version is for authentication keys (think IPsec AH or OSPF MD5 ๐Ÿ”), and the ENCRYPT version is for encryption keys (think IPsec ESP ๐Ÿ’œ). PF_KEY only deals with fully formed cryptographic keys, NOT "raw key material" ๐Ÿ™…โ€โ™€๏ธ Here's the tea: when ISAKMP/Oakley is in use, the key management daemon is always the one in charge of turning the Diffie-Hellman result into distinct, fully formed keys BEFORE it hands them to the kernel through PF_KEY. Why so strict? Because PF_KEY is built to support multiple security protocols (not just IP Security) and multiple key management schemes, including manual keying, which doesn't have any concept of "raw key material" at all. A clean, protocol-independent interface is what makes things portable across operating systems and across security protocols, and that's iconic ๐Ÿ‘‘

If an algorithm defines its key to include parity bits (DES is the classic example ๐ŸŽ€), then the key used with PF_KEY MUST also include those parity bits. So a single DES key is always a 64-bit quantity, no exceptions.

When a security protocol only needs one authentication key and/or one encryption key, the fully formed key just gets sent in the matching key extension. When a protocol needs MORE than one key for the same job (like Triple-DES using 2 or 3 keys, or asymmetric algorithms), then those fully formed keys MUST be concatenated together in the order they get used for outbound packet processing ๐Ÿงฉ And when there are multiple keys, the algorithm MUST be able to figure out how long each individual key is from the info it's given. Honestly, the total key length (combined with knowing which algorithm is in use) usually gives enough info to work that out ๐Ÿ™Œ

Keys always go through the PF_KEY interface in the order they're used for outbound packet processing. For inbound processing, the correct order might be different from that canonical concatenation order, so it's the implementation's job to use the keys in the right order for BOTH inbound and outbound processing ๐Ÿ’…

Let's do an example, because examples are everything ๐Ÿฆ‹ Picture two nodes talking unicast using an ESP three-key Triple-DES Security Association. The outbound SA on the sender and the inbound SA on the receiver both contain key-A, then key-B, then key-C in their ENCRYPT key extensions. The outbound SA uses key-A first, then key-B, then key-C when encrypting. The inbound SA uses key-C, then key-B, then key-A when decrypting. (NOTE: We are aware that 3DES is actually encrypt-decrypt-encrypt. ๐Ÿค“) The canonical ordering of key-A, key-B, key-C is used for 3DES and should be documented. The order of "encryption" is the canonical order for this example. [Sch96]

The key data bits are arranged most-significant to least-significant. So a 22-bit key would take up three octets, with the least significant two bits not holding any key material, and then five extra octets get used as padding to reach the next 64-bit boundary ๐ŸŒธ

Okay, this next bit isn't directly about PF_KEY, but it's a real user-interface headache ๐Ÿ™ˆ Odd-digit hexadecimal representation of keys can be confusing. Take this 16-bit number:

0x123

That needs two octets of storage. Without any other info, though, it's unclear whether the value is stored as:

01 23           OR              12 30

The authors think the former (0x123 == 0x0123) is the better way to read that ambiguity โœจ Extra info (like writing 0x0123 or 0x1230, or saying it's only a twelve-bit number) would fix the problem entirely.

2.3.5. Identity Extension ๐Ÿชช

The Identity extension holds endpoint identities. Key management uses this info to pick the identity certificate for negotiations ๐Ÿ” The kernel can also pass this info up to network-security-aware applications so they can identify the remote entity, maybe for access control. If this extension isn't present, key management MUST assume that the addresses in the Address extension are the ONLY identities for this Security Association. Here's the struct:

struct sadb_ident {
        uint16_t sadb_ident_len;
        uint16_t sadb_ident_exttype;
        uint16_t sadb_ident_type;
        uint16_t sadb_ident_reserved;
        uint64_t sadb_ident_id;
};
/* sizeof(struct sadb_ident) == 16 */

/* followed by the identity string, if present */

sadb_ident_type tells you the type of identity info that follows. The currently defined identity types are covered later in this document ๐Ÿ“š

sadb_ident_id is an identifier used to help build an identity string when none is present. A POSIX user id value is one such identifier that goes in this field. How that field gets used is explained later on ๐Ÿซถ

A C string holding a textual representation of the identity info can optionally follow the sadb_ident extension. Its format is determined by the value in sadb_ident_type, and that's described later in the document too โœจ

2.3.6. Sensitivity Extension ๐Ÿ”

The Sensitivity extension carries security labelling info for a security association. If this extension isn't present, no sensitivity-related data can be obtained from that SA ๐Ÿ™…โ€โ™€๏ธ If it IS present, then the need for explicit security labelling on the packet is obviated, so no more labelling the packet itself. Big brain move ๐Ÿง 

struct sadb_sens {
        uint16_t sadb_sens_len;
        uint16_t sadb_sens_exttype;
        uint32_t sadb_sens_dpd;
        uint8_t sadb_sens_sens_level;
        uint8_t sadb_sens_sens_len;
        uint8_t sadb_sens_integ_level;
        uint8_t sadb_sens_integ_len;
        uint32_t sadb_sens_reserved;
};
/* sizeof(struct sadb_sens) == 16 */

/* followed by:
        uint64_t sadb_sens_bitmap[sens_len];
        uint64_t sadb_integ_bitmap[integ_len]; */

Here's what the fields mean:

  • sadb_sens_dpd describes the protection domain, which is what lets you interpret the levels and compartment bitmaps ๐Ÿ—๏ธ
  • sadb_sens_sens_level is the sensitivity level ๐Ÿ’œ
  • sadb_sens_sens_len is the length, in 64 bit words, of the sensitivity bitmap ๐ŸŒˆ
  • sadb_sens_integ_level is the integrity level ๐Ÿฆ‹
  • sadb_sens_integ_len is the length, in 64 bit words, of the integrity bitmap โญ

This sensitivity extension is designed to support the Bell-LaPadula [BL74] security model (used in compartmented-mode or multi-level secure systems), the Clark-Wilson [CW87] commercial security model, and/or the Biba integrity model [Biba77]. These formal models can be used to build a wide variety of security policies ๐ŸŽ€ Defining a particular security policy is outside the scope of this document, so that's on someone else's plate ๐Ÿ’…

Each of the bitmaps MUST be padded to a 64-bit boundary if they aren't already implicitly 64-bit aligned. Bitmaps, padded, slay ๐Ÿซถ

2.3.7 Proposal Extension ๐Ÿ’…

okay bestie, this one is the proposal extension ๐ŸŽ€ It carries a "proposed situation" of algorithm preferences, so basically it's the menu of options someone is offering. It looks like this:

        struct sadb_prop {
                uint16_t sadb_prop_len;
                uint16_t sadb_prop_exttype;
                uint8_t sadb_prop_replay;
                uint8_t sadb_prop_reserved[3];
        };
        /* sizeof(struct sadb_prop) == 8 */
        /* followed by:
           struct sadb_comb sadb_combs[(sadb_prop_len *
               sizeof(uint64_t) - sizeof(struct sadb_prop)) /
               sizeof(struct sadb_comb)]; */

Right after the header comes a list of proposed parameter combinations, in preferential order ๐ŸŒธ The values in these fields mean the same thing as the fields they'll move into if that combination gets picked. Iconic, right? โœจ

NOTE: Some algorithms in some security protocols will have variable IV lengths per algorithm. Variable length IVs are not supported by PF_KEY v2. If they were, however, proposed IV lengths would go in the Proposal Extension. ๐Ÿ™ˆ

Each combination looks like this:

        struct sadb_comb {
                uint8_t sadb_comb_auth;
                uint8_t sadb_comb_encrypt;
                uint16_t sadb_comb_flags;
                uint16_t sadb_comb_auth_minbits;
                uint16_t sadb_comb_auth_maxbits;
                uint16_t sadb_comb_encrypt_minbits;
                uint16_t sadb_comb_encrypt_maxbits;
                uint32_t sadb_comb_reserved;
                uint32_t sadb_comb_soft_allocations;
                uint32_t sadb_comb_hard_allocations;
                uint64_t sadb_comb_soft_bytes;
                uint64_t sadb_comb_hard_bytes;
                uint64_t sadb_comb_soft_addtime;
                uint64_t sadb_comb_hard_addtime;
                uint64_t sadb_comb_soft_usetime;
                uint64_t sadb_comb_hard_usetime;
        };

        /* sizeof(struct sadb_comb) == 72 */

Let's go through the fields ๐Ÿฆ‹

sadb_comb_auth: if this combination gets accepted, this value becomes the value of sadb_sa_auth.

sadb_comb_encrypt: if this combination gets accepted, this value becomes the value of sadb_sa_encrypt.

sadb_comb_auth_minbits and sadb_comb_auth_maxbits: these are the minimum and maximum acceptable authentication key lengths, in bits, in that order. If sadb_comb_auth is zero, both of these values MUST be zero. If sadb_comb_auth is nonzero, both of these values MUST be nonzero. If this combination is accepted, a value between these two (inclusive) gets stored in the sadb_key_bits field of KEY_AUTH. The minimum MUST NOT be greater than the maximum. ๐Ÿ˜ญ

sadb_comb_encrypt_minbits and sadb_comb_encrypt_maxbits: same vibe, but for encryption key lengths, in bits, in that order. If sadb_comb_encrypt is zero, both of these values MUST be zero. If sadb_comb_encrypt is nonzero, both of these values MUST be nonzero. If this combination is accepted, a value between these two (inclusive) gets stored in the sadb_key_bits field of KEY_ENCRYPT. The minimum MUST NOT be greater than the maximum. ๐Ÿ’€

sadb_comb_soft_allocations and sadb_comb_hard_allocations: if this combination is accepted, these are the proposed values of sadb_lifetime_allocations in the SOFT and HARD lifetimes, respectively. ๐Ÿ“

sadb_comb_soft_bytes and sadb_comb_hard_bytes: if this combination is accepted, these are the proposed values of sadb_lifetime_bytes in the SOFT and HARD lifetimes, respectively. ๐Ÿงš

sadb_comb_soft_addtime and sadb_comb_hard_addtime: if this combination is accepted, these are the proposed values of sadb_lifetime_addtime in the SOFT and HARD lifetimes, respectively. โญ

sadb_comb_soft_usetime and sadb_comb_hard_usetime: if this combination is accepted, these are the proposed values of sadb_lifetime_usetime in the SOFT and HARD lifetimes, respectively. ๐Ÿ‘‘

Each combination has an authentication algorithm and an encryption algorithm, and either one can be 0, which means "none" ๐Ÿซถ A combination's flags are the same as the flags in the Association extension. The min and max key lengths (in bits) come from possible a priori policy decisions, plus basic properties of the algorithm. Lifetimes are in here too, because some algorithms might know something about their own lifetimes and can suggest limits. Main character energy ๐ŸŒˆ


2.3.8 Supported Algorithms Extension ๐ŸŒˆ

The Supported Algorithms extension is the list of every algorithm the system supports. It tells key management what it's allowed to negotiate. Authentication algorithms are listed in the SUPPORTED_AUTH extension, and encryption algorithms are listed in the SUPPORTED_ENCRYPT extension. The format looks like this:

        struct sadb_supported {
                uint16_t sadb_supported_len;
                uint16_t sadb_supported_exttype;
                uint32_t sadb_supported_reserved;
        };
        /* sizeof(struct sadb_supported) == 8 */

        /* followed by:
           struct sadb_alg sadb_algs[(sadb_supported_len *
               sizeof(uint64_t) - sizeof(struct sadb_supported)) /
               sizeof(struct sadb_alg)]; */

The header is followed by one or more algorithm descriptions ๐Ÿ’– Each one looks like this:

        struct sadb_alg {
                uint8_t sadb_alg_id;
                uint8_t sadb_alg_ivlen;
                uint16_t sadb_alg_minbits;
                uint16_t sadb_alg_maxbits;
                uint16_t sadb_alg_reserved;
        };
        /* sizeof(struct sadb_alg) == 8 */

sadb_alg_id: the algorithm identification value for this algorithm. It's the value that gets stored in sadb_sa_auth or sadb_sa_encrypt if this algorithm is selected. ๐Ÿ”ฅ

sadb_alg_ivlen: the length of the initialization vector used for the algorithm. If an IV isn't needed, this value MUST be set to zero. ๐Ÿ™ˆ

sadb_alg_minbits: the minimum acceptable key length, in bits. A value of zero is invalid. โœจ

sadb_alg_maxbits: the maximum acceptable key length, in bits. A value of zero is invalid. The minimum MUST NOT be greater than the maximum. ๐ŸŽ€


2.3.9 SPI Range Extension ๐Ÿฆ‹

One PF_KEY message, SADB_GETSPI, might need a range of acceptable SPI values, and this extension is the one that handles that ๐Ÿ’… It looks like this:

        struct sadb_spirange {
                uint16_t sadb_spirange_len;
                uint16_t sadb_spirange_exttype;
                uint32_t sadb_spirange_min;
                uint32_t sadb_spirange_max;
                uint32_t sadb_spirange_reserved;
        };
        /* sizeof(struct sadb_spirange) == 16 */

sadb_spirange_min: the minimum acceptable SPI value. ๐ŸŒธ

sadb_spirange_max: the maximum acceptable SPI value. The maximum MUST be greater than or equal to the minimum. ๐Ÿ‘‘

2.4. Illustration of Message Layout โœจ

Okay bestie, this is the picture of how the octets are laid out in a PF_KEY message ๐ŸŽ€. Optional fields are marked as such, so you'll know which ones you can skip.

The base header looks like this:

     0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
    +---------------+---------------+---------------+---------------+
    |  ...version   | sadb_msg_type | sadb_msg_errno| ...msg_satype |
    +---------------+---------------+---------------+---------------+
    |          sadb_msg_len         |       sadb_msg_reserved       |
    +---------------+---------------+---------------+---------------+
    |                         sadb_msg_seq                          |
    +---------------+---------------+---------------+---------------+
    |                         sadb_msg_pid                          |
    +---------------+---------------+---------------+---------------+

The base header can be followed by one or more extension fields, and which ones show up depends on the values in various base header fields ๐Ÿ‘€. The extensions are ordered so that if they appear, they SHOULD appear in the order presented below. Keep that order in your head, it's giving organised ๐Ÿ’….

An extension field MUST not be repeated, no double-stacking ๐Ÿšซ. If you ever hit a situation where an extension MUST be repeated, it should be brought to the attention of the authors, periodt ๐Ÿซถ.

The Association extension โœจ

       0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
       +---------------+---------------+---------------+---------------+
       |          sadb_sa_len          |        sadb_sa_exttype        |
       +---------------+---------------+---------------+---------------+
       |                          sadb_sa_spi                          |
       +---------------+---------------+---------------+---------------+
       |   ...replay   | sadb_sa_state | sadb_sa_auth  |sadb_sa_encrypt|
       +---------------+---------------+---------------+---------------+
       |                         sadb_sa_flags                         |
       +---------------+---------------+---------------+---------------+

The Lifetime extension โณ

       +---------------+---------------+---------------+---------------+
       |         sadb_lifetime_len     |    sadb_lifetime_exttype      |
       +---------------+---------------+---------------+---------------+
       |                   sadb_lifetime_allocations                   |
       +---------------+---------------+---------------+---------------+
       +---------------+---------------+---------------+---------------+
       |                    sadb_lifetime_bytes                        |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+
       |                    sadb_lifetime_addtime                      |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+
       |                    sadb_lifetime_usetime                      |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+

The Address extension ๐Ÿก

       +---------------+---------------+---------------+---------------+
       |       sadb_address_len        |     sadb_address_exttype      |
       +---------------+---------------+---------------+---------------+
       | _address_proto| ..._prefixlen |     sadb_address_reserved     |
       +---------------+---------------+---------------+---------------+
       >     Some form of 64-bit aligned struct sockaddr goes here.    <
       +---------------+---------------+---------------+---------------+

The Key extension ๐Ÿ”‘

       +---------------+---------------+---------------+---------------+
       |         sadb_key_len          |         sadb_key_exttype      |
       +---------------+---------------+---------------+---------------+
       |        sadb_key_bits          |        sadb_key_reserved      |
       +---------------+---------------+---------------+---------------+
       >    A key, padded to 64-bits, most significant bits to least.  >
       +---------------+---------------+---------------+---------------+

The Identity extension ๐Ÿชช

       +---------------+---------------+---------------+---------------+
       |        sadb_ident_len         |      sadb_ident_exttype       |
       +---------------+---------------+---------------+---------------+
       |        sadb_ident_type        |      sadb_ident_reserved      |
       +---------------+---------------+---------------+---------------+
       |                         sadb_ident_id                         |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+
       >  A null-terminated C-string which MUST be padded out for      >
       <  64-bit alignment.                                            <
       +---------------+---------------+---------------+---------------+

The Sensitivity extension ๐ŸŒธ

       +---------------+---------------+---------------+---------------+
       |         sadb_sens_len         |      sadb_sens_exttype        |
       +---------------+---------------+---------------+---------------+
       |                         sadb_sens_dpd                         |
       +---------------+---------------+---------------+---------------+
       | ...sens_level | ...sens_len   |..._integ_level| ..integ_len   |
       +---------------+---------------+---------------+---------------+
       |                       sadb_sens_reserved                      |
       +---------------+---------------+---------------+---------------+
       >    The sensitivity bitmap, followed immediately by the        <
       <    integrity bitmap, each is an array of uint64_t.            >
       +---------------+---------------+---------------+---------------+

The Proposal extension ๐Ÿง

       +---------------+---------------+---------------+---------------+
       |         sadb_prop_len         |       sadb_prop_exttype       |
       +---------------+---------------+---------------+---------------+
       |...prop_replay |           sadb_prop_reserved                  |
       +---------------+---------------+---------------+---------------+
       >     One or more combinations, specified as follows...         <
       +---------------+---------------+---------------+---------------+

Here's the Combination part, which is where the proposal details live ๐Ÿ“:

       Combination
       +---------------+---------------+---------------+---------------+
       |sadb_comb_auth |sadb_comb_encr |        sadb_comb_flags        |
       +---------------+---------------+---------------+---------------+
       |    sadb_comb_auth_minbits     |     sadb_comb_auth_maxbits    |
       +---------------+---------------+---------------+---------------+
       |   sadb_comb_encrypt_minbits   |    sadb_comb_encrypt_maxbits  |
       +---------------+---------------+---------------+---------------+
       |                       sadb_comb_reserved                      |
       +---------------+---------------+---------------+---------------+
       |                   sadb_comb_soft_allocations                  |
       +---------------+---------------+---------------+---------------+
       |                   sadb_comb_hard_allocations                  |
       +---------------+---------------+---------------+---------------+
       |                      sadb_comb_soft_bytes                     |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+
       |                      sadb_comb_hard_bytes                     |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+
       |                     sadb_comb_soft_addtime                    |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+
       +---------------+---------------+---------------+---------------+
       |                     sadb_comb_hard_addtime                    |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+
       |                     sadb_comb_soft_usetime                    |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+
       |                     sadb_comb_hard_usetime                    |
       |                           (64 bits)                           |
       +---------------+---------------+---------------+---------------+

The Supported Algorithms extension ๐Ÿงช

       +---------------+---------------+---------------+---------------+
       |      sadb_supported_len       |     sadb_supported_exttype    |
       +---------------+---------------+---------------+---------------+
       |                    sadb_supported_reserved                    |
       +---------------+---------------+---------------+---------------+

This one is followed by one or more Algorithm Descriptors, which look like this:

       +---------------+---------------+---------------+---------------+
       |  sadb_alg_id  | sadb_alg_ivlen|       sadb_alg_minbits        |
       +---------------+---------------+---------------+---------------+
       |        sadb_alg_maxbits       |       sadb_alg_reserved       |
       +---------------+---------------+---------------+---------------+

The SPI Range extension ๐Ÿฆ‹

       +---------------+---------------+---------------+---------------+
       |       sadb_spirange_len       |     sadb_spirange_exttype     |
       +---------------+---------------+---------------+---------------+
       |                      sadb_spirange_min                        |
       +---------------+---------------+---------------+---------------+
       |                      sadb_spirange_max                        |
       +---------------+---------------+---------------+---------------+
       |                    sadb_spirange_reserved                     |
       +---------------+---------------+---------------+---------------+

3. Symbolic Names ๐Ÿงš

This section is basically the glossary for PF_KEY ๐Ÿ“–. It defines the various symbols used with PF_KEY and what each symbol means. Applications MUST use the symbolic names in order to be portable, bestie, no skipping. The numbers shown are for illustration only, unless it's explicitly stated otherwise. The numeric definition MAY vary on other systems ๐ŸŒˆ, but the symbolic name MUST be kept the same for all conforming implementations, full stop ๐Ÿ’œ.

3.1 Message Types

okay so this is the menu of PF_KEY message types, and they all live in <net/pfkeyv2.h> ๐Ÿ’… Here's the full list:

#define SADB_RESERVED    0
#define SADB_GETSPI      1
#define SADB_UPDATE      2
#define SADB_ADD         3
#define SADB_DELETE      4
#define SADB_GET         5
#define SADB_ACQUIRE     6
#define SADB_REGISTER    7
#define SADB_EXPIRE      8
#define SADB_FLUSH       9

#define SADB_DUMP        10   /* not used normally */

#define SADB_MAX         10

Every message has a behavior ๐Ÿฆ‹ That's just a fancy way of saying where the first message travels (like user to kernel) and what's supposed to happen after that. Messages get drawn like this:

<base, REQUIRED EXTENSION, REQ., (OPTIONAL EXT.,) (OPT)>

The SA extension is sometimes only used for its SPI field. If all the other fields MUST be ignored, that gets written as "SA(*)" ๐ŸŽ€

Lifetime extensions show up as one to three letters after the word "lifetime": (H)ARD, (S)OFT, and (C)URRENT โณ

Address extensions use one to three letters after the word "address": (S)RC, (D)ST, (P)ROXY ๐Ÿ“

NOTE: Some security association types don't use a source address to identify the SA, while others do. That can cause EEXIST errors for some SA types where others don't report collisions. It's expected that application authors know enough about the underlying security association types to understand these differences. Read this one twice, bestie ๐Ÿ™ˆ

Key extensions use one or two letters after the word "key": (A)UTH and (E)NCRYPT ๐Ÿ”‘

Identity extensions use one or two letters after the word "identity": (S)RC and (D)ST ๐Ÿชช

If something goes wrong, only the base header comes back ๐Ÿ’€

Also, any standard error could be returned for any message ๐Ÿ˜ญ Usually it's one of the errors listed for that specific message, or one of these:

EINVAL  Various message improprieties, including SPI ranges
        that are malformed.
ENOMEM  Needed memory was not available.
ENOBUFS Needed memory was not available.
EMSGSIZ The message exceeds the maximum length allowed.

3.1.1 SADB_GETSPI

the SADB_GETSPI message lets a process grab a unique SPI value for a given security association type, source address, and destination address โœจ Sending this and then an SADB_UPDATE is one way to create a security association. SADB_ADD is the other way ๐Ÿงš

The process puts the type in the base header and the source and destination addresses in the address extension. If the SADB_GETSPI is answering a kernel-generated SADB_ACQUIRE, the sadb_msg_seq MUST be the same as the one in the SADB_ACQUIRE message ๐Ÿ”

The application can also request a particular SPI. It does that by having the kernel pick from a range of SPI values using the SPI range extension. If you want one single SPI value checked, just set the high and low values to be equal ๐ŸŽฏ Range specification matters a lot, because the kernel can allocate an SPI based on what it already knows is in use ๐Ÿง 

The kernel sends the same message back with the allocated SPI stored in the spi field of an association extension. That allocated SPI (plus the destination address) points to a LARVAL security association. An SADB_UPDATE can come along later to add an entry with the SPI you asked for ๐ŸŒธ

It is recommended that associations created with SADB_GETSPI SHOULD be deleted automatically after a fixed amount of time if no SADB_UPDATE message updates them โฐ That stops the SA storage from getting cluttered with larval associations ๐Ÿงน

The message behavior of SADB_GETSPI goes like this:

Send an SADB_GETSPI message from a user process to the kernel.

<base, address, SPI range>

The kernel returns the SADB_GETSPI message to all listening processes.

<base, SA(*), address(SD)>

Errors:

EEXIST  Requested SPI or SPI range is not available or already
        used.

3.1.2 SADB_UPDATE Message

the SADB_UPDATE message lets a process update an existing Security Association ๐Ÿ”„ SADB_GETSPI can't set certain parameters, so this message is needed to fully form the SADB_SASTATE_LARVAL SA that SADB_GETSPI made ๐Ÿงฉ The format is a base header, then an association header, then maybe a few extension headers ๐Ÿ“ฆ The kernel finds the SA with the same type, spi, source address, and destination address in the message, then updates it with the SADB_UPDATE content ๐Ÿ”

The kernel MAY disallow SADB_UPDATE from succeeding unless the message comes from the same socket that created the SA ๐Ÿšช That enforcement seriously cuts down on accidental changes to an in-use SA ๐Ÿ›ก๏ธ Sketchy trusted parties could still send an SADB_FLUSH or SADB_DELETE, but deleting SAs is easier to spot and less likely to happen by accident than a wrong SADB_UPDATE ๐Ÿ‘€ The counter-argument is the case where a user-space key management app crashes and restarts. The new instance won't have the same socket as the creator of the SA ๐Ÿ˜ฌ

The kernel MUST sanity check all significant values submitted in an SADB_UPDATE message before changing the SA in its database, and MUST return EINVAL if any of the values are invalid ๐Ÿšจ Checks that should be performed include DES key parity bits, key length checking, checks for keys known to be weak for the specified algorithm, and checks for flags or parameters known to be incompatible with the specified algorithm ๐Ÿ”

Only SADB_SASTATE_MATURE SAs may be submitted in an SADB_UPDATE message ๐ŸŒฑ If the original SA is an SADB_SASTATE_LARVAL SA, then any value in the SA may be changed except for the source address, destination address, and SPI ๐ŸŒท If the original SA is an SADB_SASTATE_DEAD SA, any attempt to perform an SADB_UPDATE on it MUST return EINVAL ๐Ÿ’€ It's not valid for established keying or algorithm information to change without the SPI changing, because that would need a brand-new SA instead of editing an existing one ๐Ÿ™…โ€โ™€๏ธ Once keying and algorithm info is negotiated, address and identity info is locked for the SA ๐Ÿ”’ So if the original SA is a SADB_SASTATE_MATURE or DYING SA, only the sadb_sa_state field in the SA header and the lifetimes (hard, soft, and current) may be changed, and any attempt to change other values MUST result in an error return of EINVAL ๐Ÿ™ƒ

The message behavior of SADB_UPDATE goes like this:

Send an SADB_UPDATE message from a user process to the kernel.

<base, SA, (lifetime(HSC),) address(SD), (address(P),)
  key(AE), (identity(SD),) (sensitivity)>

The kernel returns the SADB_UPDATE message to all listening processes.

<base, SA, (lifetime(HSC),) address(SD), (address(P),)
  (identity(SD),) (sensitivity)>

The keying material is NOT returned in the kernel's message to listening sockets, since listeners might not have the privileges to see it ๐Ÿ™Š (Lowercase version: the keying material is not returned, because listeners might not have the privileges to see it ๐Ÿ”)

Errors:

ESRCH   The security association to be updated was not found.
EINVAL  In addition to other possible causes, this error is
        returned if sanity checking on the SA values (such
        as the keys) fails.
EACCES  Insufficient privilege to update entry. The socket
        issuing the SADB_UPDATE is not creator of the entry
        to be updated.

3.1.3 SADB_ADD

the SADB_ADD message is almost the same as SADB_UPDATE, except it doesn't need a previous SADB_GETSPI call ๐ŸŽ‰ It's used in manual keying apps, and in other cases where you already know the SPI is unique ๐Ÿ—๏ธ

SADB_ADD also shows up when negotiation is finished and the second of a pair of associations gets added ๐Ÿค The SPI for that association was decided by the peer machine. The sadb_msg_seq MUST be set to the value from a kernel-generated SADB_ACQUIRE, so that both associations in the pair are bound to the same ACQUIRE request ๐Ÿ”—

The kernel MUST sanity check all used fields in the SA submitted in an SADB_ADD message before adding the SA to its database, and MUST return EINVAL if any of the values are invalid ๐Ÿง

Only SADB_SASTATE_MATURE SAs may be submitted in an SADB_ADD message ๐ŸŒŸ SADB_SASTATE_LARVAL SAs come from SADB_GETSPI, and it doesn't make sense to add a new SA in the DYING or SADB_SASTATE_DEAD state ๐Ÿ™…โ€โ™€๏ธ So the sadb_sa_state field of every submitted SA MUST be SADB_SASTATE_MATURE, and the kernel MUST return an error if that isn't true ๐Ÿšซ

The message behavior of SADB_ADD goes like this:

Send an SADB_ADD message from a user process to the kernel.

<base, SA, (lifetime(HS),) address(SD), (address(P),)
  key(AE), (identity(SD),) (sensitivity)>

The kernel returns the SADB_ADD message to all listening processes.

<base, SA, (lifetime(HS),) address(SD), (identity(SD),)
  (sensitivity)>

The keying material is not returned on the kernel's message to listening sockets, because listeners may not have the privileges to see it ๐Ÿ”

Errors:

EEXIST  The security association that was to be added already
        exists.
EINVAL  In addition to other possible causes, this error is
        returned if sanity checking on the SA values (such
        as the keys) fails.

3.1.4 SADB_DELETE

the SADB_DELETE message tells the kernel to delete a Security Association from the key table ๐Ÿ—‘๏ธ It's the base header, then the association, then the source and destination sockaddrs in the address extension ๐Ÿ“‡ The kernel deletes the SA that matches the type, spi, source address, and destination address in the message ๐ŸŽฏ

The message behavior for SADB_DELETE is as follows:

Send an SADB_DELETE message from a user process to the kernel.

<base, SA(*), address(SD)>

The kernel returns the SADB_DELETE message to all listening processes.

<base, SA(*), address(SD)>

3.1.5 SADB_GET

the SADB_GET message lets a process get a copy of a Security Association from the kernel's key table ๐Ÿ‘€ It's the base header followed by the relevant extension fields. The SA that matches the type, spi, source address, and destination address gets returned ๐Ÿ’Œ

The message behavior of SADB_GET goes like this:

Send an SADB_GET message from a user process to the kernel.

<base, SA(*), address(SD)>

The kernel returns the SADB_GET message to the socket that sent the SADB_GET message.

<base, SA, (lifetime(HSC),) address(SD), (address(P),) key(AE),
  (identity(SD),) (sensitivity)>

Errors:

ESRCH   The sought security association was not found.

3.1.6 SADB_ACQUIRE โœจ

okay so SADB_ACQUIRE is basically the kernel tapping the shoulder of every registered key socket listener like "hey, someone needs a security association, can you help?" ๐Ÿ’… It's usually sent only by the kernel, but application-level consumers of security associations can send it too, like an OSPFv2 implementation that uses OSPF security ๐Ÿฆ‹ The message is a base header plus an address extension, possibly an identity extension, and a proposal extension. The proposal is the fun part: it holds a list of desirable algorithms to fall back on if the ones in the base header aren't available ๐ŸŽ€

The values in the base header, and in the security association data after it, tell the listening process what kind of Security Association it should try to acquire ๐Ÿ”ฅ Now here's the big one ๐Ÿ‘‘ If the message comes from the kernel (meaning sadb_msg_pid is 0), the sadb_msg_seq number MUST be used by a subsequent SADB_GETSPI and SADB_UPDATE, or a subsequent SADB_ADD message, to bind a security association to the request. This is what stops the race condition where two TCP connections between the same two IP hosts each need their own unique association and one ends up stealing the other's. Iconic protection, no cap ๐Ÿ’€ And the sadb_msg_errno and sadb_msg_state fields should be ignored by the listening process ๐Ÿ™ˆ

SADB_ACQUIRE is usually triggered by an outbound packet that needs security but has no matching Security Association sitting in the key table ๐Ÿ˜ญ If the packet can be protected by more than one algorithm or combination of options, the SADB_ACQUIRE message MUST order the preference of possibilities in the Proposal extension. Ranking your options, very organised, very slay ๐Ÿ’œ

There are three messaging behaviours for SADB_ACQUIRE. The first is when the kernel itself needs a security association (for IPsec, for example) ๐ŸŒˆ

  The kernel sends an SADB_ACQUIRE message to registered sockets.

     <base, address(SD), (address(P)), (identity(SD),) (sensitivity,)
       proposal>

     NOTE:   The address(SD) extensions MUST have the port fields
             filled in with the port numbers of the session requiring
             keys if appropriate.

The second is when key management fails for some reason. Then the kernel can send an ACQUIRE message with the same sadb_msg_seq as the original ACQUIRE, carrying a non-zero errno ๐Ÿ˜ฌ

  Send an SADB_ACQUIRE to indicate key management failure.

  <base>

The third is when an application-layer consumer of security associations (like an OSPFv2 or RIPv2 daemon) needs one ๐Ÿงš

  Send an SADB_ACQUIRE message from a user process to the kernel.

  <base, address(SD), (address(P),) (identity(SD),) (sensitivity,)
    proposal>

  The kernel returns an SADB_ACQUIRE message to registered
    sockets.

  <base, address(SD), (address(P),) (identity(SD),) (sensitivity,)
    proposal>

  The user-level consumer waits for an SADB_UPDATE or SADB_ADD
  message for its particular type, and then can use that
  association by using SADB_GET messages.

The errors you can run into are EINVAL, which means the acquire request was invalid, and EPROTONOSUPPORT, which means no key management application has registered with the Key Engine as able to obtain the requested SA type, so it just can't be acquired ๐Ÿฅบ

Errors:
    EINVAL  Invalid acquire request.
    EPROTONOSUPPORT   No KM application has registered with the Key
            Engine as being able to obtain the requested SA type, so
            the requested SA cannot be acquired.

3.1.7 SADB_REGISTER ๐ŸŒธ

SADB_REGISTER is how an application says "hi, my key socket can go get new security associations for the kernel" ๐Ÿซถ It lets a socket receive SADB_ACQUIRE messages for the type of security association named in sadb_msg_satype. The application puts the type it can acquire in the type field of its register message. If an app can acquire multiple types, it MUST register each type in a separate message, no bundling allowed ๐Ÿ™…โ€โ™€๏ธ Only the base header is needed for the register message. Key management applications MAY register for a type the kernel doesn't know about, because the consumer might be in user-space (OSPFv2 security being the classic example) ๐Ÿ“

The reply to SADB_REGISTER carries a supported algorithm extension. That field is an array of supported algorithms, one per octet, so key management apps can see exactly which algorithms the kernel supports โœจ

In an environment where algorithms can be dynamically loaded and unloaded, an asynchronous SADB_REGISTER reply MAY be generated. And the list of supported algorithms MUST be a complete list, so the application can spot omissions or additions. Full transparency, bestie ๐Ÿ’–

Here's the messaging behaviour for SADB_REGISTER ๐ŸŽ€

  Send an SADB_REGISTER message from a user process to the kernel.

  <base>

  The kernel returns an SADB_REGISTER message to registered
  sockets, with algorithm types supported by the kernel being
  indicated in the supported algorithms field.

  NOTE:  This message may arrive asynchronously due to an
         algorithm being loaded or unloaded into a dynamically
         linked kernel.

  <base, supported>

3.1.8 SADB_EXPIRE Message โฐ

The operating system kernel is in charge of tracking SA expirations for security protocols implemented inside the kernel ๐Ÿง  If the soft limit or hard limit of a Security Association has expired for a security protocol that lives in the kernel, the kernel MUST issue an SADB_EXPIRE message to all key socket listeners. If the soft limit or hard limit has expired for a user-level security protocol, the user-level protocol SHOULD issue an SADB_EXPIRE message ๐ŸŒˆ

The base header carries the security association information, followed by the source sockaddr, the destination sockaddr, and (if present) the internal sockaddr, plus (if present) one or both compartment bitmaps ๐Ÿฆ‹

The lifetime extension in an SADB_EXPIRE message is super important because it tells you which lifetime expired ๐Ÿ˜ญ If a HARD lifetime extension is included, the HARD lifetime expired. That means the association MAY already have been deleted from the SADB ๐Ÿ’€ If a SOFT lifetime extension is included, the SOFT lifetime expired. The CURRENT lifetime extension shows the current status, and comparing it to the HARD or SOFT lifetime tells you which limit was reached ๐Ÿ” HARD lifetimes MUST take precedence over SOFT lifetimes. So if the HARD and SOFT lifetimes are the same, the HARD lifetime is the one that shows up on the EXPIRE message ๐Ÿ‘‘ The pathological case where HARD lifetimes are shorter than SOFT lifetimes is handled so that the SOFT lifetime never expires. Chaotic, but handled ๐Ÿ™ƒ

Here's the messaging behaviour ๐Ÿ’…

      The kernel sends an SADB_EXPIRE message to all listeners when
      the soft limit of a security association has been expired.

      <base, SA, lifetime(C and one of HS), address(SD)>

Note that the SADB_EXPIRE message is ONLY sent by the kernel to the KMd. It's a one-way informational message, so it doesn't get a reply ๐Ÿซข


3.1.9 SADB_FLUSH ๐Ÿงน

The SADB_FLUSH message tells the kernel to delete all entries in its key table for a certain sadb_msg_satype ๐Ÿ’ซ Only the base header is required for a flush message. If sadb_msg_satype has a specific value, only associations of that type get deleted. If it's set to SADB_SATYPE_UNSPEC, ALL associations get deleted. Whole-table clear-out, iconic ๐Ÿ’€

Here's the messaging behaviour for SADB_FLUSH ๐ŸŽ€

      Send an SADB_FLUSH message from a user process to the kernel.

      <base>

      The kernel will return an SADB_FLUSH message to all listening
      sockets.

      <base>

      The reply message happens only after the actual flushing
      of security associations has been attempted.

3.1.10 SADB_DUMP ๐Ÿ—‚๏ธ

The SADB_DUMP message makes the kernel dump the operating system's entire Key Table to the requesting key socket ๐Ÿ“œ Same deal as SADB_FLUSH: if a sadb_msg_satype value is in the message, only associations of that type get dumped. If SADB_SATYPE_UNSPEC is specified, everything gets dumped. Each Security Association comes back in its own SADB_DUMP message. A SADB_DUMP message with a sadb_seq field of zero marks the end of the dump transaction ๐Ÿ The dump message is for debugging purposes only and is not intended for production use, so please don't build your whole life on it ๐Ÿ™ˆ

Support for the dump message MAY be discontinued in future versions of PF_KEY. Key management applications MUST NOT depend on this message for basic operation. Not a load-bearing wall, bestie ๐Ÿงฑ

Here's the messaging behaviour for SADB_DUMP ๐ŸŒท

      Send an SADB_DUMP message from a user process to the kernel.

      <base>

      Several SADB_DUMP messages will return from the kernel to the
      sending socket.

      <base, SA, (lifetime (HSC),) address(SD), (address(P),)
        key(AE), (identity(SD),) (sensitivity)>

3.2 Security Association Flags ๐Ÿšฉ

The Security Association's flags are a bitmask field. These flags also show up in a combination that's part of a PROPOSAL extension. Use the symbolic definitions below so your applications stay portable ๐Ÿ’œ

  #define SADB_SAFLAGS_PFS 1    /* perfect forward secrecy */

The SADB_SAFLAGS_PFS flag tells key management that this association should have perfect forward secrecy in its key. In other words, any given session key can't be figured out by cryptanalysis of previous session keys or some master key. Secrets stay secret, periodt ๐Ÿ”โœจ


3.3 Security Association States ๐ŸŒ™

The security association state field is an integer that describes the states of a security association. They are:

  #define SADB_SASTATE_LARVAL   0
  #define SADB_SASTATE_MATURE   1
  #define SADB_SASTATE_DYING    2
  #define SADB_SASTATE_DEAD     3

  #define SADB_SASTATE_MAX      3

A SADB_SASTATE_LARVAL security association is one created by the SADB_GETSPI message ๐Ÿ› A SADB_SASTATE_MATURE association is one that was updated with the SADB_UPDATE message or added with the SADB_ADD message ๐Ÿฆ‹ A DYING association is one whose soft lifetime has expired ๐Ÿ˜ณ A SADB_SASTATE_DEAD association is one whose hard lifetime has expired but hasn't been reaped by system garbage collection yet ๐Ÿ’€

If a consumer of security associations has to extend an association beyond its normal lifetime (for example, OSPF Security), it MUST only set the soft lifetime for an association. Don't go mixing up your lifetimes, bestie ๐Ÿ™…โ€โ™€๏ธ๐Ÿ’–

3.4 Security Association Types ๐Ÿฆ‹

Okay so this one tells you what kind of Security Association this message is about, bestie ๐Ÿ’… The symbolic names stay the same on every implementation, so Applications SHOULD use the symbolic name if they want to be portable across different implementations โœจ You'll find them all defined in <net/pfkeyv2.h>.

  #define SADB_SATYPE_UNSPEC        0

  #define SADB_SATYPE_AH            2  /* RFC-1826 */
  #define SADB_SATYPE_ESP           3  /* RFC-1827 */

  #define SADB_SATYPE_RSVP          5  /* RSVP Authentication */
  #define SADB_SATYPE_OSPFV2        6  /* OSPFv2 Authentication */
  #define SADB_SATYPE_RIPV2         7  /* RIPv2 Authentication */
  #define SADB_SATYPE_MIP           8  /* Mobile IP Auth. */

  #define SADB_SATYPE_MAX           8

SADB_SATYPE_UNSPEC is only there for completeness ๐Ÿ™ˆ It means "no specific type of security association," and it is never used with PF_KEY SAs, so don't even try it.

SADB_SATYPE_AH is for the IP Authentication Header [Atk95b].

SADB_SATYPE_ESP is for the IP Encapsulating Security Payload [Atk95c].

SADB_SATYPE_RSVP is for the RSVP Integrity Object ๐ŸŒธ

SADB_SATYPE_OSPFV2 is for OSPFv2 Cryptographic authentication [Moy98].

SADB_SATYPE_RIPV2 is for RIPv2 Cryptographic authentication [BA97].

SADB_SATYPE_MIP is for Mobile IP's authentication extensions [Per97].

SADB_SATYPE_MAX is always set to the highest valid numeric value. Main character energy ๐Ÿ‘‘

3.5 Algorithm Types ๐Ÿ”

Okay, the algorithm type only makes sense in the context of the Security Association type we just covered, so keep that in your head ๐Ÿง  The numeric value might be different between implementations, but the symbolic name MUST NOT vary between implementations. Like, never. Applications should use the symbolic name so they stay portable ๐Ÿ’–

Some of the algorithm types below might not be standardized, or might get deprecated later on. If you need an assignment for a symbolic name, contact the authors ๐Ÿฆ‹

The symbols below live in <net/pfkeyv2.h>:

      /* Authentication algorithms */
      #define SADB_AALG_NONE          0
      #define SADB_AALG_MD5HMAC       2
      #define SADB_AALG_SHA1HMAC      3
      #define SADB_AALG_MAX           3

      /* Encryption algorithms */
      #define SADB_EALG_NONE          0
      #define SADB_EALG_DESCBC        2
      #define SADB_EALG_3DESCBC       3
      #define SADB_EALG_NULL          11
      #define SADB_EALG_MAX           11

The algorithm for SADB_AALG_MD5_HMAC is defined in [MG98a]. The algorithm for SADB_AALG_SHA1HMAC is defined in [MG98b]. The algorithm for SADB_EALG_DESCBC is defined in [MD98]. SADB_EALG_NULL is the NULL encryption algorithm, defined in [GK98] ๐ŸŒˆ

The SADB_EALG_NONE value is not to be used in any security association except those which have no possible encryption algorithm in them (e.g. IPsec AH). Don't put it where it doesn't belong, periodt ๐Ÿ™…โ€โ™€๏ธ

3.6 Extension Header Values โœจ

Quick recap of the extension header values, because we love a good refresher ๐Ÿ’œ

          #define SADB_EXT_RESERVED          0
          #define SADB_EXT_SA                1
          #define SADB_EXT_LIFETIME_CURRENT  2
          #define SADB_EXT_LIFETIME_HARD     3
          #define SADB_EXT_LIFETIME_SOFT     4
          #define SADB_EXT_ADDRESS_SRC       5
          #define SADB_EXT_ADDRESS_DST       6
          #define SADB_EXT_ADDRESS_PROXY     7
          #define SADB_EXT_KEY_AUTH          8
          #define SADB_EXT_KEY_ENCRYPT       9
          #define SADB_EXT_IDENTITY_SRC      10
          #define SADB_EXT_IDENTITY_DST      11
          #define SADB_EXT_SENSITIVITY       12
          #define SADB_EXT_PROPOSAL          13
          #define SADB_EXT_SUPPORTED_AUTH    14
          #define SADB_EXT_SUPPORTED_ENCRYPT 15
          #define SADB_EXT_SPIRANGE          16

          #define SADB_EXT_MAX               16

3.7 Identity Extension Values ๐Ÿชช

Each identity can have a certain type, and this is the list of them ๐ŸŽ€

          #define SADB_IDENTTYPE_RESERVED  0
          #define SADB_IDENTTYPE_PREFIX    1
          #define SADB_IDENTTYPE_FQDN      2
          #define SADB_IDENTTYPE_USERFQDN  3

          #define SADB_IDENTTYPE_MAX       3

Okay, the PREFIX identity is a network address, then a forward slash, then a prefix length ๐ŸŒธ The address is in a printable numeric form that fits its protocol family. The prefix length is a decimal number that's greater than or equal to zero and less than the number of bits in the network address. It tells you how many bits of the address actually count (the significant ones). All bits in the network address that are not significant MUST be set to zero. Also, implementations MUST parse the contents of the printable address into a binary form for comparison purposes, because lots of different strings can mean the same address. Some allow leading zeros, some have letters that aren't case sensitive, and it's a whole thing ๐Ÿ™ƒ

Examples of PREFIX identities are "199.33.248.64/27" and "3ffe::1/128". And if the source or destination identity is a PREFIX identity, the source or destination address for the SA (respectively) MUST be within that prefix. The sadb_ident_id field is zeroed for these identity types ๐Ÿงธ

The FQDN identity string holds a fully qualified domain name. An example is "ministry-of-truth.inner.net" ๐Ÿ‘€ The sadb_ident_id field is zeroed for these identity types too.

The UserFQDN identity is a text string shaped like the stuff used for Internet email. The syntax is a text username, then the "@" character, then the appropriate fully qualified domain name. So it specifies both a username and an associated FQDN ๐Ÿ’Œ There is no requirement that this string be a mailbox that works for SMTP or any other email use, so don't assume it'll deliver anything. This identity is handy for protocols that do user-oriented keying. It's also super convenient because the DNS Security extensions can distribute signed public key values by hooking KEY and SIG records up to an appropriate MB DNS record ๐Ÿ”‘ An example UserFQDN identity is "julia@ministry-of-love.inner.net". The sadb_ident_id field gets used to hold a POSIX user id when there's no identity string at all, so a user-level app can call getpwuid{,_r}() to get a textual user login id ๐Ÿงš If a string is present, it SHOULD match the numeric value in the sadb_ident_id field. And if it doesn't match, the string SHOULD override the numeric value ๐Ÿ’…

3.8 Sensitivity Extension Values ๐ŸŒˆ

The only field currently defined in the sensitivity extension is sadb_sens_dpd, which represents the data protection domain ๐Ÿซถ All the other data in the sensitivity extension is based off the sadb_sens_dpd value.

The DP/DOI is defined to be the same as the "Labeled Domain Identifier Value" of the IP Security DOI specification [Pip98]. As that specification notes, values in the range 0x80000000 to 0xffffffff (inclusive) are reserved for private use, and values in the range 0x00000001 through 0x7fffffff are assigned by IANA. The all-zeros DP/DOI value is permanently reserved to mean that "no DP/DOI is in use" โญ

3.9 Proposal Extension Values ๐Ÿ“œ

These are already covered in the Algorithm Types and Security Association Flags sections, so go check those out if you missed them ๐Ÿ’–

4 Future Directions ๐Ÿ”ฎ

The current specification for the Sensitivity and Integrity Labels is believed to be general enough ๐Ÿคž But if a case comes up that the current specification can't handle, that might cause a change in a future version of PF_KEY.

Similarly, PF_KEY might need extensions to work with other kinds of Security Associations in the future. It is strongly desirable for those extensions to be made in a backwards-compatible manner, should they ever be needed ๐Ÿ™

When more experience is gained with certificate management, the IDENTITY extension might have to be revisited so it can allow a finer grained selection of certificate identities ๐ŸŽ€

5. Examples ๐Ÿฆ‹

The following examples show how PF_KEY is used. The first example is an IP Security example, where the consumer of the security associations is inside an operating system kernel. The second example is an OSPF Security example, which shows a user-level consumer of security associations. The third example covers things the first two don't mention. A real system might closely follow one of these examples, or just take parts of them. These examples are purely illustrative, and they are not meant to mandate a particular implementation method โœจ

5.1 Simple IP Security Example ๐ŸŽ€

okay so picture this: the Key Management daemon (KMd) wakes up and it has to tell PF_KEY that it's ready to handle messages for the two IPsec services, AH and ESP. It does that by sending down two SADB_REGISTER messages. ๐Ÿ’…

                 +---------------+    +-------------+
                 |Key Mgmt Daemon|    | Application |
                 +---------------+    +-------------+
                   |           |     /
                   |           |    /
                   |           |    |              Applications
           ======[PF_KEY]====[PF_INET]==========================
                   |           |    |              OS Kernel
           +------------+   +-----------------+
           | Key Engine |   | TCP/IP,         |
           |  or  SADB  |---| including IPsec |
           +------------+   |                 |
                            +-----------------+
KMd->Kernel:         SADB_REGISTER for ESP
Kernel->Registered:  SADB_REGISTER for ESP, Supported Algorithms
KMd->Kernel:         SADB_REGISTER for AH
Kernel->Registered:  SADB_REGISTER for AH, Supported Algorithms

every REGISTER message gets a reply sent to all the PF_KEY sockets registered for ESP and AH respectively, and that includes the one that asked ๐Ÿ™ˆ

now let's say there are no security associations around for IPsec to use yet. then a network app starts sending data (like a TCP SYN, classic). because of policy, or just because the app asked, the kernel IPsec module needs an AH security association for this data. there isn't one, so this message gets generated:

Kernel->Registered:  SADB_ACQUIRE for AH, addrs, ID, sens,
                     proposals

the KMd reads the ACQUIRE message, especially the sadb_msg_seq number. before it starts negotiating, it sends down an SADB_GETSPI message with the sadb_msg_seq number matching the one from the ACQUIRE. the kernel sends the GETSPI results to all listening sockets. โœจ

KMd->Kernel:         SADB_GETSPI for AH, addr, SPI range
Kernel->All:         SADB_GETSPI for AH, assoc, addrs

the KMd can run a second GETSPI if it needs SPI values for both directions of IPsec. once it has an SPI for at least one of the security associations, it starts negotiating. after it derives keying material and works out the other parameters, it sends down one (or more) SADB_UPDATE messages with the same sadb_msg_seq value. ๐Ÿฆ‹

if the KMd hits any error at all during negotiation, it can send down:

KMd->Kernel:         SADB_ACQUIRE for AH, assoc (with an error)
Kernel->All:         SADB_ACQUIRE for AH, assoc (same error)

but if it succeeds, it can send this instead:

KMd->Kernel:         SADB_UPDATE for AH, assoc, addrs, keys,
                     <etc.>
Kernel->All:         SADB_UPDATE for AH, assoc, addrs, <etc.>

the UPDATE results (minus the actual keys) go out to all listening sockets. if only one SPI value was figured out locally, the other SPI has to be added with an SADB_ADD message, since IPsec SAs are unidirectional. ๐Ÿ˜ญ

KMd->Kernel:         SADB_ADD for AH, assoc, addrs, keys, <etc.>
Kernel->All:         SADB_ADD for AH, assoc, addrs, <etc.>

if one of the extensions that got passed down was a Lifetime extension, then at some point an SADB_EXPIRE message might show up when one of the lifetimes runs out. ๐Ÿ’€

Kernel->All:         SADB_EXPIRE for AH, assoc, addrs,
                     Hard or Soft, Current, <etc.>

the KMd can take that as a hint to start negotiating. or, if it has a say in policy, it can send an SADB_UPDATE down with a lifetime extension. ๐Ÿ‘‘


5.2 Proxy IP Security Example ๐ŸŒธ

lots of people want to run IP Security in a "proxy" or "firewall" setup, where an in-between system handles the security stuff for "inside" hosts. in those setups, the middle systems can use PF_KEY to talk to key management apps almost exactly like they would if they were the real endpoints. the messaging in these cases is the same as the last example, it's just the address info that's a little different. ๐Ÿงš

here's the setup:

                 A ========= B --------- C

Key:
           A           "outside" host that implements IPsec
           B           "firewall" that implements IPsec
           C           "inside" host that does not implement IPsec

           ===         IP_{A<->B} ESP [ IP_{A<->C} ULP ]
           ---         IP_{A<->C} ULP

A is one system that wants to talk to the "inside" system C. B is a "firewall" sitting between C and the outside world, and it does ESP and tunneling on C's behalf. A figures out that it needs to send traffic to C through B using methods not covered here (using the DNS' KX record might be one way to find that out). ๐Ÿซถ

for packets flowing left to right, A and B need an IPsec Security Association with:

  • SA type of ESP tunnel-mode
  • Source Identity that dominates A (e.g. A's address)
  • Destination Identity that dominates B (e.g. B's address)
  • Source Address of A
  • Destination Address of B

for packets flowing right to left, A and B need an IPsec Security Association with:

  • SA type of ESP tunnel-mode
  • Source Identity that dominates C
  • Destination Identity that dominates A
  • Source Address of B
  • Destination Address of A
  • Proxy Address of C

for this second SA (the one for packets flowing from C toward A), node A MUST verify that the inner source address is dominated by the Source Identity for the SA used with those packets. if node A doesn't do this check, an adversary could forge packets with any Source Identity they like and beat the packet origin protections that IPsec provides. this is serious, bestie, do not skip it ๐Ÿšจ

now let's make it a bit more complicated:

          A_1 --|                  |-- D_1
                |--- B ====== C ---|
          A_2 --|                  |-- D_2

Key:
          A_n     "inside" host on net 1 that does not do IPsec.
          B       "firewall" for net 1 that supports IPsec.
          C       "firewall" for net 2 that supports IPsec.
          D_n     "inside" host on net 2 that does not do IPsec.
          ===     IP_{B<->C} ESP [ IP_{A<->C} ULP ]
          ---     IP_{A<->C} ULP

for A_1 to send a packet to D_1, B and C need an SA with:

SA Type of ESP
Source Identity that dominates A_1
Destination Identity that dominates C
Source Address of B
Destination Address of C
Proxy Address of A_1

and for D_1 to send a packet back to A_1, C and B need an SA with:

SA Type of ESP Tunnel-mode
Source Identity that dominates D_1
Destination Identity that dominates B
Source Address of C
Destination Address of B
Proxy Address of D_1

A_2 and D_2 could be swapped in for A_1 and D_1 here, no problem. whether an SA is tied to a particular pair of ends (or a group of those pairs) is a policy decision made by B and/or C, and it isn't necessarily something key management decides. โœจ the same check of the Source Identity against the inner source IP address MUST also be performed in this case, for the same reason as before. ๐Ÿ’–

if you want a deeper dive into using IP Security in complicated setups, check out [Atk97]. ๐Ÿ“š

NOTE: The notion of identity domination might be unfamiliar. Let H represent some node. Let Hn represent H's fully qualified domain name. Let Ha represent the IP address of H. Let Hs represent the IP subnet containing Ha. Let Hd represent a fully qualified domain name that is a parent of the fully qualified domain name of H. Let M be a UserFQDN identity whose right-hand part is Hn or Ha. Any of M, Hn, Ha, Hs, and Hd is considered to dominate H in the example above. Hs dominates any node having an IP address within the IP address range represented by Hs. Hd dominates any node having a fully qualified domain name within underneath Hd. ๐Ÿค“

5.3 OSPF Security Example ๐Ÿฆ‹

       +---------------+    +-------------+
       |Key Mgmt Daemon|    | OSPF daemon |
       +---------------+    +-------------+
         |           |     /    /        |
         |    /------|----+    /         |
         |   /       |    +---+          |           Applications
 ======[PF_KEY]====[PF_INET]===========[PF_ROUTE]================
         |           |    |              |           OS Kernel
 +------------+   +-----------------+  +---------+
 | Key Engine |   | TCP/IP,         |  | Routing |
 |  or  SADB  |---| including IPsec |--| Table   |
 +------------+   |                 |  +---------+
                  +-----------------+

same as the previous examples, the KMd registers itself with the Key Engine through PF_KEY. even though the thing consuming the security associations lives in user-space, the PF_KEY and Key Engine implementation is smart enough to store SAs and relay messages. ๐Ÿง 

when the OSPF daemon needs to talk securely with its peers, it does an SADB_GET message to grab the right association:

OSPFd->Kernel:       SADB_GET of OSPF, assoc, addrs
Kernel->OSPFd:       SADB_GET of OSPF, assoc, addrs, keys, <etc.>

if that GET fails, the OSPFd might need to acquire a new security association. here's how that goes:

OSPFd->Kernel:       SADB_ACQUIRE of OSPF, addrs, <ID, sens,>
                     proposal
Kernel->Registered:  SADB_ACQUIRE of OSPF, <same as sent message>

the KMd sees this and does stuff similar to the previous example. the one difference is that when the UPDATE message comes back, the OSPFd does a GET of the updated SA so it can pull all of its parameters. ๐Ÿ’…


5.4 Miscellaneous โœจ

some messages are only really useful in system maintenance programs, for debugging, or for auditing. when there's a system panic, like a detected compromise, an SADB_FLUSH message should be issued for a particular SA type, or for ALL SA types. ๐Ÿšจ

Program->Kernel:     SADB_FLUSH for ALL
<Kernel then flushes all internal SAs>
Kernel->All:         SADB_FLUSH for ALL

some SAs may need to be explicitly deleted, either by a KMd or by a system maintenance program. ๐Ÿ—‘๏ธ

Program->Kernel:     SADB_DELETE for AH, association, addrs
Kernel->All:         SADB_DELETE for AH, association, addrs

using SADB_DUMP all the time is discouraged, but for debugging it can be super handy. ๐Ÿ™ˆ the output of a DUMP message should be read quickly so you don't overflow the socket buffer. ๐ŸŒˆ

Program->Kernel:     SADB_DUMP for ESP
Kernel->Program:     SADB_DUMP for ESP, association, <all fields>
Kernel->Program:     SADB_DUMP for ESP, association, <all fields>
Kernel->Program:     SADB_DUMP for ESP, association, <all fields>
<ad nauseam...>

6. Security Considerations โœจ

okay so this section is the serious one, bestie ๐Ÿซถ This memo is about making, reading, changing, and deleting Security Associations from an operating system. Only trusted, privileged users and processes should be able to do any of that ๐Ÿ” It's genuinely unclear whether this even protects anything on an operating system that doesn't have the idea of a trusted, privileged user ๐Ÿ˜ญ

If an unprivileged user can do any of these operations, the operating system can't actually deliver the security services it's supposed to. And if an adversary knows the keys and algorithms in use, cryptography can't protect anything at all ๐Ÿ’€

This mechanism is not a panacea ๐Ÿ™ˆ but it's an important piece of the OS that can help you build a secure internetwork, so it's still lowkey iconic ๐Ÿ‘‘

Users need to understand that how secure an implementation of this spec is depends completely on the overall security of the operating system, on the PF_KEY implementation being correct, and on the security and correctness of the applications that talk to PF_KEY ๐Ÿ›ก๏ธ It's giving "high assurance development techniques, please and thank you" when you implement PF_KEY and the related security association parts of the OS ๐Ÿ’…

Acknowledgments

The authors of this document are listed mostly in alphabetical order ๐Ÿ“ Randall Atkinson and Ron Lee gave super useful feedback on earlier versions ๐Ÿซถ

At one point or another, all of the authors worked at the Center for High Assurance Computer Systems at the U.S. Naval Research Laboratory ๐ŸŒˆ This work was sponsored by the Information Security Program Office (PMW-161), the U.S. Space and Naval Warfare Systems Command (SPAWAR), and the Computing Systems Technology Office of the Defense Advanced Research Projects Agency (DARPA/CSTO). They really get a big thank-you from us ๐Ÿ’– Without that support, PF_KEY would not exist ๐Ÿ˜ญ

The "CONFORMANCE and COMPLIANCE" wording was taken from [MSST98] โœจ

Finally, the authors want to thank everyone who sent in comments and questions on the different versions of this document ๐Ÿฆ‹ This specification and its implementations are discussed on the PF_KEY mailing list ๐Ÿ’œ If you want to join that list, send a note to <pf_key-request@inner.net> ๐Ÿงš

References

[AMPMC96] Randall J. Atkinson, Daniel L. McDonald, Bao G. Phan, Craig W. Metz, and Kenneth C. Chin, "Implementation of IPv6 in 4.4-Lite BSD", Proceedings of the 1996 USENIX Conference, San Diego, CA, January 1996, USENIX Association.

[Atk95a] Atkinson, R., "IP Security Architecture", RFC 1825, August 1995.

[Atk95b] Atkinson, R., "IP Authentication Header", RFC 1826, August 1995.

[Atk95c] Atkinson, R., "IP Encapsulating Security Payload", RFC 1827, August 1995.

[Atk97] Atkinson, R., "Key Exchange Delegation Record for the Domain Name System", RFC 2230, October 1997.

[BA97] Baker, F., and R. Atkinson, "RIP-2 MD5 Authentication", RFC 2082, January 1997.

[Biba77] K. J. Biba, "Integrity Considerations for Secure Computer Systems", MTR-3153, The MITRE Corporation, June 1975; ESD-TR-76-372, April 1977.

[BL74] D. Elliot Bell and Leonard J. LaPadula, "Secure Computer Systems: Unified Exposition and Multics Interpretation", MTR 2997, The MITRE Corporation, April 1974. (AD/A 020 445)

[Bra97] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[CW87] D. D. Clark and D. R. Wilson, "A Comparison of Commercial and Military Computer Security Policies", Proceedings of the 1987 Symposium on Security and Privacy, pp. 184-195, IEEE Computer Society, Washington, D.C., 1987.

[DIA] US Defense Intelligence Agency (DIA), "Compartmented Mode Workstation Specification", Technical Report DDS-2600-6243-87.

[GK98] Glenn, R., and S. Kent, "The NULL Encryption Algorithm and Its Use with IPsec", Work in Progress.

[HM97a] Harney, H., and C. Muckenhirn, "Group Key Management Protocol (GKMP) Specification", RFC 2093, July 1997.

[HM97b] Harney, H., and C. Muckenhirn, "Group Key Management Protocol (GKMP) Architecture", RFC 2094, July 1997.

[MD98] Madsen, C., and N. Doraswamy, "The ESP DES-CBC Cipher Algorithm With Explicit IV", Work in Progress.

[MG98a] Madsen, C., and R. Glenn, "The Use of HMAC-MD5-96 within ESP and AH", Work in Progress.

[MG98b] Madsen, C., and R. Glenn, "The Use of HMAC-SHA-1-96 within ESP and AH", Work in Progress.

[MSST98] Maughan, D., Schertler, M., Schneider, M., and J. Turner, "Internet Security Association and Key Management Protocol (ISAKMP)", Work in Progress.

[Moy98] Moy, J., "OSPF Version 2", STD 54, RFC 2328, April 1998.

[Per97] Perkins, C., "IP Mobility Support", RFC 2002, October 1996.

[Pip98] Piper, D., "The Internet IP Security Domain of Interpretation for ISAKMP", Work in Progress.

[Sch96] Bruce Schneier, Applied Cryptography, p. 360, John Wiley & Sons, Inc., 1996.

[Skl91] Keith Sklower, "A Tree-based Packet Routing Table for Berkeley UNIX", Proceedings of the Winter 1991 USENIX Conference, Dallas, TX, USENIX Association. 1991. pp. 93-103.

Disclaimer

The views and specification here belong to the editors and aren't necessarily those of their employers ๐ŸŽ€ The employers haven't passed judgment on the merits, if any, of this work. The editors and their employers specifically disclaim responsibility for any problems arising from correct or incorrect implementation or use of this specification ๐Ÿ™ˆ

Authors' Addresses

Daniel L. McDonald Sun Microsystems, Inc. 901 San Antonio Road, MS UMPK17-202 Palo Alto, CA 94303

Phone: +1 650 786 6815 EMail: danmcd@eng.sun.com

Craig Metz (for Code 5544) U.S. Naval Research Laboratory 4555 Overlook Ave. SW Washington, DC 20375

Phone: (DSN) 754-8590 EMail: cmetz@inner.net

Bao G. Phan U. S. Naval Research Laboratory

EMail: phan@itd.nrl.navy.mil

Appendix A: Promiscuous Send/Receive Message Type

A kernel supporting PF_KEY MAY implement the following extension for development and debugging purposes ๐Ÿงช If it does, it MUST implement the extension as specified here. An implementation MAY require an application to have additional privileges to perform promiscuous send and/or receive operations ๐Ÿ”ฅ

The SADB_X_PROMISC message lets an application send and receive messages in a "promiscuous mode" ๐Ÿ‘€ There are two forms of this message: control and data. The control form is just a message header. It toggles the promiscuous-receive function: a value of one in the sadb_msg_satype field turns on promiscuous message reception for this socket, and a value of zero in that field turns it off โœจ

The second form is the data form, used to send or receive messages in their raw form ๐Ÿ“ฆ A data-form message is a message header followed by an entire new message. You get two message headers in a row: one for the SADB_X_PROMISC message, and one for the payload message ๐Ÿ’…

Data messages sent from the application go either to the PF_KEY socket of a single process, identified by a nonzero sadb_msg_seq, or to all PF_KEY sockets if sadb_msg_seq is zero ๐Ÿฆ‹ These messages are sent without any processing of their contents by the PF_KEY interface, including sanity checking. That promiscuous-send capability lets an application send messages as if it were the kernel, and it also lets it send erroneous messages ๐Ÿ˜ญ

If promiscuous-receive is enabled, a copy of any message sent via PF_KEY by another application or by the kernel gets sent to the promiscuous application ๐Ÿ‘‘ That copy happens before any processing of the message's contents by the PF_KEY interface, again including sanity checking. This promiscuous-receive capability lets an application receive all messages sent by other parties using PF_KEY ๐ŸŒธ

The messaging behavior of the SADB_X_PROMISC message is:

     The messaging behavior of the SADB_X_PROMISC message is:

         Send a control-form SADB_X_PROMISC message from a user process
         to the kernel.

         <base>

         The kernel returns the SADB_X_PROMISC message to all listening
         processes.

         <base>

         Send a data-form SADB_X_PROMISC message from a user process to
         the kernel.

         <base, base(, others)>

         The kernel sends the encapsulated message to the target
         process(s).

         <base(, others)>

         If promiscuous-receive is enabled, the kernel will encapsulate
         and send copies of all messages sent via the PF_KEY interface.

         <base, base(, others)>

     Errors:
         EPERM Additional privileges are required to perform the
               requested operations.
         ESRCH (Data form, sending) The target process in sadb_msg_seq
               does not exist or does not have an open PF_KEY Version 2
               socket.

Appendix B: Passive Change Message Type

The SADB_X_PCHANGE message is the passive-side counterpart (the "listener" or "receiver") to the SADB_ACQUIRE message ๐ŸŽง It's really useful when key management applications want to handle incoming key management requests more effectively for passive-side sessions that deviate from systemwide default security services ๐Ÿ’œ If a passive session asks that only certain levels of security service be allowed, the SADB_X_PCHANGE message announces that change to any registered PF_KEY sockets. Unlike SADB_ACQUIRE, this message is purely informational and doesn't demand any other PF_KEY interaction ๐Ÿ™Œ

The SADB_X_PCHANGE message is typically triggered either by a change in an endpoint's requested security services, or when an endpoint that made a special request disappears ๐Ÿซง In the first case, an SADB_X_PCHANGE looks like an SADB_ACQUIRE, complete with an sadb_proposal extension showing the preferred algorithms, lifetimes, and other attributes. When a passive session either disappears or goes back to default behavior, an SADB_X_PCHANGE is issued with _no_ sadb_proposal extension, which signals that the exception to the systemwide default has gone away ๐Ÿ“

There are two messaging behaviors for SADB_X_PCHANGE. The first is the kernel-originated case:

     The kernel sends an SADB_X_PCHANGE message to registered
     sockets.

     <base, address(SD), (identity(SD),) (sensitivity,) (proposal)>
NOTE: The address(SD) extensions MUST have the port fields filled in with the port numbers of the session requiring keys, if appropriate. ๐Ÿ”‘

The second is for a user-level consumer of SAs:

     Send an SADB_X_PCHANGE message from a user process to the
     kernel.

     <base, address(SD), (identity(SD),) (sensitivity,) (proposal)>

     The kernel returns an SADB_X_PCHANGE message to registered
     sockets.

     <base, address(SD), (identity(SD),) (sensitivity,) (proposal)>

Appendix C: Key Management Private Data Extension โœจ

The Key Management Private Data extension ๐Ÿ” gets attached to either an SADB_ADD or an SADB_UPDATE message. It holds one chunk of arbitrary data for a security association. It might come in handy for key management apps that want to grab extra state with an SADB_DUMP or SADB_GET message if they need to restart or recover after a crash ๐Ÿ’€. Here's the format:

#define SADB_X_EXT_KMPRIVATE 17

struct sadb_x_kmprivate {
        uint16_t sadb_x_kmprivate_len;
        uint16_t sadb_x_kmprivate_exttype;
        uint32_t sadb_x_kmprivate_reserved;
};
/* sizeof(struct sadb_x_kmprivate) == 8 */

/* followed by arbitrary data */

The data after the sadb_x_kmprivate extension can be literally anything ๐Ÿงš. It gets stored right alongside the actual security association in the kernel. Like all data, it must be padded to an eight byte boundary ๐ŸŽ€.

Appendix D: Sample Header File ๐Ÿ’œ

Here's the sample header file, exactly as it was written. Yes, it's a big one, but it's the whole vibe ๐Ÿ’….

/*
This file defines structures and symbols for the PF_KEY Version 2
key management interface. It was written at the U.S. Naval Research
Laboratory. This file is in the public domain. The authors ask that
you leave this credit intact on any copies of this file.
*/
#ifndef __PFKEY_V2_H
#define __PFKEY_V2_H 1

#define PF_KEY_V2 2
#define PFKEYV2_REVISION        199806L

#define SADB_RESERVED    0
#define SADB_GETSPI      1
#define SADB_UPDATE      2
#define SADB_ADD         3
#define SADB_DELETE      4
#define SADB_GET         5
#define SADB_ACQUIRE     6
#define SADB_REGISTER    7
#define SADB_EXPIRE      8
#define SADB_FLUSH       9
#define SADB_DUMP        10
#define SADB_X_PROMISC   11
#define SADB_X_PCHANGE   12
#define SADB_MAX         12

struct sadb_msg {
  uint8_t sadb_msg_version;
  uint8_t sadb_msg_type;
  uint8_t sadb_msg_errno;
  uint8_t sadb_msg_satype;
  uint16_t sadb_msg_len;
  uint16_t sadb_msg_reserved;
  uint32_t sadb_msg_seq;
  uint32_t sadb_msg_pid;
};

struct sadb_ext {
  uint16_t sadb_ext_len;
  uint16_t sadb_ext_type;
};

struct sadb_sa {
  uint16_t sadb_sa_len;
  uint16_t sadb_sa_exttype;
  uint32_t sadb_sa_spi;
  uint8_t sadb_sa_replay;
  uint8_t sadb_sa_state;
  uint8_t sadb_sa_auth;
  uint8_t sadb_sa_encrypt;
  uint32_t sadb_sa_flags;
};

struct sadb_lifetime {
  uint16_t sadb_lifetime_len;
  uint16_t sadb_lifetime_exttype;
  uint32_t sadb_lifetime_allocations;
  uint64_t sadb_lifetime_bytes;
  uint64_t sadb_lifetime_addtime;
  uint64_t sadb_lifetime_usetime;
};

struct sadb_address {
  uint16_t sadb_address_len;
  uint16_t sadb_address_exttype;
  uint8_t sadb_address_proto;
  uint8_t sadb_address_prefixlen;
  uint16_t sadb_address_reserved;
};

struct sadb_key {
  uint16_t sadb_key_len;
  uint16_t sadb_key_exttype;
  uint16_t sadb_key_bits;
  uint16_t sadb_key_reserved;
};

struct sadb_ident {
  uint16_t sadb_ident_len;
  uint16_t sadb_ident_exttype;
  uint16_t sadb_ident_type;
  uint16_t sadb_ident_reserved;
  uint64_t sadb_ident_id;
};

struct sadb_sens {
  uint16_t sadb_sens_len;
  uint16_t sadb_sens_exttype;
  uint32_t sadb_sens_dpd;
  uint8_t sadb_sens_sens_level;
  uint8_t sadb_sens_sens_len;
  uint8_t sadb_sens_integ_level;
  uint8_t sadb_sens_integ_len;
  uint32_t sadb_sens_reserved;
};

struct sadb_prop {
  uint16_t sadb_prop_len;
  uint16_t sadb_prop_exttype;
  uint8_t sadb_prop_replay;
  uint8_t sadb_prop_reserved[3];
};

struct sadb_comb {
  uint8_t sadb_comb_auth;
  uint8_t sadb_comb_encrypt;
  uint16_t sadb_comb_flags;
  uint16_t sadb_comb_auth_minbits;
  uint16_t sadb_comb_auth_maxbits;
  uint16_t sadb_comb_encrypt_minbits;
  uint16_t sadb_comb_encrypt_maxbits;
  uint32_t sadb_comb_reserved;
  uint32_t sadb_comb_soft_allocations;
  uint32_t sadb_comb_hard_allocations;
  uint64_t sadb_comb_soft_bytes;
  uint64_t sadb_comb_hard_bytes;
  uint64_t sadb_comb_soft_addtime;
  uint64_t sadb_comb_hard_addtime;
  uint64_t sadb_comb_soft_usetime;
  uint64_t sadb_comb_hard_usetime;
};

struct sadb_supported {
  uint16_t sadb_supported_len;
  uint16_t sadb_supported_exttype;
  uint32_t sadb_supported_reserved;
};

struct sadb_alg {
  uint8_t sadb_alg_id;
  uint8_t sadb_alg_ivlen;
  uint16_t sadb_alg_minbits;
  uint16_t sadb_alg_maxbits;
  uint16_t sadb_alg_reserved;
};

struct sadb_spirange {
  uint16_t sadb_spirange_len;
  uint16_t sadb_spirange_exttype;
  uint32_t sadb_spirange_min;
  uint32_t sadb_spirange_max;
  uint32_t sadb_spirange_reserved;
};

struct sadb_x_kmprivate {
  uint16_t sadb_x_kmprivate_len;
  uint16_t sadb_x_kmprivate_exttype;
  uint32_t sadb_x_kmprivate_reserved;
};

#define SADB_EXT_RESERVED             0
#define SADB_EXT_SA                   1
#define SADB_EXT_LIFETIME_CURRENT     2
#define SADB_EXT_LIFETIME_HARD        3
#define SADB_EXT_LIFETIME_SOFT        4
#define SADB_EXT_ADDRESS_SRC          5
#define SADB_EXT_ADDRESS_DST          6
#define SADB_EXT_ADDRESS_PROXY        7
#define SADB_EXT_KEY_AUTH             8
#define SADB_EXT_KEY_ENCRYPT          9
#define SADB_EXT_IDENTITY_SRC         10
#define SADB_EXT_IDENTITY_DST         11
#define SADB_EXT_SENSITIVITY          12
#define SADB_EXT_PROPOSAL             13
#define SADB_EXT_SUPPORTED_AUTH       14
#define SADB_EXT_SUPPORTED_ENCRYPT    15
#define SADB_EXT_SPIRANGE             16
#define SADB_X_EXT_KMPRIVATE          17
#define SADB_EXT_MAX                  17
#define SADB_SATYPE_UNSPEC    0
#define SADB_SATYPE_AH        2
#define SADB_SATYPE_ESP       3
#define SADB_SATYPE_RSVP      5
#define SADB_SATYPE_OSPFV2    6
#define SADB_SATYPE_RIPV2     7
#define SADB_SATYPE_MIP       8
#define SADB_SATYPE_MAX       8

#define SADB_SASTATE_LARVAL   0
#define SADB_SASTATE_MATURE   1
#define SADB_SASTATE_DYING    2
#define SADB_SASTATE_DEAD     3
#define SADB_SASTATE_MAX      3

#define SADB_SAFLAGS_PFS      1

#define SADB_AALG_NONE        0
#define SADB_AALG_MD5HMAC     2
#define SADB_AALG_SHA1HMAC    3
#define SADB_AALG_MAX         3

#define SADB_EALG_NONE        0
#define SADB_EALG_DESCBC      2
#define SADB_EALG_3DESCBC     3
#define SADB_EALG_NULL        11
#define SADB_EALG_MAX         11

#define SADB_IDENTTYPE_RESERVED   0
#define SADB_IDENTTYPE_PREFIX     1
#define SADB_IDENTTYPE_FQDN       2
#define SADB_IDENTTYPE_USERFQDN   3
#define SADB_IDENTTYPE_MAX        3

#define SADB_KEY_FLAGS_MAX 0
#endif /* __PFKEY_V2_H */

Appendix E: Change Log ๐Ÿ“

Here's the tea on everything that changed between versions. Spilling it all ๐Ÿซถ.

Between 05 and 06:

  • Last change before it became an informational RFC ๐ŸŽ€. Removed all Internet-Draft references, standardized the citation strings, and now cites RFC 2119 for MUST, etc.
  • New appendix on the optional KM private data extension ๐Ÿ”.
  • Fixed the example so it shows that ACQUIRE messages with an errno mean a KM failure.
  • Added SADB_EALG_NULL.
  • Clarified the proxy examples so they match the definition of a PROXY address being the inner packet's source address. (Basically a sign-flip ๐Ÿ™ˆ. The example still shows how to protect against policy vulnerabilities in tunnel endpoints.)
  • Loosened the definition of a destination address to include broadcast ๐Ÿ“ก.
  • Recommended that LARVAL security associations have implicit short lifetimes โณ.

Between 04 and 05:

  • New appendix on the Passive Change message ๐Ÿฆ‹.
  • New sadb_address_prefixlen field.
  • Small clarifications on sadb_ident_id usage.
  • New PFKEYV2_REVISION value.
  • Small clarification on what a PROXY address is.
  • Corrected the sadb_spirange_{min,max} language.
  • In ADD messages that respond to an ACQUIRE, sadb_msg_seq MUST be the same as that of the originating ACQUIRE ๐Ÿ”.
  • Corrected the ACQUIRE message behavior: an ACQUIRE message SHOULD send up PROXY addresses when it needs them ๐ŸŒˆ.
  • Clarification on SADB_EXPIRE and user-level security protocols.

Between 03 and 04:

  • Stronger language about manual keying ๐Ÿ’ช.
  • PFKEYV2_REVISION, ร  la POSIX.
  • Put in language about sockaddr ports in ACQUIRE messages.
  • Mention of asymmetric algorithms.
  • New sadb_ident_id field for easier construction of USER_FQDN identity strings ๐Ÿชช.
  • Caveat that source addresses are not always used for collision detection (e.g. IPsec) ๐Ÿšจ.

Between 02 and 03:

  • Formatting changes โœจ.
  • Many editorial cleanups, rewordings, and clarifications ๐Ÿงน.
  • Restrictions that prevent many strange and invalid cases ๐Ÿšซ.
  • Added a definitions section ๐Ÿ“š.
  • Removed the connection identity type (it'll reappear once it's clearer what it should look like).
  • Removed 5.2.1 (Why involve the kernel?).
  • Removed the INBOUND, OUTBOUND, and FORWARD flags. They can be computed from src, dst, and proxy, and you had to compute them anyway for sanity checking ๐Ÿง .
  • Removed the REPLAY flag, since sadb_sa_replay==0 means the same thing.
  • Renamed bit lengths to "bits" to avoid potential confusion.
  • Explicitly listed lengths for structures ๐Ÿ“.
  • Reworked identities to always use a string format.
  • Removed requirements for support of shutdown() and SO_USELOOPBACK.
  • 64 bit alignment and 64 bit lengths instead of 32 bit ๐Ÿ”ข.
  • time_t replaced with uint64 in lifetimes โฑ๏ธ.
  • Inserted Appendix A (SADB_X_PROMISC) and Appendix B (SAMPLE HEADER FILE).
  • Explicit error if PF_KEY_V2 is not set at the socket() call.
  • More text on SO_USELOOPBACK.
  • Made field names and symbol names more consistent ๐Ÿ’….
  • Explicit error if PF_KEY_V2 is not in the sadb_msg_version field.
  • Bytes lifetime field is now a 64-bit quantity.
  • Explicit len/exttype wording.
  • Flattened out the extensions (LIFETIME_HARD, LIFETIME_SOFT, etc.).
  • UI example (0x123 == 0x1230 or 0x0123).
  • Cleaned up and fixed some message behavior examples.

Between 01 and 02:

  • Mentioned that people COULD use these same messages between user programs. (Also mentioned why you might still want to use the actual socket.)
  • Various wordsmithing changes.
  • Took out the netkey/ directory and made net/pfkeyv2.h ๐Ÿ“.
  • Inserted the PF_KEY_V2 proto argument per C. Metz.
  • Mentioned other socket calls and how their PF_KEY behavior is undefined.
  • SADB_EXPIRE now communicates both hard and soft lifetime expires โš ๏ธ.
  • New "association" extension, with an even smaller base header.
  • Lifetime extension improvements.
  • Length is now first in extensions.
  • Errors can now be sent from the kernel to user, too.
  • Examples section inserted ๐ŸŽฌ.
  • Some bitfield cleanups, including the STATE and SA_OPTIONS cleanup.
  • Key splitting now only happens across the auth algorithm and encryption algorithm. Thanks to B. Sommerfeld for clues here ๐Ÿ™.

Between 00 and 01:

  • Added this change log ๐ŸŽ‰.
  • Simplified TLV header syntax.
  • Split the algorithms. This may be controversial, but it lets PF_KEY be used for more than just IPsec, and it makes some kinds of policies easier to place in the KMd ๐Ÿงฉ.
  • Added solid definitions and formats for certificate identities, multiple keys, etc.
  • Specified how keys are to be laid out (most-to-least bits) ๐Ÿ—‚๏ธ.
  • Changed sequence number semantics to be like an RPC transaction ID number ๐Ÿ”ข.

Copyright (C) The Internet Society (1998). All Rights Reserved. ๐ŸŒธ

okay so the legal bit, bestie ๐Ÿ“œ You're allowed to copy this document and hand it out to people, and you're allowed to make derivative works that comment on or explain it or help with implementing it. You can prepare, copy, publish, and distribute those, in whole or in part, with no restrictions of any kind. The only condition is that the above copyright notice and this paragraph have to be included on all such copies and derivative works. ๐Ÿซถ

BUT (and this is important ๐Ÿ™…โ€โ™€๏ธ) this document itself may not be modified in any way. You can't remove the copyright notice, and you can't remove references to the Internet Society or other Internet organizations. There are two exceptions. The first is if you need it for developing Internet standards, in which case you have to follow the copyright procedures defined in the Internet Standards process. The second is if you need to translate it into languages other than English. โœจ

The permissions above are perpetual, meaning they will not be revoked by the Internet Society or its successors or assigns. Lowkey the most reassuring line in the whole doc ๐Ÿ’œ

The rest is the warranty disclaimer, and it's giving "read this carefully" energy. โš ๏ธ

This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. ๐Ÿงš

๐Ÿ“ฆ where this page comes from