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.
 
 
 
 
 
 

467 lines
11 KiB

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corp. All rights reserved.
//
// FILE
//
// localfile.cpp
//
// SYNOPSIS
//
// Defines the class LocalFile.
//
///////////////////////////////////////////////////////////////////////////////
#include <ias.h>
#include <iasutf8.h>
#include <sdoias.h>
#include <algorithm>
#include <localfile.h>
#include <classattr.h>
#include <formbuf.h>
#define STACK_ALLOC(type, num) (type*)_alloca(sizeof(type) * (num))
//////////
// Misc. enumerator constants.
//////////
const LONG INET_LOG_FORMAT_INTERNET_STD = 0;
const LONG INET_LOG_FORMAT_NCSA = 3;
const LONG INET_LOG_FORMAT_ODBC_RADIUS = 0xFFFF;
//////////
// Inserts a class attribute into the request.
//////////
extern "C"
HRESULT
WINAPI
InsertClassAttribute(
IAttributesRaw* pRaw
)
{
//////////
// Check if this was proxied.
//////////
PIASATTRIBUTE attr = IASPeekAttribute(
pRaw,
IAS_ATTRIBUTE_PROVIDER_TYPE,
IASTYPE_ENUM
);
if (attr && attr->Value.Enumerator == IAS_PROVIDER_RADIUS_PROXY)
{
return S_OK;
}
//////////
// Check if Generate Class Attribute is disabled
//////////
PIASATTRIBUTE generateClassAttr = IASPeekAttribute(
pRaw,
IAS_ATTRIBUTE_GENERATE_CLASS_ATTRIBUTE,
IASTYPE_BOOLEAN
);
if (generateClassAttr && generateClassAttr->Value.Boolean == FALSE)
{
return S_OK;
}
//////////
// Create a new class attribute
// Do not remove any existing class attribute.
//////////
ATTRIBUTEPOSITION pos;
pos.pAttribute = IASClass::createAttribute(NULL);
if (pos.pAttribute == NULL) { return E_OUTOFMEMORY; }
//////////
// Insert into the request.
//////////
HRESULT hr = pRaw->AddAttributes(1, &pos);
IASAttributeRelease(pos.pAttribute);
return hr;
}
LocalFile::LocalFile() throw ()
: computerNameLen(0)
{
}
STDMETHODIMP LocalFile::Initialize()
{
// Get the Unicode computer name.
WCHAR uniName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD len = sizeof(uniName) / sizeof(WCHAR);
if (!GetComputerNameW(uniName, &len))
{
// If it failed, we'll just use an empty string.
len = 0;
}
// Convert the Unicode to UTF-8.
computerNameLen = IASUnicodeToUtf8(uniName, len, computerName);
IASClass::initialize();
return Accountant::Initialize();
}
STDMETHODIMP LocalFile::Shutdown()
{
log.Close();
return Accountant::Shutdown();
}
STDMETHODIMP LocalFile::PutProperty(LONG Id, VARIANT *pValue)
{
if (pValue == NULL) { return E_INVALIDARG; }
switch (Id)
{
case PROPERTY_ACCOUNTING_LOG_OPEN_NEW_FREQUENCY:
{
if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; }
switch (V_I4(pValue))
{
case IAS_LOGGING_UNLIMITED_SIZE:
case IAS_LOGGING_WHEN_FILE_SIZE_REACHES:
case IAS_LOGGING_DAILY:
case IAS_LOGGING_WEEKLY:
case IAS_LOGGING_MONTHLY:
return log.SetPeriod(
static_cast<NEW_LOG_FILE_FREQUENCY>(
V_I4(pValue)
)
);
break;
default:
return E_INVALIDARG;
}
break;
}
case PROPERTY_ACCOUNTING_LOG_OPEN_NEW_SIZE:
{
if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; }
if (V_I4(pValue) <= 0) { return E_INVALIDARG; }
log.SetMaxSize(V_I4(pValue) * 0x100000ui64);
break;
}
case PROPERTY_ACCOUNTING_LOG_FILE_DIRECTORY:
{
if (V_VT(pValue) != VT_BSTR) { return DISP_E_TYPEMISMATCH; }
if (V_BSTR(pValue) == NULL)
{ return E_INVALIDARG; }
return log.SetDirectory(V_BSTR(pValue));
break;
}
case PROPERTY_ACCOUNTING_LOG_IAS1_FORMAT:
{
if (V_VT(pValue) != VT_I4) { return DISP_E_TYPEMISMATCH; }
switch (V_I4(pValue))
{
case INET_LOG_FORMAT_ODBC_RADIUS:
format = formatODBCRecord;
break;
case INET_LOG_FORMAT_INTERNET_STD:
format = formatW3CRecord;
break;
default:
return E_INVALIDARG;
}
break;
}
case PROPERTY_ACCOUNTING_LOG_DELETE_IF_FULL:
{
if (V_VT(pValue) != VT_BOOL) { return DISP_E_TYPEMISMATCH; }
log.SetDeleteIfFull((V_BOOL(pValue)) != 0);
break;
}
default:
{
return Accountant::PutProperty(Id, pValue);
}
}
return S_OK;
}
void LocalFile::Process(IASTL::IASRequest& request)
{
// Do some custom preprocessing.
switch (request.get_Request())
{
case IAS_REQUEST_ACCOUNTING:
{
if (request.get_Response() == IAS_RESPONSE_INVALID)
{
request.SetResponse(IAS_RESPONSE_ACCOUNTING, S_OK);
}
break;
}
case IAS_REQUEST_ACCESS_REQUEST:
{
InsertClassAttribute(request);
break;
}
default:
{
break;
}
}
// Create a FormattedBuffer of the correct type.
FormattedBuffer buffer((format == formatODBCRecord) ? '\"' : '\0');
RecordEvent(&buffer, request);
}
void LocalFile::InsertRecord(
void* context,
IASTL::IASRequest& request,
const SYSTEMTIME& localTime,
PATTRIBUTEPOSITION first,
PATTRIBUTEPOSITION last
)
{
FormattedBuffer& buffer = *static_cast<FormattedBuffer*>(context);
// Invoke the currently configured formatter.
(this->*format)(request, buffer, localTime, first, last);
// We're done.
buffer.endRecord();
}
void LocalFile::Flush(
void* context,
IASTL::IASRequest& request,
const SYSTEMTIME& localTime
)
{
FormattedBuffer& buffer = *static_cast<FormattedBuffer*>(context);
if (!buffer.empty())
{
if (!log.Write(
request.get_Protocol(),
localTime,
buffer.getBuffer(),
buffer.getLength()
))
{
IASTL::issue_error(HRESULT_FROM_WIN32(ERROR_WRITE_FAULT));
}
}
}
void LocalFile::formatODBCRecord(
IASTL::IASRequest& request,
FormattedBuffer& buffer,
const SYSTEMTIME& localTime,
PATTRIBUTEPOSITION firstPos,
PATTRIBUTEPOSITION lastPos
) const
{
//////////
// Column 1: Computer name.
//////////
buffer.append('\"');
buffer.append((PBYTE)computerName, computerNameLen);
buffer.append('\"');
//////////
// Column 2: Service name.
//////////
buffer.beginColumn();
switch (request.get_Protocol())
{
case IAS_PROTOCOL_RADIUS:
buffer.append((const BYTE*)"\"IAS\"", 5);
break;
case IAS_PROTOCOL_RAS:
buffer.append((const BYTE*)"\"RAS\"", 5);
break;
}
//////////
// Column 3: Record time.
//////////
buffer.beginColumn();
buffer.appendDate(localTime);
buffer.beginColumn();
buffer.appendTime(localTime);
//////////
// Allocate a blank record.
//////////
PATTRIBUTEPOSITION *firstField, *curField, *lastField;
size_t nfield = schema.getNumFields() + 1;
firstField = STACK_ALLOC(PATTRIBUTEPOSITION, nfield);
memset(firstField, 0, sizeof(PATTRIBUTEPOSITION) * nfield);
lastField = firstField + nfield;
//////////
// Sort the attributes to coalesce multi-valued attributes.
//////////
std::sort(firstPos, lastPos, IASTL::IASOrderByID());
//////////
// Add a null terminator. This will make it easier to handle multi-valued
// attributes.
//////////
lastPos->pAttribute = NULL;
//////////
// Fill in the fields.
//////////
PATTRIBUTEPOSITION curPos;
DWORD lastSeen = (DWORD)-1;
for (curPos = firstPos; curPos != lastPos; ++curPos)
{
// Only process if this is a new attribute type.
if (curPos->pAttribute->dwId != lastSeen)
{
lastSeen = curPos->pAttribute->dwId;
firstField[schema.getOrdinal(lastSeen)] = curPos;
}
}
//////////
// Pack the record into the buffer. We skip field 0, since that's where
// we map all the attributes we don't want to log.
//////////
for (curField = firstField + 1; curField != lastField; ++curField)
{
buffer.beginColumn();
if (*curField) { buffer.append(*curField); }
}
}
void LocalFile::formatW3CRecord(
IASTL::IASRequest& request,
FormattedBuffer& buffer,
const SYSTEMTIME& localTime,
PATTRIBUTEPOSITION firstPos,
PATTRIBUTEPOSITION lastPos
) const
{
//////////
// Column 1: NAS-IP-Addresses
//////////
PIASATTRIBUTE attr = IASPeekAttribute(
request,
RADIUS_ATTRIBUTE_NAS_IP_ADDRESS,
IASTYPE_INET_ADDR
);
if (attr == 0)
{
attr = IASPeekAttribute(
request,
IAS_ATTRIBUTE_CLIENT_IP_ADDRESS,
IASTYPE_INET_ADDR
);
}
if (attr != 0)
{
buffer.append(attr->Value);
}
//////////
// Column 2: User-Name
//////////
buffer.beginColumn();
attr = IASPeekAttribute(request,
RADIUS_ATTRIBUTE_USER_NAME,
IASTYPE_OCTET_STRING);
if (attr) { buffer.append(attr->Value); }
//////////
// Column 3: Record time.
//////////
buffer.beginColumn();
buffer.appendDate(localTime);
buffer.beginColumn();
buffer.appendTime(localTime);
//////////
// Column 4: Service name.
//////////
buffer.beginColumn();
switch (request.get_Protocol())
{
case IAS_PROTOCOL_RADIUS:
buffer.append("IAS");
break;
case IAS_PROTOCOL_RAS:
buffer.append("RAS");
break;
}
//////////
// Column 5: Computer name.
//////////
buffer.beginColumn();
buffer.append((PBYTE)computerName, computerNameLen);
//////////
// Pack the attributes into the buffer.
//////////
PATTRIBUTEPOSITION curPos;
for (curPos = firstPos; curPos != lastPos; ++curPos)
{
if (!schema.excludeFromLog(curPos->pAttribute->dwId))
{
buffer.beginColumn();
buffer.append(curPos->pAttribute->dwId);
buffer.beginColumn();
buffer.append(*(curPos->pAttribute));
}
}
}