Find file
Fetching contributors…
Cannot retrieve contributors at this time
455 lines (309 sloc) 16.2 KB
libike 0.9.6
Libike (aka charon) is an IKE exchange management library.
This document describes general libike structure, its functioning
principles and API. The document is work-in-progress, some sections
are incomplete and are subject to further expansion.
Copyright (c) 2003-2011 Alex Pankratov. All rights reserved.
The library is distributed under terms of BSD license.
You can obtain the copy of the license by visiting:
Libike allows its users to engage in IKE exchanges (subject to
limitations listed below) as per RFC 2407, 2408, 2409, etc.
The library implements ISAKMP packet processing, IKE state management
and various miscellaneous functionality such as handling of packet
retransmissions, SA lifetime tracking, etc.
The library does NOT provide means for defining, maintaining and
querying security policies; it delegates this functionality to the
external code via the callback mechanism instead.
The library operates in the scope of a compact framework that serves
as an OS glue and creates a platform-independent environment for the
rest of the code. The framework defines the types, the methods and
the naming convention to be used by the code to access OS and run-
-time services.
Please refer to the headers in x4/core directory for further details.
The framework is very compact and takes minimal amount of time to port
to a new platform.
This directory contains headers defining various network related
types, constants and structures.
Miscellaneous commonly used types and functions.
Crypto interface, which includes pseudo-random, hashing and symmetric
encryption algorithms, Diffie-Hellman KE, PKI methods, etc
Libike source code itself
The code for testing libike. Subject to frequent changes.
The API consists of exactly 8 methods, which are the only library
entry points. The library code is executing if and only if one of
these methods was called and has not returned yet. From time to
time libike will pass the execution back to the user via a callback
to either query some data, perform validation or report an event.
The library is reentrant in a sense that the callback code is allowed
to issue API calls.
The library is designed for a serialized use, ie no two threads
should be executing in the library simultaneously. The task of
access serialization is a responsibility of libike user.
The library implements recursion control, which is used for a mark-
now-purge-later approach of a data disposal. For instance, calling
x4_charon_term1() from the callback will not immediately dispose
associated internal data, but will rather delay the cleanup until
the outmost call to the library is about to return. All this is
transparent to the user, except it's advised to allow call recursion
to drop periodically to 0 to facilitate timely memory disposal.
Just for the record - the recursion control is used primarily to
avoid reference counting of individual datamodel elements, ie -
to simplify data housekeeping.
The following pseudocode outlines the execution flow during a typical
libike use. The example assumes phase 1 is authenticated with preshared
key and phase 2 is locally initiated (not responded).
.... an initialization ....
charon_init(...); // initialize libike
s = socket(); // create a UDP socket for talking to the peer
.... an IKE exchange ....
charon_init1(...); // initiate phase 1 exchange
# receive ph1_initiated() // remember 'exchange context'
# receive ph1_get_psk(), // return the PSK
# receive ph1_send(), // send data to the peer
while (ph1 is in progress) // enter select() loop
if (select(s, 1sec) == can_read) // wait for an inbound data
recv(s, data, len);
charon_recv(data,len); // pass received data to libike
# receive ph1_send() // more data for the peer
# receive ph1_completed() // phase 1 completed
# - or -
# receive ph1_disposed() // phase 1 failed to complete
charon_tick(); // let libike do some timed work
if (ph1 has failed) // ie received ph1_disposed()
charon_init2(...); // initiate phase 2 exchange
# receive ph2_get_spi() // return random local SPI
# receive ph2_initiated() // remember 'exchange context'
# receive ph2_send() // send(s) data to the peer
while (ph2 is in progress)
if (select(s, 1sec) == can_read)
recv(s, data, len);
# receive ph2_send()
# receive ph2_completed() // phase 2 completed
# - or -
# receive ph2_disposed() // phase 1 failed to complete
.... a use of the SA ....
# may receive ph1_disposed() // received INF/DELETE message
# may receive ph2_disposed()
.... a shutdown ....
charon_term1(...) // dispose phase 1 SA
# receive ph1_disposed()
charon_term2(...) // dispose phase 2 SA
# receive ph2_disposed()
charon_term() // shutdown libike
Note that ph2_completed() callback will provide its recipient with SPI
and keying material for a negotiated SA. Libike can also be configured
to outsource scheduling of packet retransmissions, in which case the
user will also be receiving ph*_resend() callbacks.
Libike datamodel is very simple - a list of phase 1 exchanges, each
containing a list of associated phase 2 exchanges. Calls to API may
create, remove or modify these lists as dictated by IKE logic. These
changes are often accompanied by notification callbacks allowing a
user to track datamodel changes.
For every exchange being added to the datamodel libike calls a user
back and provides an 'exchange context'. This context is a pointer
to an internal libike structure, which must be passed to API calls
to identify the exchange.
OTOH, the user can associate its own arbitrary context value with
every new exchange, and libike will pass it to every callback issued.
This context is further referred to as a 'callback context'.
This section gives a quick view at a libike API. The API is comprised
of the following methods:
The library must be brought up into a functional state by a call to
this method, which performs some trivial data initialization. Every
call to init() must be complemented by a call to term().
When the external code no longer needs libike services, it calls this
method, which will cleanup the datamodel by disposing of previously
allocated elements. The call to term() must be preceded by a call to
This method initiates new phase 1 exchange. Libike will inspect
supplied phase 1 configuration, create supporting internal structures,
query some additional information from the user via callbacks, prepare
the ISAKMP message and submit it to the user for the actual transfer.
It will advance the state of the exchange and return from the init1().
This method initiates new phase 2 exchange in the context of existing
and completed phase 1 exchange. The latter is identified by an
'exchange context' that libike passed to the user during phase 1
This method notifies libike that particular phase 1 exchange is no
longer valid. This does not affect associated phase 2 exchanges, which
will remain intact or even be able to complete, but will not allow to
initiate any new ones.
Similar to term1() this method invalidates phase 2 exchange.
Since libike does not read the data from the network, it must be fed
the IKE traffic via charon_recv() method. The socket information is
passed to the recv() together with ISAKMP message to demultiplex the
message between existing phase 1 and phase 2 exchanges.
Libike handles retransmission of the ISAKMP messages and thus requires
scheduled execution to perform timed activity. Calling charon_tick()
does just that - enables libike to serve timed tasks.
Callbacks are the essential part of libike design. They provide means
for decoupling the library from the specifics of packet transmission,
policy management and SA lifetime tracking.
There are 11 phase 1 callbacks and 8 phase 2 ones. Callbacks for both
phases share common functional purpose and fall into 4 categories -
- Status, Validation, Transmission and Query.
All callbacks, except for ph2_get_spi(), receive a 'callback context'
as a first parameter. This is a user-supplied value, which is used to
identify particular exchange to the user. How exactly the value gets
associated with an exchange is covered at the end of the next section.
Callbacks of this type notify a user on an initiation, completion and
disposal of an exchange.
ph*_initiated() is issued when the new exchange has just been added to
the libike lists. The callback will occur during processing of the
charon_init*() API call. If libike cannot initiate an exchange, it will
not call ph*_initiated() and charon_init*() will return bfalse.
The second parameter is an 'exchange context', which identifies
libike internal exchange structure. This context is required
for calling charon_term*() methods and for initiating phase 2
ph*_disposed() is issued when existing exchange is invalidated and marked
for the removal from libike lists. For every ph*_initiated() call
libike guarantees a respective ph*_disposed() call. The exchange may get
disposed due to the variety of reasons including a call to charon_term*(),
a failure to transmit ISAKMP message, a retransmission timeout, receiving
an INF/DELETE message from the peer, etc. Note that disposal of the phase
1 exchange does not automatically mean disposal of child phase 2 exchanges.
ph*_completed() notifies a user that specified exchange has been completed.
For phase 1 exchange it means it's now possible to call charon_init2() to
initiate phase 2 exchange.
The second parameter for ph2_completed() contains SPI values and keying
material for a negotiated IPsec SA.
ph1_sa_used() notifies a user that ISAKMP SA has just been used to either
encrypt or decrypt some data. The callback is provided for scenarios where
lifetime of ISAKMP SA is being tracked.
The second parameter is an amount of the processed data in bytes.
ph2_responded() notifies a user that libike received phase 2 exchange
initiation request from the peer. The request has been verified to be in
a scope of existing phase 1 exchange, accepted for further processing and
placed on the exchange lists.
Unlike for other callbacks, the first parameter is a callback context
of an associated phase **1** exchange. This allows a callback recipient
to link new exchange to its parent phase 1 exchange.
The second parameter is an 'exchange context' similar to the one passed
to the ph*_initiated() call.
The callback return value is a 'context callback' value that libike will
associate with a newly created phase 2 exchange.
The 'callback context' value for locally initiated exchanges is taken from
a 'userdata' field of ike_config* parameter passed to charon_init*() call.
These callbacks are issued to perform external to libike validation of
peer's data.
ph1_validate() is used to validate ID, CERT_REQUEST and CERT payloads
received from the peer during phase 1 exchange.
The second parameter points at the body of respective ISAKMP payload,
the third parameter defines the type of the payload submitted for
The callback must return btrue to accept the payload and resume
further inbound processing. Returning bfalse signals libike to
discard the ISAKMP message and keep exchange in its current state.
For certificate-based phase 1 exchanges the successful validation
is CERT payload authenticates the peer. Thus the callback code must
verify the certificate is of an accepted type, it comes from a
recognized CA and not on the revocation list and perform all other
relevant certificate checks.
Also note that if CERT_REQUEST has been successfully validated, libike
will issue ph1_get_cert() call, which *must* succeed. In other words,
accepting CERT_REQUEST means that requested certificate is at hand
and is ready to be passed to libike when requested. If the certificate
is not available, validate() must return bfalse, which will cause
libike to abort and dispose the exchange.
ph2_validate() callback is issued to validate SA proposal made by the
peer when acting as a responder side in phase 2 exchange. The callback
must return btrue if the proposal is accepted and bfalse otherwise.
ph*_send() callback requests external code to commence the transmission
of provided data buffer to the peer. The peer must use 'callback context'
to identify the exchange and the peer this data is destined for. Returning
bfalse from the callback signals a fatal transmission error, which causes
libike to immediately dispose the exchange.
ph*_resend() is an optional callback allowing to control retransmission
behavior of libike. The resend() is first called in 1 second after
initial packet is sent out and then every time the retransmission is
about to happen. This may happen under two circumstances:
* libike has received a copy of the packet it recently replied to
* libike has not received a reply from the peer and assumes that
the packet it previously sent is now lost
ph1_natt() callback is invoked to indicate NAT traversal related activity.
See natt.txt first for a quick overview of NAT-T.
Libike user may enable support for one or more IKE NAT-T drafts by OR'ing
ike_natt_xx constants into s1_config.natt bitmask. This causes libike to
announce its NAT-T capabilities to the peer by sending respective Vendor
ID payload in the first main mode message. If the peer happens to support
the same NAT-T draft(s), libike will send and receive NAT discovery
payloads in 3rd and 4th messages. If NAT is detected, ph1_natt() is
invoked exactly before 5th message processing. If selected draft requires
port 'floating', the second parameter will point at net_link structure,
which must be adjusted to new port values. From this point ISAKMP messages
must be exchanged with the peer over new ports and using IKE-in-ESP-UDP
encapsulation. The third parameter indicates if the localhost is behind
the NAT (which normally translates into the need for keepalives if it is).
Libike uses callbacks from this group to obtain additional crypto and SA
information it needs in a course of the exchange processing.
ph1_get_psk() returns preshared secret for a specified exchange.
ph1_get_cert() returns x.509 public key certificate to be used for local
host identification.
ph1_get_prikey() returns local private key in PKCS1 format.
ph1_get_pubkey() returns peer's public key used to verify its signature.
The key is requested only if the peer has not sent a certificate in its
6th phase 1 message.
ph2_get_spi() requests external code to generate new local SPI value for
phase 2 SA.
$Id: manual.txt,v 1.8 2003/04/28 04:29:22 alex Exp $