blob: 304ed6b557b4a6f68bdfd3a18a8c2e98682b3220 [file] [log] [blame]
/*
* Copyright (c) 2012-2013 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*
* File: $Header: //depot/software/projects/feature_branches/gen5_phase1/os/linux/classic/ap/apps/ssm/lib/aniSsmEapol.c#2 $
*
* Contains definitions of various utilities for EAPoL frame
* parsing and creation.
*
* Author: Mayank D. Upadhyay
* Date: 19-June-2002
* History:-
* Date Modified by Modification Information
* ------------------------------------------------------
*
*/
#include "vos_utils.h"
#include <bapRsnAsfPacket.h>
#include <bapRsnErrors.h>
#include <bapRsnSsmEapol.h>
#include "bapRsn8021xFsm.h"
#include "vos_memory.h"
//#include "aniSsmUtils.h"
//TODO: Put these in an array after EAPOL_TYPE is made an enum
#define ANI_EAPOL_TYPE_PACKET_STR "EAP"
#define ANI_EAPOL_TYPE_START_STR "START"
#define ANI_EAPOL_TYPE_LOGOFF_STR "LOGOFF"
#define ANI_EAPOL_TYPE_KEY_STR "KEY"
#define ANI_EAPOL_TYPE_ASF_ALERT_STR "ALERT"
#define ANI_EAPOL_TYPE_UNKNOWN_STR "UNKNOWN"
/**
* The EAPOL packet is structured as follows:
*/
#define DST_MAC_POS 0
#define SRC_MAC_POS 6
#define ETHER_PROTO_POS 12
#define EAPOL_VERSION_POS 14
#define ANI_EAPOL_TYPE_POS 15
#define EAPOL_BODY_LEN_POS 16
#define EAPOL_BODY_POS EAPOL_RX_HEADER_SIZE
#define EAPOL_BODY_LEN_SIZE 2
#define ANI_SSM_LEGACY_RC4_KEY_SIGN_OFFSET (EAPOL_BODY_POS + 28)
/**
* Bitmasks for the RSN Key Information field
*/
#define ANI_SSM_RSN_KEY_DESC_VERS_MASK 0x0007
#define ANI_SSM_RSN_UNICAST_MASK 0x0008
#define ANI_SSM_RSN_KEY_INDEX_MASK 0x0030
#define ANI_SSM_RSN_INSTALL_MASK 0x0040
#define ANI_SSM_RSN_ACK_MASK 0x0080
#define ANI_SSM_RSN_MIC_MASK 0x0100
#define ANI_SSM_RSN_SECURE_MASK 0x0200
#define ANI_SSM_RSN_ERROR_MASK 0x0400
#define ANI_SSM_RSN_REQUEST_MASK 0x0800
#define ANI_SSM_RSN_ENC_KEY_DATA_MASK 0x1000
#define ANI_SSM_RSN_KEY_DESC_VERS_OFFSET 0
#define ANI_SSM_RSN_KEY_INDEX_OFFSET 4
#define ANI_SSM_RSN_KEY_MIC_OFFSET (EAPOL_BODY_POS + 77)
/**
* Other hard coded values for convenience:
*/
static const v_U8_t
ANI_ETH_P_EAPOL_BYTES[2] = {0x00, 0x03};//BT-AMP security type{0x88, 0x8e};
static const v_U8_t
EAPOL_VERSION_BYTES[1] = {EAPOL_VERSION_1};
static const v_U8_t
ANI_EAPOL_TYPE_PACKET_BYTES[1] = {ANI_EAPOL_TYPE_PACKET};
static const v_U8_t
ANI_EAPOL_TYPE_START_BYTES[1] = {ANI_EAPOL_TYPE_START};
static const v_U8_t
ANI_EAPOL_TYPE_LOGOFF_BYTES[1] = {ANI_EAPOL_TYPE_LOGOFF};
static const v_U8_t
ANI_EAPOL_TYPE_KEY_BYTES[1] = {ANI_EAPOL_TYPE_KEY};
static const v_U8_t
ANI_EAPOL_TYPE_ASF_ALERT_BYTES[1] = {ANI_EAPOL_TYPE_ASF_ALERT};
static const v_U8_t
ZERO_BYTES[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static v_U8_t BAP_RSN_LLC_HEADER[] = {0xAA, 0xAA, 0x03, 0x00, 0x19, 0x58 };
static int
parseRsnKeyDesc(tAniPacket *packet,
tAniEapolRsnKeyDesc **rsnDescPtr);
static int
parseRsnKeyInfo(tAniPacket *packet,
tAniRsnKeyInfo *info);
static int
writeRsnKeyDesc(tAniPacket *packet,
tAniEapolRsnKeyDesc *rsnDesc,
v_U8_t keyDescType);
static int
writeRsnKeyInfo(tAniPacket *packet, tAniRsnKeyInfo *info);
static int
writeRsnKeyMic(v_U32_t cryptHandle,
tAniPacket *eapolFrame,
tAniEapolRsnKeyDesc *rsnDesc,
v_U8_t *micKey,
v_U32_t micKeyLen);
static int
checkRsnKeyMic(v_U32_t cryptHandle,
tAniPacket *eapolFrame,
tAniEapolRsnKeyDesc *rsnDesc,
v_U8_t *micKey,
v_U32_t micKeyLen);
extern void authEapolHandler( tAuthRsnFsm *fsm, tAniPacket *eapolFrame,
tAniMacAddr dstMac,
tAniMacAddr srcMac,
v_U8_t *type);
extern void suppEapolHandler( tSuppRsnFsm *fsm, tAniPacket *eapolFrame,
tAniMacAddr dstMac,
tAniMacAddr srcMac,
v_U8_t *type);
/**
* addEapolHeaders
*
* FUNCTION:
* Prepends the EAPOL header to a packet.
*
* ASSUMPTIONS:
* The packet has enough space available for prepending the EAPOL
* header.
*
* @param packet the packet to prepend to
* @param dstMac the MAC address of the destination (authenticator)
* @param srcMac the MAC address of the source (supplicant)
* @param eapolType the EAPOL-Type field
*
* @return ANI_OK if the operation succeeds
*/
static int
addEapolHeaders(tAniPacket *packet,
tAniMacAddr dstMac,
tAniMacAddr srcMac,
v_U8_t eapolType)
{
int retVal;
v_U16_t len;
do
{
retVal = aniAsfPacketGetLen(packet);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
len = retVal;
retVal = aniAsfPacketPrepend16(packet, len);
retVal = aniAsfPacketPrepend8(packet, eapolType);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketPrependBuffer(packet, EAPOL_VERSION_BYTES, 1);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketPrependBuffer(packet, ANI_ETH_P_EAPOL_BYTES, 2);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
//Since TL expects SNAP header in all packets we send, put it in
retVal = aniAsfPacketPrependBuffer(packet, BAP_RSN_LLC_HEADER, sizeof(BAP_RSN_LLC_HEADER));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
//packet length
len += 6/*length + eapolType+version + eth_type*/ + sizeof(BAP_RSN_LLC_HEADER);
retVal = aniAsfPacketPrepend16(packet, len);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketPrependBuffer(packet, srcMac, sizeof(tAniMacAddr));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketPrependBuffer(packet, dstMac, sizeof(tAniMacAddr));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
}while( 0 );
return retVal;
}
/**
* aniEapolWriteStart
*
* FUNCTION:
* Writes an EAPOL-Start frame to the packet. It is only used by the
* supplicant.
*
* LOGIC:
* Prepend the appropriate EAPOL header to the packet. There is no
* EAPOL payload for this kind of frame.
*
* ASSUMPTIONS:
* The packet has enough space available for prepending the header.
*
* @param packet the packet to which the frame should be written
* @param dstMac the MAC address of the destination (authenticator)
* @param srcMac the MAC address of the source (supplicant)
*
* @return ANI_OK if the operation succeeds
*/
int
aniEapolWriteStart(tAniPacket *packet,
tAniMacAddr dstMac,
tAniMacAddr srcMac)
{
return ( addEapolHeaders(packet, dstMac, srcMac, ANI_EAPOL_TYPE_START) );
}
/**
* aniEapolWriteEapPacket
*
* FUNCTION:
* Writes the EAPOL/EAP-Packet frame headers. It is used
* by both the authenticator and the supplicant. This creates an EAPOL
* frame that is carrying an EAP message as its payload.
*
* LOGIC:
* Prepend the appropriate EAPOL header to the packet.
*
* ASSUMPTIONS:
* The EAP message (ie., the payload) is already available in the
* packet and that the packet has enough space available for
* prepending the EAPOL header.
*
* @param packet the packet containing the EAP message
* @param dstMac the MAC address of the destination (authenticator)
* @param srcMac the MAC address of the source (supplicant)
*
* @return ANI_OK if the operation succeeds
*/
int
aniEapolWriteEapPacket(tAniPacket *eapPacket,
tAniMacAddr dstMac,
tAniMacAddr srcMac)
{
return( addEapolHeaders(eapPacket, dstMac, srcMac, ANI_EAPOL_TYPE_PACKET) );
}
/**
* aniEapolParse
*
* FUNCTION:
* Parses an EAPoL frame to the first level of headers (no EAP
* headers are parsed).
*
* NOTE: This is a non-destructive read, that is the
* headers are not stripped off the packet. However, any additional
* data at the end of the packet, beyond what the EAPoL headers encode
* will be stripped off.
*
* @param packet the packet containing the EAPoL frame to parse
* @param dstMac a pointer to set to the location of the destination
* MAC address
* @param srcMac a pointer to set to the location of the source
* MAC address
* @param type a pointer to set to the location of the EAPOL type
* field.
*
* @return the non-negative length of the EAPOL payload if the operation
* succeeds
*/
int
aniEapolParse(tAniPacket *packet,
v_U8_t **dstMac,
v_U8_t **srcMac,
v_U8_t **type)
{
v_U16_t frameType;
v_U8_t *ptr;
int retVal;
int tmp;
if (aniAsfPacketGetLen(packet) < EAPOL_BODY_POS)
return ANI_E_ILLEGAL_ARG;
retVal = aniAsfPacketGetBytes(packet, &ptr);
if( !ANI_IS_STATUS_SUCCESS( retVal ) )
{
return retVal;
}
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_INFO,
"Supp parsing EAPOL packet of len %d: \n",
retVal);
frameType = (ptr[ETHER_PROTO_POS] << 8) + ptr[ETHER_PROTO_POS+1];
/*
* Validate the EAPOL-FRAME
*/
if (frameType != ANI_ETH_P_EAPOL)
return ANI_E_ILLEGAL_ARG;
*dstMac = ptr + DST_MAC_POS;
*srcMac = ptr + SRC_MAC_POS;
// if (ptr[EAPOL_VERSION_POS] != EAPOL_VERSION_1)
// return ANI_E_ILLEGAL_ARG;
*type = ptr + ANI_EAPOL_TYPE_POS;
retVal = (ptr[EAPOL_BODY_LEN_POS] << 8) + ptr[EAPOL_BODY_LEN_POS + 1];
/*
* Validate the length of the body. Allow for longer
* packets than encoded, but encoding should not be larger than
* packet.
* Note: EAPOL body len does not include headers
*/
tmp = aniAsfPacketGetLen(packet) - EAPOL_RX_HEADER_SIZE;
if (retVal > tmp)
{
retVal = ANI_E_ILLEGAL_ARG;
}
else {
if (retVal < tmp)
{
retVal = aniAsfPacketTruncateFromRear(packet, tmp - retVal);
}
}
return retVal;
}
/**
* aniEapolWriteKey
*
* Writes out a complete EAPOL-Key frame. The key descriptor is
* appended to the packet and the EAPOL header is prepended to it. If
* a micKey is passed in, then a MIC is calculated and inserted into
* the frame.
*
* @param packet the packet to write to
* @param dstMac the destination MAC address
* @param srcMac the source MAC address
* @param descType the key descriptor type
* (ANI_EAPOL_KEY_DESC_TYPE_LEGACY_RC4 or
* ANI_EAPOL_KEY_DESC_TYPE_RSN).
* @param keyDescData the key descriptor data corresponding to the
* above descType. The signature field is ignored and will be
* generated in the packet. The key bytes are expected to be encrypted
* if they need to be encrypted.
* @param micKey the MIC key
* @param micKeyLen the number of bytes in the MIC key
*
* @return ANI_OK if the operation succeeds
*
*/
int
aniEapolWriteKey(v_U32_t cryptHandle,
tAniPacket *packet,
tAniMacAddr dstMac,
tAniMacAddr srcMac,
int descType,
void *keyDescData,
v_U8_t *micKey,
v_U32_t micKeyLen)
{
int retVal;
if (packet == NULL)
return ANI_E_NULL_VALUE;
do
{
if ((descType == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW)
|| (descType == ANI_EAPOL_KEY_DESC_TYPE_RSN))
{
retVal = writeRsnKeyDesc(packet,
(tAniEapolRsnKeyDesc *) keyDescData,
// Indicate
// ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW
// or ANI_EAPOL_KEY_DESC_TYPE_RSN
descType);
if( !ANI_IS_STATUS_SUCCESS( retVal ) )
{
break;
}
retVal = addEapolHeaders(packet, dstMac, srcMac, ANI_EAPOL_TYPE_KEY);
if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;
retVal = writeRsnKeyMic(cryptHandle,
packet,
(tAniEapolRsnKeyDesc *) keyDescData,
micKey, micKeyLen);
if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;
}
else {
VOS_ASSERT( 0 );
return ANI_E_ILLEGAL_ARG;
}
}while( 0 );
return retVal;
}
/**
* aniEapolParseKey
*
* Parses and verifies a complete EAPOL-Key frame. The key descriptor
* type is returned and so is a newly allocated key descriptor structure
* that is appropriate for the type.
*
* NOTE: This is a non-destructive read. That is, the packet headers
* will be unchanged at the end of this read operation. This is so
* that a followup MIC check may be done on the complete packet. If
* the packet parsing fails, the packet headers are not guaranteed to
* be unchanged.
*
* @param packet the packet to read from. Note that the frame is not
* expected to contain any additional padding at the end other than
* the exact number of key bytes. (The aniEapolParse function will
* ensure this.)
* @param descType is set to the key descriptor type
* (ANI_EAPOL_KEY_DESC_TYPE_LEGACY_RC4 or
* ANI_EAPOL_KEY_DESC_TYPE_RSN).
* @param keyDescData is set to a newly allocated key descriptor
* corresponding to the above descType. The signature field is
* verified. The key bytes will be returned encrypted. It is the
* responsibility of the caller to free this structure and the data
* contained therein.
*
* @return ANI_OK if the operation succeeds
*/
int
aniEapolParseKey(tAniPacket *packet,
int *descType,
void **keyDescData)
{
int retVal;
v_U8_t *bytes;
v_U32_t eapolFrameLen;
if (packet == NULL)
return ANI_E_NULL_VALUE;
do
{
eapolFrameLen = aniAsfPacketGetLen(packet);
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_INFO, "Supp parsing EAPOL-Key frame of len %d\n",
eapolFrameLen);
retVal = aniAsfPacketTruncateFromFront(packet, EAPOL_RX_HEADER_SIZE);
if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;
retVal = aniAsfPacketGetBytes(packet, &bytes);
if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;
if (*bytes == ANI_EAPOL_KEY_DESC_TYPE_RSN ||
*bytes == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW)
{
tAniEapolRsnKeyDesc *rsnDesc = NULL;
//*descType = ANI_EAPOL_KEY_DESC_TYPE_RSN;
*descType = (*bytes == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW ?
ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW : ANI_EAPOL_KEY_DESC_TYPE_RSN) ;
retVal = parseRsnKeyDesc(packet, &rsnDesc);
if( !ANI_IS_STATUS_SUCCESS(retVal) ) break;
*keyDescData = rsnDesc;
}
else
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Supp received unknown EAPOL-Key descriptor: %d\n",
*bytes);
retVal = ANI_E_ILLEGAL_ARG;
break;
}
aniAsfPacketMoveLeft(packet, eapolFrameLen);
}while( 0 );
return retVal;
}
static int
parseRsnKeyDesc(tAniPacket *packet,
tAniEapolRsnKeyDesc **rsnDescPtr)
{
int retVal = ANI_OK;
int len;
v_U8_t *bytes;
tAniEapolRsnKeyDesc *rsnDesc = NULL;
do
{
aniAsfPacketTruncateFromFront(packet, 1); // Desc-Type
rsnDesc = (tAniEapolRsnKeyDesc *)
vos_mem_malloc( sizeof(tAniEapolRsnKeyDesc) );
if (rsnDesc == NULL)
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Supp could not malloc EAPOL-Key Descriptor for RSN\n");
retVal = ANI_E_MALLOC_FAILED;
break;
}
retVal = parseRsnKeyInfo(packet, &rsnDesc->info);
if (retVal != ANI_OK) break;
retVal = aniAsfPacketGet16(packet, &rsnDesc->keyLen);
if (retVal != ANI_OK)
{
break;
}
len = sizeof(rsnDesc->replayCounter);
retVal = aniAsfPacketGetN(packet, len, &bytes);
if (retVal != ANI_OK)
{
break;
}
vos_mem_copy(rsnDesc->replayCounter, bytes, len);
len = sizeof(rsnDesc->keyNonce);
retVal = aniAsfPacketGetN(packet, len, &bytes);
if (retVal != ANI_OK)
{
break;
}
vos_mem_copy(rsnDesc->keyNonce, bytes, len);
len = sizeof(rsnDesc->keyIv);
retVal = aniAsfPacketGetN(packet, len, &bytes);
if (retVal != ANI_OK)
{
break;
}
vos_mem_copy(rsnDesc->keyIv, bytes, len);
len = sizeof(rsnDesc->keyRecvSeqCounter);
retVal = aniAsfPacketGetN(packet, len, &bytes);
if (retVal != ANI_OK)
{
break;
}
vos_mem_copy(rsnDesc->keyRecvSeqCounter, bytes, len);
len = sizeof(rsnDesc->keyId);
retVal = aniAsfPacketGetN(packet, len, &bytes);
if (retVal != ANI_OK)
{
break;
}
vos_mem_copy(rsnDesc->keyId, bytes, len);
len = sizeof(rsnDesc->keyMic);
retVal = aniAsfPacketGetN(packet, len, &bytes);
if (retVal != ANI_OK)
{
break;
}
vos_mem_copy(rsnDesc->keyMic, bytes, len);
retVal = aniAsfPacketGet16(packet, &rsnDesc->keyDataLen);
if (retVal != ANI_OK)
{
break;
}
len = rsnDesc->keyDataLen;
if (len > 0) {
// We have a key
retVal = aniAsfPacketGetN(packet, len, &bytes);
if (retVal != ANI_OK)
{
break;
}
rsnDesc->keyData = (v_U8_t*)vos_mem_malloc(len);
if (rsnDesc->keyData == NULL)
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR, "Could not allocate RSN key bytes!\n");
VOS_ASSERT( 0 );
retVal = ANI_E_MALLOC_FAILED;
break;
}
vos_mem_copy(rsnDesc->keyData, bytes, len);
}
else {
rsnDesc->keyData = NULL;
}
*rsnDescPtr = rsnDesc;
}while( 0 );
if( !ANI_IS_STATUS_SUCCESS( retVal ) )
{
vos_mem_free(rsnDesc);
}
return retVal;
}
static int
parseRsnKeyInfo(tAniPacket *packet,
tAniRsnKeyInfo *info)
{
v_U16_t tmp;
int retVal;
retVal = aniAsfPacketGet16(packet, &tmp);
if( !ANI_IS_STATUS_SUCCESS( retVal ) )
{
return retVal;
}
info->keyDescVers = (tmp & ANI_SSM_RSN_KEY_DESC_VERS_MASK)
>> ANI_SSM_RSN_KEY_DESC_VERS_OFFSET;
if (info->keyDescVers != ANI_EAPOL_KEY_DESC_VERS_RC4 &&
info->keyDescVers != ANI_EAPOL_KEY_DESC_VERS_AES)
return ANI_E_ILLEGAL_ARG;
info->unicastFlag = (tmp & ANI_SSM_RSN_UNICAST_MASK) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
info->keyId = (tmp & ANI_SSM_RSN_KEY_INDEX_MASK)
>> ANI_SSM_RSN_KEY_INDEX_OFFSET;
info->installFlag = (tmp & ANI_SSM_RSN_INSTALL_MASK) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
info->ackFlag = (tmp & ANI_SSM_RSN_ACK_MASK) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
info->micFlag = (tmp & ANI_SSM_RSN_MIC_MASK) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
info->secureFlag = (tmp & ANI_SSM_RSN_SECURE_MASK) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
info->errorFlag = (tmp & ANI_SSM_RSN_ERROR_MASK) ?
eANI_BOOLEAN_TRUE: eANI_BOOLEAN_FALSE;
info->requestFlag = (tmp & ANI_SSM_RSN_REQUEST_MASK) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
info->encKeyDataFlag = (tmp & ANI_SSM_RSN_ENC_KEY_DATA_MASK) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE;
return ANI_OK;
}
static int
writeRsnKeyDesc(tAniPacket *packet,
tAniEapolRsnKeyDesc *rsnDesc,
v_U8_t keyDescType)
{
int retVal;
do
{
// This can be either ANI_EAPOL_KEY_DESC_TYPE_RSN
// or ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW
retVal = aniAsfPacketAppend8(packet, keyDescType);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = writeRsnKeyInfo(packet, &rsnDesc->info);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketAppend16(packet, rsnDesc->keyLen);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketAppendBuffer(packet,
rsnDesc->replayCounter,
sizeof(rsnDesc->replayCounter));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketAppendBuffer(packet,
rsnDesc->keyNonce,
sizeof(rsnDesc->keyNonce));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketAppendBuffer(packet,
rsnDesc->keyIv,
sizeof(rsnDesc->keyIv));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketAppendBuffer(packet,
rsnDesc->keyRecvSeqCounter,
sizeof(rsnDesc->keyRecvSeqCounter));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketAppendBuffer(packet,
rsnDesc->keyId,
sizeof(rsnDesc->keyId));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
// Zero out the key MIC
retVal = aniAsfPacketAppendBuffer(packet,
ZERO_BYTES,
sizeof(rsnDesc->keyMic));
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
retVal = aniAsfPacketAppend16(packet, rsnDesc->keyDataLen);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
if (rsnDesc->keyDataLen != 0)
{
retVal = aniAsfPacketAppendBuffer(packet,
rsnDesc->keyData,
rsnDesc->keyDataLen);
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
}
}while( 0 );
return retVal;
}
static int
writeRsnKeyInfo(tAniPacket *packet, tAniRsnKeyInfo *info)
{
int retVal;
v_U16_t tmp;
v_U16_t infoValue;
infoValue = 0;
tmp = (v_U16_t)info->keyDescVers;
tmp = tmp << ANI_SSM_RSN_KEY_DESC_VERS_OFFSET;
infoValue |= (tmp & ANI_SSM_RSN_KEY_DESC_VERS_MASK);
if (info->unicastFlag)
infoValue |= ANI_SSM_RSN_UNICAST_MASK;
tmp = info->keyId;
tmp = tmp << ANI_SSM_RSN_KEY_INDEX_OFFSET;
infoValue |= (tmp & ANI_SSM_RSN_KEY_INDEX_MASK);
if (info->installFlag)
infoValue |= ANI_SSM_RSN_INSTALL_MASK;
if (info->ackFlag)
infoValue |= ANI_SSM_RSN_ACK_MASK;
if (info->micFlag)
infoValue |= ANI_SSM_RSN_MIC_MASK;
if (info->secureFlag)
infoValue |= ANI_SSM_RSN_SECURE_MASK;
if (info->errorFlag)
infoValue |= ANI_SSM_RSN_ERROR_MASK;
if (info->requestFlag)
infoValue |= ANI_SSM_RSN_REQUEST_MASK;
if (info->encKeyDataFlag)
infoValue |= ANI_SSM_RSN_ENC_KEY_DATA_MASK;
retVal = aniAsfPacketAppend16(packet, infoValue);
return retVal;
}
static int
writeRsnKeyMic(v_U32_t cryptHandle,
tAniPacket *eapolFrame,
tAniEapolRsnKeyDesc *rsnDesc,
v_U8_t *micKey,
v_U32_t micKeyLen)
{
int retVal = ANI_OK;
int len;
v_U8_t *ptr = NULL;
v_U8_t *micPos = NULL;
v_U8_t result[VOS_DIGEST_SHA1_SIZE]; // Larger of the two
// Sanity check the arguments and return if no MIC generation is
// needed
if (micKey != NULL)
{
if (micKeyLen == 0 || !rsnDesc->info.micFlag)
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Supp MIC key provided but micKeyLen or micFlag is not set!\n");
VOS_ASSERT( 0 );
return ANI_E_ILLEGAL_ARG;
}
}
else {
if (rsnDesc->info.micFlag)
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Supp micFlag is set but MIC key not provided!\n");
VOS_ASSERT( 0 );
return ANI_E_ILLEGAL_ARG;
}
// Normal condition where MIC is not desired by the caller
return ANI_OK;
}
len = aniAsfPacketGetBytes(eapolFrame, &ptr);
if( !ANI_IS_STATUS_SUCCESS( len ) )
{
return len;
}
micPos = ptr + ANI_SSM_RSN_KEY_MIC_OFFSET + SNAP_HEADER_SIZE;
// Clear the MIC field in the packet before the MIC computation
vos_mem_zero( micPos, VOS_DIGEST_MD5_SIZE);
// Skip to the EAPOL version field for MIC computation
ptr += EAPOL_VERSION_POS + SNAP_HEADER_SIZE;
len -= (EAPOL_VERSION_POS + SNAP_HEADER_SIZE);
if (rsnDesc->info.keyDescVers == ANI_EAPOL_KEY_DESC_VERS_AES)
{
if( VOS_IS_STATUS_SUCCESS( vos_sha1_hmac_str(cryptHandle, ptr, len, micKey, micKeyLen, result) ) )
{
retVal = ANI_OK;
}
else
{
retVal = ANI_ERROR;
}
}
else {
VOS_ASSERT( 0 );
retVal = ANI_E_ILLEGAL_ARG;
}
if (retVal == ANI_OK)
{
// Copy only 16B which is the smaller of the two and the same as
// ANI_EAPOL_KEY_RSN_MIC_SIZE
vos_mem_copy(micPos, result, VOS_DIGEST_MD5_SIZE);
}
return retVal;
}
/**
* aniEapolKeyCheckMic
*
* @param eapolFrame the complete EAPOL-Key packet
* @param descType the key descriptor type
* @param keyDescData the key descriptor
* @param micKey the MIC key
* @param micKeyLen the number of bytes in the MIC key
*
* @return ANI_OK if the operation succeeds; ANI_E_MIC_FAILED if the
* MIC check fails.
*/
int
aniEapolKeyCheckMic(v_U32_t cryptHandle,
tAniPacket *eapolFrame,
int descType,
void *keyDescData,
v_U8_t *micKey,
v_U32_t micKeyLen)
{
if (descType == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW)
{
return checkRsnKeyMic(cryptHandle, eapolFrame, keyDescData, micKey, micKeyLen);
}
else {
VOS_ASSERT( 0 );
return ANI_E_ILLEGAL_ARG;
}
}
static int
checkRsnKeyMic(v_U32_t cryptHandle,
tAniPacket *eapolFrame,
tAniEapolRsnKeyDesc *rsnDesc,
v_U8_t *micKey,
v_U32_t micKeyLen)
{
int retVal = ANI_ERROR;
int len;
v_U8_t *ptr = NULL;
v_U8_t *micPos = NULL;
v_U8_t result[VOS_DIGEST_SHA1_SIZE]; // Larger of the two
v_U8_t incomingMic[ANI_EAPOL_KEY_RSN_MIC_SIZE];
if (!rsnDesc->info.micFlag)
{
VOS_ASSERT( 0 );
return ANI_E_ILLEGAL_ARG;
}
len = aniAsfPacketGetBytes(eapolFrame, &ptr);
if( ANI_IS_STATUS_SUCCESS( len ) )
{
micPos = ptr + ANI_SSM_RSN_KEY_MIC_OFFSET;
// Skip to the EAPOL version field for MIC computation
ptr += EAPOL_VERSION_POS;
len -= EAPOL_VERSION_POS;
// Copy existing MIC to temporary location and zero it out
vos_mem_copy( incomingMic, micPos, ANI_EAPOL_KEY_RSN_MIC_SIZE );
vos_mem_zero( micPos, ANI_EAPOL_KEY_RSN_MIC_SIZE );
if (rsnDesc->info.keyDescVers == ANI_EAPOL_KEY_DESC_VERS_AES)
{
if( VOS_IS_STATUS_SUCCESS( vos_sha1_hmac_str(cryptHandle, ptr, len, micKey, micKeyLen, result) ) )
{
retVal = ANI_OK;
}
}
else {
VOS_ASSERT( 0 );
retVal = ANI_E_ILLEGAL_ARG;
}
if (retVal == ANI_OK)
{
if ( !vos_mem_compare(incomingMic, result, ANI_EAPOL_KEY_RSN_MIC_SIZE))
{
retVal = ANI_E_MIC_FAILED;
}
}
}
return retVal;
}
/**
* aniEapolKeyFreeDesc
*
* Frees the EAPOL key descriptor and the key bytes contained within it.
*
* @param descType the key descriptor type
* @param keyDescData the key descriptor
*
* @return ANI_OK if the operation succeeds
*/
int
aniEapolKeyFreeDesc(int descType, void *keyDescData)
{
tAniEapolRsnKeyDesc *rsnDesc;
if( keyDescData )
{
if ((descType == ANI_EAPOL_KEY_DESC_TYPE_RSN_NEW)
|| (descType == ANI_EAPOL_KEY_DESC_TYPE_RSN))
{
rsnDesc = (tAniEapolRsnKeyDesc *) keyDescData;
if (rsnDesc->keyData != NULL)
vos_mem_free(rsnDesc->keyData);
}
else {
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"Supp asked to free illegal type: %d\n", descType);
}
vos_mem_free(keyDescData);
}
return ANI_OK;
}
v_U8_t *
aniEapolType2Str(v_U8_t type)
{
switch (type)
{
case ANI_EAPOL_TYPE_PACKET:
return (v_U8_t *)ANI_EAPOL_TYPE_PACKET_STR;
break;
case ANI_EAPOL_TYPE_START:
return (v_U8_t *)ANI_EAPOL_TYPE_START_STR;
break;
case ANI_EAPOL_TYPE_LOGOFF:
return (v_U8_t *)ANI_EAPOL_TYPE_LOGOFF_STR;
break;
case ANI_EAPOL_TYPE_KEY:
return (v_U8_t *)ANI_EAPOL_TYPE_KEY_STR;
break;
case ANI_EAPOL_TYPE_ASF_ALERT:
return (v_U8_t *)ANI_EAPOL_TYPE_ASF_ALERT_STR;
break;
default:
return (v_U8_t *)ANI_EAPOL_TYPE_UNKNOWN_STR;
break;
}
}
void bapRsnEapolHandler( v_PVOID_t pvFsm, tAniPacket *packet, v_BOOL_t fIsAuth )
{
int retVal;
v_U8_t *dstMac = NULL;
v_U8_t *srcMac = NULL;
v_U8_t *type = NULL;
retVal = aniEapolParse(packet, &dstMac, &srcMac, &type);
if ( retVal >= 0 )
{
retVal = ANI_OK;
// Sanity check that a PAE role has been assigned to it,
// and then dispatch to the appropriate handler
if( fIsAuth )
{
tAuthRsnFsm *fsm = (tAuthRsnFsm *)pvFsm;
authEapolHandler( fsm, packet, dstMac, srcMac, type );
}
else
{
tSuppRsnFsm *fsm = (tSuppRsnFsm *)pvFsm;
suppEapolHandler(fsm, packet, dstMac, srcMac, type);
} // switch statement
} // Successfully parsed EAPOL
else
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
"eapolHandler Received bad EAPOL message of len %d (status=%d)\n",
aniAsfPacketGetLen(packet), retVal );
}
aniAsfPacketFree( packet );
}
int bapRsnFormPktFromVosPkt( tAniPacket **ppPacket, vos_pkt_t *pVosPacket )
{
int retVal = ANI_ERROR;
VOS_STATUS status;
v_U16_t uPktLen;
#define BAP_RSN_SNAP_TYPE_OFFSET 20
#define BAP_RSN_ETHERNET_3_HEADER_LEN 22
v_U8_t *pFrame;
tAniPacket *pAniPacket = NULL;
do
{
status = vos_pkt_get_packet_length( pVosPacket, &uPktLen );
if( !VOS_IS_STATUS_SUCCESS(status) ) break;
if( (uPktLen < BAP_RSN_ETHERNET_3_HEADER_LEN) )
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
" authRsnRxFrameHandler receive eapol packet size (%d) too small (%d)\n",
uPktLen, BAP_RSN_ETHERNET_3_HEADER_LEN );
break;
}
status = vos_pkt_peek_data( pVosPacket, 0, (v_VOID_t *)&pFrame, uPktLen );
if( !VOS_IS_STATUS_SUCCESS(status) ) break;
retVal = aniAsfPacketAllocateExplicit(&pAniPacket, uPktLen, 0 );
if( !ANI_IS_STATUS_SUCCESS( retVal ) )
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
" authRsnRxFrameHandler failed to get buffer size (%d) \n",
uPktLen );
break;
}
aniAsfPacketEmptyExplicit( pAniPacket, 0 );
pFrame[ETHER_PROTO_POS] = pFrame[BAP_RSN_SNAP_TYPE_OFFSET];
pFrame[ETHER_PROTO_POS + 1] = pFrame[BAP_RSN_SNAP_TYPE_OFFSET + 1];
//push ethernet II header in
retVal = aniAsfPacketAppendBuffer( pAniPacket, pFrame, ETHER_PROTO_POS + 2 );
if( !ANI_IS_STATUS_SUCCESS( retVal ) ) break;
//Get the rest of the data in
uPktLen -= BAP_RSN_ETHERNET_3_HEADER_LEN;
VOS_ASSERT( uPktLen > 0 );
retVal = aniAsfPacketAppendBuffer( pAniPacket, pFrame + BAP_RSN_ETHERNET_3_HEADER_LEN,
uPktLen );
if( !ANI_IS_STATUS_SUCCESS( retVal ) )
{
VOS_TRACE( VOS_MODULE_ID_BAP, VOS_TRACE_LEVEL_ERROR,
" authRsnRxFrameHandler cannot retrieve eapol payload size (%d)\n",
uPktLen );
break;
}
}while( 0 );
if( ANI_IS_STATUS_SUCCESS( retVal ) )
{
*ppPacket = pAniPacket;
}
else if( pAniPacket )
{
aniAsfPacketFree( pAniPacket );
}
return retVal;
}