Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

506 lines
13 KiB

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corp. All rights reserved.
//
// FILE
//
// translate.cpp
//
// SYNOPSIS
//
// Defines the class Translator.
//
// MODIFICATION HISTORY
//
// 02/04/2000 Original version.
// 04/17/2000 Add support for UTCTime.
//
///////////////////////////////////////////////////////////////////////////////
#include <proxypch.h>
#include <iasutil.h>
#include <attrdnry.h>
#include <radpack.h>
#include <translate.h>
//////////
// The offset between the UNIX and NT epochs.
//////////
const ULONG64 UNIX_EPOCH = 116444736000000000ui64;
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// ByteSource
//
// DESCRIPTION
//
// Simple class for extracting bytes from an octet string.
//
///////////////////////////////////////////////////////////////////////////////
class ByteSource
{
public:
ByteSource(const BYTE* buf, ULONG buflen) throw ()
: next(buf), last(buf + buflen) { }
// Returns true if there are any bytes remaining.
bool more() const throw ()
{
return next != last;
}
// Extracts 'nbyte' bytes.
const BYTE* extract(ULONG nbyte)
{
const BYTE* retval = next;
// Update the cursor.
next += nbyte;
// Did we overflow ?
if (next > last) { _com_issue_error(E_INVALIDARG); }
return retval;
}
ULONG remaining() const throw ()
{
return (ULONG)(last - next);
}
protected:
const BYTE* next; // The next byte in the stream.
const BYTE* last; // The end of the stream.
private:
// Not implemented.
ByteSource(const ByteSource&);
ByteSource& operator=(const ByteSource&);
};
HRESULT Translator::FinalConstruct() throw ()
{
return dnary.FinalConstruct();
}
void Translator::toRadius(
IASATTRIBUTE& src,
IASAttributeVector& dst
) const
{
if (src.dwId > 0 && src.dwId < 256)
{
// This is already a RADIUS attribute, so all we have to do is convert
// the value to an octet string.
if (src.Value.itType == IASTYPE_OCTET_STRING)
{
// It's already an octet string, so just scatter into dst.
scatter(0, src, dst);
}
else
{
// Convert to an octet string ...
IASAttribute attr(true);
encode(0, src, attr);
// ... and scatter into dst.
scatter(0, *attr, dst);
}
}
else
{
// Look up the attribute definition.
const AttributeDefinition* def = dnary.findByID(src.dwId);
// We only process VSAs. At this point, anything else is an internal
// attribute that has no RADIUS representation.
if (def && def->vendorID)
{
// Allocate an attribute for the VSA.
IASAttribute attr(true);
// USR uses a different header than everybody else.
ULONG headerLength = (def->vendorID != 429) ? 6 : 8;
// Encode the data.
ULONG dataLength = encode(headerLength, src, attr);
// Pack the Vendor-Id.
PBYTE buf = attr->Value.OctetString.lpValue;
IASInsertDWORD(buf, def->vendorID);
buf += 4;
// Pack the Vendor-Type and Vendor-Length;
if (def->vendorID != 429)
{
*buf++ = (BYTE)def->vendorType;
*buf++ = (BYTE)(dataLength + 2);
}
else
{
IASInsertDWORD(buf, def->vendorType);
buf += 4;
}
// Mark it as a VSA.
attr->dwId = RADIUS_ATTRIBUTE_VENDOR_SPECIFIC;
// Scatter into multiple attributes if necessary.
scatter(headerLength, *attr, dst);
}
}
}
void Translator::fromRadius(
const RadiusAttribute& src,
DWORD flags,
IASAttributeVector& dst
)
{
if (src.type != RADIUS_ATTRIBUTE_VENDOR_SPECIFIC)
{
// Look this up in the dictionary.
const AttributeDefinition* def = dnary.findByID(src.type);
// If we don't recognize the attribute, treat it as an octet string.
IASTYPE syntax = def ? (IASTYPE)def->syntax : IASTYPE_OCTET_STRING;
// Create the new attribute.
IASAttribute attr(true);
attr->dwId = src.type;
attr->dwFlags = flags;
decode(syntax, src.value, src.length, attr);
// Add to the destination vector.
dst.push_back(attr);
}
else
{
// Create a byte source from the attribute value.
ByteSource bytes(src.value, src.length);
// Extract the vendor ID.
ULONG vendorID = IASExtractDWORD(bytes.extract(4));
// Loop through the value and convert each sub-VSA.
do
{
// Extract the Vendor-Type and the data length.
ULONG type, length;
if (vendorID != 429)
{
type = *bytes.extract(1);
length = *bytes.extract(1) - 2;
}
else
{
type = IASExtractDWORD(bytes.extract(4));
length = bytes.remaining();
}
// Do we have this VSA in our dictionary ?
const AttributeDefinition* def = dnary.findByVendorInfo(
vendorID,
type
);
if (!def)
{
// No, so we'll just leave it 'as is'.
IASAttribute attr(true);
attr->dwId = RADIUS_ATTRIBUTE_VENDOR_SPECIFIC;
attr->dwFlags = flags;
attr.setOctetString(src.length, src.value);
dst.push_back(attr);
break;
}
// Yes, so we can decode this properly.
IASAttribute attr(true);
attr->dwId = def->id;
attr->dwFlags = flags;
decode((IASTYPE)def->syntax, bytes.extract(length), length, attr);
dst.push_back(attr);
} while (bytes.more());
}
}
void Translator::decode(
IASTYPE dstType,
const BYTE* src,
ULONG srclen,
IASAttribute& dst
)
{
// Switch based on the destination type.
switch (dstType)
{
case IASTYPE_BOOLEAN:
{
if (srclen != 4) { _com_issue_error(E_INVALIDARG); }
dst->Value.Boolean = IASExtractDWORD(src) ? TRUE : FALSE;
break;
}
case IASTYPE_INTEGER:
case IASTYPE_ENUM:
case IASTYPE_INET_ADDR:
{
if (srclen != 4) { _com_issue_error(E_INVALIDARG); }
dst->Value.Integer = IASExtractDWORD(src);
break;
}
case IASTYPE_UTC_TIME:
{
if (srclen != 4) { _com_issue_error(E_INVALIDARG); }
// Extract the UNIX time.
ULONG64 val = IASExtractDWORD(src);
// Convert from seconds to 100 nsec intervals.
val *= 10000000;
// Shift to the NT epoch.
val += 116444736000000000ui64;
// Split into the high and low DWORDs.
dst->Value.UTCTime.dwLowDateTime = (DWORD)val;
dst->Value.UTCTime.dwHighDateTime = (DWORD)(val >> 32);
break;
}
case IASTYPE_STRING:
{
dst.setString(srclen, src);
break;
}
default:
{
dst.setOctetString(srclen, src);
break;
}
}
// All went well, so set type attribute type.
dst->Value.itType = dstType;
}
ULONG Translator::getEncodedSize(
const IASATTRIBUTE& src
)
{
// Note: this is the same as RadiusUtil::getEncodedSize
// only one version should be kept
ULONG size;
switch (src.Value.itType)
{
case IASTYPE_BOOLEAN:
case IASTYPE_INTEGER:
case IASTYPE_ENUM:
case IASTYPE_INET_ADDR:
case IASTYPE_UTC_TIME:
{
size = 4;
break;
}
case IASTYPE_STRING:
{
// Convert the string to ANSI so we can count octets.
DWORD dwErr = IASAttributeAnsiAlloc(const_cast<PIASATTRIBUTE>(&src));
if (dwErr != NO_ERROR)
{
_com_issue_error(HRESULT_FROM_WIN32(dwErr));
}
// Allow for NULL strings and don't count the terminator.
if (src.Value.String.pszAnsi)
{
size = strlen(src.Value.String.pszAnsi);
}
else
{
size = 0;
}
break;
}
case IASTYPE_OCTET_STRING:
{
size = src.Value.OctetString.dwLength;
break;
}
default:
// All other types have no wire representation.
size = 0;
}
return size;
}
void Translator::encode(
PBYTE dst,
const IASATTRIBUTE& src
) throw ()
{
// Switch based on the source's type.
switch (src.Value.itType)
{
case IASTYPE_BOOLEAN:
{
IASInsertDWORD(dst, (src.Value.Boolean ? 1 : 0));
break;
}
case IASTYPE_INTEGER:
case IASTYPE_ENUM:
case IASTYPE_INET_ADDR:
{
IASInsertDWORD(dst, src.Value.Integer);
break;
}
case IASTYPE_STRING:
{
const BYTE* p = (const BYTE*)src.Value.String.pszAnsi;
// Don't use strcpy since we don't want the null terminator.
if (p)
{
while (*p) { *dst++ = *p++; }
}
break;
}
case IASTYPE_UTC_TIME:
{
ULONG64 val;
// Move in the high DWORD.
val = src.Value.UTCTime.dwHighDateTime;
val <<= 32;
// Move in the low DWORD.
val |= src.Value.UTCTime.dwLowDateTime;
// Convert to the UNIX epoch.
val -= UNIX_EPOCH;
// Convert to seconds.
val /= 10000000;
IASInsertDWORD(dst, (DWORD)val);
break;
}
case IASTYPE_OCTET_STRING:
{
memcpy(dst,
src.Value.OctetString.lpValue,
src.Value.OctetString.dwLength);
}
}
}
ULONG Translator::encode(
ULONG headerLength,
const IASATTRIBUTE& src,
IASAttribute& dst
)
{
// Compute the encoded size.
ULONG dataLength = getEncodedSize(src);
ULONG attrLength = dataLength + headerLength;
// Allocate a buffer for the value.
PBYTE buf = (PBYTE)CoTaskMemAlloc(attrLength);
if (!buf) { _com_issue_error(E_OUTOFMEMORY); }
// Encode the data.
encode(buf + headerLength, src);
// Store the buffer in the attribute.
dst->dwId = src.dwId;
dst->dwFlags = src.dwFlags;
dst->Value.itType = IASTYPE_OCTET_STRING;
dst->Value.OctetString.dwLength = attrLength;
dst->Value.OctetString.lpValue = buf;
return dataLength;
}
void Translator::scatter(
ULONG headerLength,
IASATTRIBUTE& src,
IASAttributeVector& dst
)
{
if (src.Value.OctetString.dwLength <= 253)
{
// If the attribute is already small enough, then there's nothing to do.
dst.push_back(&src);
}
else
{
// Maximum length of data that can be store in each attribute.
ULONG maxDataLength = 253 - headerLength;
// Number of bytes remaining to be scattered.
ULONG remaining = src.Value.OctetString.dwLength - headerLength;
// Next byte to be scattered.
PBYTE next = src.Value.OctetString.lpValue + headerLength;
do
{
// Allocate an attribute for the next chunk.
IASAttribute chunk(true);
// Compute the data length and attribute length for this chunk.
ULONG dataLength = min(remaining, maxDataLength);
ULONG attrLength = dataLength + headerLength;
// Allocate a buffer for the value.
PBYTE buf = (PBYTE)CoTaskMemAlloc(attrLength);
if (!buf) { _com_issue_error(E_OUTOFMEMORY); }
// Copy in the header ...
memcpy(buf, src.Value.OctetString.lpValue, headerLength);
// ... and the next chunk of data.
memcpy(buf + headerLength, next, dataLength);
// Store the buffer in the attribute.
chunk->dwId = src.dwId;
chunk->dwFlags = src.dwFlags;
chunk->Value.itType = IASTYPE_OCTET_STRING;
chunk->Value.OctetString.dwLength = attrLength;
chunk->Value.OctetString.lpValue = buf;
// Append to the destination vector.
dst.push_back(chunk);
// Advance to the next chunk.
remaining -= dataLength;
next += dataLength;
} while (remaining);
}
}