This module provides an abstract data-type representing individual ISAKMP messages. Internally, the messages are subdivided and indexed by payload type. Exported functionality consists of creation/destruction, incremental payload addition, parsing, validation, and context lookup of incoming messages, registering of post-send functions, transport-independent send logic, and message debugging. There is also generic SA negotiation logic which is covered in the implementation details section below. The reason for this logic being here is because it is driven by the physical message layout.
A fairly simple module accepting registration of functions to call at specific times together with their actual parameter. In order to get the functions called, the time module exports a function that calculates the timeout parameter to give the select call of the main loop, as well as the actual timer run function. Removal and reporting (for debugging) of timers is also supported.
These modules deal with the communication with the application for which isakmpd is negotiating SAs. Currently, only one application is supported, IPsec. Communication with it occurs through various system-dependent APIs. Operations that need to be supported include getting a fresh SPI, creating an SA, updating a ``larval'' SA, grouping SA bundles, and, finally, removing SAs. Also needed is a means for telling the IPsec stack that ISAKMP traffic needs to be unencrypted. In OpenBSD, this is achieved by setting the appropriate setsockopt(3) options in the isakmpd socket.
The transport module exports an abstract data-type representing a specific transport. It has an associated function pointer table, just like the common vtables that C++ compilers create in order to implement polymorphism. Thus the transport structure is really a base class for the real transport classes. There is just one such class at the moment, the udp class. Exported functionality consists of creation/destruction (or rather reference/dereference as they are ref-counted) of transports, getting file descriptors ready for I/O to use in the select loop of main(), as well as checking them for I/O possibility afterwards. Message sending and reception methods are exported as well, along with endpoint address determination.
This module is really just a simple command line interpreter. It conveniently accepts commands asynchronously through a one-way FIFO (named pipe). The commands are rudimentary, one letter with a few parameters each. The existing controls deal with issues like debugging, SA management, and dynamic changes to the configuration database.
isakmpd maintains a configuration database consisting of section/tag/value triplets, i.e. it maps closely to a well known format called ``.INI''. This configuration database is primed from the configuration file (.INI-style) at program start, and every time a HUP signal is sent to the isakmpd process. It is also possible to dynamically alter the database via the UI module. There is functionality to treat the value of a triplet as a comma-separated list, and easily ``walk'' that list. Otherwise, ordinary database operations like creation, lookup, and removal of entries are exported.
See section 5 for a description of this module. This module exports only one function, which is called to validate a combination of SA proposal, remote peer identity, and packet selectors (Phase 2 IDs).
A key abstraction in isakmpd is the exchange. This is the engine that drives the negotiations towards SA establishment. Exchanges form the context of all negotiations, and closely map to the exchange concept of the RFCs. Every exchange is a well-defined, fixed-length sequence of messages between the two peers. Every individual message also has a well-defined minimum content of payloads. This structure of exchanges lends itself to implementation as a generic finite state machine driven by ``scripts'' supplied by each exchange type. These scripts provide the actions to execute at message reception as well as before/after message transmission. It is also easy to have a generic ``syntax checker'' inspecting each message, ensuring the required payloads are present. This module's exported API consists of functions for establishing exchanges when acting as initiator, as well as setting up exchanges for ``incoming'' negotiations. There are also several lookup functions, finding exchanges using different criteria.
Just like the IPsec kernel, isakmpd needs to maintain its own SA database. This database actually consists of both ISAKMP SAs, which are the results of Phase 1 negotiations, and application SAs from Phase 2. Every SA has attached DOI-dependent (Domain Of Interpretation) data, should we ever need to support other DOIs than IPsec. The SA structure contains both the on-the-wire representation of the SA, as well as internal per-SA data. SAs are created when the negotiation starts, but are inactive until an exchange finalization routine is run. The SA API is mostly a set of life maintenance functions, i.e. creation, ref-counting, expiration setup, and destruction operations. Similar to the exchange module, a fairly versatile set of lookup functions is available.
IKE allows for several kinds of authentication. An authentication method needs to provide just three functions: generation of a shared secret the peers derive keys from, encoding of a keyed hash proving the authenticity of the peer, and decoding of such a hash thereby verifying the other peer's authenticity. Currently isakmpd supports the mandatory pre-shared key authentication method, as well as certificate based (X.509) RSA signature authentication. We plan to support public key encryption-based authentication in the near future.
Isakmpd builds upon some basic cryptographic and mathematic components.
There is a collection of ciphers which can be used interchangeably to protect the data that goes on the wire. It is natural to implement these ciphers as subclasses to a ``crypto'' base class, which provides hooks for initialization, cloning, and updating of key state, as well as encryption and decryption of data. The separation of key state management from the actual algorithm applications is important for maintaining cryptographic synchronization between the peers. isakmpd implements the following algorithms: DES, 3-DES, CAST, and Blowfish.
As was the case with ciphers, it is also a design requirement that hash algorithms be easy to alter. Thus, hash algorithms are also implemented as subclasses of a generic hash class, providing a simple API for incremental hash computation of concatenated data.
The Diffie-Hellman algorithm is a means of establishing a shared secret between two peers without exposing sufficient data for wire-tappers to compute that secret. The API is simple, since only two functions are needed: creation of a local random big-integer, and computation of the actual secret based on the local big-integer and a similar-type value received from the peer.
The mathematical basis for Diffie-Hellman is called group math. Groups are big-integer arithmetic systems with a few parameters. It turns out that groups are also suitable to implement in an object-oriented fashion, as there are different algorithms that comply with the group math requirements. In isakmpd, there is support for two kind of groups, elliptic curves and modP groups.
Both group mathematics and the public key cryptography used in the authentication and policy modules, need big-integer math. We currently use OpenSSL's BN functions as well as a few supplementary routines written by us. We have however made the underlying math library exchangeable so other math libraries can be used if needed. We currently support FSF's GMP but we also intend to take advantage of hardware support for big-integer operations, since such products have begun to make their appearance in the market.
Perhaps a less obvious component to have in a daemon like isakmpd is a module for dynamic loading and linking of code. The reason for this module is mainly due to the RSA patent; we cannot ship RSA code in OpenBSD as the license-free implementation cannot be imported to the United States. Therefore, we dynamically load that support if it is available (the supporting libraries can be fetched separately, different versions for different countries). This module exports a function that takes a dynamic load script, written in a very simple language we designed, that describes what files should be loaded and what symbols should be resolved.
Logging is crucial in security applications. It is also important that developers of security software are presented with debugging tools that help them find bugs faster. We consider logging to be such a tool, if it can be controlled in a fine-grained way. This module exports functions to change the levels per logging class, to control where logging information goes and, naturally to actually log both binary and textual buffers.
In order to maintain portability, every function that may need differing implementations depending on the platform, needs to be placed in a central, exchangeable, system-dependent module. Most often, functions placed here are glue or proxies.