Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

493 lines
13 KiB

/*
* F S U T I L . C P P
*
* File system routines
*
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
*/
#include "_davfs.h"
#include <aclapi.h>
const CHAR gc_szUncPrefix[] = "\\\\";
const UINT gc_cchszUncPrefix = CElems(gc_szUncPrefix) - 1;
// Location checking ---------------------------------------------------------
//
// ScCheckForLocationCorrectness() will check the url against the
// resource and either add the appropriate location header, or it will
// request a redirect if the url and the resource do not agree. The
// caller has the control over whether or not a true redirect is desired.
// As an informational return, if a location header has been added S_FALSE
// will be returned to the caller.
//
SCODE
ScCheckForLocationCorrectness (IMethUtil* pmu,
CResourceInfo& cri,
UINT modeRedirect)
{
SCODE sc = S_OK;
BOOL fTrailing;
Assert (pmu);
fTrailing = FTrailingSlash (pmu->LpwszRequestUrl());
// If the trailing slash existance does not jive with the resource type...
//
if (!cri.FCollection() != !fTrailing)
{
if (modeRedirect == REDIRECT)
{
auto_heap_ptr<CHAR> pszLocation;
// Construct the redirect url.
//
sc = pmu->ScConstructRedirectUrl (cri.FCollection(),
pszLocation.load());
if (FAILED (sc))
goto ret;
// Redirect this badboy
//
sc = pmu->ScRedirect (pszLocation);
if (FAILED (sc))
goto ret;
}
else
{
// EmitLocation takes care of the trailing slash checking
//
pmu->EmitLocation (gc_szContent_Location,
pmu->LpwszRequestUrl(),
cri.FCollection());
}
// Tell the caller we had to change the location
//
sc = S_FALSE;
}
ret:
return sc;
}
// Access checking -----------------------------------------------------------
//
// class safe_security_revert ------------------------------------------------
//
// Switches the current thread's impersonation token to the cached
// "Reverted Security-enabled Thread Token" when FSecurityInit is called,
// for the duration of the object's lifespan.
// Unconditionally reimpersonates on exit, based on the provided handle.
//
// NOTE: UNCONDITIONALLY reimpersonates on exit, using the impersonation
// handle provided at construction-time.
// (Just wanted to make that clear.)
//
// WARNING: the safe_revert class should only be used by FChildISAPIAccessCheck
// below. It is not a "quick way to get around" impersonation. If
// you do need to do something like this, please see Becky -- she will then
// wack you up'side the head.
//
class safe_security_revert
{
// Local client token to re-impersonate at dtor time.
HANDLE m_hClientToken;
// This is our cached security-enabled thread token.
static HANDLE s_hSecurityThreadToken;
// NOT IMPLEMENTED
//
safe_security_revert (const safe_security_revert&);
safe_security_revert& operator= (const safe_security_revert&);
public:
explicit safe_security_revert (HANDLE h) : m_hClientToken(h)
{
Assert (m_hClientToken);
}
~safe_security_revert()
{
if (!ImpersonateLoggedOnUser (m_hClientToken))
{
DebugTrace ("ImpersonateLoggedOnUser failed with last error %d\n", GetLastError());
// There's not much we can do in this dtor. throw
//
throw CLastErrorException();
}
}
BOOL FSecurityInit (BOOL fForceRefresh);
// Token cache manipulators
//
static inline HANDLE GetToken();
static inline VOID ClearToken();
static inline BOOL FSetToken( HANDLE hToken );
};
// Storage for our metaclass data (the cached thread token).
//
HANDLE safe_security_revert::s_hSecurityThreadToken = NULL;
// Public function to clear out the cached thread token.
// Simply calls the metaclass method.
//
void CleanupSecurityToken()
{
safe_security_revert::ClearToken();
}
// ------------------------------------------------------------------------
//
// GetToken()
//
// Return the cached security token.
//
HANDLE safe_security_revert::GetToken()
{
return s_hSecurityThreadToken;
}
// ------------------------------------------------------------------------
//
// FSetToken()
//
// Set the cached security token.
//
BOOL safe_security_revert::FSetToken( HANDLE hToken )
{
//
// If the cache is clear then set it with this token
// and return whether we cache the token.
//
return NULL == InterlockedCompareExchangePointer(&s_hSecurityThreadToken,
hToken,
NULL);
}
// ------------------------------------------------------------------------
//
// ClearToken()
//
// Clear out the cached security token
//
VOID safe_security_revert::ClearToken()
{
//
// Replace whatever token is cached with NULL.
//
HANDLE hToken = InterlockedExchangePointer( &s_hSecurityThreadToken,
NULL);
//
// If we replaced a non-NULL token then close it.
//
if (hToken)
CloseHandle (hToken);
}
// ------------------------------------------------------------------------
//
// FSecurityInit()
//
// Set our thread token to the cached security-enabled thread token.
// If no security-enabled token is cached, go get one.
//
BOOL safe_security_revert::FSecurityInit (BOOL fForceRefresh)
{
auto_handle<HANDLE> hTokenNew;
HANDLE hToken;
// Clear out the cached security token if told to do so.
//
if (fForceRefresh)
ClearToken();
// Fetch the cached security token. Note that even if
// we just cleared it out, we may get back a non-NULL
// token here if another thread has already reloaded
// the cache.
//
hToken = GetToken();
//
// If the cache was clear then create our own new token
// that is set up to do security access queries.
//
if ( NULL == hToken )
{
LUID SecurityPrivilegeID;
TOKEN_PRIVILEGES tkp;
// RevertToSelf to get us running as system (the local diety).
//
if (!RevertToSelf())
return FALSE;
// ImpersonateSelf copies the process token down to this thread.
// Then we can change the thread token's privileges without messing
// up the process token.
//
if (!ImpersonateSelf (SecurityImpersonation))
{
DebugTrace ("ssr::FSecurityInit--ImpersonateSelf failed with %d.\n",
GetLastError());
return FALSE;
}
// Open our newly-copied thread token to add a privilege (security).
// NOTE: The adjust and query flags are needed for this operation.
// The impersonate flag is needed for use to use this token for
// impersonation -- as we do in SetThreadToken below.
// OpenAsSelf -- FALSE means open as thread, possibly impersonated.
// TRUE means open as the calling process, not as the local (impersonated) thread.
//
if (!OpenThreadToken (GetCurrentThread(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY |
TOKEN_IMPERSONATE,
TRUE,
hTokenNew.load()))
{
DebugTrace ("ssr::FSecurityInit--OpenThreadToken failed with %d.\n",
GetLastError());
return FALSE;
}
// Enable the SE_SECURITY_NAME privilege, so that we can fetch
// security descriptors and call AccessCheck.
//
if (!LookupPrivilegeValue (NULL,
SE_SECURITY_NAME,
&SecurityPrivilegeID))
{
DebugTrace ("ssr::FSecurityInit--LookupPrivilegeValue failed with %d\n",
GetLastError());
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = SecurityPrivilegeID;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges (hTokenNew,
FALSE,
&tkp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL);
// The return value of AdjustTokenPrivileges cannot be tested directly...
// (always returns 1)
//
if (GetLastError() != ERROR_SUCCESS)
{
DebugTrace ("ssr::FSecurityInit--AdjustTokenPrivileges failed with %d\n",
GetLastError());
return FALSE;
}
// Use this new token
//
hToken = hTokenNew.get();
}
// At this point we must have a token
//
Assert (NULL != hToken);
// Set the current thread to use the token.
//
if (!SetThreadToken (NULL, hToken))
{
DebugTrace ("ssr::FSecurityInit--SetThreadToken failed with %d.\n",
GetLastError());
return FALSE;
}
// Everything's cool. We are now running with a thread token
// that has security-checking privileges.
//
// If we created a new token along the way then attempt to cache it.
// We don't care if caching fails, but if it succeeds we DON'T want
// to close the handle because we just gave it to the cache.
//
if (hTokenNew.get())
{
if (FSetToken(hTokenNew.get()))
{
hTokenNew.relinquish();
}
}
return TRUE;
}
GENERIC_MAPPING gc_gmFile =
{
FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
FILE_GENERIC_EXECUTE,
FILE_ALL_ACCESS
};
// ------------------------------------------------------------------------
//
// ScChildISAPIAccessCheck
//
// Checks if the client (our impersonation handle from off the ECB)
// has the specified access to the specified resource.
// NOTE: Uses a cached "security-enabled-thread-token" to query the
// security descriptor for the specified resource.
//
SCODE __fastcall
ScChildISAPIAccessCheck (const IEcb& ecb, LPCWSTR pwsz, DWORD dwAccess, LPBYTE pbSD)
{
SECURITY_DESCRIPTOR * pSD = NULL;
DWORD dwRet;
auto_handle<HANDLE> hToken;
BYTE psFile[256];
DWORD dwPS = sizeof (psFile);
DWORD dwGrantedAccess = 0;
BOOL fAccess = FALSE;
BOOL fRet;
// pbSD is used only in DAVEX, should never be passed in from HTTPEXT
//
if (NULL != pbSD)
{
// This should never happen. Removing the param is not
//
throw CHresultException (E_FAIL);
}
// IIS should have granted our impersonated token the proper access
// rights to check the ACL's on the resource. So we are going to go
// after it without any change of impersonation.
//
dwRet = GetNamedSecurityInfoW (const_cast<LPWSTR>(pwsz),
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
NULL, NULL, NULL, NULL,
reinterpret_cast<VOID **>(&pSD));
if (ERROR_SUCCESS != dwRet)
{
// If the resource does not exist at all, as no security prevent
// us from trying to access a non-existing resource, so we
// should allow the access.
//
if ((dwRet == ERROR_PATH_NOT_FOUND) ||
(dwRet == ERROR_FILE_NOT_FOUND))
{
fAccess = TRUE;
goto ret;
}
// Now then... If we got here, we don't really know what went wrong,
// so we are going to try and do things the old way.
//
// BTW: We really do not expect this code to ever get run.
//
DebugTrace ("WARNING: WARNING: WARNING: ScChildISAPIAccessCheck() -- "
"GetNamedSecurityInfoW() failed %d (0x%08x): falling back...\n",
dwRet, dwRet);
// Scope to control the lifetime of our un-impersonation.
//
safe_security_revert sr (ecb.HitUser());
dwRet = GetNamedSecurityInfoW (const_cast<LPWSTR>(pwsz),
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
NULL, NULL, NULL, NULL,
reinterpret_cast<VOID **>(&pSD));
if (ERROR_SUCCESS != dwRet)
{
// If the resource does not exist at all, as no security prevent
// us from trying to access a non-existing resource, so we
// should allow the access.
//
if ((dwRet == ERROR_PATH_NOT_FOUND) ||
(dwRet == ERROR_FILE_NOT_FOUND))
{
fAccess = TRUE;
}
goto ret;
}
// End of safe_security_revert scope.
// Now the safe_security_revert dtor will re-impersonate us.
//
}
// Get our thread's access token.
// OpenAsSelf -- TRUE means open the thread token as the process
// itself FALSE would mean as thread, possibly impersonated
// We want the impersonated access token, so we want FALSE here!
//
fRet = OpenThreadToken (GetCurrentThread(),
TOKEN_QUERY,
TRUE,
hToken.load());
if (!fRet)
{
// This should NEVER fail. We are impersonated, so we do have
// a thread-level access token. If conditions change, and we
// have a state where this can fail, remove the TrapSz below!
//
//$ REVIEW: OpenThreadToken() can fail for any number of reasons
// not excluding resource availability. So, this trap is a bit
// harsh, no?
//
// TrapSz("OpenThreadToken failed while we are impersonated!");
//
//$ REVIEW: end.
DebugTrace ("ScChildISAPIAccessCheck--"
"Error from OpenThreadToken %d (0x%08x).\n",
GetLastError(), GetLastError());
goto ret;
}
// Map the requested access to file-specific access bits....
//
MapGenericMask (&dwAccess, &gc_gmFile);
// And now check for this access on the file.
//
fRet = AccessCheck (pSD,
hToken,
dwAccess,
&gc_gmFile,
(PRIVILEGE_SET*)psFile,
&dwPS,
&dwGrantedAccess,
&fAccess);
if (!fRet)
{
DebugTrace ("ScChildISAPIAccessCheck--Error from AccessCheck %d (0x%08x).\n",
GetLastError(), GetLastError());
goto ret;
}
// Now, fAccess tells whether the impersonated token has
// the requested access. Return this to the caller.
//
ret:
if (pSD)
LocalFree (pSD);
return fAccess ? S_OK : E_ACCESSDENIED;
}