Network Working Group Craig Metz Internet Draft The Inner Net draft-metz-net-security-api-01.txt 16 January 1998 Network Security API for Sockets Status of this Memo This document is an Internet Draft. Internet Drafts are working documents. Internet Drafts are draft documents valid for a maximum of 6 months. Internet Drafts may be updated, replaced, or obsoleted by other documents at any time. It is not appropriate to use Internet Drafts as reference material or to cite them other than as "work in progress". A future version of this draft will be submitted to the RFC Editor for publication as an Informational document. Abstract This API is a means for sockets applications to request network security services from an operating system. It is designed to move most of the work and intelligence of security policy processing into the operating system so that the burden on application authors is light enough to encourage the use of network security. It is documented here for the benefit of others who might also adopt and use the API, thus providing increased portability of applications that use network security services (e.g., the IP Security ESP and AH protocols). 1. Introduction Many network protocols now provide security services such as encryption and authentication at the network layer. For example, IPv4 supports and IPv6 requires the IP Security protocols, ESP and AH. While various flow-based policy schemes can frequently identify the security requirements of a particular packet, applications and the end user should be able to provide input to the policy process and Metz Expires in 6 months [Page 1] Internet Draft Network Security API for Sockets 23 January 1998 request security services from the system. That is the main goal of this application programming interface: to provide a means for applications (and, through them, the end user) to request security services and properties from the system. Secondary goals of this API include moving most of the burden to the system, thus making it easier for the application programmer to use security services, supporting complex policy decisions with reasonable performance, and giving the application more input to and feedback from the policy process than is provided for in similar APIs. This API is built as an extension to the POSIX p1003.1g sockets interface. That interface is REQUIRED for this API. This API assumes that network security services follow a conceptual model similar to that of IP Security. This interface may need to be changed in the future to support protocol families that differ radically from that model. While not required to use this API, it is intended that the PF_KEY key management API be used in systems that implement this network security API for sockets. Readers of this document who have not read the PF_KEY specification are encouraged to do so in order to understand the context for some of the capabilities of this API. This API is intended to be usable with any network protocol supported by the POSIX p1003.1g sockets interface. However, because it leads to extra code complexity and it is almost never desirable, this API MUST NOT be used with protocol families that are only capable of system-local communication. Such protocol families include PF_LOCAL (i.e., PF_UNIX), PF_ROUTE, and PF_KEY). 1.1. Terminology Even though this document is not intended to be a standard, the words that are used to define the significance of particular features of this interface are usually capitalized. Specific behavior compliance requirements are itemized using the requirements terminology (specifically, the words MUST, SHOULD, and MAY) defined in RFC 2119. In addition, the following terms should be noted: - CONFORMANCE and COMPLIANCE Conformance to this specification has the same meaning as compliance to this specification. In either case, the mandatory-to- implement, or MUST, items MUST be fully implemented as specified here. If any mandatory item is not implemented as specified here, that implementation is not conforming and not compliant with this Metz Expires in 6 months [Page 2] Internet Draft Network Security API for Sockets 23 January 1998 specification. - IMPLEMENTORS Those who are building a software implementation that uses this API. If not otherwise specified, this term refers both to application implementors and to system implementors. Many of the concepts and caveats of this API need to be carefully noted by both. - ULP An upper-layer protocol (ULP) is an opaque payload for purposes of security processing. This can be transport protocol (e.g., TCP or UDP), a control protocol (e.g., ICMP or IGMP), or another network protocol (e.g., IP or IPv6). 1.2. Conceptual Model This section describes the conceptual model of a system that implements this API. It is intended to provide background material useful to understand the rest of this document. Presentation of this conceptual model does not constrain an implementation to strictly adhere to the conceptual components discussed in this section. Systems implementing this API are expected to have a "policy engine". This term is used to refer to whatever components of a system that have programmed with rules that control what security operations and parameters are allowed and which are preferred for given requirements. In many cases, flow information determined from the contents of network packets and the rules in this policy engine will completely satisfy the security needs of an application without the need for this API. For example, if a policy engine is programmed with a rule that tells it to require that all TCP packets with a foreign port of 23 be encrypted, outbound telnet connections will be encrypted without the need for the telnet client itself to request that encryption. The problem with this example is that all outbound telnet connections are encrypted whether or not the user wants it. In order to make the choice of whether or not to encrypt available to the user, the telnet client needs to specify an input to the policy engine that reflects the user's desire to encrypt or not. That is one of the things that this API does. This API is built on a model where the policy engine makes the decisions for the system. It has complete and final control over what, if any, security processing is done on a packet and the parameters for that processing. The application makes requests (presumably representing the needs of some user) to the policy engine that reflect what it would like the policy engine to do with its Metz Expires in 6 months [Page 3] Internet Draft Network Security API for Sockets 23 January 1998 packets. The policy engine can, if so programmed, completely or selectively ignore this request. In some environments, ignoring some or all of an application's request is critical to maintaining system security; in others, it is inappropriate and frustrating. This API is not concerned with the programming of the policy engine and how the policy engine acts on an application's request, only how the application makes that request and receives feedback from the policy engine on what actually happened. A complete network security implementation requires many components beyond those just described, but these are all hidden from the application behind the system's policy engine. Figure 1 illustrates a possible system organization to show where this API applies. User | Network Application Key Management Daemon | | | | ====Network Security API=====PF_INET Sockets=======PF_KEY Socket==== | | TCP/UDP PF_KEY Messaging | | Policy Engine-----------------SADB | | IPsec---IP | Link Figure 1 - A Possible System Organization This API uses abstract security properties instead of specific values. That is, an application might request encryption and ask that it be "stronger," but it does not specify the exact security transforms or cryptographic algorithms to be used. This design choice was made for several reasons. First, this allows applications to take advantage of network security services with the least amount of involvement in the inner workings of the system. Security associations, policy rules, available transforms, and available algorithms may change during an application's lifetime. These changes could make an application's request invalid or less desirable. Using abstract values, the policy engine can simply remap the abstract values to a new set of actual operations and parameters without the intervention of the application. Other approaches would place a more significant burden on the authors of applications that wish to use network security services. Metz Expires in 6 months [Page 4] Internet Draft Network Security API for Sockets 23 January 1998 Second, this allows the policy engine greater flexibility in combining the user's request with system policy rules. A request for a specific algorithm or transform does not tell the policy engine what the application's and/or user's requirements are or what properties are expected of that specific choice. Third, some applications will come in binary-only form and will try to select their security properties without user intervention. By abstracting the algorithms in use, a system administrator has the ability to change what actual algorithms and parameters are in use without the need for changing every such binary on the system. 2. Requests The model chosen for the network security requests themselves could be compared to a set of recipes. Each recipe is a step-by-step listing of the steps that should be taken to achieve the result, but those steps might not get executed exactly as requested if the executor "knows better". Of the possible recipes, a few will be available for frequent use, and only one can be executed at a time. Typically, only one will be chosen and executed, but there are situations in which the ability to execute one of several quickly (and cache preparatory steps) is desirable. 2.1. Requests Each request is a list of operations that specifies what security properties an application would like performed on its packets. There are four currently defined operations: Authenticate (A): Verify that the sender is as claimed and that the packet has not been changed in transit. This operation provides the properties of authentication and integrity. Encrypt (E): Protect the data from receipt by unauthorized parties. This operation provides the property of confidentiality. Encapsulate (N): Prepend a new network header to the packet. This allows applications to create half- tunnels "on-the-fly". For example, an application might make a request of: A N E And the system policy engine might translate that and build a Metz Expires in 6 months [Page 5] Internet Draft Network Security API for Sockets 23 January 1998 packet that looks like: IP ESP [ IP AH ULP ] But the system policy engine could also build a packet that looks like: IP ESP [ ULP ] On the surface, this looks like the policy engine is being unreasonable or denying service. But consider the "optimization" policy rules that could turn that request into that packet: 1. Combine A and E into an ESP combined encryption/authentication transform and put the combination in the place of E. 2. Combine N with adjacent network headers to prevent encapsulation with nothing between the two network headers. Implementors must always remember that requests are just that. The policy engine controls what services are delivered. Requests are always ordered such that the first operation is the one the application would like to be performed closest to the upper- layer protocol. Another way to look at this is that the first operation in the request is the one the application would like to be the first operation performed in output security processing and the last operation performed in input security processing. For example, a mapping scheme that used the application's ordering might map: A E to IP ESP [ AH ULP ] E A to IP AH ESP [ ULP ] A N E to IP ESP [ IP AH ULP ] 2.2. Preferences Associated with each operation is a value that specifies the application's preference towards that operation actually taking place. There are four currently defined preferences: Default (d): The application has no preference about this operation being performed or not. The operation is included to specify properties of the operation should it take place and/or as a place-holder. In absence of any other policy information suggesting otherwise, a system SHOULD default to not performing the operation. Use (u): The application prefers, but does not require, that the operation take place. Metz Expires in 6 months [Page 6] Internet Draft Network Security API for Sockets 23 January 1998 Require (r): The application requires that the operation take place and requests that processing of the request halt if the operation cannot be performed. Never (n): The application requires that the operation not take place and requests that processing of the request halt if the operation cannot be performed. For example, key management applications would need to use this request to prevent the system from attempting to provide security services for them and creating a catch-22. Policy engines SHOULD grant the application's request to cause processing of the request to halt if require or never preferences cannot be satisfied. For input processing, this would result in the request not being matched and, if the request is the last in the group, the packet being dropped. For output processing, this would result in the packet being dropped and an error being returned to the application. It is a serious security problem for processing to not fail if the application has requested it. For example, confidential data could then be sent out as cleartext if key management fails. Consider as an example the request: Au Er Suppose that the system's policy engine mapped this into an attempt to use AH and ESP. If key management failed to obtain a SA for AH but was able to obtain a SA for ESP, communication could continue and the actual packet would look like: IP ESP [ ULP ] However, if the ESP SA could not be obtained, regardless of whether key management could obtain the AH SA, the packet SHOULD be dropped and an error returned to the application. 2.3. Barriers Barriers (b) are the most difficult concept in this API. They are a flag on some operations that asks the policy engine to maintain a separation located to the right of that operator. The policy engine SHOULD NOT re-order or combine operations across or through this barrier. Thus, barriers ask the policy engine to prevent certain kinds of optimizations from taking place. For example, consider the case of an application that deliberately wants to superencrypt its packets. A policy engine might have a rule that combines consecutive encryption operations. A request of: Metz Expires in 6 months [Page 7] Internet Draft Network Security API for Sockets 23 January 1998 E E Would map to: IP ESP [ ULP ] But a request of Eb E Would map to: IP ESP [ ESP [ ULP ] ] If that policy engine is aggressive in its attempts to optimize security operations (because fewer operations performed means better performance), it might try to combine encryption operations even across an encapsulation operation. This would lead to internal steps like: E N E (Re-order: move the second E right) N E E (Combine: E + E = E) N E (Combine: remove leading N since there's a header immediately preceding it) E This results in something that probably isn't quite what the application expected when it made the original request. Insertion of a barrier solves this problem: E Nb E The barrier is "located" between the N and E requests, though the actual flag is on the N request. Now the two E requests cannot be moved and/or combined, so the desired behavior is delivered. Barriers also affect how a policy engine using certain kinds of designs would make decisions based on the application's request. More detailed discussion on this topic is outside the scope of this document. As a general rule of thumb, most encapsulation requests SHOULD be flagged as barriers and most other requests SHOULD NOT be flagged as barriers. Except as prevented by barriers, implementations SHOULD perform optimization steps such as re-ordering and combining compatible operations to attempt to decrease the amount of processing necessary for a packet. Metz Expires in 6 months [Page 8] Internet Draft Network Security API for Sockets 23 January 1998 2.4. Multiple Requests A request specifies one possible arrangement of operations to protect an application's packet. An application might provide the kernel with multiple requests. These are used differently for output and input security processing. For output, providing multiple requests allows an application to quickly flip between a small set of them and therefore change the security properties it desires for its packets. This could also be done by loading the requests into the kernel as they are needed, but providing multiple requests to the kernel has less overhead (instead of having to specify the entire request when the application wishes to change, it only has to specify an index; a system call can also be saved by using control messages) and it allows the system to cache state information associated with each request. Note that only one request may be used with any given network packet on output. For input, providing multiple requests allows an application to provide several possible acceptable input policies (in preference order) to try matching an incoming packet against. This can be used in applications that might allow communication with different security properties but might behave differently based on what properties are present. For example, one might use this capability to connect an incoming connection to a different daemon depending on whether or not its packets were encrypted. Also, this can be used in conjunction with output switching to build datagram servers that can "match" the properties of incoming packets -- packets that were received encrypted could then be sent encrypted responses, while packets that were received as cleartext would be sent cleartext responses. Consider as an example a UDP datagram server. It might make the following request: 0. Au Nr Er 1. Ar Er 2. Er 3. Ed A packet comes in: IP ESP [ IP ULP ] The application receives the packet and a notification that request zero was matched. It then sends its reply and notifies the kernel that it wishes to use request zero for that packet. All SAs are successful, so the reply looks like: Metz Expires in 6 months [Page 9] Internet Draft Network Security API for Sockets 23 January 1998 IP ESP [ IP AH ULP ] Note that the reply doesn't look exactly the same as the original packet. This is a feature of using the "use" preference. If exact matching of the input and output specifications is required, the use and default preferences MUST NOT be used. 2.5. Latching Stream sockets present a special problem because there is generally not a correlation between output boundaries at the application layer and at the network layer. Consider this sequence of events: * Open a stream socket. * Load a request of: 1. Er 2. En * Select request 1. * connect() to a remote end. * write() a byte of secret data. * Select request 2. * write() a byte of non-secret data. Typically, the two bytes written would be combined by a stream transport protocol into one packet. But should that packet be encrypted or not? Either encrypting or not encrypting that packet violates one of the requests. Stream protocols like TCP can also retransmit packets and slice/combine packets while retransmitting, which complicates things more. Some might try to modify the implementations of stream protocols to "tag" ranges of data with security properties and prevent incompatible combinations and ensure that the correct properties are present on retransmitted packets. Doing so is complex and tends to turn the stream protocol into a reliable datagram protocol, which has very different properties. Therefore, all implementations of this API MUST implement a "latching" behavior for stream protocols that "latches in" the first request that is used to successfully process a packet for a connection and does not allow any other request to be used for the lifetime of that connection. Consider this example: Both client and server open a TCP stream socket. Both client and server load a request of: 1. Er Ar 2. Er 3. En Client selects request 2 and issues a connect(). The first packet in the handshake "latches in" request 2 for the Metz Expires in 6 months [Page 10] Internet Draft Network Security API for Sockets 23 January 1998 lifetime of the TCP connection. Server receives the first packet in the handshake from the client. It first tries to match request 1, fails, and then tries to match request 2, which succeeds. The server then creates its connection state for the new connection and "latches in" request 2 for that new connection. Note that the accepting socket is "latched", but NOT the listening socket. The handshake completes and data flows. All packets beyond the initial exchange are required to meet the criteria in request 2. Latching also applies to identity information. The innermost local and remote identity used for the security associations used to process the first packet input and output MUST be used for all subsequent security associations allocated to a stream socket (including and especially on "rekey" operations). This precludes attacks where a connection could be "hijacked" by rekeying with a different identity without the application's knowledge. Implementations MUST NOT extend the latching behavior from the lifetime of the connection to the lifetime of the socket. It is a legitimate behavior for a stream socket to be connected and latched, disconnected, and connected and latched again with possibly different security properties. Sockets implementations have historically not correctly handled disconnecting a stream socket and connecting it to a new endpoint; now that these problems are finally being fixed, implementations of this API MUST NOT reintroduce this problem. 3. Detailed Interface This section discusses the actual symbols, structures, and function calls used with this API. These are all based on the concepts discussed in the previous section. 3.1. Name Space This network security API defines preprocessor symbols that start with the prefix "NET_SECURITY" and other names that start with the prefixes "net_security" and "__net_security". These are all defined as a result of including the header file . Inclusion of the file MUST NOT define symbols or structures in this name space that are not described in this document without the explicit prior permission of the author. An implementation that fails to obey this rule IS NOT COMPLIANT WITH THIS SPECIFICATION and MUST NOT make any claim to be. This rule also applies to any files that might be included as a result of including Metz Expires in 6 months [Page 11] Internet Draft Network Security API for Sockets 23 January 1998 the file . This rule provides implementors with some assurance that they will not encounter name space-related surprises. 3.2. Request Format An application using this API gives the kernel zero or more requests that describe the set of security operations that the application requests for its packets. Two constants define the limits on these requests. The first, NET_SECURITY_REQUEST_MAX, is the maximum number of requests that an application can load for each socket. Note that the requests themselves are numbered starting at zero. Therefore, the last request is numbered (NET_SECURITY_REQUEST_MAX-1). The second, NET_SECURITY_OPERATION_MAX, is the maximum number of operations that can be in a request. Both of these constants MUST have a value of greater than four and NET_SECURITY_REQUEST_MAX MUST be less than FD_SETSIZE. The recommended value of these constants is sixteen. Each request consists of zero or more operations, in order. Each operation looks like: struct net_security_sockaddr_union { struct sockaddr sa; #if AF_INET struct sockaddr_in sin; #endif /* AF_INET */ #if AF_INET6 struct sockaddr_in6 sin6; #endif /* AF_INET6 */ }; struct net_security_operation { uint8_t net_security_operation_type; uint8_t net_security_operation_preference; uint8_t net_security_operation_barrier; uint8_t net_security_operation_reserved; /* compiler-inserted pad if 64 bit */ union { struct { uint8_t __alloctype; uint8_t __hints; uint8_t __algid; uint8_t __reserved; } __forsa; union net_security_sockaddr_union __sockaddr; } __union; }; Metz Expires in 6 months [Page 12] Internet Draft Network Security API for Sockets 23 January 1998 #define net_security_operation_alloctype __union.__forsa.__alloctype #define net_security_operation_hints __union.__forsa.__hints #define net_security_operation_algid __union.__forsa.__algid #define net_security_operation_sockaddr __union.__sockaddr.sa type The type of operation to be executed. Defined operations are described in section 2.1. preference The preference of the application toward this operation being executed. Defined preferences are described in section 2.2. barrier If set to one, indicates that a barrier should be placed after this operation. If set to zero, no barrier is placed there. All other values are reserved. reserved MUST be set to zero. alloctype For E and A requests, indicates the allocation type requested for SAs obtained for this operation. Defined values for this field are described in section 3.3. hints For E and A requests, a set of bit-mapped values that give the policy engine hints as to what algorithm and parameters should be used for this operation. Defined values for this field are described in section 3.5. algid For E and A requests, if nonzero, an algorithm identifier that requests that a specific cryptographic algorithm be used. Values for this field are defined in [MMP97]. This MUST be used as a means of last resort only. The use of this field is a privileged operation and subject to system policy; if it is nonzero and the application is not privileged, the system MUST return EPERM when the request is loaded. If an system's policy rejects the use of the algorithm specified in this field, the request SHOULD fail. Applications MUST NOT require this capability for normal operation. Systems MAY universally refuse to honor this field. [cmetz: This field is a concept-breaking blemish, but it's here by popular demand.] sockaddr For N requests, if sa_family is nonzero, the destination address of the requested encapsulation. This MAY be an address in a protocol family other than that of the socket. Specification of a destination MAY be a privileged operation, the details of which are specific to a particular system implementation. If sa_family is zero, the destination is the same as the destination address of the inside packet. This field MUST NOT contain addresses for system-local protocol families (e.g., PF_UNIX, PF_LOCAL, PF_ROUTE, and PF_KEY). net_security_sockaddr_union is defined as being large enough to Metz Expires in 6 months [Page 13] Internet Draft Network Security API for Sockets 23 January 1998 hold any sockaddr on the system that can be used with a socket protocol family that supports this API. 3.3. Allocation Types This API gives applications the ability to request the granularity with which the system shares (or doesn't share) its SAs. The different granularities are called allocation types because they control the SA database's allocation functions. Six allocation types are currently defined: System: The application requests that SAs allocated to this socket be shared with any other socket on the system. GID: The application requests that SAs allocated to this socket be shared only with other sockets with the same group ID. UID: The application requests that SAs allocated to this socket be shared only with other sockets with the same user ID. PGID: The application requests that SAs allocated to this socket be shared only with other sockets with the same process group (sometimes called session) ID. Family: The application requests that SAs allocated to this socket be allocated only to this socket and its descendants. Descendants are sockets created by through calls such as accept() as well as those copies of a socket created for child processes. Socket: The application requests that SAs allocated to this socket be allocated only to this socket. In the case of a passively created stream socket, control of the SAs created for connection setup will be transferred to the child socket returned at accept() time. After that, the listening socket MUST NOT have access to those SAs. Note that the GID, UID, and PGID MUST for a socket MUST be recorded at the time of socket creation and that stored copy is the GID, UID, and/or PGID used for SA allocation. This means that, if a program changes any of these after a socket has been opened, the ID used for allocation of SAs to a socket does not change. Also note that the actual UID used is the REAL UID. Application programmers should note that this behavior may not be what they would expect. For example, if an application opens a socket, requests an allocation type of PGID, then calls setpgid(), fork(), and exec()s a new process that also opens a socket and requests an allocation type of PGID, the SAs WILL NOT be shared Metz Expires in 6 months [Page 14] Internet Draft Network Security API for Sockets 23 January 1998 between the two sockets. 3.4. Identity Types This API gives applications the ability to request the type of identity information sent to remote key management. The following identity types are currently defined: Address: The application requests that no identity information beyond the addresses present on a SA be specified. Prefix: The application requests that additional identity information for the system's prefix be specified. (For IP systems, a prefix is the same thing as a subnet) FQDN: The application requests that additional identity information for the system's fully qualified domain name be specified. UserFQDN: The application requests that additional identity information in the form of a user's name and a system's fully qualified domain name be specified. Please note that some combinations of identity type and allocation type may or may not make sense for a given system. For example, most systems will probably not want to allow system or GID allocation with UserFQDN identities. System implementations SHOULD make the allowable combinations a policy control available to system administrators. More information on identity types may be found in the IP Security DOI specification [Piper97]. 3.5. Hints This API gives applications the ability to give certain hints to the policy engine about its expected security needs. These hints SHOULD affect the selection of specific transforms and algorithms by the policy engine. There are three parameters that an application can give hints about: the sensitivity of its data, the expected volume of data, and the latency needs of the application. Note that these parameters are not quite independent or dependent. How these parameters affect algorithm selection SHOULD be a policy decision that is configurable by the system administrator. The following sensitivity levels are currently defined. Note that there are sixteen numeric values currently available but only five Metz Expires in 6 months [Page 15] Internet Draft Network Security API for Sockets 23 January 1998 named values; these extra intermediate values MAY be used when extra granularity is needed. Unknown: The application does not know in advance how sensitive its data will be. Lowest: The application expects its data to have the lowest sensitivity of any on the system and requires the weakest security. Low: The application expects its data to have low sensitivity; a weak algorithm is acceptable. Medium: The application expects its data to have medium sensitivity. High: The application expects its data to have high sensitivity; a strong algorithm should be used. Highest: The application expects its data to have the highest sensitivity of any on the system and requires the strongest security available. Applications MUST NOT use this level unless absolutely necessary. The following volume levels are currently defined: Unknown: The application does not know in advance what volume of data it will communicate. Low: The application expects to communicate a low volume of data. For example, diagnostic applications like ping(8) might use this. Medium: The application expects to communicate a moderate volume of data. High: The application expects to communicate a high volume of data. For example, bulk data transfers such as FTP might use this. The following latency levels are currently defined: Unknown: The application does not know in advance what latency requirements it has for its data. Low: The application expects its data to need low latencies. For example, certain real-time traffic might need this. Medium: The application expects its data to tolerate moderate latencies. High: The application expects its data to tolerate high latencies. For example, bulk data transfers such as FTP might use this. Applications SHOULD use the hints field to describe their security needs in abstract properties if possible. This API tries to prevent applications from directly selecting security algorithms and Metz Expires in 6 months [Page 16] Internet Draft Network Security API for Sockets 23 January 1998 parameters. This is a deliberate and useful feature, though some may consider this a bug. 3.6. Defined Values The following values have been defined for various fields. All other values are reserved. Operation types (net_security_operation_type): /* Authenticate (A) */ #define NET_SECURITY_TYPE_AUTHENTICATE 1 /* Encrypt (E) */ #define NET_SECURITY_TYPE_ENCRYPT 2 /* Encapsulate (N) */ #define NET_SECURITY_TYPE_ENCAPSULATE 3 Operation preferences (net_security_operation_preference): /* Default (d) */ #define NET_SECURITY_PREFERENCE_DEFAULT 0 /* Use (u) */ #define NET_SECURITY_PREFERENCE_USE 1 /* Require (r) */ #define NET_SECURITY_PREFERENCE_REQUIRE 2 /* Never (n) */ #define NET_SECURITY_PREFERENCE_NEVER 3 Operation SA allocation types (net_security_operation_alloctype): /* System */ #define NET_SECURITY_ALLOCTYPE_SYSTEM 1 /* GID */ #define NET_SECURITY_ALLOCTYPE_GID 2 /* UID */ #define NET_SECURITY_ALLOCTYPE_UID 3 /* PGID */ #define NET_SECURITY_ALLOCTYPE_PGID 4 /* Family */ #define NET_SECURITY_ALLOCTYPE_FAMILY 5 /* Socket */ #define NET_SECURITY_ALLOCTYPE_SOCKET 6 Operation SA hints (net_security_operation_hints): /* Mask for sensitivity hints */ #define NET_SECURITY_HINTS_SENSITIVITY 0x0f /* Mask for volume hints */ Metz Expires in 6 months [Page 17] Internet Draft Network Security API for Sockets 23 January 1998 #define NET_SECURITY_HINTS_VOLUME 0x30 /* Mask for latency hints */ #define NET_SECURITY_HINTS_LATENCY 0xc0 /* Unknown sensitivity */ #define NET_SECURITY_SENSITIVITY_UNKNOWN 0x00 /* Lowest sensitivity */ #define NET_SECURITY_SENSITIVITY_LOWEST 0x01 /* Low sensitivity */ #define NET_SECURITY_SENSITIVITY_LOW 0x04 /* Medium sensitivity */ #define NET_SECURITY_SENSITIVITY_MEDIUM 0x07 /* High sensitivity */ #define NET_SECURITY_SENSITIVITY_HIGH 0x0c /* Highest sensitivity */ #define NET_SECURITY_SENSITIVITY_HIGHEST 0x0f /* Unknown volume */ #define NET_SECURITY_SENSITIVITY_UNKNOWN 0x00 /* Low volume */ #define NET_SECURITY_VOLUME_LOW 0x10 /* Medium volume */ #define NET_SECURITY_VOLUME_MEDIUM 0x20 /* High volume */ #define NET_SECURITY_VOLUME_HIGH 0x30 /* Unknown latency */ #define NET_SECURITY_LATENCY_UNKNOWN 0x00 /* Low latency */ #define NET_SECURITY_LATENCY_LOW 0x40 /* Medium latency */ #define NET_SECURITY_LATENCY_MEDIUM 0x80 /* High latency */ #define NET_SECURITY_LATENCY_HIGH 0xc0 Identity types: /* Address */ #define NET_SECURITY_IDENTTYPE_ADDRESS 1 /* Prefix */ #define NET_SECURITY_IDENTTYPE_PREFIX 2 /* FQDN */ #define NET_SECURITY_IDENTTYPE_FQDN 3 /* USERFQDN */ #define NET_SECURITY_IDENTTYPE_USERFQDN 4 Identity size: /* Maximum buffer needed to hold an identity string */ Metz Expires in 6 months [Page 18] Internet Draft Network Security API for Sockets 23 January 1998 #define NET_SECURITY_IDENTITY_MAX 1024 Receive identity types (for net_security_receiveident() et al.): /* Receive local identity information */ #define NET_SECURITY_RECEIVEIDENT_LOCAL 1 /* Receive remote identity information */ #define NET_SECURITY_RECEIVEIDENT_REMOTE 2 3.7. Function Calls This API specifies several functions that will typically be implemented as a simple setsockopt() or getsockopt() call to the operating system. However, systems MAY choose to implement this using a different low-level interface. Other functions provide user interface or helper functions that SHOULD be implemented completely in user space. Unless otherwise specified, all functions return zero on success, negative one on failure, and return any further indication of the reason for failure in the global variable errno. 3.7.1. Set and Get Request int net_security_setrequest(int s, int number, struct net_security_operation *ops, int numops); int net_security_getrequest(int s, int number, struct net_security_operation *ops, int *numops); These calls set and get, respectively, the application's current request for network security services from the kernel. The net_security_setrequest() call requests that the system replace the currently registered network security request with the specified number for the socket s with the new request pointed to by ops of numops operations. This function MUST verify that the request is correctly formed and that all values are within range; that verification SHOULD be done in the kernel. If this is not the case, the function MUST fail with errno=EINVAL. If the socket is a stream socket that is currently latched, this call MUST fail with errno=EPERM. If this call fails for any reason, the loaded request MUST remain unchanged. System implementors must be careful about attempting to check the request against system policy when net_security_setrequest() is called. Values that might be acceptable to the policy at registration time might not be acceptable when it's time to send or Metz Expires in 6 months [Page 19] Internet Draft Network Security API for Sockets 23 January 1998 receive data, and the converse is also true. Therefore, the system MUST NOT check the request against any part of system policy that could change during the life of the socket. The net_security_getrequest() call returns the currently registered request for a socket. This call is useful because it allows applications that receive an existing socket from a parent, such as children of inetd(8), to determine what security properties were originally requested. Note that numops MUST be initialized to the maximum number of operations that may be stored in the buffer ops before this call and the actual number of operations returned is stored in numops upon success. Both of these calls SHOULD be implemented as much as possible in kernel space and SHOULD be implemented as a setsockopt() and getsockopt() call, respectively. 3.7.2. Request Bitmap Functions int net_security_activerequests(int s, fd_set *which); int net_security_inputrequests(int s, fd_set *which); The net_security_activerequests() function returns a bitmap that identifies which request numbers point to a request that has been loaded into the kernel. This can be used to determine if a request number is in use or to find an unused request number. A set bit indicates that the request number corresponding to the bit number is in use by a loaded request. The net_security_inputrequests() function loads into the kernel a bitmap that identifies which request numbers identify requests that the application would like to attempt to match against an incoming packet if the request number has been set to -1. A set bit indicates that the request number corresponding to the bit number corresponds to a request that the application would like to attempt to match. The kernel SHOULD silently ignore set bits in this bitmap if they point to unused request numbers. The default for this bitmap MUST be all set bits. Both of these calls SHOULD be implemented as much as possible in kernel space and SHOULD be implemented as a setsockopt() and getsockopt() call, respectively. 3.7.3. Request Selection Functions int net_security_setnum(int s, int num); int net_security_getnum(int s, int *num); Metz Expires in 6 months [Page 20] Internet Draft Network Security API for Sockets 23 January 1998 The net_security_setnum() call sets the currently selected request number for I/O operations. If the socket is a stream socket and a request has been latched in, this call MUST fail with errno=EPERM. If a value between and including zero and (NET_SECURITY_REQUEST_MAX-1) is specified, that request will be used for all inputs or outputs unless otherwise specified in a control message. Implementations MUST NOT return an error if a valid request number is specified for which a request has not yet been set. If a value of negative one is specified, output operations will fail. Input operations will attempt to match requests starting at zero if the corresponding bit in the input mask has been set and will loop through all loaded requests until one matches or all eligible requests have been tried. If the socket is a stream socket, the index of the succeeding request will then be latched in and will replace the value specified in this request. Thus, subsequent outputs will succeed. The net_security_getnum() call gets the currently selected request number for I/O operations. This is useful both for sockets that are inherited from another process and to determine which request got latched on a stream socket. Note that, for datagram and raw sockets, a net_security_setnum(num=-1) followed by a successful input operation will still cause net_security_getnum to return -1; datagram and raw- socket applications that want to know which request was matched MUST use control messages to receive that information. For stream sockets, this function will return the number of the matched request because the original setting of negative one will have been replaced by the matched request number as part of the latching process. Both of these calls SHOULD be implemented as much as possible in kernel space and SHOULD be implemented as a setsockopt() and getsockopt() call, respectively. 3.7.4. Request Selection Control Message Functions int net_security_receivenum(int s, int onoff); int net_security_cmsgtonum(void *cmsg, int cmsglen, int *num); int net_security_numtocmsg(int num, void *cmsg, int *cmsglen, int maxlen); If the socket is a datagram or raw socket, the application can, on a per-packet basis, set the recipe to use for a particular packet or get the recipe matched for a particular packet. This is done using the sendmsg() and recvmsg() functions' control message facility and these functions. This can be used, for example, to "reflect" security Metz Expires in 6 months [Page 21] Internet Draft Network Security API for Sockets 23 January 1998 properties. Note that all of these operations MUST fail with errno=EPERM for stream sockets. Whether or not the request number is returned for input operations must be selected in advance by the application. The net_security_receivenum() function is used to turn receipt on and off. A value of one for onoff turns receipt on; a value of zero for onoff turns receipt off. Implementations MUST return EINVAL for any other value of onoff. The net_security_cmsgtonum() function is used to parse a control message pointed to by cmsg of length cmsglen and return the message recipe number in num. If no received message number information is available, this function MUST return an error. The net_security_numtocmsg() function is used to add a request to use the recipe number num to the control message cmsg with a current length of cmsglen and a total allowable length of maxlen. If successful, the current length is updated. The net_security_receivenum() function SHOULD be implemented as much as possible in kernel space and SHOULD be implemented as a setsockopt() call. The net_security_cmsgtonum() and net_security_numtocmsg() calls SHOULD be implemented entirely in user space. Note that calling sendmsg() and recvmsg() with an appropriately constructed message header is the responsibility of the application. 3.7.5. Identity Selection Functions int net_security_setlocalident(int s, int identtype, char *identstr); int net_security_getlocalident(int s, int *identtype, char *identstr, int maxlen); int net_security_setremoteident(int s, int identtype, char *identstr); int net_security_getremoteident(int s, int *identtype, char *identstr, int maxlen); The net_security_setlocalident() and net_security_setremoteident calls set the local and remote identities for I/O operations. If the socket is a stream socket that has been latched, these calls MUST fail with errno=EPERM. The net_security_setlocalident() function is used to select the local identity used for security associations allocated to the socket s. If identstr is NULL, the actual identity string used will be generated by the system based on the application's process state and the value of type. If identstr is not NULL, the identity string in Metz Expires in 6 months [Page 22] Internet Draft Network Security API for Sockets 23 January 1998 identstr SHOULD be passed to key management for negotiation of security associations. This function SHOULD check to make sure that identstr has a content that is valid for the given identtype and that its content is appropriate for the application's process state. For example, when using a USER_FQDN identity certificate, it is usually an error for an application to specify a certificate where the local username resolves to a user ID that is not one of those available to the process. Systems MUST take care NOT to trust the application or any part of this function that might reside in user space or identity spoofing attacks may result. The net_security_setremoteident() function is used to select the remote identity used for security associations allocated to the socket s. If identstr is NULL, any identity of the specified type will be accepted. If identstr is not NULL, the identity string in identstr SHOULD be passed to key management for negotiation of security associations. Security associations with an identity string other than the one specified MUST NOT be allocated to the socket s. Note carefully that use of this function without a priori knowledge of exactly what identity information a remote system will send will result in an application being unable to communicate. Most applications SHOULD NOT use this function at all and SHOULD instead leave this decision making completely to the policy engine. The net_security_getlocalident() function is used to retrieve the local identity used for security associations allocated to the socket s. It is only valid for stream sockets; if used on a datagram or raw socket, this function MUST fail with errno=EPERM. The innermost local identity latched into the socket state is returned. If no identity information is present (for example, because the socket is not yet connected or because that operation didn't result in the allocation of security associations), this function MUST fail with errno=ESRCH. If the local identity string used is longer than maxlen, this function must fail with errno=ENOSPC. The net_security_getremoteident() function is used to retrieve the remote identity used for security associations allocated to the socket s. It is only valid for stream sockets; if used on a datagram or raw socket, this function MUST fail with errno=EPERM. The innermost remote identity latched into the socket state is returned. If no identity information is present (for example, because the socket is not yet connected or because that operation didn't result in the allocation of security associations), this function MUST fail with errno=ESRCH. If the local identity string used is longer than maxlen, this function must fail with errno=ENOSPC. All four of these calls SHOULD be implemented as much as possible in kernel space. The first two SHOULD be implemented as setsockopt() Metz Expires in 6 months [Page 23] Internet Draft Network Security API for Sockets 23 January 1998 calls; the second two SHOULD be implemented as getsockopt() calls. 3.7.6. Identity Selection Control Message Functions int net_security_receiveident(int s, int which); int net_security_cmsgtolocalident(void *cmsg, int cmsglen, int *identtype, char *identstr, int maxlen); int net_security_localidenttocmsg(int identtype, char *identstr, void *cmsg, int *cmsglen, int maxlen); int net_security_cmsgtoremoteident(void *cmsg, int cmsglen, int *identtype, char *identstr, int maxlen); int net_security_remoteidenttocmsg(int identtype, char *identstr, void *cmsg, int *cmsglen, int maxlen); If the socket is a datagram or raw socket, the application can, on a per-packet basis, set the identities to use for a particular packet or get the identities used for a particular packet. This is done using the sendmsg and recvmsg functions' control message facility and these functions. This can be used, for example, to "reflect" security properties. Note that all of these operations MUST fail with errno=EPERM for stream sockets. Whether or not identity information is returned for input operations must be selected in advance by the application. The net_security_receiveident function is used to select which, if either, identities are returned for received packets. The argument which is a bitmap field; if the bit with value NET_SECURITY_RECEIVEIDENT_LOCAL is set, local identities are to be received, and, if the bit with value NET_SECURITY_RECEIVEIDENT_REMOTE is set, remote identities are to be received. Implementations MUST return EINVAL if any other bits are set. Applications SHOULD not have receipt of identities enabled unless they are really going to use that information because the processing involved in making that information available to the application might be expensive. The net_security_cmsgtolocalident() and net_security_cmsgtoremoteident() functions are used to parse a control message pointed to by cmsg of length cmsglen and return the identity type in identtype and the identity string in identstr. If the identity string is longer than maxlen or no received message number is available, this function MUST return an error. The net_security_localidenttocmsg() and net_security_remoteidenttocmsg() functions are used to add a request to use the identity type identtype and, if not NULL, the identity string identstr to the control message cmsg with a current length of cmsglen and a total allowable length of maxlen. If successful, the current length is updated. Metz Expires in 6 months [Page 24] Internet Draft Network Security API for Sockets 23 January 1998 The net_security_receiveident() function SHOULD be implemented as much as possible in kernel space and SHOULD be implemented as a setsockopt() call. The other calls SHOULD be implemented entirely in user space. Note that calling sendmsg() and recvmsg() with an appropriately constructed message header is the responsibility of the application. 3.7.7. String Conversion Functions int net_security_strtorequest(char *str, struct net_security_operation *ops, int *numops); int net_security_requesttostr(struct net_security_operation *ops, int numops, char *str, int maxlen); The most common use of this API is intended to be where a user specifies a string parameter to the application that represents the user's requested security properties. These functions convert such a string to a request that can be passed to a kernel and vice versa. The net_security_strtorequest() function converts the string str to a request suitable for passing to net_security_setrequest(). The request is returned in the buffer pointed to by ops. Note that numops MUST be initialized to the maximum number of operations that may be stored in the buffer ops before this call and the actual number of operations returned is stored in numops upon success. The net_security_requesttostr() function converts the request ops of length numops to a string suitable for printing. The string is returned in the buffer pointed to by str. If the string would be longer than maxlen, this function MUST return an error. These functions SHOULD be implemented in user space. 3.7.8. EPOLICY This API defines a new errno value, EPOLICY. This value shall have the string definition (for functions like strerror()) of "Operation failed by policy". Output operations that fail as a result of a request and/or the system policy (for example, if require or never preferences are involved) MUST return this value. Input operations that result in a condition where the system would never receive packets for a socket (due to the interaction between the system's policy and the application's request) MUST fail and return this value. [cmetz: I know that I should avoid defining a new errno value because it's painful for people, but I think that this is an error condition Metz Expires in 6 months [Page 25] Internet Draft Network Security API for Sockets 23 January 1998 that needs to be handled separately.] 3.7.9. Blocking Behavior System implementations MUST make security operations either blocking or non-blocking depending on the application's I/O style request. That is, if the application has requested non-blocking I/O (e.g., via fcntl(F_SETFL, ...)), then security operations MUST also be non- blocking, and vice versa. For blocking I/O, the application MUST block while key management operations are taking place and while security processing (including policy processing and cryptographic operations) is performed. For non-blocking I/O, the application MUST be allowed to be execute while key management operations are taking place and SHOULD be allowed to execute while security processing (including policy processing and cryptographic operations) is performed (within the constraints of a time-slicing system, if appropriate). In the case of use and default preference levels, if SA negotiation is in progress and the system's policy configuration would allow the packet to be transmitted without security processing (e.g., as cleartext), the system MAY do so until a SA has been successfully negotiated. 4. Discussion Applications that use the loopback interface SHOULD request that security services NOT be provided for that communication (i.e., preference=never). An application has no choice but to trust that its OS is doing the right thing. Therefore, encryption and authentication of data over the loopback interface is usually nothing more than a waste of system resources. There are some cases where it's useful to have the system provide security services on loopback traffic, but implementations typically blindly request encryption and authentication without checking for loopback and end up just wasting cycles. Systems SHOULD have policy rules that aggressively prevent applications from actually doing encryption and authentication over loopback. [cmetz: more stuff will go here] Future Work This document needs lots of examples and discussion of border cases. This will be coming in a future revision. A companion document describing one possible design and implementation of a policy engine is in progress. Metz Expires in 6 months [Page 26] Internet Draft Network Security API for Sockets 23 January 1998 Acknowledgments Randall Atkinson, Ron Lee, Daniel L. McDonald, and Chris Winters provided useful feedback on earlier versions of this document. Parts of this work was done 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), U.S. Space and Naval Warfare Systems Command (SPAWAR) and the Computing Systems Technology Office, Defense Advanced Research Projects Agency (DARPA/CSTO). The author and his co-workers really appreciate their sponsorship of NRL's network security efforts and their continued support of IPsec development. Without that support, this document, among many others, would not exist. The "CONFORMANCE and COMPLIANCE" wording was taken from [MSST97]. The author has created a mailing list for discussion of this specification and implementations of it. If you would like to be added to this list, send a note to . References [Atk95a] Randall J. Atkinson, "IP Security Architecture", RFC 1825, August 1995. [Atk95b] Randall J. Atkinson, "IP Authentication Header", RFC 1826, August 1995. [Atk95c] Randall J. Atkinson, "IP Encapsulating Security Payload", RFC 1827, August 1995. [MMP97] D. L. McDonald, C. W. Metz, B. G. Phan, "PF_KEY Key Management API, Version 2", Internet Draft, July 1997. [MSST97] Douglas Maughan, Mark Schertler, Mark Schneider, Jeff Turner, "Internet Security Association and Key Management Protocol (ISAKMP)", Internet Draft, February 1997. [Piper97] Derrel Piper, "The Internet IP Security Domain of Interpretation for ISAKMP", Internet Draft, February 1997. Disclaimer The views and specification here are those of the author and are not necessarily those of his employer. His employer has not passed judgment on the merits, if any, of this work. The author and his Metz Expires in 6 months [Page 27] Internet Draft Network Security API for Sockets 23 January 1998 employer specifically disclaim responsibility for any problems arising from correct or incorrect implementation or use of this specification. Author's Address Craig Metz The Inner Net Box 10314-1933 Blacksburg, VA 24062-0314 (DSN) 354-8590 cmetz@inner.net Revision History 01 Replaced all setsockopt()/getsockopt() calls with function calls expected to be front-ends; this follows POSIX p1003.1g's lead and might also be easier to implement on some systems. Replaced identity specification on each E/A request with a selection global to all requests. Split function calls up into separate sections. Removed magic cookie. Removed the "request" as a block of "recipes" and renamed "recipe" to "request"; made other changes that follow from this. Updated system organization graph. 00 Initial draft. Metz Expires in 6 months [Page 28]