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.
 
 
 
 
 
 

1568 lines
41 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
secutil.c
Abstract:
This module contains code to accomplish the following tasks:
1) Translate a SID to a name.
2) Translate a name to a SID.
3) Change the password for a given user.
4) Translate a SID to a Mac Id.
5) Translate a Mac Id to a SID.
6) Server event logging
This module communicates with the AFP Server Service to accomplish these
functions. The real work is done in the Server Service. This utility
exists because these functions cannot be made by calling APIs in kernel
mode.
The basic flow of control begins with an FSCTL from the server service.
This FSCTL is marked as pending till one of the four functions is to be
carried out. Then the IRP output buffer contains the function ID and
function input data and the IRP is maeked as complete. The actual
function is executed by the server service and the results are obtained
by the server FSD via the next FSCTL. Most if this information is cached
in paged-memory.
Author:
Narendra Gidwani (microsoft!nareng)
Revision History:
8 Sept 1992 Initial Version
28 Jan 1993 SueA - added support for server event logging
--*/
#define _SECUTIL_LOCALS
#define FILENUM FILE_SECUTIL
#include <afp.h>
#include <scavengr.h>
#include <secutil.h>
#include <access.h>
#include <seposix.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, AfpSecUtilInit)
#pragma alloc_text(PAGE, AfpSecUtilDeInit)
#pragma alloc_text(PAGE, afpDeInitializeSecurityUtility)
#pragma alloc_text(PAGE, AfpInitSidOffsets)
#pragma alloc_text(PAGE, AfpNameToSid)
#pragma alloc_text(PAGE, afpCompleteNameToSid)
#pragma alloc_text(PAGE, AfpSidToName)
#pragma alloc_text(PAGE, afpCompleteSidToName)
#pragma alloc_text(PAGE, AfpSidToMacId)
#pragma alloc_text(PAGE, AfpMacIdToSid)
#pragma alloc_text(PAGE, AfpChangePassword)
#pragma alloc_text(PAGE, afpCompleteChangePassword)
#pragma alloc_text(PAGE, afpLookupSid)
#pragma alloc_text(PAGE, afpUpdateNameSidCache)
#pragma alloc_text(PAGE, afpHashSid)
#pragma alloc_text(PAGE, AfpLogEvent)
#pragma alloc_text(PAGE, afpCompleteLogEvent)
#pragma alloc_text(PAGE, afpQueueSecWorkItem)
#pragma alloc_text(PAGE, afpAgeSidNameCache)
#endif
/*** AfpSecUtilInit
*
* This routine will allocate intialize all the cache tables and
* data structures used by this module. afpDeInitializeSecurityUtility
* should be call to Deallocate this memory.
*/
NTSTATUS
AfpSecUtilInit(
VOID
)
{
ULONG Index;
NTSTATUS Status = STATUS_SUCCESS;
// Initialize
do
{
INITIALIZE_SPIN_LOCK(&afpSecUtilLock);
// Set to Signalled state initially since there is no work in progress
KeInitializeEvent(&afpUtilWorkInProgressEvent, NotificationEvent, True);
// Initialize Single Write Multi-reader access for the SID/NAME cache
AfpSwmrInitSwmr(&afpSWMRForSidNameCache);
InitializeListHead(&afpSecWorkItemQ);
// Allocate space for the SID Lookup table
afpSidLookupTable = (PAFP_SID_NAME*)ALLOC_ACCESS_MEM(sizeof(PAFP_SID_NAME) * SIZE_SID_LOOKUP_TABLE);
if (afpSidLookupTable == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
// Initialize Sid lookup table
RtlZeroMemory(afpSidLookupTable,
sizeof(PAFP_SID_NAME) * SIZE_SID_LOOKUP_TABLE);
afpSidToMacIdTable = (PAFP_SID_MACID *)
ALLOC_ACCESS_MEM(sizeof(PAFP_SID_MACID) * SIZE_SID_LOOKUP_TABLE);
if (afpSidToMacIdTable == NULL)
{
AfpFreeMemory(afpSidLookupTable);
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
RtlZeroMemory(afpSidToMacIdTable, sizeof(PAFP_SID_NAME) * SIZE_SID_LOOKUP_TABLE);
// Initialize array of thread structures.
for (Index = 0; Index < NUM_SECURITY_UTILITY_THREADS; Index++)
{
afpSecurityThread[Index].State = NOT_AVAILABLE;
afpSecurityThread[Index].pSecWorkItem = (PSEC_WORK_ITEM)NULL;
afpSecurityThread[Index].pIrp = (PIRP)NULL;
}
// Start the aging process
AfpScavengerScheduleEvent(afpAgeSidNameCache,
NULL,
SID_NAME_AGE,
True);
} while(False);
return Status;
}
/*** AfpSecUtilDeInit
*
* This routine will free the allocated resources from this module.
* This is called during server unload.
*/
VOID
AfpSecUtilDeInit(
VOID
)
{
PAFP_SID_NAME pSidName, pFree;
PAFP_SID_MACID pSidMacId, pFreeM;
DWORD Count;
PAGED_CODE();
// De-Allocate space for the Sid Lookup table
for(Count = 0; Count < SIZE_SID_LOOKUP_TABLE; Count++)
{
for (pSidName = afpSidLookupTable[Count]; pSidName != NULL; NOTHING)
{
pFree = pSidName;
pSidName = pSidName->SidLink;
AfpFreeMemory(pFree);
}
}
AfpFreeMemory(afpSidLookupTable);
afpLastCachedSid = NULL;
// De-Allocate space for the Sid-to-MacId Lookup table
for(Count = 0; Count < SIZE_SID_LOOKUP_TABLE; Count++)
{
for (pSidMacId = afpSidToMacIdTable[Count]; pSidMacId != NULL; NOTHING)
{
pFreeM = pSidMacId;
pSidMacId = pSidMacId->Next;
AfpFreeMemory(pFreeM);
}
}
AfpFreeMemory(afpSidToMacIdTable);
ASSERT(IsListEmpty(&afpSecWorkItemQ));
}
/*** AfpTerminateSecurityUtility
*
* This is called during server stop. All the service threads are told
* to terminate.
*/
VOID
AfpTerminateSecurityUtility(
VOID
)
{
KIRQL OldIrql;
ULONG Index;
PAFP_SECURITY_THREAD pSecThrd;
PVOID pBufOut;
PIO_STACK_LOCATION pIrpSp;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("AfpTerminateSecurityUtility: waiting for workers to finish work..."));
// Allow any remaining event logs to be processed
AfpIoWait(&afpUtilWorkInProgressEvent, NULL);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("AfpTerminateSecurityUtility: done waiting."));
do {
ACQUIRE_SPIN_LOCK(&afpSecUtilLock, &OldIrql);
for (pSecThrd = afpSecurityThread,Index = 0;
Index < NUM_SECURITY_UTILITY_THREADS;
Index++, pSecThrd++)
{
if (pSecThrd->State != NOT_AVAILABLE)
{
ASSERT(pSecThrd->State != BUSY);
pSecThrd->State = NOT_AVAILABLE ;
break;
}
}
RELEASE_SPIN_LOCK(&afpSecUtilLock, OldIrql);
// We are done, all threads are terminated
if (Index == NUM_SECURITY_UTILITY_THREADS)
return;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("AfpTerminateSecurityUtility: Terminating thread %ld\n", Index));
pIrpSp = IoGetCurrentIrpStackLocation(pSecThrd->pIrp);
pBufOut = pSecThrd->pIrp->AssociatedIrp.SystemBuffer;
ASSERT(pIrpSp->Parameters.FileSystemControl.OutputBufferLength >= sizeof(AFP_FSD_CMD_HEADER));
((PAFP_FSD_CMD_HEADER)pBufOut)->dwId = Index;
((PAFP_FSD_CMD_HEADER)pBufOut)->FsdCommand = AFP_FSD_CMD_TERMINATE_THREAD;
pSecThrd->pIrp->IoStatus.Information = sizeof(AFP_FSD_CMD_HEADER);
pSecThrd->pIrp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(pSecThrd->pIrp, IO_NETWORK_INCREMENT);
pSecThrd->pIrp = NULL;
} while (True);
}
/*** AfpInitSidOffsets
*
* This routine will be called by AfpAdmServerSetParms to initialize the
* the array of Sid-Offset pairs.
*/
AFPSTATUS FASTCALL
AfpInitSidOffsets(
IN ULONG SidOffstPairs,
IN PAFP_SID_OFFSET pSidOff
)
{
ULONG SizeOfBufReqd = 0, SizeAdminSid = 0, SizeNoneSid = 0, SubAuthCount;
LONG i;
BOOLEAN IsDC = True; // Assume Domain Controller
PAGED_CODE();
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("AfpInitSidOffsets: Entered, Count = %ld\n", SidOffstPairs));
//
// Determine if this is a Domain Controller or not by looking for
// the 'account' domain. If the machine is a PDC/BDC, the service will
// NOT send down the Account domain offset.
//
for (i = 0; i < (LONG)SidOffstPairs; i++)
{
if ((pSidOff[i].SidType == AFP_SID_TYPE_DOMAIN) &&
(pSidOff[i].Offset == SE_ACCOUNT_DOMAIN_POSIX_OFFSET))
{
// We are either a server or a workstation (i.e. NtProductServer
// or NtProductWinNt)
IsDC = False;
}
}
//
// Determine the amount of memory needed
//
for (i = 0; i < (LONG)SidOffstPairs; i++)
{
SizeOfBufReqd += sizeof(AFP_SID_OFFSET) + RtlLengthSid(pSidOff[i].pSid);
// Initialize DomainAdmins sid and size if this is a domain controller
// AND this is the primary domain offset
if (IsDC && (pSidOff[i].SidType == AFP_SID_TYPE_PRIMARY_DOMAIN))
{
ASSERT (SizeAdminSid == 0);
ASSERT (AfpSidAdmins == NULL);
SubAuthCount = *RtlSubAuthorityCountSid(pSidOff[i].pSid);
SizeAdminSid = RtlLengthRequiredSid(SubAuthCount + 1);
if ((AfpSidAdmins = (PSID)ALLOC_ACCESS_MEM(SizeAdminSid)) == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopySid(SizeAdminSid, AfpSidAdmins, pSidOff[i].pSid);
// Add the relative ID
*RtlSubAuthorityCountSid(AfpSidAdmins) = (UCHAR)(SubAuthCount+1);
*RtlSubAuthoritySid(AfpSidAdmins, SubAuthCount) = DOMAIN_GROUP_RID_ADMINS;
AfpSizeSidAdmins = RtlLengthSid(AfpSidAdmins);
}
}
ASSERT (SizeOfBufReqd != 0);
// HACK: To fake out the loop below we set SizeNoneSid to nonzero
// on PDC/BDC. Since the AfpServerIsStandalone variable will not
// get set until service calls AfpAdmWServerSetInfo we can
// infer it here since we don't want to try to manufacture the None
// sid on a PDC/BDC.
if (IsDC)
SizeNoneSid = 1;
// If we did not get the Domain admins sid, we must be running on a
// stand-alone machine. So manufacture MACHINE\Administrators
// SID instead. Also manufacture MACHINE\None if this is not a DC.
for (i = SidOffstPairs - 1;
((SizeAdminSid == 0) || (SizeNoneSid == 0)) && (i >= 0);
i--)
{
// Initialize "Administrators" sid and size
if (pSidOff[i].SidType == AFP_SID_TYPE_DOMAIN)
{
if (RtlEqualSid(&AfpSidBuiltIn, pSidOff[i].pSid))
{
ASSERT (SizeAdminSid == 0);
ASSERT (AfpSidAdmins == NULL);
SubAuthCount = *RtlSubAuthorityCountSid(pSidOff[i].pSid);
SizeAdminSid = RtlLengthRequiredSid(SubAuthCount + 1);
if ((AfpSidAdmins = (PSID)ALLOC_ACCESS_MEM(SizeAdminSid)) == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopySid(SizeAdminSid, AfpSidAdmins, pSidOff[i].pSid);
// Add the relative ID
*RtlSubAuthorityCountSid(AfpSidAdmins) = (UCHAR)(SubAuthCount+1);
*RtlSubAuthoritySid(AfpSidAdmins, SubAuthCount) = DOMAIN_ALIAS_RID_ADMINS;
AfpSizeSidAdmins = RtlLengthSid(AfpSidAdmins);
}
else if (pSidOff[i].Offset == SE_ACCOUNT_DOMAIN_POSIX_OFFSET)
{
ASSERT (SizeNoneSid == 0);
ASSERT (AfpSidNone == NULL);
SubAuthCount = *RtlSubAuthorityCountSid(pSidOff[i].pSid);
SizeNoneSid = RtlLengthRequiredSid(SubAuthCount + 1);
if ((AfpSidNone = (PSID)ALLOC_ACCESS_MEM(SizeNoneSid)) == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopySid(SizeNoneSid, AfpSidNone, pSidOff[i].pSid);
// Add the relative ID
*RtlSubAuthorityCountSid(AfpSidNone) = (UCHAR)(SubAuthCount+1);
// Note that the "None" sid on standalone is the same as the
// "Domain Users" Sid on PDC/BDC. (On PDC/BDC the primary
// domain is the same as the account domain).
*RtlSubAuthoritySid(AfpSidNone, SubAuthCount) = DOMAIN_GROUP_RID_USERS;
AfpSizeSidNone = RtlLengthSid(AfpSidNone);
}
}
}
ASSERT (SizeAdminSid != 0);
ASSERT (AfpSidAdmins != NULL);
#if DBG
if (IsDC)
{
ASSERT(AfpSidNone == NULL);
}
else
{
ASSERT(AfpSidNone != NULL);
}
#endif
return AFP_ERR_NONE;
}
/*** AfpSecurityUtilityWorker
*
* This is the main entry point for the security utility thread that
* comes from the AFP server service. This is called if the FSD receives
* a IRP_MJ_FILE_SYSTEM_CONTROL major function code.
*
* This routine will:
* 1) Assign a thread structure if this is a newly created thread.
* 2) Complete the previous work item if this is not a newly created
* thread.
* 3) Check to see if there are any work items to be processed from the
* Security utility work item queue. If there is a work item, it will
* de-queue the work item and complete the IRP. Otherwise it will
* mark the IRP as pending and return STATUS_PENDING.
*
*/
NTSTATUS
AfpSecurityUtilityWorker(
IN PIRP pIrp,
IN PIO_STACK_LOCATION pIrpSp // Pointer to the IRP stack location
)
{
USHORT FuncCode;
USHORT Method;
KIRQL OldIrql;
PVOID pBufIn;
PVOID pBufOut;
LONG iBufLen;
ULONG Index;
NTSTATUS Status;
BOOLEAN FoundMoreWork = False;
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("afpSecurityUtilityWorker: Entered \n"));
FuncCode = (USHORT)
AFP_CC_BASE(pIrpSp->Parameters.FileSystemControl.FsControlCode);
Method = (USHORT)
AFP_CC_METHOD(pIrpSp->Parameters.FileSystemControl.FsControlCode);
if ((FuncCode != CC_BASE_GET_FSD_COMMAND) || (Method != METHOD_BUFFERED))
return STATUS_INVALID_PARAMETER;
// Get the output buffer and its length. Input and Output buffers are
// both pointed to by the SystemBuffer
iBufLen = pIrpSp->Parameters.FileSystemControl.InputBufferLength;
pBufIn = pIrp->AssociatedIrp.SystemBuffer;
if ((iBufLen != 0) && (iBufLen < sizeof(AFP_FSD_CMD_HEADER)))
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("afpSecurityUtilityWorker: iBufLen too small %d\n",iBufLen));
ASSERT(0);
return STATUS_INVALID_PARAMETER;
}
pBufOut = pBufIn;
if (pBufOut == NULL)
return STATUS_INVALID_PARAMETER;
// If this is a newly created thread, we need to find a slot for it
if (iBufLen == 0)
{
ACQUIRE_SPIN_LOCK(&afpSecUtilLock,&OldIrql);
for (Index = 0; Index < NUM_SECURITY_UTILITY_THREADS; Index++)
{
if (afpSecurityThread[Index].State == NOT_AVAILABLE)
{
afpSecurityThread[Index].State = BUSY;
break;
}
}
RELEASE_SPIN_LOCK(&afpSecUtilLock,OldIrql);
// no more threads? fail the request
if (Index == NUM_SECURITY_UTILITY_THREADS)
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("afpSecurityUtilityWorker: no thread available, failing request\n"));
ASSERT(0);
return STATUS_INSUFFICIENT_RESOURCES;
}
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("afpSecurityUtilityWorker: New Thread given slot=%d\n",Index));
}
else
{
PAFP_SECURITY_THREAD pSecThrd;
// The id is actually the slot index into the array of security threads
Index = ((PAFP_FSD_CMD_HEADER)pBufIn)->dwId;
if (Index >= NUM_SECURITY_UTILITY_THREADS)
return STATUS_INVALID_PARAMETER;
pSecThrd = &afpSecurityThread[Index];
if (pSecThrd->State != BUSY)
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR,
("afpSecurityUtilityWorker: thread is not busy!\n"));
ASSERT(0);
return STATUS_INVALID_PARAMETER;
}
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("afpSecurityUtilityThread: Thread slot=%d completed request\n",Index));
// Complete the current job
(*((pSecThrd->pSecWorkItem)->pCompletionRoutine))(Index, pBufIn);
// The job is completed so set the work item pointer to NULL.
pSecThrd->pSecWorkItem = (PSEC_WORK_ITEM)NULL;
}
// OK, we are done with the previous job. Now we check to see if there
// are any jobs in the queue
ACQUIRE_SPIN_LOCK(&afpSecUtilLock,&OldIrql);
if (iBufLen != 0)
{
ASSERT(afpUtilWorkInProgress > 0);
// This is not a newly created thread, so decrement the count of
// work items in progress. If it goes to zero and the work queue
// is empty, signal the event signifying there is no work in progress
if ((--afpUtilWorkInProgress == 0) && IsListEmpty(&afpSecWorkItemQ))
{
KeSetEvent(&afpUtilWorkInProgressEvent, IO_NETWORK_INCREMENT, False);
}
}
if (IsListEmpty(&afpSecWorkItemQ))
{
// There is no work to be done so mark this irp as pending and
// wait for a job
afpSecurityThread[Index].State = IDLE;
IoMarkIrpPending(pIrp);
afpSecurityThread[Index].pIrp = pIrp;
Status = STATUS_PENDING;
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("afpSecurityUtilityWorker: Thread slot=%d marked as IDLE\n",Index));
}
else
{
// Otherwise, there is a job to be processed, so take it off the queue.
// Increment the count of work items in progress and set the event
// to not signalled
afpUtilWorkInProgress ++;
KeClearEvent(&afpUtilWorkInProgressEvent);
FoundMoreWork = True;
afpSecurityThread[Index].State = BUSY;
afpSecurityThread[Index].pSecWorkItem =
(PSEC_WORK_ITEM)RemoveHeadList(&afpSecWorkItemQ);
ASSERT(afpSecWorkItemQLength > 0);
afpSecWorkItemQLength--;
ASSERT((LONG)(pIrpSp->Parameters.FileSystemControl.OutputBufferLength) >=
(afpSecurityThread[Index].pSecWorkItem)->OutputBufSize);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("afpSecurityUtilityWorker: Thread slot=%d marked as BUSY\n",Index));
}
RELEASE_SPIN_LOCK(&afpSecUtilLock,OldIrql);
// If there is a work item to process
if (FoundMoreWork)
{
Status = STATUS_SUCCESS;
// Simply copy the command packet into the IRP and return.
RtlCopyMemory(pBufOut,
(afpSecurityThread[Index].pSecWorkItem)->pOutput,
(afpSecurityThread[Index].pSecWorkItem)->OutputBufSize);
((PAFP_FSD_CMD_HEADER)pBufOut)->dwId = Index;
pIrp->IoStatus.Information =
(afpSecurityThread[Index].pSecWorkItem)->OutputBufSize;
}
return Status;
}
/*** afpGetIndexOfIdle
*
* This routine will first check to see if there are any threads that
* are idle and are waiting for work to do. If there are, then it will
* mark it as busy and up the count of in progress items and release the
* InProgress event. Else it will queue up the work-item.
*/
LONG FASTCALL
afpGetIndexOfIdle(
IN PSEC_WORK_ITEM pSecWorkItem
)
{
KIRQL OldIrql;
LONG Index;
ACQUIRE_SPIN_LOCK(&afpSecUtilLock, &OldIrql);
// See if there are any threads that are ready to process this request
for (Index = 0; Index < NUM_SECURITY_UTILITY_THREADS; Index++)
{
if (afpSecurityThread[Index].State == IDLE)
{
// If we found a thread that is ready, mark it as busy
// Increment the count of work items in progress and set the event
// to not signalled
afpUtilWorkInProgress ++;
KeClearEvent(&afpUtilWorkInProgressEvent);
afpSecurityThread[Index].State = BUSY;
break;
}
}
if (Index == NUM_SECURITY_UTILITY_THREADS)
{
// All threads are busy so queue up this request.
// Alternatively, it could be the case that someone has tried
// to log an event before the usermode utility thread(s) have
// started, in which case we should just queue up the item.
InsertTailList(&afpSecWorkItemQ, &pSecWorkItem->Links);
afpSecWorkItemQLength++;
}
RELEASE_SPIN_LOCK(&afpSecUtilLock, OldIrql);
return Index;
}
/*** afpQueueSecWorkItem
*
* This routine will first check to see if there are any threads that
* are idle and are waiting for work to do. If there are, then it will
* copy the command packet into the IRP's output buffer and mark that
* IRP as complete. Otherwise, it will insert this work item at the
* tail of the work item queue.
*/
LOCAL NTSTATUS
afpQueueSecWorkItem(
IN AFP_FSD_CMD_ID FsdCommand,
IN PSDA pSda,
IN PKEVENT pEvent,
IN PAFP_FSD_CMD_PKT pAfpFsdCmdPkt,
IN LONG BufSize,
IN SEC_COMPLETION_ROUTINE pCompletionRoutine
)
{
LONG Index;
PSEC_WORK_ITEM pSecWorkItem;
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("afpQueueSecWorkItem: Entered \n"));
if ((pSecWorkItem = ALLOC_SWI()) == NULL)
return STATUS_NO_MEMORY;
pSecWorkItem->pSda = pSda;
pSecWorkItem->pCompletionEvent = pEvent;
pSecWorkItem->pCompletionRoutine = pCompletionRoutine;
pSecWorkItem->OutputBufSize = BufSize;
pSecWorkItem->pOutput = pAfpFsdCmdPkt;
pAfpFsdCmdPkt->Header.FsdCommand = FsdCommand;
Index = afpGetIndexOfIdle(pSecWorkItem);
if (Index < NUM_SECURITY_UTILITY_THREADS)
{
PAFP_SECURITY_THREAD pSecThrd;
PIO_STACK_LOCATION pIrpSp;
// Wake this thread up by marking this IRP as complete
pSecThrd = &afpSecurityThread[Index];
pIrpSp = IoGetCurrentIrpStackLocation(pSecThrd->pIrp);
ASSERT((LONG)(pIrpSp->Parameters.FileSystemControl.OutputBufferLength) >=
pSecWorkItem->OutputBufSize);
pAfpFsdCmdPkt->Header.dwId = Index;
RtlCopyMemory(pSecThrd->pIrp->AssociatedIrp.SystemBuffer,
pAfpFsdCmdPkt,
BufSize);
pSecThrd->pSecWorkItem = pSecWorkItem;
pSecThrd->pIrp->IoStatus.Information = (ULONG)(pSecWorkItem->OutputBufSize);
pSecThrd->pIrp->IoStatus.Status = STATUS_SUCCESS;
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("afpQueueSecWorkItem: Abount to release IRP\n"));
IoCompleteRequest(afpSecurityThread[Index].pIrp, IO_NETWORK_INCREMENT);
}
return AFP_ERR_EXTENDED;
}
/*** AfpNameToSid
*
* The FSD will call this routine to do a Name to SID translation.
* This routine will simply create a work item to do the translation.
* This work item will eventually be executed by the user-mode service.
* When the work item is completed, afpCompleteNameToSid will be called
* which will put the result in the SDA.
*
* Returns: STATUS_SUCCESS
* STATUS_NO_MEMORY
*
* MODE: Non-blocking
*/
NTSTATUS FASTCALL
AfpNameToSid(
IN PSDA pSda,
IN PUNICODE_STRING Name
)
{
PAFP_FSD_CMD_PKT pAfpFsdCmdPkt;
LONG BufSize;
PAGED_CODE();
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("AfpNameToSid: mapping %ws\n", Name->Buffer));
// Set up the work item that will translate the name to the SID
BufSize = sizeof(AFP_FSD_CMD_PKT) + Name->Length + sizeof(WCHAR);
if ((pAfpFsdCmdPkt = (PAFP_FSD_CMD_PKT)AfpAllocPagedMemory(BufSize)) == NULL)
{
return STATUS_NO_MEMORY;
}
RtlCopyMemory(pAfpFsdCmdPkt->Data.Name,
Name->Buffer,
Name->Length);
*(PWCHAR)(&pAfpFsdCmdPkt->Data.Name[Name->Length]) = UNICODE_NULL;
return afpQueueSecWorkItem(AFP_FSD_CMD_NAME_TO_SID,
pSda,
NULL,
pAfpFsdCmdPkt,
BufSize,
afpCompleteNameToSid);
}
/*** afpCompleteNameToSid
*
* This routine will be called by AfpSecurityUtilityWorker when the
* thread that processed the work item queued up by afpNameToSid returns.
* This routine will free memory allocated by the afpNameToSid routine.
* It will insert the result in the SDA, and then queue up the worker
* routine that originally requested the lookup.
*/
LOCAL VOID
afpCompleteNameToSid(
IN ULONG Index,
IN PVOID pInBuf
)
{
PAFP_FSD_CMD_PKT pAfpFsdCmdPkt;
PSDA pSda;
PSID pSid;
PAGED_CODE();
pSda = (afpSecurityThread[Index].pSecWorkItem)->pSda;
pAfpFsdCmdPkt = (PAFP_FSD_CMD_PKT)
(afpSecurityThread[Index].pSecWorkItem)->pOutput;
// If there was no error then set the result in the SDA
if (NT_SUCCESS(((PAFP_FSD_CMD_PKT)pInBuf)->Header.ntStatus))
{
pSid = (PSID)(((PAFP_FSD_CMD_PKT)pInBuf)->Data.Sid);
afpUpdateNameSidCache((PWCHAR)pAfpFsdCmdPkt->Data.Name, pSid);
pSda->sda_SecUtilSid = (PSID)AfpAllocPagedMemory(RtlLengthSid(pSid));
if (pSda->sda_SecUtilSid == (PSID)NULL)
pSda->sda_SecUtilResult = STATUS_NO_MEMORY;
else RtlCopySid(RtlLengthSid(pSid), pSda->sda_SecUtilSid, pSid);
}
else pSda->sda_SecUtilSid = (PSID)NULL;
pSda->sda_SecUtilResult = ((PAFP_FSD_CMD_PKT)pInBuf)->Header.ntStatus;
AfpFreeMemory(afpSecurityThread[Index].pSecWorkItem->pOutput);
AfpFreeMemory(afpSecurityThread[Index].pSecWorkItem);
AfpQueueWorkItem(&(pSda->sda_WorkItem));
}
/*** AfpSidToName
*
* The FSD will call this routine to do a SID to Name translation. It
* will first check to see if the SID is in the cache. If it is, it
* will return a pointer to the AFP_SID_NAME structure from which the
* translated Name value may be extracted and it will return
* STATUS_SUCCESS.
* Otherwise, it will queue up a SID to Name lookup request to the
* AFP server service and return AFP_ERR_EXTENDED.
*
* MODE: Non-blocking
*/
NTSTATUS
AfpSidToName(
IN PSDA pSda,
IN PSID Sid,
OUT PAFP_SID_NAME *ppTranslatedSid
)
{
PAFP_FSD_CMD_PKT pAfpFsdCmdPkt;
LONG BufSize;
PAGED_CODE();
// First, check to see if the SID is cached
AfpDumpSid("AfpSidToName: mapping Sid", Sid);
if ((*ppTranslatedSid = afpLookupSid(Sid)) != NULL)
{
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("AfpSidToName: mapped to %ws\n", (*ppTranslatedSid)->Name.Buffer));
return STATUS_SUCCESS;
}
// Not cached so we need to call the user-mode service to do this
// translation
BufSize = sizeof(AFP_FSD_CMD_PKT) + RtlLengthSid(Sid);
if ((pAfpFsdCmdPkt = (PAFP_FSD_CMD_PKT)AfpAllocPagedMemory(BufSize)) == NULL)
{
return STATUS_NO_MEMORY;
}
RtlCopyMemory(pAfpFsdCmdPkt->Data.Sid, Sid, BufSize - sizeof(AFP_FSD_CMD_PKT));
return afpQueueSecWorkItem(AFP_FSD_CMD_SID_TO_NAME,
pSda,
NULL,
pAfpFsdCmdPkt,
BufSize,
afpCompleteSidToName);
}
/*** afpCompleteSidToName
*
* This routine will be called by AfpSecurityUtilityWorker when the
* thread that processed the work item queued up by AfpSidToName returns.
* This routine will update the Name/SID cache, free memory allocated
* by the AfpSidtoName routine, and then queue up the worker routine that
* originally requested the lookup.
*/
LOCAL VOID
afpCompleteSidToName(
IN ULONG Index,
IN PVOID pInBuf
)
{
PAFP_FSD_CMD_PKT pAfpFsdCmdPkt;
PSDA pSda;
PAGED_CODE();
pAfpFsdCmdPkt = (PAFP_FSD_CMD_PKT)
(afpSecurityThread[Index].pSecWorkItem)->pOutput;
// If there was no error then update the cache
if (NT_SUCCESS(((PAFP_FSD_CMD_PKT)pInBuf)->Header.ntStatus))
afpUpdateNameSidCache((WCHAR*)(((PAFP_FSD_CMD_PKT)pInBuf)->Data.Name),
(PSID)(pAfpFsdCmdPkt->Data.Sid));
pSda = (afpSecurityThread[Index].pSecWorkItem)->pSda;
pSda->sda_SecUtilResult = ((PAFP_FSD_CMD_PKT)pInBuf)->Header.ntStatus;
AfpFreeMemory(afpSecurityThread[Index].pSecWorkItem->pOutput);
AfpFreeMemory(afpSecurityThread[Index].pSecWorkItem);
AfpQueueWorkItem(&(pSda->sda_WorkItem));
}
/*** AfpSidToMacId
*
* This routine is called by the FSD to map a SID to an AFP ID. This call
* will first extract the domain SID from this SID. IT will then check
* to see if this domain SID exists in the afpSidOffsetTable cache.
* If it does not exist STATUS_NONE_MAPPED will be returned.
*
* MODE: Blocking
*/
NTSTATUS FASTCALL
AfpSidToMacId(
IN PSID pSid,
OUT PULONG pMacId
)
{
PAFP_SID_MACID pSidMacId, pPrevSidMacId=NULL;
USHORT SidLen;
NTSTATUS Status;
ULONG Location;
PAGED_CODE();
AfpDumpSid("AfpSidToMacId: Mapping Sid", pSid);
if (RtlEqualSid(pSid, &AfpSidNull) ||
(AfpServerIsStandalone && RtlEqualSid(pSid, AfpSidNone)))
{
*pMacId = 0;
return STATUS_SUCCESS;
}
ASSERT(afpSWMRForSidNameCache.swmr_ExclusiveOwner != PsGetCurrentThread());
AfpSwmrAcquireExclusive(&afpSWMRForSidNameCache);
Location = afpHashSid(pSid);
for (pSidMacId = afpSidToMacIdTable[Location];
pSidMacId != NULL;
pSidMacId = pSidMacId->Next)
{
// Found the MacId for this Sid? we already have it: return it
if (RtlEqualSid(pSid, &(pSidMacId->Sid)))
{
*pMacId = pSidMacId->MacId;
AfpSwmrRelease(&afpSWMRForSidNameCache);
return STATUS_SUCCESS;
}
pPrevSidMacId = pSidMacId;
}
//
// we don't have a MacId for this sid in our cache. Create a new one
//
SidLen = (USHORT)RtlLengthSid(pSid);
pSidMacId = (PAFP_SID_MACID)ALLOC_ACCESS_MEM(sizeof(AFP_SID_MACID) + SidLen);
if (pSidMacId == NULL)
{
AfpSwmrRelease(&afpSWMRForSidNameCache);
return STATUS_NO_MEMORY;
}
RtlCopyMemory(pSidMacId->Sid, pSid, SidLen);
pSidMacId->Next = NULL;
// assign a MacId for this Sid
pSidMacId->MacId = afpNextMacIdToUse++;
// and insert this into the list
if (pPrevSidMacId)
{
ASSERT(pPrevSidMacId->Next == NULL);
pPrevSidMacId->Next = pSidMacId;
}
else
{
ASSERT(afpSidToMacIdTable[Location] == NULL);
afpSidToMacIdTable[Location] = pSidMacId;
}
*pMacId = pSidMacId->MacId;
afpLastCachedSid = pSidMacId;
AfpSwmrRelease(&afpSWMRForSidNameCache);
return STATUS_SUCCESS;
}
/*** AfpMacIdToSid
*
* This routine is called by the FSD to map a Afp Id to SID.
* *ppSid should be freed the caller using AfpFreeMemory.
*
* MODE: Blocking
*/
NTSTATUS FASTCALL
AfpMacIdToSid(
IN ULONG MacId,
OUT PSID * ppSid
)
{
PAFP_SID_MACID pSidMacId;
ULONG Count;
DWORD cbSid;
DWORD SubAuthCount;
DWORD GreatestOffset;
NTSTATUS Status;
PAGED_CODE();
if (MacId == 0)
{
*ppSid = &AfpSidNull;
return STATUS_SUCCESS;
}
AfpSwmrAcquireShared(&afpSWMRForSidNameCache);
// see if we just cached this Sid (quite likely)
if ((afpLastCachedSid != NULL) &&
(afpLastCachedSid->MacId == MacId))
{
*ppSid = &(afpLastCachedSid->Sid);
AfpSwmrRelease(&afpSWMRForSidNameCache);
return STATUS_SUCCESS;
}
for (Count = 0; Count < SIZE_SID_LOOKUP_TABLE; Count++)
{
for (pSidMacId = afpSidToMacIdTable[Count];
pSidMacId != NULL;
pSidMacId = pSidMacId->Next )
{
if (pSidMacId->MacId == MacId)
{
*ppSid = &(pSidMacId->Sid);
AfpSwmrRelease(&afpSWMRForSidNameCache);
return STATUS_SUCCESS;
}
}
}
AfpSwmrRelease(&afpSWMRForSidNameCache);
*ppSid = NULL;
return STATUS_NONE_MAPPED;
}
/*** AfpChangePassword
*
* This routine is called by the FSD to change a password for a user.
* Most of the work for this is done by the AFP service. The work item
* is simply queued up. This routine waits for the completion and returns
* with thre result of the call.
*
* MODE: Blocking
*/
NTSTATUS FASTCALL
AfpChangePassword(
IN PSDA pSda,
IN PAFP_PASSWORD_DESC pPassword
)
{
KEVENT CompletionEvent;
PAFP_FSD_CMD_PKT pAfpFsdCmdPkt = NULL;
NTSTATUS Status;
PAGED_CODE();
do
{
// Initialize the event that we will wait for
//
KeInitializeEvent(&CompletionEvent, NotificationEvent, False);
if ((pAfpFsdCmdPkt =
(PAFP_FSD_CMD_PKT)AfpAllocPagedMemory(sizeof(AFP_FSD_CMD_PKT))) == NULL)
{
Status = STATUS_NO_MEMORY;
break;
}
// Copy all the change password data
RtlCopyMemory(&(pAfpFsdCmdPkt->Data.Password),
pPassword,
sizeof(AFP_PASSWORD_DESC));
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO,
("afpChangePassword: Queing work item\n"));
// Block till request completes
if ((Status = afpQueueSecWorkItem(AFP_FSD_CMD_CHANGE_PASSWORD,
pSda,
&CompletionEvent,
pAfpFsdCmdPkt,
sizeof(AFP_FSD_CMD_PKT),
afpCompleteChangePassword)) == AFP_ERR_EXTENDED)
{
AfpIoWait(&CompletionEvent, NULL);
// Request complete. Set return code.
Status = pSda->sda_SecUtilResult;
}
else AfpFreeMemory(pAfpFsdCmdPkt);
} while(False);
return Status;
}
/*** afpCompleteChangePassword
*
* MODE: Blocking
*/
LOCAL VOID
afpCompleteChangePassword(
IN ULONG Index,
IN PVOID pInBuf
)
{
PSEC_WORK_ITEM pSecWorkItem = afpSecurityThread[Index].pSecWorkItem;
PAGED_CODE();
// Set the completion result
pSecWorkItem->pSda->sda_SecUtilResult =
((PAFP_FSD_CMD_PKT)pInBuf)->Header.ntStatus;
AfpFreeMemory(afpSecurityThread[Index].pSecWorkItem->pOutput);
// Signal that this call is completed
KeSetEvent(pSecWorkItem->pCompletionEvent,
IO_NETWORK_INCREMENT,
False);
AfpFreeMemory(afpSecurityThread[Index].pSecWorkItem);
}
/*** afpLookupSid
*
* Given a pointer to a SID value, this routine will search the cache
* for it. If it is found it returns a pointer to the AFP_SID_NAME
* structure so that the translated name may be extracted from it.
*/
LOCAL PAFP_SID_NAME FASTCALL
afpLookupSid(
IN PSID Sid
)
{
PAFP_SID_NAME pAfpSidName;
PAGED_CODE();
AfpSwmrAcquireShared(&afpSWMRForSidNameCache);
for (pAfpSidName = afpSidLookupTable[afpHashSid(Sid)];
pAfpSidName != NULL;
pAfpSidName = pAfpSidName->SidLink)
{
if (RtlEqualSid(Sid, &(pAfpSidName->Sid)))
{
break;
}
}
AfpSwmrRelease(&afpSWMRForSidNameCache);
return pAfpSidName;
}
/*** afpUpdateNameSidCache
*
* This routine will update the SID/Name cache given a SID/translated
* name pair.
*/
LOCAL NTSTATUS FASTCALL
afpUpdateNameSidCache(
IN WCHAR * Name,
IN PSID Sid
)
{
PAFP_SID_NAME pAfpSidName;
ULONG Location;
USHORT NameLen, SidLen;
PAGED_CODE();
NameLen = wcslen(Name) * sizeof(WCHAR);
SidLen = (USHORT)RtlLengthSid(Sid);
pAfpSidName = (PAFP_SID_NAME)ALLOC_ACCESS_MEM(sizeof(AFP_SID_NAME) +
NameLen + SidLen + sizeof(WCHAR));
if (pAfpSidName == NULL)
return STATUS_NO_MEMORY;
// Copy the data into the cache node
RtlCopyMemory(pAfpSidName->Sid, Sid, SidLen);
pAfpSidName->Name.Length = NameLen;
pAfpSidName->Name.MaximumLength = NameLen + sizeof(WCHAR);
pAfpSidName->Name.Buffer = (LPWSTR)((PBYTE)pAfpSidName +
sizeof(AFP_SID_NAME) + SidLen);
RtlCopyMemory(pAfpSidName->Name.Buffer, Name, NameLen);
AfpGetCurrentTimeInMacFormat(&pAfpSidName->LastAccessedTime);
// Insert into Sid lookup table
AfpSwmrAcquireExclusive(&afpSWMRForSidNameCache);
Location = afpHashSid(Sid);
pAfpSidName->SidLink = afpSidLookupTable[Location];
afpSidLookupTable[Location] = pAfpSidName;
AfpSwmrRelease(&afpSWMRForSidNameCache);
return STATUS_SUCCESS;
}
/*** afpHashSid
*
* Given a SID value, this routine will return the bucket index of
* where this value is or should be stored.
*/
LOCAL ULONG FASTCALL
afpHashSid(
IN PSID Sid
)
{
ULONG Count;
ULONG Index;
ULONG Location;
PBYTE pByte;
PAGED_CODE();
for(Count = RtlLengthSid(Sid),
pByte = (PBYTE)Sid,
Index = 0,
Location = 0;
Index < Count;
Index++,
pByte++)
Location = (Location * SID_HASH_RADIX) + *pByte;
return (Location % SIZE_SID_LOOKUP_TABLE);
}
/*** afpAgeSidNameCache
*
* This is called by the scavenger periodically to age out the cache. The
* entries that are aged are the ones not accessed for atleast SID_NAME_AGE
* seconds.
*/
AFPSTATUS FASTCALL
afpAgeSidNameCache(
IN PVOID pContext
)
{
PAFP_SID_NAME pSidName, *ppSidName;
AFPTIME Now;
int i;
PAGED_CODE();
AfpGetCurrentTimeInMacFormat(&Now);
AfpSwmrAcquireExclusive(&afpSWMRForSidNameCache);
for (i = 0; i < SIZE_SID_LOOKUP_TABLE; i++)
{
for (ppSidName = &afpSidLookupTable[i];
(pSidName = *ppSidName) != NULL;)
{
if ((Now - pSidName->LastAccessedTime) > SID_NAME_AGE)
{
*ppSidName = pSidName->SidLink;
AfpFreeMemory(pSidName);
}
else ppSidName = &pSidName->SidLink;
}
}
AfpSwmrRelease(&afpSWMRForSidNameCache);
// Requeue ourselves
return AFP_ERR_REQUEUE;
}
/*** AfpLogEvent
*
* Create a work item containing the event information for the user-mode
* service to write to the event log on behalf of the server. When the
* work item is completed, afpCompleteLogEvent will be called to cleanup
* the work item buffers. This routine is called to log both errors and
* events. If FileHandle is specified, the name of the file/dir associated
* with the handle will be queried, and that will be used as the *first*
* insertion string. Only one insertion string is allowed.
* Errorlog data will always be preceded by the file+line number from which
* the error was logged, and the NTSTATUS code.
*/
VOID
AfpLogEvent(
IN USHORT EventType, // Error, Information etc.
IN ULONG MsgId,
IN DWORD File_Line OPTIONAL,// For errorlog only
IN NTSTATUS Status OPTIONAL,// For errorlog only
IN PBYTE RawDataBuf OPTIONAL,
IN LONG RawDataLen,
IN HANDLE FileHandle OPTIONAL,// For fileio errorlogs only
IN LONG String1Len,
IN PWSTR String1 OPTIONAL
)
{
PAFP_FSD_CMD_PKT pAfpFsdCmdPkt;
LONG outbuflen, extradatalen = 0;
UNICODE_STRING path;
PBYTE tmpptr = NULL;
PWSTR UNALIGNED * ppstr = NULL;
int stringcount = 0;
PAGED_CODE();
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
#ifdef STOP_ON_ERRORS
DBGBRK(DBG_LEVEL_ERR);
#endif
AfpSetEmptyUnicodeString(&path, 0, NULL);
//
// if due to some weird condition, we have too many items pending on the queue, don't
// accept this (note that we aren't taking a spinlock here: it's ok to be off by 1!)
//
if (afpSecWorkItemQLength > MAX_SECWORKITEM_QLEN)
{
ASSERT(0);
return;
}
outbuflen = sizeof(AFP_FSD_CMD_HEADER) + sizeof(AFP_EVENTLOG_DESC) +
RawDataLen + String1Len + sizeof(WCHAR) +
sizeof(DWORD); // extra space for aligning string ptrs if needed
if (ARGUMENT_PRESENT(String1))
{
outbuflen += sizeof(PWSTR);
stringcount ++;
}
if (EventType == EVENTLOG_ERROR_TYPE)
{
extradatalen = sizeof(File_Line) + sizeof(Status);
outbuflen += extradatalen;
// Update error statistics count
INTERLOCKED_INCREMENT_LONG(&AfpServerStatistics.stat_Errors);
}
if (ARGUMENT_PRESENT(FileHandle))
{
outbuflen += sizeof(PWSTR);
stringcount ++;
// Figure out the filename associated with the handle
if (!NT_SUCCESS(AfpQueryPath(FileHandle, &path,
MAX_FSD_CMD_SIZE - outbuflen - sizeof(WCHAR))))
{
return;
}
outbuflen += path.Length + sizeof(WCHAR);
}
ASSERT(outbuflen <= MAX_FSD_CMD_SIZE);
pAfpFsdCmdPkt = (PAFP_FSD_CMD_PKT)AfpAllocZeroedNonPagedMemory(outbuflen);
if (pAfpFsdCmdPkt == NULL)
{
if (path.Buffer != NULL)
{
AfpFreeMemory(path.Buffer);
}
return;
}
// Fill in the command data
pAfpFsdCmdPkt->Data.Eventlog.MsgID = MsgId;
pAfpFsdCmdPkt->Data.Eventlog.EventType = EventType;
pAfpFsdCmdPkt->Data.Eventlog.StringCount = (USHORT)stringcount;
pAfpFsdCmdPkt->Data.Eventlog.DumpDataLen = RawDataLen + extradatalen;
// Fill in the offset to the dump data
pAfpFsdCmdPkt->Data.Eventlog.pDumpData = tmpptr = (PBYTE)0 +
sizeof(AFP_FSD_CMD_HEADER) +
sizeof(AFP_EVENTLOG_DESC);
OFFSET_TO_POINTER(tmpptr, pAfpFsdCmdPkt);
if (tmpptr == NULL)
{
if (path.Buffer != NULL)
{
AfpFreeMemory(path.Buffer);
path.Buffer = NULL;
}
if (pAfpFsdCmdPkt != NULL)
{
AfpFreeMemory(pAfpFsdCmdPkt);
pAfpFsdCmdPkt = NULL;
}
return;
}
if (EventType == EVENTLOG_ERROR_TYPE)
{
RtlCopyMemory(tmpptr, &File_Line, sizeof(File_Line));
tmpptr += sizeof(File_Line);
RtlCopyMemory(tmpptr, &Status, sizeof(Status));
tmpptr += sizeof(Status);
}
RtlCopyMemory(tmpptr, RawDataBuf, RawDataLen);
tmpptr += RawDataLen;
// Align tmpptr on DWORD boundary for filling in string pointers
tmpptr = (PBYTE)DWLEN((ULONG_PTR)tmpptr);
if (tmpptr == NULL)
{
if (path.Buffer != NULL)
{
AfpFreeMemory(path.Buffer);
path.Buffer = NULL;
}
if (pAfpFsdCmdPkt != NULL)
{
AfpFreeMemory(pAfpFsdCmdPkt);
pAfpFsdCmdPkt = NULL;
}
return;
}
// Fill in the offset to the insertion string pointers
pAfpFsdCmdPkt->Data.Eventlog.ppStrings = (PWSTR *)(tmpptr - (PBYTE)pAfpFsdCmdPkt);
ppstr = (PWSTR *)tmpptr;
ASSERT(((ULONG_PTR)ppstr & 3) == 0);
*ppstr = NULL;
// Advance over the string pointers to the place we will copy the strings
tmpptr += stringcount * sizeof(PWSTR);
ASSERT((LONG)(tmpptr - (PBYTE)pAfpFsdCmdPkt) < outbuflen);
// If a handle was supplied, its path will always be the first string
if (path.Length > 0)
{
ASSERT((LONG)(tmpptr + path.Length - (PBYTE)pAfpFsdCmdPkt) < outbuflen);
RtlCopyMemory(tmpptr, path.Buffer, path.Length);
*ppstr = (PWSTR)(tmpptr - (PBYTE)pAfpFsdCmdPkt);
ppstr ++;
tmpptr += path.Length;
ASSERT((LONG)(tmpptr + sizeof(WCHAR) - (PBYTE)pAfpFsdCmdPkt) <=
outbuflen);
*(PWCHAR)tmpptr = UNICODE_NULL;
tmpptr += sizeof(WCHAR);
AfpFreeMemory(path.Buffer);
}
ASSERT((LONG)(tmpptr + String1Len - (PBYTE)pAfpFsdCmdPkt) <
outbuflen);
if (String1Len > 0)
{
RtlCopyMemory(tmpptr, String1, String1Len);
*ppstr = (LPWSTR)(tmpptr - (ULONG_PTR)pAfpFsdCmdPkt);
tmpptr += String1Len;
ASSERT((LONG)(tmpptr + sizeof(WCHAR) - (PBYTE)pAfpFsdCmdPkt) <=
outbuflen);
*(PWCHAR)tmpptr = UNICODE_NULL;
}
afpQueueSecWorkItem(AFP_FSD_CMD_LOG_EVENT,
NULL,
NULL,
pAfpFsdCmdPkt,
outbuflen,
afpCompleteLogEvent);
}
/*** afpCompleteLogEvent
*
* This routine will be called by AfpSecurityUtilityWorker when the
* thread that processed the AfpLogEvent returns. All this does is frees
* up the work item memory.
*/
LOCAL VOID
afpCompleteLogEvent(
IN ULONG Index,
IN PVOID pInBuf
)
{
PAGED_CODE();
AfpFreeMemory(afpSecurityThread[Index].pSecWorkItem->pOutput);
AfpFreeMemory(afpSecurityThread[Index].pSecWorkItem);
}