david/ipxe
Archived
1
0

[802.11] Add core support for detecting and using encrypted networks

Signed-off-by: Marty Connor <mdc@etherboot.org>
This commit is contained in:
Joshua Oreman 2009-08-07 22:03:30 -07:00 committed by Marty Connor
parent 8d08da3a99
commit dd8a3e2e70
6 changed files with 1130 additions and 206 deletions

View File

@ -158,6 +158,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_ib_mi ( ERRFILE_NET | 0x00200000 ) #define ERRFILE_ib_mi ( ERRFILE_NET | 0x00200000 )
#define ERRFILE_ib_cmrc ( ERRFILE_NET | 0x00210000 ) #define ERRFILE_ib_cmrc ( ERRFILE_NET | 0x00210000 )
#define ERRFILE_ib_srp ( ERRFILE_NET | 0x00220000 ) #define ERRFILE_ib_srp ( ERRFILE_NET | 0x00220000 )
#define ERRFILE_sec80211 ( ERRFILE_NET | 0x00230000 )
#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )

View File

@ -2,6 +2,7 @@
#define _GPXE_IEEE80211_H #define _GPXE_IEEE80211_H
#include <gpxe/if_ether.h> /* for ETH_ALEN */ #include <gpxe/if_ether.h> /* for ETH_ALEN */
#include <endian.h>
/** @file /** @file
* Constants and data structures defined in IEEE 802.11, subsetted * Constants and data structures defined in IEEE 802.11, subsetted
@ -779,10 +780,9 @@ struct ieee80211_ie_erp_info {
* *
* Showing once again a striking clarity of design, the IEEE folks put * Showing once again a striking clarity of design, the IEEE folks put
* dynamically-sized data in the middle of this structure. As such, * dynamically-sized data in the middle of this structure. As such,
* the below structure definition is only a guideline; the * the below structure definition only works for IEs we create
* @c IEEE80211_RSN_FIELD, @c IEEE80211_RSN_CIPHER, and * ourselves, which always have one pairwise cipher and one AKM;
* @c IEEE80211_RSN_AUTHTYPE macros should be used to access any * received IEs should be parsed piecemeal.
* data.
* *
* Also inspired was IEEE's choice of 16-bit fields to count the * Also inspired was IEEE's choice of 16-bit fields to count the
* number of 4-byte elements in a structure with a maximum length of * number of 4-byte elements in a structure with a maximum length of
@ -790,11 +790,9 @@ struct ieee80211_ie_erp_info {
* *
* Many fields reference a cipher or authentication-type ID; this is a * Many fields reference a cipher or authentication-type ID; this is a
* three-byte OUI followed by one byte identifying the cipher with * three-byte OUI followed by one byte identifying the cipher with
* respect to that OUI. For all standard ciphers the OUI is 00:0F:AC. * respect to that OUI. For all standard ciphers the OUI is 00:0F:AC,
* * except in old-style WPA IEs encapsulated in vendor-specific IEs,
* The authentication types referenced in this structure have nothing * where it's 00:50:F2.
* to do with 802.11 authentication frames or the @c algorithm field
* within them.
*/ */
struct ieee80211_ie_rsn { struct ieee80211_ie_rsn {
/** Information element ID */ /** Information element ID */
@ -807,21 +805,21 @@ struct ieee80211_ie_rsn {
u16 version; u16 version;
/** Cipher ID for the cipher used in multicast/broadcast frames */ /** Cipher ID for the cipher used in multicast/broadcast frames */
u8 group_cipher[4]; u32 group_cipher;
/** Number of unicast ciphers supported */ /** Number of unicast ciphers supported */
u16 pairwise_count; u16 pairwise_count;
/** List of cipher IDs for supported unicast frame ciphers */ /** List of cipher IDs for supported unicast frame ciphers */
u8 pairwise_cipher[4]; u32 pairwise_cipher[1];
/** Number of authentication types supported */ /** Number of authentication types supported */
u16 akm_count; u16 akm_count;
/** List of authentication type IDs for supported types */ /** List of authentication type IDs for supported types */
u8 akm_list[4]; u32 akm_list[1];
/** Security capabilities field. */ /** Security capabilities field (RSN only) */
u16 rsn_capab; u16 rsn_capab;
/** Number of PMKIDs included (present only in association frames) */ /** Number of PMKIDs included (present only in association frames) */
@ -834,140 +832,69 @@ struct ieee80211_ie_rsn {
/** Information element ID for Robust Security Network information element */ /** Information element ID for Robust Security Network information element */
#define IEEE80211_IE_RSN 48 #define IEEE80211_IE_RSN 48
/** OUI for standard ciphers in RSN information element */
#define IEEE80211_RSN_OUI "\x00\x0F\xAC"
/** Extract RSN IE version field */
#define IEEE80211_RSN_FIELD_version( rsnp ) ( (rsnp)->version )
/** Extract RSN IE group_cipher field */
#define IEEE80211_RSN_FIELD_group_cipher( rsnp ) ( (rsnp)->group_cipher )
/** Extract RSN IE pairwise_count field */
#define IEEE80211_RSN_FIELD_pairwise_count( rsnp ) ( (rsnp)->pairwise_count )
/** Extract RSN IE akm_count field */
#define IEEE80211_RSN_FIELD_akm_count( rsnp ) \
( ( ( struct ieee80211_ie_rsn * ) ( ( void * ) ( rsnp ) + \
4*( ( rsnp )->pairwise_count - 1 ) ) )->akm_count )
/** Extract RSN IE rsn_capab field */
#define IEEE80211_RSN_FIELD_rsn_capab( rsnp ) \
( ( ( struct ieee80211_ie_rsn * ) ( ( void * ) ( rsnp ) + \
4*( ( rsnp )->pairwise_count - 1 ) + \
4*( ( rsnp )->akm_count - 1 ) ) )->rsn_capab )
/** Extract RSN IE pmkid_count field */
#define IEEE80211_RSN_FIELD_pmkid_count( rsnp ) \
( ( ( struct ieee80211_ie_rsn * ) ( ( void * ) ( rsnp ) + \
4*( ( rsnp )->pairwise_count - 1 ) + \
4*( ( rsnp )->akm_count - 1 ) ) )->pmkid_count )
/** Extract field from RSN information element
*
* @v rsnp Pointer to RSN information element
* @v field Name of field to extract
* @ret val Lvalue of the requested field
*
* You must fill the fields of the structure in order for this to work
* properly.
*/
#define IEEE80211_RSN_FIELD( rsnp, field ) \
IEEE80211_RSN_FIELD_ ## field ( rsnp )
/** Get pointer to pairwise cipher from RSN information element
*
* @v rsnp Pointer to RSN information element
* @v cipher Index of pairwise cipher to extract
* @ret ptr Pointer to requested cipher
*/
#define IEEE80211_RSN_CIPHER( rsnp, cipher ) \
( ( rsnp )->pairwise_cipher + 4 * ( cipher ) )
/** Get pointer to authentication type from RSN information element
*
* @v rsnp Pointer to RSN information element
* @v akm Index of authentication type to extract
* @ret ptr Pointer to requested authentication type
*
* The @c pairwise_count field must be correct.
*/
#define IEEE80211_RSN_AUTHTYPE( rsnp, akm ) \
( ( rsnp )->akm_list + 4 * ( ( rsnp )->pairwise_count - 1 ) + 4 * ( akm ) )
/** Get pointer to PMKID from RSN information element
*
* @v rsnp Pointer to RSN information element
* @v idx Index of PMKID to extract
* @ret ptr Pointer to requested PMKID
*
* The @c pairwise_count and @c akm_count fields must be correct.
*/
#define IEEE80211_RSN_PMKID( rsnp, idx ) \
( ( rsnp )->pmkid_list + 4 * ( ( rsnp )->pairwise_count - 1 ) + \
4 * ( ( rsnp )->akm_count - 1 ) + 16 * ( idx ) )
/** Verify size of RSN information element
*
* @v rsnp Pointer to RSN information element
* @ret ok TRUE if count fields are consistent with length field
*
* It is important to drop any RSN IE that does not pass this function
* before using the @c IEEE80211_RSN_FIELD, @c IEEE80211_RSN_CIPHER,
* and @c IEEE80211_RSN_AUTHTYPE macros, to avoid potential security
* compromise due to a malformed RSN IE.
*
* This function does not consider the possibility of some PMKIDs
* included in the RSN IE, because PMKIDs are only included in RSN IEs
* sent in association request frames, and we should never receive an
* association request frame. An RSN IE that includes PMKIDs will
* always fail this check.
*/
static inline int ieee80211_rsn_check ( struct ieee80211_ie_rsn *rsnp ) {
if ( rsnp->len < 12 + 4 * rsnp->pairwise_count )
return 0;
return ( rsnp->len == 12 + 4 * ( rsnp->pairwise_count +
IEEE80211_RSN_FIELD ( rsnp, akm_count ) ) );
}
/** Calculate necessary size of RSN information element /** Calculate necessary size of RSN information element
* *
* @v npair Number of pairwise ciphers supported * @v npair Number of pairwise ciphers supported
* @v nauth Number of authentication types supported * @v nauth Number of authentication types supported
* @v npmkid Number of PMKIDs to include * @v npmkid Number of PMKIDs to include
* @ret size Necessary size of RSN IE, including header bytes * @v is_rsn If TRUE, calculate RSN IE size; if FALSE, calculate WPA IE size
* @ret size Necessary size of IE, including header bytes
*/ */
static inline size_t ieee80211_rsn_size ( int npair, int nauth, int npmkid ) { static inline size_t ieee80211_rsn_size ( int npair, int nauth, int npmkid,
return 16 + 4 * ( npair + nauth ) + 16 * npmkid; int rsn_ie ) {
return 16 + 4 * ( npair + nauth ) + 16 * npmkid - 4 * ! rsn_ie;
} }
/** Make OUI plus type byte into 32-bit integer for easy comparison */
#if __BYTE_ORDER == __BIG_ENDIAN
#define _MKOUI( a, b, c, t ) \
( ( ( a ) << 24 ) | ( ( b ) << 16 ) | ( ( c ) << 8 ) | ( d ) )
#define OUI_ORG_MASK 0xFFFFFF00
#define OUI_TYPE_MASK 0x000000FF
#else
#define _MKOUI( a, b, c, t ) \
( ( ( t ) << 24 ) | ( ( c ) << 16 ) | ( ( b ) << 8 ) | ( a ) )
#define OUI_ORG_MASK 0x00FFFFFF
#define OUI_TYPE_MASK 0xFF000000
#endif
/** Organization part for OUIs in standard RSN IE */
#define IEEE80211_RSN_OUI _MKOUI ( 0x00, 0x0F, 0xAC, 0 )
/** Organization part for OUIs in old WPA IE */
#define IEEE80211_WPA_OUI _MKOUI ( 0x00, 0x50, 0xF2, 0 )
/** Old vendor-type WPA IE OUI type + subtype */
#define IEEE80211_WPA_OUI_VEN _MKOUI ( 0x00, 0x50, 0xF2, 0x01 )
/** 802.11 RSN IE: expected version number */ /** 802.11 RSN IE: expected version number */
#define IEEE80211_RSN_VERSION 1 #define IEEE80211_RSN_VERSION 1
/** 802.11 RSN IE: fourth byte of cipher type for 40-bit WEP */ /** 802.11 RSN IE: cipher type for 40-bit WEP */
#define IEEE80211_RSN_CTYPE_WEP40 1 #define IEEE80211_RSN_CTYPE_WEP40 _MKOUI ( 0, 0, 0, 0x01 )
/** 802.11 RSN IE: fourth byte of cipher type for 104-bit WEP */ /** 802.11 RSN IE: cipher type for 104-bit WEP */
#define IEEE80211_RSN_CTYPE_WEP104 5 #define IEEE80211_RSN_CTYPE_WEP104 _MKOUI ( 0, 0, 0, 0x05 )
/** 802.11 RSN IE: fourth byte of cipher type for TKIP ("WPA") */ /** 802.11 RSN IE: cipher type for TKIP ("WPA") */
#define IEEE80211_RSN_CTYPE_TKIP 2 #define IEEE80211_RSN_CTYPE_TKIP _MKOUI ( 0, 0, 0, 0x02 )
/** 802.11 RSN IE: fourth byte of cipher type for CCMP ("WPA2") */ /** 802.11 RSN IE: cipher type for CCMP ("WPA2") */
#define IEEE80211_RSN_CTYPE_CCMP 4 #define IEEE80211_RSN_CTYPE_CCMP _MKOUI ( 0, 0, 0, 0x04 )
/** 802.11 RSN IE: fourth byte of cipher type for "use group" /** 802.11 RSN IE: cipher type for "use group"
* *
* This can only appear as a pairwise cipher, and means unicast frames * This can only appear as a pairwise cipher, and means unicast frames
* should be encrypted in the same way as broadcast/multicast frames. * should be encrypted in the same way as broadcast/multicast frames.
*/ */
#define IEEE80211_RSN_CTYPE_USEGROUP 0 #define IEEE80211_RSN_CTYPE_USEGROUP _MKOUI ( 0, 0, 0, 0x00 )
/** 802.11 RSN IE: fourth byte of auth method type for using an 802.1X server */ /** 802.11 RSN IE: auth method type for using an 802.1X server */
#define IEEE80211_RSN_ATYPE_8021X 1 #define IEEE80211_RSN_ATYPE_8021X _MKOUI ( 0, 0, 0, 0x01 )
/** 802.11 RSN IE: fourth byte of auth method type for using a pre-shared key */ /** 802.11 RSN IE: auth method type for using a pre-shared key */
#define IEEE80211_RSN_ATYPE_PSK 2 #define IEEE80211_RSN_ATYPE_PSK _MKOUI ( 0, 0, 0, 0x02 )
/** 802.11 RSN IE capabilities: AP supports pre-authentication */ /** 802.11 RSN IE capabilities: AP supports pre-authentication */
#define IEEE80211_RSN_CAPAB_PREAUTH 0x001 #define IEEE80211_RSN_CAPAB_PREAUTH 0x001
@ -997,6 +924,42 @@ static inline size_t ieee80211_rsn_size ( int npair, int nauth, int npmkid ) {
#define IEEE80211_RSN_CAPAB_PEERKEY 0x200 #define IEEE80211_RSN_CAPAB_PEERKEY 0x200
/** 802.11 RSN IE capabilities: One replay counter
*
* This should be AND'ed with @c IEEE80211_RSN_CAPAB_PTKSA_REPLAY or
* @c IEEE80211_RSN_CAPAB_GTKSA_REPLAY (or both) to produce a value
* which can be OR'ed into the capabilities field.
*/
#define IEEE80211_RSN_1_CTR 0x000
/** 802.11 RSN IE capabilities: Two replay counters */
#define IEEE80211_RSN_2_CTR 0x014
/** 802.11 RSN IE capabilities: Four replay counters */
#define IEEE80211_RSN_4_CTR 0x028
/** 802.11 RSN IE capabilities: 16 replay counters */
#define IEEE80211_RSN_16_CTR 0x03C
/** 802.11 Vendor Specific information element
*
* One often sees the RSN IE masquerading as vendor-specific on
* devices that were produced prior to 802.11i (the WPA amendment)
* being finalized.
*/
struct ieee80211_ie_vendor {
u8 id; /**< Vendor-specific ID: 221 */
u8 len; /**< Vendor-specific length: variable */
u32 oui; /**< OUI and vendor-specific type byte */
u8 data[0]; /**< Vendor-specific data */
} __attribute__ ((packed));
/** Information element ID for Vendor Specific information element */
#define IEEE80211_IE_VENDOR 221
/** Any 802.11 information element /** Any 802.11 information element
* *
@ -1034,8 +997,23 @@ union ieee80211_ie
/** Security information */ /** Security information */
struct ieee80211_ie_rsn rsn; struct ieee80211_ie_rsn rsn;
/** Vendor-specific */
struct ieee80211_ie_vendor vendor;
}; };
/** Check that 802.11 information element is bounded by buffer
*
* @v ie Information element
* @v end End of buffer in which information element is stored
* @ret ok TRUE if the IE is completely contained within the buffer
*/
static inline int ieee80211_ie_bound ( union ieee80211_ie *ie, void *end )
{
void *iep = ie;
return ( iep + 2 <= end && iep + 2 + ie->len <= end );
}
/** Advance to next 802.11 information element /** Advance to next 802.11 information element
* *
* @v ie Current information element pointer * @v ie Current information element pointer
@ -1055,7 +1033,7 @@ static inline union ieee80211_ie * ieee80211_next_ie ( union ieee80211_ie *ie,
if ( ! end ) if ( ! end )
return next_ie; return next_ie;
if ( next_ie_byte < end && next_ie_byte + next_ie->len <= end ) if ( ieee80211_ie_bound ( next_ie, end ) )
return next_ie; return next_ie;
return NULL; return NULL;

View File

@ -119,6 +119,9 @@ enum net80211_security_proto {
* in the same 4-way handshake as the PSK method. * in the same 4-way handshake as the PSK method.
*/ */
NET80211_SECPROT_EAP = 2, NET80211_SECPROT_EAP = 2,
/** Dummy value used when the handshaking type can't be detected */
NET80211_SECPROT_UNKNOWN = 3,
}; };
@ -169,6 +172,9 @@ enum net80211_crypto_alg {
* against WEP and minor success against TKIP fail. * against WEP and minor success against TKIP fail.
*/ */
NET80211_CRYPT_CCMP = 3, NET80211_CRYPT_CCMP = 3,
/** Dummy value used when the cryptosystem can't be detected */
NET80211_CRYPT_UNKNOWN = 4,
}; };
@ -539,37 +545,171 @@ struct net80211_frag_cache
struct io_buffer *iob[16]; struct io_buffer *iob[16];
}; };
/** Interface to an 802.11 cryptographic algorithm
/** Interface to an 802.11 security handshaking protocol
* *
* Cryptographic algorithms define a net80211_crypto structure * Security handshaking protocols handle parsing a user-specified key
* statically, using a gPXE linker table to make it available to the * into a suitable input to the encryption algorithm, and for WPA and
* 802.11 layer. When the algorithm needs to be used, the 802.11 code * better systems, manage performing whatever authentication with the
* will allocate a copy of the static definition plus whatever space * network is necessary.
* the algorithm has requested for private state, and point *
* net80211_device::crypto at it. * At all times when any method in this structure is called with a
* net80211_device argument @a dev, a dynamically allocated copy of
* the handshaker structure itself with space for the requested amount
* of private data may be accessed as @c dev->handshaker. The
* structure will not be modified, and will only be freed during
* reassociation and device closing after the @a stop method has been
* called.
*/
struct net80211_handshaker
{
/** The security handshaking protocol implemented */
enum net80211_security_proto protocol;
/** Initialize security handshaking protocol
*
* @v dev 802.11 device
* @ret rc Return status code
*
* This method is expected to access @c netX/key or other
* applicable settings to determine the parameters for
* handshaking. If no handshaking is required, it should call
* sec80211_install() with the cryptosystem and key that are
* to be used, and @c start and @c step should be set to @c
* NULL.
*
* This is always called just before association is performed,
* but after its parameters have been set; in particular, you
* may rely on the contents of the @a essid field in @a dev.
*/
int ( * init ) ( struct net80211_device *dev );
/** Start handshaking
*
* @v dev 802.11 device
* @ret rc Return status code
*
* This method is expected to set up internal state so that
* packets sent immediately after association, before @a step
* can be called, will be handled appropriately.
*
* This is always called just before association is attempted.
*/
int ( * start ) ( struct net80211_device *dev );
/** Process handshaking state
*
* @v dev 802.11 device
* @ret rc Return status code, or positive if done
*
* This method is expected to perform as much progress on the
* protocol it implements as is possible without blocking. It
* should return 0 if it wishes to be called again, a negative
* return status code on error, or a positive value if
* handshaking is complete. In the case of a positive return,
* net80211_crypto_install() must have been called.
*
* If handshaking may require further action (e.g. an AP that
* might decide to rekey), handlers must be installed by this
* function that will act without further calls to @a step.
*/
int ( * step ) ( struct net80211_device *dev );
/** Change cryptographic key based on setting
*
* @v dev 802.11 device
* @ret rc Return status code
*
* This method is called whenever the @c netX/key setting
* @e may have been changed. It is expected to determine
* whether it did in fact change, and if so, to install the
* new key using net80211_crypto_install(). If it is not
* possible to do this immediately, this method should return
* an error; in that case the 802.11 stack will reassociate,
* following the usual init/start/step sequence.
*
* This method is only relevant when it is possible to
* associate successfully with an incorrect key. When it is
* not, a failed association will be retried until the user
* changes the key setting, and a successful association will
* not be dropped due to such a change. When association with
* an incorrect key is impossible, this function should return
* 0 after performing no action.
*/
int ( * change_key ) ( struct net80211_device *dev );
/** Stop security handshaking handlers
*
* @v dev 802.11 device
*
* This method is called just before freeing a security
* handshaker; it could, for example, delete a process that @a
* start had created to manage the security of the connection.
* If not needed it may be set to NULL.
*/
void ( * stop ) ( struct net80211_device *dev );
/** Amount of private data requested
*
* Before @c init is called for the first time, this structure's
* @c priv pointer will point to this many bytes of allocated
* data, where the allocation will be performed separately for
* each net80211_device.
*/
int priv_len;
/** Whether @a start has been called
*
* Reset to 0 after @a stop is called.
*/
int started;
/** Pointer to private data
*
* In initializing this structure statically for a linker
* table, set this to NULL.
*/
void *priv;
};
#define NET80211_HANDSHAKERS __table ( struct net80211_handshaker, \
"net80211_handshakers" )
#define __net80211_handshaker __table_entry ( NET80211_HANDSHAKERS, 01 )
/** Interface to an 802.11 cryptosystem
*
* Cryptosystems define a net80211_crypto structure statically, using
* a gPXE linker table to make it available to the 802.11 layer. When
* the cryptosystem needs to be used, the 802.11 code will allocate a
* copy of the static definition plus whatever space the algorithm has
* requested for private state, and point net80211_device::crypto or
* net80211_device::gcrypto at it.
*/ */
struct net80211_crypto struct net80211_crypto
{ {
/** The cryptographic algorithm implemented */ /** The cryptographic algorithm implemented */
enum net80211_crypto_alg algorithm; enum net80211_crypto_alg algorithm;
/** Initialize cryptographic algorithm using a given key /** Initialize cryptosystem using a given key
* *
* @v crypto 802.11 cryptographic algorithm * @v crypto 802.11 cryptosystem
* @v key Pointer to key bytes * @v key Pointer to key bytes
* @v keylen Number of key bytes * @v keylen Number of key bytes
* @v rsc Initial receive sequence counter, if applicable
* @ret rc Return status code * @ret rc Return status code
* *
* This method is passed the communication key provided by the * This method is passed the communication key provided by the
* security handshake handler, which will already be in the * security handshake handler, which will already be in the
* low-level form required. * low-level form required. It may not store a pointer to the
* key after returning; it must copy it to its private storage.
*/ */
int ( * initialize ) ( struct net80211_crypto *crypto, u8 *key, int ( * init ) ( struct net80211_crypto *crypto, const void *key,
int keylen ); int keylen, const void *rsc );
/** Encrypt a frame using the cryptographic algorithm /** Encrypt a frame using the cryptosystem
* *
* @v crypto 802.11 cryptographic algorithm * @v crypto 802.11 cryptosystem
* @v iob I/O buffer * @v iob I/O buffer
* @ret eiob Newly allocated I/O buffer with encrypted packet * @ret eiob Newly allocated I/O buffer with encrypted packet
* *
@ -593,9 +733,9 @@ struct net80211_crypto
struct io_buffer * ( * encrypt ) ( struct net80211_crypto *crypto, struct io_buffer * ( * encrypt ) ( struct net80211_crypto *crypto,
struct io_buffer *iob ); struct io_buffer *iob );
/** Decrypt a frame using the cryptographic algorithm /** Decrypt a frame using the cryptosystem
* *
* @v crypto 802.11 cryptographic algorithm * @v crypto 802.11 cryptosystem
* @v eiob Encrypted I/O buffer * @v eiob Encrypted I/O buffer
* @ret iob Newly allocated I/O buffer with decrypted packet * @ret iob Newly allocated I/O buffer with decrypted packet
* *
@ -626,6 +766,9 @@ struct net80211_crypto
void *priv; void *priv;
}; };
#define NET80211_CRYPTOS __table ( struct net80211_crypto, "net80211_cryptos" )
#define __net80211_crypto __table_entry ( NET80211_CRYPTOS, 01 )
struct net80211_probe_ctx; struct net80211_probe_ctx;
struct net80211_assoc_ctx; struct net80211_assoc_ctx;
@ -732,6 +875,9 @@ struct net80211_device
struct net80211_assoc_ctx *assoc; struct net80211_assoc_ctx *assoc;
} ctx; } ctx;
/** Security handshaker being used */
struct net80211_handshaker *handshaker;
/** State of our association to the network /** State of our association to the network
* *
* Since the association process happens asynchronously, it's * Since the association process happens asynchronously, it's
@ -777,14 +923,33 @@ struct net80211_device
/** Return status code associated with @c state */ /** Return status code associated with @c state */
int assoc_rc; int assoc_rc;
/** RSN or WPA information element to include with association
*
* If set to @c NULL, none will be included. It is expected
* that this will be set by the @a init function of a security
* handshaker if it is needed.
*/
union ieee80211_ie *rsn_ie;
/* ---------- Parameters of currently associated network ---------- */ /* ---------- Parameters of currently associated network ---------- */
/** 802.11 cryptographic algorithm for our current network /** 802.11 cryptosystem for our current network
* *
* For an open network, this will be set to NULL. * For an open network, this will be set to NULL.
*/ */
struct net80211_crypto *crypto; struct net80211_crypto *crypto;
/** 802.11 cryptosystem for multicast and broadcast frames
*
* If this is NULL, the cryptosystem used for receiving
* unicast frames will also be used for receiving multicast
* and broadcast frames. Transmitted multicast and broadcast
* frames are always sent unicast to the AP, who multicasts
* them on our behalf; thus they always use the unicast
* cryptosystem.
*/
struct net80211_crypto *gcrypto;
/** MAC address of the access point most recently associated */ /** MAC address of the access point most recently associated */
u8 bssid[ETH_ALEN]; u8 bssid[ETH_ALEN];
@ -927,6 +1092,10 @@ struct net80211_wlan
}; };
/** 802.11 encryption key setting */
extern struct setting net80211_key_setting __setting;
/** /**
* @defgroup net80211_probe 802.11 network location API * @defgroup net80211_probe 802.11 network location API
* @{ * @{
@ -974,6 +1143,7 @@ int net80211_send_auth ( struct net80211_device *dev,
struct net80211_wlan *wlan, int method ); struct net80211_wlan *wlan, int method );
int net80211_send_assoc ( struct net80211_device *dev, int net80211_send_assoc ( struct net80211_device *dev,
struct net80211_wlan *wlan ); struct net80211_wlan *wlan );
void net80211_deauthenticate ( struct net80211_device *dev, int rc );
/** @} */ /** @} */

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _GPXE_SEC80211_H
#define _GPXE_SEC80211_H
FILE_LICENCE ( GPL2_OR_LATER );
#include <gpxe/net80211.h>
#include <errno.h>
/** @file
*
* Definitions for general secured-network routines.
*
* Any function in this file which may be referenced by code which is
* not exclusive to encryption-enabled builds (e.g. sec80211_detect(),
* which is called by net80211_probe_step() to fill the net80211_wlan
* structure's security fields) must be declared as a weak symbol,
* using an inline interface similar to that used for
* sec80211_detect() below. This prevents secure network support from
* bloating general builds by any more than a few tiny hooks to call
* crypto functions when crypto structures are non-NULL.
*/
int _sec80211_detect ( struct io_buffer *iob,
enum net80211_security_proto *secprot,
enum net80211_crypto_alg *crypt )
__attribute__ (( weak ));
/**
* Inline safety wrapper for _sec80211_detect()
*
* @v iob I/O buffer containing beacon frame
* @ret secprot Security handshaking protocol used by network
* @ret crypt Cryptosystem used by network
* @ret rc Return status code
*
* This function transparently calls _sec80211_detect() if the file
* containing it was compiled in, or returns an error indication of
* @c -ENOTSUP if not.
*/
static inline int sec80211_detect ( struct io_buffer *iob,
enum net80211_security_proto *secprot,
enum net80211_crypto_alg *crypt ) {
if ( _sec80211_detect )
return _sec80211_detect ( iob, secprot, crypt );
return -ENOTSUP;
}
int sec80211_detect_ie ( int is_rsn, u8 *start, u8 *end,
enum net80211_security_proto *secprot,
enum net80211_crypto_alg *crypt );
u8 * sec80211_find_rsn ( union ieee80211_ie *ie, void *ie_end,
int *is_rsn, u8 **end );
int sec80211_install ( struct net80211_crypto **which,
enum net80211_crypto_alg crypt,
const void *key, int len, const void *rsc );
u32 sec80211_rsn_get_crypto_desc ( enum net80211_crypto_alg crypt, int rsnie );
u32 sec80211_rsn_get_akm_desc ( enum net80211_security_proto secprot,
int rsnie );
enum net80211_crypto_alg sec80211_rsn_get_net80211_crypt ( u32 desc );
#endif /* _GPXE_SEC80211_H */

View File

@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <gpxe/ieee80211.h> #include <gpxe/ieee80211.h>
#include <gpxe/netdevice.h> #include <gpxe/netdevice.h>
#include <gpxe/net80211.h> #include <gpxe/net80211.h>
#include <gpxe/sec80211.h>
#include <gpxe/timer.h> #include <gpxe/timer.h>
#include <gpxe/nap.h> #include <gpxe/nap.h>
#include <unistd.h> #include <unistd.h>
@ -188,7 +189,8 @@ static void net80211_handle_auth ( struct net80211_device *dev,
struct io_buffer *iob ); struct io_buffer *iob );
static void net80211_handle_assoc_reply ( struct net80211_device *dev, static void net80211_handle_assoc_reply ( struct net80211_device *dev,
struct io_buffer *iob ); struct io_buffer *iob );
static int net80211_send_disassoc ( struct net80211_device *dev, int reason ); static int net80211_send_disassoc ( struct net80211_device *dev, int reason,
int deauth );
static void net80211_handle_mgmt ( struct net80211_device *dev, static void net80211_handle_mgmt ( struct net80211_device *dev,
struct io_buffer *iob, int signal ); struct io_buffer *iob, int signal );
/** @} */ /** @} */
@ -208,15 +210,16 @@ static void net80211_rx_frag ( struct net80211_device *dev,
* @defgroup net80211_settings 802.11 settings handlers * @defgroup net80211_settings 802.11 settings handlers
* @{ * @{
*/ */
static int net80211_check_ssid_update ( void ); static int net80211_check_settings_update ( void );
/** 802.11 settings applicator /** 802.11 settings applicator
* *
* When the SSID is changed, this will cause any open devices to * When the SSID is changed, this will cause any open devices to
* re-associate. * re-associate; when the encryption key is changed, we similarly
* update their state.
*/ */
struct settings_applicator net80211_ssid_applicator __settings_applicator = { struct settings_applicator net80211_applicator __settings_applicator = {
.apply = net80211_check_ssid_update, .apply = net80211_check_settings_update,
}; };
/** The network name to associate with /** The network name to associate with
@ -242,6 +245,18 @@ struct setting net80211_active_setting __setting = {
.type = &setting_type_int8, .type = &setting_type_int8,
}; };
/** The cryptographic key to use
*
* For hex WEP keys, as is common, this must be entered using the
* normal gPXE method for entering hex settings; an ASCII string of
* hex characters will not behave as expected.
*/
struct setting net80211_key_setting __setting = {
.name = "key",
.description = "Encryption key for protected 802.11 networks",
.type = &setting_type_string,
};
/** @} */ /** @} */
@ -294,7 +309,16 @@ static void net80211_netdev_close ( struct net_device *netdev )
/* Send disassociation frame to AP, to be polite */ /* Send disassociation frame to AP, to be polite */
if ( dev->state & NET80211_ASSOCIATED ) if ( dev->state & NET80211_ASSOCIATED )
net80211_send_disassoc ( dev, IEEE80211_REASON_LEAVING ); net80211_send_disassoc ( dev, IEEE80211_REASON_LEAVING, 0 );
if ( dev->handshaker && dev->handshaker->stop &&
dev->handshaker->started )
dev->handshaker->stop ( dev );
free ( dev->crypto );
free ( dev->handshaker );
dev->crypto = NULL;
dev->handshaker = NULL;
netdev_link_down ( netdev ); netdev_link_down ( netdev );
dev->state = 0; dev->state = 0;
@ -317,16 +341,25 @@ static int net80211_netdev_transmit ( struct net_device *netdev,
struct io_buffer *iobuf ) struct io_buffer *iobuf )
{ {
struct net80211_device *dev = netdev->priv; struct net80211_device *dev = netdev->priv;
struct ieee80211_frame *hdr = iobuf->data;
int rc = -ENOSYS; int rc = -ENOSYS;
if ( dev->crypto ) { if ( dev->crypto && ! ( hdr->fc & IEEE80211_FC_PROTECTED ) &&
( ( hdr->fc & IEEE80211_FC_TYPE ) == IEEE80211_TYPE_DATA ) ) {
struct io_buffer *niob = dev->crypto->encrypt ( dev->crypto, struct io_buffer *niob = dev->crypto->encrypt ( dev->crypto,
iobuf ); iobuf );
if ( ! niob ) if ( ! niob )
return -ENOMEM; /* only reason encryption could fail */ return -ENOMEM; /* only reason encryption could fail */
free_iob ( iobuf ); /* Free the non-encrypted iob */
iobuf = niob; netdev_tx_complete ( netdev, iobuf );
/* Transmit the encrypted iob; the Protected flag is
set, so we won't recurse into here again */
netdev_tx ( netdev, niob );
/* Don't transmit the freed packet */
return 0;
} }
if ( dev->op->transmit ) if ( dev->op->transmit )
@ -482,7 +515,7 @@ static int net80211_ll_push ( struct net_device *netdev,
( void * ) hdr + IEEE80211_TYP_FRAME_HEADER_LEN; ( void * ) hdr + IEEE80211_TYP_FRAME_HEADER_LEN;
/* We can't send data packets if we're not associated. */ /* We can't send data packets if we're not associated. */
if ( ! netdev_link_ok ( netdev ) ) { if ( ! ( dev->state & NET80211_ASSOCIATED ) ) {
if ( dev->assoc_rc ) if ( dev->assoc_rc )
return dev->assoc_rc; return dev->assoc_rc;
return -ENETUNREACH; return -ENETUNREACH;
@ -1032,7 +1065,7 @@ static int net80211_process_ie ( struct net80211_device *dev,
int changed = 0; int changed = 0;
int band = dev->channels[dev->channel].band; int band = dev->channels[dev->channel].band;
if ( ( void * ) ie >= ie_end ) if ( ! ieee80211_ie_bound ( ie, ie_end ) )
return 0; return 0;
for ( ; ie; ie = ieee80211_next_ie ( ie, ie_end ) ) { for ( ; ie; ie = ieee80211_next_ie ( ie, ie_end ) ) {
@ -1103,10 +1136,6 @@ static int net80211_process_ie ( struct net80211_device *dev,
if ( ! ( ie->erp_info & IEEE80211_ERP_BARKER_LONG ) ) if ( ! ( ie->erp_info & IEEE80211_ERP_BARKER_LONG ) )
dev->phy_flags |= NET80211_PHY_USE_SHORT_PREAMBLE; dev->phy_flags |= NET80211_PHY_USE_SHORT_PREAMBLE;
break; break;
case IEEE80211_IE_RSN:
/* XXX need to implement WPA stuff */
break;
} }
} }
@ -1140,10 +1169,20 @@ static int net80211_process_ie ( struct net80211_device *dev,
exist, so insertion sort works well. */ exist, so insertion sort works well. */
for ( i = 1; i < dev->nr_rates; i++ ) { for ( i = 1; i < dev->nr_rates; i++ ) {
u16 rate = dev->rates[i]; u16 rate = dev->rates[i];
u32 tmp, br, mask;
for ( j = i - 1; j >= 0 && dev->rates[j] >= rate; j-- ) for ( j = i - 1; j >= 0 && dev->rates[j] >= rate; j-- )
dev->rates[j + 1] = dev->rates[j]; dev->rates[j + 1] = dev->rates[j];
dev->rates[j + 1] = rate; dev->rates[j + 1] = rate;
/* Adjust basic_rates to match by rotating the
bits from bit j+1 to bit i left one position. */
mask = ( ( 1 << i ) - 1 ) & ~( ( 1 << ( j + 1 ) ) - 1 );
br = dev->basic_rates;
tmp = br & ( 1 << i );
br = ( br & ~( mask | tmp ) ) | ( ( br & mask ) << 1 );
br |= ( tmp >> ( i - j - 1 ) );
dev->basic_rates = br;
} }
net80211_set_rtscts_rate ( dev ); net80211_set_rtscts_rate ( dev );
@ -1187,28 +1226,42 @@ net80211_marshal_request_info ( struct net80211_device *dev,
ie->id = IEEE80211_IE_RATES; ie->id = IEEE80211_IE_RATES;
ie->len = dev->nr_rates; ie->len = dev->nr_rates;
if ( ie->len > 8 )
ie->len = 8;
for ( i = 0; i < ie->len; i++ ) { for ( i = 0; i < ie->len; i++ ) {
ie->rates[i] = dev->rates[i] / 5; ie->rates[i] = dev->rates[i] / 5;
if ( dev->basic_rates & ( 1 << i ) ) if ( dev->basic_rates & ( 1 << i ) )
ie->rates[i] |= 0x80; ie->rates[i] |= 0x80;
} }
if ( ie->len > 8 ) { ie = ieee80211_next_ie ( ie, NULL );
/* 802.11 requires we use an Extended Basic Rates IE
for the rates beyond the eighth. */
int rates = ie->len;
memmove ( ( void * ) ie + 2 + 8 + 2, ( void * ) ie + 2 + 8,
rates - 8 );
ie->len = 8;
if ( dev->rsn_ie && dev->rsn_ie->id == IEEE80211_IE_RSN ) {
memcpy ( ie, dev->rsn_ie, dev->rsn_ie->len + 2 );
ie = ieee80211_next_ie ( ie, NULL ); ie = ieee80211_next_ie ( ie, NULL );
ie->id = IEEE80211_IE_EXT_RATES;
ie->len = rates - 8;
} }
ie = ieee80211_next_ie ( ie, NULL ); if ( dev->nr_rates > 8 ) {
/* 802.11 requires we use an Extended Basic Rates IE
for the rates beyond the eighth. */
ie->id = IEEE80211_IE_EXT_RATES;
ie->len = dev->nr_rates - 8;
for ( ; i < dev->nr_rates; i++ ) {
ie->rates[i - 8] = dev->rates[i] / 5;
if ( dev->basic_rates & ( 1 << i ) )
ie->rates[i - 8] |= 0x80;
}
ie = ieee80211_next_ie ( ie, NULL );
}
if ( dev->rsn_ie && dev->rsn_ie->id == IEEE80211_IE_VENDOR ) {
memcpy ( ie, dev->rsn_ie, dev->rsn_ie->len + 2 );
ie = ieee80211_next_ie ( ie, NULL );
}
return ie; return ie;
} }
@ -1275,13 +1328,6 @@ struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev,
ie = net80211_marshal_request_info ( dev, ie = net80211_marshal_request_info ( dev,
probe_req->info_element ); probe_req->info_element );
ie->id = IEEE80211_IE_REQUEST;
ie->len = 3;
ie->request[0] = IEEE80211_IE_COUNTRY;
ie->request[1] = IEEE80211_IE_ERP_INFO;
ie->request[2] = IEEE80211_IE_RSN;
ie = ieee80211_next_ie ( ie, NULL );
iob_put ( ctx->probe, ( void * ) ie - ctx->probe->data ); iob_put ( ctx->probe, ( void * ) ie - ctx->probe->data );
} }
@ -1404,6 +1450,10 @@ int net80211_probe_step ( struct net80211_probe_ctx *ctx )
} }
ie = beacon->info_element; ie = beacon->info_element;
if ( ! ieee80211_ie_bound ( ie, iob->tail ) )
ie = NULL;
while ( ie && ie->id != IEEE80211_IE_SSID ) while ( ie && ie->id != IEEE80211_IE_SSID )
ie = ieee80211_next_ie ( ie, iob->tail ); ie = ieee80211_next_ie ( ie, iob->tail );
@ -1459,10 +1509,27 @@ int net80211_probe_step ( struct net80211_probe_ctx *ctx )
memcpy ( iob_put ( wlan->beacon, iob_len ( iob ) ), memcpy ( iob_put ( wlan->beacon, iob_len ( iob ) ),
iob->data, iob_len ( iob ) ); iob->data, iob_len ( iob ) );
/* XXX actually check capab and RSN ie to if ( ( rc = sec80211_detect ( wlan->beacon, &wlan->handshaking,
figure this out */ &wlan->crypto ) ) == -ENOTSUP ) {
wlan->handshaking = NET80211_SECPROT_NONE; struct ieee80211_beacon *beacon =
wlan->crypto = NET80211_CRYPT_NONE; ( struct ieee80211_beacon * ) hdr->data;
if ( beacon->capability & IEEE80211_CAPAB_PRIVACY ) {
DBG ( "802.11 %p probe: secured network %s but "
"encryption support not compiled in\n",
dev, wlan->essid );
wlan->handshaking = NET80211_SECPROT_UNKNOWN;
wlan->crypto = NET80211_CRYPT_UNKNOWN;
} else {
wlan->handshaking = NET80211_SECPROT_NONE;
wlan->crypto = NET80211_CRYPT_NONE;
}
} else if ( rc != 0 ) {
DBGC ( dev, "802.11 %p probe warning: network "
"%s with unidentifiable security "
"settings: %s\n", dev, wlan->essid,
strerror ( rc ) );
}
ctx->ticks_beacon = now; ctx->ticks_beacon = now;
@ -1739,6 +1806,14 @@ static void net80211_step_associate ( struct process *proc )
DBGC ( dev, "802.11 %p associating\n", dev ); DBGC ( dev, "802.11 %p associating\n", dev );
if ( dev->handshaker && dev->handshaker->start &&
! dev->handshaker->started ) {
rc = dev->handshaker->start ( dev );
if ( rc < 0 )
goto fail;
dev->handshaker->started = 1;
}
rc = net80211_send_assoc ( dev, dev->associating ); rc = net80211_send_assoc ( dev, dev->associating );
if ( rc ) if ( rc )
goto fail; goto fail;
@ -1750,9 +1825,29 @@ static void net80211_step_associate ( struct process *proc )
/* state: crypto sync */ /* state: crypto sync */
DBGC ( dev, "802.11 %p security handshaking\n", dev ); DBGC ( dev, "802.11 %p security handshaking\n", dev );
dev->state |= NET80211_CRYPTO_SYNCED; if ( ! dev->handshaker || ! dev->handshaker->step ) {
/* XXX need to actually do something here once we dev->state |= NET80211_CRYPTO_SYNCED;
support WPA */ return;
}
rc = dev->handshaker->step ( dev );
if ( rc < 0 ) {
/* Only record the returned error if we're
still marked as associated, because an
asynchronous error will have already been
reported to net80211_deauthenticate() and
assoc_rc thereby set. */
if ( dev->state & NET80211_ASSOCIATED )
dev->assoc_rc = rc;
rc = 0;
goto fail;
}
if ( rc > 0 ) {
dev->assoc_rc = 0;
dev->state |= NET80211_CRYPTO_SYNCED;
}
return; return;
} }
@ -1804,27 +1899,36 @@ static void net80211_step_associate ( struct process *proc )
} }
/** /**
* Check for 802.11 SSID updates * Check for 802.11 SSID or key updates
* *
* This acts as a settings applicator; if the user changes netX/ssid, * This acts as a settings applicator; if the user changes netX/ssid,
* and netX is currently open, the association task will be invoked * and netX is currently open, the association task will be invoked
* again. * again. If the user changes the encryption key, the current security
* handshaker will be asked to update its state to match; if that is
* impossible without reassociation, we reassociate.
*/ */
static int net80211_check_ssid_update ( void ) static int net80211_check_settings_update ( void )
{ {
struct net80211_device *dev; struct net80211_device *dev;
char ssid[IEEE80211_MAX_SSID_LEN + 1]; char ssid[IEEE80211_MAX_SSID_LEN + 1];
int key_reassoc;
list_for_each_entry ( dev, &net80211_devices, list ) { list_for_each_entry ( dev, &net80211_devices, list ) {
if ( ! ( dev->netdev->state & NETDEV_OPEN ) ) if ( ! ( dev->netdev->state & NETDEV_OPEN ) )
continue; continue;
key_reassoc = 0;
if ( dev->handshaker && dev->handshaker->change_key &&
dev->handshaker->change_key ( dev ) < 0 )
key_reassoc = 1;
fetch_string_setting ( netdev_settings ( dev->netdev ), fetch_string_setting ( netdev_settings ( dev->netdev ),
&net80211_ssid_setting, ssid, &net80211_ssid_setting, ssid,
IEEE80211_MAX_SSID_LEN + 1 ); IEEE80211_MAX_SSID_LEN + 1 );
if ( strcmp ( ssid, dev->essid ) != 0 && if ( key_reassoc ||
! ( ! ssid[0] && ( dev->state & NET80211_AUTO_SSID ) ) ) { ( ! ( ! ssid[0] && ( dev->state & NET80211_AUTO_SSID ) ) &&
strcmp ( ssid, dev->essid ) != 0 ) ) {
DBGC ( dev, "802.11 %p updating association: " DBGC ( dev, "802.11 %p updating association: "
"%s -> %s\n", dev, dev->essid, ssid ); "%s -> %s\n", dev, dev->essid, ssid );
net80211_autoassociate ( dev ); net80211_autoassociate ( dev );
@ -1846,6 +1950,8 @@ void net80211_autoassociate ( struct net80211_device *dev )
if ( ! ( dev->state & NET80211_WORKING ) ) { if ( ! ( dev->state & NET80211_WORKING ) ) {
DBGC2 ( dev, "802.11 %p spawning association process\n", dev ); DBGC2 ( dev, "802.11 %p spawning association process\n", dev );
process_add ( &dev->proc_assoc ); process_add ( &dev->proc_assoc );
} else {
DBGC2 ( dev, "802.11 %p restarting association\n", dev );
} }
/* Clean up everything an earlier association process might /* Clean up everything an earlier association process might
@ -1865,6 +1971,7 @@ void net80211_autoassociate ( struct net80211_device *dev )
IEEE80211_MAX_SSID_LEN + 1 ); IEEE80211_MAX_SSID_LEN + 1 );
dev->ctx.probe = NULL; dev->ctx.probe = NULL;
dev->associating = NULL; dev->associating = NULL;
dev->assoc_rc = 0;
net80211_set_state ( dev, NET80211_PROBED, NET80211_WORKING, 0 ); net80211_set_state ( dev, NET80211_PROBED, NET80211_WORKING, 0 );
} }
@ -2020,6 +2127,7 @@ int net80211_prepare_assoc ( struct net80211_device *dev,
struct ieee80211_frame *hdr = wlan->beacon->data; struct ieee80211_frame *hdr = wlan->beacon->data;
struct ieee80211_beacon *beacon = struct ieee80211_beacon *beacon =
( struct ieee80211_beacon * ) hdr->data; ( struct ieee80211_beacon * ) hdr->data;
struct net80211_handshaker *handshaker;
int rc; int rc;
assert ( dev->netdev->state & NETDEV_OPEN ); assert ( dev->netdev->state & NETDEV_OPEN );
@ -2028,11 +2136,12 @@ int net80211_prepare_assoc ( struct net80211_device *dev,
memcpy ( dev->bssid, wlan->bssid, ETH_ALEN ); memcpy ( dev->bssid, wlan->bssid, ETH_ALEN );
strcpy ( dev->essid, wlan->essid ); strcpy ( dev->essid, wlan->essid );
free ( dev->rsn_ie );
dev->rsn_ie = NULL;
dev->last_beacon_timestamp = beacon->timestamp; dev->last_beacon_timestamp = beacon->timestamp;
dev->tx_beacon_interval = 1024 * beacon->beacon_interval; dev->tx_beacon_interval = 1024 * beacon->beacon_interval;
/* XXX do crypto setup here */
/* Barring an IE that tells us the channel outright, assume /* Barring an IE that tells us the channel outright, assume
the channel we heard this AP best on is the channel it's the channel we heard this AP best on is the channel it's
communicating on. */ communicating on. */
@ -2051,6 +2160,46 @@ int net80211_prepare_assoc ( struct net80211_device *dev,
dev->rate = 0; dev->rate = 0;
dev->op->config ( dev, NET80211_CFG_RATE ); dev->op->config ( dev, NET80211_CFG_RATE );
/* Free old handshaker and crypto, if they exist */
if ( dev->handshaker && dev->handshaker->stop &&
dev->handshaker->started )
dev->handshaker->stop ( dev );
free ( dev->handshaker );
dev->handshaker = NULL;
free ( dev->crypto );
free ( dev->gcrypto );
dev->crypto = dev->gcrypto = NULL;
/* Find new security handshaker to use */
for_each_table_entry ( handshaker, NET80211_HANDSHAKERS ) {
if ( handshaker->protocol == wlan->handshaking ) {
dev->handshaker = zalloc ( sizeof ( *handshaker ) +
handshaker->priv_len );
if ( ! dev->handshaker )
return -ENOMEM;
memcpy ( dev->handshaker, handshaker,
sizeof ( *handshaker ) );
dev->handshaker->priv = ( ( void * ) dev->handshaker +
sizeof ( *handshaker ) );
break;
}
}
if ( ( wlan->handshaking != NET80211_SECPROT_NONE ) &&
! dev->handshaker ) {
DBGC ( dev, "802.11 %p no support for handshaking scheme %d\n",
dev, wlan->handshaking );
return -( ENOTSUP | ( wlan->handshaking << 8 ) );
}
/* Initialize security handshaker */
if ( dev->handshaker ) {
rc = dev->handshaker->init ( dev );
if ( rc < 0 )
return rc;
}
return 0; return 0;
} }
@ -2091,8 +2240,8 @@ int net80211_send_auth ( struct net80211_device *dev,
* *
* If the authentication method being used is Shared Key, and the * If the authentication method being used is Shared Key, and the
* frame that was received included challenge text, the frame is * frame that was received included challenge text, the frame is
* encrypted using the cryptographic algorithm currently in effect and * encrypted using the cryptosystem currently in effect and sent back
* sent back to the AP to complete the authentication. * to the AP to complete the authentication.
*/ */
static void net80211_handle_auth ( struct net80211_device *dev, static void net80211_handle_auth ( struct net80211_device *dev,
struct io_buffer *iob ) struct io_buffer *iob )
@ -2181,8 +2330,6 @@ int net80211_send_assoc ( struct net80211_device *dev,
DBGP ( "802.11 %p about to send association request:\n", dev ); DBGP ( "802.11 %p about to send association request:\n", dev );
DBGP_HD ( iob->data, ( void * ) ie - iob->data ); DBGP_HD ( iob->data, ( void * ) ie - iob->data );
/* XXX add RSN ie for WPA support */
iob_put ( iob, ( void * ) ie - iob->data ); iob_put ( iob, ( void * ) ie - iob->data );
return net80211_tx_mgmt ( dev, IEEE80211_STYPE_ASSOC_REQ, return net80211_tx_mgmt ( dev, IEEE80211_STYPE_ASSOC_REQ,
@ -2227,9 +2374,11 @@ static void net80211_handle_assoc_reply ( struct net80211_device *dev,
* *
* @v dev 802.11 device * @v dev 802.11 device
* @v reason Reason for disassociation * @v reason Reason for disassociation
* @v deauth If TRUE, send deauthentication instead of disassociation
* @ret rc Return status code * @ret rc Return status code
*/ */
static int net80211_send_disassoc ( struct net80211_device *dev, int reason ) static int net80211_send_disassoc ( struct net80211_device *dev, int reason,
int deauth )
{ {
struct io_buffer *iob = alloc_iob ( 64 ); struct io_buffer *iob = alloc_iob ( 64 );
struct ieee80211_disassoc *disassoc; struct ieee80211_disassoc *disassoc;
@ -2242,8 +2391,28 @@ static int net80211_send_disassoc ( struct net80211_device *dev, int reason )
disassoc = iob_put ( iob, sizeof ( *disassoc ) ); disassoc = iob_put ( iob, sizeof ( *disassoc ) );
disassoc->reason = reason; disassoc->reason = reason;
return net80211_tx_mgmt ( dev, IEEE80211_STYPE_DISASSOC, dev->bssid, return net80211_tx_mgmt ( dev, deauth ? IEEE80211_STYPE_DEAUTH :
iob ); IEEE80211_STYPE_DISASSOC, dev->bssid, iob );
}
/**
* Deauthenticate from current network and try again
*
* @v dev 802.11 device
* @v rc Return status code indicating reason
*
* The deauthentication will be sent using an 802.11 "unspecified
* reason", as is common, but @a rc will be set as a link-up
* error to aid the user in debugging.
*/
void net80211_deauthenticate ( struct net80211_device *dev, int rc )
{
net80211_send_disassoc ( dev, IEEE80211_REASON_UNSPECIFIED, 1 );
dev->assoc_rc = rc;
netdev_link_err ( dev->netdev, rc );
net80211_autoassociate ( dev );
} }
@ -2557,14 +2726,29 @@ void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
iob_unput ( iob, 4 ); iob_unput ( iob, 4 );
} }
if ( hdr->fc & IEEE80211_FC_PROTECTED ) { /* Only decrypt packets from our BSSID, to avoid spurious errors */
if ( ( hdr->fc & IEEE80211_FC_PROTECTED ) &&
! memcmp ( hdr->addr2, dev->bssid, ETH_ALEN ) ) {
/* Decrypt packet; record and drop if it fails */
struct io_buffer *niob; struct io_buffer *niob;
if ( ! dev->crypto ) struct net80211_crypto *crypto = dev->crypto;
goto drop; /* can't decrypt packets on an open network */
niob = dev->crypto->decrypt ( dev->crypto, iob ); if ( ! dev->crypto ) {
if ( ! niob ) DBGC ( dev, "802.11 %p cannot decrypt packet "
goto drop; /* drop failed decryption */ "without a cryptosystem\n", dev );
goto drop_crypt;
}
if ( ( hdr->addr1[0] & 1 ) && dev->gcrypto ) {
/* Use group decryption if needed */
crypto = dev->gcrypto;
}
niob = crypto->decrypt ( crypto, iob );
if ( ! niob ) {
DBGC ( dev, "802.11 %p decryption error\n", dev );
goto drop_crypt;
}
free_iob ( iob ); free_iob ( iob );
iob = niob; iob = niob;
} }
@ -2593,11 +2777,16 @@ void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
rc80211_update_rx ( dev, hdr->fc & IEEE80211_FC_RETRY, rate ); rc80211_update_rx ( dev, hdr->fc & IEEE80211_FC_RETRY, rate );
/* Pass packet onward */ /* Pass packet onward */
if ( netdev_link_ok ( dev->netdev ) ) { if ( dev->state & NET80211_ASSOCIATED ) {
netdev_rx ( dev->netdev, iob ); netdev_rx ( dev->netdev, iob );
return; return;
} }
/* No association? Drop it. */
goto drop;
drop_crypt:
netdev_rx_err ( dev->netdev, NULL, EINVAL_CRYPTO_REQUEST );
drop: drop:
DBGC2 ( dev, "802.11 %p dropped packet fc=%04x seq=%04x\n", dev, DBGC2 ( dev, "802.11 %p dropped packet fc=%04x seq=%04x\n", dev,
hdr->fc, hdr->seq ); hdr->fc, hdr->seq );

503
src/net/80211/sec80211.c Normal file
View File

@ -0,0 +1,503 @@
/*
* Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <gpxe/ieee80211.h>
#include <gpxe/net80211.h>
#include <gpxe/sec80211.h>
/** @file
*
* 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.
*/
/** Mapping from net80211 crypto/secprot types to RSN OUI descriptors */
struct descriptor_map {
/** Value of net80211_crypto_alg or net80211_security_proto */
u32 net80211_type;
/** OUI+type in appropriate byte order, masked to exclude vendor */
u32 oui_type;
};
/** Magic number in @a oui_type showing end of list */
#define END_MAGIC 0xFFFFFFFF
/** Mapping between net80211 cryptosystems and 802.11i cipher IDs */
static struct descriptor_map rsn_cipher_map[] = {
{ .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 handshakers and 802.11i AKM IDs */
static struct descriptor_map rsn_akm_map[] = {
{ .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 },
};
/**
* Install 802.11 cryptosystem
*
* @v which Pointer to the cryptosystem structure to install in
* @v crypt Cryptosystem ID number
* @v key Encryption key to use
* @v len Length of encryption key
* @v rsc Initial receive sequence counter, if applicable
* @ret 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.
*
* @a which must point to either @c dev->crypto (for the normal case
* of installing a unicast cryptosystem) or @c dev->gcrypto (to
* install a cryptosystem that will be used only for decrypting
* group-source frames).
*/
int sec80211_install ( struct net80211_crypto **which,
enum net80211_crypto_alg crypt,
const void *key, int len, const void *rsc )
{
struct net80211_crypto *crypto = *which;
struct net80211_crypto *tbl_crypto;
/* Remove old crypto if it exists */
free ( *which );
*which = NULL;
if ( crypt == NET80211_CRYPT_NONE ) {
DBG ( "802.11-Sec not installing null cryptography\n" );
return 0;
}
/* Find cryptosystem to use */
for_each_table_entry ( tbl_crypto, NET80211_CRYPTOS ) {
if ( tbl_crypto->algorithm == crypt ) {
crypto = zalloc ( sizeof ( *crypto ) +
tbl_crypto->priv_len );
if ( ! crypto ) {
DBG ( "802.11-Sec out of memory\n" );
return -ENOMEM;
}
memcpy ( crypto, tbl_crypto, sizeof ( *crypto ) );
crypto->priv = ( ( void * ) crypto +
sizeof ( *crypto ) );
break;
}
}
if ( ! crypto ) {
DBG ( "802.11-Sec no support for cryptosystem %d\n", crypt );
return -( ENOTSUP | EUNIQ_10 | ( crypt << 8 ) );
}
*which = crypto;
DBG ( "802.11-Sec installing cryptosystem %d as %p with key of "
"length %d\n", crypt, crypto, len );
return crypto->init ( crypto, key, len, rsc );
}
/**
* Determine net80211 crypto or handshaking type value to return for RSN info
*
* @v rsnp Pointer to next descriptor count field in RSN IE
* @v rsn_end Pointer to end of RSN IE
* @v map Descriptor map to use
* @v tbl_start Start of linker table to examine for gPXE support
* @v tbl_end End of linker table to examine for gPXE support
* @ret rsnp Updated to point to first byte after descriptors
* @ret 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 @a tbl_stride must be set to
* sizeof() the appropriate one.
*
* This function expects @a rsnp to point at a two-byte descriptor
* count followed by a list of four-byte cipher or AKM descriptors; it
* will return @c NULL if the input packet is malformed, and otherwise
* set @a 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.
*/
static struct descriptor_map * rsn_pick_desc ( u8 **rsnp, u8 *rsn_end,
struct descriptor_map *map,
void *tbl_start, void *tbl_end )
{
int ndesc;
int ok = 0;
struct descriptor_map *map_ent, *map_ret = NULL;
u8 *rsn = *rsnp;
void *tblp;
size_t tbl_stride = ( map == rsn_cipher_map ?
sizeof ( struct net80211_crypto ) :
sizeof ( struct net80211_handshaker ) );
if ( map != rsn_cipher_map && map != rsn_akm_map )
return NULL;
/* Determine which types we support */
for ( tblp = tbl_start; tblp < tbl_end; tblp += tbl_stride ) {
struct net80211_crypto *crypto = tblp;
struct net80211_handshaker *hs = tblp;
if ( map == rsn_cipher_map )
ok |= ( 1 << crypto->algorithm );
else
ok |= ( 1 << hs->protocol );
}
/* RSN sanity checks */
if ( rsn + 2 > rsn_end ) {
DBG ( "RSN detect: malformed descriptor count\n" );
return NULL;
}
ndesc = *( u16 * ) rsn;
rsn += 2;
if ( ! ndesc ) {
DBG ( "RSN detect: no descriptors\n" );
return NULL;
}
/* Determine which net80211 crypto types are listed */
while ( ndesc-- ) {
u32 desc;
if ( rsn + 4 > rsn_end ) {
DBG ( "RSN detect: malformed descriptor (%d left)\n",
ndesc );
return NULL;
}
desc = *( u32 * ) rsn;
rsn += 4;
for ( map_ent = map; map_ent->oui_type != END_MAGIC; map_ent++ )
if ( map_ent->oui_type == ( desc & OUI_TYPE_MASK ) )
break;
/* Use first cipher as a fallback */
if ( ! map_ret )
map_ret = map_ent;
/* Once we find one we support, use it */
if ( ok & ( 1 << map_ent->net80211_type ) ) {
map_ret = map_ent;
break;
}
}
if ( ndesc > 0 )
rsn += 4 * ndesc;
*rsnp = rsn;
return map_ret;
}
/**
* Find the RSN or WPA information element in the provided beacon frame
*
* @v ie Pointer to first information element to check
* @v ie_end Pointer to end of information element space
* @ret is_rsn TRUE if returned IE is RSN, FALSE if it's WPA
* @ret end Pointer to byte immediately after last byte of data
* @ret 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 @c NULL and
* leaves @a is_rsn and @a 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 @a
* version field is incorrect.
*/
u8 * sec80211_find_rsn ( union ieee80211_ie *ie, void *ie_end,
int *is_rsn, u8 **end )
{
u8 *rsn = NULL;
if ( ! ieee80211_ie_bound ( ie, ie_end ) )
return NULL;
while ( ie ) {
if ( ie->id == IEEE80211_IE_VENDOR &&
ie->vendor.oui == IEEE80211_WPA_OUI_VEN ) {
DBG ( "RSN detect: old-style WPA IE found\n" );
rsn = &ie->vendor.data[0];
*end = rsn + ie->len - 4;
*is_rsn = 0;
} else if ( ie->id == IEEE80211_IE_RSN ) {
DBG ( "RSN detect: 802.11i RSN IE found\n" );
rsn = ( u8 * ) &ie->rsn.version;
*end = rsn + ie->len;
*is_rsn = 1;
}
if ( rsn && ( *end > ( u8 * ) ie_end || rsn >= *end ||
*( u16 * ) rsn != IEEE80211_RSN_VERSION ) ) {
DBG ( "RSN detect: malformed RSN IE or unknown "
"version, keep trying\n" );
rsn = NULL;
}
if ( rsn )
break;
ie = ieee80211_next_ie ( ie, ie_end );
}
if ( ! ie ) {
DBG ( "RSN detect: no RSN IE found\n" );
return NULL;
}
return rsn;
}
/**
* Detect crypto and AKM types from RSN information element
*
* @v is_rsn If TRUE, IE is a new-style RSN information element
* @v start Pointer to first byte of @a version field
* @v end Pointer to first byte not in the RSN IE
* @ret secprot Security handshaking protocol used by network
* @ret crypt Cryptosystem used by network
* @ret rc Return status code
*
* If the IE cannot be parsed, returns an error indication and leaves
* @a secprot and @a crypt unchanged.
*/
int sec80211_detect_ie ( int is_rsn, u8 *start, u8 *end,
enum net80211_security_proto *secprot,
enum net80211_crypto_alg *crypt )
{
enum net80211_security_proto sp;
enum net80211_crypto_alg cr;
struct descriptor_map *map;
u8 *rsn = start;
/* Set some defaults */
cr = ( is_rsn ? NET80211_CRYPT_CCMP : NET80211_CRYPT_TKIP );
sp = NET80211_SECPROT_EAP;
rsn += 2; /* version - already checked */
rsn += 4; /* group cipher - we don't use it here */
if ( rsn >= end )
goto done;
/* Pick crypto algorithm */
map = rsn_pick_desc ( &rsn, end, rsn_cipher_map,
table_start ( NET80211_CRYPTOS ),
table_end ( NET80211_CRYPTOS ) );
if ( ! map )
goto invalid_rsn;
cr = map->net80211_type;
if ( rsn >= end )
goto done;
/* Pick handshaking algorithm */
map = rsn_pick_desc ( &rsn, end, rsn_akm_map,
table_start ( NET80211_HANDSHAKERS ),
table_end ( NET80211_HANDSHAKERS ) );
if ( ! map )
goto invalid_rsn;
sp = map->net80211_type;
done:
DBG ( "RSN detect: OK, crypto type %d, secprot type %d\n", cr, sp );
*secprot = sp;
*crypt = cr;
return 0;
invalid_rsn:
DBG ( "RSN detect: invalid RSN IE\n" );
return -EINVAL;
}
/**
* Detect the cryptosystem and handshaking protocol used by an 802.11 network
*
* @v iob I/O buffer containing beacon frame
* @ret secprot Security handshaking protocol used by network
* @ret crypt Cryptosystem used by network
* @ret 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 @c NULL before calling
* it. If it does not exist, any network with the PRIVACY bit set in
* beacon->capab should be considered unknown.
*/
int _sec80211_detect ( struct io_buffer *iob,
enum net80211_security_proto *secprot,
enum net80211_crypto_alg *crypt )
{
struct ieee80211_frame *hdr = iob->data;
struct ieee80211_beacon *beacon =
( struct ieee80211_beacon * ) hdr->data;
u8 *rsn, *rsn_end;
int is_rsn, rc;
*crypt = NET80211_CRYPT_UNKNOWN;
*secprot = NET80211_SECPROT_UNKNOWN;
/* Find RSN or WPA IE */
if ( ! ( rsn = sec80211_find_rsn ( beacon->info_element, iob->tail,
&is_rsn, &rsn_end ) ) ) {
/* No security IE at all; either WEP or no security. */
*secprot = NET80211_SECPROT_NONE;
if ( beacon->capability & IEEE80211_CAPAB_PRIVACY )
*crypt = NET80211_CRYPT_WEP;
else
*crypt = NET80211_CRYPT_NONE;
return 0;
}
/* Determine type of security */
if ( ( rc = sec80211_detect_ie ( is_rsn, rsn, rsn_end, secprot,
crypt ) ) == 0 )
return 0;
/* If we get here, the RSN IE was invalid */
*crypt = NET80211_CRYPT_UNKNOWN;
*secprot = NET80211_SECPROT_UNKNOWN;
DBG ( "Failed to handle RSN IE:\n" );
DBG_HD ( rsn, rsn_end - rsn );
return rc;
}
/**
* Determine RSN descriptor for specified net80211 ID
*
* @v id net80211 ID value
* @v rsnie Whether to return a new-format (RSN IE) descriptor
* @v map Map to use in translation
* @ret desc RSN descriptor, or 0 on error
*
* If @a rsnie is false, returns an old-format (WPA vendor IE)
* descriptor.
*/
static u32 rsn_get_desc ( unsigned id, int rsnie, struct descriptor_map *map )
{
u32 vendor = ( rsnie ? IEEE80211_RSN_OUI : IEEE80211_WPA_OUI );
for ( ; map->oui_type != END_MAGIC; map++ ) {
if ( map->net80211_type == id )
return map->oui_type | vendor;
}
return 0;
}
/**
* Determine RSN descriptor for specified net80211 cryptosystem number
*
* @v crypt Cryptosystem number
* @v rsnie Whether to return a new-format (RSN IE) descriptor
* @ret desc RSN descriptor
*
* If @a rsnie is false, returns an old-format (WPA vendor IE)
* descriptor.
*/
u32 sec80211_rsn_get_crypto_desc ( enum net80211_crypto_alg crypt, int rsnie )
{
return rsn_get_desc ( crypt, rsnie, rsn_cipher_map );
}
/**
* Determine RSN descriptor for specified net80211 handshaker number
*
* @v secprot Handshaker number
* @v rsnie Whether to return a new-format (RSN IE) descriptor
* @ret desc RSN descriptor
*
* If @a rsnie is false, returns an old-format (WPA vendor IE)
* descriptor.
*/
u32 sec80211_rsn_get_akm_desc ( enum net80211_security_proto secprot,
int rsnie )
{
return rsn_get_desc ( secprot, rsnie, rsn_akm_map );
}
/**
* Determine net80211 cryptosystem number from RSN descriptor
*
* @v desc RSN descriptor
* @ret crypt net80211 cryptosystem enumeration value
*/
enum net80211_crypto_alg sec80211_rsn_get_net80211_crypt ( u32 desc )
{
struct descriptor_map *map = rsn_cipher_map;
for ( ; map->oui_type != END_MAGIC; map++ ) {
if ( map->oui_type == ( desc & OUI_TYPE_MASK ) )
break;
}
return map->net80211_type;
}