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.
 
 
 
 
 
 

1131 lines
39 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
netroot.c
Abstract:
This module implements the user mode DAV miniredir routine(s) pertaining to
the CreateVNetRoot call.
Author:
Rohan Kumar [RohanK] 1-Sept-2000
Revision History:
--*/
#include "pch.h"
#pragma hdrstop
#include "ntumrefl.h"
#include "usrmddav.h"
#include "global.h"
#include "nodefac.h"
#include "UniUtf.h"
#include <wincrypt.h>
//
// Mentioned below are the custom OFFICE and TAHOE headers which will be
// returned in the response to a PROPFIND request.
//
WCHAR *DavTahoeCustomHeader = L"MicrosoftTahoeServer";
WCHAR *DavOfficeCustomHeader = L"MicrosoftOfficeWebServer";
ULONG
DavFsCreateVNetRoot(
PDAV_USERMODE_WORKITEM DavWorkItem
)
/*++
Routine Description:
This routine handles CreateVNetRoot requests for the DAV Mini-Redir that
get reflected from the kernel.
Arguments:
DavWorkItem - The buffer that contains the request parameters and options.
Return Value:
The return status for the operation
--*/
{
ULONG WStatus = ERROR_SUCCESS;
PWCHAR ServerName = NULL, ShareName = NULL, CanName = NULL;
ULONG_PTR CallBackContext = (ULONG_PTR)0;
BOOL EnCriSec = FALSE, CallBackContextInitialized = FALSE;
BOOL didICreateUserEntry = FALSE;
ULONG ServerID = 0;
PPER_USER_ENTRY PerUserEntry = NULL;
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
HINTERNET DavConnHandle, DavOpenHandle;
PDAV_USERMODE_CREATE_V_NET_ROOT_REQUEST CreateVNetRootRequest = NULL;
BOOL didImpersonate = FALSE;
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
BOOL BStatus = FALSE;
BOOL UserEntryExists = FALSE;
//
// Get the request buffer from the DavWorkItem.
//
CreateVNetRootRequest = &(DavWorkItem->CreateVNetRootRequest);
//
// The first character is a '\' which has to be stripped.
//
ServerName = &(CreateVNetRootRequest->ServerName[1]);
if (!ServerName) {
DavPrint((DEBUG_ERRORS, "DavFsCreateVNetRoot: ServerName is NULL.\n"));
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: ServerName = %ws.\n", ServerName));
ServerID = CreateVNetRootRequest->ServerID;
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: ServerID = %d.\n", ServerID));
//
// The first character is a '\' which has to be stripped.
//
ShareName = &(CreateVNetRootRequest->ShareName[1]);
if (!ShareName) {
DavPrint((DEBUG_ERRORS, "DavFsCreateVNetRoot: ShareName is NULL.\n"));
WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: ShareName = %ws.\n", ShareName));
//
// If ShareName is a dummy share, we need to remove it right now before we
// contact the server.
//
DavRemoveDummyShareFromFileName(ShareName);
DavPrint((DEBUG_MISC,
"DavFsCreateVNetRoot: LogonId.LowPart = %d, LogonId.HighPart = %d\n",
CreateVNetRootRequest->LogonID.LowPart,
CreateVNetRootRequest->LogonID.HighPart));
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
//
// If we are using WinInet synchronously, then we need to impersonate the
// clients context now.
//
#ifndef DAV_USE_WININET_ASYNCHRONOUSLY
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/UMReflectorImpersonate. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
didImpersonate = TRUE;
#endif
//
// We need to call this only if "DAV_USE_WININET_ASYNCHRONOUSLY" has been
// defined. Otherwise, if we are using WinInet synchronously, then we
// would have already done this in the DavWorkerThread function. This
// ultimately gets deleted (the impersonation token that is) in the
// DavAsyncCreateCompletion function.
//
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
//
// Set the DavCallBackContext.
//
WStatus = DavFsSetTheDavCallBackContext(DavWorkItem);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/DavFsSetTheDavCallBackContext. "
"Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
CallBackContextInitialized = TRUE;
//
// Store the address of the DavWorkItem which serves as a callback in the
// variable CallBackContext. This will now be used in all the async calls
// that follow.
//
CallBackContext = (ULONG_PTR)(DavWorkItem);
#endif
//
// Allocate memory for the INTERNET_ASYNC_RESULT structure.
//
DavWorkItem->AsyncResult = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
sizeof(INTERNET_ASYNC_RESULT));
if (DavWorkItem->AsyncResult == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/LocalAlloc. Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Find out whether we already have a "InternetConnect" handle to the
// server. One could have been created during the CreateSrvCall process.
// We can check the per user entries hanging off this server to see if an
// entry for this user exists. If it does, use the InternetConnect handle
// to do the HttpOpen. Otherwise, create and entry for this user and add it
// to the list of the per user entries of the server.
//
//
// Now check whether this user has an entry hanging off the server entry in
// the hash table. Obviously, we have to take a lock before accessing the
// server entries of the hash table.
//
EnterCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = TRUE;
UserEntryExists = DavDoesUserEntryExist(ServerName,
ServerID,
&(CreateVNetRootRequest->LogonID),
&PerUserEntry,
&ServerHashEntry);
//
// If the CreateVNetRoot gets cancelled in the kernel after the CreateSrvCall
// succeeds, then you could get the FinalizeSrvCall go through if the thread
// that picks up the CreateVNetRoot request gets preempted. This removes the
// entry from the ServerHashList. We need to check for the value of
// ServerHashEntry being NULL before proceeding further while Creating the
// PerUserEntry below. Since this condition can only arise if the operation
// has been cancelled we return ERROR_CANCELLED. Actually the return value
// doesn't matter since the kernel request has already been cancelled.
//
if (ServerHashEntry == NULL) {
WStatus = ERROR_CANCELLED;
DavPrint((DEBUG_ERRORS, "DavFsCreateVNetRoot: ServerHashEntry == NULL\n"));
goto EXIT_THE_FUNCTION;
}
DavWorkItem->AsyncCreateVNetRoot.ServerHashEntry = ServerHashEntry;
if (!UserEntryExists) {
//
// The user entry was not found, so we need to create one.
//
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: UserEntryNotFound. Calling InternetConnect\n"));
PerUserEntry = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(PER_USER_ENTRY));
if (PerUserEntry == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/LocalAlloc. Error Val = %d.\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// Yes, I created this UserEntry. If I fail now, I need to finalize twice.
// This is because, we don't want to keep this entry if we fail.
//
didICreateUserEntry = TRUE;
//
// Add the user entry to the per user list of the server.
//
InsertHeadList(&(ServerHashEntry->PerUserEntry), &(PerUserEntry->UserEntry));
//
// Take a reference on the ServerHashEntry. This ServerHashEntry needs
// to be valid as long as this PerUserEntry is in use. With the logic
// of cancellation that has been added to the kernel mode this can no
// longer be guaranteed by the RDBSS logic. As an example, you can
// get a FinalizeSrvCall while a usermode thread is creating a NetRoot,
// because the CreateVNetRoot in the kernel got cancelled since the
// usermode thread that was handling the CreateVNetRoot call took a
// long time. You want the ServerHashEntry to hang around till all the
// PerUserEntries associated with it are in use.
//
ServerHashEntry->ServerEntryRefCount++;
//
// Back pointer to the Server hash entry.
//
PerUserEntry->ServerHashEntry = ServerHashEntry;
PerUserEntry->UserEntryState = UserEntryInitializing;
//
// Set the value of Reference count to 1. This value is decremented
// when the finalization of this VNetRoot happens.
//
PerUserEntry->UserEntryRefCount = 1;
//
// We keep track of the fact that we took a reference on this
// PerUserEntry.
//
DavWorkItem->AsyncCreateVNetRoot.didITakeReference = TRUE;
//
// Copy the LogonID.
//
PerUserEntry->LogonID.LowPart = CreateVNetRootRequest->LogonID.LowPart;
PerUserEntry->LogonID.HighPart = CreateVNetRootRequest->LogonID.HighPart;
//
// Create a event which has to be manually set to non-signalled state and
// set it to "not signalled".
//
PerUserEntry->UserEventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
if (PerUserEntry->UserEventHandle == NULL) {
//
// Set the state of the entry to error in initialization.
//
PerUserEntry->UserEntryState = UserEntryInitializationError;
WStatus = GetLastError();
PerUserEntry->ErrorStatus = WStatus;
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/CreateEvent. Error Val = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
if (wcslen(DavWorkItem->UserName)) {
PerUserEntry->UserName = LocalAlloc((LMEM_FIXED | LMEM_ZEROINIT),
(wcslen(DavWorkItem->UserName) + 1) * sizeof(WCHAR));
if (PerUserEntry->UserName == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/LocalAlloc: Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
wcscpy(PerUserEntry->UserName, DavWorkItem->UserName);
}
if (wcslen(DavWorkItem->Password)) {
DWORD NumOfBlocks = 0, BlockSizeInBytes = 0, PasswordLenInBytes = 0;
BOOL ReturnVal = FALSE;
PasswordLenInBytes = ( (wcslen(DavWorkItem->Password) + 1) * sizeof(WCHAR) );
NumOfBlocks = ( (PasswordLenInBytes / CRYPTPROTECTMEMORY_BLOCK_SIZE) + 1 );
BlockSizeInBytes = (NumOfBlocks * CRYPTPROTECTMEMORY_BLOCK_SIZE);
PerUserEntry->Password = LocalAlloc((LMEM_FIXED | LMEM_ZEROINIT), BlockSizeInBytes);
if (PerUserEntry->Password == NULL) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/LocalAlloc: Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
wcscpy(PerUserEntry->Password, DavWorkItem->Password);
ReturnVal = CryptProtectMemory(PerUserEntry->Password, BlockSizeInBytes, CRYPTPROTECTMEMORY_SAME_PROCESS);
if (!ReturnVal) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/CryptProtectMemory: Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
PerUserEntry->BlockSizeInBytes = BlockSizeInBytes;
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: NumOfBlocks = %d\n", NumOfBlocks));
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: BlockSizeInBytes = %d\n", BlockSizeInBytes));
DavPrint((DEBUG_MISC,
"DavFsCreateVNetRoot: PlainPassword = %ws, EncryptedPassword = %ws\n",
DavWorkItem->Password, PerUserEntry->Password));
} else {
PerUserEntry->BlockSizeInBytes = 0;
}
} else {
if ( PerUserEntry->UserName && wcslen(DavWorkItem->UserName) ) {
if (wcscmp(PerUserEntry->UserName, DavWorkItem->UserName) != 0) {
WStatus = ERROR_SESSION_CREDENTIAL_CONFLICT;
goto EXIT_THE_FUNCTION;
}
}
}
//
// If the user entry did not exist, we would have created one by now.
//
ASSERT(PerUserEntry != NULL);
DavWorkItem->AsyncCreateVNetRoot.PerUserEntry = PerUserEntry;
//
// We enter the following if under two conditions.
// 1. If the DavConnHandle is not NULL. This means that some other thread
// is either in the process of completing the VNetRoot create or that the
// VNetRoot create has already completed and we have a DavConnHandle
// which can be used to issue the Http query. If the handle is in the
// process if being created then we wait since the thread that is
// creating the handle will finally signal when its done.
// 2. DavConnHandle is NULL, but the UserEntryState is UserEntryInitializing
// and this thread did not create this user entry. This means that some
// other thread which created the user entry or which took the created
// user entry in UserEntryAllocated state is in the process of completing
// the VNetRoot create. Once this is done the DavConnHandle will be
// available to issue the Http queries.
//
if ( ( PerUserEntry->DavConnHandle != NULL ||
( PerUserEntry->UserEntryState == UserEntryInitializing &&
didICreateUserEntry == FALSE ) ) ) {
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: PerUserEntry->DavConnHandle != NULL\n"));
//
// If the code comes here, it imples that this thread did not create
// the PerUserEntry. This is because, if we created the PerUserEntry
// above, PerUserEntry->DavConnHandle will be NULL and no one would have
// changed it since we are still holding the CriticalSection lock.
//
ASSERT(didICreateUserEntry == FALSE);
//
// We need to increment the reference count on the PerUserEntry since
// this VNetRoot create is for a different share than the one for which
// a thread is currently creating or has already created the WinInet
// InternetConnect handle.
//
PerUserEntry->UserEntryRefCount++;
//
// We keep track of the fact that we took a reference on this
// PerUserEntry.
//
DavWorkItem->AsyncCreateVNetRoot.didITakeReference = TRUE;
//
// An entry does exist. But, we need to take the next step depending
// upon the state of this entry.
//
//
// If its initializing, then I need to free the lock and wait on the
// event.
//
if (PerUserEntry->UserEntryState == UserEntryInitializing) {
DWORD WaitStatus;
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
WaitStatus = WaitForSingleObject(PerUserEntry->UserEventHandle, INFINITE);
if (WaitStatus == WAIT_FAILED) {
WStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/WaitForSingleObject. Error Val = %d.\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
ASSERT(WaitStatus == WAIT_OBJECT_0);
}
//
// We could have left the lock while waiting on an event. If we have,
// then we need to acquire it back before proceeding further.
//
if (!EnCriSec) {
EnterCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = TRUE;
}
if (PerUserEntry->UserEntryState == UserEntryClosing) {
DavPrint((DEBUG_ERRORS, "DavFsCreateVNetRoot: UserEntryClosing.\n"));
WStatus = ERROR_INVALID_PARAMETER;
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
goto EXIT_THE_FUNCTION;
}
if (PerUserEntry->UserEntryState == UserEntryInitializationError) {
DavPrint((DEBUG_ERRORS, "DavFsCreateVNetRoot: UserEntryInitializationError\n"));
WStatus = PerUserEntry->ErrorStatus;
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
goto EXIT_THE_FUNCTION;
}
ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized);
//
// Since its initialized, the DavConnHandle should be OK.
//
ASSERT(PerUserEntry->DavConnHandle != NULL);
DavConnHandle = PerUserEntry->DavConnHandle;
//
// And yes, we obviously have to leave the critical section
// before returning.
//
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
} else {
//
// If we come here, it means that the PerUserEntry has been created, but
// the InternetConnect handle has not. We could have created the user
// entry above or it could have been created in the Passport Auth code
// which creates PerUserEnrty to store cookies, but does not do the
// InternetConnect.
//
if (PerUserEntry->UserEntryState == UserEntryInitializing) {
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: PerUserEntry->UserEntryState == UserEntryInitializing\n"));
ASSERT(didICreateUserEntry == TRUE);
} else {
//
// This entry was created to store the Passport cookies and was not
// created above. We need to add a reference to the PerUserEntry here
// since this user entry was created in the DavAddEntriesForPassportCookies
// routine. This reference count will be decremented when the
// finalization of this VNetRoot happens.
//
PerUserEntry->UserEntryRefCount++;
//
// We keep track of the fact that we took a reference on this
// PerUserEntry.
//
DavWorkItem->AsyncCreateVNetRoot.didITakeReference = TRUE;
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: PerUserEntry->UserEntryState != UserEntryInitializing\n"));
ASSERT(PerUserEntry->UserEntryState == UserEntryAllocated);
PerUserEntry->UserEntryState = UserEntryInitializing;
}
//
// We don't need to hold the CriticalSection anymore.
//
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
//
// Need to set the DavOperation field before submitting the asynchronous
// request. This is a internet connect operation.
//
DavWorkItem->DavOperation = DAV_CALLBACK_INTERNET_CONNECT;
//
// Create a handle to connect to a HTTP/DAV server.
//
DavConnHandle = InternetConnectW(IHandle,
(LPCWSTR)ServerName,
INTERNET_DEFAULT_HTTP_PORT,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
0,
CallBackContext);
if (DavConnHandle == NULL) {
WStatus = GetLastError();
if (WStatus != ERROR_IO_PENDING) {
//
// Set the state of the entry to error in initialization.
//
EnterCriticalSection( &(HashServerEntryTableLock) );
PerUserEntry->UserEntryState = UserEntryInitializationError;
PerUserEntry->ErrorStatus = WStatus;
SetEvent(PerUserEntry->UserEventHandle);
LeaveCriticalSection( &(HashServerEntryTableLock) );
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/InternetConnect. Error Val = %d\n", WStatus));
}
goto EXIT_THE_FUNCTION;
}
//
// Cache the InternetConnect handle in the PerUserEntry struct.
//
PerUserEntry->DavConnHandle = DavConnHandle;
//
// If we fail after this stage, we can keep the PerUserEntry since the
// InternetConnect handle has already been stored successfully.
//
didICreateUserEntry = FALSE;
}
WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
DavPrint((DEBUG_ERRORS,
"DavFsCreateVNetRoot/DavAsyncCommonStates. Error Val = %08lx\n",
WStatus));
}
EXIT_THE_FUNCTION:
if (EnCriSec) {
LeaveCriticalSection( &(HashServerEntryTableLock) );
EnCriSec = FALSE;
}
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
//
// Some resources should not be freed if we are returning ERROR_IO_PENDING
// because they will be used in the callback functions.
//
if (WStatus != ERROR_IO_PENDING) {
//
// Set the return status of the operation. This is used by the kernel
// mode routines to figure out the completion status of the user mode
// request.
//
if (WStatus != ERROR_SUCCESS) {
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
} else {
DavWorkItem->Status = STATUS_SUCCESS;
}
DavAsyncCreateVNetRootCompletion(DavWorkItem);
} else {
DavPrint((DEBUG_MISC, "DavFsCreateVNetRoot: Returning ERROR_IO_PENDING.\n"));
}
#else
//
// If we are using WinInet synchronously, then we should never get back
// ERROR_IO_PENDING from WinInet.
//
ASSERT(WStatus != ERROR_IO_PENDING);
//
// If this thread impersonated a user, we need to revert back.
//
if (didImpersonate) {
RevertToSelf();
}
//
// Set the return status of the operation. This is used by the kernel
// mode routines to figure out the completion status of the user mode
// request. This is done here because the async completion routine that is
// called immediately afterwards needs the status set.
//
if (WStatus != ERROR_SUCCESS) {
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
} else {
DavWorkItem->Status = STATUS_SUCCESS;
}
DavAsyncCreateVNetRootCompletion(DavWorkItem);
#endif
return WStatus;
}
DWORD
DavAsyncCreateVNetRoot(
PDAV_USERMODE_WORKITEM DavWorkItem,
BOOLEAN CalledByCallBackThread
)
/*++
Routine Description:
This is the callback routine for the CreateVNetRoot operation.
Arguments:
DavWorkItem - The DAV_USERMODE_WORKITEM value.
CalledByCallbackThread - TRUE, if this function was called by the thread
which picks of the DavWorkItem from the Callback
function. This happens when an Async WinInet call
returns ERROR_IO_PENDING and completes later.
Return Value:
ERROR_SUCCESS or the appropriate error value.
--*/
{
ULONG WStatus = ERROR_SUCCESS;
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL;
BOOL didImpersonate = FALSE;
HINTERNET DavOpenHandle = NULL;
BOOL ReturnVal = FALSE;
ULONG TahoeCustomHeaderLength = 0, OfficeCustomHeaderLength = 0;
PDAV_USERMODE_CREATE_V_NET_ROOT_RESPONSE CreateVNetRootResponse = NULL;
WCHAR DavCustomBuffer[100];
DAV_FILE_ATTRIBUTES DavFileAttributes;
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
CreateVNetRootResponse = &(DavWorkItem->CreateVNetRootResponse);
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
if (CalledByCallBackThread) {
//
// We are running in the context of a worker thread which has different
// credentials than the user that initiated the I/O request. Before
// proceeding further, we should impersonate the user that initiated the
// request.
//
WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle);
if (WStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRoot/UMReflectorImpersonate. "
"Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
}
didImpersonate = TRUE;
//
// Before proceeding further, check to see if the Async operation failed.
// If it did, then cleanup and move on.
//
if ( !DavWorkItem->AsyncResult->dwResult ) {
WStatus = DavWorkItem->AsyncResult->dwError;
//
// If the error we got back is ERROR_INTERNET_FORCE_RETRY, then
// WinInet is trying to authenticate itself with the server. In
// such a scenario this is what happens.
//
// Client ----Request-----> Server
// Server ----AccessDenied-----> Client
// Client----Challenge Me-------> Server
// Server-----Challenge--------> Client
// Client-----Challenge Resp----> Server
//
if (WStatus == ERROR_INTERNET_FORCE_RETRY) {
ASSERT(DavWorkItem->DavOperation == DAV_CALLBACK_HTTP_END);
//
// We need to repeat the HttpSend and HttpEnd request calls.
//
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN;
WStatus = DavAsyncCommonStates(DavWorkItem, FALSE);
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRoot/DavAsyncCommonStates. "
"Error Val = %08lx\n", WStatus));
}
} else {
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRoot. AsyncFunction failed. "
"Error Val = %d\n", WStatus));
}
goto EXIT_THE_FUNCTION;
}
}
#else
ASSERT(CalledByCallBackThread == FALSE);
#endif
DavOpenHandle = DavWorkItem->AsyncCreateVNetRoot.DavOpenHandle;
WStatus = DavQueryAndParseResponse(DavOpenHandle);
if (WStatus != ERROR_SUCCESS) {
//
// The PROPFIND request that was sent to the server failed.
//
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRoot/DavQueryAndParseResponse. "
"WStatus = %d\n", WStatus));
goto EXIT_THE_FUNCTION;
}
//
// We read the value of AcceptOfficeAndTahoeServers from the registry when
// the WebClient service starts up. If this is set to 0, it means that we
// should be rejecting OfficeWebServers, Tahoe servers and the shares on
// these servers even though they speak DAV. We do this since WebFolders
// needs to claim this name and Shell will only call into WebFolders if the
// DAV Redir fails. If this value is non-zero, we accept all servers that
// speak DAV.
//
//
if (AcceptOfficeAndTahoeServers == 0) {
//
// Figure out if this is an OFFICE Web Server share. If it is then the
// response will have an entry "MicrosoftOfficeWebServer: ", in the header.
// If this is an OFFICE share then we should not claim it since the user
// actually intends to use the OFFICE specific features in Shell.
//
RtlZeroMemory(DavCustomBuffer, sizeof(DavCustomBuffer));
wcscpy(DavCustomBuffer, DavOfficeCustomHeader);
OfficeCustomHeaderLength = ( sizeof(DavCustomBuffer) / sizeof(WCHAR) );
ReturnVal = HttpQueryInfoW(DavOpenHandle,
HTTP_QUERY_CUSTOM,
(PVOID)DavCustomBuffer,
&(OfficeCustomHeaderLength),
NULL);
if ( !ReturnVal ) {
WStatus = GetLastError();
if (WStatus != ERROR_HTTP_HEADER_NOT_FOUND) {
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRoot/HttpQueryInfoW: Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
} else {
WStatus = ERROR_SUCCESS;
DavPrint((DEBUG_MISC, "DavAsyncCreateVNetRoot: NOT OFFICE Share\n"));
CreateVNetRootResponse->isOfficeShare = FALSE;
}
} else {
DavPrint((DEBUG_MISC, "DavAsyncCreateVNetRoot: OFFICE Share\n"));
CreateVNetRootResponse->isOfficeShare = TRUE;
}
//
// Figure out if this is a TAHOE share. If it is then the response will have
// an entry "MicrosoftTahoeServer: ", in the header. If this is a TAHOE share
// then we should not claim it since the user actually intends to use the
// TAHOE specific features in Rosebud.
//
RtlZeroMemory(DavCustomBuffer, sizeof(DavCustomBuffer));
wcscpy(DavCustomBuffer, DavTahoeCustomHeader);
TahoeCustomHeaderLength = ( sizeof(DavCustomBuffer) / sizeof(WCHAR) );
ReturnVal = HttpQueryInfoW(DavOpenHandle,
HTTP_QUERY_CUSTOM,
(PVOID)DavCustomBuffer,
&(TahoeCustomHeaderLength),
NULL);
if ( !ReturnVal ) {
WStatus = GetLastError();
if (WStatus != ERROR_HTTP_HEADER_NOT_FOUND) {
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRoot/HttpQueryInfoW: Error Val = %d\n",
WStatus));
goto EXIT_THE_FUNCTION;
} else {
WStatus = ERROR_SUCCESS;
DavPrint((DEBUG_MISC, "DavAsyncCreateVNetRoot: NOT TAHOE Share\n"));
CreateVNetRootResponse->isTahoeShare = FALSE;
}
} else {
DavPrint((DEBUG_MISC, "DavAsyncCreateVNetRoot: TAHOE Share\n"));
CreateVNetRootResponse->isTahoeShare = TRUE;
}
}
CreateVNetRootResponse->fAllowsProppatch = TRUE;
#if 0
WStatus = DavTestProppatch(DavWorkItem,
DavWorkItem->AsyncCreateVNetRoot.PerUserEntry->DavConnHandle,
DavWorkItem->CreateVNetRootRequest.ShareName)
if (WStatus != NO_ERROR) {
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRoot/DavTestPropatch. WStatus = %d \n",
WStatus));
if (WStatus == HTTP_STATUS_BAD_METHOD) {
CreateVNetRootResponse->fAllowsProppatch = FALSE;
}
WStatus = STATUS_SUCCESS;
}
#endif
WStatus = DavParseXmlResponse(DavOpenHandle, &DavFileAttributes, NULL);
if (WStatus == ERROR_SUCCESS) {
CreateVNetRootResponse->fReportsAvailableSpace = DavFileAttributes.fReportsAvailableSpace;
DavFinalizeFileAttributesList(&DavFileAttributes, FALSE);
}
EXIT_THE_FUNCTION:
//
// If we did impersonate, we need to revert back.
//
if (didImpersonate) {
ULONG RStatus;
RStatus = UMReflectorRevert(UserWorkItem);
if (RStatus != ERROR_SUCCESS) {
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRoot/UMReflectorRevert. Error Val = %d\n",
RStatus));
}
}
#ifdef DAV_USE_WININET_ASYNCHRONOUSLY
//
// Some resources should not be freed if we are returning ERROR_IO_PENDING
// because they will be used in the callback functions.
//
if ( WStatus != ERROR_IO_PENDING && CalledByCallBackThread ) {
//
// Set the return status of the operation. This is used by the kernel
// mode routines to figure out the completion status of the user mode
// request.
//
if (WStatus != ERROR_SUCCESS) {
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
} else {
DavWorkItem->Status = STATUS_SUCCESS;
}
//
// Call the DavAsyncCreateVNetRootCompletion routine.
//
DavAsyncCreateVNetRootCompletion(DavWorkItem);
//
// This thread now needs to send the response back to the kernel. It
// does not wait in the kernel (to get another request) after submitting
// the response.
//
UMReflectorCompleteRequest(DavReflectorHandle, UserWorkItem);
} else {
DavPrint((DEBUG_MISC, "DavAsyncCreateVNetRoot: Returning ERROR_IO_PENDING.\n"));
}
#endif
return WStatus;
}
VOID
DavAsyncCreateVNetRootCompletion(
PDAV_USERMODE_WORKITEM DavWorkItem
)
/*++
Routine Description:
This routine handles the CreateVNetRoot completion. It basically frees up the
resources allocated during the operation.
Arguments:
DavWorkItem - The DAV_USERMODE_WORKITEM value.
Return Value:
none.
--*/
{
if (DavWorkItem->AsyncCreateVNetRoot.DavOpenHandle != NULL) {
BOOL ReturnVal;
ULONG FreeStatus;
HINTERNET DavOpenHandle = DavWorkItem->AsyncCreateVNetRoot.DavOpenHandle;
ReturnVal = InternetCloseHandle( DavOpenHandle );
if (!ReturnVal) {
FreeStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRootCompletion/InternetCloseHandle. Error Val "
"= %d\n", FreeStatus));
}
}
if (DavWorkItem->AsyncResult != NULL) {
HLOCAL FreeHandle;
ULONG FreeStatus;
FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncResult);
if (FreeHandle != NULL) {
FreeStatus = GetLastError();
DavPrint((DEBUG_ERRORS,
"DavAsyncCreateVNetRootCompletion/LocalFree. Error Val = %d\n",
FreeStatus));
}
}
DavFsFinalizeTheDavCallBackContext(DavWorkItem);
//
// If we did not succeed, then we need to finalize this PerUserEntry. Also,
// we only do this if we took a reference on this in the first place.
//
if (DavWorkItem->Status != STATUS_SUCCESS) {
if ( (DavWorkItem->AsyncCreateVNetRoot.PerUserEntry) &&
(DavWorkItem->AsyncCreateVNetRoot.didITakeReference) ) {
DavFinalizePerUserEntry( &(DavWorkItem->AsyncCreateVNetRoot.PerUserEntry) );
}
}
return;
}
ULONG
DavFsFinalizeVNetRoot(
PDAV_USERMODE_WORKITEM DavWorkItem
)
/*++
Routine Description:
This routine handles FinalizeVNetRoot requests for the DAV Mini-Redir that
get reflected from the kernel.
Arguments:
DavWorkItem - The buffer that contains the request parameters and options.
Return Value:
The return status for the operation
--*/
{
ULONG WStatus = ERROR_SUCCESS;
PDAV_USERMODE_FINALIZE_V_NET_ROOT_REQUEST DavFinalizeVNetRootRequest = NULL;
PPER_USER_ENTRY PerUserEntry = NULL;
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
PWCHAR ServerName = NULL;
BOOL ReturnVal = FALSE;
DavFinalizeVNetRootRequest = &(DavWorkItem->FinalizeVNetRootRequest);
ServerName = DavFinalizeVNetRootRequest->ServerName;
//
// If the server name is NULL, return.
//
if (ServerName == NULL) {
DavPrint((DEBUG_ERRORS, "DavFsFinalizeVNetRoot: ServerName == NULL\n"));
WStatus = ERROR_INVALID_PARAMETER;
goto EXIT_THE_FUNCTION;
}
DavPrint((DEBUG_MISC, "DavFsFinalizeVNetRoot: ServerName: %ws.\n", ServerName));
DavPrint((DEBUG_MISC,
"DavFsFinalizeVNetRoot: LogonId.LowPart = %d, LogonId.HighPart = %d\n",
DavFinalizeVNetRootRequest->LogonID.LowPart,
DavFinalizeVNetRootRequest->LogonID.HighPart));
//
// Now check whether this user has an entry hanging off the server entry in
// the hash table. Obviously, we have to take a lock before accessing the
// server entries of the hash table.
//
EnterCriticalSection( &(HashServerEntryTableLock) );
ReturnVal = DavDoesUserEntryExist(ServerName,
DavFinalizeVNetRootRequest->ServerID,
&(DavFinalizeVNetRootRequest->LogonID),
&(PerUserEntry),
&(ServerHashEntry));
//
// Since we are finalizing the PerUserEntry, its important that this entry
// exists. This means that the following ASSERTs are TRUE. This is because
// till a VNetRoot for this server exists for this user in the kernel, we
// keep the PerUserEntry alive.
//
ASSERT(ReturnVal == TRUE);
ASSERT(ServerHashEntry != NULL);
ASSERT(PerUserEntry != NULL);
//
// Finalize the PerUserEntry. The function below will free the PerUserEntry
// if the reference count goes to zero.
//
DavFinalizePerUserEntry( &(PerUserEntry) );
//
// We are done finalizing the entry so we can leave the critical section
// now.
//
LeaveCriticalSection( &(HashServerEntryTableLock) );
EXIT_THE_FUNCTION:
//
// Set the return status of the operation. This is used by the kernel
// mode routines to figure out the completion status of the user mode
// request. This is done here because the async completion routine that is
// called immediately afterwards needs the status set.
//
if (WStatus != ERROR_SUCCESS) {
DavWorkItem->Status = DavMapErrorToNtStatus(WStatus);
} else {
DavWorkItem->Status = STATUS_SUCCESS;
}
return WStatus;
}