Before an 802.11 device can be used, it must become associated with a
network by exchanging a series of management frames with the Access
Point. In some cases there is additional security handshaking required
after association. gPXE's 802.11 layer handles this transparently by
always having the association task running if association has not yet
succeeded, and using the link-up bit in the wrapping net_device
to
indicate association status. Commands such as DHCP or autoboot will
therefore block on association success, and time out if an underlying
error condition rather than a transient failure is responsible for
association not working.
After association has succeeded, reassociation will be attempted upon a change in the SSID setting. It is not necessary to reassociate when the encryption key is changed, because association can only succeed with an invalid key on an open-system WEP network, and rekeying does not require reassociation in that case. If the user specified the encryption key erroneously at first, association will fail and be retried indefinitely, and will succeed soon after the correct key is provided.
Association is handled in a separate gPXE process, which runs through a series of states in predetermined order:
net80211_probe_*()
functions, which are exported to allow for user-level code like iwlist
to use them as well. Once a good AP has been found, the NET80211_PROBED
bit is set in dev→state
, and the dev→ctx
union should henceforth be understood to contain an association context rather than a probe context.net80211_device
state (BSSID, ESSID, supported rates) is set to reflect it and an authentication packet is sent using the Open System authentication method. If the access point uses WEP and requires Shared Key authentication, it will respond with a failure indication and gPXE will attempt Shared Key authentication instead. If that fails as well, association restarts. Otherwise, the NET80211_AUTHENTICATED
bit is sert in dev→state
. If the AP does not respond within one second, the authentication packet will be resent a few times.NET80211_ASSOCIATED
bit is set in dev→state
.step()
function (see below), the NET80211_CRYPTO_SYNCED
bit is set in dev→state
and the wrapping net_device
is set link-up. Data traffic can now proceed.The gPXE 802.11 layer includes abstract support for encrypted wireless networks, as well as implementations of that support for WEP- and WPA-protected networks. We introduce two abstractions: a security handshaker and a wireless cryptosystem.
A security handshaker is responsible for everything that must occur
before encrypted packets can be sent and received: deriving a master
encryption key from the user's specified net0/key
, securely
authenticating to the Access Point by some specified means, possibly
deriving more keys from the master key and using them instead of or in
addition to the master key, and specifying encryption keys and a
cryptosystem to use them for packet-level security. The currently
implemented security handshakers are “trivial”, WPA-Personal (PSK /
pre-shared key), and WPA-Enterprise (802.1X / EAP). The trivial
handshaker, used for WEP, just does some sanity checks on the
user-specified key and then installs it directly without a further
exchange with the AP. The WPA handshakers interface with a common WPA
core, supplying it with a pairwise master key derived either from the
user's passphrase (for Personal) or the EAP Master Session Key (for
Enterprise).
It is not possible to manually specify the security handshaker to be
used for a network; it is autodetected by _sec80211_detect()
in
net/80211/sec80211.c
. gPXE's linker tables are searched for a
handshaker of the requisite type, a new structure is allocated with
space for the requested amount of private data, and
dev→handshaker
is set to point at it. The dev→handshaker
pointer remains valid from the time the handshaker's init()
method
is called to the time its stop()
method is called.
Additional security handshakers are declared using gPXE's linker table
mechanism, by defining a structure of type net80211_handshaker
tagged with the _
_net80211_handshaker
attribute, containing
the following fields:
protocol
: one of the enumeration values of net80211_security_proto
indicating the type of network with which the handshaker should be used. For a new type of network it is necessary to modify the function _sec80211_setect()
in net/80211/sec80211.c
.priv_len
: the size of the private data area to allocate. When any of the methods below are called, dev→handshaker→priv
will point to this many bytes of dynamically allocated memory.init()
: a method called before the association process starts, to set up whatever is needed for association to succeed. In WPA, this sets dev→rsn_ie
to a pointer to the RSN information element that is to be advertised in the association request frame. In the trivial handshaker, this method installs the cryptosystem directly, and start()
and step()
are NULL
.start()
: a method called immediately before the association request frame is sent, to set up things so that security packets received in response to it will be handled appropriately.step()
: called continually by the association process as long as it returns 0. It is expected to process security handshaking packets, or monitor the progress of their processing by an asynchronous handler, and indicate when a definitive success or failure state has been reached.change_key()
: called whenever the netX/key
setting might have changed, to update the cryptosystem with the new key if that makes sense for this type of handshaker. (It does for WEP/trivial, but not for WPA, because it's not possible to associate with the wrong key on WPA.)stop()
: called just before the handshaker is freed due to reassociation or closing of the 802.11 device. This method can free memory and clean up resources.When a security handshaker has finished negotiating whatever handshaking is necessary, it will usually need to finish its job by setting up the 802.11 device to use some sort of cryptography on all future data packets. This is accomplished by the function:
#include <gpxe/sec80211.h> int sec80211_install ( struct net80211_crypto **which, enum net80211_crypto_alg crypt, const void *key, int len, const void *rsc );
Install the 802.11 cryptosystem of type crypt
for packets handled
by which
, which is &dev→crypto
for unicast RX and all TX
packets, or &dev→gcrypto
for broadcast RX packets. The encryption
key is len
bytes and is pointed to by key
. If applicable to the
cryptosystem in question, the initial receive sequence counter is pointed
to by rsc
with a cryptosystem-dependent length; NULL
can be passed
for an RSC of zero.
The currently defined values for crypt
are:
NET80211_CRYPT_NONE
: an open network with no securityNET80211_CRYPT_WEP
: WEP encryptionNET80211_CRYPT_TKIP
: TKIP, the original encryption method for WPANET80211_CRYPT_CCMP
: CCMP, a better encryption method added in WPA2NET80211_CRYPT_UNKNOWN
: anything else
Supporting a new crypto method, if one ever comes into use, is a
matter of updating net/80211/sec80211.c
to recognize it (if it
uses WPA handshaking, edit the rsn_cipher_map
array at the
beginning of that file) and adding a cryptosystem to implement it.
Similarly to security handshakers, cryptosystems are provided using a
linker table: define a struct net80211_crypto
tagged with
_
_net80211_crypto
and including some or all of the following
fields:
algorithm
: the net80211_crypto_alg
enumeration value implementedpriv_len
: the size of private data to allocateinit()
: initialize cryptosystem with a specific key and RSCencrypt()
: make an encrypted copy of an I/O buffer containing a frame to transmitdecrypt()
: make a decrypted copy of an I/O buffer containing an encrypted frame that was received
The cryptosystem implementations are expected to be “pure”; that is,
they should not need to access any properties of the 802.11 device
that are not contained in the frame headers themselves. To enforce
this expectation, they are not provided with a net80211_device
pointer.
The implementation of sec80211_install
simply searches for a
cryptosystem implementing the requested algorithm, allocates storage
for it in dev→crypto
or dev→gcrypto
, and calls its
init()
method with the key and RSC it is given.
The 802.11 code is extensively documented using Doxygen comments, on both API and internal functions. To further understand what it's doing, I recommend the following sources:
net/mac80211/
and, to a lesser extent, net/wireless/
); while much more complicated than gPXE's implementation, it is an excellent, robust, and well-documented example of an 802.11 stack.wpa_supplicant
, which handles the WPA Four-Way Handshake implemented by gPXE in net/80211/wpa.c
.The standard is divided into 19 “clauses” (roughly chapters) and 16 “annexes” (appendices). You certainly do not need to read all of them. The useful ones are
If you run into something you can't figure out, email me (username oremanj located at the domain rwcr in the TLD .net) and I'll be happy to try and clarify.