1569 lines
39 KiB
C
1569 lines
39 KiB
C
/*++
|
||
|
||
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);
|
||
|
||
}
|
||
|