/*++ Copyright (c) 1999 Microsoft Corporation Module Name: davcreat.c Abstract: This module implements the user mode DAV miniredir routine(s) pertaining to creation of files. Author: Rohan Kumar [RohanK] 30-March-1999 Revision History: Notes: Webdav Service is running in Local Services group. The local cache of the URL is stored in the Local Services profile directories. These directories have the ACLs set to allow Local Services and Local System to access. The encryption is done on the local cache file. Since encrypted file can only be operated in the user context, We have to impersonate before access the local cache file. In order to get the access to the file that is created in the Local Services profile directory in the user's context, we need to set the ACL to the encrypted file to allow everybody to access it. It won't result in a security hole because the file is encrypted. --*/ #include "pch.h" #pragma hdrstop #include "ntumrefl.h" #include "usrmddav.h" #include "global.h" #include "nodefac.h" #include "efsstruc.h" // For EFS Stuff. #include "UniUtf.h" #include #define SECURITY_WIN32 // needed by security.h #include #include #define FILE_SIGNATURE L"ROBS" #define STREAM_SIGNATURE L"NTFS" #define DATA_SIGNATURE L"GURE" BOOL DavIsThisFileEncrypted( PVOID DataBuff ); ULONG DavCheckSignature( PVOID Signature ); DWORD DavRestoreEncryptedFile( PWCHAR ExportFile, PWCHAR ImportFile ); DWORD DavWriteRawCallback( PBYTE DataBuff, PVOID CallbackContext, PULONG DataLength ); DWORD DavReuseCacheFileIfNotModified( IN PDAV_USERMODE_WORKITEM pDavWorkItem ); DWORD DavCreateUrlCacheEntry( IN PDAV_USERMODE_WORKITEM pDavWorkItem ); DWORD DavCommitUrlCacheEntry( IN PDAV_USERMODE_WORKITEM pDavWorkItem ); DWORD DavSetAclForEncryptedFile( PWCHAR FilePath ); DWORD DavGetUrlCacheEntry( IN PDAV_USERMODE_WORKITEM pDavWorkItem ); DWORD DavAddIfModifiedSinceHeader( IN PDAV_USERMODE_WORKITEM pDavWorkItem ); DWORD DavQueryUrlCacheEntry( IN PDAV_USERMODE_WORKITEM pDavWorkItem ); DWORD DavAsyncCreatePropFind( PDAV_USERMODE_WORKITEM DavWorkItem ); DWORD DavAsyncCreateQueryParentDirectory( PDAV_USERMODE_WORKITEM DavWorkItem ); DWORD DavAsyncCreateGet( PDAV_USERMODE_WORKITEM DavWorkItem ); DWORD DavLockTheFileOnTheServer( IN PDAV_USERMODE_WORKITEM DavWorkItem ); // // The maximum file size that is allowed by the WebDAV Redir. We keep a limit // on the file size to avoid being attacked by a rogue server. A rogue server // could keep on sending infinite amount of data which can cause the WebClient // service to use 100% of the CPU. // ULONG DavFileSizeLimitInBytes; // // The maximum attributes size that is allowed by the WebDAV Redir. We keep a // limit on this size to avoid being attacked by a rogue server. A rogue server // could keep on sending infinite amount of data which can cause the WebClient // service to use 100% of the CPU. This attribute limit covers all the // PROPFIND and PROPPATCH responses. For PROPFINDs with Depth 1 we make the // limit a multiple of DavFileAttributesLimitInBytes (10 times). // ULONG DavFileAttributesLimitInBytes; #define FileCacheExpiryInterval 600000000 // 60 seconds CHAR rgchIMS[] = "If-Modified-Since"; CHAR rgHttpHeader[] = "Content-Type: text/xml; charset=\"utf-8\""; CHAR rgLockInfoHeader[] = ""; CHAR rgLockInfoTrailer[] = ""; CHAR rgLockScopeHeader[] = ""; CHAR rgLockTypeHeader[] = ""; CHAR rgOwnerHeader[] = ""; CHAR rgOwnerTrailer[] = ""; // // Implementation of functions begins here. // ULONG DavFsCreate( PDAV_USERMODE_WORKITEM DavWorkItem ) /*++ Routine Description: This routine handles DAV create/open requests 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; HINTERNET DavConnHandle; PWCHAR ServerName = NULL, FileName = NULL, CanName, UrlBuffer = NULL; PWCHAR CompletePathName, cPtr, FileNameBuff = NULL; DWORD urlLength = 0, ServerLen, ServerLenInBytes, PathLen, PathLenInBytes; DWORD FileNameBuffBytes, i = 0, ServerID; ULONG_PTR CallBackContext = (ULONG_PTR)0; BOOL ReturnVal, CallBackContextInitialized = FALSE, EnCriSec = FALSE; BOOL didImpersonate = FALSE; URL_COMPONENTSW UrlComponents; PDAV_USERMODE_CREATE_REQUEST CreateRequest; PDAV_USERMODE_CREATE_RESPONSE CreateResponse; PPER_USER_ENTRY PerUserEntry = NULL; PHASH_SERVER_ENTRY ServerHashEntry = NULL; PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL; BOOL BStatus = FALSE; // // Get the request buffer pointers from the DavWorkItem. // CreateRequest = &(DavWorkItem->CreateRequest); CreateResponse = &(DavWorkItem->CreateResponse); ServerID = CreateRequest->ServerID; // // If the complete path name is NULL, then we have nothing to create. // if (CreateRequest->CompletePathName == NULL) { DavPrint((DEBUG_ERRORS, "DavFsCreate: ERROR: CompletePathName is NULL.\n")); WStatus = ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER; goto EXIT_THE_FUNCTION; } UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem; // // CreateRequest->CompletePathName contains the complete path name. // DavPrint((DEBUG_MISC, "DavFsCreate: DavWorkItem = %08lx\n", DavWorkItem)); DavPrint((DEBUG_MISC, "DavFsCreate: CompletePathName: %ws\n", CreateRequest->CompletePathName)); // // We need to do some name munging, if the create is because of a local // drive being mapped to a UNC name. The fomat in that case would be // \;X:0\server\share // if ( CreateRequest->CompletePathName[1] == L';') { CompletePathName = &(CreateRequest->CompletePathName[6]); } else { CompletePathName = &(CreateRequest->CompletePathName[1]); } // // Here, we parse the Complete path name and remove the server name and the // file name from it. We use these to construct the URL for the WinInet // calls. The complete path name is of the form \server\filename. // The name ends with a '\0'. Note that the filename could be of the form // share\foo\bar\duh.txt. // // [\;X:0]\server\filename // ^ // | // CompletePathName(CPN) // \server\filename // ^ ^ // | | // CPN cPtr cPtr = wcschr(CompletePathName, '\\'); // // Length of the server name including the terminating '\0' char. // ServerLen = 1 + (((PBYTE)cPtr - (PBYTE)CompletePathName) / sizeof(WCHAR)); ServerLenInBytes = ServerLen * sizeof(WCHAR); // \server\filename // ^ ^ // | | // CPN cPtr cPtr++; // // Length of the server name including the terminating '\0' char. // PathLen = 1 + wcslen(cPtr); PathLenInBytes = PathLen * sizeof(WCHAR); // // Allocate the memory and fill in the server name char by char. // ServerName = (PWCHAR) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, ServerLenInBytes); if (ServerName == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsCreate/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // \server\filename // ^^^^^^ ^ // |||||| | // CPN cPtr while(CompletePathName[i] != '\\') { ASSERT(i < ServerLen); ServerName[i] = CompletePathName[i]; i++; } ASSERT((i + 1) == ServerLen); ServerName[i] = '\0'; // // Allocate the memory and copy the file name. // FileName = (PWCHAR) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, PathLenInBytes); if (FileName == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsCreate/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // This remaining path name is needed in Async Create Callback function. // DavWorkItem->AsyncCreate.RemPathName = FileName; wcscpy(FileName, cPtr); CanName = FileName; // // The file name can contain \ characters. Replace them by / characters. // while (*CanName) { if (*CanName == L'\\') { *CanName = L'/'; } CanName++; } // // Check if this is a stream, if so, bailout right from here. // if(wcschr(FileName, L':')) { WStatus = ERROR_INVALID_NAME; DavPrint((DEBUG_ERRORS, "DavFsCreate: Streams Not Supported\n")); goto EXIT_THE_FUNCTION; } // // If we have a dummy share name in the FileName, we need to remove it // right now before we contact the server. // DavRemoveDummyShareFromFileName(FileName); DavPrint((DEBUG_MISC, "DavFsCreate: ServerName: %ws, File Name: %ws\n", ServerName, FileName)); // // Create the URL to be sent to the server. Initialize the UrlComponents // structure before making the call. // UrlComponents.dwStructSize = sizeof(URL_COMPONENTSW); UrlComponents.lpszScheme = NULL; UrlComponents.dwSchemeLength = 0; UrlComponents.nScheme = INTERNET_SCHEME_HTTP; UrlComponents.lpszHostName = ServerName; UrlComponents.dwHostNameLength = wcslen(ServerName); UrlComponents.nPort = DEFAULT_HTTP_PORT; UrlComponents.lpszUserName = NULL; UrlComponents.dwUserNameLength = 0; UrlComponents.lpszPassword = NULL; UrlComponents.dwPasswordLength = 0; UrlComponents.lpszUrlPath = FileName; UrlComponents.dwUrlPathLength = wcslen(FileName); UrlComponents.lpszExtraInfo = NULL; UrlComponents.dwExtraInfoLength = 0; ReturnVal = InternetCreateUrlW(&(UrlComponents), 0, NULL, &(urlLength)); if (!ReturnVal) { ULONG urlLengthInWChars = 0; WStatus = GetLastError(); // // We pre-allocate the Url buffer on the CreateResponse with the size of // MAX_PATH * 2. Any Url longer than that will overrun the buffer. The Url // will be used to update the LastAccessTime of the WinInet cache on rename // and close later. Note urlLength is the number of bytes. // if (urlLength >= MAX_PATH * 4) { WStatus = ERROR_NO_SYSTEM_RESOURCES; goto EXIT_THE_FUNCTION; } if (WStatus == ERROR_INSUFFICIENT_BUFFER) { UrlBuffer = (PWCHAR) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, urlLength); if (UrlBuffer != NULL) { ZeroMemory(UrlBuffer, urlLength); // // This UrlBuffer is needed in Async Create Callback function. // We need to supply the length (4th Parameter) in WChars. // DavWorkItem->AsyncCreate.UrlBuffer = UrlBuffer; urlLengthInWChars = ( urlLength/sizeof(WCHAR) ); ReturnVal = InternetCreateUrlW(&(UrlComponents), 0, UrlBuffer, &(urlLengthInWChars)); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsCreate/InternetCreateUrl. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } } else { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsCreate/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } } else { DavPrint((DEBUG_ERRORS, "DavFsCreate/InternetCreateUrl. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } } DavPrint((DEBUG_MISC, "URL: %ws\n", UrlBuffer)); // // 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, "DavFsCreate/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. This needs to be done only if we are calling the WinInet // APIs asynchronously. // 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, "DavFsCreate/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } DavPrint((DEBUG_MISC, "DavFsCreate: LogonId.LowPart = %d, LogonId.HighPart = %d\n", CreateRequest->LogonID.LowPart, CreateRequest->LogonID.HighPart)); // // 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; ReturnVal = DavDoesUserEntryExist(ServerName, ServerID, &(CreateRequest->LogonID), &PerUserEntry, &ServerHashEntry); // // If the Create request in the kernel get cancelled even before the // corresponding usermode thread gets a chance to execute this code, then // it possible that the VNetRoot (hence the PerUserEntry) and SrvCall get // finalized before the thread that is handling the create comes here. This // could happen if this request was the only one for this share and the // server as well. This is why we need to check if the ServerHashEntry and // the PerUserEntry are valid before proceeding. // if (ReturnVal == FALSE || ServerHashEntry == NULL || PerUserEntry == NULL) { WStatus = ERROR_CANCELLED; DavPrint((DEBUG_ERRORS, "DavFsCreate: (ServerHashEntry == NULL || PerUserEntry == NULL)\n")); goto EXIT_THE_FUNCTION; } DavWorkItem->AsyncCreate.ServerHashEntry = ServerHashEntry; DavWorkItem->AsyncCreate.PerUserEntry = PerUserEntry; // // Add a reference to the user entry. // PerUserEntry->UserEntryRefCount++; // // Since a create had succeeded earlier, the entry must be good. // ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized); ASSERT(PerUserEntry->DavConnHandle != NULL); DavConnHandle = PerUserEntry->DavConnHandle; // // And yes, we obviously have to leave the critical section // before returning. // LeaveCriticalSection( &(HashServerEntryTableLock) ); EnCriSec = FALSE; // // If we are using WinInet synchronously, then we need to impersonate the // clients context now. We shouldn't do it before we call CreateUrlCacheEntry // because that call will fail if the thread is not running in the context // of the Web Client Service. // #ifndef DAV_USE_WININET_ASYNCHRONOUSLY WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsCreate/UMReflectorImpersonate. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; #endif // // We now call the HttpOpenRequest function and return. // DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN; DavPrint((DEBUG_MISC, "DavFsCreate: DavConnHandle = %08lx.\n", DavConnHandle)); DavWorkItem->AsyncCreate.AsyncCreateState = AsyncCreatePropFind; DavWorkItem->DavMinorOperation = DavMinorQueryInfo; DavWorkItem->AsyncCreate.DataBuff = NULL; DavWorkItem->AsyncCreate.didRead = NULL; DavWorkItem->AsyncCreate.Context1 = NULL; DavWorkItem->AsyncCreate.Context2 = NULL; if (CreateRequest->FileInformationCached) { DavPrint((DEBUG_MISC, "Cached info %x %x %x %ws\n", CreateResponse->BasicInformation.FileAttributes, CreateResponse->StandardInformation.AllocationSize.LowPart, CreateResponse->StandardInformation.EndOfFile.LowPart, DavWorkItem->AsyncCreate.UrlBuffer)); } if ((CreateRequest->FileNotExists) || (CreateRequest->FileInformationCached)) { FILE_BASIC_INFORMATION BasicInformation = CreateResponse->BasicInformation; FILE_STANDARD_INFORMATION StandardInformation = CreateResponse->StandardInformation; RtlZeroMemory(CreateResponse, sizeof(*CreateResponse)); // // Restore the file information on the create request // if (CreateRequest->FileInformationCached) { CreateResponse->BasicInformation = BasicInformation; CreateResponse->StandardInformation = StandardInformation; DavWorkItem->AsyncCreate.doesTheFileExist = TRUE; } if (!didImpersonate) { WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsCreate/UMReflectorImpersonate. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; } DavPrint((DEBUG_MISC, "DavFsCreate skip PROPFIND for %x %x %ws\n", CreateRequest->FileAttributes, CreateResponse->BasicInformation.FileAttributes, DavWorkItem->AsyncCreate.UrlBuffer)); WStatus = DavAsyncCreatePropFind(DavWorkItem); } else { RtlZeroMemory(CreateResponse, sizeof(*CreateResponse)); // // Convert the unicode object name to UTF-8 URL format. Space and other // white characters will remain untouched. These should be taken care of by // the wininet calls. // BStatus = DavHttpOpenRequestW(DavConnHandle, L"PROPFIND", FileName, L"HTTP/1.1", NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_NO_COOKIES, CallBackContext, L"DavFsCreate", &(DavWorkItem->AsyncCreate.DavOpenHandle)); if(BStatus == FALSE) { WStatus = GetLastError(); goto EXIT_THE_FUNCTION; } if (DavWorkItem->AsyncCreate.DavOpenHandle == NULL) { WStatus = GetLastError(); if (WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavFsCreate/HttpOpenRequestW. Error Val = %d.\n", WStatus)); } goto EXIT_THE_FUNCTION; } WStatus = DavAsyncCommonStates(DavWorkItem, FALSE); if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING && WStatus != ERROR_FILE_NOT_FOUND) { DavPrint((DEBUG_ERRORS, "DavFsCreate/DavAsyncCommonStates. Error Val = %08lx\n", WStatus)); } } EXIT_THE_FUNCTION: // Do the necessary cleanup and return. // // We could have taken the lock and come down an error path without // releasing it. If thats the case, then we need to release the lock now. // if (EnCriSec) { LeaveCriticalSection( &(HashServerEntryTableLock) ); EnCriSec = FALSE; } if (ServerName != NULL) { HLOCAL FreeHandle; ULONG FreeStatus; FreeHandle = LocalFree((HLOCAL)ServerName); if (FreeHandle != NULL) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsCreate/LocalFree. Error Val = %d\n", FreeStatus)); } } if (WStatus == ERROR_SUCCESS) { wcscpy(CreateResponse->Url, DavWorkItem->AsyncCreate.UrlBuffer); DavPrint((DEBUG_MISC, "Returned info %x %x %x %ws\n", CreateResponse->BasicInformation.FileAttributes, CreateResponse->StandardInformation.AllocationSize.LowPart, CreateResponse->StandardInformation.EndOfFile.LowPart, DavWorkItem->AsyncCreate.UrlBuffer)); } // // If the Create failed after taking a LOCK on the file, then we need to // UNLOCK the file before returning. // if (WStatus != ERROR_SUCCESS) { if (CreateResponse->LockWasTakenOnThisCreate) { ULONG UnLockStatus; if (!didImpersonate) { UnLockStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (UnLockStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsCreate/UMReflectorImpersonate. Error Val = %d\n", UnLockStatus)); } else { didImpersonate = TRUE; } } UnLockStatus = DavUnLockTheFileOnTheServer(DavWorkItem); if (UnLockStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsCreate/DavUnLockTheFileOnTheServer. Error Val = %d\n", UnLockStatus)); } CreateResponse->LockWasTakenOnThisCreate = 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; } DavAsyncCreateCompletion(DavWorkItem); } else { DavPrint((DEBUG_MISC, "DavFsCreate: 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(); didImpersonate = FALSE; } // // 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); // // The error cannot map to STATUS_SUCCESS. If it does, we need to // break here and investigate. // if (DavWorkItem->Status == STATUS_SUCCESS) { DbgBreakPoint(); } } else { PDAV_USERMODE_CREATE_RESPONSE CreateResponse; CreateResponse = &(DavWorkItem->CreateResponse); DavWorkItem->Status = STATUS_SUCCESS; // // If we suceeded and it was a file and the open was not a pseudo open, // the handle should be set. Otherwise we screwed up. We should then // break here and investigate. // if ( !(CreateResponse->StandardInformation.Directory) && !(CreateResponse->fPsuedoOpen) ) { if (CreateResponse->Handle == NULL) { DbgBreakPoint(); } } } DavAsyncCreateCompletion(DavWorkItem); #endif return WStatus; } DWORD DavAsyncCreate( PDAV_USERMODE_WORKITEM DavWorkItem, BOOLEAN CalledByCallBackThread ) /*++ Routine Description: This is the callback routine for the create 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; NTSTATUS NtStatus = STATUS_SUCCESS; ULONG NumOfFileEntries = 0; PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL; BOOL ReturnVal, didImpersonate = FALSE, readDone = FALSE; BOOL doesTheFileExist = FALSE; HANDLE FileHandle = INVALID_HANDLE_VALUE; DWORD toRead = 0, didRead = 0, didWrite = 0; LPDWORD NumRead = NULL; PVOID Ctx1 = NULL, Ctx2 = NULL; PDAV_FILE_ATTRIBUTES DavFileAttributes; PCHAR DataBuff = NULL; DWORD DataBuffBytes; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; SECURITY_QUALITY_OF_SERVICE QualityOfService; PDAV_USERMODE_CREATE_REQUEST CreateRequest; PDAV_USERMODE_CREATE_RESPONSE CreateResponse; ULONG_PTR CallBackContext = (ULONG_PTR)0; PWCHAR pEncryptedCachedFile = NULL; PDAV_FILE_ATTRIBUTES DavDirectoryAttributes = NULL; ACCESS_MASK DesiredAccess = 0; BOOL BStatus = FALSE, fCacheFileReused = FALSE; // // Get the request and response buffer pointers from the DavWorkItem. // CreateRequest = &(DavWorkItem->CreateRequest); CreateResponse = &(DavWorkItem->CreateResponse); CreateResponse->fPsuedoOpen = FALSE; UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem; #ifdef DAV_USE_WININET_ASYNCHRONOUSLY // // We set the CallbackContext only if we are calling the WinInet APIs // asynchronously. // CallBackContext = (ULONG_PTR)DavWorkItem; // // If this function was called by the thread that picked off the DavWorkItem // from the Callback function, we need to do a few things first. These are // done below. // 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, "DavAsyncCreate/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, "DavAsyncCreate/DavAsyncCommonStates. Error Val =" " %08lx\n", WStatus)); } } else if (WStatus == ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION) { // // MSN has this BUG where it returns 302 instead of 404 when // queried for a file (eg:Desktop.ini) which does not exist at // the share level. // WStatus = ERROR_FILE_NOT_FOUND; } else { DavPrint((DEBUG_ERRORS, "DavAsyncCreate. AsyncFunction failed. Error Val = %d\n", WStatus)); } goto EXIT_THE_FUNCTION; } } #else // // If we are using synchronous WinInet then we enter this function // impersonating the client. // didImpersonate = TRUE; ASSERT(CalledByCallBackThread == FALSE); #endif switch (DavWorkItem->DavOperation) { case DAV_CALLBACK_HTTP_END: case DAV_CALLBACK_HTTP_READ: { if (DavWorkItem->AsyncCreate.DataBuff == NULL) { // // Need to allocate memory for the read buffer. // DataBuffBytes = NUM_OF_BYTES_TO_READ; DataBuff = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, DataBuffBytes); if (DataBuff == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } DavWorkItem->AsyncCreate.DataBuff = DataBuff; } if (DavWorkItem->AsyncCreate.didRead == NULL) { // // Allocate memory for the DWORD that stores the number of bytes read. // NumRead = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, sizeof(DWORD)); if (NumRead == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } DavWorkItem->AsyncCreate.didRead = NumRead; } DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_READ; DavPrint((DEBUG_MISC, "DavAsyncCreate: AsyncCreateState = %d\n", DavWorkItem->AsyncCreate.AsyncCreateState)); DavPrint((DEBUG_MISC, "DavAsyncCreate: CalledByCallBackThread = %d\n", CalledByCallBackThread)); // // When we come here, we could either be doing a PROPFIND or GET on the // file. The PROPFIND is done to get the file attributes and the GET to // get the whole file from the server. // if (DavWorkItem->AsyncCreate.AsyncCreateState == AsyncCreatePropFind) { if (DavWorkItem->DavMinorOperation == DavMinorQueryInfo) { ULONG ResponseStatus; // // If the file for which the PROPFIND was done does not exist, then // we need to Create one or fail, depending on the create options // specified by the application. // // // Does this file exist ? If the ResponseStatus is not // ERROR_SUCCESS, then we are sure that the file does not // exist. But, if it is we cannot be sure that the file exists. // ResponseStatus = DavQueryAndParseResponse(DavWorkItem->AsyncCreate.DavOpenHandle); if (ResponseStatus == ERROR_SUCCESS) { doesTheFileExist = TRUE; } else { // // Carry on only if http really didn't find it. Bailout if // there is some other error. // if (ResponseStatus == ERROR_FILE_NOT_FOUND) { doesTheFileExist = FALSE; } else { WStatus = ResponseStatus; goto EXIT_THE_FUNCTION; } } DavWorkItem->AsyncCreate.doesTheFileExist = doesTheFileExist; DavPrint((DEBUG_MISC, "DavAsyncCreate: doesTheFileExist = %d\n", doesTheFileExist)); // // Since the file existed, the next thing we do is read the // XML response which contains the properties of the file. // DavWorkItem->DavMinorOperation = DavMinorReadData; } doesTheFileExist = DavWorkItem->AsyncCreate.doesTheFileExist; if (doesTheFileExist) { DWORD TotalDataBytesRead = 0; NumRead = DavWorkItem->AsyncCreate.didRead; DataBuff = DavWorkItem->AsyncCreate.DataBuff; Ctx1 = DavWorkItem->AsyncCreate.Context1; Ctx2 = DavWorkItem->AsyncCreate.Context2; do { switch (DavWorkItem->DavMinorOperation) { case DavMinorReadData: DavWorkItem->DavMinorOperation = DavMinorPushData; ReturnVal = InternetReadFile(DavWorkItem->AsyncCreate.DavOpenHandle, (LPVOID)DataBuff, NUM_OF_BYTES_TO_READ, NumRead); if (!ReturnVal) { WStatus = GetLastError(); if (WStatus != ERROR_IO_PENDING) { DavCloseContext(Ctx1, Ctx2); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/InternetReadFile. Error Val" " = %d\n", WStatus)); } DavPrint((DEBUG_MISC, "DavAsyncCreate/InternetReadFile(1). " "ERROR_IO_PENDING.\n")); goto EXIT_THE_FUNCTION; } // // We reject files whose attributes are greater than a // certain size (DavFileAttributesLimitInBytes). This // is a parameter that can be set in the registry. This // is done to avoid attacks by rogue servers. // TotalDataBytesRead += *NumRead; if (TotalDataBytesRead > DavFileAttributesLimitInBytes) { WStatus = ERROR_BAD_NET_RESP; DavPrint((DEBUG_ERRORS, "DavAsyncCreate. FileAttributesSize > %d\n", DavFileAttributesLimitInBytes)); goto EXIT_THE_FUNCTION; } // // Lack of break is intentional. // case DavMinorPushData: DavWorkItem->DavMinorOperation = DavMinorReadData; didRead = *NumRead; DavPrint((DEBUG_MISC, "DavAsyncCreate(1): toRead = %d, didRead = %d.\n", NUM_OF_BYTES_TO_READ, didRead)); DavPrint((DEBUG_MISC, "DavAsyncCreate(1): DataBuff = %s\n", DataBuff)); readDone = (didRead == 0) ? TRUE : FALSE; WStatus = DavPushData(DataBuff, &Ctx1, &Ctx2, didRead, readDone); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavPushData. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } if (DavWorkItem->AsyncCreate.Context1 == NULL) { DavWorkItem->AsyncCreate.Context1 = Ctx1; } if (DavWorkItem->AsyncCreate.Context2 == NULL) { DavWorkItem->AsyncCreate.Context2 = Ctx2; } break; default: WStatus = ERROR_INVALID_PARAMETER; DavPrint((DEBUG_ERRORS, "DavAsyncCreate. Invalid DavMinorOperation = %d.\n", DavWorkItem->DavMinorOperation)); goto EXIT_THE_FUNCTION; break; } if (readDone) { break; } } while ( TRUE ); // // We now need to parse the data. // DavFileAttributes = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof(DAV_FILE_ATTRIBUTES) ); if (DavFileAttributes == NULL) { DavCloseContext(Ctx1, Ctx2); WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } InitializeListHead( &(DavFileAttributes->NextEntry) ); WStatus = DavParseData(DavFileAttributes, Ctx1, Ctx2, &NumOfFileEntries); if (WStatus != ERROR_SUCCESS) { DavFinalizeFileAttributesList(DavFileAttributes, TRUE); DavFileAttributes = NULL; DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavParseData. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // Its possible to get a 207 response for the PROPFIND request // even if the request failed. In such a case the status value // in the XML response indicates the error. If this happens, // set InvalidNode to TRUE. // if (DavFileAttributes->InvalidNode) { WStatus = ERROR_INTERNAL_ERROR; DavPrint((DEBUG_ERRORS, "DavAsyncCreate. Invalid Node!! Status = %ws\n", DavFileAttributes->Status)); DavFinalizeFileAttributesList(DavFileAttributes, TRUE); DavFileAttributes = NULL; goto EXIT_THE_FUNCTION; } // // If the file is being created for any kind of write access or // FILE_SHARE_WRITE is not a part of the shareaccess and the // file has already been locked on the server, we need to fail // this call with ERROR_LOCK_VIOLATION and copy the name of the // LockOwner in the CreateResponse buffer. // if ( (CreateRequest->DesiredAccess & (GENERIC_WRITE | DELETE | GENERIC_ALL | FILE_WRITE_DATA | FILE_APPEND_DATA)) || !(CreateRequest->ShareAccess & FILE_SHARE_WRITE) ) { if (DavFileAttributes->OpaqueLockToken) { ASSERT(DavFileAttributes->LockOwner != NULL); WStatus = ERROR_LOCK_VIOLATION; CreateResponse->FileWasAlreadyLocked = TRUE; wcscpy(CreateResponse->LockOwner, DavFileAttributes->LockOwner); DavPrint((DEBUG_ERRORS, "DavAsyncCreate: fileIsLocked!! LockOwner = %ws\n", CreateResponse->LockOwner)); DavFinalizeFileAttributesList(DavFileAttributes, TRUE); DavFileAttributes = NULL; goto EXIT_THE_FUNCTION; } } DavPrint((DEBUG_MISC,"DavAsyncCreate: NumOfFileEntries = %d\n", NumOfFileEntries)); // // If this is a directory create and the intention is to delete // it, we perform the following checks. // if ( (DavFileAttributes->isCollection) && (CreateRequest->DesiredAccess & DELETE || CreateRequest->CreateOptions & FILE_DELETE_ON_CLOSE)) { PWCHAR CPN1 = NULL; BOOL ServerShareDelete = TRUE; DWORD wackCount = 0; // // If the delete is just for \\server\share then we return // ERROR_ACCESS_DENIED. CompletePathName has the form // \server\share\dir. If its \server\share or \server\share\, // we return the error. This is because we do not allow // a client to delete a share on the server. // CPN1 = CreateRequest->CompletePathName; while ( *CPN1 != L'\0' ) { if ( *CPN1 == L'\\' || *CPN1 == L'/' ) { wackCount++; if ( (wackCount > 2) && (*(CPN1 + 1) != L'\0') ) { ServerShareDelete = FALSE; break; } } CPN1++; } if (ServerShareDelete) { DavFinalizeFileAttributesList(DavFileAttributes, TRUE); DavFileAttributes = NULL; WStatus = ERROR_ACCESS_DENIED; DavPrint((DEBUG_ERRORS, "DavAsyncCreate: ServerShareDelete & ERROR_ACCESS_DENIED\n")); goto EXIT_THE_FUNCTION; } // // If the directory is not empty, we return the following. // if (NumOfFileEntries > 1) { DavFinalizeFileAttributesList(DavFileAttributes, TRUE); DavFileAttributes = NULL; WStatus = ERROR_DIR_NOT_EMPTY; DavPrint((DEBUG_ERRORS, "DavAsyncCreate: ERROR_DIR_NOT_EMPTY\n")); goto EXIT_THE_FUNCTION; } } // // During the create call, we only query the attributes for the file // or the directory. Hence if the request succeeded, the number of // DavFileAttribute entries created should be = 1. If it failed, // the NumOfFileEntries == 0. The request could fail even if the // response was "HTTP/1.1 207 Multi-Status". The status is returned // in the XML response. // if (NumOfFileEntries != 1) { PLIST_ENTRY listEntry = &(DavFileAttributes->NextEntry); PDAV_FILE_ATTRIBUTES DavFA = NULL; DavPrint((DEBUG_ERRORS, "DavAsyncCreate. NumOfFileEntries = %d\n", NumOfFileEntries)); do { DavFA = CONTAINING_RECORD(listEntry, DAV_FILE_ATTRIBUTES, NextEntry); DavPrint((DEBUG_MISC, "DavAsyncCreate. FileName = %ws\n", DavFA->FileName)); listEntry = listEntry->Flink; } while ( listEntry != &(DavFileAttributes->NextEntry) ); DavFinalizeFileAttributesList(DavFileAttributes, TRUE); DavFileAttributes = NULL; doesTheFileExist = FALSE; DavWorkItem->AsyncCreate.doesTheFileExist = FALSE; } } if (doesTheFileExist) { // // Set the FILE_BASIC_INFORMATION. // CreateResponse->BasicInformation.CreationTime.HighPart = DavFileAttributes->CreationTime.HighPart; CreateResponse->BasicInformation.CreationTime.LowPart = DavFileAttributes->CreationTime.LowPart; CreateResponse->BasicInformation.LastAccessTime.HighPart = DavFileAttributes->LastModifiedTime.HighPart; CreateResponse->BasicInformation.LastAccessTime.LowPart = DavFileAttributes->LastModifiedTime.LowPart; CreateResponse->BasicInformation.LastWriteTime.HighPart = DavFileAttributes->LastModifiedTime.HighPart; CreateResponse->BasicInformation.LastWriteTime.LowPart = DavFileAttributes->LastModifiedTime.LowPart; CreateResponse->BasicInformation.ChangeTime.HighPart = DavFileAttributes->LastModifiedTime.HighPart; CreateResponse->BasicInformation.ChangeTime.LowPart = DavFileAttributes->LastModifiedTime.LowPart; CreateResponse->BasicInformation.FileAttributes = DavFileAttributes->dwFileAttributes; DavPrint((DEBUG_MISC, "DavAsyncCreate. attributes %x %ws\n",DavFileAttributes->dwFileAttributes,DavWorkItem->AsyncCreate.RemPathName)); if (DavFileAttributes->isHidden || (DavFileAttributes->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) { CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_HIDDEN; } else { CreateResponse->BasicInformation.FileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; } if (DavFileAttributes->isCollection) { CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } else { CreateResponse->BasicInformation.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; } // // Set the FILE_STANDARD_INFORMATION. // CreateResponse->StandardInformation.AllocationSize.HighPart = DavFileAttributes->FileSize.HighPart; CreateResponse->StandardInformation.AllocationSize.LowPart = DavFileAttributes->FileSize.LowPart; CreateResponse->StandardInformation.EndOfFile.HighPart = DavFileAttributes->FileSize.HighPart; CreateResponse->StandardInformation.EndOfFile.LowPart = DavFileAttributes->FileSize.LowPart; CreateResponse->StandardInformation.NumberOfLinks = 0; CreateResponse->StandardInformation.DeletePending = 0; CreateResponse->StandardInformation.Directory = DavFileAttributes->isCollection; // // We don't need the attributes list any more, so finalize it. // DavFinalizeFileAttributesList(DavFileAttributes, TRUE); DavFileAttributes = NULL; // // CLose the XML parser contexts. // DavCloseContext(Ctx1, Ctx2); DavWorkItem->AsyncCreate.Context1 = NULL; DavWorkItem->AsyncCreate.Context2 = NULL; } // // We are done with the Open handle to PROPFIND. Now we need to GET // the file from the server. // InternetCloseHandle(DavWorkItem->AsyncCreate.DavOpenHandle); DavWorkItem->AsyncCreate.DavOpenHandle = NULL; ASSERT(didImpersonate); WStatus = DavAsyncCreatePropFind(DavWorkItem); } else if (DavWorkItem->AsyncCreate.AsyncCreateState == AsyncCreateQueryParentDirectory) { ULONG ResponseStatus; BOOL doesTheDirectoryExist = FALSE; DWORD TotalDataBytesRead = 0; // // If the parent directory for which the PROPFIND was done do not have encryption flag set, // the file will be created normally. Otherwise, the file will be encrypted when created. // DavPrint((DEBUG_MISC, "AsyncCreateQueryParentDirectory\n")); NumRead = DavWorkItem->AsyncCreate.didRead; DataBuff = DavWorkItem->AsyncCreate.DataBuff; Ctx1 = DavWorkItem->AsyncCreate.Context1; Ctx2 = DavWorkItem->AsyncCreate.Context2; // // Does this file exist ? If the ResponseStatus is not // ERROR_SUCCESS, then we are sure that the file does not // exist. But, if it is we cannot be sure that the file exists. // ResponseStatus = DavQueryAndParseResponse(DavWorkItem->AsyncCreate.DavOpenHandle); if (ResponseStatus != ERROR_SUCCESS) { // // If the parent directory does not exist, return error. // WStatus = ResponseStatus; DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/DavQueryAndParseResponse %x %d\n",WStatus,WStatus)); goto EXIT_THE_FUNCTION; } do { ReturnVal = InternetReadFile(DavWorkItem->AsyncCreate.DavOpenHandle, (LPVOID)DataBuff, NUM_OF_BYTES_TO_READ, NumRead); if (!ReturnVal) { WStatus = GetLastError(); if (WStatus != ERROR_IO_PENDING) { DavCloseContext(Ctx1, Ctx2); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/InternetReadFile. Error Val" " = %d\n", WStatus)); } DavPrint((DEBUG_MISC, "DavAsyncCreate/InternetReadFile(1). " "ERROR_IO_PENDING.\n")); goto EXIT_THE_FUNCTION; } // // We reject files whose attributes are greater than a // certain size (DavFileAttributesLimitInBytes). This // is a parameter that can be set in the registry. This // is done to avoid attacks by rogue servers. // TotalDataBytesRead += *NumRead; if (TotalDataBytesRead > DavFileAttributesLimitInBytes) { WStatus = ERROR_BAD_NET_RESP; DavPrint((DEBUG_ERRORS, "DavAsyncCreate. QueryParentAttributesSize > %d\n", DavFileAttributesLimitInBytes)); goto EXIT_THE_FUNCTION; } didRead = *NumRead; DavPrint((DEBUG_MISC, "DavAsyncCreate(1): toRead = %d, didRead = %d.\n", NUM_OF_BYTES_TO_READ, didRead)); DavPrint((DEBUG_MISC, "DavAsyncCreate(1): DataBuff = %s\n", DataBuff)); readDone = (didRead == 0) ? TRUE : FALSE; WStatus = DavPushData(DataBuff, &Ctx1, &Ctx2, didRead, readDone); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavPushData. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } if (DavWorkItem->AsyncCreate.Context1 == NULL) { DavWorkItem->AsyncCreate.Context1 = Ctx1; } if (DavWorkItem->AsyncCreate.Context2 == NULL) { DavWorkItem->AsyncCreate.Context2 = Ctx2; } } while (!readDone); // // We now need to parse the data. // DavDirectoryAttributes = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,sizeof(DAV_FILE_ATTRIBUTES)); if (DavDirectoryAttributes == NULL) { DavCloseContext(Ctx1, Ctx2); WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } InitializeListHead( &(DavDirectoryAttributes->NextEntry) ); WStatus = DavParseData(DavDirectoryAttributes, Ctx1, Ctx2, &NumOfFileEntries); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavParseData. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } if ((CreateRequest->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) || (DavDirectoryAttributes->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) { CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; DavPrint((DEBUG_MISC, "DavAsyncCreate: ParentDirectory Is Encrypted\n")); } else { DavPrint((DEBUG_MISC, "DavAsyncCreate: ParentDirectory Is Not Encrypted\n")); } DavFinalizeFileAttributesList(DavDirectoryAttributes, TRUE); DavDirectoryAttributes = NULL; DavCloseContext(Ctx1, Ctx2); DavWorkItem->AsyncCreate.Context1 = NULL; DavWorkItem->AsyncCreate.Context2 = NULL; ASSERT(didImpersonate == TRUE); WStatus = DavAsyncCreateQueryParentDirectory(DavWorkItem); } else if (DavWorkItem->AsyncCreate.AsyncCreateState == AsyncCreateGet) { LARGE_INTEGER ByteOffset; ULONG BytesToRead; BOOL EncryptedFile = FALSE, ZeroByteFile = FALSE; FILE_STANDARD_INFORMATION FileStdInfo; ULONG ResponseStatus; ResponseStatus = DavQueryAndParseResponse(DavWorkItem->AsyncCreate.DavOpenHandle); if (ResponseStatus != ERROR_SUCCESS) { WStatus = ResponseStatus; DavPrint((DEBUG_ERRORS, "DavAsyncCreate(AsyncCreateGet)/DavQueryAndParseResponse: WStatus = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // This thread is currently impersonating the client that // made this request. Before we call CreateFile, we need to // revert back to the context of the Web Client service. // RevertToSelf(); didImpersonate = FALSE; if (DavReuseCacheFileIfNotModified(DavWorkItem) == ERROR_SUCCESS) { fCacheFileReused = TRUE; } if (!fCacheFileReused) { // // Call DavCreateUrlCacheEntry to create an entry in the // WinInet's cache. // WStatus = DavCreateUrlCacheEntry(DavWorkItem); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavCreateUrlCacheEntry %d\n", WStatus)); goto EXIT_THE_FUNCTION; } } if (DavWorkItem->AsyncCreate.FileHandle == NULL) { // // Create a handle to the file whose entry was created in the // cache. // FileHandle = CreateFileW(DavWorkItem->AsyncCreate.FileName, (GENERIC_READ | GENERIC_WRITE), FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (FileHandle == INVALID_HANDLE_VALUE) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/CreateFile. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } DavWorkItem->AsyncCreate.FileHandle = FileHandle; // // Impersonate back again, so that we are in the context of // the user who issued this request. // WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/UMReflectorImpersonate. " "Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; } FileHandle = DavWorkItem->AsyncCreate.FileHandle; DataBuff = DavWorkItem->AsyncCreate.DataBuff; NumRead = DavWorkItem->AsyncCreate.didRead; if (!fCacheFileReused) { DWORD TotalDataBytesRead = 0; do { switch (DavWorkItem->DavMinorOperation) { case DavMinorReadData: DavWorkItem->DavMinorOperation = DavMinorWriteData; ReturnVal = InternetReadFile(DavWorkItem->AsyncCreate.DavOpenHandle, (LPVOID)DataBuff, NUM_OF_BYTES_TO_READ, NumRead); if (!ReturnVal) { WStatus = GetLastError(); if (WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/InternetReadFile. Error Val" " = %08lx.\n", WStatus)); } DavPrint((DEBUG_MISC, "DavAsyncCreate/InternetReadFile(2). " "ERROR_IO_PENDING.\n")); goto EXIT_THE_FUNCTION; } // // We reject files which are greater than a certain // size (DavFileSizeLimitInBytes). This is a parameter // that can be set in the registry. This is done to // avoid attacks by rogue servers. // TotalDataBytesRead += *NumRead; if (TotalDataBytesRead > DavFileSizeLimitInBytes) { WStatus = ERROR_BAD_NET_RESP; DavPrint((DEBUG_ERRORS, "DavAsyncCreate. FileSize > %d\n", DavFileSizeLimitInBytes)); goto EXIT_THE_FUNCTION; } // // Lack of break is intentional. // case DavMinorWriteData: DavWorkItem->DavMinorOperation = DavMinorReadData; didRead = *NumRead; DavPrint((DEBUG_MISC, "DavAsyncCreate(2): toRead = %d, didRead = %d.\n", NUM_OF_BYTES_TO_READ, didRead)); readDone = (didRead == 0) ? TRUE : FALSE; if (readDone) { break; } // // This thread is currently impersonating the client that // made this request. Before we call WriteFile, we need to // revert back to the context of the Web Client service. // RevertToSelf(); didImpersonate = FALSE; // // Write the buffer to the file which has been cached on // persistent storage. // ReturnVal = WriteFile(FileHandle, DataBuff, didRead, &didWrite, NULL); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/WriteFile. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } ASSERT(didRead == didWrite); // // Impersonate back again, so that we are in the context of // the user who issued this request. // WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/UMReflectorImpersonate. " "Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; break; default: WStatus = ERROR_INVALID_PARAMETER; DavPrint((DEBUG_ERRORS, "DavAsyncCreate. Invalid DavMinorOperation = %d.\n", DavWorkItem->DavMinorOperation)); goto EXIT_THE_FUNCTION; break; } if (readDone) { break; } } while ( TRUE ); } // // At this point, we have read the entire file. // We need to figure out if this is an encrypted file. // If it is, we need to RESTORE it, since it was stored as a // Backup stream on the server. We read the first 100 bytes of the // file to check for the EFS signature. // // // This thread could be currently impersonating the client that made // this request. Before we call ReadFile, we need to revert back to // the context of the Web Client service. // if (didImpersonate) { RevertToSelf(); didImpersonate = FALSE; } // // Set the last access time on the URL Cache so that we can avoid // a GET on subsequent Create's if the file has not changed on the // server. // if (!fCacheFileReused) { // // Commit to the cache only if it is not being reused. If it // is being reused, it has already been committed on a previous // Create. // WStatus = DavCommitUrlCacheEntry(DavWorkItem); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavCommitUrlCacheEntry(2). " "WStatus = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } if (CreateResponse->BasicInformation.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { // // Set the ACLs on the file, so that it can be accessed // after impersonating the user whoc is creating it. // WStatus = DavSetAclForEncryptedFile(DavWorkItem->AsyncCreate.FileName); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavSetAclForEncryptedFile(1). Error Val = %d, FileName = %ws\n", WStatus, DavWorkItem->AsyncCreate.FileName)); goto EXIT_THE_FUNCTION; } } } WStatus = DavQueryUrlCacheEntry(DavWorkItem); if (WStatus == ERROR_SUCCESS) { SYSTEMTIME SystemTime; GetSystemTime(&SystemTime); SystemTimeToFileTime( &SystemTime, &((LPINTERNET_CACHE_ENTRY_INFOW)DavWorkItem->AsyncCreate.lpCEI)->LastAccessTime); DavPrint((DEBUG_MISC, "DavAsyncCreate/SetUrlCacheEntryInfo %u %ws\n", ((LPINTERNET_CACHE_ENTRY_INFOW)DavWorkItem->AsyncCreate.lpCEI)->LastAccessTime.dwLowDateTime, DavWorkItem->AsyncCreate.UrlBuffer)); SetUrlCacheEntryInfo( DavWorkItem->AsyncCreate.UrlBuffer, (LPINTERNET_CACHE_ENTRY_INFOW)DavWorkItem->AsyncCreate.lpCEI, CACHE_ENTRY_ACCTIME_FC); } WStatus = DavAsyncCreateGet(DavWorkItem); } else if (DavWorkItem->AsyncCreate.AsyncCreateState == AsyncCreateMkCol) { WStatus = DavQueryAndParseResponse(DavWorkItem->AsyncCreate.DavOpenHandle); if (WStatus != ERROR_SUCCESS) { goto EXIT_THE_FUNCTION; } WStatus = ERROR_SUCCESS; } else { ASSERT(DavWorkItem->AsyncCreate.AsyncCreateState == AsyncCreatePut); WStatus = DavQueryAndParseResponse(DavWorkItem->AsyncCreate.DavOpenHandle); if (WStatus != ERROR_SUCCESS) { goto EXIT_THE_FUNCTION; } // // The CreateResponse structure has already been set. All we need to // do now is return. // WStatus = ERROR_SUCCESS; } } break; default: { WStatus = ERROR_INVALID_PARAMETER; DavPrint((DEBUG_ERRORS, "DavAsyncCreate: Invalid DavWorkItem->DavOperation = %d.\n", DavWorkItem->DavOperation)); } break; } // End of switch. EXIT_THE_FUNCTION: // // Free the pEncryptedCachedFile since we have allocated a new file name // for the restored encrypted file. // if (pEncryptedCachedFile) { LocalFree(pEncryptedCachedFile); } if (DavDirectoryAttributes) { DavFinalizeFileAttributesList(DavDirectoryAttributes, TRUE); DavDirectoryAttributes = NULL; } #ifdef DAV_USE_WININET_ASYNCHRONOUSLY // // If we did impersonate, we need to revert back. // if (didImpersonate) { ULONG RStatus; RStatus = UMReflectorRevert(UserWorkItem); if (RStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/UMReflectorRevert. Error Val = %d\n", RStatus)); } } // // 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 ) { DavPrint((DEBUG_MISC, "DavAsyncCreate: Leaving!!! WStatus = %08lx\n", WStatus)); // // 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 AsyncCreateCompletion routine. // DavAsyncCreateCompletion(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); } if (WStatus == ERROR_IO_PENDING ) { DavPrint((DEBUG_MISC, "DavAsyncCreate: Returning ERROR_IO_PENDING.\n")); } #else // // If we are using WinInet synchronously, we need to impersonate the client // if we somehow reverted in between and failed. This is because we came // into this function impersonating a client and the final revert happens // in DavFsCreate. // if ( !didImpersonate ) { ULONG IStatus; IStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (IStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/UMReflectorImpersonate. " "Error Val = %d\n", IStatus)); } } #endif return WStatus; } DWORD DavAsyncCreatePropFind( PDAV_USERMODE_WORKITEM DavWorkItem ) /*++ Routine Description: This routine handles the Get completion. Arguments: DavWorkItem - The DAV_USERMODE_WORKITEM value. Return Value: none. --*/ { ULONG WStatus = ERROR_SUCCESS; NTSTATUS NtStatus = STATUS_SUCCESS; PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL; BOOL ReturnVal, didImpersonate = TRUE; BOOL doesTheFileExist = FALSE; HANDLE FileHandle = INVALID_HANDLE_VALUE; PDAV_USERMODE_CREATE_REQUEST CreateRequest; PDAV_USERMODE_CREATE_RESPONSE CreateResponse; ULONG_PTR CallBackContext = (ULONG_PTR)0; PWCHAR ParentDirectoryName = NULL; BOOL BStatus = FALSE; UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem; // // Get the request and response buffer pointers from the DavWorkItem. // CreateRequest = &(DavWorkItem->CreateRequest); CreateResponse = &(DavWorkItem->CreateResponse); CreateResponse->fPsuedoOpen = FALSE; doesTheFileExist = DavWorkItem->AsyncCreate.doesTheFileExist; DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind: DesiredAccess = %x, FileAttributes = %x," "ShareAccess = %x, CreateDisposition = %x, CreateOptions = %x," "FileName = %ws\n", CreateRequest->DesiredAccess, CreateRequest->FileAttributes, CreateRequest->ShareAccess, CreateRequest->CreateDisposition, CreateRequest->CreateOptions, CreateRequest->CompletePathName)); // // We don't support compression of files or directories over the // DAV Redir since there is no way to do this with the current status // of the protocol (Jan 2001) and hence we filter this flag so that // we never set any attributes. Also, for this version ,we are // emulating FAT which doesn't support compression. Similarly we don't // support the Offline scenario. // CreateRequest->FileAttributes &= ~(FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_OFFLINE); // // If this file is a new file (not a directory), then according to // functionality expected from CreateFile, FILE_ATTRIBUTE_ARCHIVE // should be combined with specified value of attributes. // if ( (doesTheFileExist == FALSE) && !(CreateRequest->CreateOptions & (FILE_DIRECTORY_FILE)) ) { CreateRequest->FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; } // // If the file exists, we need to make sure that a few things are // right before proceeding further. // if (doesTheFileExist) { // // If the dwFileAttributes had the READ_ONLY bit set, then // these cannot be TRUE. // 1. CreateDisposition cannot be FILE_OVERWRITE_IF or // FILE_OVERWRITE or FILE_SUPERSEDE. // 2. CreateDisposition cannot be FILE_DELETE_ON_CLOSE. // 3. DesiredAccess cannot be GENERIC_ALL or GENERIC_WRITE or // FILE_WRITE_DATA or FILE_APPEND_DATA. // This is because these intend to overwrite the existing file. // if ( (CreateResponse->BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY) && ( (CreateRequest->CreateDisposition == FILE_OVERWRITE) || (CreateRequest->CreateDisposition == FILE_OVERWRITE_IF) || (CreateRequest->CreateDisposition == FILE_SUPERSEDE) || (CreateRequest->CreateOptions & (FILE_DELETE_ON_CLOSE) || (CreateRequest->DesiredAccess & (GENERIC_ALL | GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) ) ) ) { DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind: Object Mismatch!!! CreateDisposition = " "%d, DesiredAccess = %x, dwFileAttributes = %x\n", CreateRequest->CreateDisposition, CreateRequest->DesiredAccess, CreateResponse->BasicInformation.FileAttributes)); WStatus = ERROR_ACCESS_DENIED; // mismatch goto EXIT_THE_FUNCTION; } // // If the file is a directory and the caller supplied // FILE_NON_DIRECTORY_FILE as one of the CreateOptions or if the // file as a file and the CreateOptions has FILE_DIRECTORY_FILE // then we return error. There is no good WIN32 errors for these situations. // ERROR_ACCESS_DENIED will cause confusion for EFS. // if ((CreateRequest->CreateOptions & FILE_DIRECTORY_FILE) && !CreateResponse->StandardInformation.Directory) { DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind: Object Mismatch!!! CreateOptions = " "%x, CreateResponse = %x\n", CreateRequest->CreateOptions, CreateResponse->BasicInformation.FileAttributes)); WStatus = STATUS_NOT_A_DIRECTORY; // mismatch goto EXIT_THE_FUNCTION; } if ((CreateRequest->CreateOptions & FILE_NON_DIRECTORY_FILE) && CreateResponse->StandardInformation.Directory) { DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind: Object Mismatch!!! CreateOptions = " "%x, CreateResponse = %x\n", CreateRequest->CreateOptions, CreateResponse->BasicInformation.FileAttributes)); WStatus = STATUS_FILE_IS_A_DIRECTORY; // mismatch goto EXIT_THE_FUNCTION; } } // // We LOCK the resource if the following conditions are TRUE. // 1. The resource already exists on the server AND, // 2. The resource being opened is a file and NOT a directory AND, // 3. The SharedMode is 0 (exclusive) OR FILE_SHARE_READ OR the file is // being opened for write access. // if (DavSupportLockingOfFiles) { if (doesTheFileExist && !CreateResponse->StandardInformation.Directory) { if ( (CreateRequest->ShareAccess == 0) || (CreateRequest->ShareAccess == FILE_SHARE_READ) || (CreateRequest->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE | GENERIC_ALL)) ) { WStatus = DavLockTheFileOnTheServer(DavWorkItem); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/DavLockTheFileOnTheServer. WStatus = %08lx\n", WStatus)); goto EXIT_THE_FUNCTION; } } } } // // If the file exists, we need to set the information field if the // CreateDisposition is one of the following. This is because the // CreateFile API expects these values to be set on return. // if (doesTheFileExist) { switch (CreateRequest->CreateDisposition) { case FILE_OVERWRITE: case FILE_OVERWRITE_IF: DavWorkItem->Information = FILE_OVERWRITTEN; break; case FILE_SUPERSEDE: DavWorkItem->Information = FILE_SUPERSEDED; break; default: DavWorkItem->Information = FILE_OPENED; } } else { DavWorkItem->Information = FILE_CREATED; } // // If the file does not exist on the server, create one locally. // Once its closed, we will PUT it on the server. If the file // exists on the server, and the CreateDisposition is equal to // FILE_OVERWRITE_IF, we create a copy locally and PUT it on the // server (overwrite) on close. // if ( ( !doesTheFileExist ) || ( doesTheFileExist && CreateRequest->CreateDisposition == FILE_OVERWRITE_IF ) ) { DWORD NameLength = 0, i; BOOL BackSlashFound = FALSE; DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind: doesTheFileExist = " "%d, CreateDisposition = %d\n", doesTheFileExist, CreateRequest->CreateDisposition)); DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind: CreateOptions = %d\n", CreateRequest->CreateOptions)); // // We need to check the CreateDisposition value to figure // out what to do next. // switch (CreateRequest->CreateDisposition) { // // If FILE_OPEN was specified, we need to return failure // since the specified file does not exist. // case FILE_OPEN: WStatus = ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND; DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind. CreateDisposition & FILE_OPEN\n")); goto EXIT_THE_FUNCTION; // // If FILE_OVERWRITE was specified, we need to return failure // since the specified file does not exist. // case FILE_OVERWRITE: WStatus = ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND; DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind. CreateDisposition & FILE_OVERWRITE\n")); goto EXIT_THE_FUNCTION; default: break; } if (CreateRequest->ParentDirInfomationCached || (CreateRequest->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) { // // Since we already know whether to encrypt the file, we don't need // to query the parent directory. // if (CreateRequest->ParentDirIsEncrypted || (CreateRequest->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) { CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } BStatus = DavHttpOpenRequestW(DavWorkItem->AsyncCreate.PerUserEntry->DavConnHandle, L"PROPFIND", L"", L"HTTP/1.1", NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_COOKIES, CallBackContext, L"DavAsyncCreatePropFind", &(DavWorkItem->AsyncCreate.DavOpenHandle)); if(BStatus == FALSE) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/DavHttpOpenRequestW failed %x %d\n",WStatus,WStatus)); goto EXIT_THE_FUNCTION; } ASSERT(didImpersonate == TRUE); WStatus = DavAsyncCreateQueryParentDirectory(DavWorkItem); } else { // // We need to query the attributes of the parent directory of this new file // on the server. If it is encrypted, the new file needs to be encrypted as well. // DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind: Query Parent Directory for %ws\n",DavWorkItem->AsyncCreate.RemPathName)); NameLength = wcslen(DavWorkItem->AsyncCreate.RemPathName); for (i=NameLength;i>0;i--) { if (DavWorkItem->AsyncCreate.RemPathName[i] == L'/') { BackSlashFound = TRUE; break; } } if (!BackSlashFound) { DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind: Invalid file path %ws\n",DavWorkItem->AsyncCreate.RemPathName)); WStatus = ERROR_INVALID_PARAMETER; goto EXIT_THE_FUNCTION; } ParentDirectoryName = (PWCHAR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, (i+1)*sizeof(WCHAR)); if (ParentDirectoryName == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } RtlCopyMemory(ParentDirectoryName, DavWorkItem->AsyncCreate.RemPathName, i*sizeof(WCHAR)); DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind/ParentDirectoryName %ws\n",ParentDirectoryName)); // // Set the DavOperation and AsyncCreateState values.For PUT // the DavMinorOperation value is irrelavant. // DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN; DavWorkItem->AsyncCreate.AsyncCreateState = AsyncCreateQueryParentDirectory; DavWorkItem->DavMinorOperation = DavMinorQueryInfo; // // Convert the unicode object name to UTF-8 URL format. // Space and other white characters will remain untouched. // These should be taken care of by wininet calls. // BStatus = DavHttpOpenRequestW(DavWorkItem->AsyncCreate.PerUserEntry->DavConnHandle, L"PROPFIND", ParentDirectoryName, L"HTTP/1.1", NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_COOKIES, CallBackContext, L"DavAsyncCreate", &(DavWorkItem->AsyncCreate.DavOpenHandle)); if(BStatus == FALSE) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/DavHttpOpenRequestW failed %x %d\n",WStatus,WStatus)); goto EXIT_THE_FUNCTION; } if (DavWorkItem->AsyncCreate.DavOpenHandle == NULL) { WStatus = GetLastError(); if (WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/HttpOpenRequestW" ". Error Val = %d\n", WStatus)); } goto EXIT_THE_FUNCTION; } WStatus = DavAsyncCommonStates(DavWorkItem, FALSE); if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/DavAsyncCommonStates(PUT). " "Error Val = %08lx\n", WStatus)); } } goto EXIT_THE_FUNCTION; } else { // // The file exists on the server and the value of CreateDisposition != // FILE_OVERWRITE_IF. // // // We return failure if FILE_CREATE was specified since the // file already exists. // if (CreateRequest->CreateDisposition == FILE_CREATE) { WStatus = ERROR_ALREADY_EXISTS; // STATUS_OBJECT_NAME_COLLISION DavPrint((DEBUG_ERRORS, "DavAsyncCreate. CreateDisposition & FILE_CREATE\n")); goto EXIT_THE_FUNCTION; } } // // If "FILE_DELETE_ON_CLOSE" flag was specified as one of // the CreateOptions, then we need to remember this and // delete this file on close. // if (CreateRequest->CreateOptions & FILE_DELETE_ON_CLOSE) { DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind: FileName: %ws. FILE_DELETE_ON_CLOSE.\n", DavWorkItem->AsyncCreate.RemPathName)); CreateResponse->DeleteOnClose = TRUE; } // // In some cases, we don't need to do a GET. // if (CreateResponse->StandardInformation.Directory) { // // We do not need to GET a directory. // goto EXIT_THE_FUNCTION; } else if (!(CreateResponse->BasicInformation.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && (CreateRequest->DesiredAccess & ~(SYNCHRONIZE | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)) == 0 ) { // // If we don't need to GET the file from the server because the // user doesn't intend to manipulate the data, we return right // now. We call such an open a Pseudo open.Set the fPsuedoOpen // field to TRUE in the CreateResponse. // CreateResponse->fPsuedoOpen = TRUE; goto EXIT_THE_FUNCTION; } else { if (didImpersonate) { RevertToSelf(); didImpersonate = FALSE; } DavQueryUrlCacheEntry(DavWorkItem); if (DavWorkItem->AsyncCreate.lpCEI) { SYSTEMTIME SystemTime; FILETIME CurrentFileTime; FILETIME LastAccessTime; LARGE_INTEGER Difference; LPINTERNET_CACHE_ENTRY_INFOW lpCEI = (LPINTERNET_CACHE_ENTRY_INFOW)DavWorkItem->AsyncCreate.lpCEI; LastAccessTime = ((LPINTERNET_CACHE_ENTRY_INFOW)DavWorkItem->AsyncCreate.lpCEI)->LastAccessTime; GetSystemTime(&SystemTime); SystemTimeToFileTime(&SystemTime,&CurrentFileTime); Difference.QuadPart = *((LONGLONG *)(&CurrentFileTime)) - *((LONGLONG *)(&LastAccessTime)); // // If the local cache has not timed out, we don't need to query the server // if (Difference.QuadPart < FileCacheExpiryInterval) { DavPrint((DEBUG_MISC, "DavAsyncCreatePropFind/Skip GET %u %u %u %ws\n", CurrentFileTime.dwLowDateTime, LastAccessTime.dwLowDateTime, Difference.LowPart, DavWorkItem->AsyncCreate.UrlBuffer)); ASSERT(DavWorkItem->AsyncCreate.FileName == NULL); DavWorkItem->AsyncCreate.FileName = LocalAlloc(LPTR, (lstrlen(lpCEI->lpszLocalFileName)+1)*sizeof(WCHAR)); if (DavWorkItem->AsyncCreate.FileName) { wcscpy(DavWorkItem->CreateResponse.FileName, lpCEI->lpszLocalFileName); wcscpy(DavWorkItem->AsyncCreate.FileName, lpCEI->lpszLocalFileName); ASSERT(DavWorkItem->AsyncCreate.FileHandle == NULL); // // Create a handle to the file whose entry was created in // the cache. // DavWorkItem->AsyncCreate.FileHandle = CreateFileW(DavWorkItem->AsyncCreate.FileName, (GENERIC_READ | GENERIC_WRITE), FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (DavWorkItem->AsyncCreate.FileHandle == INVALID_HANDLE_VALUE) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/CreateFile. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } WStatus = DavAsyncCreateGet(DavWorkItem); didImpersonate = TRUE; goto EXIT_THE_FUNCTION; } else { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/CreateFile. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } } } WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/UMReflectorImpersonate. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; } // // PROPFIND is done. Now we need to do a GET. // DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN; DavWorkItem->AsyncCreate.AsyncCreateState = AsyncCreateGet; DavWorkItem->DavMinorOperation = DavMinorReadData; // // Convert the unicode object name to UTF-8 URL format. // Space and other white characters will remain untouched - these // should be taken care of by wininet calls. // BStatus = DavHttpOpenRequestW(DavWorkItem->AsyncCreate.PerUserEntry->DavConnHandle, L"GET", DavWorkItem->AsyncCreate.RemPathName, L"HTTP/1.1", NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES, CallBackContext, L"DavAsyncCreate", &(DavWorkItem->AsyncCreate.DavOpenHandle)); if(BStatus == FALSE) { WStatus = GetLastError(); goto EXIT_THE_FUNCTION; } if (DavWorkItem->AsyncCreate.DavOpenHandle == NULL) { WStatus = GetLastError(); if (WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/HttpOpenRequest. Error Val = %d\n", WStatus)); } goto EXIT_THE_FUNCTION; } ASSERT(didImpersonate); RevertToSelf(); didImpersonate = FALSE; // try to add if-modified-since header. don't sweat it if we fail DavAddIfModifiedSinceHeader(DavWorkItem); WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/UMReflectorImpersonate. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; WStatus = DavAsyncCommonStates(DavWorkItem, FALSE); if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/DavAsyncCommonStates. " "Error Val(GET) = %08lx\n", WStatus)); } EXIT_THE_FUNCTION: if (ParentDirectoryName) { LocalFree(ParentDirectoryName); } // // Impersonate back again, so that we are in the context of // the user who issued this request. // if (!didImpersonate) { ULONG LocalStatus; LocalStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (LocalStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreatePropFind/UMReflectorImpersonate. " "Error Val = %d\n", LocalStatus)); if (WStatus == ERROR_SUCCESS) { WStatus = LocalStatus; } } } if (WStatus != ERROR_SUCCESS && WStatus != ERROR_FILE_NOT_FOUND) { DavPrint((DEBUG_ERRORS,"DavAsyncCreatePropFind return %x\n", WStatus)); } return WStatus; } DWORD DavAsyncCreateQueryParentDirectory( PDAV_USERMODE_WORKITEM DavWorkItem ) /*++ Routine Description: This routine handles the Get completion. Arguments: DavWorkItem - The DAV_USERMODE_WORKITEM value. Return Value: none. --*/ { ULONG WStatus = ERROR_SUCCESS; NTSTATUS NtStatus = STATUS_SUCCESS; PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL; BOOL ReturnVal, didImpersonate = TRUE; BOOL doesTheFileExist = FALSE; HANDLE FileHandle = INVALID_HANDLE_VALUE; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; SECURITY_QUALITY_OF_SERVICE QualityOfService; PDAV_USERMODE_CREATE_REQUEST CreateRequest; PDAV_USERMODE_CREATE_RESPONSE CreateResponse; UNICODE_STRING UnicodeFileName; ULONG_PTR CallBackContext = (ULONG_PTR)0; ACCESS_MASK DesiredAccess = 0; BOOL BStatus = FALSE, ShouldEncrypt = FALSE; UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem; UnicodeFileName.Buffer = NULL; UnicodeFileName.Length = 0; UnicodeFileName.MaximumLength = 0; // // Get the request and response buffer pointers from the DavWorkItem. // CreateRequest = &(DavWorkItem->CreateRequest); CreateResponse = &(DavWorkItem->CreateResponse); if (CreateResponse->BasicInformation.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { ShouldEncrypt = TRUE; } // // If this is a create of a new directory, then we need to // send a MKCOL to the server to create this new directory. // If this is a create of a file, then the create // options will have FILE_DIRECTORY_FILE set. // if ( !(CreateRequest->CreateOptions & FILE_DIRECTORY_FILE) ) { // // This Create is for a file. // This thread is currently impersonating the client that // made this request. Before we call DavDavCreateUrlCacheEntry, // we need to revert back to the context of the Web Client // service. // RevertToSelf(); didImpersonate = FALSE; // // Call DavCreateUrlCacheEntry to create an entry in the // WinInet's cache. // WStatus = DavCreateUrlCacheEntry(DavWorkItem); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavCreateUrlCacheEntry(1). " "WStatus = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // Call DavCommitUrlCacheEntry to commit (pin) the entry // created above in the WinInet's cache. // WStatus = DavCommitUrlCacheEntry(DavWorkItem); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavCommitUrlCacheEntry(1). " "WStatus = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } if (ShouldEncrypt) { // // if the file will be encrypted, we set the ACL to allow everyone to access. This // is because the thread needs to be impersonated to do encrypt the file in the user's // context. The URL cache in created in the Local System's context which won't be // accessiable to the user if the ACL is not set. // WStatus = DavSetAclForEncryptedFile(DavWorkItem->AsyncCreate.FileName); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavSetAclForEncryptedFile(2). Error Val = %d, FileName = %ws\n", WStatus, DavWorkItem->AsyncCreate.FileName)); goto EXIT_THE_FUNCTION; } } // // Impersonate back again, so that we are in the context of // the user who issued this request. // WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/UMReflectorImpersonate. " "Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; // // This file exists on the server, but this create operation // has FILE_OVERWRITE_IF as its CreateDisposition. So, we // can create this file locally overwrite the one on the // server on close. // if (CreateRequest->CreateDisposition == FILE_OVERWRITE_IF) { DavPrint((DEBUG_MISC, "DavAsyncCreate/QueryPDirectory: FileName: %ws. ExistsAndOverWriteIf = TRUE\n", DavWorkItem->AsyncCreate.FileName)); ASSERT(CreateRequest->CreateDisposition == FILE_OVERWRITE_IF); CreateResponse->ExistsAndOverWriteIf = TRUE; } // // If "FILE_DELETE_ON_CLOSE" flag was specified as one of // the CreateOptions, then we need to remember this and // delete this file on close. // if (CreateRequest->CreateOptions & FILE_DELETE_ON_CLOSE) { DavPrint((DEBUG_MISC, "DavAsyncCreate/QueryPDirectory: FileName: %ws. DeleteOnClose = TRUE\n", DavWorkItem->AsyncCreate.FileName)); CreateResponse->DeleteOnClose = TRUE; } // // Create the file handle to be returned back to the kernel. // QualityOfService.Length = sizeof(QualityOfService); QualityOfService.ImpersonationLevel = CreateRequest->ImpersonationLevel; QualityOfService.ContextTrackingMode = FALSE; QualityOfService.EffectiveOnly = (BOOLEAN) (CreateRequest->SecurityFlags & DAV_SECURITY_EFFECTIVE_ONLY); // // Create an NT path name for the cached file. This is used in the // NtCreateFile call below. // ReturnVal = RtlDosPathNameToNtPathName_U(DavWorkItem->AsyncCreate.FileName, &UnicodeFileName, NULL, NULL); if (!ReturnVal) { WStatus = ERROR_BAD_PATHNAME; DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/RtlDosPathNameToNtPathName. " "Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } InitializeObjectAttributes(&ObjectAttributes, &UnicodeFileName, OBJ_CASE_INSENSITIVE, 0, NULL); if (CreateRequest->SecurityDescriptor != NULL) { ObjectAttributes.SecurityDescriptor = CreateRequest->SecurityDescriptor; } ObjectAttributes.SecurityQualityOfService = &QualityOfService; // // If CreateRequest->CreateDisposition == FILE_CREATE, then // the NtCreateFile operation below will fail because we // have already created the file with the CreateUrlCacheEntry // call. So we change the value to FILE_OPEN_IF. // if (CreateRequest->CreateDisposition == FILE_CREATE) { CreateRequest->CreateDisposition = FILE_OPEN_IF; } if (ShouldEncrypt) { // // The file is encrypted in the user's context // BStatus = EncryptFile(DavWorkItem->AsyncCreate.FileName); if (BStatus) { DavPrint((DEBUG_MISC, "DavAsyncCreate: Local cache is encrypted %wZ\n", &UnicodeFileName)); CreateResponse->LocalFileIsEncrypted = TRUE; CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } else { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/EncryptFile. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } } else { DavPrint((DEBUG_MISC, "DavAsyncCreate: Local cache is not encrypted %wZ\n", &UnicodeFileName)); // // This thread is currently impersonating the client that // made this request. Before we call NtCreateFile, we need // to revert back to the context of the Web Client service. // RevertToSelf(); didImpersonate = FALSE; } // // We use FILE_SHARE_VALID_FLAGS for share access because RDBSS // checks this for us. Moreover, we delay the close after the final // close happens and this could cause problems. Consider the scenario. // 1. Open with NO share access. // 2. We create a local handle with this share access. // 3. The app closes the handle. We delay the close and keep the local // handle. // 4. Another open comes with any share access. This will be // conflicting share access since the first one was done with no // share access. This should succeed since the previous open has // been closed from the app and the I/O systems point of view. // 5. It will not if we have created the local handle with the share // access which came with the first open. // Therefore we need to pass FILE_SHARE_VALID_FLAGS while creating // the local handle. // // // We have FILE_NO_INTERMEDIATE_BUFFERING ORed with the CreateOptions // the user specified, becuase we don't want the underlying file system // to create another cache map. This way all the I/O that comes to us // will directly go to the disk. BUG 128843 in the Windows RAID database // explains some deadlock scenarios that could happen with PagingIo if // we don't do this. Also since we supply the FILE_NO_INTERMEDIATE_BUFFERING // option we filter out the FILE_APPEND_DATA from the DesiredAccess flags // since the filesystem expects this. // // // We also always create the file with DesiredAccess ORed with FILE_WRITE_DATA // if either FILE_READ_DATA or FILE_EXECUTE was specified because there // can be situations where we get write IRPs on a FILE_OBJECT which was // not opened with Write Access and was only opened with FILE_READ_DATA // or FILE_EXECUTE. This is BUG 284557. To get around the problem, we do // this. // // // We filter the FILE_ATTRIBUTE_READONLY attribute during the create. // This is done because we store the READ_ONLY bit in the FCB and do // the checks at the RDBSS level before going to the local filesystem. // Also, since some of our creates open the file with FILE_WRITE_DATA, // if someone creates a read_only file and we stamp the read_only // attribute on the local file then all subsequent creates will fail // since we always ask for Write access to the underlying file as // explained above. // DesiredAccess = (CreateRequest->DesiredAccess & ~(FILE_APPEND_DATA)); if ( DesiredAccess & (FILE_READ_DATA | FILE_EXECUTE) ) { DesiredAccess |= (FILE_WRITE_DATA); } NtStatus = NtCreateFile(&(FileHandle), DesiredAccess, &ObjectAttributes, &IoStatusBlock, &CreateRequest->AllocationSize, (CreateRequest->FileAttributes & ~FILE_ATTRIBUTE_READONLY), FILE_SHARE_VALID_FLAGS, CreateRequest->CreateDisposition, (CreateRequest->CreateOptions | FILE_NO_INTERMEDIATE_BUFFERING), CreateRequest->EaBuffer, CreateRequest->EaLength); if (NtStatus != STATUS_SUCCESS) { // // We convert the NtStatus to DOS error here. The Win32 // error code is finally set to an NTSTATUS value in // the DavFsCreate function just before returning. // WStatus = RtlNtStatusToDosError(NtStatus); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/NtCreateFile(1). Error Val = " "%08lx\n", NtStatus)); CreateResponse->Handle = NULL; CreateResponse->UserModeKey = NULL; goto EXIT_THE_FUNCTION; } DavPrint((DEBUG_MISC, "DavAsyncCreate/QueryPDirectory: DavWorkItem = %08lx.\n", DavWorkItem)); DavPrint((DEBUG_MISC, "DavAsyncCreate/QueryPDirectory: FileHandle = %08lx.\n", FileHandle)); CreateResponse->Handle = FileHandle; CreateResponse->UserModeKey = (PVOID)FileHandle; // // If the file already exists on the server, then we don't // need to create it and are done. // if (DavWorkItem->AsyncCreate.doesTheFileExist) { DavPrint((DEBUG_MISC, "DavAsyncCreate/QueryPDirectory: doesTheFileExist == TRUE\n")); WStatus = ERROR_SUCCESS; goto EXIT_THE_FUNCTION; } else { SYSTEMTIME CurrentSystemTime, NewSystemTime; FILETIME CurrentFileTime; BOOL ConvertTime = FALSE; LARGE_INTEGER CurrentTime; WCHAR chTimeBuff[INTERNET_RFC1123_BUFSIZE + 4]; // // This file may have been created locally and does not exist // on the server. We need to remember this information and // set attributes on this file on the server on close. // if (CreateRequest->FileAttributes != 0) { CreateResponse->NewFileCreatedAndSetAttributes = TRUE; // // Copy the attributes in the CreateResponse. These // will get PROPPATCHED to the server on Close. // CreateResponse->BasicInformation.FileAttributes = CreateRequest->FileAttributes; if (ShouldEncrypt) { CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } } DavPrint((DEBUG_MISC, "DavAsyncCreate/QueryPDirectory NewFileCreatedAndSetAttributes %x %x %ws\n", CreateRequest->FileAttributes, CreateResponse->BasicInformation.FileAttributes, DavWorkItem->AsyncCreate.FileName)); // // We also need to set the FILE_BASIC_INFORMATION time values to // the current time. We get the systemtime, convert it into the // RFC1123 format and then convert the format back into systemtime. // We do this because on close when we PROPPATCH these times we send // them in the RFC1123 format. Since the least count of this format is // seconds, some data is lost when we convert the LARGE_INTEGER to // RFC1123 format and back. So, we lose this data right now to be // consistent. To give an example about the loss, see below. // CreationTime.LowPart = 802029d0, CreationTime.HighPart = 1c0def1 // maps to "Thu, 17 May 2001 16:50:38 GMT" // And "Thu, 17 May 2001 16:50:38 GMT" is what we get back when we do a // PROPFIND which converts back into // CreationTime.LowPart = 7fdc4300, CreationTime.HighPart = 1c0def1 // Note that the LowPart is different. So, the values in the name cache // and the server will be different. To avoid this inconsistency we lose // this data by doing the conversion right away. // GetSystemTime( &(CurrentSystemTime) ); RtlZeroMemory(chTimeBuff, sizeof(chTimeBuff)); ConvertTime = InternetTimeFromSystemTimeW(&(CurrentSystemTime), INTERNET_RFC1123_FORMAT, chTimeBuff, sizeof(chTimeBuff)); if (ConvertTime) { ConvertTime = InternetTimeToSystemTimeW(chTimeBuff, &(NewSystemTime), 0); if (ConvertTime) { ConvertTime = SystemTimeToFileTime( &(NewSystemTime), &(CurrentFileTime) ); if (ConvertTime) { CreateResponse->PropPatchTheTimeValues = TRUE; CurrentTime.LowPart = CurrentFileTime.dwLowDateTime; CurrentTime.HighPart = CurrentFileTime.dwHighDateTime; CreateResponse->BasicInformation.CreationTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.LastAccessTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.LastWriteTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.ChangeTime.QuadPart = CurrentTime.QuadPart; } } } // // If the above conversion from systemtime to RFC1123 format and then // back to systemtime from RFc1123 format failed then we go ahead and // convert the systemtime to filetime and use that. // if (!ConvertTime) { ConvertTime = SystemTimeToFileTime( &(CurrentSystemTime), &(CurrentFileTime) ); if (ConvertTime) { CreateResponse->PropPatchTheTimeValues = TRUE; CurrentTime.LowPart = CurrentFileTime.dwLowDateTime; CurrentTime.HighPart = CurrentFileTime.dwHighDateTime; CreateResponse->BasicInformation.CreationTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.LastAccessTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.LastWriteTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.ChangeTime.QuadPart = CurrentTime.QuadPart; } else { // // This is not a fatal error. We can still continie with the // Create call. // DavPrint((DEBUG_ERRORS, "DavAsyncCreateQueryParentDirectory/SystemTimeToFileTime(1): %x\n", GetLastError())); } } } // // We are done with the Open handle to PROPFIND. // Now we need to create the directory on the server. // if (DavWorkItem->AsyncCreate.DavOpenHandle) { InternetCloseHandle(DavWorkItem->AsyncCreate.DavOpenHandle); DavWorkItem->AsyncCreate.DavOpenHandle = NULL; } // // We need to "PUT" this new file on the server. // DavPrint((DEBUG_MISC, "DavAsyncCreate/QueryPDirectory: PUT New File\n")); // // If we are not currently impersonating, we need to impersonate back // again, so that we are in the context of the user who issued this // request. // if (didImpersonate == FALSE) { WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/UMReflectorImpersonate. " "Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; } // // Set the DavOperation and AsyncCreateState values.For PUT // the DavMinorOperation value is irrelavant. // DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN; DavWorkItem->AsyncCreate.AsyncCreateState = AsyncCreatePut; // // Convert the unicode object name to UTF-8 URL format. // Space and other white characters will remain untouched. // These should be taken care of by wininet calls. // BStatus = DavHttpOpenRequestW(DavWorkItem->AsyncCreate.PerUserEntry->DavConnHandle, L"PUT", DavWorkItem->AsyncCreate.RemPathName, L"HTTP/1.1", NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_COOKIES, CallBackContext, L"QueryPDirectory", &(DavWorkItem->AsyncCreate.DavOpenHandle)); if(BStatus == FALSE) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS,"DavAsyncCreate/QueryPDirectory/DavHttpOpenRequestW error: %x\n",WStatus)); goto EXIT_THE_FUNCTION; } if (DavWorkItem->AsyncCreate.DavOpenHandle == NULL) { WStatus = GetLastError(); if (WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/HttpOpenRequestW" ". Error Val = %d\n", WStatus)); } goto EXIT_THE_FUNCTION; } WStatus = DavAsyncCommonStates(DavWorkItem, FALSE); if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/DavAsyncCommonStates(PUT). " "Error Val = %08lx\n", WStatus)); } // // We LOCK the resource if the following conditions are TRUE. // 1. The resource already exists on the server AND, // 2. The resource being opened is a file and NOT a directory AND, // 3. The SharedMode is 0 (exclusive) OR FILE_SHARE_READ OR the file is // being opened for write access. // if (DavSupportLockingOfFiles) { if ( (CreateRequest->ShareAccess == 0) || (CreateRequest->ShareAccess == FILE_SHARE_READ) || (CreateRequest->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE | GENERIC_ALL)) ) { WStatus = DavLockTheFileOnTheServer(DavWorkItem); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreateQueryParentDirectory/DavLockTheFileOnTheServer. WStatus = %08lx\n", WStatus)); goto EXIT_THE_FUNCTION; } } } goto EXIT_THE_FUNCTION; } else { SYSTEMTIME CurrentSystemTime, NewSystemTime; FILETIME CurrentFileTime; BOOL ConvertTime = FALSE; LARGE_INTEGER CurrentTime; WCHAR chTimeBuff[INTERNET_RFC1123_BUFSIZE + 4]; // // We are done with the Open handle to PROPFIND. // Now we need to create the directory on the server. // InternetCloseHandle(DavWorkItem->AsyncCreate.DavOpenHandle); DavWorkItem->AsyncCreate.DavOpenHandle = NULL; // // This Create is for a Directory. We need to send an // MKCOL to the server. // DavPrint((DEBUG_MISC, "DavAsyncCreate/QueryPDirectory: Create Directory\n")); // // Set the DavOperation and AsyncCreateState values. // For MKCOL the DavMinorOperation value is irrelavant. // DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN; DavWorkItem->AsyncCreate.AsyncCreateState = AsyncCreateMkCol; // // The data is parsed. We now need to set the file attributes in the // response buffer. // CreateResponse->BasicInformation.FileAttributes = CreateRequest->FileAttributes; CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; CreateResponse->StandardInformation.Directory = TRUE; // // Since we are creating a new directory we need to PROPPATCH the // attributes on the directory that is getting created below on close. // CreateResponse->NewFileCreatedAndSetAttributes = TRUE; if (ShouldEncrypt) { DavPrint((DEBUG_MISC, "DavAsyncCreate: New directory is encrypted\n")); CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } // // We also need to set the FILE_BASIC_INFORMATION time values to // the current time. We get the systemtime, convert it into the // RFC1123 format and then convert the format back into systemtime. // We do this because on close when we PROPPATCH these times we send // them in the RFC1123 format. Since the least count of this format is // seconds, some data is lost when we convert the LARGE_INTEGER to // RFC1123 format and back. So, we lose this data right now to be // consistent. To give an example about the loss, see below. // CreationTime.LowPart = 802029d0, CreationTime.HighPart = 1c0def1 // maps to "Thu, 17 May 2001 16:50:38 GMT" // And "Thu, 17 May 2001 16:50:38 GMT" is what we get back when we do a // PROPFIND which converts back into // CreationTime.LowPart = 7fdc4300, CreationTime.HighPart = 1c0def1 // Note that the LowPart is different. So, the values in the name cache // and the server will be different. To avoid this inconsistency we lose // this data by doing the conversion right away. // GetSystemTime( &(CurrentSystemTime) ); RtlZeroMemory(chTimeBuff, sizeof(chTimeBuff)); ConvertTime = InternetTimeFromSystemTimeW(&(CurrentSystemTime), INTERNET_RFC1123_FORMAT, chTimeBuff, sizeof(chTimeBuff)); if (ConvertTime) { ConvertTime = InternetTimeToSystemTimeW(chTimeBuff, &(NewSystemTime), 0); if (ConvertTime) { ConvertTime = SystemTimeToFileTime( &(NewSystemTime), &(CurrentFileTime) ); if (ConvertTime) { CreateResponse->PropPatchTheTimeValues = TRUE; CurrentTime.LowPart = CurrentFileTime.dwLowDateTime; CurrentTime.HighPart = CurrentFileTime.dwHighDateTime; CreateResponse->BasicInformation.CreationTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.LastAccessTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.LastWriteTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.ChangeTime.QuadPart = CurrentTime.QuadPart; } } } // // If the above conversion from systemtime to RFC1123 format and then // back to systemtime from RFc1123 format failed then we go ahead and // convert the systemtime to filetime and use that. // if (!ConvertTime) { ConvertTime = SystemTimeToFileTime( &(CurrentSystemTime), &(CurrentFileTime) ); if (ConvertTime) { CreateResponse->PropPatchTheTimeValues = TRUE; CurrentTime.LowPart = CurrentFileTime.dwLowDateTime; CurrentTime.HighPart = CurrentFileTime.dwHighDateTime; CreateResponse->BasicInformation.CreationTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.LastAccessTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.LastWriteTime.QuadPart = CurrentTime.QuadPart; CreateResponse->BasicInformation.ChangeTime.QuadPart = CurrentTime.QuadPart; } else { // // This is not a fatal error. We can still continie with the // Create call. // DavPrint((DEBUG_ERRORS, "DavAsyncCreateQueryParentDirectory/SystemTimeToFileTime(2): %x\n", GetLastError())); } } // // Convert the unicode object name to UTF-8 URL format. // Space and other white characters will remain untouched. // These should be taken care of by wininet calls. // BStatus = DavHttpOpenRequestW(DavWorkItem->AsyncCreate.PerUserEntry->DavConnHandle, L"MKCOL", DavWorkItem->AsyncCreate.RemPathName, L"HTTP/1.1", NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_COOKIES, CallBackContext, L"QueryPDirectory", &(DavWorkItem->AsyncCreate.DavOpenHandle )); if(BStatus == FALSE) { WStatus = GetLastError(); goto EXIT_THE_FUNCTION; } if (DavWorkItem->AsyncCreate.DavOpenHandle == NULL) { WStatus = GetLastError(); if (WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/HttpOpenRequestW" ". Error Val = %d\n", WStatus)); } goto EXIT_THE_FUNCTION; } WStatus = DavAsyncCommonStates(DavWorkItem, FALSE); if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/DavAsyncCommonStates(MKCOL). " "Error Val = %08lx\n", WStatus)); } goto EXIT_THE_FUNCTION; } EXIT_THE_FUNCTION: // // The function RtlDosPathNameToNtPathName_U allocates memory from the // processes heap. If we did, we need to free it now. // if (UnicodeFileName.Buffer != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, UnicodeFileName.Buffer); } if (!didImpersonate) { ULONG LocalStatus; // // Impersonate back again, so that we are in the context of // the user who issued this request. // LocalStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (LocalStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/UMReflectorImpersonate. " "Error Val = %d\n", LocalStatus)); if (WStatus == ERROR_SUCCESS) { WStatus = LocalStatus; } } } return WStatus; } DWORD DavAsyncCreateGet( PDAV_USERMODE_WORKITEM DavWorkItem ) /*++ Routine Description: This routine handles the Get completion. Arguments: DavWorkItem - The DAV_USERMODE_WORKITEM value. Return Value: none. --*/ { ULONG WStatus = ERROR_SUCCESS; NTSTATUS NtStatus = STATUS_SUCCESS; PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL; BOOL ReturnVal, didImpersonate = FALSE; HANDLE FileHandle = INVALID_HANDLE_VALUE; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; SECURITY_QUALITY_OF_SERVICE QualityOfService; PDAV_USERMODE_CREATE_REQUEST CreateRequest; PDAV_USERMODE_CREATE_RESPONSE CreateResponse; UNICODE_STRING UnicodeFileName; ULONG_PTR CallBackContext = (ULONG_PTR)0; PWCHAR pEncryptedCachedFile = NULL; ACCESS_MASK DesiredAccess = 0; BOOL EncryptedFile = FALSE; FILE_STANDARD_INFORMATION FileStdInfo; UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem; UnicodeFileName.Buffer = NULL; UnicodeFileName.Length = 0; UnicodeFileName.MaximumLength = 0; // // Get the request and response buffer pointers from the DavWorkItem. // CreateRequest = &(DavWorkItem->CreateRequest); CreateResponse = &(DavWorkItem->CreateResponse); CreateResponse->fPsuedoOpen = FALSE; if (CreateResponse->BasicInformation.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { // // This file is encrypted. We need to restore the file. For doing this // we need to create another entry in the WinInet cache in which the // file will be restored. // DavPrint((DEBUG_MISC, "DavAsyncCreateGet: This is an Encrypted File.\n")); EncryptedFile = TRUE; // // Save the encrypted file name. // pEncryptedCachedFile = DavWorkItem->AsyncCreate.FileName; DavWorkItem->AsyncCreate.FileName = NULL; WStatus = DavCreateUrlCacheEntry(DavWorkItem); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/CreateUrlCacheEntry. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } DavPrint((DEBUG_MISC, "DavAsyncCreateGet: EncryptedCachedFile = %ws\n", pEncryptedCachedFile)); DavPrint((DEBUG_MISC, "DavAsyncCreateGet: NewFileName = %ws\n", DavWorkItem->AsyncCreate.FileName)); WStatus = DavSetAclForEncryptedFile(DavWorkItem->AsyncCreate.FileName); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavSetAclForEncryptedFile(3). Error Val = %d, FileName = %ws\n", WStatus, DavWorkItem->AsyncCreate.FileName)); goto EXIT_THE_FUNCTION; } if (DavWorkItem->AsyncCreate.FileHandle != NULL) { // // Close the opened file handle since we don't need it anymore. We // close the file after setting the ACLs so that the file won't be // scavenged by WinInet by any chance. // ReturnVal = CloseHandle(DavWorkItem->AsyncCreate.FileHandle); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/CloseHandle. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } DavWorkItem->AsyncCreate.FileHandle = NULL; } WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/UMReflectorImpersonate. " "Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; WStatus = DavRestoreEncryptedFile(pEncryptedCachedFile, DavWorkItem->AsyncCreate.FileName); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/DavRestoreEncryptedFile. Error Val" " = %d %x %x\n", WStatus, CreateRequest->FileAttributes, CreateResponse->BasicInformation.FileAttributes)); goto EXIT_THE_FUNCTION; } // // Copy the "new" file name in the response buffer. // wcscpy(CreateResponse->FileName, DavWorkItem->AsyncCreate.FileName); CreateResponse->LocalFileIsEncrypted = TRUE; // // Don't commit the restored EFS file so that the next open will still // see the file in the back up format and the EFS header. // } else { if (DavWorkItem->AsyncCreate.FileHandle != NULL) { // // Close the opened file handle since we don't need it anymore. // ReturnVal = CloseHandle(DavWorkItem->AsyncCreate.FileHandle); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/CloseHandle. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } DavWorkItem->AsyncCreate.FileHandle = NULL; } // // If the file already exists, encryption can only be taken place if the // CreateDisposition is not FILE_SUPERSEDE, FILE_OVERWRITE or // FILE_OVERWRITE_IF. // if ((CreateRequest->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && ((CreateRequest->CreateDisposition == FILE_SUPERSEDE) || (CreateRequest->CreateDisposition == FILE_OVERWRITE) || (CreateRequest->CreateDisposition == FILE_OVERWRITE_IF))) { WStatus = DavSetAclForEncryptedFile(DavWorkItem->AsyncCreate.FileName); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreate/DavSetAclForEncryptedFile(4). Error Val = %d, FileName = %ws\n", WStatus, DavWorkItem->AsyncCreate.FileName)); goto EXIT_THE_FUNCTION; } WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/UMReflectorImpersonate. " "Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; // // The file is encrypted in the user's context // if (EncryptFile(DavWorkItem->AsyncCreate.FileName)) { DavPrint((DEBUG_MISC, "DavAsyncCreate: Local cache is encrypted %wZ\n", &UnicodeFileName)); CreateResponse->LocalFileIsEncrypted = TRUE; CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; } else { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreate/QueryPDirectory/EncryptFile. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } CreateRequest->FileAttributes &= ~FILE_ATTRIBUTE_ENCRYPTED; CreateResponse->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_ENCRYPTED; CreateResponse->LocalFileIsEncrypted = TRUE; } } #ifdef WEBCLIENT_SUPPORTS_BACKUP_RESTORE_FOR_EFS // // Enable the Backup/Restore privilege on the thread for the encrypted file // so that Backup/Restore operation can be done to the file even if the // the thread is not impersonated to the owner of the file. // if (EncryptedFile) { PTOKEN_PRIVILEGES pPrevPriv = NULL; DWORD cbPrevPriv = sizeof(TOKEN_PRIVILEGES); BYTE rgbNewPriv[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; PTOKEN_PRIVILEGES pNewPriv = (PTOKEN_PRIVILEGES)rgbNewPriv; HANDLE hToken = 0; for (;;) { if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken)) { DbgPrint("OpenThreadToken failed %d\n", GetLastError()); break; // need to close the hToken at the end. } // set up the new priviledge state memset(rgbNewPriv, 0, sizeof(rgbNewPriv)); pNewPriv->PrivilegeCount = 1; if(!LookupPrivilegeValueW(NULL, SE_SECURITY_NAME, &(pNewPriv->Privileges[0].Luid))) { DbgPrint("LookupPrivilegeValueW failed \n"); break; } pNewPriv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; pNewPriv->Privileges[0].Luid = RtlConvertLongToLuid(SE_RESTORE_PRIVILEGE); // alloc for the previous state pPrevPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LMEM_ZEROINIT,sizeof(TOKEN_PRIVILEGES)); if (!pPrevPriv) { DbgPrint("LocalAlloc for Adjust token failed \n"); break; } // adjust the priviledge and get the previous state if (!AdjustTokenPrivileges(hToken, FALSE, pNewPriv, cbPrevPriv, (PTOKEN_PRIVILEGES)pPrevPriv, &cbPrevPriv)) { DbgPrint("AdjustTokenPrivileges failed %d\n", GetLastError()); break; } DbgPrint("AdjustTokenPrivileges succeeded\n") break; } if (pPrevPriv) { LocalFree(pPrevPriv); } } #endif // // Create the file handle to be returned back to the kernel. // QualityOfService.Length = sizeof(QualityOfService); QualityOfService.ImpersonationLevel = CreateRequest->ImpersonationLevel; QualityOfService.ContextTrackingMode = FALSE; QualityOfService.EffectiveOnly = (BOOLEAN)(CreateRequest->SecurityFlags & DAV_SECURITY_EFFECTIVE_ONLY); // // Create an NT path name for the cached file. This is used in the // NtCreateFile call below. // ReturnVal = RtlDosPathNameToNtPathName_U(DavWorkItem->AsyncCreate.FileName, &UnicodeFileName, NULL, NULL); if (!ReturnVal) { WStatus = ERROR_BAD_PATHNAME; DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/RtlDosPathNameToNtPathName. " "Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } InitializeObjectAttributes(&ObjectAttributes, &UnicodeFileName, OBJ_CASE_INSENSITIVE, 0, NULL); if (CreateRequest->SecurityDescriptor != NULL) { ObjectAttributes.SecurityDescriptor = CreateRequest->SecurityDescriptor; } ObjectAttributes.SecurityQualityOfService = &QualityOfService; // // We use FILE_SHARE_VALID_FLAGS for share access because RDBSS // checks this for us. Moreover, we delay the close after the final // close happens and this could cause problems. Consider the scenario. // 1. Open with NO share access. // 2. We create a local handle with this share access. // 3. The app closes the handle. We delay the close and keep the local // handle. // 4. Another open comes with any share access. This will be // conflicting share access since the first one was done with no // share access. This should succeed since the previous open has // been closed from the app and the I/O systems point of view. // 5. It will not if we have created the local handle with the share // access which came with the first open. // Therefore we need to pass FILE_SHARE_VALID_FLAGS while creating // the local handle. // // // We have FILE_NO_INTERMEDIATE_BUFFERING ORed with the CreateOptions // the user specified, becuase we don't want the underlying file system // to create another cache map. This way all the I/O that comes to us // will directly go to the disk. BUG 128843 in the Windows RAID database // explains some deadlock scenarios that could happen with PagingIo if // we don't do this. Also since we supply the FILE_NO_INTERMEDIATE_BUFFERING // option we filter out the FILE_APPEND_DATA from the DesiredAccess flags // since the filesystem expects this. // // // We also always create the file with DesiredAccess ORed with FILE_WRITE_DATA // if either FILE_READ_DATA or FILE_EXECUTE was specified because there can // be situations where we get write IRPs on a FILE_OBJECT which was not // opened with Write Access and was only opened with FILE_READ_DATA or // FILE_EXECUTE. This is BUG 284557. To get around the problem, we do this. // // // We filter the FILE_ATTRIBUTE_READONLY attribute during the create. // This is done because we store the READ_ONLY bit in the FCB and do // the checks at the RDBSS level before going to the local filesystem. // Also, since some of our creates open the file with FILE_WRITE_DATA, // if someone creates a read_only file and we stamp the read_only // attribute on the local file then all subsequent creates will fail // since we always ask for Write access to the underlying file as // explained above. // // // We add to the DesiredAccess FILE_READ_ATTRIBUTES since we read the // attributes of this file since the file size value we got from the server // could be different from the GET Content-Length. // // // We need to remove the ACCESS_SYSTEM_SECURITY which will prevent us from // opening the file in the LocalService context even if the file is created // in LocalService context. // DesiredAccess = (CreateRequest->DesiredAccess & ~(FILE_APPEND_DATA | ACCESS_SYSTEM_SECURITY)); DesiredAccess |= (FILE_READ_ATTRIBUTES); if ( DesiredAccess & (FILE_READ_DATA | FILE_EXECUTE) ) { DesiredAccess |= (FILE_WRITE_DATA); } NtStatus = NtCreateFile(&(FileHandle), DesiredAccess, &ObjectAttributes, &IoStatusBlock, &CreateRequest->AllocationSize, (CreateRequest->FileAttributes & ~FILE_ATTRIBUTE_READONLY), FILE_SHARE_VALID_FLAGS, CreateRequest->CreateDisposition, (CreateRequest->CreateOptions | FILE_NO_INTERMEDIATE_BUFFERING), CreateRequest->EaBuffer, CreateRequest->EaLength); if (NtStatus != STATUS_SUCCESS) { // // We convert the NtStatus to DOS error here. The Win32 // error code is finally set to an NTSTATUS value in // the DavFsCreate function just before returning. // WStatus = RtlNtStatusToDosError(NtStatus); DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/NtCreateFile(2). Error Val = " "%x %x %x %x %x %x %x %x %ws\n", NtStatus, CreateRequest->ImpersonationLevel, CreateRequest->SecurityFlags, CreateRequest->SecurityDescriptor, DesiredAccess, CreateRequest->FileAttributes, CreateRequest->CreateDisposition, CreateRequest->CreateOptions, DavWorkItem->AsyncCreate.FileName)); CreateResponse->Handle = NULL; CreateResponse->UserModeKey = NULL; FileHandle = INVALID_HANDLE_VALUE; goto EXIT_THE_FUNCTION; } // // We don't impersonate back the client as yet since we might need // to call NtQueryInformationFile next (if the file is encrypted) // which requires us to be in the context of the Web Client Service. // DavPrint((DEBUG_MISC, "DavAsyncCreateGet(2): FileHandle = %08lx\n", FileHandle)); // // We query the StandardInformation of the file to find out if the FileSize // returned by PROPFIND is different from the content-length returned by GET. // NtStatus = NtQueryInformationFile(FileHandle, &(IoStatusBlock), &(FileStdInfo), sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation); if (NtStatus != STATUS_SUCCESS) { // // We convert the NtStatus to DOS error here. The Win32 // error code is finally set to an NTSTATUS value in // the DavFsCreate function just before returning. // WStatus = RtlNtStatusToDosError(NtStatus); NtClose(FileHandle); DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/NtQueryInformationFile. Error Val " "= %08lx\n", NtStatus)); CreateResponse->Handle = NULL; CreateResponse->UserModeKey = NULL; FileHandle = INVALID_HANDLE_VALUE; goto EXIT_THE_FUNCTION; } // // The FileSize returned by PROPFIND is different from the the amount of // data that GET returned. Server screwup. We reset the filesize and the // allocationsize to match the underlying file. For encrypted files these // values will be different because PROPFIND returns the size of the backup // blob which is different from the size of the restored file. // if (!EncryptedFile && FileStdInfo.EndOfFile.QuadPart != CreateResponse->StandardInformation.EndOfFile.QuadPart) { // // Reset the FileSize and AllocationSize info in the response to the // FileSize and AllocationSize of the underlying file. // DavPrint((DEBUG_DEBUG, "DavAsyncCreate: FileSizes Different!! CPN = %ws, " "FileStdInfo.EndOfFile.HighPart = %x, " "FileStdInfo.EndOfFile.LowPart = %x, " "CreateResponse.EndOfFile.HighPart = %x, " "CreateResponse.EndOfFile.LowPart = %x\n", CreateRequest->CompletePathName, FileStdInfo.EndOfFile.HighPart, FileStdInfo.EndOfFile.LowPart, CreateResponse->StandardInformation.EndOfFile.HighPart, CreateResponse->StandardInformation.EndOfFile.LowPart)); CreateResponse->StandardInformation.EndOfFile.QuadPart = FileStdInfo.EndOfFile.QuadPart; CreateResponse->StandardInformation.AllocationSize.QuadPart = FileStdInfo.AllocationSize.QuadPart; } // // If the File was encrypted, we need to reset the file size in the response // buffer. // if (EncryptedFile) { // // Set the new AllocationSize and EndOfFile values. // CreateResponse->StandardInformation.AllocationSize.QuadPart = FileStdInfo.AllocationSize.QuadPart; CreateResponse->StandardInformation.EndOfFile.QuadPart = FileStdInfo.EndOfFile.QuadPart; } CreateResponse->Handle = FileHandle; CreateResponse->UserModeKey = (PVOID)FileHandle; EXIT_THE_FUNCTION: // // The function RtlDosPathNameToNtPathName_U allocates memory from the // processes heap. If we did, we need to free it now. // if (UnicodeFileName.Buffer != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, UnicodeFileName.Buffer); } // // Impersonate back again, so that we are in the context of // the user who issued this request. // if (!didImpersonate) { ULONG LocalStatus; LocalStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (LocalStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavAsyncCreateGet/UMReflectorImpersonate. " "Error Val = %d\n", LocalStatus)); if (WStatus == ERROR_SUCCESS) { WStatus = LocalStatus; } } didImpersonate = TRUE; } return WStatus; } VOID DavAsyncCreateCompletion( PDAV_USERMODE_WORKITEM DavWorkItem ) /*++ Routine Description: This routine handles the Create completion. It basically frees up the resources allocated during the Create operation. Arguments: DavWorkItem - The DAV_USERMODE_WORKITEM value. Return Value: none. --*/ { PDAV_USERMODE_CREATE_RESPONSE CreateResponse; CreateResponse = &(DavWorkItem->CreateResponse); if (DavWorkItem->AsyncCreate.RemPathName != NULL) { HLOCAL FreeHandle; ULONG FreeStatus; FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncCreate.RemPathName); if (FreeHandle != NULL) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/LocalFree(1). Error Val = %d\n", FreeStatus)); } } if (DavWorkItem->AsyncCreate.UrlBuffer != NULL) { HLOCAL FreeHandle; ULONG FreeStatus; FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncCreate.UrlBuffer); if (FreeHandle != NULL) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/LocalFree(2). Error Val = %d\n", FreeStatus)); } } if (DavWorkItem->AsyncCreate.DataBuff != NULL) { HLOCAL FreeHandle; ULONG FreeStatus; FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncCreate.DataBuff); if (FreeHandle != NULL) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/LocalFree(3). Error Val = %d\n", FreeStatus)); } } if (DavWorkItem->AsyncCreate.didRead != NULL) { HLOCAL FreeHandle; ULONG FreeStatus; FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncCreate.didRead); if (FreeHandle != NULL) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/LocalFree(4). Error Val = %d\n", FreeStatus)); } } if (DavWorkItem->AsyncCreate.FileName != NULL) { HLOCAL FreeHandle; ULONG FreeStatus; FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncCreate.FileName); if (FreeHandle != NULL) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/LocalFree(5). Error Val = %d\n", FreeStatus)); } } if (DavWorkItem->AsyncCreate.FileHandle != NULL) { BOOL ReturnVal; ULONG CloseStatus; ReturnVal = CloseHandle(DavWorkItem->AsyncCreate.FileHandle); if (!ReturnVal) { CloseStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/CloseHandle. " "Error Val = %d\n", CloseStatus)); } } if (DavWorkItem->AsyncCreate.DavOpenHandle != NULL) { BOOL ReturnVal; ULONG FreeStatus; ReturnVal = InternetCloseHandle( DavWorkItem->AsyncCreate.DavOpenHandle ); if (!ReturnVal) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/InternetCloseHandle. " "Error Val = %d\n", FreeStatus)); } } if (DavWorkItem->AsyncCreate.lpCEI != NULL) { HLOCAL FreeHandle; ULONG FreeStatus; FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncCreate.lpCEI); if (FreeHandle != NULL) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/LocalFree(6). Error Val = %d\n", FreeStatus)); } DavWorkItem->AsyncCreate.lpCEI = NULL; } if (DavWorkItem->AsyncResult != NULL) { HLOCAL FreeHandle; ULONG FreeStatus; FreeHandle = LocalFree((HLOCAL)DavWorkItem->AsyncResult); if (FreeHandle != NULL) { FreeStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavAsyncCreateCompletion/LocalFree(7). Error Val = %d\n", FreeStatus)); } } // // If we did not succeed, but landed up creating a handle to the local file, // we need to close that now. // if (DavWorkItem->Status != ERROR_SUCCESS) { if (CreateResponse->Handle != NULL) { NtClose(CreateResponse->Handle); CreateResponse->Handle = NULL; CreateResponse->UserModeKey = NULL; } } // // The callback context should not be finalized if we are returning // ERROR_IO_PENDING. // DavFsFinalizeTheDavCallBackContext(DavWorkItem); // // We are done with the per user entry, so finalize it. // if (DavWorkItem->AsyncCreate.PerUserEntry) { DavFinalizePerUserEntry( &(DavWorkItem->AsyncCreate.PerUserEntry) ); } return; } BOOL DavIsThisFileEncrypted( PVOID DataBuff ) /*++ Routine Description: This routine checks the buffer supplied to see if it matches the first few bytes of a BackedUp encrypted file. Arguments: DataBuff - The buffer to be checked. Return Value: TRUE - DataBuff matches the first few bytes of a BackedUp encrypted file. FALSE - It does not. --*/ { if ( SIG_EFS_FILE != DavCheckSignature((char *)DataBuff + sizeof(ULONG)) || SIG_EFS_STREAM != DavCheckSignature((char *)DataBuff + sizeof(EFSEXP_FILE_HEADER) + sizeof(ULONG)) || SIG_EFS_DATA != DavCheckSignature((char *)DataBuff + sizeof(EFSEXP_FILE_HEADER) + sizeof(EFSEXP_STREAM_HEADER) + sizeof(USHORT) + sizeof(ULONG)) || EFS_STREAM_ID != *((USHORT *)((char *)DataBuff + sizeof(EFSEXP_FILE_HEADER) + sizeof(EFSEXP_STREAM_HEADER))) || EFS_EXP_FORMAT_CURRENT_VERSION != ((PEFSEXP_FILE_HEADER)DataBuff)->VersionID ) { // // Signature does not match. // return FALSE; } else { return TRUE; } } ULONG DavCheckSignature( PVOID Signature ) /*++ Routine Description: This routine returns the signature type. Arguments: Signature - Signature string. Return Value: The type of signature. SIG_NO_MATCH for bogus signature. --*/ { if ( !memcmp( Signature, FILE_SIGNATURE, SIG_LENGTH ) ) { return SIG_EFS_FILE; } if ( !memcmp( Signature, STREAM_SIGNATURE, SIG_LENGTH ) ) { return SIG_EFS_STREAM; } if ( !memcmp( Signature, DATA_SIGNATURE, SIG_LENGTH ) ) { return SIG_EFS_DATA; } return SIG_NO_MATCH; } DWORD DavRestoreEncryptedFile( PWCHAR ExportFile, PWCHAR ImportFile ) /*++ Routine Description: This function performs the restoration of encrypted files. In other words the import operation of the exported file by calling the appropriate EFS APIs. Arguments: ExportFile - The File containing the backup. ImportFile - The File with the restored data. Return Value: Returned value of the EFS APIs. --*/ { DWORD WStatus = ERROR_SUCCESS; HANDLE RawImport = INVALID_HANDLE_VALUE; PVOID RawContext = NULL; DavPrint((DEBUG_MISC, "DavRestoreEncryptedFile: ExportFile = %ws, ImportFile = %ws\n", ExportFile, ImportFile)); RawImport = CreateFileW(ExportFile, (GENERIC_WRITE | GENERIC_READ), 0, // Exclusive access. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL); if (RawImport == INVALID_HANDLE_VALUE) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavRestoreEncryptedFile/CreateFileW. Error Val = %d %ws\n", WStatus,ExportFile)); goto EXIT_THE_FUNCTION; } // // Open a raw context to the file. // WStatus = OpenEncryptedFileRawW(ImportFile, CREATE_FOR_IMPORT, &(RawContext)); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavRestoreEncryptedFile/OpenEncryptedFileRaw. Error Val = %d %ws\n", WStatus,ImportFile)); goto EXIT_THE_FUNCTION; } WStatus = WriteEncryptedFileRaw((PFE_IMPORT_FUNC)DavWriteRawCallback, (PVOID)RawImport, RawContext); if (WStatus == RPC_X_PIPE_DISCIPLINE_ERROR) { WStatus = ERROR_ACCESS_DENIED; } if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavRestoreEncryptedFile/WriteEncryptedFileRaw. Error Val = %d %ws\n", WStatus,ImportFile)); } EXIT_THE_FUNCTION: if (RawImport != INVALID_HANDLE_VALUE) { CloseHandle(RawImport); } if (RawContext) { CloseEncryptedFileRaw(RawContext); } return WStatus; } DWORD DavWriteRawCallback( PBYTE DataBuff, PVOID CallbackContext, PULONG DataLength ) /*++ Routine Description: Call-back function for WriteEncryptedFileRaw() called in Restore(). This function reads the backed up data from the backup file, and provides it to WriteEncryptedFileRaw() through this callback function which in turn transforms the raw data back to its original form. This call-back function is called until all the data content has been read. Arguments: DataBuffer - Data to be read. CallbackContext - Handle to the Backup file. DataLength - Size of the DataBuffer. Return Value: Returned value of the operation. --*/ { DWORD WStatus = ERROR_SUCCESS; BOOL ReturnVal; DWORD BytesRead = 0; DavPrint((DEBUG_MISC, "DavWriteRawCallback: DataLength = %d\n", *DataLength)); // // Restore the file's content with the information stored in the temporary // location. // ReturnVal = ReadFile((HANDLE)CallbackContext, DataBuff, *DataLength, &(BytesRead), NULL); if ( !ReturnVal ) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavWriteRawCallback/ReadFile. Error Val = %d\n", WStatus)); } DavPrint((DEBUG_MISC, "DavWriteRawCallback: BytesRead = %d\n", BytesRead)); *DataLength = BytesRead; return WStatus; } DWORD DavReuseCacheFileIfNotModified( IN PDAV_USERMODE_WORKITEM pDavWorkItem ) /*++ Routine Description: If we get an NOT-MODIFIED response, then we just get the filename from wininet and use it Arguments: pDavWorkItem - The buffer that contains the request parameters and options. Return Value: ERROR_SUCCESS or the appropriate error code. --*/ { DWORD dwError = ERROR_SUCCESS; PWCHAR pFileNameBuff = NULL; DWORD dwBufferSize = 0, dwStatus = 0; LPINTERNET_CACHE_ENTRY_INFOW lpCEI = NULL; lpCEI = (LPINTERNET_CACHE_ENTRY_INFOW)pDavWorkItem->AsyncCreate.lpCEI; if (!pDavWorkItem->AsyncCreate.lpCEI) { return ERROR_FILE_NOT_FOUND; } dwBufferSize = sizeof(dwStatus); if (!HttpQueryInfoW(pDavWorkItem->AsyncCreate.DavOpenHandle, (HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER), (LPVOID)&dwStatus, &dwBufferSize, NULL)) { return GetLastError(); } if (dwStatus == HTTP_STATUS_NOT_MODIFIED) { pFileNameBuff = LocalAlloc(LPTR, (lstrlen(lpCEI->lpszLocalFileName) + 1) * sizeof(WCHAR)); if (pFileNameBuff) { dwError = ERROR_SUCCESS; pDavWorkItem->AsyncCreate.FileName = pFileNameBuff; if (!InternetCloseHandle(pDavWorkItem->AsyncCreate.DavOpenHandle)) { dwError = GetLastError(); LocalFree(pFileNameBuff); pFileNameBuff = NULL; pDavWorkItem->AsyncCreate.FileName = NULL; } else { pDavWorkItem->AsyncCreate.DavOpenHandle = NULL; wcscpy(pDavWorkItem->CreateResponse.FileName, lpCEI->lpszLocalFileName); wcscpy(pDavWorkItem->AsyncCreate.FileName, lpCEI->lpszLocalFileName); } goto EXIT_THE_FUNCTION; } } else { dwError = ERROR_FILE_NOT_FOUND; } EXIT_THE_FUNCTION: return dwError; } DWORD DavCreateUrlCacheEntry( IN PDAV_USERMODE_WORKITEM pDavWorkItem ) /*++ Routine Description: This routine creates an entry for a file in the WinInet's cache. Arguments: pDavWorkItem - The buffer that contains the request parameters and options. Return Value: ERROR_SUCCESS or the appropriate error code. --*/ { DWORD dwError = ERROR_SUCCESS; PWCHAR pFileExt = NULL; PWCHAR pFileNameBuff = NULL; BOOL ReturnVal = FALSE; // // Get the file extension. For now we assume that the extension follows the // last '.' char. We do a ++ after the call to wcsrchr to go past the '.'. // If '.' itself is the last char, the extension is NULL. // if ( *(pDavWorkItem->AsyncCreate.RemPathName) ) { pFileExt = ( pDavWorkItem->AsyncCreate.RemPathName + (wcslen(pDavWorkItem->AsyncCreate.RemPathName) - 1) ); while (pFileExt != pDavWorkItem->AsyncCreate.RemPathName) { if ( *pFileExt == L'.' || *pFileExt == L'/' || *pFileExt == L'\\' ) { break; } pFileExt--; } if ( pFileExt != pDavWorkItem->AsyncCreate.RemPathName && *pFileExt == L'.' && *(pFileExt + 1) != '\0' ) { pFileExt++; DavPrint((DEBUG_MISC, "DavCreateUrlCacheEntry. FileExt: %ws\n", pFileExt)); } else { pFileExt = NULL; DavPrint((DEBUG_MISC, "DavCreateUrlCacheEntry. No FileExt.\n")); } } else { pFileExt = NULL; DavPrint((DEBUG_MISC, "DavCreateUrlCacheEntry. No FileExt.\n")); } DavPrint((DEBUG_MISC, "DavCreateUrlCacheEntry. pFileExt: %ws\n", pFileExt)); // // Allocate memory for pFileNameBuff. // pFileNameBuff = LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, MAX_PATH * sizeof(WCHAR)); if (pFileNameBuff == NULL) { dwError = GetLastError(); DavPrint((DEBUG_ERRORS, "DavCreateUrlCacheEntry/LocalAlloc. Error Val = %d\n", dwError)); goto EXIT_THE_FUNCTION; } // // Create a file name for the URL in the cache. // ReturnVal = CreateUrlCacheEntryW(pDavWorkItem->AsyncCreate.UrlBuffer, 0, pFileExt, pFileNameBuff, 0); // // The CreateUrlCacheEntry API call may fail with GetLastError() = // ERROR_FILENAME_EXCED_RANGE for long extension names. In such a scenario // we make the call again with the file extension set to NULL. // if (!ReturnVal && pFileExt != NULL) { DavPrint((DEBUG_ERRORS, "DavCreateUrlCacheEntry/CreateUrlCacheEntry(1). Error Val = %d\n", GetLastError())); // // Another attempt to create a file name for the URL in the cache with // no extension name. // pFileExt = NULL; ReturnVal = CreateUrlCacheEntryW(pDavWorkItem->AsyncCreate.UrlBuffer, 0, NULL, pFileNameBuff, 0); } // // If we've failed the both the calls, then we return the failure. // if (!ReturnVal) { dwError = GetLastError(); DavPrint((DEBUG_ERRORS, "DavCreateUrlCacheEntry/CreateUrlCacheEntry(2). Error Val = %d %ws\n", dwError, pDavWorkItem->AsyncCreate.FileName)); goto EXIT_THE_FUNCTION; } pDavWorkItem->AsyncCreate.FileName = pFileNameBuff; DavPrint((DEBUG_MISC, "DavCreateUrlCacheEntry: FileName = %ws\n", pDavWorkItem->AsyncCreate.FileName)); // // Copy the file name in the response buffer. // wcscpy(pDavWorkItem->CreateResponse.FileName, pDavWorkItem->AsyncCreate.FileName); EXIT_THE_FUNCTION: // // If we did not succeed then we need to free up the memory allocated for // pFileNameBuff (if we did allocate at all). // if (dwError != ERROR_SUCCESS) { if (pFileNameBuff != NULL) { LocalFree(pFileNameBuff); pDavWorkItem->AsyncCreate.FileName = NULL; } } return dwError; } WCHAR wszEtagHeader[] = L"ETag: "; #define CONST_TEN_MINUTES ((LONGLONG)10 * 60 * 10000000) DWORD DavCommitUrlCacheEntry( IN PDAV_USERMODE_WORKITEM pDavWorkItem ) /*++ Routine Description: This routine commits (pins) the entry for a file in the WinInet's cache. This entry would have been created using DavCreateUrlCacheEntry. Arguments: pDavWorkItem - The buffer that contains the request parameters and options. Return Value: ERROR_SUCCESS or the appropriate error code. --*/ { DWORD dwTemp, dwIndex; SYSTEMTIME sSystemTime; BOOL fRet= FALSE, fHasEtag = FALSE; FILETIME ExTime, LmTime; char chEtagBuff[1024]; dwTemp = sizeof(SYSTEMTIME); dwIndex = 0; // // If the expiry time is available in the OpenHandle, get it. // if( !HttpQueryInfo(pDavWorkItem->AsyncCreate.DavOpenHandle, (HTTP_QUERY_EXPIRES | HTTP_QUERY_FLAG_SYSTEMTIME), &sSystemTime, &dwTemp, &dwIndex) || !SystemTimeToFileTime(&sSystemTime, &ExTime) ) { SYSTEMTIME sSysT; GetSystemTime(&sSysT); SystemTimeToFileTime(&sSysT, &ExTime); *(LONGLONG *)&ExTime += CONST_TEN_MINUTES; } dwTemp = sizeof(SYSTEMTIME); dwIndex = 0; // // If the last modified time is available in the OpenHandle, get it. // if( !HttpQueryInfo(pDavWorkItem->AsyncCreate.DavOpenHandle, (HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME), &sSystemTime, &dwTemp, &dwIndex) || !SystemTimeToFileTime(&sSystemTime, &LmTime) ) { LmTime.dwLowDateTime = 0; LmTime.dwHighDateTime = 0; } #if 0 dwIndex = 0; memcpy(chEtagBuff, wszEtagHeader, sizeof(wszEtagHeader)-2); dwTemp = sizeof(chEtagBuff)-(sizeof(wszEtagHeader)-2); if( HttpQueryInfo(pDavWorkItem->AsyncCreate.DavOpenHandle, HTTP_QUERY_ETAG, ( chEtagBuff + sizeof(wszEtagHeader) - 2 ), &dwTemp, &dwIndex) ) { fHasEtag = TRUE; dwTemp += sizeof(wszEtagHeader)-2; DavPrint((DEBUG_ERRORS, "DavCreateUrlCacheEntry/CreateUrlCacheEntry. Etag %s\n", chEtagBuff)); } #endif // // Close the DavOpenHandle. This needs to be done, otherwise the commit // below will fail with SHARING_VIOLATON as WinInet has a cached file open. // fRet = InternetCloseHandle(pDavWorkItem->AsyncCreate.DavOpenHandle); if (!fRet) { DavPrint((DEBUG_ERRORS, "DavCommitUrlCacheEntry/InternetCloseHandle = %d\n", GetLastError())); goto bailout; } pDavWorkItem->AsyncCreate.DavOpenHandle = NULL; fRet = CommitUrlCacheEntryW(pDavWorkItem->AsyncCreate.UrlBuffer, pDavWorkItem->AsyncCreate.FileName, ExTime, LmTime, STICKY_CACHE_ENTRY, (fHasEtag ? ((LPWSTR)chEtagBuff) : NULL), (fHasEtag ? dwTemp : 0), NULL, NULL); if (!fRet) { DavPrint((DEBUG_ERRORS, "DavCommitUrlCacheEntry/CommitUrlCacheEntryW = %d\n", GetLastError())); } bailout: if (!fRet) { return GetLastError(); } else { return ERROR_SUCCESS; } } DWORD DavSetAclForEncryptedFile( PWCHAR FilePath ) /*++ Routine Description: This routine set the ACLs on the file that allows everybody to access it. Arguments: FilePath - The path of the file. Return Value: ERROR_SUCCESS or the appropriate error code. --*/ { DWORD status = NO_ERROR; DWORD cb = 0; PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; // // Initialize the Security Descriptor with the ACL allowing everybody to // access the file. // if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:(A;;GAGRGWGX;;;WD)", SDDL_REVISION_1, &SecurityDescriptor, &cb)) { status = GetLastError(); DavPrint((DEBUG_ERRORS, "DavSetAclForEncryptedFile/ConvertStringSecurityDescriptorToSecurityDescriptorW = %d\n", status)); goto EXIT_THE_FUNCTION; } // // Put the DACL onto the file. // if (!SetFileSecurity(FilePath, DACL_SECURITY_INFORMATION, SecurityDescriptor)) { status = GetLastError(); DavPrint((DEBUG_ERRORS, "DavSetAclForEncryptedFile/SetFileSecurity = %d\n", status)); } EXIT_THE_FUNCTION: if (SecurityDescriptor) { LocalFree(SecurityDescriptor); } return status; } DWORD DavQueryUrlCacheEntry( IN PDAV_USERMODE_WORKITEM pDavWorkItem ) /*++ Routine Description: This routine is called to Create a entry in the WinInet cache for the given URL. Arguments: pDavWorkItem - The buffer that contains the request parameters and options. Return Value: ERROR_SUCCESS or the appropriate error code. --*/ { DWORD dwError = ERROR_SUCCESS; DWORD cbCEI = 0, count = 0; LPINTERNET_CACHE_ENTRY_INFOW lpCEI = NULL; if (pDavWorkItem->AsyncCreate.lpCEI != NULL) { return ERROR_SUCCESS; } cbCEI = ( sizeof(INTERNET_CACHE_ENTRY_INFOW) + (MAX_PATH * 2) ); do { lpCEI = LocalAlloc(LPTR, cbCEI); if (!lpCEI) { dwError = GetLastError(); break; } ++count; if ( !GetUrlCacheEntryInfo(pDavWorkItem->AsyncCreate.UrlBuffer, lpCEI, &cbCEI) ) { if ((dwError = GetLastError()) == ERROR_INSUFFICIENT_BUFFER) { LocalFree(lpCEI); lpCEI = NULL; } else { DavPrint((DEBUG_MISC, "DavQueryUrlCacheEntry/GetUrlCacheEntryInfo: dwError = %d, UrlBuffer = %ws\n", dwError, pDavWorkItem->AsyncCreate.UrlBuffer)); break; } } else { dwError = ERROR_SUCCESS; break; } } while (count < 2); if (dwError == ERROR_SUCCESS) { pDavWorkItem->AsyncCreate.lpCEI = lpCEI; } else { // // If some error occurred in adding the header, set the correct error // code. // dwError = GetLastError(); if (lpCEI) { LocalFree(lpCEI); lpCEI = NULL; } } return dwError; } DWORD DavAddIfModifiedSinceHeader( IN PDAV_USERMODE_WORKITEM pDavWorkItem ) /*++ Routine Description: This routine is called to add the If-Modified-Since header to the request being sent to the server. Arguments: pDavWorkItem - The buffer that contains the request parameters and options. Return Value: ERROR_SUCCESS or the appropriate error code. --*/ { DWORD dwError = ERROR_SUCCESS; DWORD cbCEI = 0, count = 0; LPINTERNET_CACHE_ENTRY_INFOW lpCEI = NULL; CHAR chBuff[(sizeof(rgchIMS) + INTERNET_RFC1123_BUFSIZE + 5)]; SYSTEMTIME systemtime; if (pDavWorkItem->AsyncCreate.lpCEI == NULL) { DavQueryUrlCacheEntry(pDavWorkItem); } lpCEI = pDavWorkItem->AsyncCreate.lpCEI; if ((lpCEI != NULL) && ((lpCEI->LastModifiedTime.dwLowDateTime != 0) || (lpCEI->LastModifiedTime.dwHighDateTime != 0)) && FileTimeToSystemTime((CONST FILETIME *)&(lpCEI->LastModifiedTime), &systemtime)) { memcpy(chBuff, rgchIMS, (sizeof(rgchIMS) - 1)); chBuff[((sizeof(rgchIMS)) - 1)] = ':'; chBuff[sizeof(rgchIMS)] = ' '; if (InternetTimeFromSystemTimeA((CONST SYSTEMTIME *)&systemtime, INTERNET_RFC1123_FORMAT, &chBuff[(sizeof(rgchIMS) + 1)], (sizeof(chBuff) - sizeof(rgchIMS) - 2))) { HttpAddRequestHeadersA(pDavWorkItem->AsyncCreate.DavOpenHandle, chBuff, lstrlenA(chBuff), (HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE)); } } return dwError; } DWORD DavLockTheFileOnTheServer( IN PDAV_USERMODE_WORKITEM DavWorkItem ) /*++ Routine Description: This routine is called during the create when we need to LOCK the file on the server. This is done to provide consistency so that users do not overwrite each others data. IMPORTANT!! Its important to note that if we fail to LOCK the file on the server because the DAV server doesn't support LOCKs, we do not fail the create call. This is because according to the DAV RFC, a server is not required to support LOCKs. So, if the server returns 405 (Method not allowed), this function will return ERROR_SUCCESS back to the caller. Arguments: DavWorkItem - The buffer that contains the request parameters and options. Return Value: ERROR_SUCCESS or the appropriate error code. --*/ { DWORD WStatus = ERROR_SUCCESS; HINTERNET DavOpenHandle = NULL; BOOL BStatus = FALSE, ReturnVal = FALSE, readDone = FALSE, fileIsLocked = FALSE; PWCHAR PassportCookie = NULL; PCHAR LockRequestBuffer = NULL, lpTemp = NULL, DataBuff = NULL; PCHAR UserNameBuffer = NULL; ULONG LockRequestBufferLength = 0, ResponseStatus = 0, UserNameSize = 0; DAV_FILE_ATTRIBUTES DavFileAttributes; PVOID Ctx1 = NULL, Ctx2 = NULL; DWORD NumRead = 0, NumOfFileEntries = 0, TotalDataBytesRead = 0; PDAV_USERMODE_CREATE_RESPONSE CreateResponse; DavPrint((DEBUG_MISC, "DavLockTheFileOnTheServer. Locking File: %ws\n", DavWorkItem->AsyncCreate.RemPathName)); // // Get the UserName of the user in whose context this LOCK is being // issued. The first GetUserNameExA call is to get the number of chars // necessary to hold the UserName. The return value is ERROR_MORE_DATA // and not ERROR_INSUFFICIENT_BUFFER if the buffer passed in is not of // sufficient length. // ReturnVal = GetUserNameExA(NameSamCompatible, UserNameBuffer, &UserNameSize); if (!ReturnVal) { WStatus = GetLastError(); if (WStatus != ERROR_MORE_DATA) { DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/GetUserNameExA(1). Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } } UserNameBuffer = LocalAlloc(LPTR, UserNameSize * sizeof(CHAR)); if (UserNameBuffer == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/LocalAlloc. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } ReturnVal = GetUserNameExA(NameSamCompatible, UserNameBuffer, &UserNameSize); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/GetUserNameExA(2). Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } DavPrint((DEBUG_MISC, "DavLockTheFileOnTheServer. UserNameBuffer: %s\n", UserNameBuffer)); // // Allocate the buffer big enough to hold the entire XML LOCK request // that has to be sent to the server. // LockRequestBufferLength = strlen(rgLockInfoHeader); LockRequestBufferLength += strlen(rgLockInfoTrailer); LockRequestBufferLength += strlen(rgLockScopeHeader); LockRequestBufferLength += strlen(rgLockTypeHeader); LockRequestBufferLength += strlen(rgOwnerHeader); LockRequestBufferLength += strlen(rgOwnerTrailer); LockRequestBufferLength += strlen(UserNameBuffer); LockRequestBufferLength += 1; // for the final \0 char. LockRequestBufferLength = LockRequestBufferLength * sizeof(CHAR); LockRequestBuffer = LocalAlloc(LPTR, LockRequestBufferLength); if (LockRequestBuffer == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/DavHttpOpenRequestW. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // Format the XML LOCK request that needs to be sent to the server. // memset(LockRequestBuffer, 0, sizeof(LockRequestBuffer)); lpTemp = LockRequestBuffer; memcpy(lpTemp, rgLockInfoHeader, (sizeof(rgLockInfoHeader) - 1)); lpTemp += (sizeof(rgLockInfoHeader) - 1); memcpy(lpTemp, rgLockScopeHeader, (sizeof(rgLockScopeHeader) - 1)); lpTemp += (sizeof(rgLockScopeHeader) - 1); memcpy(lpTemp, rgLockTypeHeader, (sizeof(rgLockTypeHeader) - 1)); lpTemp += (sizeof(rgLockTypeHeader) - 1); memcpy(lpTemp, rgOwnerHeader, (sizeof(rgOwnerHeader) - 1)); lpTemp += (sizeof(rgOwnerHeader) - 1); memcpy(lpTemp, UserNameBuffer, (strlen(UserNameBuffer) * sizeof(CHAR))); lpTemp += (strlen(UserNameBuffer) * sizeof(CHAR)); memcpy(lpTemp, rgOwnerTrailer, (sizeof(rgOwnerTrailer) - 1)); lpTemp += (sizeof(rgOwnerTrailer) - 1); memcpy(lpTemp, rgLockInfoTrailer, (sizeof(rgLockInfoTrailer) - 1)); // // Convert the unicode object name to UTF-8 URL format. // Space and other white characters will remain untouched - these should // be taken care of by wininet calls. // This has to be a W API as the name in CloseRequest is unicode. // BStatus = DavHttpOpenRequestW(DavWorkItem->AsyncCreate.PerUserEntry->DavConnHandle, L"LOCK", DavWorkItem->AsyncCreate.RemPathName, L"HTTP/1.1", NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD, 0, L"DavLockTheFileOnTheServer", &DavOpenHandle); if(BStatus == FALSE) { WStatus = GetLastError(); goto EXIT_THE_FUNCTION; } if (DavOpenHandle == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/DavHttpOpenRequestW. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // We need to add the header "translate:f" to tell IIS that it should // allow the user to excecute this VERB on the specified path which it // would not allow (in some cases) otherwise. Finally, there is a special // flag in the metabase to allow for uploading of "dangerous" content // (anything that can be run on the server). This is the ScriptSourceAccess // flag in the UI or the AccessSource flag in the metabase. You will need // to set this bit to true as well as correct NT ACLs in order to be able // to upload .exes or anything executable. We set this header on all requests // that are sent to the server including the LOCK request. // ReturnVal = HttpAddRequestHeadersA(DavOpenHandle, "translate: f\n", -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/HttpAddRequestHeadersA. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } WStatus = DavAttachPassportCookie(DavWorkItem, DavOpenHandle, &PassportCookie); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/DavAttachPassportCookie. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } WStatus = DavInternetSetOption(DavWorkItem, DavOpenHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/DavInternetSetOption. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // Request the LOCK on this resource for an hour. We will have to refresh // the LOCK request if we need to keep the LOCK request for more than an // hour. // ReturnVal = HttpAddRequestHeadersA(DavOpenHandle, "Timeout: Second-3600\n", -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/HttpAddRequestHeadersA. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } ReturnVal = HttpSendRequestA(DavOpenHandle, rgHttpHeader, strlen(rgHttpHeader), (LPVOID)LockRequestBuffer, strlen(LockRequestBuffer)); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/HttpSendRequestA: Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } WStatus = DavQueryAndParseResponseEx(DavOpenHandle, &ResponseStatus); if (WStatus != ERROR_SUCCESS) { // // If the server doesn't support the LOCK operation, we do not fail the // create call. // if (ResponseStatus == HTTP_STATUS_NOT_SUPPORTED) { WStatus = ERROR_SUCCESS; goto EXIT_THE_FUNCTION; } else if (ResponseStatus == DAV_STATUS_LOCKED) { // // If the return status from the server was 423 (the file is locked // by someone else) then we still need to parse the XML response // to find out who the owner of the LOCK is. This information is // displayed to the user. // DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/DavQueryAndParseResponseEx: Error Val = %d\n", WStatus)); fileIsLocked = TRUE; } else { SetLastError(WStatus); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/DavQueryAndParseResponseEx: Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } } DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, NUM_OF_BYTES_TO_READ); if (DataBuff == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/LocalAlloc(2): WStatus = %08lx\n", WStatus)); goto EXIT_THE_FUNCTION; } // // Read the response and parse it. // do { ReturnVal = InternetReadFile(DavOpenHandle, (LPVOID)DataBuff, NUM_OF_BYTES_TO_READ, &(NumRead)); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/InternetReadFile: WStatus = " "%08lx\n", WStatus)); goto EXIT_THE_FUNCTION; } DavPrint((DEBUG_MISC, "DavLockTheFileOnTheServer: NumRead = %d\n", NumRead)); // // We reject files whose attributes are greater than a // certain size (DavFileAttributesLimitInBytes). This // is a parameter that can be set in the registry. This // is done to avoid attacks by rogue servers. // TotalDataBytesRead += NumRead; if (TotalDataBytesRead > DavFileAttributesLimitInBytes) { WStatus = ERROR_BAD_NET_RESP; DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer. LockResponseSize > %d\n", DavFileAttributesLimitInBytes)); goto EXIT_THE_FUNCTION; } readDone = (NumRead == 0) ? TRUE : FALSE; WStatus = DavPushData(DataBuff, &Ctx1, &Ctx2, NumRead, readDone); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/DavPushData. WStatus = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } if (readDone) { break; } } while ( TRUE ); memset(&DavFileAttributes, 0, sizeof(DavFileAttributes)); InitializeListHead( &(DavFileAttributes.NextEntry) ); WStatus = DavParseData(&DavFileAttributes, Ctx1, Ctx2, &NumOfFileEntries); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer/DavParseData. WStatus = %d\n", WStatus)); DavFinalizeFileAttributesList(&DavFileAttributes, FALSE); goto EXIT_THE_FUNCTION; } CreateResponse = &(DavWorkItem->CreateResponse); // // Copy the LockOwner value if the file is already LOCKed on the server and // this LOCK request failed with 423 OR the OpaqueLockToken and LockTimeout // values if the LOCK was successfully taken on this file. // CreateResponse->FileWasAlreadyLocked = FALSE; CreateResponse->LockWasTakenOnThisCreate = FALSE; if (fileIsLocked) { WStatus = ERROR_LOCK_VIOLATION; // // If the DavFileAttributes.LockOwner is NULL, it means that the // XML response from the server was bogus. In such a case, we just // fail the request with ERROR_LOCK_VIOLATION. // if (DavFileAttributes.LockOwner) { CreateResponse->FileWasAlreadyLocked = TRUE; if ( wcslen(DavFileAttributes.LockOwner) <= (256 + 256) ) { wcscpy(CreateResponse->LockOwner, DavFileAttributes.LockOwner); DavPrint((DEBUG_MISC, "DavLockTheFileOnTheServer: fileIsLocked!! LockOwner = %ws\n", CreateResponse->LockOwner)); } } } else { CreateResponse->LockWasTakenOnThisCreate = TRUE; if ( wcslen(DavFileAttributes.OpaqueLockToken) < MAX_PATH ) { wcscpy(CreateResponse->OpaqueLockToken, DavFileAttributes.OpaqueLockToken); CreateResponse->LockTimeout = DavFileAttributes.LockTimeout; DavPrint((DEBUG_MISC, "DavLockTheFileOnTheServer: OpaqueLockToken = %ws, LockTimeout = %dsec\n", CreateResponse->OpaqueLockToken, CreateResponse->LockTimeout)); } else { WStatus = ERROR_INVALID_PARAMETER; DavPrint((DEBUG_ERRORS, "DavLockTheFileOnTheServer: OpaqueLockToken > MAX_PATH. WStatus = %d\n", WStatus)); } } DavFinalizeFileAttributesList(&DavFileAttributes, FALSE); DavCloseContext(Ctx1, Ctx2); EXIT_THE_FUNCTION: if (DavOpenHandle != NULL) { InternetCloseHandle(DavOpenHandle); DavOpenHandle = NULL; } if (PassportCookie) { LocalFree(PassportCookie); PassportCookie = NULL; } if (LockRequestBuffer) { LocalFree(LockRequestBuffer); LockRequestBuffer = NULL; } if (DataBuff) { LocalFree(DataBuff); DataBuff = NULL; } return WStatus; } DWORD DavFsLockRefresh( PDAV_USERMODE_WORKITEM DavWorkItem ) /*++ Routine Description: This routine handles LOCK refresh requests 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; HINTERNET DavConnHandle = NULL, DavOpenHandle = NULL; PDAV_USERMODE_LOCKREFRESH_REQUEST LockRefreshRequest = NULL; PDAV_USERMODE_LOCKREFRESH_RESPONSE LockRefreshResponse = NULL; BOOL BStatus = FALSE, ReturnVal = FALSE, readDone = FALSE; PPER_USER_ENTRY PerUserEntry = NULL; PHASH_SERVER_ENTRY ServerHashEntry = NULL; PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem = NULL; PWCHAR ServerName = NULL, PathName = NULL, CanName = NULL, PassportCookie = NULL; DAV_FILE_ATTRIBUTES DavFileAttributes; PVOID Ctx1 = NULL, Ctx2 = NULL; DWORD NumRead = 0, NumOfFileEntries = 0, TotalDataBytesRead = 0; BOOL didImpersonate = FALSE; BOOLEAN EnCriSec = FALSE, didITakeAPUEReference = FALSE; PCHAR DataBuff = NULL; LockRefreshRequest = &(DavWorkItem->LockRefreshRequest); LockRefreshResponse = &(DavWorkItem->LockRefreshResponse); ServerName = &(LockRefreshRequest->ServerName[1]); PathName = &(LockRefreshRequest->PathName[1]); // // The PathName can contain \ characters. Replace them by / characters. // CanName = PathName; while (*CanName) { if (*CanName == L'\\') { *CanName = L'/'; } CanName++; } UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem; // // A User Entry for this user must have been created during the create call // earlier. The user entry contains the handle used to send an HttpOpen // request. // EnterCriticalSection( &(HashServerEntryTableLock) ); EnCriSec = TRUE; ReturnVal = DavDoesUserEntryExist(ServerName, LockRefreshRequest->ServerID, &(LockRefreshRequest->LogonID), &PerUserEntry, &ServerHashEntry); // // If the following request in the kernel get cancelled even before the // corresponding usermode thread gets a chance to execute this code, then // it possible that the VNetRoot (hence the PerUserEntry) and SrvCall get // finalized before the thread that is handling the create comes here. This // could happen if this request was the only one for this share and the // server as well. This is why we need to check if the ServerHashEntry and // the PerUserEntry are valid before proceeding. // if (ReturnVal == FALSE || ServerHashEntry == NULL || PerUserEntry == NULL) { WStatus = ERROR_CANCELLED; DavPrint((DEBUG_ERRORS, "DavFsLockRefresh: (ServerHashEntry == NULL || PerUserEntry == NULL)\n")); goto EXIT_THE_FUNCTION; } DavWorkItem->ServerUserEntry.PerUserEntry = PerUserEntry; // // Add a reference to the user entry and set didITakeAPUEReference to TRUE. // PerUserEntry->UserEntryRefCount++; didITakeAPUEReference = TRUE; // // Since a create had succeeded earlier, the entry must be good. // ASSERT(PerUserEntry->UserEntryState == UserEntryInitialized); ASSERT(PerUserEntry->DavConnHandle != NULL); // // And yes, we obviously have to leave the critical section before // returning. // LeaveCriticalSection( &(HashServerEntryTableLock) ); EnCriSec = FALSE; WStatus = UMReflectorImpersonate(UserWorkItem, DavWorkItem->ImpersonationHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/UMReflectorImpersonate. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } didImpersonate = TRUE; // // Convert the unicode object name to UTF-8 URL format. // Space and other white characters will remain untouched - these should // be taken care of by wininet calls. // This has to be a W API as the name in CloseRequest is unicode. // BStatus = DavHttpOpenRequestW(PerUserEntry->DavConnHandle, L"LOCK", PathName, L"HTTP/1.1", NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD, 0, L"DavFsLockRefresh", &DavOpenHandle); if(BStatus == FALSE) { WStatus = GetLastError(); goto EXIT_THE_FUNCTION; } if (DavOpenHandle == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/DavHttpOpenRequestW. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // We need to add the header "translate:f" to tell IIS that it should // allow the user to excecute this VERB on the specified path which it // would not allow (in some cases) otherwise. Finally, there is a special // flag in the metabase to allow for uploading of "dangerous" content // (anything that can be run on the server). This is the ScriptSourceAccess // flag in the UI or the AccessSource flag in the metabase. You will need // to set this bit to true as well as correct NT ACLs in order to be able // to upload .exes or anything executable. We set this header on all requests // that are sent to the server including the LOCK request. // ReturnVal = HttpAddRequestHeadersW(DavOpenHandle, L"translate: f\n", -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/HttpAddRequestHeadersW. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } WStatus = DavAttachPassportCookie(DavWorkItem, DavOpenHandle, &PassportCookie); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/DavAttachPassportCookie. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } WStatus = DavInternetSetOption(DavWorkItem, DavOpenHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/DavInternetSetOption. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } // // Request the LOCK on this resource for an hour. We will have to refresh // the LOCK request if we need to keep the LOCK request for more than an // hour. // ReturnVal = HttpAddRequestHeadersW(DavOpenHandle, L"Timeout: Second-3600\n", -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/HttpAddRequestHeadersW. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } ASSERT(LockRefreshRequest->OpaqueLockToken != NULL); ReturnVal = HttpAddRequestHeadersW(DavOpenHandle, LockRefreshRequest->OpaqueLockToken, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/HttpAddRequestHeadersW. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } RESEND_THE_REQUEST: ReturnVal = HttpSendRequestExW(DavOpenHandle, NULL, NULL, HSR_SYNC, (ULONG_PTR)0); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/HttpSendRequestExW. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } ReturnVal = HttpEndRequestW(DavOpenHandle, NULL, HSR_SYNC, (ULONG_PTR)0); if (!ReturnVal) { WStatus = GetLastError(); // // If the error we got back is ERROR_INTERNET_FORCE_RETRY, then WinInet // is trying to authenticate itself with the server. If we get back // ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION, WinInet is expecting us to // confirm that the redirect needs to be followed. In these scenarios, // we need to repeat the HttpSend and HttpEnd request calls. // if (WStatus == ERROR_INTERNET_FORCE_RETRY || WStatus == ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION) { goto RESEND_THE_REQUEST; } DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/HttpEndRequestW. Error Val = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } WStatus = DavQueryAndParseResponse(DavOpenHandle); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/DavQueryAndParseResponse. WStatus = %d\n", WStatus)); } DataBuff = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, NUM_OF_BYTES_TO_READ); if (DataBuff == NULL) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/LocalAlloc: WStatus = %08lx\n", WStatus)); goto EXIT_THE_FUNCTION; } // // Read the response and parse it. // do { ReturnVal = InternetReadFile(DavOpenHandle, (LPVOID)DataBuff, NUM_OF_BYTES_TO_READ, &(NumRead)); if (!ReturnVal) { WStatus = GetLastError(); DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/InternetReadFile: WStatus = %08lx\n", WStatus)); goto EXIT_THE_FUNCTION; } DavPrint((DEBUG_MISC, "DavFsLockRefresh: NumRead = %d\n", NumRead)); // // We reject files whose attributes are greater than a certain size // (DavFileAttributesLimitInBytes). This is a parameter that can be // set in the registry. This is done to avoid attacks by rogue servers. // TotalDataBytesRead += NumRead; if (TotalDataBytesRead > DavFileAttributesLimitInBytes) { WStatus = ERROR_BAD_NET_RESP; DavPrint((DEBUG_ERRORS, "DavFsLockRefresh. LockResponseSize > %d\n", DavFileAttributesLimitInBytes)); goto EXIT_THE_FUNCTION; } readDone = (NumRead == 0) ? TRUE : FALSE; WStatus = DavPushData(DataBuff, &Ctx1, &Ctx2, NumRead, readDone); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/DavPushData. WStatus = %d\n", WStatus)); goto EXIT_THE_FUNCTION; } if (readDone) { break; } } while ( TRUE ); memset(&DavFileAttributes, 0, sizeof(DavFileAttributes)); InitializeListHead( &(DavFileAttributes.NextEntry) ); WStatus = DavParseData(&DavFileAttributes, Ctx1, Ctx2, &NumOfFileEntries); if (WStatus != ERROR_SUCCESS) { DavPrint((DEBUG_ERRORS, "DavFsLockRefresh/DavParseData. WStatus = %d\n", WStatus)); DavFinalizeFileAttributesList(&DavFileAttributes, FALSE); goto EXIT_THE_FUNCTION; } // // Get the NewTimeoutValue returned by the server. // LockRefreshResponse->NewTimeOutInSec = DavFileAttributes.LockTimeout; DavFinalizeFileAttributesList(&DavFileAttributes, FALSE); DavCloseContext(Ctx1, Ctx2); EXIT_THE_FUNCTION: if (DavOpenHandle != NULL) { InternetCloseHandle(DavOpenHandle); DavOpenHandle = NULL; } // // If didITakeAPUEReference is TRUE we need to remove the reference we // took on the PerUserEntry. // if (didITakeAPUEReference) { DavFinalizePerUserEntry( &(DavWorkItem->ServerUserEntry.PerUserEntry) ); } if (PassportCookie) { LocalFree(PassportCookie); PassportCookie = NULL; } if (DataBuff) { LocalFree(DataBuff); DataBuff = NULL; } // // If this thread impersonated a user, we need to revert back. // if (didImpersonate) { RevertToSelf(); didImpersonate = FALSE; } return WStatus; }