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.

1254 lines
41 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation
  3. Module Name:
  4. sxsisol.c
  5. Abstract:
  6. Current directory support
  7. Author:
  8. sxsdev(mgrier, jaykrell, xiaoyuw, jonwis) 04-Jun-2002
  9. Revision History:
  10. --*/
  11. #include "pch.cxx"
  12. #include "nt.h"
  13. #include "ntos.h"
  14. #include "ntrtl.h"
  15. #include "nturtl.h"
  16. #include "string.h"
  17. #include "ctype.h"
  18. #include "sxstypes.h"
  19. #include "ntdllp.h"
  20. //
  21. // This seems bizarre that these constants are here but I don't see them elsewhere.
  22. #define SXSISOL_DOT L"."
  23. #define SXSISOL_CONSTANT_STRING_U(s) { sizeof(s) - sizeof((s)[0]), sizeof(s), (PWSTR)(s) }
  24. const static UNICODE_STRING sxsisol_ExtensionDelimiters = SXSISOL_CONSTANT_STRING_U(SXSISOL_DOT);
  25. #define IFNOT_NTSUCCESS_EXIT(x) \
  26. do { \
  27. Status = (x); \
  28. if (!NT_SUCCESS(Status)) \
  29. goto Exit; \
  30. } while (0)
  31. #define PARAMETER_CHECK(x) \
  32. do { \
  33. if (!(x)) { \
  34. Status = STATUS_INVALID_PARAMETER; \
  35. goto Exit; \
  36. } \
  37. } while (0)
  38. #define INTERNAL_ERROR_CHECK(x) \
  39. do { \
  40. if (!(x)) { \
  41. RtlAssert("Internal error check failed", __FILE__, __LINE__, #x); \
  42. Status = STATUS_INTERNAL_ERROR; \
  43. goto Exit; \
  44. } \
  45. } while (0)
  46. typedef struct _SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS {
  47. RTL_UNICODE_STRING_BUFFER PublicUnicodeStringBuffer;
  48. PUNICODE_STRING PrivatePreallocatedString;
  49. PUNICODE_STRING PrivateDynamicallyAllocatedString;
  50. PUNICODE_STRING *PrivateUsedString;
  51. BOOLEAN Valid;
  52. } SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS, *PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS;
  53. typedef RTL_STRING_LENGTH_TYPE *PRTL_STRING_LENGTH_TYPE;
  54. static
  55. void
  56. sxsisol_InitUnicodeStringBufferAroundUnicodeStrings(
  57. PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This,
  58. PUNICODE_STRING PreallocatedString,
  59. PUNICODE_STRING DynamicallyAllocatedString,
  60. PUNICODE_STRING *UsedString
  61. );
  62. static
  63. NTSTATUS
  64. sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Success(
  65. PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This
  66. );
  67. static
  68. void
  69. sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Failure(
  70. PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This
  71. );
  72. static
  73. NTSTATUS
  74. sxsisol_PathHasExtension(
  75. IN PCUNICODE_STRING Path,
  76. OUT bool &rfHasExtension
  77. );
  78. static
  79. NTSTATUS
  80. sxsisol_PathAppendDefaultExtension(
  81. IN PCUNICODE_STRING Path,
  82. IN PCUNICODE_STRING DefaultExtension OPTIONAL,
  83. OUT PRTL_UNICODE_STRING_BUFFER PathWithExtension,
  84. OUT bool &rfAppended
  85. );
  86. static
  87. NTSTATUS
  88. sxsisol_CanonicalizeFullPathFileName(
  89. IN OUT PUNICODE_STRING FileName, // could be a fullpath or relative path, and only fullpath are dealt with
  90. IN PUNICODE_STRING FullPathPreallocatedString,
  91. IN OUT PUNICODE_STRING FullPathDynamicString
  92. );
  93. static
  94. NTSTATUS
  95. sxsisol_RespectDotLocal(
  96. IN PCUNICODE_STRING FileName,
  97. OUT PRTL_UNICODE_STRING_BUFFER FullPathResult,
  98. IN OUT ULONG *OutFlags OPTIONAL
  99. );
  100. static
  101. NTSTATUS
  102. sxsisol_SearchActCtxForDllName(
  103. IN PCUNICODE_STRING FileName,
  104. IN BOOLEAN fExistenceTest,
  105. IN OUT SIZE_T &TotalLength,
  106. IN OUT ULONG *OutFlags,
  107. OUT PRTL_UNICODE_STRING_BUFFER FullPathResult
  108. );
  109. static
  110. NTSTATUS
  111. sxsisol_ExpandEnvironmentStrings_UEx(
  112. IN PVOID Environment OPTIONAL,
  113. IN PCUNICODE_STRING Source,
  114. OUT PRTL_UNICODE_STRING_BUFFER Destination
  115. );
  116. NTSTATUS
  117. NTAPI
  118. RtlDosApplyFileIsolationRedirection_Ustr(
  119. ULONG Flags,
  120. PCUNICODE_STRING FileNameIn,
  121. PCUNICODE_STRING DefaultExtension,
  122. PUNICODE_STRING PreAllocatedString,
  123. PUNICODE_STRING DynamicallyAllocatedString,
  124. PUNICODE_STRING *OutFullPath,
  125. ULONG *OutFlags,
  126. SIZE_T *FilePartPrefixCch,
  127. SIZE_T *BytesRequired
  128. )
  129. // ++
  130. //
  131. // Routine Description:
  132. //
  133. // This function implements the basic capability of taking a path
  134. // and applying any appropriate isolation/redirection to it.
  135. //
  136. // Notably it today includes redirection for Fusion manifest-based
  137. // isolation and .local isolation.
  138. //
  139. // Arguments:
  140. //
  141. // Flags - Flags affecting the behavior of this function.
  142. //
  143. // RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL
  144. // Controls whether this function applies ".local" semantics
  145. // to the redirection.
  146. //
  147. // FileNameIn - Path that is being checked for isolation/redirection.
  148. // This may be "any" sort of Win32/DOS path - absolute, relative,
  149. // leaf name only.
  150. //
  151. // .local redirection applies equally to all kinds of paths.
  152. //
  153. // Fusion manifest-based redirection normally applies to relative
  154. // paths, except when a component is listed in the "system default
  155. // manifest", in which case attempts to load the DLL by full path
  156. // (e.g. "c:\windows\system32\comctl32.dll") need to be redirected
  157. // into the side-by-side store.
  158. //
  159. // DefaultExtension
  160. //
  161. // Default extension to apply to FileNameIn if it does not have
  162. // an extension.
  163. //
  164. // PreAllocatedString (optional)
  165. //
  166. // UNICODE_STRING that this function can use to return results
  167. // where the caller is responsible for managing the storage
  168. // associated with the allocation. The function may use this
  169. // string and will not attempt to dynamically resize it;
  170. // if the space required exceeds the maximum length of
  171. // the preallocated string, either dynamic storage is allocated
  172. // or the function will fail with the buffer-too-small
  173. // NTSTATUS code.
  174. //
  175. // If NULL is passed for this parameter, the space for the
  176. // output and temporary strings will be dynamically allocated.
  177. //
  178. // DynamicallyAllocatedString (optional)
  179. //
  180. // UNICODE_STRING which if present should refer to an "empty"
  181. // UNICODE_STRING (sizes an buffer pointer 0 and NULL
  182. // respectively). If PreAllocatedString was not passed or
  183. // is not big enough for the results, the information about
  184. // the dynamically allocated string is returned in this
  185. // parameter. If no dynamic allocation was performed, this
  186. // string is left null.
  187. //
  188. // OutFullPath (optional)
  189. //
  190. // PUNICODE_STRING pointer that is filled in with the
  191. // UNICODE_STRING pointer passed in as PreAllocatedString or
  192. // DynamicallyAllocatedString as appropriate. If only one
  193. // of the two (PreAllocatedString, DynamicallyAllocatedString)
  194. // is passed in, this parameter may be omitted.
  195. //
  196. // FilePartPrefixCch
  197. //
  198. // Returned number of characters in the resultant path up to
  199. // and including the last path separator.
  200. //
  201. // BytesRequired
  202. //
  203. // Returned byte count of the size of the string needed for
  204. // storing the resultant full path.
  205. //
  206. // The expected calling sequence for RtlDosApplyFileIsolationRedirection_Ustr() is something
  207. // like this:
  208. //
  209. // {
  210. // WCHAR Buffer[MAX_PATH];
  211. // UNICODE_STRING PreAllocatedString;
  212. // UNICODE_STRING DynamicallyAllocatedString = { 0, 0, NULL };
  213. // PUNICODE_STRING FullPath;
  214. //
  215. // PreAllocatedString.Length = 0;
  216. // PreAllocatedString.MaximumLength = sizeof(Buffer);
  217. // PreAllocatedString.Buffer = Buffer;
  218. //
  219. // Status = RtlDosApplyFileIsolationRedirection_Ustr(
  220. // Flags,
  221. // FileToCheck,
  222. // DefaultExtensionToApply, // for example ".DLL"
  223. // &PreAllocatedString,
  224. // &DynamicallyAllocatedString,
  225. // &FullPath);
  226. // if (!NT_SUCCESS(Status)) return Status;
  227. // // now code uses FullPath as the complete path name...
  228. //
  229. // // In exit paths, always free the dynamic string:
  230. // RtlFreeUnicodeString(&DynamicallyAllocatedString);
  231. // }
  232. //
  233. // Return Value:
  234. //
  235. // NTSTATUS indicating the success/failure of the function.
  236. //
  237. // --
  238. {
  239. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  240. //
  241. // perf and frame size problems here..
  242. //
  243. WCHAR FullPathPreallocatedBuffer[64]; // seldom used
  244. UNICODE_STRING FullPathDynamicString = { 0, 0, NULL };
  245. UNICODE_STRING FullPathPreallocatedString = { 0, sizeof(FullPathPreallocatedBuffer), FullPathPreallocatedBuffer };
  246. //
  247. // This is where we append .dll (or any other extension).
  248. // This is considered an unusual case -- when people say LoadLibrary(kernel32) instead of LoadLibrary(kernel32.dll).
  249. //
  250. UCHAR FileNameWithExtensionStaticBuffer[16 * sizeof(WCHAR)];
  251. RTL_UNICODE_STRING_BUFFER FileNameWithExtensionBuffer;
  252. UNICODE_STRING FileName;
  253. SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS StringWrapper;
  254. const PRTL_UNICODE_STRING_BUFFER FullPathResult = &StringWrapper.PublicUnicodeStringBuffer;
  255. SIZE_T TotalLength = 0;
  256. USHORT FilePartPrefixCb = 0;
  257. ULONG OutFlagsTemp = 0; // local copy; we'll copy out on success
  258. // Initialize out parameters first
  259. if (OutFlags != NULL)
  260. *OutFlags = 0;
  261. if (FilePartPrefixCch != NULL)
  262. *FilePartPrefixCch = 0;
  263. if (BytesRequired != NULL)
  264. *BytesRequired = (DOS_MAX_PATH_LENGTH * sizeof(WCHAR)); // sleazy, but I doubt it was usually correct
  265. if (DynamicallyAllocatedString != NULL) {
  266. RtlInitEmptyUnicodeString(DynamicallyAllocatedString, NULL, 0);
  267. }
  268. //
  269. // step1 : initialization
  270. //
  271. RtlInitUnicodeStringBuffer(
  272. &FileNameWithExtensionBuffer,
  273. FileNameWithExtensionStaticBuffer,
  274. sizeof(FileNameWithExtensionStaticBuffer));
  275. sxsisol_InitUnicodeStringBufferAroundUnicodeStrings(
  276. &StringWrapper,
  277. PreAllocatedString,
  278. DynamicallyAllocatedString,
  279. OutFullPath);
  280. // Valid input conditions:
  281. // 1. You have to have a filename
  282. // 2. If you specify both the preallocated buffer and the dynamically
  283. // allocated buffer, you need the OutFullPath parameter to detect
  284. // which was actually populated.
  285. // 3. If you ask for the file part prefix cch, you need an output
  286. // buffer; otherwise you can't know what the cch is relative to.
  287. PARAMETER_CHECK((Flags & ~(RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL)) == 0);
  288. PARAMETER_CHECK(FileNameIn != NULL);
  289. PARAMETER_CHECK(
  290. !(((PreAllocatedString == NULL) && (DynamicallyAllocatedString == NULL) && (FilePartPrefixCch != NULL)) ||
  291. ((PreAllocatedString != NULL) && (DynamicallyAllocatedString != NULL) && (OutFullPath == NULL))));
  292. FileName = *FileNameIn;
  293. //
  294. // step2: append extension (".dll" usually) if needed
  295. //
  296. bool fAppended;
  297. IFNOT_NTSUCCESS_EXIT(
  298. sxsisol_PathAppendDefaultExtension(
  299. &FileName,
  300. DefaultExtension,
  301. &FileNameWithExtensionBuffer,
  302. fAppended));
  303. if (fAppended)
  304. FileName = FileNameWithExtensionBuffer.String;
  305. //
  306. // step3: For fullpaths, canonicalize .. and . and such.
  307. // Comments:
  308. // We do this so fullpaths like c:\windows\.\system32\comctl32.dll can be correctly
  309. // redirected to "system default".
  310. //
  311. // It'd be nice to use the buffers our caller gave us here, but it is a bit tricky.
  312. //
  313. IFNOT_NTSUCCESS_EXIT(
  314. sxsisol_CanonicalizeFullPathFileName(
  315. &FileName, // FileName could be reset inside this func
  316. &FullPathPreallocatedString,
  317. &FullPathDynamicString));
  318. //
  319. // Step4: Deal with .local if existed
  320. //
  321. if ((Flags & RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL) &&
  322. (NtCurrentPeb()->ProcessParameters != NULL) &&
  323. (NtCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROC_DLL_REDIRECTION_LOCAL)) {
  324. IFNOT_NTSUCCESS_EXIT(
  325. sxsisol_RespectDotLocal(
  326. &FileName,
  327. FullPathResult,
  328. &OutFlagsTemp));
  329. }
  330. // If it was not redirected by .local, try activation contexts/manifests
  331. if (!(OutFlagsTemp & RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_DOT_LOCAL_REDIRECT)) {
  332. IFNOT_NTSUCCESS_EXIT(
  333. sxsisol_SearchActCtxForDllName(
  334. &FileName,
  335. ((PreAllocatedString == NULL) && (DynamicallyAllocatedString == NULL))? TRUE : FALSE,
  336. TotalLength,
  337. OutFlags,
  338. FullPathResult));
  339. }
  340. // we got the path but the input buffer is not big-enough
  341. if ((DynamicallyAllocatedString == NULL) && (PreAllocatedString != NULL) && (FullPathResult->String.Buffer != PreAllocatedString->Buffer))
  342. {
  343. Status = STATUS_BUFFER_TOO_SMALL; // no dynamic buffer, only a small static buffer
  344. goto Exit;
  345. }
  346. if (FilePartPrefixCch != NULL) {
  347. //
  348. // We should have a full path at this point. Compute the length of the
  349. // string up through the last path separator.
  350. //
  351. IFNOT_NTSUCCESS_EXIT(
  352. RtlFindCharInUnicodeString(
  353. RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
  354. &FullPathResult->String,
  355. &RtlDosPathSeperatorsString,
  356. &FilePartPrefixCb));
  357. // The prefix length from RtlFindCharInUnicodeString does not include
  358. // the pattern character matched. Convert from byte count to character
  359. // count and include space for the separator.
  360. *FilePartPrefixCch = (FilePartPrefixCb / sizeof(WCHAR)) + 1;
  361. }
  362. IFNOT_NTSUCCESS_EXIT(sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Success(&StringWrapper));
  363. if (OutFlags != NULL)
  364. *OutFlags = OutFlagsTemp;
  365. Status = STATUS_SUCCESS;
  366. Exit:
  367. if (!NT_SUCCESS(Status))
  368. sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Failure(&StringWrapper);
  369. RtlFreeUnicodeString(&FullPathDynamicString);
  370. RtlFreeUnicodeStringBuffer(&FileNameWithExtensionBuffer);
  371. // Map section not found back to key not found so that callers don't
  372. // have to worry about sections being present vs. the lookup key
  373. // being present.
  374. ASSERT(Status != STATUS_SXS_SECTION_NOT_FOUND);
  375. INTERNAL_ERROR_CHECK(Status != STATUS_SXS_SECTION_NOT_FOUND);
  376. return Status;
  377. }
  378. void
  379. sxsisol_InitUnicodeStringBufferAroundUnicodeStrings(
  380. PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This,
  381. PUNICODE_STRING PreallocatedString,
  382. PUNICODE_STRING DynamicallyAllocatedString,
  383. PUNICODE_STRING *UsedString
  384. )
  385. //++
  386. //
  387. // Routine Description:
  388. //
  389. // Initialize a wrapper around the UNICODE_STRING pair
  390. // (preallocated vs. dynamically allocated) and the
  391. // actual PUNICODE_STRING which holds the value.
  392. //
  393. // Arguments:
  394. //
  395. // This
  396. // Pointer to the wrapper struct to initialize.
  397. //
  398. // PreallocatedString (optional)
  399. // Pointer to a UNICODE_STRING that holds the preallocated
  400. // buffer.
  401. //
  402. // DynamicallyAllocatedString (optional)
  403. // Pointer to a UNICODE_STRING which will store information
  404. // about any dynamic allocations done to support the
  405. // RTL_UNICODE_STRING_BUFFER manipulations.
  406. //
  407. // UsedString
  408. // Pointer to the PUNICODE_STRING which will be updated
  409. // to indicate which of PreallocatedString or
  410. // DynamicallyAllocatedString were actually used.
  411. //
  412. // Return Value:
  413. //
  414. // NTSTATUS indicating the overall success or failure of
  415. // the function.
  416. //
  417. //--
  418. {
  419. ASSERT(This != NULL);
  420. if (PreallocatedString != NULL) {
  421. RtlInitUnicodeStringBuffer(
  422. &This->PublicUnicodeStringBuffer,
  423. (PUCHAR)PreallocatedString->Buffer,
  424. PreallocatedString->MaximumLength);
  425. } else
  426. RtlInitUnicodeStringBuffer(&This->PublicUnicodeStringBuffer, NULL, 0);
  427. This->PrivatePreallocatedString = PreallocatedString;
  428. This->PrivateDynamicallyAllocatedString = DynamicallyAllocatedString;
  429. This->PrivateUsedString = UsedString;
  430. This->Valid = TRUE;
  431. }
  432. NTSTATUS
  433. sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Success(
  434. PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This
  435. )
  436. //++
  437. //
  438. // Routine Description:
  439. //
  440. // Cleans up use of a SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS
  441. // structure, performing appropriate cleanup for success vs. failure
  442. // cases.
  443. //
  444. // Arguments:
  445. //
  446. // This
  447. // Pointer to the wrapper struct to clean up
  448. //
  449. // Return Value:
  450. //
  451. // NTSTATUS indicating the overall success or failure of
  452. // the function.
  453. //
  454. //--
  455. {
  456. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  457. INTERNAL_ERROR_CHECK(This != NULL);
  458. if (This->Valid) {
  459. RTL_UNICODE_STRING_BUFFER &rUSB = This->PublicUnicodeStringBuffer;
  460. UNICODE_STRING &rUS = rUSB.String;
  461. INTERNAL_ERROR_CHECK(
  462. (This->PrivateDynamicallyAllocatedString == NULL) ||
  463. (This->PrivateDynamicallyAllocatedString->Buffer == NULL));
  464. if ((This->PrivatePreallocatedString != NULL) &&
  465. (This->PrivatePreallocatedString->Buffer == rUS.Buffer)) {
  466. INTERNAL_ERROR_CHECK(rUS.Length <= This->PrivatePreallocatedString->MaximumLength);
  467. This->PrivatePreallocatedString->Length = rUS.Length;
  468. INTERNAL_ERROR_CHECK(This->PrivateUsedString != NULL);
  469. *This->PrivateUsedString = This->PrivatePreallocatedString;
  470. } else if (This->PrivateDynamicallyAllocatedString != NULL) {
  471. *This->PrivateDynamicallyAllocatedString = rUS;
  472. INTERNAL_ERROR_CHECK(This->PrivateUsedString != NULL);
  473. *This->PrivateUsedString = This->PrivateDynamicallyAllocatedString;
  474. } else {
  475. RtlFreeUnicodeStringBuffer(&rUSB);
  476. //Status = STATUS_NAME_TOO_LONG;
  477. //goto Exit;
  478. }
  479. }
  480. Status = STATUS_SUCCESS;
  481. Exit:
  482. if (This != NULL) {
  483. RtlZeroMemory(This, sizeof(*This));
  484. ASSERT(!This->Valid);
  485. }
  486. return Status;
  487. }
  488. void
  489. sxsisol_FreeUnicodeStringBufferAroundUnicodeStrings_Failure(
  490. PSXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS This
  491. )
  492. //++
  493. //
  494. // Routine Description:
  495. //
  496. // Cleans up use of a SXSISOL_UNICODE_STRING_BUFFER_AROUND_UNICODE_STRINGS
  497. // structure, performing appropriate cleanup for failure cases.
  498. //
  499. // Arguments:
  500. //
  501. // This
  502. // Pointer to the wrapper struct to initialize.
  503. //
  504. //
  505. // Return Value:
  506. //
  507. // None
  508. //
  509. //--
  510. {
  511. ASSERT(This != NULL);
  512. if (This->Valid) {
  513. if (This->PrivateDynamicallyAllocatedString != NULL) {
  514. ASSERT(This->PrivateDynamicallyAllocatedString->Buffer == NULL);
  515. }
  516. RtlFreeUnicodeStringBuffer(&This->PublicUnicodeStringBuffer);
  517. }
  518. RtlZeroMemory(This, sizeof(*This));
  519. ASSERT(!This->Valid);
  520. }
  521. NTSTATUS
  522. sxsisol_PathHasExtension(
  523. IN PCUNICODE_STRING Path,
  524. OUT bool &rfHasExtension
  525. )
  526. //++
  527. //
  528. // Routine Description:
  529. //
  530. // Locate the extension of a file in a path.
  531. //
  532. // Arguments:
  533. //
  534. // Path
  535. // Path in which to find extension.
  536. //
  537. // rfHasExtension
  538. // On a successful outcome of the call, indicates that
  539. // the path passed in Path does have an extension.
  540. //
  541. // Return Value:
  542. //
  543. // NTSTATUS indicating the overall success/failure of the
  544. // call.
  545. //
  546. //--
  547. {
  548. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  549. RTL_STRING_LENGTH_TYPE LocalPrefixLength;
  550. rfHasExtension = false;
  551. Status = RtlFindCharInUnicodeString(
  552. RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
  553. Path,
  554. &sxsisol_ExtensionDelimiters,
  555. &LocalPrefixLength);
  556. if (!NT_SUCCESS(Status)) {
  557. if (Status != STATUS_NOT_FOUND)
  558. goto Exit;
  559. } else {
  560. rfHasExtension = true;
  561. }
  562. Status = STATUS_SUCCESS;
  563. Exit:
  564. return Status;
  565. }
  566. NTSTATUS
  567. sxsisol_PathAppendDefaultExtension(
  568. IN PCUNICODE_STRING Path,
  569. IN PCUNICODE_STRING DefaultExtension OPTIONAL,
  570. IN OUT PRTL_UNICODE_STRING_BUFFER PathWithExtension,
  571. OUT bool &rfAppended
  572. )
  573. //++
  574. //
  575. // Routine Description:
  576. //
  577. // If a path does not contain an extension, add one.
  578. //
  579. // Arguments:
  580. //
  581. // Path
  582. // Path in which to find extension.
  583. //
  584. // DefaultExtension
  585. // Extension to append to the path.
  586. //
  587. // PathWithExtension
  588. // Initialized RTL_UNICODE_STRING_BUFFER into which
  589. // the modified path (including extension) is written.
  590. //
  591. // If the path already has an extension, nothing
  592. // is written to PathWithExtension.
  593. //
  594. // rfAppended
  595. // Out bool set to true iff the path was modified with
  596. // an extension.
  597. //
  598. // Return Value:
  599. //
  600. // NTSTATUS indicating the overall success/failure of the
  601. // call.
  602. //
  603. //--
  604. {
  605. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  606. UNICODE_STRING Strings[2];
  607. ULONG NumberOfStrings = 1;
  608. rfAppended = false;
  609. PARAMETER_CHECK(Path != NULL);
  610. PARAMETER_CHECK(PathWithExtension != NULL);
  611. if ((DefaultExtension != NULL) && !RTL_STRING_IS_EMPTY(DefaultExtension)) {
  612. bool fHasExtension = false;
  613. IFNOT_NTSUCCESS_EXIT(sxsisol_PathHasExtension(Path, fHasExtension));
  614. if (!fHasExtension) {
  615. Strings[0] = *Path;
  616. Strings[1] = *DefaultExtension;
  617. NumberOfStrings = 2;
  618. PathWithExtension->String.Length = 0;
  619. IFNOT_NTSUCCESS_EXIT(RtlMultiAppendUnicodeStringBuffer(PathWithExtension, NumberOfStrings, Strings));
  620. rfAppended = true;
  621. }
  622. }
  623. Status = STATUS_SUCCESS;
  624. Exit:
  625. return Status;
  626. }
  627. NTSTATUS
  628. sxsisol_CanonicalizeFullPathFileName(
  629. IN OUT PUNICODE_STRING FileName, // could be a fullpath or relative path, and only fullpath are dealt with
  630. IN PUNICODE_STRING FullPathPreallocatedString,
  631. IN OUT PUNICODE_STRING FullPathDynamicString
  632. )
  633. //++
  634. //
  635. // Routine Description:
  636. //
  637. // Canonicalize a path name for comparison against globally
  638. // redirected absolute file paths (e.g. the system default
  639. // activation context).
  640. //
  641. // Given an absolute path, like c:\foo\bar.dll or c:\foo\..\bar.dll,
  642. // return a canonicalized fullpaty like c:\foo\bar.dll or c:\bar.dll
  643. //
  644. // Given a relative path like bar.dll or ..\bar.dll, leave it unchanged.
  645. //
  646. // \\?\ and \\.\ followed by drive letter colon slash are changed,
  647. // but followed by anything else is not.
  648. // \\?\c:\foo => c:\foo
  649. // \\.\c:\foo => c:\foo
  650. // \\?\pipe => \\?\pipe
  651. // \\.\pipe => \\.\pipe
  652. // \\?\unc\machine\share\foo => \\?\unc\machine\share\foo
  653. //
  654. // Arguments:
  655. //
  656. // FileName
  657. // Name of the file to be canonicalized.
  658. // If the file name is modified, this UNICODE_STRING
  659. // is overwritten with information about where the
  660. // canonicalized path is written; this will be one of
  661. // the two UNICODE_STRING parameters:
  662. // FullPathPreallocatedString or FullPathDynamicString.
  663. //
  664. // Note that is may not be exactly either one of their
  665. // values but may instead reference a substring contained
  666. // in them.
  667. //
  668. // FullPathPreallocatedString
  669. // Optional preallocated buffer used to hold the canonicalized
  670. // full path of FileName.
  671. //
  672. // FullPathDynamicString
  673. // Optional UNICODE_STRING which is used if the file name
  674. // passed in FileName must be canonicalized and the
  675. // canonicalization does not fit in FullPathPreallocatedString.
  676. //
  677. // Return Value:
  678. //
  679. // NTSTATUS indicating the overall success/failure of the
  680. // call.
  681. //
  682. //--
  683. {
  684. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  685. RTL_PATH_TYPE PathType;
  686. PUNICODE_STRING FullPathUsed = NULL;
  687. bool fDynamicAllocatedBufferUsed = false;
  688. PARAMETER_CHECK(FileName != NULL);
  689. // The dynamic string is optional, but if it is present, the buffer must be NULL.
  690. PARAMETER_CHECK((FullPathDynamicString == NULL) || (FullPathDynamicString->Buffer == NULL));
  691. IFNOT_NTSUCCESS_EXIT(
  692. RtlGetFullPathName_UstrEx(
  693. FileName,
  694. FullPathPreallocatedString,
  695. FullPathDynamicString,
  696. &FullPathUsed,
  697. NULL,
  698. NULL,
  699. &PathType,
  700. NULL));
  701. if ((PathType == RtlPathTypeLocalDevice) || (PathType == RtlPathTypeDriveAbsolute) || (PathType == RtlPathTypeUncAbsolute)) {
  702. UNICODE_STRING RealFullPath;
  703. RealFullPath = *FullPathUsed;
  704. if (PathType == RtlPathTypeLocalDevice) {
  705. ASSERT(RTL_STRING_GET_LENGTH_CHARS(FullPathUsed) > 4);
  706. ASSERT((FileName->Buffer[0] == L'\\') && (FileName->Buffer[1] == L'\\') &&
  707. ((FileName->Buffer[2] == L'.') || (FileName->Buffer[2] == L'?' )) && (FileName->Buffer[3] == L'\\'));
  708. //
  709. // only deal with \\?\C:\ or \\.\C:\, ignore other cases such as \\?\UNC\ or \\.\UNC\
  710. //
  711. if ((FileName->Buffer[5] == L':') && ( FileName->Buffer[6] == L'\\')) {
  712. //
  713. // Remove first four characters from string, "\\?\c:\" => "c:\"
  714. //
  715. FileName->Length -= (4 * sizeof(WCHAR));
  716. FileName->Buffer += 4;
  717. FileName->MaximumLength -= (4 * sizeof(WCHAR));
  718. RealFullPath.Length -= (4 * sizeof(WCHAR));
  719. RealFullPath.Buffer += 4;
  720. RealFullPath.MaximumLength -= (4 * sizeof(WCHAR));
  721. }
  722. }
  723. if (FileName->Length > RealFullPath.Length) { // FileName contains redundant path part, so FileName must be reset
  724. if (FullPathUsed == FullPathDynamicString)
  725. fDynamicAllocatedBufferUsed = true;
  726. *FileName = RealFullPath;
  727. }
  728. }
  729. Status = STATUS_SUCCESS;
  730. Exit:
  731. if (!fDynamicAllocatedBufferUsed)
  732. RtlFreeUnicodeString(FullPathDynamicString); // free here or at the end of the caller
  733. return Status;
  734. }
  735. NTSTATUS
  736. sxsisol_RespectDotLocal(
  737. IN PCUNICODE_STRING FileName,
  738. OUT PRTL_UNICODE_STRING_BUFFER FullPathResult,
  739. OUT ULONG *OutFlags OPTIONAL
  740. )
  741. //++
  742. //
  743. // Routine Description:
  744. //
  745. // Determine if the file pass in FileName has .local based
  746. // redirection in the app folder.
  747. //
  748. // Arguments:
  749. //
  750. // FileName
  751. // Leaf name of the file to look for. (e.g. "foo.dll")
  752. //
  753. // FullPathResult
  754. // Returned full path to the file found.
  755. //
  756. // OutFlags
  757. // Option out parameter; the value
  758. // RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_DOT_LOCAL_REDIRECT
  759. // is ORed in if a .local based redirection is found.
  760. //
  761. // Return Value:
  762. //
  763. // NTSTATUS indicating the overall success/failure of the
  764. // call.
  765. //
  766. //--
  767. {
  768. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  769. UNICODE_STRING LocalDllNameInAppDir = { 0 };
  770. UNICODE_STRING LocalDllNameInDotLocalDir = { 0 };
  771. PUNICODE_STRING LocalDllNameFound = NULL;
  772. PARAMETER_CHECK(FullPathResult != NULL);
  773. IFNOT_NTSUCCESS_EXIT(
  774. RtlComputePrivatizedDllName_U(
  775. FileName,
  776. &LocalDllNameInAppDir,
  777. &LocalDllNameInDotLocalDir));
  778. if (RtlDoesFileExists_UStr(&LocalDllNameInDotLocalDir)) {
  779. LocalDllNameFound = &LocalDllNameInDotLocalDir;
  780. } else if (RtlDoesFileExists_UStr(&LocalDllNameInAppDir)) {
  781. LocalDllNameFound = &LocalDllNameInAppDir;
  782. }
  783. if (LocalDllNameFound != NULL) {
  784. IFNOT_NTSUCCESS_EXIT(RtlAssignUnicodeStringBuffer(FullPathResult, LocalDllNameFound));
  785. if (OutFlags != NULL)
  786. *OutFlags |= RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_DOT_LOCAL_REDIRECT;
  787. }
  788. Status = STATUS_SUCCESS;
  789. Exit:
  790. RtlFreeUnicodeString(&LocalDllNameInAppDir);
  791. RtlFreeUnicodeString(&LocalDllNameInDotLocalDir);
  792. return Status;
  793. }
  794. NTSTATUS
  795. sxsisol_SearchActCtxForDllName(
  796. IN PCUNICODE_STRING FileNameIn,
  797. IN BOOLEAN fExistenceTest,
  798. IN OUT SIZE_T &TotalLength,
  799. IN OUT ULONG *OutFlags,
  800. OUT PRTL_UNICODE_STRING_BUFFER FullPathResult
  801. )
  802. //++
  803. //
  804. // Routine Description:
  805. //
  806. // Determine if the active activation context(s) contain a redirection
  807. // for a given file name and if so, compute the full absolute path
  808. // of the redirection.
  809. //
  810. // Arguments:
  811. //
  812. // FileNameIn
  813. // Name of the file to search for in the activation context.
  814. //
  815. // This file name is searched for exactly in the activation
  816. // context dll redirection section (case insensitive).
  817. //
  818. // fExistenceTest
  819. // Set to true to indicate that we're only interested in
  820. // whether the path in FileNameIn is redirected.
  821. //
  822. // TotalLength
  823. // Total length of the path out.
  824. //
  825. // OutFlags
  826. // Flags set to indicate the disposition of the call.
  827. //
  828. // RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_ACTCTX_REDIRECT
  829. // Activation context based redirection was found and applied.
  830. //
  831. // Return Value:
  832. //
  833. // NTSTATUS indicating the overall success/failure of the
  834. // call.
  835. //
  836. //--
  837. {
  838. UNICODE_STRING FileNameTemp;
  839. PUNICODE_STRING FileName;
  840. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  841. ACTIVATION_CONTEXT_SECTION_KEYED_DATA askd = {sizeof(askd)};
  842. const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION *DllRedirData;
  843. const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT *PathSegmentArray;
  844. RTL_UNICODE_STRING_BUFFER EnvironmentExpandedDllPathBuffer; // seldom used
  845. SIZE_T PathSegmentCount = 0;
  846. PACTIVATION_CONTEXT ActivationContext = NULL;
  847. PCUNICODE_STRING AssemblyStorageRoot = NULL;
  848. ULONG i;
  849. FileNameTemp = *FileNameIn;
  850. FileName = &FileNameTemp;
  851. RtlInitUnicodeStringBuffer(&EnvironmentExpandedDllPathBuffer, NULL, 0);
  852. Status = RtlFindActivationContextSectionString(
  853. FIND_ACTIVATION_CONTEXT_SECTION_KEY_RETURN_ACTIVATION_CONTEXT |
  854. FIND_ACTIVATION_CONTEXT_SECTION_KEY_RETURN_FLAGS,
  855. NULL,
  856. ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
  857. FileName,
  858. &askd);
  859. if (!NT_SUCCESS(Status)) {
  860. if (Status == STATUS_SXS_SECTION_NOT_FOUND)
  861. Status = STATUS_SXS_KEY_NOT_FOUND;
  862. goto Exit;
  863. }
  864. if (fExistenceTest == TRUE) {
  865. Status = STATUS_SUCCESS;
  866. // This was just an existence test. return the successful status.
  867. goto Exit;
  868. }
  869. ActivationContext = askd.ActivationContext;
  870. if ((askd.Length < sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION)) ||
  871. (askd.DataFormatVersion != ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_FORMAT_WHISTLER)) {
  872. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  873. goto Exit;
  874. }
  875. DllRedirData = (const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION *) askd.Data;
  876. // validate DllRedirData
  877. // If the path segment list extends beyond the section, we're outta here
  878. if ((((ULONG) DllRedirData->PathSegmentOffset) > askd.SectionTotalLength) ||
  879. (DllRedirData->PathSegmentCount > (MAXULONG / sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT))) ||
  880. (DllRedirData->PathSegmentOffset > (MAXULONG - (DllRedirData->PathSegmentCount * sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT)))) ||
  881. (((ULONG) (DllRedirData->PathSegmentOffset + (DllRedirData->PathSegmentCount * sizeof(ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT)))) > askd.SectionTotalLength)) {
  882. #if DBG
  883. DbgPrintEx(
  884. DPFLTR_SXS_ID,
  885. DPFLTR_ERROR_LEVEL,
  886. "SXS: %s - Path segment array extends beyond section limits\n",
  887. __FUNCTION__);
  888. #endif // DBG
  889. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  890. goto Exit;
  891. }
  892. // If the entry requires path root resolution, do so!
  893. if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_OMITS_ASSEMBLY_ROOT) {
  894. NTSTATUS InnerStatus = STATUS_SUCCESS;
  895. ULONG GetAssemblyStorageRootFlags = 0;
  896. // There's no need to support both a dynamic root and environment variable
  897. // expansion.
  898. if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_EXPAND) {
  899. DbgPrintEx(
  900. DPFLTR_SXS_ID,
  901. DPFLTR_ERROR_LEVEL,
  902. "[%x.%x] SXS: %s - Relative redirection plus env var expansion.\n",
  903. HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess),
  904. HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread),
  905. __FUNCTION__);
  906. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  907. goto Exit;
  908. }
  909. if (askd.Flags & ACTIVATION_CONTEXT_SECTION_KEYED_DATA_FLAG_FOUND_IN_PROCESS_DEFAULT) {
  910. INTERNAL_ERROR_CHECK(!(askd.Flags & ACTIVATION_CONTEXT_SECTION_KEYED_DATA_FLAG_FOUND_IN_SYSTEM_DEFAULT));
  911. GetAssemblyStorageRootFlags |= RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_PROCESS_DEFAULT;
  912. }
  913. if (askd.Flags & ACTIVATION_CONTEXT_SECTION_KEYED_DATA_FLAG_FOUND_IN_SYSTEM_DEFAULT){
  914. GetAssemblyStorageRootFlags |= RTL_GET_ASSEMBLY_STORAGE_ROOT_FLAG_ACTIVATION_CONTEXT_USE_SYSTEM_DEFAULT;
  915. }
  916. Status = RtlGetAssemblyStorageRoot(
  917. GetAssemblyStorageRootFlags,
  918. ActivationContext,
  919. askd.AssemblyRosterIndex,
  920. &AssemblyStorageRoot,
  921. &RtlpAssemblyStorageMapResolutionDefaultCallback,
  922. (PVOID) &InnerStatus);
  923. if (!NT_SUCCESS(Status)) {
  924. if ((Status == STATUS_CANCELLED) && (!NT_SUCCESS(InnerStatus)))
  925. Status = InnerStatus;
  926. goto Exit;
  927. }
  928. }
  929. PathSegmentArray = (PCACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT) (((ULONG_PTR) askd.SectionBase) + DllRedirData->PathSegmentOffset);
  930. TotalLength = 0;
  931. PathSegmentCount = DllRedirData->PathSegmentCount;
  932. for (i=0; i != PathSegmentCount; i++) {
  933. const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT *PathSegment = &PathSegmentArray[i];
  934. // If the character array is outside the bounds of the section, something's hosed.
  935. if ((((ULONG) PathSegmentArray[i].Offset) > askd.SectionTotalLength) ||
  936. (PathSegmentArray[i].Offset > (MAXULONG - PathSegmentArray[i].Length)) ||
  937. (((ULONG) (PathSegmentArray[i].Offset + PathSegmentArray[i].Length)) > askd.SectionTotalLength)) {
  938. #if DBG
  939. DbgPrintEx(
  940. DPFLTR_SXS_ID,
  941. DPFLTR_ERROR_LEVEL,
  942. "SXS: %s - path segment %lu at %p (offset: %ld, length: %lu, bounds: %lu) buffer is outside section bounds\n",
  943. __FUNCTION__,
  944. i,
  945. PathSegment->Offset,
  946. PathSegment->Offset,
  947. PathSegment->Length,
  948. askd.SectionTotalLength);
  949. #endif // DBG
  950. Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
  951. goto Exit;
  952. }
  953. TotalLength += (RTL_STRING_LENGTH_TYPE)PathSegment->Length;
  954. if (TotalLength >= MAXUSHORT) { // everytime TotalLength is changed, needs to check whether it is out of range.
  955. Status = STATUS_NAME_TOO_LONG;
  956. }
  957. }
  958. if (AssemblyStorageRoot != NULL) {
  959. TotalLength += AssemblyStorageRoot->Length;
  960. if (TotalLength >= MAXUSHORT)
  961. Status = STATUS_NAME_TOO_LONG;
  962. }
  963. //
  964. // TotalLength is an approximation, the better the approx, the better perf, but
  965. // it does not need to be exact.
  966. // Jaykrell May 2002
  967. //
  968. IFNOT_NTSUCCESS_EXIT(RtlEnsureUnicodeStringBufferSizeBytes(FullPathResult, (RTL_STRING_LENGTH_TYPE)TotalLength));
  969. if (AssemblyStorageRoot != NULL)
  970. IFNOT_NTSUCCESS_EXIT(RtlAssignUnicodeStringBuffer(FullPathResult, AssemblyStorageRoot));
  971. for (i=0; i != PathSegmentCount; i++) {
  972. UNICODE_STRING PathSegmentString;
  973. const ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SEGMENT *PathSegment = &PathSegmentArray[i];
  974. PathSegmentString.Length = (RTL_STRING_LENGTH_TYPE)PathSegment->Length;
  975. PathSegmentString.Buffer = (PWSTR)(((ULONG_PTR) askd.SectionBase) + PathSegment->Offset);
  976. IFNOT_NTSUCCESS_EXIT(RtlAppendUnicodeStringBuffer(FullPathResult, &PathSegmentString));
  977. }
  978. if (!(DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_INCLUDES_BASE_NAME)) {
  979. if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_SYSTEM_DEFAULT_REDIRECTED_SYSTEM32_DLL) {
  980. // only in this case, FileName needs to be reseted
  981. RTL_STRING_LENGTH_TYPE PrefixLength;
  982. //
  983. // get the leaf filename
  984. //
  985. Status = RtlFindCharInUnicodeString(
  986. RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
  987. FileName,
  988. &RtlDosPathSeperatorsString,
  989. &PrefixLength);
  990. if (!NT_SUCCESS(Status)) {
  991. INTERNAL_ERROR_CHECK(Status != STATUS_NOT_FOUND);
  992. goto Exit;
  993. }
  994. FileName->Length = FileName->Length - PrefixLength - sizeof(WCHAR);
  995. FileName->Buffer = FileName->Buffer + (PrefixLength / sizeof(WCHAR)) + 1;
  996. }
  997. TotalLength += FileName->Length;
  998. if (TotalLength >= MAXUSHORT) {
  999. Status = STATUS_NAME_TOO_LONG;
  1000. goto Exit;
  1001. }
  1002. IFNOT_NTSUCCESS_EXIT(RtlAppendUnicodeStringBuffer(FullPathResult, FileName));
  1003. }
  1004. if (DllRedirData->Flags & ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_EXPAND) {
  1005. //
  1006. // Apply any environment strings as necessary (rare case),
  1007. // in this case, ACTIVATION_CONTEXT_DATA_DLL_REDIRECTION_PATH_OMITS_ASSEMBLY_ROOT must not been set
  1008. //
  1009. IFNOT_NTSUCCESS_EXIT(sxsisol_ExpandEnvironmentStrings_UEx(NULL, &(FullPathResult->String), &EnvironmentExpandedDllPathBuffer));
  1010. //
  1011. // TotalLength does not account for this. Jaykrell May 2002
  1012. //
  1013. IFNOT_NTSUCCESS_EXIT(RtlAssignUnicodeStringBuffer(FullPathResult, &EnvironmentExpandedDllPathBuffer.String));
  1014. }
  1015. if (OutFlags != NULL)
  1016. *OutFlags |= RTL_DOS_APPLY_FILE_REDIRECTION_USTR_OUTFLAG_ACTCTX_REDIRECT;
  1017. Status = STATUS_SUCCESS;
  1018. Exit:
  1019. ASSERT(Status != STATUS_INTERNAL_ERROR);
  1020. RtlFreeUnicodeStringBuffer(&EnvironmentExpandedDllPathBuffer);
  1021. if (ActivationContext != NULL)
  1022. RtlReleaseActivationContext(ActivationContext);
  1023. return Status;
  1024. }
  1025. NTSTATUS
  1026. sxsisol_ExpandEnvironmentStrings_UEx(
  1027. IN PVOID Environment OPTIONAL,
  1028. IN PCUNICODE_STRING Source,
  1029. OUT PRTL_UNICODE_STRING_BUFFER Destination
  1030. )
  1031. //++
  1032. //
  1033. // Routine Description:
  1034. //
  1035. // Wrapper around RtlExpandEnvironmentStrings_U which handles
  1036. // dynamic allocation/resizing of the output buffer.
  1037. //
  1038. // Arguments:
  1039. //
  1040. // Environment
  1041. // Optional environment block to query.
  1042. //
  1043. // Source
  1044. // Source string with replacement strings to use.
  1045. //
  1046. // Destination
  1047. // RTL_UNICODE_STRING_BUFFER into which the translated
  1048. // string is written.
  1049. //
  1050. // Return Value:
  1051. //
  1052. // NTSTATUS indicating the overall success/failure of the call.
  1053. //
  1054. //--
  1055. {
  1056. ULONG RequiredLengthBytes;
  1057. NTSTATUS Status = STATUS_INTERNAL_ERROR;
  1058. UNICODE_STRING EmptyBuffer;
  1059. PARAMETER_CHECK(Source != NULL);
  1060. PARAMETER_CHECK(Destination != NULL);
  1061. PARAMETER_CHECK(Source != &Destination->String);
  1062. if (Source->Length == 0) {
  1063. Status = RtlAssignUnicodeStringBuffer(Destination, Source);
  1064. goto Exit;
  1065. }
  1066. RtlInitEmptyUnicodeString(&EmptyBuffer, NULL, 0);
  1067. RtlAcquirePebLock();
  1068. __try {
  1069. Status = RtlExpandEnvironmentStrings_U(Environment, Source, &EmptyBuffer, &RequiredLengthBytes);
  1070. if ((!NT_SUCCESS(Status)) &&
  1071. (Status != STATUS_BUFFER_TOO_SMALL)) {
  1072. __leave;
  1073. }
  1074. if (RequiredLengthBytes > UNICODE_STRING_MAX_BYTES) {
  1075. Status = STATUS_NAME_TOO_LONG;
  1076. __leave;
  1077. }
  1078. ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
  1079. Status = RtlEnsureUnicodeStringBufferSizeBytes(Destination, RequiredLengthBytes + sizeof(UNICODE_NULL));
  1080. if (!NT_SUCCESS(Status)) {
  1081. __leave;
  1082. }
  1083. Status = RtlExpandEnvironmentStrings_U(NULL, Source, &Destination->String, NULL);
  1084. ASSERT(NT_SUCCESS(Status)); // the environment variable changed with the peb lock held?
  1085. if (!NT_SUCCESS(Status)) {
  1086. __leave;
  1087. }
  1088. Status = STATUS_SUCCESS;
  1089. } __finally {
  1090. RtlReleasePebLock();
  1091. }
  1092. Exit:
  1093. ASSERT(Status != STATUS_INTERNAL_ERROR);
  1094. return Status;
  1095. }