407 lines
9.9 KiB
C++
407 lines
9.9 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 1998, Microsoft Corp. All rights reserved.
|
|
//
|
|
// FILE
|
|
//
|
|
// blob.cpp
|
|
//
|
|
// SYNOPSIS
|
|
//
|
|
// Defines the various 'blob' classes.
|
|
//
|
|
// MODIFICATION HISTORY
|
|
//
|
|
// 08/24/1998 Original version.
|
|
// 10/25/1998 New symbolic constants for ARAP.
|
|
// 11/10/1998 Added isLmPresent().
|
|
// 01/04/1999 MSChapError::insert takes a PPP error code.
|
|
// 01/25/1999 MS-CHAP v2
|
|
// 05/04/1999 New reason codes.
|
|
// 05/11/1999 Fix RADIUS encryption.
|
|
// 05/28/1999 Fix MS-MPPE-Keys format.
|
|
// 02/17/2000 Key encryption is now handled by the protocol.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <ias.h>
|
|
#include <sdoias.h>
|
|
#include <align.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstdio>
|
|
|
|
#include <samutil.h>
|
|
#include <blob.h>
|
|
|
|
bool MSChapResponse::isLmPresent() const throw ()
|
|
{
|
|
const BYTE* p = get().lmResponse + _LM_RESPONSE_LENGTH;
|
|
|
|
do { } while (--p >= get().lmResponse && *p == 0);
|
|
|
|
return p >= get().lmResponse;
|
|
}
|
|
|
|
bool MSChapCPW2::isLmPresent() const throw ()
|
|
{
|
|
// Do we have either an LM response or an LM hash.
|
|
if ((get().flags[1] & 0x3) != 0x1) { return true; }
|
|
|
|
// Now make sure the LM fields are zeroed out.
|
|
const BYTE* p = get().oldLmHash +
|
|
_ENCRYPTED_LM_OWF_PASSWORD_LENGTH + _LM_RESPONSE_LENGTH;
|
|
|
|
do { } while (--p >= get().oldLmHash && *p == 0);
|
|
|
|
return p >= get().oldLmHash;
|
|
}
|
|
|
|
//////////
|
|
// Retrieves and assembles an MS-CHAP encrypted password. Returns 'true' if
|
|
// the password is present, false otherwise.
|
|
//////////
|
|
BOOL MSChapEncPW::getEncryptedPassword(
|
|
IASRequest& request,
|
|
DWORD dwId,
|
|
PBYTE buf
|
|
)
|
|
{
|
|
//////////
|
|
// Are there any attribute with the desired ID ?
|
|
//////////
|
|
|
|
IASAttributeVectorWithBuffer<8> attrs;
|
|
if (attrs.load(request, dwId) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//////////
|
|
// Allocate space on the stack for the blobs.
|
|
//////////
|
|
|
|
MSChapEncPW* begin = IAS_STACK_NEW(MSChapEncPW, attrs.size());
|
|
MSChapEncPW* end = begin;
|
|
|
|
//////////
|
|
// Convert the attributes to password chunks and determine the total length.
|
|
//////////
|
|
|
|
DWORD length = 0;
|
|
IASAttributeVector::iterator i;
|
|
for (i = attrs.begin(); i != attrs.end(); ++i, ++end)
|
|
{
|
|
*end = blob_cast<MSChapEncPW>(i->pAttribute);
|
|
|
|
length += end->getStringLength();
|
|
}
|
|
|
|
//////////
|
|
// Do we have the right length ?
|
|
//////////
|
|
|
|
if (length != _SAMPR_ENCRYPTED_USER_PASSWORD_LENGTH)
|
|
{
|
|
_com_issue_error(IAS_MALFORMED_REQUEST);
|
|
}
|
|
|
|
//////////
|
|
// Sort the chunks ...
|
|
//////////
|
|
|
|
std::sort(begin, end);
|
|
|
|
//////////
|
|
// ... then concatenate the strings into the buffer.
|
|
//////////
|
|
|
|
for ( ; begin != end; ++begin)
|
|
{
|
|
memcpy(buf, begin->get().string, begin->getStringLength());
|
|
|
|
buf += begin->getStringLength();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MSChapDomain::insert(
|
|
IASRequest& request,
|
|
BYTE ident,
|
|
PCWSTR domain
|
|
)
|
|
{
|
|
//////////
|
|
// Allocate an attribute.
|
|
//////////
|
|
|
|
IASAttribute attr(true);
|
|
|
|
//////////
|
|
// Allocate memory for the blob.
|
|
//////////
|
|
|
|
int len = WideCharToMultiByte(CP_ACP, 0, domain, -1, 0, 0, 0, 0);
|
|
Layout* val = (Layout*)CoTaskMemAlloc(len + sizeof(Layout));
|
|
if (val == NULL) { _com_issue_error(E_OUTOFMEMORY); }
|
|
|
|
//////////
|
|
// Initialize the blob.
|
|
//////////
|
|
|
|
val->ident = ident;
|
|
WideCharToMultiByte(CP_ACP, 0, domain, -1, (PSTR)val->string, len, 0, 0);
|
|
|
|
//////////
|
|
// Initialize the attribute and store.
|
|
//////////
|
|
|
|
attr->dwId = MS_ATTRIBUTE_CHAP_DOMAIN;
|
|
attr->Value.itType = IASTYPE_OCTET_STRING;
|
|
attr->Value.OctetString.lpValue = (PBYTE)val;
|
|
attr->Value.OctetString.dwLength = len + sizeof(Layout) - 1;
|
|
attr->dwFlags = IAS_INCLUDE_IN_ACCEPT;
|
|
|
|
attr.store(request);
|
|
}
|
|
|
|
void MSChapError::insert(
|
|
IASRequest& request,
|
|
BYTE ident,
|
|
DWORD errorCode
|
|
)
|
|
{
|
|
//////////
|
|
// Allocate an attribute.
|
|
//////////
|
|
|
|
IASAttribute attr(true);
|
|
|
|
//////////
|
|
// Format the error message.
|
|
//////////
|
|
|
|
CHAR buffer[32];
|
|
sprintf(buffer, "E=%lu R=0 V=3", errorCode);
|
|
|
|
//////////
|
|
// Allocate memory for the blob.
|
|
//////////
|
|
|
|
ULONG len = strlen(buffer);
|
|
Layout* val = (Layout*)CoTaskMemAlloc(len + sizeof(Layout));
|
|
if (val == NULL) { _com_issue_error(E_OUTOFMEMORY); }
|
|
|
|
//////////
|
|
// Initialize the blob.
|
|
//////////
|
|
|
|
val->ident = ident;
|
|
memcpy(val->string, buffer, len);
|
|
|
|
//////////
|
|
// Initialize the attribute and store.
|
|
//////////
|
|
|
|
attr->dwId = MS_ATTRIBUTE_CHAP_ERROR;
|
|
attr->Value.itType = IASTYPE_OCTET_STRING;
|
|
attr->Value.OctetString.lpValue = (PBYTE)val;
|
|
attr->Value.OctetString.dwLength = len + sizeof(Layout);
|
|
attr->dwFlags = IAS_INCLUDE_IN_REJECT;
|
|
|
|
attr.store(request);
|
|
}
|
|
|
|
void MSChapMPPEKeys::insert(
|
|
IASRequest& request,
|
|
PBYTE lmKey,
|
|
PBYTE ntKey,
|
|
PBYTE challenge
|
|
)
|
|
{
|
|
//////////
|
|
// Allocate an attribute.
|
|
//////////
|
|
|
|
IASAttribute attr(true);
|
|
|
|
//////////
|
|
// Allocate memory for the value.
|
|
//////////
|
|
|
|
Layout* val = (Layout*)CoTaskMemAlloc(sizeof(Layout));
|
|
if (val == NULL) { _com_issue_error(E_OUTOFMEMORY); }
|
|
|
|
//////////
|
|
// Initialize the blob.
|
|
//////////
|
|
|
|
memcpy(val->lmKey, lmKey, sizeof(val->lmKey));
|
|
memcpy(val->ntKey, ntKey, sizeof(val->ntKey));
|
|
memcpy(val->challenge, challenge, sizeof(val->challenge));
|
|
|
|
//////////
|
|
// Initialize the attribute and store.
|
|
//////////
|
|
|
|
attr->dwId = MS_ATTRIBUTE_CHAP_MPPE_KEYS;
|
|
attr->Value.itType = IASTYPE_OCTET_STRING;
|
|
attr->Value.OctetString.lpValue = (PBYTE)val;
|
|
attr->Value.OctetString.dwLength = sizeof(Layout);
|
|
attr->dwFlags = IAS_INCLUDE_IN_ACCEPT;
|
|
|
|
attr.store(request);
|
|
}
|
|
|
|
// Convert a number to a hex representation.
|
|
inline BYTE num2Digit(BYTE num) throw ()
|
|
{
|
|
return (num < 10) ? num + '0' : num + ('A' - 10);
|
|
}
|
|
|
|
void MSChap2Success::insert(
|
|
IASRequest& request,
|
|
BYTE ident,
|
|
PBYTE authenticatorResponse
|
|
)
|
|
{
|
|
//////////
|
|
// Allocate an attribute.
|
|
//////////
|
|
|
|
IASAttribute attr(true);
|
|
|
|
//////////
|
|
// Allocate memory for the value.
|
|
//////////
|
|
|
|
Layout* val = (Layout*)CoTaskMemAlloc(sizeof(Layout));
|
|
if (val == NULL) { _com_issue_error(E_OUTOFMEMORY); }
|
|
|
|
//////////
|
|
// Initialize the blob.
|
|
//////////
|
|
|
|
val->ident = ident;
|
|
PBYTE p = val->string;
|
|
*p++ = 'S';
|
|
*p++ = '=';
|
|
for (size_t i = 0; i < 20; ++i)
|
|
{
|
|
*p++ = num2Digit(authenticatorResponse[i] >> 4);
|
|
*p++ = num2Digit(authenticatorResponse[i] & 0xF);
|
|
}
|
|
|
|
//////////
|
|
// Initialize the attribute and store.
|
|
//////////
|
|
|
|
attr->dwId = MS_ATTRIBUTE_CHAP2_SUCCESS;
|
|
attr->Value.itType = IASTYPE_OCTET_STRING;
|
|
attr->Value.OctetString.lpValue = (PBYTE)val;
|
|
attr->Value.OctetString.dwLength = sizeof(Layout);
|
|
attr->dwFlags = IAS_INCLUDE_IN_ACCEPT;
|
|
|
|
attr.store(request);
|
|
}
|
|
|
|
void MSMPPEKey::insert(
|
|
IASRequest& request,
|
|
ULONG keyLength,
|
|
PBYTE key,
|
|
BOOL isSendKey
|
|
)
|
|
{
|
|
//////////
|
|
// Allocate an attribute.
|
|
//////////
|
|
|
|
IASAttribute attr(true);
|
|
|
|
//////////
|
|
// Allocate memory for the value.
|
|
//////////
|
|
|
|
ULONG nbyte = ROUND_UP_COUNT(keyLength + 1, 16) + 2;
|
|
Layout* val = (Layout*)CoTaskMemAlloc(nbyte);
|
|
if (val == NULL) { _com_issue_error(E_OUTOFMEMORY); }
|
|
memset(val, 0, nbyte);
|
|
|
|
//////////
|
|
// Initialize the blob.
|
|
//////////
|
|
|
|
val->keyLength = (BYTE)keyLength;
|
|
memcpy(val->key, key, keyLength);
|
|
|
|
//////////
|
|
// Initialize the attribute, encrypt, and store.
|
|
//////////
|
|
|
|
attr->dwId = isSendKey ? MS_ATTRIBUTE_MPPE_SEND_KEY
|
|
: MS_ATTRIBUTE_MPPE_RECV_KEY;
|
|
attr->Value.itType = IASTYPE_OCTET_STRING;
|
|
attr->Value.OctetString.lpValue = (PBYTE)val;
|
|
attr->Value.OctetString.dwLength = nbyte;
|
|
attr->dwFlags = IAS_INCLUDE_IN_ACCEPT;
|
|
|
|
attr.store(request);
|
|
}
|
|
|
|
void ArapChallengeResponse::insert(
|
|
IASRequest& request,
|
|
DWORD NTResponse1,
|
|
DWORD NTResponse2
|
|
)
|
|
{
|
|
// Allocate an attribute.
|
|
IASAttribute attr(true);
|
|
|
|
// Pack the fields. These are already in network order.
|
|
Layout value;
|
|
memcpy(value.ntResponse1, &NTResponse1, 4);
|
|
memcpy(value.ntResponse2, &NTResponse2, 4);
|
|
|
|
// Store the value.
|
|
attr.setOctetString(sizeof(value), (const BYTE*)&value);
|
|
|
|
// Initialize the remaining fields.
|
|
attr->dwId = RADIUS_ATTRIBUTE_ARAP_CHALLENGE_RESPONSE;
|
|
attr->dwFlags = IAS_INCLUDE_IN_ACCEPT;
|
|
|
|
// Insert the attribute into the request.
|
|
attr.store(request);
|
|
}
|
|
|
|
void ArapFeatures::insert(
|
|
IASRequest& request,
|
|
DWORD PwdCreationDate,
|
|
DWORD PwdExpiryDelta,
|
|
DWORD CurrentTime
|
|
)
|
|
{
|
|
// Allocate an attribute.
|
|
IASAttribute attr(true);
|
|
|
|
// Pack the fields.
|
|
Layout value;
|
|
value.changePasswordAllowed = 1; // Change password always allowed.
|
|
value.minPasswordLength = 3; // Arbitrary.
|
|
|
|
// These are already in network order.
|
|
memcpy(value.pwdCreationDate, &PwdCreationDate, 4);
|
|
memcpy(value.pwdExpiryDelta, &PwdExpiryDelta, 4);
|
|
memcpy(value.currentTime, &CurrentTime, 4);
|
|
|
|
// Store the value.
|
|
attr.setOctetString(sizeof(value), (const BYTE*)&value);
|
|
|
|
// Initialize the rest of the fields.
|
|
attr->dwId = RADIUS_ATTRIBUTE_ARAP_FEATURES;
|
|
attr->dwFlags = IAS_INCLUDE_IN_ACCEPT;
|
|
|
|
// Insert the attribute into the request.
|
|
attr.store(request);
|
|
}
|