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.
 
 
 
 
 
 

2896 lines
70 KiB

/*++
Copyright (c) 2002 Microsoft Corporation
Module Name:
ulnamesp.c
Abstract:
This module implements the namespace reservation and registration
functions.
Author:
Anish Desai (anishd) 13-May-2002
Revision History:
--*/
#include "precomp.h"
#include "cgroupp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, UlpFindPortNumberIndex)
#pragma alloc_text(PAGE, UlpQuerySchemeForPort)
#pragma alloc_text(PAGE, UlpBindSchemeToPort)
#pragma alloc_text(PAGE, UlpUnbindSchemeFromPort)
#pragma alloc_text(PAGE, UlpUpdateReservationInRegistry)
#pragma alloc_text(INIT, UlpLogGeneralInitFailure)
#pragma alloc_text(INIT, UlpLogSpecificInitFailure)
#pragma alloc_text(INIT, UlpValidateUrlSdPair)
#pragma alloc_text(INIT, UlpReadReservations)
#pragma alloc_text(INIT, UlInitializeNamespace)
#pragma alloc_text(PAGE, UlTerminateNamespace)
#pragma alloc_text(PAGE, UlpNamespaceAccessCheck)
#pragma alloc_text(PAGE, UlpTreeAllocateNamespace)
#pragma alloc_text(PAGE, UlpTreeReserveNamespace)
#pragma alloc_text(PAGE, UlpReserveUrlNamespace)
#pragma alloc_text(PAGE, UlpAllocateDeferredRemoveItem)
#pragma alloc_text(PAGE, UlpTreeRegisterNamespace)
#pragma alloc_text(PAGE, UlpRegisterUrlNamespace)
#pragma alloc_text(PAGE, UlpPrepareSecurityDescriptor)
#pragma alloc_text(PAGE, UlpAddReservationEntry)
#pragma alloc_text(PAGE, UlpDeleteReservationEntry)
#endif
//
// File Global variables.
//
PUL_PORT_SCHEME_TABLE g_pPortSchemeTable = NULL;
UL_PUSH_LOCK g_PortSchemeTableLock;
BOOLEAN g_InitNamespace = FALSE;
HANDLE g_pUrlAclKeyHandle = NULL;
/**************************************************************************++
Routine Description:
This routine searches the global port scheme assignment table for a
given port number. g_PortSchemeTableLock must be acquired either
exclusive or shared.
Arguments:
PortNumber - Supplies the port number to be searched.
pIndex - Returns the index where the port number is present.
If no match is found, it contains the index where this PortNumber
must be inserted.
Return Value:
TRUE - If a match is found.
FALSE - Otherwise.
--**************************************************************************/
BOOLEAN
UlpFindPortNumberIndex(
IN USHORT PortNumber,
OUT PLONG pIndex
)
{
LONG StartIndex, EndIndex, Index;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pIndex != NULL);
ASSERT(g_pPortSchemeTable != NULL);
ASSERT(g_pPortSchemeTable->UsedCount <= MAXUSHORT + 1);
ASSERT(g_pPortSchemeTable->AllocatedCount <= MAXUSHORT + 1);
StartIndex = 0;
EndIndex = g_pPortSchemeTable->UsedCount - 1;
//
// Binary search the table of port numbers and schemes.
//
while (StartIndex <= EndIndex)
{
ASSERT(0 <= StartIndex && StartIndex < g_pPortSchemeTable->UsedCount);
ASSERT(0 <= EndIndex && EndIndex < g_pPortSchemeTable->UsedCount);
Index = (StartIndex + EndIndex) / 2;
if (PortNumber == g_pPortSchemeTable->Table[Index].PortNumber)
{
//
// Found the port number.
//
*pIndex = Index;
return TRUE;
}
else if (PortNumber < g_pPortSchemeTable->Table[Index].PortNumber)
{
EndIndex = Index - 1;
}
else
{
StartIndex = Index + 1;
}
}
//
// Did not find a match. Return the position where it should be inserted.
//
*pIndex = StartIndex;
return FALSE;
}
/**************************************************************************++
Routine Description:
This routine returns scheme bound to a port number.
Arguments:
PortNumber - Supplies the port number.
Secure - Returns whether a http or https scheme is bound to the
supplied port number.
Return Value:
STATUS_SUCCESS - if a scheme is bound to the port number.
STATUS_INVALID_PARAMETER - if no scheme is bound to the port number.
--**************************************************************************/
NTSTATUS
UlpQuerySchemeForPort(
IN USHORT PortNumber,
OUT PBOOLEAN Secure
)
{
NTSTATUS Status;
LONG Index;
//
// Sanity check.
//
PAGED_CODE();
//
// Find if there is a scheme bound to the port number.
//
Status = STATUS_INVALID_PARAMETER;
UlAcquirePushLockShared(&g_PortSchemeTableLock);
if (UlpFindPortNumberIndex(PortNumber, &Index))
{
*Secure = g_pPortSchemeTable->Table[Index].Secure;
Status = STATUS_SUCCESS;
}
UlReleasePushLockShared(&g_PortSchemeTableLock);
return Status;
}
/**************************************************************************++
Routine Description:
This routine adds a port, scheme pair to the global table.
Arguments:
PortNumber - Supplies the port number.
Scheme - Supplies the scheme.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpBindSchemeToPort(
IN BOOLEAN Secure,
IN USHORT PortNumber
)
{
LONG StartIndex;
BOOLEAN bFound;
NTSTATUS Status;
//
// Sanity check.
//
PAGED_CODE();
//
// Acquire lock exclusively.
//
UlAcquirePushLockExclusive(&g_PortSchemeTableLock);
//
// Find an existing scheme that is bound to the port.
//
bFound = UlpFindPortNumberIndex(PortNumber, &StartIndex);
if (bFound)
{
ASSERT(0 <= StartIndex && StartIndex < g_pPortSchemeTable->UsedCount);
ASSERT(g_pPortSchemeTable->Table[StartIndex].PortNumber == PortNumber);
if (g_pPortSchemeTable->Table[StartIndex].Secure != Secure)
{
//
// Trying to bind a scheme that is different from an
// existing bound scheme.
//
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
}
//
// The reference count is a LONG. Don't let it overflow.
//
if (g_pPortSchemeTable->Table[StartIndex].RefCount == MAXLONG)
{
Status = STATUS_INTEGER_OVERFLOW;
goto end;
}
//
// Found an existing entry, increment the reference count.
//
ASSERT(g_pPortSchemeTable->Table[StartIndex].RefCount > 0);
g_pPortSchemeTable->Table[StartIndex].RefCount++;
Status = STATUS_SUCCESS;
goto end;
}
//
// StartIndex is the place where this new entry must be added!
//
ASSERT(0 <= StartIndex && StartIndex <= g_pPortSchemeTable->UsedCount);
//
// Is the table full?
//
if (g_pPortSchemeTable->UsedCount == g_pPortSchemeTable->AllocatedCount)
{
//
// Allocate a bigger table.
//
PUL_PORT_SCHEME_TABLE pNewTable;
ULONG NewTableSize;
//
// Table does not need more than 65536 entries.
//
ASSERT(g_pPortSchemeTable->AllocatedCount < MAXUSHORT + 1);
NewTableSize = MIN(g_pPortSchemeTable->AllocatedCount*2, MAXUSHORT+1);
pNewTable = UL_ALLOCATE_STRUCT_WITH_SPACE(
PagedPool,
UL_PORT_SCHEME_TABLE,
sizeof(UL_PORT_SCHEME_PAIR) * NewTableSize,
UL_PORT_SCHEME_TABLE_POOL_TAG
);
if (pNewTable == NULL)
{
Status = STATUS_NO_MEMORY;
goto end;
}
//
// Initialize the new table.
//
pNewTable->UsedCount = g_pPortSchemeTable->UsedCount;
pNewTable->AllocatedCount = NewTableSize;
//
// Copy 0 to (StartIndex-1) entries from the current table to the
// new table.
//
if (StartIndex > 0)
{
RtlCopyMemory(
&pNewTable->Table[0],
&g_pPortSchemeTable->Table[0],
sizeof(UL_PORT_SCHEME_PAIR) * StartIndex
);
}
//
// Copy StartIndex to (UsedCount-1) entries from the current table
// to the new table. They are copied from position (StartIndex+1)
// in the new table, effectively creating a free entry at StartIndex.
//
if (g_pPortSchemeTable->UsedCount - StartIndex > 0)
{
RtlCopyMemory(
&pNewTable->Table[StartIndex + 1],
&g_pPortSchemeTable->Table[StartIndex],
sizeof(UL_PORT_SCHEME_PAIR)
* (g_pPortSchemeTable->UsedCount - StartIndex)
);
}
//
// Free the current table. Make new table current.
//
UL_FREE_POOL(g_pPortSchemeTable, UL_PORT_SCHEME_TABLE_POOL_TAG);
g_pPortSchemeTable = pNewTable;
}
else
{
//
// Table must have free entries at the bottom (higher indices.)
//
ASSERT(g_pPortSchemeTable->UsedCount
< g_pPortSchemeTable->AllocatedCount);
//
// No table expansion. But move entries from StartIndex to
// (UsedCount-1) to new location at (StartIndex+1) to UsedCount.
//
if (g_pPortSchemeTable->UsedCount - StartIndex > 0)
{
RtlMoveMemory(
&g_pPortSchemeTable->Table[StartIndex + 1],
&g_pPortSchemeTable->Table[StartIndex],
sizeof(UL_PORT_SCHEME_PAIR)
* (g_pPortSchemeTable->UsedCount - StartIndex)
);
}
}
//
// Add the new entry to the table.
//
ASSERT(g_pPortSchemeTable->UsedCount < g_pPortSchemeTable->AllocatedCount);
ASSERT(0 <= StartIndex && StartIndex <= g_pPortSchemeTable->UsedCount);
g_pPortSchemeTable->UsedCount++;
g_pPortSchemeTable->Table[StartIndex].PortNumber = PortNumber;
g_pPortSchemeTable->Table[StartIndex].Secure = Secure;
g_pPortSchemeTable->Table[StartIndex].RefCount = 1;
Status = STATUS_SUCCESS;
end:
UlReleasePushLockExclusive(&g_PortSchemeTableLock);
return Status;
}
/**************************************************************************++
Routine Description:
This routine unbinds a previously bound scheme from a port.
Arguments:
PortNumber - Supplies the port number.
Scheme - Supplies the scheme.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpUnbindSchemeFromPort(
IN BOOLEAN Secure,
IN USHORT PortNumber
)
{
LONG StartIndex;
BOOLEAN bFound;
NTSTATUS Status;
//
// Sanity check.
//
PAGED_CODE();
//
// Acquire lock exclusively.
//
UlAcquirePushLockExclusive(&g_PortSchemeTableLock);
//
// Find an existing scheme that is bound to PortNumber.
//
bFound = UlpFindPortNumberIndex(PortNumber, &StartIndex);
if (bFound == FALSE)
{
//
// There is no scheme bound to PortNumber.
//
ASSERT(FALSE); // catch this misuse
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
//
// Sanity check. StartIndex must be within bounds. PortNumber must match.
//
ASSERT(0 <= StartIndex && StartIndex < g_pPortSchemeTable->UsedCount);
ASSERT(g_pPortSchemeTable->Table[StartIndex].PortNumber == PortNumber);
//
// Is the scheme bound to PortNumber same as Scheme?
//
if (g_pPortSchemeTable->Table[StartIndex].Secure != Secure)
{
//
// Tried to unbind a scheme from a port and the port is not bound to
// the scheme.
//
ASSERT(FALSE); // catch this misuse
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
}
//
// Decrement the ref count.
//
ASSERT(g_pPortSchemeTable->Table[StartIndex].RefCount > 0);
g_pPortSchemeTable->Table[StartIndex].RefCount--;
//
// If RefCount drops to zero, its time to cleanup that entry.
//
if (g_pPortSchemeTable->Table[StartIndex].RefCount == 0)
{
LONG NewTableSize = 0;
PUL_PORT_SCHEME_TABLE pNewTable = NULL;
BOOLEAN bContract = FALSE;
//
// Do we need to contract the table?
//
if (4 * (g_pPortSchemeTable->UsedCount - 1)
<= g_pPortSchemeTable->AllocatedCount)
{
//
// Current table is less than 25% used.
//
NewTableSize = g_pPortSchemeTable->AllocatedCount / 2;
if (NewTableSize >= UL_DEFAULT_PORT_SCHEME_TABLE_SIZE)
{
//
// Current table is at least twice bigger than the default
// size.
//
pNewTable = UL_ALLOCATE_STRUCT_WITH_SPACE(
PagedPool,
UL_PORT_SCHEME_TABLE,
sizeof(UL_PORT_SCHEME_PAIR) * NewTableSize,
UL_PORT_SCHEME_TABLE_POOL_TAG
);
if (pNewTable != NULL)
{
//
// Could allocate memory for a smaller table.
//
bContract = TRUE;
}
}
}
if (bContract)
{
//
// We are going to contract the existing table.
//
ASSERT(pNewTable != NULL);
ASSERT(NewTableSize >= UL_DEFAULT_PORT_SCHEME_TABLE_SIZE);
ASSERT(NewTableSize >= g_pPortSchemeTable->UsedCount - 1);
//
// Initialize the new table.
//
pNewTable->UsedCount = g_pPortSchemeTable->UsedCount;
pNewTable->AllocatedCount = NewTableSize;
//
// Copy all entries from 0 to (StartIndex - 1)
// from the current table to the new table.
//
if (StartIndex > 0)
{
RtlCopyMemory(
&pNewTable->Table[0],
&g_pPortSchemeTable->Table[0],
sizeof(UL_PORT_SCHEME_PAIR) * StartIndex
);
}
//
// Copy all entries from (StartIndex+1) to (UsedCount-1)
// from the current table to the new table.
//
// Effectively, StartIndex entry is eliminated.
//
if (g_pPortSchemeTable->UsedCount - StartIndex - 1 > 0)
{
RtlCopyMemory(
&pNewTable->Table[StartIndex],
&g_pPortSchemeTable->Table[StartIndex+1],
sizeof(UL_PORT_SCHEME_PAIR)
* (g_pPortSchemeTable->UsedCount - StartIndex - 1)
);
}
//
// Free the current table.
//
UL_FREE_POOL(g_pPortSchemeTable, UL_PORT_SCHEME_TABLE_POOL_TAG);
//
// The new table becomes the current table.
//
g_pPortSchemeTable = pNewTable;
}
else
{
//
// We are not going to contract the table but still have to
// eliminate the unused table entry. Move all entries from
// (StartIndex + 1) to (UsedCount - 1) one position up.
//
if (g_pPortSchemeTable->UsedCount - StartIndex - 1 > 0)
{
RtlMoveMemory(
&g_pPortSchemeTable->Table[StartIndex],
&g_pPortSchemeTable->Table[StartIndex+1],
sizeof(UL_PORT_SCHEME_PAIR)
* (g_pPortSchemeTable->UsedCount - StartIndex - 1)
);
}
}
g_pPortSchemeTable->UsedCount--;
}
Status = STATUS_SUCCESS;
end:
UlReleasePushLockExclusive(&g_PortSchemeTableLock);
return Status;
}
/**************************************************************************++
Routine Description:
This routine adds (or deletes) a (url, security descriptor) pair to
(or from) the registry.
Arguments:
Add - Supplies the operation, TRUE imples add and FALSE implies delete.
pParsedUrl - Supplies the parsed url of the reservation.
pSecurityDescriptor - Supplies the security descriptor.
(It must be valid while adding and NULL while deleting.)
SecurityDescriptorLength - Supplies the length of the security
descriptor in bytes. (It must be non-zero when adding and
zero when deleting.)
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpUpdateReservationInRegistry(
IN BOOLEAN Add,
IN PHTTP_PARSED_URL pParsedUrl,
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
IN ULONG SecurityDescriptorLength
)
{
NTSTATUS Status;
PWSTR pUrlToWrite = NULL;
PWSTR pNewUrl = NULL;
UNICODE_STRING UnicodeUrl;
//
// sanity check
//
PAGED_CODE();
ASSERT(IS_VALID_HTTP_PARSED_URL(pParsedUrl));
ASSERT(pParsedUrl->pFullUrl[pParsedUrl->UrlLength] == UNICODE_NULL);
ASSERT(Add?(pSecurityDescriptor != NULL):(pSecurityDescriptor == NULL));
ASSERT(Add?(SecurityDescriptorLength > 0):(SecurityDescriptorLength == 0));
//
// Do we have a valid handle to the registry key? It can be invalid,
// for instance, if during driver initialization ZwCreateKey failed.
//
if (g_pUrlAclKeyHandle == NULL)
{
return STATUS_INVALID_HANDLE;
}
//
// default error code
//
Status = STATUS_INVALID_PARAMETER;
//
// Do some special processing for literal ip address sites.
//
if (HttpUrlSite_IP == pParsedUrl->SiteType)
{
//
// convert scheme://ip:port:ip/ to scheme://ip:port/
//
PWSTR pToken;
PWSTR pLiteralAddr;
SIZE_T UrlLength;
//
// length of original url in wchar (+1 for UNICODE_NULL)
//
UrlLength = (pParsedUrl->UrlLength + 1);
pNewUrl = UL_ALLOCATE_ARRAY(
PagedPool,
WCHAR,
UrlLength,
URL_POOL_TAG
);
if (pNewUrl == NULL)
{
Status = STATUS_NO_MEMORY;
goto end;
}
RtlCopyMemory(pNewUrl, pParsedUrl->pFullUrl, UrlLength*sizeof(WCHAR));
//
// skip ipv6 literal address if present
//
pToken = &pNewUrl[HTTP_PREFIX_COLON_INDEX + 3];
if (pToken[0] == L'[' || pToken[1] == L'[')
{
pToken = wcschr(pToken, L']');
if (pToken == NULL)
{
ASSERT(FALSE);
goto end;
}
}
//
// skip to the port number
//
pToken = wcschr(pToken, L':');
if (pToken == NULL)
{
ASSERT(FALSE);
goto end;
}
//
// skip ':'
//
pToken++;
//
// find the literal address
//
pToken = wcschr(pToken, L':');
if (pToken == NULL)
{
ASSERT(FALSE);
goto end;
}
pLiteralAddr = pToken;
//
// find begining of abs path
//
pToken = wcschr(pToken, L'/');
if (pToken == NULL)
{
ASSERT(FALSE);
goto end;
}
//
// overwrite literal address. effectively convert
// scheme://host:port:ip/abs_path -> scheme://host:port/abs_path
//
while (*pToken != L'\0')
{
*pLiteralAddr++ = *pToken++;
}
*pLiteralAddr = L'\0';
//
// use new url when writing to registry
//
pUrlToWrite = pNewUrl;
}
else
{
pUrlToWrite = pParsedUrl->pFullUrl;
}
ASSERT(pUrlToWrite != NULL);
//
// convert url to unicode for registry functions
//
Status = UlInitUnicodeStringEx(&UnicodeUrl, pUrlToWrite);
if (!NT_SUCCESS(Status))
{
goto end;
}
if (Add)
{
//
// write url and security descriptor to registry
//
Status = ZwSetValueKey(
g_pUrlAclKeyHandle,
&UnicodeUrl,
0, // title index; must be zero.
REG_BINARY,
pSecurityDescriptor,
SecurityDescriptorLength
);
if (!NT_SUCCESS(Status))
{
//
// too bad...can't write to registry. delete the old key value.
// ignore the return status.
//
ZwDeleteValueKey(g_pUrlAclKeyHandle, &UnicodeUrl);
}
}
else
{
//
// delete url from the registry.
//
Status = ZwDeleteValueKey(g_pUrlAclKeyHandle, &UnicodeUrl);
}
end:
if (pNewUrl != NULL)
{
UL_FREE_POOL(pNewUrl, URL_POOL_TAG);
}
return Status;
}
/**************************************************************************++
Routine Description:
This routine is called during namespace initialization if an error
occurs while intializing the config group url tree using the entries
in registry.
Arguments:
LogCount - Supplies the number of logs written in the past. Returns
one more than the input value.
LogStatus - Supplies the status to log in the event log.
Return Value:
NTSTATUS.
--**************************************************************************/
__inline
NTSTATUS
UlpLogGeneralInitFailure(
IN NTSTATUS LogStatus
)
{
NTSTATUS Status;
//
// Sanity check.
//
PAGED_CODE();
//
// Write an event log entry.
//
Status = UlWriteEventLogEntry(
EVENT_HTTP_NAMESPACE_INIT_FAILED, // EventCode
0, // UniqueEventValue
0, // NumStrings
NULL, // pStringArray
sizeof(LogStatus), // DataSize
&LogStatus // Data
);
return Status;
}
/**************************************************************************++
Routine Description:
This routine writes an event log message about a specific namespace
reservation initialization failure. It logs the url present in the
full info structure.
Arguments:
LogCount - Supplies the number of logs written in the past. Returns
one more than the input value.
pFullInfo - Supplies information read from registry.
LogStatus - Supplies the error status to log.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpLogSpecificInitFailure(
IN PKEY_VALUE_FULL_INFORMATION pFullInfo,
IN NTSTATUS LogStatus
)
{
NTSTATUS Status;
PWSTR pMessage;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pFullInfo != NULL);
//
// pFullInfo->Name contains the url to be written and it not
// UNICODE_NULL terminated. Allocate memory to copy.
//
pMessage = UL_ALLOCATE_ARRAY(
PagedPool,
WCHAR,
(pFullInfo->NameLength / sizeof(WCHAR)) + 1,
URL_POOL_TAG
);
if (pMessage != NULL)
{
//
// Copy url and null terminate it.
//
RtlCopyMemory(pMessage, pFullInfo->Name, pFullInfo->NameLength);
pMessage[pFullInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
//
// Write event log entry.
//
Status = UlEventLogOneStringEntry(
EVENT_HTTP_NAMESPACE_INIT2_FAILED,
pMessage,
TRUE,
LogStatus
);
UL_FREE_POOL(pMessage, URL_POOL_TAG);
}
else
{
//
// Could not allocate memory. Log just the error code.
//
Status = UlpLogGeneralInitFailure(LogStatus);
}
return Status;
}
/**************************************************************************++
Routine Description:
This routine validates registry key value name (url) and data (security
descriptor). Called only during driver initialization.
Arguments:
pFullInfo - Supplies the registry key value name and data.
ppSanitizeUrl - Returns sanitized url. Must be freed to paged pool.
pParsedUrl - Returns parsed url information.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpValidateUrlSdPair(
IN PKEY_VALUE_FULL_INFORMATION pFullInfo,
OUT PWSTR * ppSanitizedUrl,
OUT PHTTP_PARSED_URL pParsedUrl
)
{
NTSTATUS Status;
BOOLEAN Success;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pFullInfo != NULL);
ASSERT(ppSanitizedUrl != NULL);
ASSERT(pParsedUrl != NULL);
//
// Key value type must be binary.
//
if (pFullInfo->Type != REG_BINARY)
{
return STATUS_INVALID_PARAMETER;
}
//
// Then, validate security descriptor. It must be a self-relative
// security descriptor.
//
Success = RtlValidRelativeSecurityDescriptor(
(PUCHAR)pFullInfo + pFullInfo->DataOffset,
pFullInfo->DataLength,
0
);
if (!Success)
{
return STATUS_INVALID_PARAMETER;
}
//
// Value name must be at least one unicode char long.
//
if (pFullInfo->NameLength < sizeof(WCHAR))
{
return STATUS_INVALID_PARAMETER;
}
//
// Then validate the url.
//
Status = UlSanitizeUrl(
pFullInfo->Name,
pFullInfo->NameLength / sizeof(WCHAR),
TRUE, // trailing slash required
ppSanitizedUrl,
pParsedUrl
);
return Status;
}
/***************************************************************************++
Routine Description:
This function gets called from the Driver Load routine. It builds the
URL ACLing information from the registry. If the URL ACLing key itself
is not present, we'll add the defaults.
If there are any bogus entries in the URIACL entry, we'll ignore them with
and eventlog. The routine will return failure only on a major error.
Not-reentrant.
Arguments:
None.
Return Value:
STATUS_SUCCESS.
--***************************************************************************/
NTSTATUS
UlpReadReservations(
VOID
)
{
KEY_VALUE_FULL_INFORMATION fullInfo;
PKEY_VALUE_FULL_INFORMATION pFullInfo = NULL;
ULONG Length;
ULONG Index;
UNICODE_STRING BaseName;
NTSTATUS Status;
OBJECT_ATTRIBUTES objectAttributes;
ULONG Disposition;
ULONG bEventLog = TRUE;
ULONG dataLength;
//
// Sanity check.
//
PAGED_CODE();
//
// Open the registry.
//
Status = UlInitUnicodeStringEx(&BaseName, REGISTRY_URLACL_INFORMATION);
if (!NT_SUCCESS(Status))
{
//
// Write an event log entry.
//
ASSERT(FALSE); // shouldn't happen!
UlpLogGeneralInitFailure(Status);
goto end;
}
InitializeObjectAttributes(
&objectAttributes, // ObjectAttributes
&BaseName, // ObjectName
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL, // RootDirectory
NULL // SecurityDescriptor
);
Status = ZwCreateKey(
&g_pUrlAclKeyHandle,
KEY_READ | KEY_WRITE, // AccessMask
&objectAttributes,
0, // TitleIndex
NULL, // Class
REG_OPTION_NON_VOLATILE,
&Disposition
);
if (!NT_SUCCESS(Status))
{
//
// Write an event log entry.
//
UlpLogGeneralInitFailure(Status);
//
// Make the handle NULL so that it is not used elsewhere accidentally.
//
g_pUrlAclKeyHandle = NULL;
goto end;
}
if (Disposition == REG_CREATED_NEW_KEY)
{
//
// we created the key, hence there is nothing to read!
//
goto end;
}
pFullInfo = &fullInfo;
Length = sizeof(fullInfo);
Index = 0;
dataLength = 0;
RtlZeroMemory(pFullInfo, Length);
//
// loop through all registry key values, making reservations for each
//
for (;;)
{
Status = ZwEnumerateValueKey(
g_pUrlAclKeyHandle,
Index,
KeyValueFullInformation,
(PVOID) pFullInfo,
Length,
&dataLength
);
if (Status == STATUS_SUCCESS)
{
PWSTR pSanitizedUrl;
HTTP_PARSED_URL ParsedUrl;
//
// First validate the registry data.
//
Status = UlpValidateUrlSdPair(
pFullInfo,
&pSanitizedUrl,
&ParsedUrl
);
if (NT_SUCCESS(Status))
{
//
// Add this url to the CG url tree.
//
Status = UlpAddReservationEntry(
&ParsedUrl,
(PSECURITY_DESCRIPTOR)
((PUCHAR) pFullInfo + pFullInfo->DataOffset),
pFullInfo->DataLength,
(PACCESS_STATE)NULL,
(ACCESS_MASK)0,
KernelMode,
FALSE
);
//
// Free memory that was allocated by UlSanitizeUrl.
//
UL_FREE_POOL(pSanitizedUrl, URL_POOL_TAG);
}
else
{
Status = STATUS_REGISTRY_CORRUPT;
}
if (!NT_SUCCESS(Status))
{
//
// Write an event log entry that an error occurred.
// Ignore the error and continue processing.
//
if (bEventLog)
{
bEventLog = FALSE;
UlpLogSpecificInitFailure(pFullInfo, Status);
}
}
//
// Move to the next value in registry.
//
Index ++;
}
else if (Status == STATUS_NO_MORE_ENTRIES)
{
//
// We've reached the end, so this is a success.
//
break;
}
else if (Status == STATUS_BUFFER_OVERFLOW)
{
//
// Sanity check.
//
ASSERT(dataLength >= pFullInfo->DataLength +
pFullInfo->NameLength +
FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name)
);
//
// Remember how much memory to allocate.
//
Length = dataLength;
//
// If any memory was allocated in previous iterations, free it now.
//
if (pFullInfo != &fullInfo)
{
UL_FREE_POOL(pFullInfo, UL_REGISTRY_DATA_POOL_TAG);
}
//
// Allocate memory.
//
pFullInfo = UL_ALLOCATE_POOL(
PagedPool,
dataLength,
UL_REGISTRY_DATA_POOL_TAG
);
if(!pFullInfo)
{
//
// Write an event log entry.
//
UlpLogGeneralInitFailure(STATUS_INSUFFICIENT_RESOURCES);
goto end;
}
//
// Initialize.
//
RtlZeroMemory(pFullInfo, dataLength);
}
else
{
//
// An unknown error occurred. Event log and get out.
//
UlpLogGeneralInitFailure(Status);
goto end;
}
}
end:
//
// Free pFullInfo structure if it was allocated above.
//
if (pFullInfo != &fullInfo && pFullInfo != NULL)
{
UL_FREE_POOL(pFullInfo, UL_REGISTRY_DATA_POOL_TAG);
}
return STATUS_SUCCESS;
}
/**************************************************************************++
Routine Description:
This routine initializes the namespace registration and reservation
support. Not re-entrant.
Arguments:
None.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlInitializeNamespace(
VOID
)
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(!g_InitNamespace);
if (!g_InitNamespace)
{
//
// Allocate port scheme table.
//
g_pPortSchemeTable = UL_ALLOCATE_STRUCT_WITH_SPACE(
PagedPool,
UL_PORT_SCHEME_TABLE,
sizeof(UL_PORT_SCHEME_PAIR)
* UL_DEFAULT_PORT_SCHEME_TABLE_SIZE,
UL_PORT_SCHEME_TABLE_POOL_TAG
);
if (g_pPortSchemeTable == NULL)
{
Status = STATUS_NO_MEMORY;
goto end;
}
g_pPortSchemeTable->UsedCount = 0;
g_pPortSchemeTable->AllocatedCount = UL_DEFAULT_PORT_SCHEME_TABLE_SIZE;
//
// Initialize pushlock.
//
UlInitializePushLock(&g_PortSchemeTableLock,
"g_PortSchemeTableLock",
0,
UL_PORT_SCHEME_TABLE_POOL_TAG
);
//
// Now add reservation entries from registry.
//
CG_LOCK_WRITE();
Status = UlpReadReservations();
CG_UNLOCK_WRITE();
if (!NT_SUCCESS(Status))
{
UL_FREE_POOL(g_pPortSchemeTable, UL_PORT_SCHEME_TABLE_POOL_TAG);
UlDeletePushLock(&g_PortSchemeTableLock);
goto end;
}
g_InitNamespace = TRUE;
}
end:
return Status;
}
/**************************************************************************++
Routine Description:
This routine terminates the namespace stuff. Not re-entrant.
Arguments:
None.
Return Value:
None.
--**************************************************************************/
VOID
UlTerminateNamespace(
VOID
)
{
//
// Sanity check.
//
PAGED_CODE();
if (g_InitNamespace)
{
//
// Delete pushlock.
//
UlDeletePushLock(&g_PortSchemeTableLock);
//
// Delete port scheme table.
//
ASSERT(g_pPortSchemeTable != NULL);
UL_FREE_POOL(g_pPortSchemeTable, UL_PORT_SCHEME_TABLE_POOL_TAG);
g_pPortSchemeTable = NULL;
//
// Delete the handle to registry key.
//
if(g_pUrlAclKeyHandle != NULL)
{
ZwClose(g_pUrlAclKeyHandle);
g_pUrlAclKeyHandle = NULL;
}
//
// Terminated.
//
g_InitNamespace = FALSE;
}
}
/**************************************************************************++
Routine Description:
This routine performs access check based on the supplied access state.
All access check succeeds for administrators and local system.
Note: If the access state is NULL, the function returns success.
USE AT YOUR OWN RISK!
Arguments:
pSecurityDescriptor - Supplies security descriptor protecting the
namespace.
AccessState - Supplies a pointer to the access state of the caller.
DesiredAccess - Supplies access mask.
RequestorMode - Supplies the requestor mode.
pObjectName - Supplies the namespace that is being access.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpNamespaceAccessCheck(
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
IN PACCESS_STATE AccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN KPROCESSOR_MODE RequestorMode OPTIONAL,
IN PCWSTR pObjectName OPTIONAL
)
{
NTSTATUS Status;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pSecurityDescriptor != NULL);
ASSERT(RtlValidSecurityDescriptor(pSecurityDescriptor));
if (AccessState == NULL)
{
//
// No access check. USE EXTREME CAUTION!
//
return STATUS_SUCCESS;
}
//
// Check the access.
//
Status = UlAccessCheck(
pSecurityDescriptor,
AccessState,
DesiredAccess,
RequestorMode,
pObjectName
);
if (!NT_SUCCESS(Status))
{
//
// Access check failed. See if the caller has admin or system
// privileges.
//
Status = UlAccessCheck(
g_pAdminAllSystemAll,
AccessState,
DesiredAccess,
RequestorMode,
pObjectName
);
}
return Status;
}
/**************************************************************************++
Routine Description:
This routine allocates a namespace in the config group url tree. It is
mainly called during reservations and registrations.
It ensures that the caller is authorized to do the operation and there
are no duplicate reservations and registrations.
Arguments:
pParsedUrl - Supplies the parsed url of the namespace.
OperatorType - Supplies the operation being performed (either
registration or reservation.)
AccessState - Supplies the access state of the caller.
DesiredAccess - Supplies the access desired by the caller.
RequestorMode - Supplies the processor mode of the caller.
ppEntry - Returns the config group url tree entry for the namespace.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpTreeAllocateNamespace(
IN PHTTP_PARSED_URL pParsedUrl,
IN HTTP_URL_OPERATOR_TYPE OperatorType,
IN PACCESS_STATE AccessState,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode,
OUT PUL_CG_URL_TREE_ENTRY *ppEntry
)
{
NTSTATUS Status;
PUL_CG_URL_TREE_ENTRY pSiteEntry;
PUL_CG_URL_TREE_ENTRY pEntry;
PUL_CG_URL_TREE_ENTRY pReservation;
PWSTR pNextToken;
PSECURITY_DESCRIPTOR pSD;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_CG_LOCK_OWNED_WRITE());
ASSERT(IS_VALID_HTTP_PARSED_URL(pParsedUrl));
ASSERT(ppEntry != NULL);
//
// Initialize return value.
//
*ppEntry = NULL;
//
// Find a matching site.
//
Status = UlpTreeFindSite(pParsedUrl->pFullUrl, &pNextToken, &pSiteEntry);
if (NT_SUCCESS(Status))
{
//
// Found a matching site.
//
//
// Find the longest matching reservation and exact matching entries.
//
Status = UlpTreeFindNodeHelper(
pSiteEntry,
pNextToken,
FNC_LONGEST_RESERVATION,
&pReservation,
&pEntry
);
if (NT_SUCCESS(Status))
{
//
// Found an exact match.
//
//
// Fail duplicate registrations and reservations.
//
ASSERT(IS_VALID_TREE_ENTRY(pEntry));
if (OperatorType == HttpUrlOperatorTypeRegistration)
{
if (pEntry->Registration == TRUE)
{
//
// Adding a registration when it already exists!
//
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
}
}
else if (OperatorType == HttpUrlOperatorTypeReservation)
{
if (pEntry->Reservation == TRUE)
{
//
// Adding a reservation when it already exists!
//
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
}
}
else
{
//
// Should not be here!
//
ASSERT(FALSE);
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
}
}
else
{
//
// Did not find an exact match.
//
pEntry = NULL;
}
//
// Find a security descriptor to check access against.
//
if (pReservation != NULL)
{
//
// Use the SD from the longest matching reservation entry.
//
ASSERT(IS_VALID_TREE_ENTRY(pReservation));
pSD = pReservation->pSecurityDescriptor;
}
else
{
//
// No longest matching reservations...use default SD.
//
pSD = g_pAdminAllSystemAll;
}
ASSERT(pSD != NULL && RtlValidSecurityDescriptor(pSD));
//
// Perform access check.
//
Status = UlpNamespaceAccessCheck(
pSD,
AccessState,
DesiredAccess,
RequestorMode,
pParsedUrl->pFullUrl
);
if (!NT_SUCCESS(Status))
{
//
// Ouch...
//
goto end;
}
}
else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
//
// Adding a new site. Perform access check.
// Only admin and system can add new site.
//
Status = UlpNamespaceAccessCheck(
g_pAdminAllSystemAll,
AccessState,
DesiredAccess,
RequestorMode,
pParsedUrl->pFullUrl
);
if (!NT_SUCCESS(Status))
{
//
// Ouch...
//
goto end;
}
//
// The caller has permission to create a new site.
//
Status = UlpTreeCreateSite(
pParsedUrl->pFullUrl,
pParsedUrl->SiteType,
&pNextToken,
&pSiteEntry
);
if (!NT_SUCCESS(Status))
{
goto end;
}
//
// We have not found an exact entry.
// Note: If one tries to allocate http://site:80/ and site is not
// present, we'll create the site above. Now the pEntry
// is same as pSiteEntry. We don't handle the expection
// here and let the UlpTreeInsert() take care of it.
//
pEntry = NULL;
}
else
{
//
// Could not find the site for some reason.
//
goto end;
}
//
// If there is no exact matching entry, create one.
//
if (pEntry == NULL)
{
//
// Try to insert. This will cleanup dummy nodes and sites if it fails.
// Note: If UlpTreeInsert finds a existing entry, it just returns
// the existing entry and does not add a new one.
//
Status = UlpTreeInsert(
pParsedUrl->pFullUrl,
pParsedUrl->SiteType,
pNextToken,
pSiteEntry,
&pEntry
);
if (!NT_SUCCESS(Status))
{
goto end;
}
}
//
// Return the entry.
//
ASSERT(NT_SUCCESS(Status));
*ppEntry = pEntry;
end:
return Status;
}
/**************************************************************************++
Routine Description:
This routine reserves a namespace in the CG tree.
Arguments:
pParsedUrl - Supplies the parsed url of the namespace to be reserved.
pNextToken - Supplies the unparsed portion of the url (abs path.)
pUrlSD - Supplies the security descriptor to be applied to the
namespace.
pSiteEntry - Supplies the site level tree entry under which the
reservation will be made.
AccessState - Supplies the access state of the caller.
DesiredAccess - Supplies the access mask of the caller.
RequestorMode - Supplies the processor mode of the caller.
Retutn Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpTreeReserveNamespace(
IN PHTTP_PARSED_URL pParsedUrl,
IN PSECURITY_DESCRIPTOR pUrlSD,
IN PACCESS_STATE AccessState,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode
)
{
NTSTATUS Status;
PUL_CG_URL_TREE_ENTRY pEntry;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_CG_LOCK_OWNED_WRITE());
ASSERT(IS_VALID_HTTP_PARSED_URL(pParsedUrl));
ASSERT(pUrlSD != NULL && RtlValidSecurityDescriptor(pUrlSD));
Status = UlpTreeAllocateNamespace(
pParsedUrl,
HttpUrlOperatorTypeReservation,
AccessState,
DesiredAccess,
RequestorMode,
&pEntry
);
if (!NT_SUCCESS(Status))
{
goto end;
}
//
// mark it as a reservation entry.
//
ASSERT(pEntry->Reservation == FALSE);
pEntry->Reservation = TRUE;
InsertTailList(&g_ReservationListHead, &pEntry->ReservationListEntry);
//
// add security descriptor.
//
ASSERT(pEntry->pSecurityDescriptor == NULL);
pEntry->pSecurityDescriptor = pUrlSD;
//
// all done
//
Status = STATUS_SUCCESS;
end:
return Status;
}
/**************************************************************************++
Routine Description:
The higher level routine calls helper routines to make a namespace
reservation.
Arguments:
pUrl - Supplies the namespace.
SiteType - Supplies the type of the url.
pUrlSD - Supplies the security descriptor to be applied to the
namespace.
AccessState - Supplies the access state of the caller.
DesiredAccess - Supplies the access mask of the caller.
RequestorMode - Supplies the processor mode of the caller.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpReserveUrlNamespace(
IN PHTTP_PARSED_URL pParsedUrl,
IN PSECURITY_DESCRIPTOR pUrlSD,
IN PACCESS_STATE AccessState,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode
)
{
NTSTATUS Status;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_CG_LOCK_OWNED_WRITE());
ASSERT(pParsedUrl != NULL);
ASSERT(pUrlSD != NULL);
ASSERT(RtlValidSecurityDescriptor(pUrlSD));
//
// Don't allow reservation of urls with different schemes but same port
// number. For instance, if http://www.microsoft.com:80/ is reserved,
// then don't allow reservation of https://anyhostname:80/. Because
// these different schemes can not share the same port.
//
Status = UlpBindSchemeToPort(pParsedUrl->Secure, pParsedUrl->PortNumber);
if (!NT_SUCCESS(Status))
{
goto end;
}
//
// Proceed to the actual reservation.
//
Status = UlpTreeReserveNamespace(
pParsedUrl,
pUrlSD,
AccessState,
DesiredAccess,
RequestorMode
);
if (!NT_SUCCESS(Status))
{
//
// The reservation failed. Undo the binding done above.
//
NTSTATUS TempStatus;
TempStatus = UlpUnbindSchemeFromPort(
pParsedUrl->Secure,
pParsedUrl->PortNumber
);
ASSERT(NT_SUCCESS(TempStatus));
}
end:
return Status;
}
/**************************************************************************++
Routine Description:
This routine allocates a UL_DEFERRED_REMOVE_ITEM and initializes
it with port number and scheme. Caller must free it to the paged
pool.
Arguments:
pParsedUrl - Supplies the parsed url containing scheme and port number.
Return Value:
PUL_DEFERRED_REMOVE_ITEM - if successful.
NULL - otherwise.
--**************************************************************************/
__inline
PUL_DEFERRED_REMOVE_ITEM
UlpAllocateDeferredRemoveItem(
IN PHTTP_PARSED_URL pParsedUrl
)
{
PUL_DEFERRED_REMOVE_ITEM pWorker;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pParsedUrl != NULL);
//
// Allocate the structure.
//
pWorker = UL_ALLOCATE_STRUCT(
PagedPool,
UL_DEFERRED_REMOVE_ITEM,
UL_DEFERRED_REMOVE_ITEM_POOL_TAG
);
if (pWorker == NULL)
{
return NULL;
}
//
// Initialize the structure.
//
pWorker->Signature = UL_DEFERRED_REMOVE_ITEM_POOL_TAG;
pWorker->UrlSecure = pParsedUrl->Secure;
pWorker->UrlPort = pParsedUrl->PortNumber;
return pWorker;
}
/**************************************************************************++
Routine Description:
This routine does the actual reservation in the CG tree.
Arguments:
pParsedUrl - Supplies the parsed url of the namespace to be reserverd.
pNextToken - Supplies the unparsed portion of the url.
UrlContext - Supplies an opaque associated with this url.
pConfigObject - Supplies a pointer to the config group to which the
url belongs.
pSiteEntry - Supplies a pointer to the site node.
AccessState - Supplies access state of the caller.
DesiredAccess - Supplies access mask of the caller.
RequestorMode - Supplies the processor mode of the caller.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpTreeRegisterNamespace(
IN PHTTP_PARSED_URL pParsedUrl,
IN HTTP_URL_CONTEXT UrlContext,
IN PUL_CONFIG_GROUP_OBJECT pConfigObject,
IN PACCESS_STATE AccessState,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode
)
{
NTSTATUS Status;
PUL_CG_URL_TREE_ENTRY pEntry;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_CG_LOCK_OWNED_WRITE());
ASSERT(pParsedUrl != NULL);
ASSERT(AccessState != NULL);
ASSERT(RequestorMode == UserMode);
Status = UlpTreeAllocateNamespace(
pParsedUrl,
HttpUrlOperatorTypeRegistration,
AccessState,
DesiredAccess,
RequestorMode,
&pEntry
);
if (!NT_SUCCESS(Status))
{
goto end;
}
//
// mark the site
//
ASSERT(pEntry->Registration == FALSE);
pEntry->Registration = TRUE;
//
// context associated with this url
//
pEntry->UrlContext = UrlContext;
//
// link the cfg group + the url
//
ASSERT(pEntry->pConfigGroup == NULL);
ASSERT(pEntry->ConfigGroupListEntry.Flink == NULL);
pEntry->pConfigGroup = pConfigObject;
InsertTailList(&pConfigObject->UrlListHead, &pEntry->ConfigGroupListEntry);
//
// Sanity check.
//
ASSERT(pEntry->pRemoveSiteWorkItem == NULL);
ASSERT(pEntry->SiteAddedToEndpoint == FALSE);
//
// Allocate a work item (initialization is done during allocation.)
//
Status = STATUS_INSUFFICIENT_RESOURCES;
pEntry->pRemoveSiteWorkItem = UlpAllocateDeferredRemoveItem(pParsedUrl);
if (pEntry->pRemoveSiteWorkItem != NULL)
{
//
// Allocation succeeded. Now add pEntry to endpoint list.
//
ASSERT(IS_VALID_DEFERRED_REMOVE_ITEM(pEntry->pRemoveSiteWorkItem));
Status = UlAddSiteToEndpointList(pParsedUrl);
if (NT_SUCCESS(Status))
{
//
// Remember that this entry was added to endpoint list.
//
pEntry->SiteAddedToEndpoint = TRUE;
}
}
if (!NT_SUCCESS(Status))
{
//
// Something went wrong. Need to cleanup this entry.
//
NTSTATUS TempStatus;
ASSERT(pEntry->SiteAddedToEndpoint == FALSE);
//
// Free work item.
//
if (pEntry->pRemoveSiteWorkItem != NULL)
{
ASSERT(IS_VALID_DEFERRED_REMOVE_ITEM(pEntry->pRemoveSiteWorkItem));
UL_FREE_POOL(
pEntry->pRemoveSiteWorkItem,
UL_DEFERRED_REMOVE_ITEM_POOL_TAG
);
pEntry->pRemoveSiteWorkItem = NULL;
pEntry->SiteAddedToEndpoint = FALSE;
}
//
// Delete the registration.
//
TempStatus = UlpTreeDeleteRegistration(pEntry);
ASSERT(NT_SUCCESS(TempStatus));
}
end:
return Status;
}
/**************************************************************************++
Routine Description:
This routine is called for registering a namespace.
Arguments:
pParsedUrl - Supplies the parsed url of the namespace to be reserverd.
UrlContext - Supplies an opaque associated with this url.
pConfigObject - Supplies a pointer to the config group to which the
url belongs.
AccessState - Supplies access state of the caller.
DesiredAccess - Supplies access mask of the caller.
RequestorMode - Supplies the processor mode of the caller.
Return Value:
--**************************************************************************/
NTSTATUS
UlpRegisterUrlNamespace(
IN PHTTP_PARSED_URL pParsedUrl,
IN HTTP_URL_CONTEXT UrlContext,
IN PUL_CONFIG_GROUP_OBJECT pConfigObject,
IN PACCESS_STATE AccessState,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode
)
{
NTSTATUS Status;
BOOLEAN Secure;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_CG_LOCK_OWNED_WRITE());
ASSERT(pParsedUrl != NULL);
ASSERT(pConfigObject != NULL);
ASSERT(AccessState != NULL);
ASSERT(RequestorMode == UserMode);
//
// If there are any reservations on for a different scheme on the same
// port, then fail this registration.
//
Status = UlpQuerySchemeForPort(pParsedUrl->PortNumber, &Secure);
if (NT_SUCCESS(Status) && Secure != pParsedUrl->Secure)
{
//
// Ouch...
//
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
}
//
// Add actual registration.
//
Status = UlpTreeRegisterNamespace(
pParsedUrl,
UrlContext,
pConfigObject,
AccessState,
DesiredAccess,
RequestorMode
);
end:
return Status;
}
/**************************************************************************++
Routine Description:
Given a security descriptor, this routine returns two security
descriptors. One (called captured security descriptor), is the
the caputured and validated version of the input security descriptor.
The other (call prepared security descriptor), is a copy of the
captured security descriptor with the generic access mask bits
in the DACL mapped.
Arguments:
pInSecurityDescriptor - Supplies the input security descriptor to
prepare.
RequestorMode - Supplies the processor mode of the caller.
ppPreparedSecurityDescriptor - Returns a security descriptor that is
captured and mapped.
ppCapturedSecurityDescriptor - Returns the captued security descriptor.
pCapturedSecurityDescriptorLength - Returns the length of the captured
security descriptor.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpPrepareSecurityDescriptor(
IN PSECURITY_DESCRIPTOR pInSecurityDescriptor,
IN KPROCESSOR_MODE RequestorMode,
OUT PSECURITY_DESCRIPTOR * ppPreparedSecurityDescriptor,
OUT PSECURITY_DESCRIPTOR * ppCapturedSecurityDescriptor,
OUT PULONG pCapturedSecurityDescriptorLength
)
{
NTSTATUS Status;
PSECURITY_DESCRIPTOR pSD;
ULONG SDLength;
PSECURITY_DESCRIPTOR pPreparedSD;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pInSecurityDescriptor != NULL);
ASSERT(ppPreparedSecurityDescriptor != NULL);
ASSERT(ppCapturedSecurityDescriptor != NULL);
ASSERT(pCapturedSecurityDescriptorLength != NULL);
//
// Initialize locals.
//
pSD = NULL; // Caputured security descriptor
SDLength = 0; // Caputured security descriptor length
pPreparedSD = NULL;
//
// First capture the security descriptor.
//
Status = SeCaptureSecurityDescriptor(
pInSecurityDescriptor,
RequestorMode,
PagedPool,
TRUE, // force capture
&pSD
);
if (!NT_SUCCESS(Status))
{
pSD = NULL;
goto end;
}
//
// Now validate the security descriptor.
//
if (!RtlValidSecurityDescriptor(pSD))
{
Status = STATUS_INVALID_PARAMETER;
goto end;
}
//
// Calculate the length of the security descriptor.
//
SDLength = RtlLengthSecurityDescriptor(pSD);
//
// Make sure that the security descriptor is self-relative.
//
if (!RtlValidRelativeSecurityDescriptor(pSD, SDLength, 0))
{
Status = STATUS_INVALID_PARAMETER;
goto end;
}
//
// Make a copy of the captured security descriptor.
//
Status = SeCaptureSecurityDescriptor(
pSD,
KernelMode,
PagedPool,
TRUE, // force capture
&pPreparedSD
);
if (!NT_SUCCESS(Status))
{
pPreparedSD = NULL;
goto end;
}
//
// Map generic access to url namespace specific rights.
//
Status = UlMapGenericMask(pPreparedSD);
if (!NT_SUCCESS(Status))
{
goto end;
}
end:
if (!NT_SUCCESS(Status))
{
//
// If necessary, cleanup.
//
if (pSD != NULL)
{
SeReleaseSecurityDescriptor(pSD, RequestorMode, TRUE);
}
if (pPreparedSD != NULL)
{
SeReleaseSecurityDescriptor(pPreparedSD, KernelMode, TRUE);
}
*ppPreparedSecurityDescriptor = NULL;
*ppCapturedSecurityDescriptor = NULL;
*pCapturedSecurityDescriptorLength = 0;
}
else
{
//
// Return values.
//
*ppPreparedSecurityDescriptor = pPreparedSD;
*ppCapturedSecurityDescriptor = pSD;
*pCapturedSecurityDescriptorLength = SDLength;
}
return Status;
}
/***************************************************************************++
Routine Description:
This routine adds a reservation entry to the CG tree and optionally to
registry.
Arguments:
pParsedUrl - Supplies the parsed url of the namespace to be reserved.
pUserSecurityDescriptor - Supplies the security descriptor to be applied
to the namespace.
SecurityDescriptorLength - Supplies the length of the security
descriptor.
AccessState - Supplies the access state of the caller.
DesiredAccess - Supplies the access mask of the caller.
RequestorMode - Supplies the mode of the caller.
bPersiste - Supplies the flag to force a write to the registry.
Return Value:
NTSTATUS.
--***************************************************************************/
NTSTATUS
UlpAddReservationEntry(
IN PHTTP_PARSED_URL pParsedUrl,
IN PSECURITY_DESCRIPTOR pUserSecurityDescriptor,
IN ULONG SecurityDescriptorLength,
IN PACCESS_STATE AccessState,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode,
IN BOOLEAN bPersist
)
{
NTSTATUS Status;
PSECURITY_DESCRIPTOR pSecurityDescriptor;
PSECURITY_DESCRIPTOR pCapturedSecurityDescriptor;
ULONG CapturedSecurityDescriptorLength;
UNREFERENCED_PARAMETER(SecurityDescriptorLength);
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_CG_LOCK_OWNED_WRITE());
ASSERT(IS_VALID_HTTP_PARSED_URL(pParsedUrl));
//
// Prepare security descriptor.
//
Status = UlpPrepareSecurityDescriptor(
pUserSecurityDescriptor,
RequestorMode,
&pSecurityDescriptor,
&pCapturedSecurityDescriptor,
&CapturedSecurityDescriptorLength
);
if (!NT_SUCCESS(Status))
{
pSecurityDescriptor = NULL;
pCapturedSecurityDescriptor = NULL;
CapturedSecurityDescriptorLength = 0;
goto Cleanup;
}
//
// Try reserving for the namespace.
//
Status = UlpReserveUrlNamespace(
pParsedUrl,
pSecurityDescriptor,
AccessState,
DesiredAccess,
RequestorMode
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Security descriptor will be freed with the reservation entry.
//
pSecurityDescriptor = NULL;
//
// If we are required to write this entry to registry, do it now.
//
if (bPersist)
{
//
// Use the captured security descriptor while writing to the
// registry.
//
Status = UlpUpdateReservationInRegistry(
TRUE,
pParsedUrl,
pCapturedSecurityDescriptor,
CapturedSecurityDescriptorLength
);
if (!NT_SUCCESS(Status))
{
//
// failed to write to registry. now delete reservation.
//
UlpDeleteReservationEntry(
pParsedUrl,
AccessState,
DesiredAccess,
RequestorMode
);
}
else
{
//
// Successful reservation. Write an event log entry.
//
UlEventLogOneStringEntry(
EVENT_HTTP_NAMESPACE_RESERVED,
pParsedUrl->pFullUrl,
FALSE, // don't write error code
STATUS_SUCCESS // don't care
);
}
}
Cleanup:
if (!NT_SUCCESS(Status) && pSecurityDescriptor != NULL)
{
SeReleaseSecurityDescriptor(pSecurityDescriptor, RequestorMode, TRUE);
}
if (pCapturedSecurityDescriptor != NULL)
{
SeReleaseSecurityDescriptor(
pCapturedSecurityDescriptor,
KernelMode,
TRUE
);
}
return Status;
}
/**************************************************************************++
Routine Description:
This routine delete a valid reservation both from CG tree and registry.
Arguments:
pParsedUrl - Supplies parsed url of the reservation to be deleted.
AccessState - Supplies the access state of the caller.
DesiredAccess - Supplies the access mask of the caller.
RequestorMode - Supplies the processor mode of the caller.
Return Value:
NTSTATUS.
--**************************************************************************/
NTSTATUS
UlpDeleteReservationEntry(
IN PHTTP_PARSED_URL pParsedUrl,
IN PACCESS_STATE AccessState,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode
)
{
NTSTATUS Status;
PUL_CG_URL_TREE_ENTRY pEntry;
PUL_CG_URL_TREE_ENTRY pAncestor;
PSECURITY_DESCRIPTOR pSD;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_CG_LOCK_OWNED_WRITE());
ASSERT(IS_VALID_HTTP_PARSED_URL(pParsedUrl));
ASSERT(AccessState != NULL);
ASSERT(RequestorMode == UserMode);
//
// Find the reservation entry.
//
Status = UlpTreeFindReservationNode(pParsedUrl->pFullUrl, &pEntry);
if (!NT_SUCCESS(Status))
{
//
// Too bad...did not find a matching entry.
//
goto end;
}
//
// Sanity check.
//
ASSERT(IS_VALID_TREE_ENTRY(pEntry));
ASSERT(pEntry->Reservation == TRUE);
//
// Find the closest ancestor that is a reservation.
//
pAncestor = pEntry->pParent;
while (pAncestor != NULL && pAncestor->Reservation == FALSE)
{
pAncestor = pAncestor->pParent;
}
//
// Did we find a suitable ancestor?
//
if (pAncestor == NULL)
{
//
// Nope. Assume the default security descriptor.
//
pSD = g_pAdminAllSystemAll;
}
else
{
//
// Good. We found an ancestor reservation. Pick sd from there.
//
ASSERT(IS_VALID_TREE_ENTRY(pAncestor));
ASSERT(pAncestor->Reservation == TRUE);
ASSERT(pAncestor->pSecurityDescriptor != NULL);
pSD = pAncestor->pSecurityDescriptor;
}
//
// Sanity check.
//
ASSERT(pSD != NULL && RtlValidSecurityDescriptor(pSD));
//
// Perform access check.
//
Status = UlpNamespaceAccessCheck(
pSD,
AccessState,
DesiredAccess,
RequestorMode,
pParsedUrl->pFullUrl
);
if (NT_SUCCESS(Status))
{
//
// Permission granted. Delete the reservation from registry.
//
Status = UlpUpdateReservationInRegistry(
FALSE, // delete
pParsedUrl, // url to delete
NULL, // must be NULL
0 // must be 0
);
if (NT_SUCCESS(Status))
{
//
// Remove the reservation from the CG url tree.
//
Status = UlpTreeDeleteReservation(pEntry);
ASSERT(NT_SUCCESS(Status));
//
// Successful deletion. Now unbind the scheme from port.
//
Status = UlpUnbindSchemeFromPort(
pParsedUrl->Secure,
pParsedUrl->PortNumber
);
ASSERT(NT_SUCCESS(Status));
//
// Successful deletion. Write an event log entry.
//
UlEventLogOneStringEntry(
EVENT_HTTP_NAMESPACE_DERESERVED,
pParsedUrl->pFullUrl,
FALSE, // don't write error code
STATUS_SUCCESS // unused
);
}
}
end:
return Status;
}