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
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;
|
|
}
|
|
|