iPXE
Functions | Variables
wpa.c File Reference

Handler for the aspects of WPA handshaking that are independent of 802.1X/PSK or TKIP/CCMP; this mostly involves the 4-Way Handshake. More...

#include <ipxe/net80211.h>
#include <ipxe/sec80211.h>
#include <ipxe/wpa.h>
#include <ipxe/eapol.h>
#include <ipxe/crypto.h>
#include <ipxe/arc4.h>
#include <ipxe/crc32.h>
#include <ipxe/sha1.h>
#include <ipxe/hmac.h>
#include <ipxe/list.h>
#include <ipxe/ethernet.h>
#include <ipxe/rbg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER)
static int wpa_fail (struct wpa_common_ctx *ctx, int rc)
 Return an error code and deauthenticate.
static struct net80211_cryptowpa_find_cryptosystem (enum net80211_crypto_alg crypt)
 Find a cryptosystem handler structure from a crypto ID.
struct wpa_kiewpa_find_kie (int version)
 Find WPA key integrity and encryption handler from key version field.
int wpa_make_rsn_ie (struct net80211_device *dev, union ieee80211_ie **ie_ret)
 Construct RSN or WPA information element.
int wpa_start (struct net80211_device *dev, struct wpa_common_ctx *ctx, const void *pmk, size_t pmk_len)
 Set up generic WPA support to handle 4-Way Handshake.
void wpa_stop (struct net80211_device *dev)
 Disable handling of received WPA handshake frames.
static void wpa_derive_ptk (struct wpa_common_ctx *ctx)
 Derive pairwise transient key.
static int wpa_install_ptk (struct wpa_common_ctx *ctx, int len)
 Install pairwise transient key.
static int wpa_install_gtk (struct wpa_common_ctx *ctx, int len, const void *rsc)
 Install group transient key.
static int wpa_maybe_install_gtk (struct wpa_common_ctx *ctx, union ieee80211_ie *ie, void *ie_end, const void *rsc)
 Search for group transient key, and install it if found.
static struct io_bufferwpa_alloc_frame (int kdlen)
 Allocate I/O buffer for construction of outgoing EAPOL-Key frame.
static int wpa_send_eapol (struct io_buffer *iob, struct wpa_common_ctx *ctx, struct wpa_kie *kie)
 Send EAPOL-Key packet.
static int wpa_send_2_of_4 (struct wpa_common_ctx *ctx, struct eapol_key_pkt *pkt, int is_rsn, struct wpa_kie *kie)
 Send second frame in 4-Way Handshake.
static int wpa_handle_1_of_4 (struct wpa_common_ctx *ctx, struct eapol_key_pkt *pkt, int is_rsn, struct wpa_kie *kie)
 Handle receipt of first frame in 4-Way Handshake.
static int wpa_send_final (struct wpa_common_ctx *ctx, struct eapol_key_pkt *pkt, int is_rsn, struct wpa_kie *kie)
 Send fourth frame in 4-Way Handshake, or second in Group Key Handshake.
static int wpa_handle_3_of_4 (struct wpa_common_ctx *ctx, struct eapol_key_pkt *pkt, int is_rsn, struct wpa_kie *kie)
 Handle receipt of third frame in 4-Way Handshake.
static int wpa_handle_1_of_2 (struct wpa_common_ctx *ctx, struct eapol_key_pkt *pkt, int is_rsn, struct wpa_kie *kie)
 Handle receipt of first frame in Group Key Handshake.
static int eapol_key_rx (struct io_buffer *iob, struct net_device *netdev, const void *ll_dest __unused, const void *ll_source)
 Handle receipt of EAPOL-Key frame for WPA.
 REQUIRING_SYMBOL (eapol_key_handler)
 REQUIRE_OBJECT (eapol)

Variables

struct list_head wpa_contexts = LIST_HEAD_INIT ( wpa_contexts )
 List of WPA contexts in active use.
struct eapol_handler
eapol_key_handler 
__eapol_handler

Detailed Description

Handler for the aspects of WPA handshaking that are independent of 802.1X/PSK or TKIP/CCMP; this mostly involves the 4-Way Handshake.

Definition in file wpa.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER  )
static int wpa_fail ( struct wpa_common_ctx ctx,
int  rc 
) [static]

Return an error code and deauthenticate.

Parameters:
ctxWPA common context
rcReturn status code
Return values:
rcThe passed return status code

Definition at line 56 of file wpa.c.

References wpa_common_ctx::dev, net80211_deauthenticate(), and rc.

Referenced by eapol_key_rx(), wpa_handle_1_of_2(), and wpa_handle_3_of_4().

{
        net80211_deauthenticate ( ctx->dev, rc );
        return rc;
}
static struct net80211_crypto* wpa_find_cryptosystem ( enum net80211_crypto_alg  crypt) [static, read]

Find a cryptosystem handler structure from a crypto ID.

Parameters:
cryptCryptosystem ID
Return values:
cryptoCryptosystem handler structure

If support for crypt is not compiled in to iPXE, or if crypt is NET80211_CRYPT_UNKNOWN, returns NULL.

Definition at line 73 of file wpa.c.

References net80211_crypto::algorithm, for_each_table_entry, NET80211_CRYPTOS, and NULL.

Referenced by wpa_make_rsn_ie().

{
        struct net80211_crypto *crypto;

        for_each_table_entry ( crypto, NET80211_CRYPTOS ) {
                if ( crypto->algorithm == crypt )
                        return crypto;
        }

        return NULL;
}
struct wpa_kie* wpa_find_kie ( int  version) [read]

Find WPA key integrity and encryption handler from key version field.

Parameters:
verVersion bits of EAPOL-Key info field
Return values:
kieKey integrity and encryption handler

Definition at line 92 of file wpa.c.

References for_each_table_entry, NULL, wpa_kie::version, and WPA_KIES.

Referenced by eapol_key_rx().

{
        struct wpa_kie *kie;

        for_each_table_entry ( kie, WPA_KIES ) {
                if ( kie->version == version )
                        return kie;
        }

        return NULL;
}
int wpa_make_rsn_ie ( struct net80211_device dev,
union ieee80211_ie **  ie_ret 
)

Construct RSN or WPA information element.

Parameters:
dev802.11 device
Return values:
ie_retRSN or WPA information element
rcReturn status code

This function allocates, fills, and returns a RSN or WPA information element suitable for including in an association request frame to the network identified by dev->associating. If it is impossible to construct an information element consistent with iPXE's capabilities that is compatible with that network, or if none should be sent because that network's beacon included no security information, returns an error indication and leaves ie_ret unchanged.

The returned IE will be of the same type (RSN or WPA) as was included in the beacon for the network it is destined for.

Definition at line 124 of file wpa.c.

References ieee80211_ie_rsn::akm_count, ieee80211_ie_rsn::akm_list, net80211_device::associating, net80211_wlan::beacon, net80211_wlan::crypto, io_buffer::data, ieee80211_frame::data, DBG, EINVAL, ENOMEM, ENOTSUP, ieee80211_ie_rsn::group_cipher, group_cipher, net80211_wlan::handshaking, hdr, ieee80211_beacon, IEEE80211_IE_RSN, IEEE80211_IE_VENDOR, ieee80211_rsn_size(), IEEE80211_RSN_VERSION, IEEE80211_WPA_OUI_VEN, malloc(), ieee80211_ie_rsn::pairwise_cipher, ieee80211_ie_rsn::pairwise_count, ieee80211_ie_rsn::pmkid_count, ieee80211_ie_rsn::rsn_capab, sec80211_find_rsn(), sec80211_rsn_get_akm_desc(), sec80211_rsn_get_crypto_desc(), sec80211_rsn_get_net80211_crypt(), io_buffer::tail, ieee80211_ie_rsn::version, and wpa_find_cryptosystem().

Referenced by wpa_psk_init().

{
        u8 *rsn, *rsn_end;
        int is_rsn;
        u32 group_cipher;
        enum net80211_crypto_alg gcrypt;
        int ie_len;
        u8 *iep;
        struct ieee80211_ie_rsn *ie;
        struct ieee80211_frame *hdr;
        struct ieee80211_beacon *beacon;

        if ( ! dev->associating ) {
                DBG ( "WPA: Can't make RSN IE for a non-associating device\n" );
                return -EINVAL;
        }

        hdr = dev->associating->beacon->data;
        beacon = ( struct ieee80211_beacon * ) hdr->data;
        rsn = sec80211_find_rsn ( beacon->info_element,
                                  dev->associating->beacon->tail, &is_rsn,
                                  &rsn_end );
        if ( ! rsn ) {
                DBG ( "WPA: Can't make RSN IE when we didn't get one\n" );
                return -EINVAL;
        }

        rsn += 2;               /* skip version */
        group_cipher = *( u32 * ) rsn;
        gcrypt = sec80211_rsn_get_net80211_crypt ( group_cipher );

        if ( ! wpa_find_cryptosystem ( gcrypt ) ||
             ! wpa_find_cryptosystem ( dev->associating->crypto ) ) {
                DBG ( "WPA: No support for (GC:%d, PC:%d)\n",
                      gcrypt, dev->associating->crypto );
                return -ENOTSUP;
        }

        /* Everything looks good - make our IE. */

        /* WPA IEs need 4 more bytes for the OUI+type */
        ie_len = ieee80211_rsn_size ( 1, 1, 0, is_rsn ) + ( 4 * ! is_rsn );
        iep = malloc ( ie_len );
        if ( ! iep )
                return -ENOMEM;

        *ie_ret = ( union ieee80211_ie * ) iep;

        /* Store ID and length bytes. */
        *iep++ = ( is_rsn ? IEEE80211_IE_RSN : IEEE80211_IE_VENDOR );
        *iep++ = ie_len - 2;

        /* Store OUI+type for WPA IEs. */
        if ( ! is_rsn ) {
                *( u32 * ) iep = IEEE80211_WPA_OUI_VEN;
                iep += 4;
        }

        /* If this is a WPA IE, the id and len bytes in the
           ieee80211_ie_rsn structure will not be valid, but by doing
           the cast we can fill all the other fields much more
           readily. */

        ie = ( struct ieee80211_ie_rsn * ) ( iep - 2 );
        ie->version = IEEE80211_RSN_VERSION;
        ie->group_cipher = group_cipher;
        ie->pairwise_count = 1;
        ie->pairwise_cipher[0] =
                sec80211_rsn_get_crypto_desc ( dev->associating->crypto,
                                               is_rsn );
        ie->akm_count = 1;
        ie->akm_list[0] =
                sec80211_rsn_get_akm_desc ( dev->associating->handshaking,
                                            is_rsn );
        if ( is_rsn ) {
                ie->rsn_capab = 0;
                ie->pmkid_count = 0;
        }

        return 0;
}
int wpa_start ( struct net80211_device dev,
struct wpa_common_ctx ctx,
const void *  pmk,
size_t  pmk_len 
)

Set up generic WPA support to handle 4-Way Handshake.

Parameters:
dev802.11 device
ctxWPA common context
pmkPairwise Master Key to use for session
pmk_lenLength of PMK, almost always 32
Return values:
rcReturn status code

Definition at line 216 of file wpa.c.

References wpa_common_ctx::ap_rsn_ie, wpa_common_ctx::ap_rsn_ie_len, wpa_common_ctx::ap_rsn_is_rsn, net80211_device::associating, net80211_wlan::beacon, wpa_common_ctx::crypt, net80211_wlan::crypto, io_buffer::data, ieee80211_frame::data, wpa_common_ctx::dev, EINVAL, ENOENT, ENOMEM, wpa_common_ctx::gcrypt, hdr, ieee80211_beacon, wpa_common_ctx::list, list_add_tail, malloc(), memcpy(), NET80211_CRYPT_UNKNOWN, NULL, wpa_common_ctx::pmk, wpa_common_ctx::pmk_len, wpa_common_ctx::replay, net80211_device::rsn_ie, sec80211_find_rsn(), wpa_common_ctx::state, io_buffer::tail, and WPA_READY.

Referenced by wpa_psk_start().

{
        struct io_buffer *iob;
        struct ieee80211_frame *hdr;
        struct ieee80211_beacon *beacon;
        u8 *ap_rsn_ie = NULL, *ap_rsn_ie_end;

        if ( ! dev->rsn_ie || ! dev->associating )
                return -EINVAL;

        ctx->dev = dev;
        memcpy ( ctx->pmk, pmk, ctx->pmk_len = pmk_len );
        ctx->state = WPA_READY;
        ctx->replay = ~0ULL;

        iob = dev->associating->beacon;
        hdr = iob->data;
        beacon = ( struct ieee80211_beacon * ) hdr->data;
        ap_rsn_ie = sec80211_find_rsn ( beacon->info_element, iob->tail,
                                        &ctx->ap_rsn_is_rsn, &ap_rsn_ie_end );
        if ( ap_rsn_ie ) {
                ctx->ap_rsn_ie = malloc ( ap_rsn_ie_end - ap_rsn_ie );
                if ( ! ctx->ap_rsn_ie )
                        return -ENOMEM;
                memcpy ( ctx->ap_rsn_ie, ap_rsn_ie, ap_rsn_ie_end - ap_rsn_ie );
                ctx->ap_rsn_ie_len = ap_rsn_ie_end - ap_rsn_ie;
        } else {
                return -ENOENT;
        }

        ctx->crypt = dev->associating->crypto;
        ctx->gcrypt = NET80211_CRYPT_UNKNOWN;

        list_add_tail ( &ctx->list, &wpa_contexts );
        return 0;
}
void wpa_stop ( struct net80211_device dev)

Disable handling of received WPA handshake frames.

Parameters:
dev802.11 device

Definition at line 260 of file wpa.c.

References wpa_common_ctx::ap_rsn_ie, ctx, wpa_common_ctx::dev, free, wpa_common_ctx::list, list_del, list_for_each_entry_safe, and NULL.

Referenced by wpa_psk_stop().

{
        struct wpa_common_ctx *ctx, *tmp;

        list_for_each_entry_safe ( ctx, tmp, &wpa_contexts, list ) {
                if ( ctx->dev == dev ) {
                        free ( ctx->ap_rsn_ie );
                        ctx->ap_rsn_ie = NULL;
                        list_del ( &ctx->list );
                }
        }
}
static void wpa_derive_ptk ( struct wpa_common_ctx ctx) [static]

Derive pairwise transient key.

Parameters:
ctxWPA common context

Definition at line 279 of file wpa.c.

References __attribute__, wpa_common_ctx::Anonce, net80211_device::bssid, DBGC2, DBGC2_HD, wpa_common_ctx::dev, ETH_ALEN, eth_ntoa(), net_device::ll_addr, memcmp(), memcpy(), net80211_device::netdev, wpa_common_ctx::pmk, wpa_common_ctx::pmk_len, prf_sha1(), wpa_common_ctx::ptk, wpa_common_ctx::Snonce, and WPA_NONCE_LEN.

Referenced by wpa_handle_1_of_4().

{
        struct {
                u8 mac1[ETH_ALEN];
                u8 mac2[ETH_ALEN];
                u8 nonce1[WPA_NONCE_LEN];
                u8 nonce2[WPA_NONCE_LEN];
        } __attribute__ (( packed )) ptk_data;

        /* The addresses and nonces are stored in numerical order (!) */

        if ( memcmp ( ctx->dev->netdev->ll_addr, ctx->dev->bssid,
                      ETH_ALEN ) < 0 ) {
                memcpy ( ptk_data.mac1, ctx->dev->netdev->ll_addr, ETH_ALEN );
                memcpy ( ptk_data.mac2, ctx->dev->bssid, ETH_ALEN );
        } else {
                memcpy ( ptk_data.mac1, ctx->dev->bssid, ETH_ALEN );
                memcpy ( ptk_data.mac2, ctx->dev->netdev->ll_addr, ETH_ALEN );
        }

        if ( memcmp ( ctx->Anonce, ctx->Snonce, WPA_NONCE_LEN ) < 0 ) {
                memcpy ( ptk_data.nonce1, ctx->Anonce, WPA_NONCE_LEN );
                memcpy ( ptk_data.nonce2, ctx->Snonce, WPA_NONCE_LEN );
        } else {
                memcpy ( ptk_data.nonce1, ctx->Snonce, WPA_NONCE_LEN );
                memcpy ( ptk_data.nonce2, ctx->Anonce, WPA_NONCE_LEN );
        }

        DBGC2 ( ctx, "WPA %p A1 %s, A2 %s\n", ctx, eth_ntoa ( ptk_data.mac1 ),
               eth_ntoa ( ptk_data.mac2 ) );
        DBGC2 ( ctx, "WPA %p Nonce1, Nonce2:\n", ctx );
        DBGC2_HD ( ctx, ptk_data.nonce1, WPA_NONCE_LEN );
        DBGC2_HD ( ctx, ptk_data.nonce2, WPA_NONCE_LEN );

        prf_sha1 ( ctx->pmk, ctx->pmk_len,
                   "Pairwise key expansion",
                   &ptk_data, sizeof ( ptk_data ),
                   &ctx->ptk, sizeof ( ctx->ptk ) );

        DBGC2 ( ctx, "WPA %p PTK:\n", ctx );
        DBGC2_HD ( ctx, &ctx->ptk, sizeof ( ctx->ptk ) );
}
static int wpa_install_ptk ( struct wpa_common_ctx ctx,
int  len 
) [inline, static]

Install pairwise transient key.

Parameters:
ctxWPA common context
lenKey length (16 for CCMP, 32 for TKIP)
Return values:
rcReturn status code

Definition at line 330 of file wpa.c.

References wpa_common_ctx::crypt, net80211_device::crypto, DBGC, DBGC2_HD, wpa_common_ctx::dev, NULL, wpa_common_ctx::ptk, sec80211_install(), and wpa_ptk::tk.

Referenced by wpa_handle_3_of_4().

{
        DBGC ( ctx, "WPA %p: installing %d-byte pairwise transient key\n",
               ctx, len );
        DBGC2_HD ( ctx, &ctx->ptk.tk, len );

        return sec80211_install ( &ctx->dev->crypto, ctx->crypt,
                                  &ctx->ptk.tk, len, NULL );
}
static int wpa_install_gtk ( struct wpa_common_ctx ctx,
int  len,
const void *  rsc 
) [inline, static]

Install group transient key.

Parameters:
ctxWPA common context
lenKey length (16 for CCMP, 32 for TKIP)
rscReceive sequence counter field in EAPOL-Key packet
Return values:
rcReturn status code

Definition at line 348 of file wpa.c.

References DBGC, DBGC2_HD, wpa_common_ctx::dev, wpa_common_ctx::gcrypt, net80211_device::gcrypto, wpa_common_ctx::gtk, sec80211_install(), and wpa_gtk::tk.

Referenced by wpa_handle_1_of_2(), and wpa_maybe_install_gtk().

{
        DBGC ( ctx, "WPA %p: installing %d-byte group transient key\n",
               ctx, len );
        DBGC2_HD ( ctx, &ctx->gtk.tk, len );

        return sec80211_install ( &ctx->dev->gcrypto, ctx->gcrypt,
                                  &ctx->gtk.tk, len, rsc );
}
static int wpa_maybe_install_gtk ( struct wpa_common_ctx ctx,
union ieee80211_ie ie,
void *  ie_end,
const void *  rsc 
) [static]

Search for group transient key, and install it if found.

Parameters:
ctxWPA common context
iePointer to first IE in key data field
ie_endPointer to first byte not in key data field
rscReceive sequence counter field in EAPOL-Key packet
Return values:
rcReturn status code

Definition at line 368 of file wpa.c.

References DBGC, EINVAL, ENOENT, wpa_common_ctx::gtk, wpa_kde_gtk_encap::gtk, wpa_kde::gtk_encap, ieee80211_ie::id, ieee80211_ie_bound(), IEEE80211_IE_VENDOR, ieee80211_next_ie(), wpa_kde::len, ieee80211_ie::len, memcpy(), ieee80211_ie_vendor::oui, wpa_gtk::tk, u, ieee80211_ie::vendor, wpa_install_gtk(), and WPA_KDE_GTK.

Referenced by wpa_handle_1_of_2(), and wpa_handle_3_of_4().

{
        struct wpa_kde *kde;

        if ( ! ieee80211_ie_bound ( ie, ie_end ) )
                return -ENOENT;

        while ( ie ) {
                if ( ie->id == IEEE80211_IE_VENDOR &&
                     ie->vendor.oui == WPA_KDE_GTK )
                        break;

                ie = ieee80211_next_ie ( ie, ie_end );
        }

        if ( ! ie )
                return -ENOENT;

        if ( ie->len - 6u > sizeof ( ctx->gtk.tk ) ) {
                DBGC ( ctx, "WPA %p: GTK KDE is too long (%d bytes, max %zd)\n",
                       ctx, ie->len - 4, sizeof ( ctx->gtk.tk ) );
                return -EINVAL;
        }

        /* XXX We ignore key ID for now. */
        kde = ( struct wpa_kde * ) ie;
        memcpy ( &ctx->gtk.tk, &kde->gtk_encap.gtk, kde->len - 6 );

        return wpa_install_gtk ( ctx, kde->len - 6, rsc );
}
static struct io_buffer* wpa_alloc_frame ( int  kdlen) [static, read]

Allocate I/O buffer for construction of outgoing EAPOL-Key frame.

Parameters:
kdlenMaximum number of bytes in the Key Data field
Return values:
iobNewly allocated I/O buffer

The returned buffer will have space reserved for the link-layer and EAPOL headers, and will have iob->tail pointing to the start of the Key Data field. Thus, it is necessary to use iob_put() in filling the Key Data.

Definition at line 413 of file wpa.c.

References alloc_iob(), EAPOL_HDR_LEN, iob_put, iob_reserve, MAX_LL_HEADER_LEN, memset(), NULL, and ret.

Referenced by wpa_send_2_of_4(), and wpa_send_final().

{
        struct io_buffer *ret = alloc_iob ( sizeof ( struct eapol_key_pkt ) +
                                            kdlen + EAPOL_HDR_LEN +
                                            MAX_LL_HEADER_LEN );
        if ( ! ret )
                return NULL;

        iob_reserve ( ret, MAX_LL_HEADER_LEN + EAPOL_HDR_LEN );
        memset ( iob_put ( ret, sizeof ( struct eapol_key_pkt ) ), 0,
                 sizeof ( struct eapol_key_pkt ) );

        return ret;
}
static int wpa_send_eapol ( struct io_buffer iob,
struct wpa_common_ctx ctx,
struct wpa_kie kie 
) [static]

Send EAPOL-Key packet.

Parameters:
iobI/O buffer, with sufficient headroom for headers
dev802.11 device
kieKey integrity and encryption handler
is_rsnIf TRUE, handshake uses new RSN format
Return values:
rcReturn status code

If a KIE is specified, the MIC will be filled in before transmission.

Definition at line 440 of file wpa.c.

References net80211_device::bssid, cpu_to_be64, io_buffer::data, eapol_key_pkt::datalen, wpa_common_ctx::dev, EAPOL_HDR_LEN, EAPOL_THIS_VERSION, EAPOL_TYPE_KEY, htons, eapol_key_pkt::info, iob_push, wpa_ptk::kck, eapol_key_pkt::keysize, eapol_frame::length, net_device::ll_addr, memset(), eapol_key_pkt::mic, wpa_kie::mic, net_tx(), net80211_device::netdev, ntohs, wpa_common_ctx::ptk, eapol_key_pkt::replay, io_buffer::tail, eapol_frame::type, and eapol_frame::version.

Referenced by wpa_send_2_of_4(), and wpa_send_final().

{
        struct eapol_key_pkt *pkt = iob->data;
        struct eapol_frame *eapol = iob_push ( iob, EAPOL_HDR_LEN );

        pkt->info = htons ( pkt->info );
        pkt->keysize = htons ( pkt->keysize );
        pkt->datalen = htons ( pkt->datalen );
        pkt->replay = cpu_to_be64 ( pkt->replay );
        eapol->version = EAPOL_THIS_VERSION;
        eapol->type = EAPOL_TYPE_KEY;
        eapol->length = htons ( iob->tail - iob->data - sizeof ( *eapol ) );

        memset ( pkt->mic, 0, sizeof ( pkt->mic ) );
        if ( kie )
                kie->mic ( &ctx->ptk.kck, eapol, EAPOL_HDR_LEN +
                           sizeof ( *pkt ) + ntohs ( pkt->datalen ),
                           pkt->mic );

        return net_tx ( iob, ctx->dev->netdev, &eapol_protocol,
                        ctx->dev->bssid, ctx->dev->netdev->ll_addr );
}
static int wpa_send_2_of_4 ( struct wpa_common_ctx ctx,
struct eapol_key_pkt pkt,
int  is_rsn,
struct wpa_kie kie 
) [static]

Send second frame in 4-Way Handshake.

Parameters:
ctxWPA common context
pktFirst frame, to which this is a reply
is_rsnIf TRUE, handshake uses new RSN format
kieKey integrity and encryption handler
Return values:
rcReturn status code

Definition at line 474 of file wpa.c.

References io_buffer::data, eapol_key_pkt::datalen, DBGC, wpa_common_ctx::dev, EAPOL_KEY_INFO_KEY_ACK, EAPOL_KEY_INFO_KEY_MIC, ENOMEM, eapol_key_pkt::info, iob_put, eapol_key_pkt::keysize, ieee80211_ie::len, memcpy(), eapol_key_pkt::nonce, net80211_device::rsn_ie, wpa_common_ctx::Snonce, wpa_alloc_frame(), and wpa_send_eapol().

Referenced by wpa_handle_1_of_4().

{
        struct io_buffer *iob = wpa_alloc_frame ( ctx->dev->rsn_ie->len + 2 );
        struct eapol_key_pkt *npkt;

        if ( ! iob )
                return -ENOMEM;

        npkt = iob->data;
        memcpy ( npkt, pkt, sizeof ( *pkt ) );
        npkt->info &= ~EAPOL_KEY_INFO_KEY_ACK;
        npkt->info |= EAPOL_KEY_INFO_KEY_MIC;
        if ( is_rsn )
                npkt->keysize = 0;
        memcpy ( npkt->nonce, ctx->Snonce, sizeof ( npkt->nonce ) );
        npkt->datalen = ctx->dev->rsn_ie->len + 2;
        memcpy ( iob_put ( iob, npkt->datalen ), ctx->dev->rsn_ie,
                 npkt->datalen );

        DBGC ( ctx, "WPA %p: sending 2/4\n", ctx );

        return wpa_send_eapol ( iob, ctx, kie );
}
static int wpa_handle_1_of_4 ( struct wpa_common_ctx ctx,
struct eapol_key_pkt pkt,
int  is_rsn,
struct wpa_kie kie 
) [static]

Handle receipt of first frame in 4-Way Handshake.

Parameters:
ctxWPA common context
pktEAPOL-Key packet
is_rsnIf TRUE, frame uses new RSN format
kieKey integrity and encryption handler
Return values:
rcReturn status code

Definition at line 510 of file wpa.c.

References wpa_common_ctx::Anonce, DBGC, EINVAL, wpa_common_ctx::have_Snonce, memcpy(), eapol_key_pkt::nonce, NULL, rbg_generate(), wpa_common_ctx::Snonce, wpa_common_ctx::state, wpa_derive_ptk(), wpa_send_2_of_4(), WPA_WAITING, and WPA_WORKING.

Referenced by eapol_key_rx().

{
        if ( ctx->state == WPA_WAITING )
                return -EINVAL;

        ctx->state = WPA_WORKING;
        memcpy ( ctx->Anonce, pkt->nonce, sizeof ( ctx->Anonce ) );
        if ( ! ctx->have_Snonce ) {
                rbg_generate ( NULL, 0, 0, ctx->Snonce,
                               sizeof ( ctx->Snonce ) );
                ctx->have_Snonce = 1;
        }

        DBGC ( ctx, "WPA %p: received 1/4, looks OK\n", ctx );

        wpa_derive_ptk ( ctx );

        return wpa_send_2_of_4 ( ctx, pkt, is_rsn, kie );
}
static int wpa_send_final ( struct wpa_common_ctx ctx,
struct eapol_key_pkt pkt,
int  is_rsn,
struct wpa_kie kie 
) [static]

Send fourth frame in 4-Way Handshake, or second in Group Key Handshake.

Parameters:
ctxWPA common context
pktEAPOL-Key packet for frame to which we're replying
is_rsnIf TRUE, frame uses new RSN format
kieKey integrity and encryption handler
Return values:
rcReturn status code

Definition at line 542 of file wpa.c.

References io_buffer::data, eapol_key_pkt::datalen, DBGC, EAPOL_KEY_INFO_INSTALL, EAPOL_KEY_INFO_KEY_ACK, EAPOL_KEY_INFO_KEY_ENC, EAPOL_KEY_INFO_TYPE, ENOMEM, eapol_key_pkt::info, eapol_key_pkt::iv, eapol_key_pkt::keysize, memcpy(), memset(), eapol_key_pkt::nonce, wpa_alloc_frame(), and wpa_send_eapol().

Referenced by wpa_handle_1_of_2(), and wpa_handle_3_of_4().

{
        struct io_buffer *iob = wpa_alloc_frame ( 0 );
        struct eapol_key_pkt *npkt;

        if ( ! iob )
                return -ENOMEM;

        npkt = iob->data;
        memcpy ( npkt, pkt, sizeof ( *pkt ) );
        npkt->info &= ~( EAPOL_KEY_INFO_KEY_ACK | EAPOL_KEY_INFO_INSTALL |
                         EAPOL_KEY_INFO_KEY_ENC );
        if ( is_rsn )
                npkt->keysize = 0;
        memset ( npkt->nonce, 0, sizeof ( npkt->nonce ) );
        memset ( npkt->iv, 0, sizeof ( npkt->iv ) );
        npkt->datalen = 0;

        if ( npkt->info & EAPOL_KEY_INFO_TYPE )
                DBGC ( ctx, "WPA %p: sending 4/4\n", ctx );
        else
                DBGC ( ctx, "WPA %p: sending 2/2\n", ctx );

        return wpa_send_eapol ( iob, ctx, kie );

}
static int wpa_handle_3_of_4 ( struct wpa_common_ctx ctx,
struct eapol_key_pkt pkt,
int  is_rsn,
struct wpa_kie kie 
) [static]

Handle receipt of third frame in 4-Way Handshake.

Parameters:
ctxWPA common context
pktEAPOL-Key packet
is_rsnIf TRUE, frame uses new RSN format
kieKey integrity and encryption handler
Return values:
rcReturn status code

Definition at line 581 of file wpa.c.

References wpa_common_ctx::Anonce, wpa_common_ctx::ap_rsn_ie, wpa_common_ctx::ap_rsn_ie_len, wpa_common_ctx::ap_rsn_is_rsn, net80211_device::associating, net80211_wlan::crypto, eapol_key_pkt::data, eapol_key_pkt::datalen, DBGC, DBGC2, DBGC2_HD, wpa_common_ctx::dev, EACCES, EAPOL_KEY_INFO_KEY_ENC, EINVAL, ENOENT, wpa_common_ctx::gcrypt, net80211_wlan::handshaking, wpa_common_ctx::have_Snonce, eapol_key_pkt::info, eapol_key_pkt::keysize, memcmp(), eapol_key_pkt::nonce, NULL, rc, eapol_key_pkt::rsc, sec80211_detect_ie(), sec80211_find_rsn(), sec80211_rsn_get_net80211_crypt(), wpa_common_ctx::state, strerror(), wpa_fail(), wpa_install_ptk(), wpa_maybe_install_gtk(), WPA_NONCE_LEN, wpa_send_final(), WPA_SUCCESS, WPA_WAITING, and WPA_WORKING.

Referenced by eapol_key_rx().

{
        int rc;
        u8 *this_rsn, *this_rsn_end;
        u8 *new_rsn, *new_rsn_end;
        int this_is_rsn, new_is_rsn;

        if ( ctx->state == WPA_WAITING )
                return -EINVAL;

        ctx->state = WPA_WORKING;

        /* Check nonce */
        if ( memcmp ( ctx->Anonce, pkt->nonce, WPA_NONCE_LEN ) != 0 ) {
                DBGC ( ctx, "WPA %p ALERT: nonce mismatch in 3/4\n", ctx );
                return wpa_fail ( ctx, -EACCES );
        }

        /* Check RSN IE */
        this_rsn = sec80211_find_rsn ( ( union ieee80211_ie * ) pkt->data,
                                       pkt->data + pkt->datalen,
                                       &this_is_rsn, &this_rsn_end );
        if ( this_rsn )
                new_rsn = sec80211_find_rsn ( ( union ieee80211_ie * )
                                                      this_rsn_end,
                                              pkt->data + pkt->datalen,
                                              &new_is_rsn, &new_rsn_end );
        else
                new_rsn = NULL;

        if ( ! ctx->ap_rsn_ie || ! this_rsn ||
             ctx->ap_rsn_ie_len != ( this_rsn_end - this_rsn ) ||
             ctx->ap_rsn_is_rsn != this_is_rsn ||
             memcmp ( ctx->ap_rsn_ie, this_rsn, ctx->ap_rsn_ie_len ) != 0 ) {
                DBGC ( ctx, "WPA %p ALERT: RSN mismatch in 3/4\n", ctx );
                DBGC2 ( ctx, "WPA %p RSNs (in 3/4, in beacon):\n", ctx );
                DBGC2_HD ( ctx, this_rsn, this_rsn_end - this_rsn );
                DBGC2_HD ( ctx, ctx->ap_rsn_ie, ctx->ap_rsn_ie_len );
                return wpa_fail ( ctx, -EACCES );
        }

        /* Don't switch if they just supplied both styles of IE
           simultaneously; we need two RSN IEs or two WPA IEs to
           switch ciphers. They'll be immediately consecutive because
           of ordering guarantees. */
        if ( new_rsn && this_is_rsn == new_is_rsn ) {
                struct net80211_wlan *assoc = ctx->dev->associating;
                DBGC ( ctx, "WPA %p: accommodating bait-and-switch tactics\n",
                       ctx );
                DBGC2 ( ctx, "WPA %p RSNs (in 3/4+beacon, new in 3/4):\n",
                        ctx );
                DBGC2_HD ( ctx, this_rsn, this_rsn_end - this_rsn );
                DBGC2_HD ( ctx, new_rsn, new_rsn_end - new_rsn );

                if ( ( rc = sec80211_detect_ie ( new_is_rsn, new_rsn,
                                                 new_rsn_end,
                                                 &assoc->handshaking,
                                                 &assoc->crypto ) ) != 0 )
                        DBGC ( ctx, "WPA %p: bait-and-switch invalid, staying "
                               "with original request\n", ctx );
        } else {
                new_rsn = this_rsn;
                new_is_rsn = this_is_rsn;
                new_rsn_end = this_rsn_end;
        }

        /* Grab group cryptosystem ID */
        ctx->gcrypt = sec80211_rsn_get_net80211_crypt ( *( u32 * )
                                                        ( new_rsn + 2 ) );

        /* Check for a GTK, if info field is encrypted */
        if ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) {
                rc = wpa_maybe_install_gtk ( ctx,
                                             ( union ieee80211_ie * ) pkt->data,
                                             pkt->data + pkt->datalen,
                                             pkt->rsc );
                if ( rc < 0 ) {
                        DBGC ( ctx, "WPA %p did not install GTK in 3/4: %s\n",
                               ctx, strerror ( rc ) );
                        if ( rc != -ENOENT )
                                return wpa_fail ( ctx, rc );
                }
        }

        DBGC ( ctx, "WPA %p: received 3/4, looks OK\n", ctx );

        /* Send final message */
        rc = wpa_send_final ( ctx, pkt, is_rsn, kie );
        if ( rc < 0 )
                return wpa_fail ( ctx, rc );

        /* Install PTK */
        rc = wpa_install_ptk ( ctx, pkt->keysize );
        if ( rc < 0 ) {
                DBGC ( ctx, "WPA %p failed to install PTK: %s\n", ctx,
                       strerror ( rc ) );
                return wpa_fail ( ctx, rc );
        }

        /* Mark us as needing a new Snonce if we rekey */
        ctx->have_Snonce = 0;

        /* Done! */
        ctx->state = WPA_SUCCESS;
        return 0;
}
static int wpa_handle_1_of_2 ( struct wpa_common_ctx ctx,
struct eapol_key_pkt pkt,
int  is_rsn,
struct wpa_kie kie 
) [static]

Handle receipt of first frame in Group Key Handshake.

Parameters:
ctxWPA common context
pktEAPOL-Key packet
is_rsnIf TRUE, frame uses new RSN format
kieKey integrity and encryption handler
Return values:
rcReturn status code

Definition at line 700 of file wpa.c.

References eapol_key_pkt::data, eapol_key_pkt::datalen, DBGC, wpa_kie::decrypt, EAPOL_KEY_INFO_KEY_ENC, EINVAL, wpa_common_ctx::gtk, eapol_key_pkt::info, eapol_key_pkt::iv, wpa_ptk::kek, memcpy(), wpa_common_ctx::ptk, rc, eapol_key_pkt::rsc, strerror(), wpa_gtk::tk, wpa_fail(), wpa_install_gtk(), wpa_maybe_install_gtk(), and wpa_send_final().

Referenced by eapol_key_rx().

{
        int rc;

        /*
         * WPA and RSN do this completely differently.
         *
         * The idea of encoding the GTK (or PMKID, or various other
         * things) into a KDE that looks like an information element
         * is an RSN innovation; old WPA code never encapsulates
         * things like that. If it looks like an info element, it
         * really is (for the WPA IE check in frames 2/4 and 3/4). The
         * "key data encrypted" bit in the info field is also specific
         * to RSN.
         *
         * So from an old WPA host, 3/4 does not contain an
         * encapsulated GTK. The first frame of the GK handshake
         * contains it, encrypted, but without a KDE wrapper, and with
         * the key ID field (which iPXE doesn't use) shoved away in
         * the reserved bits in the info field, and the TxRx bit
         * stealing the Install bit's spot.
         */

        if ( is_rsn && ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) ) {
                rc = wpa_maybe_install_gtk ( ctx,
                                             ( union ieee80211_ie * ) pkt->data,
                                             pkt->data + pkt->datalen,
                                             pkt->rsc );
                if ( rc < 0 ) {
                        DBGC ( ctx, "WPA %p: failed to install GTK in 1/2: "
                               "%s\n", ctx, strerror ( rc ) );
                        return wpa_fail ( ctx, rc );
                }
        } else {
                rc = kie->decrypt ( &ctx->ptk.kek, pkt->iv, pkt->data,
                                    &pkt->datalen );
                if ( rc < 0 ) {
                        DBGC ( ctx, "WPA %p: failed to decrypt GTK: %s\n",
                               ctx, strerror ( rc ) );
                        return rc; /* non-fatal */
                }
                if ( pkt->datalen > sizeof ( ctx->gtk.tk ) ) {
                        DBGC ( ctx, "WPA %p: too much GTK data (%d > %zd)\n",
                               ctx, pkt->datalen, sizeof ( ctx->gtk.tk ) );
                        return wpa_fail ( ctx, -EINVAL );
                }

                memcpy ( &ctx->gtk.tk, pkt->data, pkt->datalen );
                wpa_install_gtk ( ctx, pkt->datalen, pkt->rsc );
        }

        DBGC ( ctx, "WPA %p: received 1/2, looks OK\n", ctx );

        return wpa_send_final ( ctx, pkt, is_rsn, kie );
}
static int eapol_key_rx ( struct io_buffer iob,
struct net_device netdev,
const void *ll_dest  __unused,
const void *  ll_source 
) [static]

Handle receipt of EAPOL-Key frame for WPA.

Parameters:
iobI/O buffer
netdevNetwork device
ll_destLink-layer destination address
ll_sourceSource link-layer address

Definition at line 767 of file wpa.c.

References be64_to_cpu, net80211_device::bssid, ctx, io_buffer::data, eapol_key_pkt::data, eapol_key_pkt::datalen, DBG, DBGC, DBGC2, DBGC2_HD, wpa_kie::decrypt, wpa_common_ctx::dev, EAPOL_HDR_LEN, EAPOL_KEY_INFO_KEY_ACK, EAPOL_KEY_INFO_KEY_ENC, EAPOL_KEY_INFO_KEY_MIC, EAPOL_KEY_INFO_TYPE, EAPOL_KEY_INFO_VERSION, EAPOL_KEY_TYPE_GTK, EAPOL_KEY_TYPE_PTK, EAPOL_KEY_TYPE_RSN, EAPOL_KEY_TYPE_WPA, EINVAL, ENOENT, ENOTSUP, ETH_ALEN, free_iob(), eapol_key_pkt::info, eapol_key_pkt::iv, wpa_ptk::kck, wpa_ptk::kek, eapol_key_pkt::keysize, list_for_each_entry, memcmp(), memcpy(), memset(), eapol_key_pkt::mic, wpa_kie::mic, net80211_get(), ntohs, wpa_common_ctx::ptk, rc, eapol_key_pkt::replay, wpa_common_ctx::replay, strerror(), io_buffer::tail, eapol_key_pkt::type, wpa_fail(), wpa_find_kie(), wpa_handle_1_of_2(), wpa_handle_1_of_4(), and wpa_handle_3_of_4().

{
        struct net80211_device *dev = net80211_get ( netdev );
        struct eapol_key_pkt *pkt = iob->data;
        int is_rsn, found_ctx;
        struct wpa_common_ctx *ctx;
        int rc = 0;
        struct wpa_kie *kie;
        u8 their_mic[16], our_mic[16];

        if ( pkt->type != EAPOL_KEY_TYPE_WPA &&
             pkt->type != EAPOL_KEY_TYPE_RSN ) {
                DBG ( "EAPOL-Key: packet not of 802.11 type\n" );
                rc = -EINVAL;
                goto drop;
        }

        is_rsn = ( pkt->type == EAPOL_KEY_TYPE_RSN );

        if ( ! dev ) {
                DBG ( "EAPOL-Key: packet not from 802.11\n" );
                rc = -EINVAL;
                goto drop;
        }

        if ( memcmp ( dev->bssid, ll_source, ETH_ALEN ) != 0 ) {
                DBG ( "EAPOL-Key: packet not from associated AP\n" );
                rc = -EINVAL;
                goto drop;
        }

        if ( ! ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_KEY_ACK ) ) {
                DBG ( "EAPOL-Key: packet sent in wrong direction\n" );
                rc = -EINVAL;
                goto drop;
        }

        found_ctx = 0;
        list_for_each_entry ( ctx, &wpa_contexts, list ) {
                if ( ctx->dev == dev ) {
                        found_ctx = 1;
                        break;
                }
        }

        if ( ! found_ctx ) {
                DBG ( "EAPOL-Key: no WPA context to handle packet for %p\n",
                      dev );
                rc = -ENOENT;
                goto drop;
        }

        if ( ( void * ) ( pkt + 1 ) + ntohs ( pkt->datalen ) > iob->tail ) {
                DBGC ( ctx, "WPA %p: packet truncated (has %zd extra bytes, "
                       "states %d)\n", ctx, iob->tail - ( void * ) ( pkt + 1 ),
                       ntohs ( pkt->datalen ) );
                rc = -EINVAL;
                goto drop;
        }

        /* Get a handle on key integrity/encryption handler */
        kie = wpa_find_kie ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_VERSION );
        if ( ! kie ) {
                DBGC ( ctx, "WPA %p: no support for packet version %d\n", ctx,
                       ntohs ( pkt->info ) & EAPOL_KEY_INFO_VERSION );
                rc = wpa_fail ( ctx, -ENOTSUP );
                goto drop;
        }

        /* Check MIC */
        if ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_KEY_MIC ) {
                memcpy ( their_mic, pkt->mic, sizeof ( pkt->mic ) );
                memset ( pkt->mic, 0, sizeof ( pkt->mic ) );
                kie->mic ( &ctx->ptk.kck, ( void * ) pkt - EAPOL_HDR_LEN,
                           EAPOL_HDR_LEN + sizeof ( *pkt ) +
                           ntohs ( pkt->datalen ), our_mic );
                DBGC2 ( ctx, "WPA %p MIC comparison (theirs, ours):\n", ctx );
                DBGC2_HD ( ctx, their_mic, 16 );
                DBGC2_HD ( ctx, our_mic, 16 );
                if ( memcmp ( their_mic, our_mic, sizeof ( pkt->mic ) ) != 0 ) {
                        DBGC ( ctx, "WPA %p: EAPOL MIC failure\n", ctx );
                        goto drop;
                }
        }

        /* Fix byte order to local */
        pkt->info = ntohs ( pkt->info );
        pkt->keysize = ntohs ( pkt->keysize );
        pkt->datalen = ntohs ( pkt->datalen );
        pkt->replay = be64_to_cpu ( pkt->replay );

        /* Check replay counter */
        if ( ctx->replay != ~0ULL && ctx->replay >= pkt->replay ) {
                DBGC ( ctx, "WPA %p ALERT: Replay detected! "
                       "(%08x:%08x >= %08x:%08x)\n", ctx,
                       ( u32 ) ( ctx->replay >> 32 ), ( u32 ) ctx->replay,
                       ( u32 ) ( pkt->replay >> 32 ), ( u32 ) pkt->replay );
                rc = 0;         /* ignore without error */
                goto drop;
        }
        ctx->replay = pkt->replay;

        /* Decrypt key data */
        if ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) {
                rc = kie->decrypt ( &ctx->ptk.kek, pkt->iv, pkt->data,
                                    &pkt->datalen );
                if ( rc < 0 ) {
                        DBGC ( ctx, "WPA %p: failed to decrypt packet: %s\n",
                               ctx, strerror ( rc ) );
                        goto drop;
                }
        }

        /* Hand it off to appropriate handler */
        switch ( pkt->info & ( EAPOL_KEY_INFO_TYPE |
                               EAPOL_KEY_INFO_KEY_MIC ) ) {
        case EAPOL_KEY_TYPE_PTK:
                rc = wpa_handle_1_of_4 ( ctx, pkt, is_rsn, kie );
                break;

        case EAPOL_KEY_TYPE_PTK | EAPOL_KEY_INFO_KEY_MIC:
                rc = wpa_handle_3_of_4 ( ctx, pkt, is_rsn, kie );
                break;

        case EAPOL_KEY_TYPE_GTK | EAPOL_KEY_INFO_KEY_MIC:
                rc = wpa_handle_1_of_2 ( ctx, pkt, is_rsn, kie );
                break;

        default:
                DBGC ( ctx, "WPA %p: Invalid combination of key flags %04x\n",
                       ctx, pkt->info );
                rc = -EINVAL;
                break;
        }

 drop:
        free_iob ( iob );
        return rc;
}
REQUIRING_SYMBOL ( eapol_key_handler  )
REQUIRE_OBJECT ( eapol  )

Variable Documentation

List of WPA contexts in active use.

Definition at line 46 of file wpa.c.

struct eapol_handler eapol_key_handler __eapol_handler
Initial value:
 {
        .type = EAPOL_TYPE_KEY,
        .rx = eapol_key_rx,
}

Definition at line 909 of file wpa.c.