Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
When an EAP extension succesfully completes authentication, it can fill in the PPP_EAP_OUTPUT pUserAttributes field with MPPE key information for use by other networking components. For example, RAS can use those keys for data encryption, and WPA/WPA2 will use them for the 4-way handshake on an 802.11 link.
The PPP_EAP_OUTPUT pUserAttribute field points to an array of RAS_AUTH_ATTRIBUTE structures, terminated by an array element with raaType = raatMinimum. Documentation on the format of the RAS_AUTH_ATTRIBUTE structure can be found at https://msdn2.microsoft.com/en-gb/library/aa363535.aspx.
For MPPE Keys, there will be 2 elements in the array of RAS_AUTH_ATTRIBUTES: one for the send key and one for the receive key. For the MPPE Key attributes, the PVOID Value field of the RAS_AUTH_ATTRIBUTE will point to a structure that is described in https://msdn2.microsoft.com/en-us/library/aa363636.aspx. See the section labeled "eatVendorSpecific".
Below are some sample utility functions that can assist in the creation of the data structures that contain these keys.
struct RAS_AUTH_ATTRIBUTE_VALUE
{
BYTE VendorId[4]; // network byte order
BYTE VendorType;
BYTE Length; // number of bytes from VendorType to the end of the value
BYTE VendorValue[1]; // Length-2 bytes long
};
DWORD
MyEapUtilAuthAttributeInsertVSA(
OUT RAS_AUTH_ATTRIBUTE * pAttribute,
IN DWORD VendorId,
IN BYTE VendorType,
IN PBYTE pVendorValue,
IN BYTE cbVendorValue)
//
// Insert a vendor specific attribute into the location pointed to by pAttribute.
//
{
DWORD dwResult = NO_ERROR;
ASSERT(cbVendorValue < 254);
pAttribute->raaType = raatVendorSpecific;
pAttribute->dwLength = offsetof(struct RAS_AUTH_ATTRIBUTE_VALUE, VendorValue) + cbVendorValue;
pAttribute->Value = LocalAlloc(LPTR, pAttribute->dwLength);
if (pAttribute->Value == NULL)
{
dwResult = ERROR_OUTOFMEMORY;
}
else
{
struct RAS_AUTH_ATTRIBUTE_VALUE *pAttributeValue = (struct RAS_AUTH_ATTRIBUTE_VALUE *)pAttribute->Value;
VendorId = htonl(VendorId);
memcpy(&pAttributeValue->VendorId[0], &VendorId, sizeof(VendorId));
pAttributeValue->VendorType = VendorType;
pAttributeValue->Length = 2 + cbVendorValue; // 1 byte for VendorType, 1 byte for Length, then cbVendorValue bytes of VendorValue
memcpy(&pAttributeValue->VendorValue[0], pVendorValue, cbVendorValue );
}
return dwResult;
}
RAS_AUTH_ATTRIBUTE *
MyEapUtilAuthAttributeArrayAlloc(
DWORD nAttrs)
//
// Allocate an array of sufficient size to hold the requested number
// of authentication attributes.
//
{
RAS_AUTH_ATTRIBUTE *pAttributes;
nAttrs += 1; // Add one for the terminator
pAttributes = (RAS_AUTH_ATTRIBUTE *)(LocalAlloc(LPTR, nAttrs * sizeof(RAS_AUTH_ATTRIBUTE)));
if (pAttributes)
{
// Set the terminator array element
pAttributes[nAttrs - 1].raaType = raatMinimum;
}
return pAttributes;
}
void
MyEapUtilAuthAttributeArrayFree(
RAS_AUTH_ATTRIBUTE *pAttributes)
//
// Free an array of authentication attribute values.
//
{
if (pAttributes)
{
for (int i=0; pAttributes[i].raaType != raatMinimum; i++)
{
if (pAttributes[i].Value)
{
SecureZeroMemory(pAttributes[i].Value, pAttributes[i].dwLength);
LocalFree(pAttributes[i].Value);
pAttributes[i].Value = NULL;
}
}
LocalFree(pAttributes);
}
}
#define VENDOR_MICROSOFT 311
#define MS_VSA_MPPE_Send_Key 16
#define MS_VSA_MPPE_Recv_Key 17
#define MAX_MPPEKEY_LENGTH 32
// Format of the MPPE Key
// Byte 1-2: Salt
// Byte 3: Key Length (this is the length of the Actual Key and does not include Salt, Key Length and Padding fields)
// Byte 4-35: Actual Key (Most access points require 32 byte keys for WPA)
// Byte 35-50: Padding (required to make the length of the MPPE Key (not including the Salt) a multiple of 16 bytes)
struct MPPEKey
{
BYTE Salt[2];
BYTE KeyLength;
BYTE Key[MAX_MPPEKEY_LENGTH];
BYTE Padding[15];
};
DWORD
MyEapUtilAuthAttributeInsertMPPEKeyVSA(
OUT RAS_AUTH_ATTRIBUTE * pAttribute,
IN BYTE VendorType, // MS_VSA_MPPE_Send_Key or MS_VSA_MPPE_Recv_Key
IN BYTE const * const pKey,
IN size_t cbKey)
//
// Insert an MPPEKey VSA into the location specified by pAttribute.
//
{
DWORD dwResult;
struct MPPEKey MPPEKey;
if (cbKey > MAX_MPPEKEY_LENGTH)
return ERROR_INVALID_PARAMETER;
memset(&MPPEKey, 0, sizeof(MPPEKey));
MPPEKey.KeyLength = cbKey;
memcpy(&MPPEKey.Key[0], pKey, cbKey);
dwResult = MyEapUtilAuthAttributeInsertVSA(pAttribute, VENDOR_MICROSOFT, VendorType, (PBYTE)&MPPEKey, sizeof(MPPEKey) - MAX_MPPEKEY_LENGTH + cbKey);
SecureZeroMemory(&MPPEKey, sizeof(MPPEKey));
return dwResult;
}
DWORD
MyEapUtilCreateMPPEAuthAttributes(
BYTE const * const pSendKey,
size_t cbSendKey,
BYTE const * const pRecvKey,
size_t cbRecvKey,
RAS_AUTH_ATTRIBUTE **ppSendRecvKeyAttr)
//
// Save the MPPE Send and Recv Session Keys as Auth Attributes
//
{
DWORD dwResult;
RAS_AUTH_ATTRIBUTE* pSendRecvKeyAttr;
// Allocate an auth attribute array able to hold 2 attributes (send key and recv key)
pSendRecvKeyAttr = MyEapUtilAuthAttributeArrayAlloc(2);
if ( NULL == pSendRecvKeyAttr )
{
dwResult = ERROR_OUTOFMEMORY;
goto done;
}
// Add the send key to the attribute array at index 0
dwResult = MyEapUtilAuthAttributeInsertMPPEKeyVSA(&pSendRecvKeyAttr[0], MS_VSA_MPPE_Send_Key, pSendKey, cbSendKey);
if (dwResult != NO_ERROR)
goto done;
// Add the receive key to the attribute array at index 1
dwResult = MyEapUtilAuthAttributeInsertMPPEKeyVSA(&pSendRecvKeyAttr[1], MS_VSA_MPPE_Recv_Key, pRecvKey, cbRecvKey);
if (dwResult != NO_ERROR)
goto done;
done:
if (dwResult != NO_ERROR)
{
// Free any resources
MyEapUtilAuthAttributeArrayFree(pSendRecvKeyAttr);
pSendRecvKeyAttr = NULL;
}
*ppSendRecvKeyAttr = pSendRecvKeyAttr;
return dwResult;
}