sec80211.c File Reference

General secured-network routines required whenever any secure network support at all is compiled in. More...

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <gpxe/ieee80211.h>
#include <gpxe/net80211.h>
#include <gpxe/sec80211.h>

Go to the source code of this file.

Data Structures

struct  descriptor_map
 Mapping from net80211 crypto/secprot types to RSN OUI descriptors. More...

Defines

#define END_MAGIC   0xFFFFFFFF
 Magic number in oui_type showing end of list.

Functions

 FILE_LICENCE (GPL2_OR_LATER)
int sec80211_install (struct net80211_crypto **which, enum net80211_crypto_alg crypt, const void *key, int len, const void *rsc)
 Install 802.11 cryptosystem.
static struct descriptor_maprsn_pick_desc (u8 **rsnp, u8 *rsn_end, struct descriptor_map *map, void *tbl_start, void *tbl_end)
 Determine net80211 crypto or handshaking type value to return for RSN info.
u8sec80211_find_rsn (union ieee80211_ie *ie, void *ie_end, int *is_rsn, u8 **end)
 Find the RSN or WPA information element in the provided beacon frame.
int sec80211_detect_ie (int is_rsn, u8 *start, u8 *end, enum net80211_security_proto *secprot, enum net80211_crypto_alg *crypt)
 Detect crypto and AKM types from RSN information element.
int _sec80211_detect (struct io_buffer *iob, enum net80211_security_proto *secprot, enum net80211_crypto_alg *crypt)
 Detect the cryptosystem and handshaking protocol used by an 802.11 network.
static u32 rsn_get_desc (unsigned id, int rsnie, struct descriptor_map *map)
 Determine RSN descriptor for specified net80211 ID.
u32 sec80211_rsn_get_crypto_desc (enum net80211_crypto_alg crypt, int rsnie)
 Determine RSN descriptor for specified net80211 cryptosystem number.
u32 sec80211_rsn_get_akm_desc (enum net80211_security_proto secprot, int rsnie)
 Determine RSN descriptor for specified net80211 handshaker number.
enum net80211_crypto_alg sec80211_rsn_get_net80211_crypt (u32 desc)
 Determine net80211 cryptosystem number from RSN descriptor.

Variables

static struct descriptor_map rsn_cipher_map []
 Mapping between net80211 cryptosystems and 802.11i cipher IDs.
static struct descriptor_map rsn_akm_map []
 Mapping between net80211 handshakers and 802.11i AKM IDs.


Detailed Description

General secured-network routines required whenever any secure network support at all is compiled in.

This involves things like installing keys, determining the type of security used by a probed network, and some small helper functions that take advantage of static data in this file.

Definition in file sec80211.c.


Define Documentation

#define END_MAGIC   0xFFFFFFFF

Magic number in oui_type showing end of list.

Definition at line 47 of file sec80211.c.

Referenced by rsn_get_desc(), and sec80211_rsn_get_net80211_crypt().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER   ) 

int sec80211_install ( struct net80211_crypto **  which,
enum net80211_crypto_alg  crypt,
const void *  key,
int  len,
const void *  rsc 
)

Install 802.11 cryptosystem.

Parameters:
which Pointer to the cryptosystem structure to install in
crypt Cryptosystem ID number
key Encryption key to use
len Length of encryption key
rsc Initial receive sequence counter, if applicable
Return values:
rc Return status code
The encryption key will not be accessed via the provided pointer after this function returns, so you may keep it on the stack.

which must point to either dev->crypto (for the normal case of installing a unicast cryptosystem) or dev->gcrypto (to install a cryptosystem that will be used only for decrypting group-source frames).

Definition at line 98 of file sec80211.c.

References net80211_crypto::algorithm, DBG, ENOMEM, ENOTSUP, EUNIQ_10, for_each_table_entry, free(), net80211_crypto::init, memcpy, NET80211_CRYPT_NONE, NET80211_CRYPTOS, NULL, net80211_crypto::priv, net80211_crypto::priv_len, and zalloc().

Referenced by trivial_init(), wpa_install_gtk(), and wpa_install_ptk().

00101 {
00102         struct net80211_crypto *crypto = *which;
00103         struct net80211_crypto *tbl_crypto;
00104 
00105         /* Remove old crypto if it exists */
00106         free ( *which );
00107         *which = NULL;
00108 
00109         if ( crypt == NET80211_CRYPT_NONE ) {
00110                 DBG ( "802.11-Sec not installing null cryptography\n" );
00111                 return 0;
00112         }
00113 
00114         /* Find cryptosystem to use */
00115         for_each_table_entry ( tbl_crypto, NET80211_CRYPTOS ) {
00116                 if ( tbl_crypto->algorithm == crypt ) {
00117                         crypto = zalloc ( sizeof ( *crypto ) +
00118                                           tbl_crypto->priv_len );
00119                         if ( ! crypto ) {
00120                                 DBG ( "802.11-Sec out of memory\n" );
00121                                 return -ENOMEM;
00122                         }
00123 
00124                         memcpy ( crypto, tbl_crypto, sizeof ( *crypto ) );
00125                         crypto->priv = ( ( void * ) crypto +
00126                                          sizeof ( *crypto ) );
00127                         break;
00128                 }
00129         }
00130 
00131         if ( ! crypto ) {
00132                 DBG ( "802.11-Sec no support for cryptosystem %d\n", crypt );
00133                 return -( ENOTSUP | EUNIQ_10 | ( crypt << 8 ) );
00134         }
00135 
00136         *which = crypto;
00137 
00138         DBG ( "802.11-Sec installing cryptosystem %d as %p with key of "
00139               "length %d\n", crypt, crypto, len );
00140 
00141         return crypto->init ( crypto, key, len, rsc );
00142 }

static struct descriptor_map* rsn_pick_desc ( u8 **  rsnp,
u8 rsn_end,
struct descriptor_map map,
void *  tbl_start,
void *  tbl_end 
) [static, read]

Determine net80211 crypto or handshaking type value to return for RSN info.

Parameters:
rsnp Pointer to next descriptor count field in RSN IE
rsn_end Pointer to end of RSN IE
map Descriptor map to use
tbl_start Start of linker table to examine for gPXE support
tbl_end End of linker table to examine for gPXE support
Return values:
rsnp Updated to point to first byte after descriptors
map_ent Descriptor map entry of translation to use
The entries in the linker table must be either net80211_crypto or net80211_handshaker structures, and tbl_stride must be set to sizeof() the appropriate one.

This function expects rsnp to point at a two-byte descriptor count followed by a list of four-byte cipher or AKM descriptors; it will return NULL if the input packet is malformed, and otherwise set rsnp to the first byte it has not looked at. It will return the first cipher in the list that is supported by the current build of gPXE, or the first of all if none are supported.

We play rather fast and loose with type checking, because this function is only called from two well-defined places in the RSN-checking code. Don't try to use it for anything else.

Definition at line 171 of file sec80211.c.

References NULL, and u8.

Referenced by sec80211_detect_ie().

00174 {
00175         int ndesc;
00176         int ok = 0;
00177         struct descriptor_map *map_ent, *map_ret = NULL;
00178         u8 *rsn = *rsnp;
00179         void *tblp;
00180         size_t tbl_stride = ( map == rsn_cipher_map ?
00181                               sizeof ( struct net80211_crypto ) :
00182                               sizeof ( struct net80211_handshaker ) );
00183 
00184         if ( map != rsn_cipher_map && map != rsn_akm_map )
00185                 return NULL;
00186 
00187         /* Determine which types we support */
00188         for ( tblp = tbl_start; tblp < tbl_end; tblp += tbl_stride ) {
00189                 struct net80211_crypto *crypto = tblp;
00190                 struct net80211_handshaker *hs = tblp;
00191 
00192                 if ( map == rsn_cipher_map )
00193                         ok |= ( 1 << crypto->algorithm );
00194                 else
00195                         ok |= ( 1 << hs->protocol );
00196         }
00197 
00198         /* RSN sanity checks */
00199         if ( rsn + 2 > rsn_end ) {
00200                 DBG ( "RSN detect: malformed descriptor count\n" );
00201                 return NULL;
00202         }
00203 
00204         ndesc = *( u16 * ) rsn;
00205         rsn += 2;
00206 
00207         if ( ! ndesc ) {
00208                 DBG ( "RSN detect: no descriptors\n" );
00209                 return NULL;
00210         }
00211 
00212         /* Determine which net80211 crypto types are listed */
00213         while ( ndesc-- ) {
00214                 u32 desc;
00215 
00216                 if ( rsn + 4 > rsn_end ) {
00217                         DBG ( "RSN detect: malformed descriptor (%d left)\n",
00218                               ndesc );
00219                         return NULL;
00220                 }
00221 
00222                 desc = *( u32 * ) rsn;
00223                 rsn += 4;
00224 
00225                 for ( map_ent = map; map_ent->oui_type != END_MAGIC; map_ent++ )
00226                         if ( map_ent->oui_type == ( desc & OUI_TYPE_MASK ) )
00227                                 break;
00228 
00229                 /* Use first cipher as a fallback */
00230                 if ( ! map_ret )
00231                         map_ret = map_ent;
00232 
00233                 /* Once we find one we support, use it */
00234                 if ( ok & ( 1 << map_ent->net80211_type ) ) {
00235                         map_ret = map_ent;
00236                         break;
00237                 }
00238         }
00239 
00240         if ( ndesc > 0 )
00241                 rsn += 4 * ndesc;
00242 
00243         *rsnp = rsn;
00244         return map_ret;
00245 }

u8* sec80211_find_rsn ( union ieee80211_ie ie,
void *  ie_end,
int *  is_rsn,
u8 **  end 
)

Find the RSN or WPA information element in the provided beacon frame.

Parameters:
ie Pointer to first information element to check
ie_end Pointer to end of information element space
Return values:
is_rsn TRUE if returned IE is RSN, FALSE if it's WPA
end Pointer to byte immediately after last byte of data
data Pointer to first byte of data (the `version' field)
If both an RSN and a WPA information element are found, this function will return the first one seen, which by ordering rules should always prefer the newer RSN IE.

If no RSN or WPA infomration element is found, returns NULL and leaves is_rsn and end in an undefined state.

This function will not return a pointer to an information element that states it extends past the tail of the io_buffer, or whose version field is incorrect.

Definition at line 268 of file sec80211.c.

References DBG, ieee80211_ie_bound(), IEEE80211_IE_RSN, IEEE80211_IE_VENDOR, ieee80211_next_ie(), IEEE80211_RSN_VERSION, IEEE80211_WPA_OUI_VEN, NULL, u16, and u8.

Referenced by _sec80211_detect(), wpa_handle_3_of_4(), wpa_make_rsn_ie(), and wpa_start().

00270 {
00271         u8 *rsn = NULL;
00272 
00273         if ( ! ieee80211_ie_bound ( ie, ie_end ) )
00274                 return NULL;
00275 
00276         while ( ie ) {
00277                 if ( ie->id == IEEE80211_IE_VENDOR &&
00278                      ie->vendor.oui == IEEE80211_WPA_OUI_VEN ) {
00279                         DBG ( "RSN detect: old-style WPA IE found\n" );
00280                         rsn = &ie->vendor.data[0];
00281                         *end = rsn + ie->len - 4;
00282                         *is_rsn = 0;
00283                 } else if ( ie->id == IEEE80211_IE_RSN ) {
00284                         DBG ( "RSN detect: 802.11i RSN IE found\n" );
00285                         rsn = ( u8 * ) &ie->rsn.version;
00286                         *end = rsn + ie->len;
00287                         *is_rsn = 1;
00288                 }
00289 
00290                 if ( rsn && ( *end > ( u8 * ) ie_end || rsn >= *end ||
00291                               *( u16 * ) rsn != IEEE80211_RSN_VERSION ) ) {
00292                         DBG ( "RSN detect: malformed RSN IE or unknown "
00293                               "version, keep trying\n" );
00294                         rsn = NULL;
00295                 }
00296 
00297                 if ( rsn )
00298                         break;
00299 
00300                 ie = ieee80211_next_ie ( ie, ie_end );
00301         }
00302 
00303         if ( ! ie ) {
00304                 DBG ( "RSN detect: no RSN IE found\n" );
00305                 return NULL;
00306         }
00307 
00308         return rsn;
00309 }

int sec80211_detect_ie ( int  is_rsn,
u8 start,
u8 end,
enum net80211_security_proto secprot,
enum net80211_crypto_alg crypt 
)

Detect crypto and AKM types from RSN information element.

Parameters:
is_rsn If TRUE, IE is a new-style RSN information element
start Pointer to first byte of version field
end Pointer to first byte not in the RSN IE
Return values:
secprot Security handshaking protocol used by network
crypt Cryptosystem used by network
rc Return status code
If the IE cannot be parsed, returns an error indication and leaves secprot and crypt unchanged.

Definition at line 325 of file sec80211.c.

References cr, DBG, EINVAL, NET80211_CRYPT_CCMP, NET80211_CRYPT_TKIP, NET80211_CRYPTOS, NET80211_HANDSHAKERS, NET80211_SECPROT_EAP, descriptor_map::net80211_type, rsn_pick_desc(), table_end, table_start, and u8.

Referenced by _sec80211_detect(), and wpa_handle_3_of_4().

00328 {
00329         enum net80211_security_proto sp;
00330         enum net80211_crypto_alg cr;
00331         struct descriptor_map *map;
00332         u8 *rsn = start;
00333 
00334         /* Set some defaults */
00335         cr = ( is_rsn ? NET80211_CRYPT_CCMP : NET80211_CRYPT_TKIP );
00336         sp = NET80211_SECPROT_EAP;
00337 
00338         rsn += 2;               /* version - already checked */
00339         rsn += 4;               /* group cipher - we don't use it here */
00340 
00341         if ( rsn >= end )
00342                 goto done;
00343 
00344         /* Pick crypto algorithm */
00345         map = rsn_pick_desc ( &rsn, end, rsn_cipher_map,
00346                               table_start ( NET80211_CRYPTOS ),
00347                               table_end ( NET80211_CRYPTOS ) );
00348         if ( ! map )
00349                 goto invalid_rsn;
00350 
00351         cr = map->net80211_type;
00352 
00353         if ( rsn >= end )
00354                 goto done;
00355 
00356         /* Pick handshaking algorithm */
00357         map = rsn_pick_desc ( &rsn, end, rsn_akm_map,
00358                               table_start ( NET80211_HANDSHAKERS ),
00359                               table_end ( NET80211_HANDSHAKERS ) );
00360         if ( ! map )
00361                 goto invalid_rsn;
00362 
00363         sp = map->net80211_type;
00364 
00365  done:
00366         DBG ( "RSN detect: OK, crypto type %d, secprot type %d\n", cr, sp );
00367         *secprot = sp;
00368         *crypt = cr;
00369         return 0;
00370 
00371  invalid_rsn:
00372         DBG ( "RSN detect: invalid RSN IE\n" );
00373         return -EINVAL;
00374 }

int _sec80211_detect ( struct io_buffer iob,
enum net80211_security_proto secprot,
enum net80211_crypto_alg crypt 
)

Detect the cryptosystem and handshaking protocol used by an 802.11 network.

Parameters:
iob I/O buffer containing beacon frame
Return values:
secprot Security handshaking protocol used by network
crypt Cryptosystem used by network
rc Return status code
This function uses weak linkage, as it must be called from generic contexts but should only be linked in if some encryption is supported; you must test its address against NULL before calling it. If it does not exist, any network with the PRIVACY bit set in beacon->capab should be considered unknown.

Definition at line 391 of file sec80211.c.

References ieee80211_frame::data, io_buffer::data, DBG, DBG_HD, ieee80211_beacon, IEEE80211_CAPAB_PRIVACY, NET80211_CRYPT_NONE, NET80211_CRYPT_UNKNOWN, NET80211_CRYPT_WEP, NET80211_SECPROT_NONE, NET80211_SECPROT_UNKNOWN, sec80211_detect_ie(), sec80211_find_rsn(), io_buffer::tail, and u8.

Referenced by sec80211_detect().

00394 {
00395         struct ieee80211_frame *hdr = iob->data;
00396         struct ieee80211_beacon *beacon =
00397                 ( struct ieee80211_beacon * ) hdr->data;
00398         u8 *rsn, *rsn_end;
00399         int is_rsn, rc;
00400 
00401         *crypt = NET80211_CRYPT_UNKNOWN;
00402         *secprot = NET80211_SECPROT_UNKNOWN;
00403 
00404         /* Find RSN or WPA IE */
00405         if ( ! ( rsn = sec80211_find_rsn ( beacon->info_element, iob->tail,
00406                                            &is_rsn, &rsn_end ) ) ) {
00407                 /* No security IE at all; either WEP or no security. */
00408                 *secprot = NET80211_SECPROT_NONE;
00409 
00410                 if ( beacon->capability & IEEE80211_CAPAB_PRIVACY )
00411                         *crypt = NET80211_CRYPT_WEP;
00412                 else
00413                         *crypt = NET80211_CRYPT_NONE;
00414 
00415                 return 0;
00416         }
00417 
00418         /* Determine type of security */
00419         if ( ( rc = sec80211_detect_ie ( is_rsn, rsn, rsn_end, secprot,
00420                                          crypt ) ) == 0 )
00421                 return 0;
00422 
00423         /* If we get here, the RSN IE was invalid */
00424 
00425         *crypt = NET80211_CRYPT_UNKNOWN;
00426         *secprot = NET80211_SECPROT_UNKNOWN;
00427         DBG ( "Failed to handle RSN IE:\n" );
00428         DBG_HD ( rsn, rsn_end - rsn );
00429         return rc;
00430 }

static u32 rsn_get_desc ( unsigned  id,
int  rsnie,
struct descriptor_map map 
) [static]

Determine RSN descriptor for specified net80211 ID.

Parameters:
id net80211 ID value
rsnie Whether to return a new-format (RSN IE) descriptor
map Map to use in translation
Return values:
desc RSN descriptor, or 0 on error
If rsnie is false, returns an old-format (WPA vendor IE) descriptor.

Definition at line 444 of file sec80211.c.

References END_MAGIC, IEEE80211_RSN_OUI, IEEE80211_WPA_OUI, descriptor_map::net80211_type, descriptor_map::oui_type, u32, and vendor.

Referenced by sec80211_rsn_get_akm_desc(), and sec80211_rsn_get_crypto_desc().

00445 {
00446         u32 vendor = ( rsnie ? IEEE80211_RSN_OUI : IEEE80211_WPA_OUI );
00447 
00448         for ( ; map->oui_type != END_MAGIC; map++ ) {
00449                 if ( map->net80211_type == id )
00450                         return map->oui_type | vendor;
00451         }
00452 
00453         return 0;
00454 }

u32 sec80211_rsn_get_crypto_desc ( enum net80211_crypto_alg  crypt,
int  rsnie 
)

Determine RSN descriptor for specified net80211 cryptosystem number.

Parameters:
crypt Cryptosystem number
rsnie Whether to return a new-format (RSN IE) descriptor
Return values:
desc RSN descriptor
If rsnie is false, returns an old-format (WPA vendor IE) descriptor.

Definition at line 466 of file sec80211.c.

References rsn_get_desc().

Referenced by wpa_make_rsn_ie().

00467 {
00468         return rsn_get_desc ( crypt, rsnie, rsn_cipher_map );
00469 }

u32 sec80211_rsn_get_akm_desc ( enum net80211_security_proto  secprot,
int  rsnie 
)

Determine RSN descriptor for specified net80211 handshaker number.

Parameters:
secprot Handshaker number
rsnie Whether to return a new-format (RSN IE) descriptor
Return values:
desc RSN descriptor
If rsnie is false, returns an old-format (WPA vendor IE) descriptor.

Definition at line 481 of file sec80211.c.

References rsn_get_desc().

Referenced by wpa_make_rsn_ie().

00483 {
00484         return rsn_get_desc ( secprot, rsnie, rsn_akm_map );
00485 }

enum net80211_crypto_alg sec80211_rsn_get_net80211_crypt ( u32  desc  ) 

Determine net80211 cryptosystem number from RSN descriptor.

Parameters:
desc RSN descriptor
Return values:
crypt net80211 cryptosystem enumeration value

Definition at line 493 of file sec80211.c.

References END_MAGIC, descriptor_map::net80211_type, descriptor_map::oui_type, and OUI_TYPE_MASK.

Referenced by wpa_handle_3_of_4(), and wpa_make_rsn_ie().

00494 {
00495         struct descriptor_map *map = rsn_cipher_map;
00496 
00497         for ( ; map->oui_type != END_MAGIC; map++ ) {
00498                 if ( map->oui_type == ( desc & OUI_TYPE_MASK ) )
00499                         break;
00500         }
00501 
00502         return map->net80211_type;
00503 }


Variable Documentation

struct descriptor_map rsn_cipher_map[] [static]

Initial value:

 {
        { .net80211_type = NET80211_CRYPT_WEP,
          .oui_type = IEEE80211_RSN_CTYPE_WEP40 },

        { .net80211_type = NET80211_CRYPT_WEP,
          .oui_type = IEEE80211_RSN_CTYPE_WEP104 },

        { .net80211_type = NET80211_CRYPT_TKIP,
          .oui_type = IEEE80211_RSN_CTYPE_TKIP },

        { .net80211_type = NET80211_CRYPT_CCMP,
          .oui_type = IEEE80211_RSN_CTYPE_CCMP },

        { .net80211_type = NET80211_CRYPT_UNKNOWN,
          .oui_type = END_MAGIC },
}
Mapping between net80211 cryptosystems and 802.11i cipher IDs.

Definition at line 50 of file sec80211.c.

struct descriptor_map rsn_akm_map[] [static]

Initial value:

 {
        { .net80211_type = NET80211_SECPROT_EAP,
          .oui_type = IEEE80211_RSN_ATYPE_8021X },

        { .net80211_type = NET80211_SECPROT_PSK,
          .oui_type = IEEE80211_RSN_ATYPE_PSK },

        { .net80211_type = NET80211_SECPROT_UNKNOWN,
          .oui_type = END_MAGIC },
}
Mapping between net80211 handshakers and 802.11i AKM IDs.

Definition at line 68 of file sec80211.c.


Generated on Tue Apr 6 20:01:54 2010 for gPXE by  doxygen 1.5.7.1