/*++ Copyright (c) 1998 Microsoft Corporation Module Name : virtroot.cxx Abstract: This module defines functions for managing virtual roots. Author: ??? ( ???) ??-??-1994/5 Environment: User Mode -- Win32 Project: TSunami DLL for Internet Services Functions Exported: Revision History: MuraliK Added File System type to information stored about each virtual root. MuraliK Modified TsLookupVirtualRoot() to support variable length buffer and hence check for invalid writes MuraliK 22-Jan-1996 Cache & return UNC virtual root impersonation token. MCourage 05-Jan-1997 Moved to cache2 directory for cache rewrite SaurabN 07-Oct-1998 Reimplement using LKRHash. --*/ /************************************************************ * Include Headers ************************************************************/ #include #include "TsunamiP.Hxx" #pragma hdrstop #include #include #include #include "dbgutil.h" #include #include #include #include #include #include IIS_VROOT_TABLE::IIS_VROOT_TABLE( VOID ) : CLKRHashTable("IISVRootTable", ExtractKey, CalcKeyHash, EqualKeys, AddRefRecord, DFLT_LK_INITSIZE, LK_SMALL_TABLESIZE), m_nVroots (0 ) { } // IIS_VROOT_TABLE::IIS_VROOT_TABLE IIS_VROOT_TABLE::~IIS_VROOT_TABLE( VOID ) { RemoveVirtualRoots( ); DBG_ASSERT( m_nVroots == 0 ); } // IIS_VROOT_TABLE::~IIS_VROOT_TABLE BOOL IIS_VROOT_TABLE::AddVirtualRoot( PCHAR pszRoot, PCHAR pszDirectory, DWORD dwAccessMask, PCHAR pszAccountName, HANDLE hImpersonationToken, DWORD dwFileSystem, BOOL fDoCache ) /*++ Description: This function adds a symbolic link root and directory mapping part to the virtual root list We always strip trailing slashes from the root and directory name. If the root is "\" or "/", then the effective root will be zero length and will always be placed last in the list. Thus if a lookup can't find a match, it will always match the last entry. Arguments: pszRoot - Virtual symbolic link root pszDirectory - Physical directory dwAccessMask - Type of access allowed on this virtual root pszAccountName - User name to impersonate if UNC (only gets stored for RPC apis) hImpersonationToken - Impersonation token to use for UNC directory paths dwFileSystem - DWORD containing the file system type ( symbolic constant) fDoCache - Should we cache the vdir Returns: TRUE on success and FALSE if any failure. --*/ { PVIRTUAL_ROOT_MAPPING pVrm, pVrmOld; PLIST_ENTRY pEntry; BOOL fRet = FALSE; BOOL fUNC; DWORD cchRoot; if ( !pszRoot || !pszDirectory || !*pszDirectory ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } // // Disallow allow UNC roots if we don't have an impersonation token and // this isn't a placeholder // fUNC = (BOOL)((pszDirectory[0] == '\\') && (pszDirectory[1] == '\\')); // // Strip the trailing '/' from the virtual root // cchRoot = strlen( pszRoot ); if ( IS_CHAR_TERM_A( pszRoot, cchRoot - 1)) { pszRoot[--cchRoot] = '\0'; } // // Look in the current list and see if the root is already there. // If the directory is the same, we just return success. If the // directory is different, we remove the old item and add the new one. // LockShared(); if (LK_SUCCESS == FindKey((DWORD_PTR) pszRoot, (const void **)&pVrm)) { // // Key exists // if ( !lstrcmpi( pszDirectory, pVrm->pszDirectoryA ) && IS_CHAR_TERM_A( pszDirectory, pVrm->cchDirectoryA )) { // // This root is already in the list // Unlock(); return TRUE; } else { // // A root is having its directory entry changed // Unlock(); SetLastError( ERROR_NOT_SUPPORTED ); return FALSE; } } // // Add a new key // pVrm = ( PVIRTUAL_ROOT_MAPPING )ALLOC( sizeof( VIRTUAL_ROOT_MAPPING ) ); if ( pVrm == NULL ) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); Unlock(); return FALSE; } // // Initialize the new root item // pVrm->Signature = VIRT_ROOT_SIGNATURE; pVrm->cchRootA = strlen( pszRoot ); if (pVrm->cchRootA > MAX_LENGTH_VIRTUAL_ROOT) { SetLastError( ERROR_BAD_PATHNAME ); Unlock(); return FALSE; } pVrm->cchDirectoryA = strlen( pszDirectory ); if (pVrm->cchDirectoryA > MAX_PATH) { SetLastError( ERROR_BAD_PATHNAME ); Unlock(); return FALSE; } pVrm->dwFileSystem = dwFileSystem; pVrm->dwAccessMask = dwAccessMask; pVrm->fUNC = (BOOLEAN)fUNC; pVrm->hImpersonationToken = hImpersonationToken; // // The old cache design used APCs to do change notification, the new cache // uses the CDirMon class so we don't need any refcounting. // strcpy( pVrm->pszRootA, pszRoot ); strcpy( pVrm->pszDirectoryA, pszDirectory ); strcpy( pVrm->pszAccountName, pszAccountName ? pszAccountName : "" ); // // Strip trailing slashes from the root and directory // if ( (pVrm->cchRootA != 0) && IS_CHAR_TERM_A( pVrm->pszRootA, pVrm->cchRootA - 1 )) { pVrm->pszRootA[--pVrm->cchRootA] = '\0'; } if ( IS_CHAR_TERM_A( pVrm->pszDirectoryA, pVrm->cchDirectoryA - 1)) { // // Note we assume virtual directories always begin with a '/...' to // provide the necessary path separator between the root directory // path and the remaining virtual directory // pVrm->pszDirectoryA[--pVrm->cchDirectoryA] = '\0'; } // // Add the item to the list // LockConvertExclusive(); fRet = (LK_SUCCESS == InsertRecord(pVrm)); if (fRet) { fRet = fDoCache ? DcmAddRoot( pVrm ) : TRUE; if (!fRet) { DeleteRecord(pVrm); } } if ( fRet) { m_nVroots++; IF_DEBUG( VIRTUAL_ROOTS ) DBGPRINTF(( DBG_CONTEXT, "Successfully added Vroot - %s => %s\n", pVrm->pszRootA, pVrm->pszDirectoryA )); } else { FREE( pVrm ); /* // // this memory was already released! // DBGPRINTF(( DBG_CONTEXT, " Error %d adding Vroot - %s => %s\n", GetLastError(), pVrm->pszRootA, pVrm->pszDirectoryA )); */ } Unlock(); return fRet; } // IIS_VROOT_TABLE::AddVirtualRoot BOOL IIS_VROOT_TABLE::LookupVirtualRoot( IN const CHAR * pszVirtPath, OUT CHAR * pszDirectory, IN OUT LPDWORD lpcbSize, OUT LPDWORD lpdwAccessMask, // Optional OUT LPDWORD pcchDirRoot, // Optional OUT LPDWORD pcchVRoot, // Optional OUT HANDLE * phImpersonationToken, // Optional OUT LPDWORD lpdwFileSystem // Optional ) /*++ Description: This function looks in the map list for the specified root and returns the corresponding directory Arguments: pszVirtPath - Virtual symbolic link path pszDirectory - receives Physical directory. This is of the size specified by lpcbSize lpcbSize - pointer to DWORD containing the size of buffer pszDirectory On retur contains the number of bytes written lpdwAccessMask - The access mask for this root pcchDirRoot - Number of characters of the directory this virtual root maps to (i.e., /foo/ ==> c:\root, lookup "/foo/bar/abc.htm" this value returns the length of "c:\root") pcchVRoot - Number of characters that made up the found virtual root (i.e., returns the lenght of "/foo/") phImpersonationToken - pointer to handle object that will contain the handle to be used for impersonation for UNC/secure virtual roots lpdwFileSystem - on successful return will contain the file system type for the directory matched with root specified. Returns: TRUE on success and FALSE if any failure. History: MuraliK 28-Apr-1995 Improved robustness MuraliK 18-Jan-1996 Support imperonstaion token Note: This function is growing in the number of parameters returned. Maybe we should expose the VIRTUAL_ROOT_MAPPING structure and return a pointer to this object and allow the callers to extract all required pieces of data. --*/ { DWORD dwError = NO_ERROR; PVIRTUAL_ROOT_MAPPING pVrm = NULL; DBG_ASSERT( pszDirectory != NULL); DBG_ASSERT( lpcbSize != NULL); DWORD cchPath = strlen( pszVirtPath ); char * pszPath = (char *)_alloca(cchPath); // make local copy that we can modify strcpy(pszPath, pszVirtPath); // // Strip the trailing '/' from the virtual path // if ( IS_CHAR_TERM_A( pszPath, cchPath - 1)) { pszPath[--cchPath] = '\0'; } char * pCh = pszPath; LockShared(); while(pCh != NULL) { if (LK_SUCCESS == FindKey((DWORD_PTR) pszPath, (const void **)&pVrm)) { break; } else { // // Trim the VRoot path from the right to the next / // pCh = (char *)IISstrrchr( (const UCHAR *)pszPath, '/'); if (pCh) { *pCh = '\0'; // truncate search string } } } if (pVrm) { // // we found a match. return all requested parameters. // DBG_ASSERT( pVrm->Signature == VIRT_ROOT_SIGNATURE ); DWORD cbReqd = ( pVrm->cchDirectoryA + strlen(pszVirtPath + pVrm->cchRootA)); if ( cbReqd <= *lpcbSize) { PCHAR pathStart = pszDirectory + pVrm->cchDirectoryA; // // Copy the physical directory base then append the rest of // the non-matching virtual path // CopyMemory( pszDirectory, pVrm->pszDirectoryA, pVrm->cchDirectoryA ); strcpy( pathStart, pszVirtPath + pVrm->cchRootA ); if ( lpdwFileSystem != NULL) { *lpdwFileSystem = pVrm->dwFileSystem; } if ( pcchDirRoot ) { *pcchDirRoot = pVrm->cchDirectoryA; } if ( pcchVRoot ) { *pcchVRoot = pVrm->cchRootA; } if ( lpdwAccessMask != NULL) { *lpdwAccessMask = pVrm->dwAccessMask; } if ( phImpersonationToken != NULL) { // Should we increment refcount of the impersonation token? *phImpersonationToken = pVrm->hImpersonationToken; } Unlock(); FlipSlashes( pathStart ); *lpcbSize = cbReqd; return(TRUE); } else { dwError = ERROR_INSUFFICIENT_BUFFER; } *lpcbSize = cbReqd; } if ( lpdwAccessMask ) { *lpdwAccessMask = 0; } if ( lpdwFileSystem != NULL) { *lpdwFileSystem = FS_ERROR; } if ( phImpersonationToken != NULL) { *phImpersonationToken = NULL; } if ( dwError == NO_ERROR) { dwError = ERROR_PATH_NOT_FOUND; } SetLastError( dwError ); Unlock(); return FALSE; } // IIS_VROOT_TABLE::LookupVirtualRoot BOOL IIS_VROOT_TABLE::RemoveVirtualRoots( VOID ) /*++ Description: Removes all of the virtual roots for the instance Arguments: None. Returns: TRUE on success and FALSE if any failure. --*/ { PVIRTUAL_ROOT_MAPPING pVrm; CIterator iter; DWORD dwValue; LockExclusive(); LK_RETCODE lkrc = InitializeIterator(&iter); while (LK_SUCCESS == lkrc) { pVrm = (PVIRTUAL_ROOT_MAPPING) iter.Record(); DBG_ASSERT( pVrm->Signature == VIRT_ROOT_SIGNATURE ); // // Increment the iterator before removing the record from the Hash // Table else we hit an Assert in the increment. // lkrc = IncrementIterator(&iter); DeleteVRootEntry(pVrm); // Removes from Hash Table Also } CloseIterator(&iter); Clear(); Unlock(); return TRUE; } // TsRemoveVirtualRoots BOOL IIS_VROOT_TABLE::RemoveVirtualRoot( IN PCHAR pszVirtPath ) /*++ Description: Removes the virtual roots named pszVirtPath for the instance. Arguments: pszVirtPath - Name of the virtual root to remove. Returns: TRUE on success and FALSE if any failure. --*/ { PVIRTUAL_ROOT_MAPPING pVrm; BOOL bSuccess = FALSE; DWORD cchPath = strlen( pszVirtPath ); // // Strip the trailing '/' from the virtual path // if ( IS_CHAR_TERM_A( pszVirtPath, cchPath - 1)) { pszVirtPath[--cchPath] = '\0'; } LockExclusive(); if (LK_SUCCESS == FindKey((DWORD_PTR) pszVirtPath, (const void **)&pVrm)) { // // Found a match // DeleteRecord(pVrm); // Remove Entry from Hash Table DeleteVRootEntry(pVrm); bSuccess = TRUE; } Unlock(); return bSuccess; } VOID IIS_VROOT_TABLE::DeleteVRootEntry( IN PVOID pEntry ) { DBG_ASSERT(NULL != pEntry); PVIRTUAL_ROOT_MAPPING pVrm = (PVIRTUAL_ROOT_MAPPING)pEntry; DBG_ASSERT(pVrm->Signature == VIRT_ROOT_SIGNATURE ); m_nVroots--; IF_DEBUG( DIRECTORY_CHANGE ) DBGPRINTF(( DBG_CONTEXT, "Removing root %s\n", pVrm->pszDirectoryA )); // // Remove reference to Dir Monitor // DcmRemoveRoot( pVrm ); // // We need to close the impersonation token, if one exists. // if ( pVrm->hImpersonationToken != NULL) { DBG_REQUIRE( CloseHandle( pVrm->hImpersonationToken )); pVrm->hImpersonationToken = NULL; } pVrm->Signature = 0; FREE( pVrm ); } const DWORD_PTR IIS_VROOT_TABLE::ExtractKey(const void* pvRecord) { PVIRTUAL_ROOT_MAPPING pVrm = (PVIRTUAL_ROOT_MAPPING)pvRecord; return (DWORD_PTR)pVrm->pszRootA; } DWORD IIS_VROOT_TABLE::CalcKeyHash(const DWORD_PTR pnKey) { return HashStringNoCase((LPCSTR)pnKey); } bool IIS_VROOT_TABLE::EqualKeys(const DWORD_PTR pnKey1, const DWORD_PTR pnKey2) { return ( IISstricmp((UCHAR *)pnKey1, (UCHAR *)pnKey2) == 0); }