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.
372 lines
9.7 KiB
372 lines
9.7 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// SYNOPSIS
|
|
//
|
|
// Defines the class Action.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "ias.h"
|
|
#include "action.h"
|
|
#include "attrcvt.h"
|
|
#include "eapprofile.h"
|
|
#include "sdoias.h"
|
|
#include "TunnelTagger.h"
|
|
|
|
_COM_SMARTPTR_TYPEDEF(ISdo, __uuidof(ISdo));
|
|
_COM_SMARTPTR_TYPEDEF(ISdoCollection, __uuidof(ISdoCollection));
|
|
|
|
Action::Action(
|
|
PCWSTR name,
|
|
DWORD nameAttr,
|
|
_variant_t& action,
|
|
const TunnelTagger& tagger
|
|
)
|
|
: attributes(4),
|
|
realmsTarget(RADIUS_ATTRIBUTE_USER_NAME)
|
|
{
|
|
using _com_util::CheckError;
|
|
|
|
//////////
|
|
// Add the policy name attribute.
|
|
//////////
|
|
|
|
IASAttribute policyName(true);
|
|
policyName->dwId = nameAttr;
|
|
policyName.setString(name);
|
|
policyName.setFlag(IAS_INCLUDE_IN_RESPONSE);
|
|
attributes.push_back(policyName);
|
|
|
|
//////////
|
|
// Get an enumerator for the attributes collection.
|
|
//////////
|
|
|
|
ISdoCollectionPtr profile(action);
|
|
IUnknownPtr unk;
|
|
CheckError(profile->get__NewEnum(&unk));
|
|
IEnumVARIANTPtr iter(unk);
|
|
|
|
//////////
|
|
// Iterate through the attributes.
|
|
//////////
|
|
|
|
_variant_t element;
|
|
unsigned long fetched;
|
|
while (iter->Next(1, &element, &fetched) == S_OK && fetched == 1)
|
|
{
|
|
// Convert to an SDO.
|
|
ISdoPtr attribute(element);
|
|
element.Clear();
|
|
|
|
// Get the necessary properties.
|
|
_variant_t id, value, syntax;
|
|
CheckError(attribute->GetProperty(PROPERTY_ATTRIBUTE_ID, &id));
|
|
CheckError(attribute->GetProperty(PROPERTY_ATTRIBUTE_VALUE, &value));
|
|
CheckError(attribute->GetProperty(PROPERTY_ATTRIBUTE_SYNTAX, &syntax));
|
|
|
|
// Attribute-Manipulation-Rule gets processed 'as is'.
|
|
if (V_I4(&id) == IAS_ATTRIBUTE_MANIPULATION_RULE)
|
|
{
|
|
realms.setRealms(&value);
|
|
continue;
|
|
}
|
|
// As does the EAP per-policy config.
|
|
else if (V_I4(&id) == IAS_ATTRIBUTE_EAP_CONFIG)
|
|
{
|
|
EapProfile eap;
|
|
CheckError(eap.Load(value));
|
|
while (!eap.IsEmpty())
|
|
{
|
|
IASAttribute config(true);
|
|
|
|
EapProfile::ConfigData data;
|
|
eap.Pop(data);
|
|
|
|
config->dwId = IAS_ATTRIBUTE_EAP_CONFIG;
|
|
config->Value.itType = IASTYPE_OCTET_STRING;
|
|
config->Value.OctetString.dwLength = data.length;
|
|
config->Value.OctetString.lpValue = data.value;
|
|
|
|
attributes.push_back(config);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// For everything else we process the VARIANTs one at a time.
|
|
VARIANT *begin, *end;
|
|
if (V_VT(&value) == (VT_VARIANT | VT_ARRAY))
|
|
{
|
|
begin = (VARIANT*)V_ARRAY(&value)->pvData;
|
|
end = begin + V_ARRAY(&value)->rgsabound[0].cElements;
|
|
}
|
|
else
|
|
{
|
|
begin = &value;
|
|
end = begin + 1;
|
|
}
|
|
|
|
// Iterate through each value.
|
|
for (VARIANT* v = begin; v != end; ++v)
|
|
{
|
|
// Process based on the attribute ID.
|
|
switch (V_I4(&id))
|
|
{
|
|
case IAS_ATTRIBUTE_MANIPULATION_TARGET:
|
|
{
|
|
realmsTarget = V_I4(v);
|
|
break;
|
|
}
|
|
case IAS_ATTRIBUTE_AUTH_PROVIDER_TYPE:
|
|
{
|
|
IASAttribute type(true);
|
|
type->dwId = IAS_ATTRIBUTE_PROVIDER_TYPE;
|
|
type->Value.itType = IASTYPE_ENUM;
|
|
type->Value.Integer = V_I4(v);
|
|
authProvider.push_back(type);
|
|
break;
|
|
}
|
|
case IAS_ATTRIBUTE_AUTH_PROVIDER_NAME:
|
|
{
|
|
IASAttribute name(true);
|
|
name->dwId = IAS_ATTRIBUTE_PROVIDER_NAME;
|
|
name.setString(V_BSTR(v));
|
|
authProvider.push_back(name);
|
|
break;
|
|
}
|
|
case IAS_ATTRIBUTE_ACCT_PROVIDER_TYPE:
|
|
{
|
|
IASAttribute type(true);
|
|
type->dwId = IAS_ATTRIBUTE_PROVIDER_TYPE;
|
|
type->Value.itType = IASTYPE_ENUM;
|
|
type->Value.Integer = V_I4(v);
|
|
acctProvider.push_back(type);
|
|
break;
|
|
}
|
|
case IAS_ATTRIBUTE_ACCT_PROVIDER_NAME:
|
|
{
|
|
IASAttribute name(true);
|
|
name->dwId = IAS_ATTRIBUTE_PROVIDER_NAME;
|
|
name.setString(V_BSTR(v));
|
|
acctProvider.push_back(name);
|
|
break;
|
|
}
|
|
case RADIUS_ATTRIBUTE_VENDOR_SPECIFIC:
|
|
{
|
|
IASAttribute attr(VSAFromString(V_BSTR(v)), false);
|
|
attr->dwId = RADIUS_ATTRIBUTE_VENDOR_SPECIFIC;
|
|
attr.setFlag(IAS_INCLUDE_IN_ACCEPT);
|
|
attributes.push_back(attr);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
IASTYPEENUM type = (IASTYPEENUM)V_I4(&syntax);
|
|
IASAttribute attr(IASAttributeFromVariant(v, type), false);
|
|
attr->dwId = V_I4(&id);
|
|
attr.setFlag(IAS_INCLUDE_IN_ACCEPT);
|
|
attributes.push_back(attr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tagger.Tag(attributes);
|
|
}
|
|
|
|
|
|
void Action::doAction(IASRequest& request) const
|
|
{
|
|
// Populate the provider information:
|
|
switch (request.get_Request())
|
|
{
|
|
case IAS_REQUEST_ACCESS_REQUEST:
|
|
authProvider.store(request);
|
|
break;
|
|
|
|
case IAS_REQUEST_ACCOUNTING:
|
|
acctProvider.store(request);
|
|
break;
|
|
}
|
|
|
|
// Perform attribute manipulation.
|
|
if (!realms.empty())
|
|
{
|
|
IASAttribute attr;
|
|
attr.load(request, realmsTarget, IASTYPE_OCTET_STRING);
|
|
|
|
if (attr)
|
|
{
|
|
CComBSTR newVal;
|
|
realms.process(IAS_OCT2WIDE(attr->Value.OctetString), &newVal);
|
|
if (newVal)
|
|
{
|
|
if (realmsTarget == RADIUS_ATTRIBUTE_USER_NAME)
|
|
{
|
|
IASAttribute userName(true);
|
|
userName->dwId = RADIUS_ATTRIBUTE_USER_NAME;
|
|
userName->dwFlags = attr->dwFlags;
|
|
userName.setOctetString(newVal);
|
|
userName.store(request);
|
|
|
|
// Now that the new User-Name is safely stored, we can rename
|
|
// the old User-Name.
|
|
attr->dwId = IAS_ATTRIBUTE_ORIGINAL_USER_NAME;
|
|
}
|
|
else
|
|
{
|
|
// No need to save the old, so modify in place.
|
|
attr.setOctetString(newVal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store the profile attributes.
|
|
attributes.store(request);
|
|
|
|
}
|
|
|
|
/////////
|
|
// Various formats of VSA strings.
|
|
/////////
|
|
enum Format
|
|
{
|
|
FORMAT_RAW_HEX,
|
|
FORMAT_STRING,
|
|
FORMAT_INTEGER,
|
|
FORMAT_HEX,
|
|
FORMAT_INET_ADDR
|
|
};
|
|
|
|
/////////
|
|
// Layout of the VSA strings.
|
|
/////////
|
|
struct VSAFormat
|
|
{
|
|
WCHAR format[2];
|
|
WCHAR vendorID[8];
|
|
union
|
|
{
|
|
WCHAR rawValue[1];
|
|
struct
|
|
{
|
|
WCHAR vendorType[2];
|
|
WCHAR vendorLength[2];
|
|
WCHAR value[1];
|
|
};
|
|
};
|
|
};
|
|
|
|
//////////
|
|
// Convert a hex digit to the number it represents.
|
|
//////////
|
|
BYTE digit2Num(WCHAR digit) throw ()
|
|
{
|
|
if ((digit >= L'0') && (digit <= L'9'))
|
|
{
|
|
return digit - L'0';
|
|
}
|
|
else if ((digit >= L'A') && (digit <= L'F'))
|
|
{
|
|
return digit - (L'A' - 10);
|
|
}
|
|
else
|
|
{
|
|
return digit - (L'a' - 10);
|
|
}
|
|
}
|
|
|
|
//////////
|
|
// Pack a hex digit into a byte stream.
|
|
//////////
|
|
PBYTE packHex(PCWSTR src, ULONG srclen, PBYTE dst) throw ()
|
|
{
|
|
for (ULONG dstlen = srclen / 2; dstlen; --dstlen)
|
|
{
|
|
*dst = digit2Num(*src++) << 4;
|
|
*dst++ |= digit2Num(*src++);
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
//////////
|
|
// Convert a string describing a VSA into an IASATTRIBUTE.
|
|
//////////
|
|
PIASATTRIBUTE Action::VSAFromString(PCWSTR string)
|
|
{
|
|
// Number of characters to process.
|
|
SIZE_T len = wcslen(string);
|
|
|
|
// Overlay the layout struct.
|
|
VSAFormat* vsa = (VSAFormat*)string;
|
|
|
|
// Get the string format.
|
|
ULONG format = digit2Num(vsa->format[0]);
|
|
format <<= 8;
|
|
format |= digit2Num(vsa->format[1]);
|
|
|
|
// Temporary buffer used for formatting the VSA.
|
|
BYTE buffer[253], *dst = buffer;
|
|
|
|
// Pack the Vendor-ID.
|
|
dst = packHex(vsa->vendorID, 8, dst);
|
|
|
|
// Pack the Vendor-Type and Vendor-Length for conformant VSAs.
|
|
if (format != FORMAT_RAW_HEX)
|
|
{
|
|
dst = packHex(vsa->vendorType, 2, dst);
|
|
dst = packHex(vsa->vendorLength, 2, dst);
|
|
}
|
|
|
|
// Pack the value.
|
|
switch (format)
|
|
{
|
|
case FORMAT_RAW_HEX:
|
|
{
|
|
dst = packHex(
|
|
vsa->rawValue,
|
|
len - 10,
|
|
dst
|
|
);
|
|
break;
|
|
}
|
|
|
|
case FORMAT_INTEGER:
|
|
case FORMAT_HEX:
|
|
case FORMAT_INET_ADDR:
|
|
{
|
|
dst = packHex(
|
|
vsa->value,
|
|
len - 14,
|
|
dst
|
|
);
|
|
break;
|
|
}
|
|
|
|
case FORMAT_STRING:
|
|
{
|
|
int nchar = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
vsa->value,
|
|
len - 14,
|
|
(PSTR)dst,
|
|
sizeof(buffer) - 6,
|
|
NULL,
|
|
NULL
|
|
);
|
|
dst += nchar;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Store the temporary buffer in an attribute ...
|
|
IASAttribute attr(true);
|
|
attr.setOctetString(dst - buffer, buffer);
|
|
|
|
// ... and return.
|
|
return attr.detach();
|
|
}
|