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

  1. /*
  2. * F S U T I L . C P P
  3. *
  4. * File system routines
  5. *
  6. * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include "_davfs.h"
  9. #include <aclapi.h>
  10. const CHAR gc_szUncPrefix[] = "\\\\";
  11. const UINT gc_cchszUncPrefix = CElems(gc_szUncPrefix) - 1;
  12. // Location checking ---------------------------------------------------------
  13. //
  14. // ScCheckForLocationCorrectness() will check the url against the
  15. // resource and either add the appropriate location header, or it will
  16. // request a redirect if the url and the resource do not agree. The
  17. // caller has the control over whether or not a true redirect is desired.
  18. // As an informational return, if a location header has been added S_FALSE
  19. // will be returned to the caller.
  20. //
  21. SCODE
  22. ScCheckForLocationCorrectness (IMethUtil* pmu,
  23. CResourceInfo& cri,
  24. UINT modeRedirect)
  25. {
  26. SCODE sc = S_OK;
  27. BOOL fTrailing;
  28. Assert (pmu);
  29. fTrailing = FTrailingSlash (pmu->LpwszRequestUrl());
  30. // If the trailing slash existance does not jive with the resource type...
  31. //
  32. if (!cri.FCollection() != !fTrailing)
  33. {
  34. if (modeRedirect == REDIRECT)
  35. {
  36. auto_heap_ptr<CHAR> pszLocation;
  37. // Construct the redirect url.
  38. //
  39. sc = pmu->ScConstructRedirectUrl (cri.FCollection(),
  40. pszLocation.load());
  41. if (FAILED (sc))
  42. goto ret;
  43. // Redirect this badboy
  44. //
  45. sc = pmu->ScRedirect (pszLocation);
  46. if (FAILED (sc))
  47. goto ret;
  48. }
  49. else
  50. {
  51. // EmitLocation takes care of the trailing slash checking
  52. //
  53. pmu->EmitLocation (gc_szContent_Location,
  54. pmu->LpwszRequestUrl(),
  55. cri.FCollection());
  56. }
  57. // Tell the caller we had to change the location
  58. //
  59. sc = S_FALSE;
  60. }
  61. ret:
  62. return sc;
  63. }
  64. // Access checking -----------------------------------------------------------
  65. //
  66. // class safe_security_revert ------------------------------------------------
  67. //
  68. // Switches the current thread's impersonation token to the cached
  69. // "Reverted Security-enabled Thread Token" when FSecurityInit is called,
  70. // for the duration of the object's lifespan.
  71. // Unconditionally reimpersonates on exit, based on the provided handle.
  72. //
  73. // NOTE: UNCONDITIONALLY reimpersonates on exit, using the impersonation
  74. // handle provided at construction-time.
  75. // (Just wanted to make that clear.)
  76. //
  77. // WARNING: the safe_revert class should only be used by FChildISAPIAccessCheck
  78. // below. It is not a "quick way to get around" impersonation. If
  79. // you do need to do something like this, please see Becky -- she will then
  80. // wack you up'side the head.
  81. //
  82. class safe_security_revert
  83. {
  84. // Local client token to re-impersonate at dtor time.
  85. HANDLE m_hClientToken;
  86. // This is our cached security-enabled thread token.
  87. static HANDLE s_hSecurityThreadToken;
  88. // NOT IMPLEMENTED
  89. //
  90. safe_security_revert (const safe_security_revert&);
  91. safe_security_revert& operator= (const safe_security_revert&);
  92. public:
  93. explicit safe_security_revert (HANDLE h) : m_hClientToken(h)
  94. {
  95. Assert (m_hClientToken);
  96. }
  97. ~safe_security_revert()
  98. {
  99. if (!ImpersonateLoggedOnUser (m_hClientToken))
  100. {
  101. DebugTrace ("ImpersonateLoggedOnUser failed with last error %d\n", GetLastError());
  102. // There's not much we can do in this dtor. throw
  103. //
  104. throw CLastErrorException();
  105. }
  106. }
  107. BOOL FSecurityInit (BOOL fForceRefresh);
  108. // Token cache manipulators
  109. //
  110. static inline HANDLE GetToken();
  111. static inline VOID ClearToken();
  112. static inline BOOL FSetToken( HANDLE hToken );
  113. };
  114. // Storage for our metaclass data (the cached thread token).
  115. //
  116. HANDLE safe_security_revert::s_hSecurityThreadToken = NULL;
  117. // Public function to clear out the cached thread token.
  118. // Simply calls the metaclass method.
  119. //
  120. void CleanupSecurityToken()
  121. {
  122. safe_security_revert::ClearToken();
  123. }
  124. // ------------------------------------------------------------------------
  125. //
  126. // GetToken()
  127. //
  128. // Return the cached security token.
  129. //
  130. HANDLE safe_security_revert::GetToken()
  131. {
  132. return s_hSecurityThreadToken;
  133. }
  134. // ------------------------------------------------------------------------
  135. //
  136. // FSetToken()
  137. //
  138. // Set the cached security token.
  139. //
  140. BOOL safe_security_revert::FSetToken( HANDLE hToken )
  141. {
  142. //
  143. // If the cache is clear then set it with this token
  144. // and return whether we cache the token.
  145. //
  146. return NULL == InterlockedCompareExchangePointer(&s_hSecurityThreadToken,
  147. hToken,
  148. NULL);
  149. }
  150. // ------------------------------------------------------------------------
  151. //
  152. // ClearToken()
  153. //
  154. // Clear out the cached security token
  155. //
  156. VOID safe_security_revert::ClearToken()
  157. {
  158. //
  159. // Replace whatever token is cached with NULL.
  160. //
  161. HANDLE hToken = InterlockedExchangePointer( &s_hSecurityThreadToken,
  162. NULL);
  163. //
  164. // If we replaced a non-NULL token then close it.
  165. //
  166. if (hToken)
  167. CloseHandle (hToken);
  168. }
  169. // ------------------------------------------------------------------------
  170. //
  171. // FSecurityInit()
  172. //
  173. // Set our thread token to the cached security-enabled thread token.
  174. // If no security-enabled token is cached, go get one.
  175. //
  176. BOOL safe_security_revert::FSecurityInit (BOOL fForceRefresh)
  177. {
  178. auto_handle<HANDLE> hTokenNew;
  179. HANDLE hToken;
  180. // Clear out the cached security token if told to do so.
  181. //
  182. if (fForceRefresh)
  183. ClearToken();
  184. // Fetch the cached security token. Note that even if
  185. // we just cleared it out, we may get back a non-NULL
  186. // token here if another thread has already reloaded
  187. // the cache.
  188. //
  189. hToken = GetToken();
  190. //
  191. // If the cache was clear then create our own new token
  192. // that is set up to do security access queries.
  193. //
  194. if ( NULL == hToken )
  195. {
  196. LUID SecurityPrivilegeID;
  197. TOKEN_PRIVILEGES tkp;
  198. // RevertToSelf to get us running as system (the local diety).
  199. //
  200. if (!RevertToSelf())
  201. return FALSE;
  202. // ImpersonateSelf copies the process token down to this thread.
  203. // Then we can change the thread token's privileges without messing
  204. // up the process token.
  205. //
  206. if (!ImpersonateSelf (SecurityImpersonation))
  207. {
  208. DebugTrace ("ssr::FSecurityInit--ImpersonateSelf failed with %d.\n",
  209. GetLastError());
  210. return FALSE;
  211. }
  212. // Open our newly-copied thread token to add a privilege (security).
  213. // NOTE: The adjust and query flags are needed for this operation.
  214. // The impersonate flag is needed for use to use this token for
  215. // impersonation -- as we do in SetThreadToken below.
  216. // OpenAsSelf -- FALSE means open as thread, possibly impersonated.
  217. // TRUE means open as the calling process, not as the local (impersonated) thread.
  218. //
  219. if (!OpenThreadToken (GetCurrentThread(),
  220. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY |
  221. TOKEN_IMPERSONATE,
  222. TRUE,
  223. hTokenNew.load()))
  224. {
  225. DebugTrace ("ssr::FSecurityInit--OpenThreadToken failed with %d.\n",
  226. GetLastError());
  227. return FALSE;
  228. }
  229. // Enable the SE_SECURITY_NAME privilege, so that we can fetch
  230. // security descriptors and call AccessCheck.
  231. //
  232. if (!LookupPrivilegeValue (NULL,
  233. SE_SECURITY_NAME,
  234. &SecurityPrivilegeID))
  235. {
  236. DebugTrace ("ssr::FSecurityInit--LookupPrivilegeValue failed with %d\n",
  237. GetLastError());
  238. return FALSE;
  239. }
  240. tkp.PrivilegeCount = 1;
  241. tkp.Privileges[0].Luid = SecurityPrivilegeID;
  242. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  243. AdjustTokenPrivileges (hTokenNew,
  244. FALSE,
  245. &tkp,
  246. sizeof(TOKEN_PRIVILEGES),
  247. (PTOKEN_PRIVILEGES) NULL,
  248. (PDWORD) NULL);
  249. // The return value of AdjustTokenPrivileges cannot be tested directly...
  250. // (always returns 1)
  251. //
  252. if (GetLastError() != ERROR_SUCCESS)
  253. {
  254. DebugTrace ("ssr::FSecurityInit--AdjustTokenPrivileges failed with %d\n",
  255. GetLastError());
  256. return FALSE;
  257. }
  258. // Use this new token
  259. //
  260. hToken = hTokenNew.get();
  261. }
  262. // At this point we must have a token
  263. //
  264. Assert (NULL != hToken);
  265. // Set the current thread to use the token.
  266. //
  267. if (!SetThreadToken (NULL, hToken))
  268. {
  269. DebugTrace ("ssr::FSecurityInit--SetThreadToken failed with %d.\n",
  270. GetLastError());
  271. return FALSE;
  272. }
  273. // Everything's cool. We are now running with a thread token
  274. // that has security-checking privileges.
  275. //
  276. // If we created a new token along the way then attempt to cache it.
  277. // We don't care if caching fails, but if it succeeds we DON'T want
  278. // to close the handle because we just gave it to the cache.
  279. //
  280. if (hTokenNew.get())
  281. {
  282. if (FSetToken(hTokenNew.get()))
  283. {
  284. hTokenNew.relinquish();
  285. }
  286. }
  287. return TRUE;
  288. }
  289. GENERIC_MAPPING gc_gmFile =
  290. {
  291. FILE_GENERIC_READ,
  292. FILE_GENERIC_WRITE,
  293. FILE_GENERIC_EXECUTE,
  294. FILE_ALL_ACCESS
  295. };
  296. // ------------------------------------------------------------------------
  297. //
  298. // ScChildISAPIAccessCheck
  299. //
  300. // Checks if the client (our impersonation handle from off the ECB)
  301. // has the specified access to the specified resource.
  302. // NOTE: Uses a cached "security-enabled-thread-token" to query the
  303. // security descriptor for the specified resource.
  304. //
  305. SCODE __fastcall
  306. ScChildISAPIAccessCheck (const IEcb& ecb, LPCWSTR pwsz, DWORD dwAccess, LPBYTE pbSD)
  307. {
  308. SECURITY_DESCRIPTOR * pSD = NULL;
  309. DWORD dwRet;
  310. auto_handle<HANDLE> hToken;
  311. BYTE psFile[256];
  312. DWORD dwPS = sizeof (psFile);
  313. DWORD dwGrantedAccess = 0;
  314. BOOL fAccess = FALSE;
  315. BOOL fRet;
  316. // pbSD is used only in DAVEX, should never be passed in from HTTPEXT
  317. //
  318. if (NULL != pbSD)
  319. {
  320. // This should never happen. Removing the param is not
  321. //
  322. throw CHresultException (E_FAIL);
  323. }
  324. // IIS should have granted our impersonated token the proper access
  325. // rights to check the ACL's on the resource. So we are going to go
  326. // after it without any change of impersonation.
  327. //
  328. dwRet = GetNamedSecurityInfoW (const_cast<LPWSTR>(pwsz),
  329. SE_FILE_OBJECT,
  330. OWNER_SECURITY_INFORMATION |
  331. GROUP_SECURITY_INFORMATION |
  332. DACL_SECURITY_INFORMATION,
  333. NULL, NULL, NULL, NULL,
  334. reinterpret_cast<VOID **>(&pSD));
  335. if (ERROR_SUCCESS != dwRet)
  336. {
  337. // If the resource does not exist at all, as no security prevent
  338. // us from trying to access a non-existing resource, so we
  339. // should allow the access.
  340. //
  341. if ((dwRet == ERROR_PATH_NOT_FOUND) ||
  342. (dwRet == ERROR_FILE_NOT_FOUND))
  343. {
  344. fAccess = TRUE;
  345. goto ret;
  346. }
  347. // Now then... If we got here, we don't really know what went wrong,
  348. // so we are going to try and do things the old way.
  349. //
  350. // BTW: We really do not expect this code to ever get run.
  351. //
  352. DebugTrace ("WARNING: WARNING: WARNING: ScChildISAPIAccessCheck() -- "
  353. "GetNamedSecurityInfoW() failed %d (0x%08x): falling back...\n",
  354. dwRet, dwRet);
  355. // Scope to control the lifetime of our un-impersonation.
  356. //
  357. safe_security_revert sr (ecb.HitUser());
  358. dwRet = GetNamedSecurityInfoW (const_cast<LPWSTR>(pwsz),
  359. SE_FILE_OBJECT,
  360. OWNER_SECURITY_INFORMATION |
  361. GROUP_SECURITY_INFORMATION |
  362. DACL_SECURITY_INFORMATION,
  363. NULL, NULL, NULL, NULL,
  364. reinterpret_cast<VOID **>(&pSD));
  365. if (ERROR_SUCCESS != dwRet)
  366. {
  367. // If the resource does not exist at all, as no security prevent
  368. // us from trying to access a non-existing resource, so we
  369. // should allow the access.
  370. //
  371. if ((dwRet == ERROR_PATH_NOT_FOUND) ||
  372. (dwRet == ERROR_FILE_NOT_FOUND))
  373. {
  374. fAccess = TRUE;
  375. }
  376. goto ret;
  377. }
  378. // End of safe_security_revert scope.
  379. // Now the safe_security_revert dtor will re-impersonate us.
  380. //
  381. }
  382. // Get our thread's access token.
  383. // OpenAsSelf -- TRUE means open the thread token as the process
  384. // itself FALSE would mean as thread, possibly impersonated
  385. // We want the impersonated access token, so we want FALSE here!
  386. //
  387. fRet = OpenThreadToken (GetCurrentThread(),
  388. TOKEN_QUERY,
  389. TRUE,
  390. hToken.load());
  391. if (!fRet)
  392. {
  393. // This should NEVER fail. We are impersonated, so we do have
  394. // a thread-level access token. If conditions change, and we
  395. // have a state where this can fail, remove the TrapSz below!
  396. //
  397. //$ REVIEW: OpenThreadToken() can fail for any number of reasons
  398. // not excluding resource availability. So, this trap is a bit
  399. // harsh, no?
  400. //
  401. // TrapSz("OpenThreadToken failed while we are impersonated!");
  402. //
  403. //$ REVIEW: end.
  404. DebugTrace ("ScChildISAPIAccessCheck--"
  405. "Error from OpenThreadToken %d (0x%08x).\n",
  406. GetLastError(), GetLastError());
  407. goto ret;
  408. }
  409. // Map the requested access to file-specific access bits....
  410. //
  411. MapGenericMask (&dwAccess, &gc_gmFile);
  412. // And now check for this access on the file.
  413. //
  414. fRet = AccessCheck (pSD,
  415. hToken,
  416. dwAccess,
  417. &gc_gmFile,
  418. (PRIVILEGE_SET*)psFile,
  419. &dwPS,
  420. &dwGrantedAccess,
  421. &fAccess);
  422. if (!fRet)
  423. {
  424. DebugTrace ("ScChildISAPIAccessCheck--Error from AccessCheck %d (0x%08x).\n",
  425. GetLastError(), GetLastError());
  426. goto ret;
  427. }
  428. // Now, fAccess tells whether the impersonated token has
  429. // the requested access. Return this to the caller.
  430. //
  431. ret:
  432. if (pSD)
  433. LocalFree (pSD);
  434. return fAccess ? S_OK : E_ACCESSDENIED;
  435. }