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 <net/security.h>.
Inclusion of the file <net/security.h> 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 <net/security.h>. 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 <net-security-api-
request@inner.net>.
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]