|
|
///////////////////////////////////////////////////////////////////////////////
//
// 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); } }
|