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.

626 lines
18 KiB

  1. /******************************************************************************
  2. Copyright (c) 2002 Microsoft Corporation
  3. Module Name:
  4. safepath.cpp
  5. Abstract:
  6. Implements safe path function
  7. ******************************************************************************/
  8. #include "stdafx.h"
  9. #include <shlwapi.h>
  10. // We use a little C++ precompiler trick to be able to code both ANSI & Unicode
  11. // versions of the below functions in the same file with only one copy of the
  12. // source code. This is what all the 'X' suffixes below are doing.
  13. // During the first pass through the source file, we build ANSI source code.
  14. // When we reach the bottom, we define a symbol & #include this source file,
  15. // causing it to be compiled again. However, in this second pass, the symbol
  16. // we defined causes it to be compiled as Unicode.
  17. #undef XCHAR
  18. #undef _X
  19. #undef LPXSTR
  20. #undef LPCXSTR
  21. #undef StringCchCatExX
  22. #undef StringCchCopyExX
  23. #undef StringCchCopyNExX
  24. #undef PathCchAppendX
  25. #undef PathCchCombineX
  26. #undef PathCchAddBackslashX
  27. #undef PathCchAddExtensionX
  28. #undef PathCchRenameExtensionX
  29. #undef PathCchCanonicalizeX
  30. #undef lstrlenX
  31. #undef PathIsRelativeX
  32. #undef PathIsRootX
  33. #undef PathIsUNCX
  34. #undef PathStripToRootX
  35. #undef PathFindExtensionX
  36. #undef StrChrX
  37. #undef StrRChrX
  38. #undef c_szDotExeX
  39. #undef WUGetPCEndX
  40. #undef WUGetPCStartX
  41. #undef WUNearRootFixupsX
  42. #if defined(SAFEPATH_UNICODEPASS)
  43. static const WCHAR c_szDotExeW[] = L".exe";
  44. // define Unicode versions
  45. #define XCHAR WCHAR
  46. #define _X(ch) L ## ch
  47. #define LPXSTR LPWSTR
  48. #define LPCXSTR LPCWSTR
  49. #define StringCchCatExX StringCchCatExW
  50. #define StringCchCopyExX StringCchCopyExW
  51. #define StringCchCopyNExX StringCchCopyNExW
  52. #define PathCchAppendX PathCchAppendW
  53. #define PathCchCombineX PathCchCombineW
  54. #define PathCchAddBackslashX PathCchAddBackslashW
  55. #define PathCchAddExtensionX PathCchAddExtensionW
  56. #define PathCchRenameExtensionX PathCchRenameExtensionW
  57. #define PathCchCanonicalizeX PathCchCanonicalizeW
  58. #define PathIsRelativeX PathIsRelativeW
  59. #define PathIsRootX PathIsRootW
  60. #define PathIsUNCX PathIsUNCW
  61. #define PathStripToRootX PathStripToRootW
  62. #define PathFindExtensionX PathFindExtensionW
  63. #define StrChrX StrChrW
  64. #define StrRChrX StrRChrW
  65. #define lstrlenX lstrlenW
  66. #define c_szDotExeX c_szDotExeW
  67. #define WUGetPCEndX WUGetPCEndW
  68. #define WUGetPCStartX WUGetPCStartW
  69. #define WUNearRootFixupsX WUNearRootFixupsW
  70. #else
  71. static const CHAR c_szDotExeA[] = ".exe";
  72. // define ANSI versions
  73. #define XCHAR char
  74. #define _X(ch) ch
  75. #define LPXSTR LPSTR
  76. #define LPCXSTR LPCSTR
  77. #define StringCchCatExX StringCchCatExA
  78. #define StringCchCopyExX StringCchCopyExA
  79. #define StringCchCopyNExX StringCchCopyNExA
  80. #define PathCchAppendX PathCchAppendA
  81. #define PathCchCombineX PathCchCombineA
  82. #define PathCchAddBackslashX PathCchAddBackslashA
  83. #define PathCchAddExtensionX PathCchAddExtensionA
  84. #define PathCchRenameExtensionX PathCchRenameExtensionA
  85. #define PathCchCanonicalizeX PathCchCanonicalizeA
  86. #define PathIsRelativeX PathIsRelativeA
  87. #define PathIsRootX PathIsRootA
  88. #define PathIsUNCX PathIsUNCA
  89. #define PathStripToRootX PathStripToRootA
  90. #define PathFindExtensionX PathFindExtensionA
  91. #define StrChrX StrChrA
  92. #define StrRChrX StrRChrA
  93. #define lstrlenX lstrlenA
  94. #define c_szDotExeX c_szDotExeA
  95. #define WUGetPCEndX WUGetPCEndA
  96. #define WUGetPCStartX WUGetPCStartA
  97. #define WUNearRootFixupsX WUNearRootFixupsA
  98. #endif
  99. #define SAFEPATH_STRING_FLAGS (MISTSAFE_STRING_FLAGS | STRSAFE_NO_TRUNCATION)
  100. #define CH_WHACK _X('\\')
  101. //////////////////////////////////////////////////////////////////////////////
  102. // Utility functions
  103. // **************************************************************************
  104. // Return a pointer to the end of the next path componenent in the string.
  105. // ie return a pointer to the next backslash or terminating NULL.
  106. static inline
  107. LPCXSTR WUGetPCEndX(LPCXSTR pszStart)
  108. {
  109. LPCXSTR pszEnd;
  110. pszEnd = StrChrX(pszStart, CH_WHACK);
  111. if (pszEnd == NULL)
  112. pszEnd = pszStart + lstrlenX(pszStart);
  113. return pszEnd;
  114. }
  115. // **************************************************************************
  116. // Given a pointer to the end of a path component, return a pointer to
  117. // its begining.
  118. // ie return a pointer to the previous backslash (or start of the string).
  119. static inline
  120. LPXSTR WUGetPCStartX(LPXSTR pszStart, LPCXSTR pszCurrent)
  121. {
  122. LPXSTR pszBegin;
  123. pszBegin = StrRChrX(pszStart, pszCurrent, CH_WHACK);
  124. if (pszBegin == NULL)
  125. pszBegin = pszStart;
  126. return pszBegin;
  127. }
  128. // **************************************************************************
  129. // Fix up a few special cases so that things roughly make sense.
  130. static inline
  131. void WUNearRootFixupsX(LPXSTR pszPath, DWORD cchPath, BOOL fUNC)
  132. {
  133. // Empty path?
  134. if (cchPath > 1 && pszPath[0] == _X('\0'))
  135. {
  136. pszPath[0] = CH_WHACK;
  137. pszPath[1] = _X('\0');
  138. }
  139. // Missing slash? (In the case of ANSI, be sure to check if the first
  140. // character is a lead byte
  141. else if (cchPath > 3 &&
  142. #if !defined(SAFEPATH_UNICODEPASS)
  143. IsDBCSLeadByte(pszPath[0]) == FALSE &&
  144. #endif
  145. pszPath[1] == _X(':') && pszPath[2] == _X('\0'))
  146. {
  147. pszPath[2] = _X('\\');
  148. pszPath[3] = _X('\0');
  149. }
  150. // UNC root?
  151. else if (cchPath > 2 &&
  152. fUNC &&
  153. pszPath[0] == _X('\\') && pszPath[1] == _X('\0'))
  154. {
  155. pszPath[1] = _X('\\');
  156. pszPath[2] = _X('\0');
  157. }
  158. }
  159. // **************************************************************************
  160. static inline
  161. LPXSTR AllocNewDest(LPXSTR pszDest, DWORD cchDest, LPXSTR *ppchDest, LPXSTR *ppszMax)
  162. {
  163. HRESULT hr;
  164. LPXSTR pszNewDest = NULL;
  165. DWORD cchToCopy;
  166. pszNewDest = (LPXSTR)HeapAlloc(GetProcessHeap(), 0, cchDest * sizeof(XCHAR));
  167. if (pszNewDest == NULL)
  168. {
  169. SetLastError(ERROR_OUTOFMEMORY);
  170. goto done;
  171. }
  172. cchToCopy = (DWORD)(DWORD_PTR)(*ppchDest - pszDest);
  173. hr = StringCchCopyNExX(pszNewDest, cchDest, pszDest, cchToCopy,
  174. NULL, NULL, SAFEPATH_STRING_FLAGS);
  175. if (FAILED(hr))
  176. {
  177. HeapFree(GetProcessHeap(), 0, pszNewDest);
  178. SetLastError(HRESULT_CODE(hr));
  179. pszNewDest = NULL;
  180. goto done;
  181. }
  182. *ppchDest = pszNewDest + cchToCopy;
  183. *ppszMax = pszNewDest + cchDest - 1;
  184. done:
  185. return pszNewDest;
  186. }
  187. //////////////////////////////////////////////////////////////////////////////
  188. // Exported functions
  189. // **************************************************************************
  190. HRESULT PathCchCanonicalizeX(LPXSTR pszDest, DWORD cchDest, LPCXSTR pszSrc)
  191. {
  192. HRESULT hr = NOERROR;
  193. LPCXSTR pchSrc, pchPCEnd;
  194. LPXSTR pszMax = pszDest + cchDest - 1;
  195. LPXSTR pchDest;
  196. LPXSTR pszDestReal = pszDest;
  197. DWORD cchPC;
  198. BOOL fUNC, fRoot;
  199. if (pszDest == NULL || cchDest == 0 || pszSrc == NULL)
  200. {
  201. hr = STRSAFE_E_INVALID_PARAMETER;
  202. goto done;
  203. }
  204. pchSrc = pszSrc;
  205. pchDest = pszDestReal;
  206. // Need to keep track of whether we have a UNC path so we can potentially
  207. // fix it up below
  208. fUNC = PathIsUNCX(pszSrc);
  209. while (*pchSrc != _T('\0'))
  210. {
  211. pchPCEnd = WUGetPCEndX(pchSrc);
  212. cchPC = (DWORD)(DWORD_PTR)(pchPCEnd - pchSrc) + 1;
  213. // is it a backslash?
  214. if (cchPC == 1 && *pchSrc == CH_WHACK)
  215. {
  216. if (pchDest + 1 > pszMax)
  217. {
  218. // source string too big for the buffer. Put a NULL at the end
  219. // to ensure that it is NULL terminated.
  220. pszDestReal[cchDest - 1] = 0;
  221. hr = STRSAFE_E_INSUFFICIENT_BUFFER;
  222. goto done;
  223. }
  224. // Just copy it.
  225. *pchDest++ = CH_WHACK;
  226. pchSrc++;
  227. }
  228. // ok, how about a dot?
  229. else if (cchPC == 2 && *pchSrc == _X('.'))
  230. {
  231. if (pszDest == pszSrc && pszDestReal == pszDest)
  232. {
  233. pszDestReal = AllocNewDest(pszDest, cchDest, &pchDest, &pszMax);
  234. if (pszDestReal == NULL)
  235. {
  236. hr = HRESULT_FROM_WIN32(GetLastError());
  237. goto done;
  238. }
  239. }
  240. // Are we at the end?
  241. if (*(pchSrc + 1) == 0)
  242. {
  243. pchSrc++;
  244. // remove the last slash we copied (if we've copied one), but
  245. // don't make a malformed root
  246. if (pchDest > pszDestReal && PathIsRootX(pszDestReal) == FALSE)
  247. pchDest--;
  248. }
  249. else
  250. {
  251. pchSrc += 2;
  252. }
  253. }
  254. // any double dots?
  255. else if (cchPC == 3 && *pchSrc == _X('.') && *(pchSrc + 1) == _X('.'))
  256. {
  257. if (pszDest == pszSrc && pszDestReal == pszDest)
  258. {
  259. pszDestReal = AllocNewDest(pszDest, cchDest, &pchDest, &pszMax);
  260. if (pszDestReal == NULL)
  261. {
  262. hr = HRESULT_FROM_WIN32(GetLastError());
  263. goto done;
  264. }
  265. }
  266. // make sure we aren't already at the root. If not, just remove
  267. // the previous path component
  268. if (PathIsRootX(pszDestReal) == FALSE)
  269. {
  270. pchDest = WUGetPCStartX(pszDestReal, pchDest - 1);
  271. }
  272. // we are at the root- however, we must make sure to skip the
  273. // backslash at the end of the ..\ so we don't copy another
  274. // one (otherwise, C:\..\FOO would become C:\\FOO)
  275. else
  276. {
  277. if (*(pchSrc + 2) == CH_WHACK)
  278. pchSrc++;
  279. }
  280. // skip ".."
  281. pchSrc += 2;
  282. }
  283. // just choose 'none of the above'...
  284. else
  285. {
  286. if (pchDest != pchSrc)
  287. {
  288. DWORD cchAvail;
  289. cchAvail = cchDest - (DWORD)(DWORD_PTR)(pchDest - pszDestReal);
  290. hr = StringCchCopyNExX(pchDest, cchAvail, pchSrc, cchPC,
  291. NULL, NULL, SAFEPATH_STRING_FLAGS);
  292. if (FAILED(hr))
  293. goto done;
  294. }
  295. pchDest += (cchPC - 1);
  296. pchSrc += (cchPC - 1);
  297. }
  298. // make sure we always have a NULL terminated string
  299. if (pszDestReal != pszSrc)
  300. *pchDest = _X('\0');
  301. }
  302. // Check for weirdo root directory stuff.
  303. WUNearRootFixupsX(pszDestReal, cchDest, fUNC);
  304. if (pszDest != pszDestReal)
  305. {
  306. hr = StringCchCopyExX(pszDest, cchDest, pszDestReal,
  307. NULL, NULL, SAFEPATH_STRING_FLAGS);
  308. }
  309. done:
  310. if (pszDest != pszDestReal && pszDestReal != NULL)
  311. HeapFree(GetProcessHeap(), 0, pszDestReal);
  312. return hr;
  313. }
  314. // **************************************************************************
  315. HRESULT PathCchRenameExtensionX(LPXSTR pszPath, DWORD cchPath, LPCXSTR pszExt)
  316. {
  317. HRESULT hr = NOERROR;
  318. LPXSTR pszOldExt;
  319. DWORD cchPathWithoutExt;
  320. if (pszPath == NULL || pszExt == NULL)
  321. {
  322. hr = STRSAFE_E_INVALID_PARAMETER;
  323. goto done;
  324. }
  325. // This function returns a pointer to the end of the string if there
  326. // is no extension. This is exactly what we want cuz we will want
  327. // to add an extension to the end of the string if none exists.
  328. pszOldExt = PathFindExtensionX(pszPath);
  329. cchPathWithoutExt = (DWORD)(DWORD_PTR)(pszOldExt - pszPath);
  330. hr = StringCchCopyExX(pszOldExt, cchPath - cchPathWithoutExt, pszExt,
  331. NULL, NULL, SAFEPATH_STRING_FLAGS);
  332. done:
  333. return hr;
  334. }
  335. // **************************************************************************
  336. HRESULT PathCchAddExtensionX(LPXSTR pszPath, DWORD cchPath, LPCXSTR pszExt)
  337. {
  338. HRESULT hr = NOERROR;
  339. LPXSTR pszOldExt;
  340. if (pszPath == NULL)
  341. {
  342. hr = STRSAFE_E_INVALID_PARAMETER;
  343. goto done;
  344. }
  345. // since we're *adding* an extension here, don't want to do anything if
  346. // one already exists
  347. pszOldExt = PathFindExtensionX(pszPath);
  348. if (*pszOldExt == _T('\0'))
  349. {
  350. if (pszExt == NULL)
  351. pszExt = c_szDotExeX;
  352. hr = StringCchCatExX(pszPath, cchPath, pszExt,
  353. NULL, NULL, SAFEPATH_STRING_FLAGS);
  354. }
  355. done:
  356. return hr;
  357. }
  358. // **************************************************************************
  359. HRESULT PathCchAddBackslashX(LPXSTR pszPath, DWORD cchPathBuff)
  360. {
  361. HRESULT hr = NOERROR;
  362. LPCXSTR psz;
  363. DWORD cch;
  364. if (pszPath == NULL)
  365. {
  366. hr = STRSAFE_E_INVALID_PARAMETER;
  367. goto done;
  368. }
  369. cch = lstrlenX(pszPath);
  370. if (cch == 0)
  371. goto done;
  372. #if defined(SAFEPATH_UNICODEPASS)
  373. psz = &pszPath[cch - 1];
  374. #else
  375. psz = CharPrevA(pszPath, &pszPath[cch]);
  376. #endif
  377. // if the end of the base string does not have a backslash, then add one
  378. if (*psz != CH_WHACK)
  379. {
  380. // make sure we have enough space for the backslash in the buffer
  381. if (cch + 1 >= cchPathBuff)
  382. {
  383. hr = STRSAFE_E_INSUFFICIENT_BUFFER;
  384. goto done;
  385. }
  386. pszPath[cch++] = CH_WHACK;
  387. pszPath[cch] = _X('\0');
  388. }
  389. done:
  390. return hr;
  391. }
  392. // **************************************************************************
  393. HRESULT PathCchCombineX(LPXSTR pszPath, DWORD cchPathBuff, LPCXSTR pszPrefix,
  394. LPCXSTR pszSuffix)
  395. {
  396. HRESULT hr = NOERROR;
  397. if (pszPath == NULL || cchPathBuff == 0)
  398. {
  399. hr = STRSAFE_E_INVALID_PARAMETER;
  400. goto done;
  401. }
  402. // if both fields are NULL, just bail now.
  403. if (pszPrefix == NULL && pszSuffix == NULL)
  404. {
  405. pszPath[0] = L'\0';
  406. goto done;
  407. }
  408. if ((pszPrefix == NULL || *pszPrefix == _X('\0')) &&
  409. (pszSuffix == NULL || *pszSuffix == _X('\0')))
  410. {
  411. if (cchPathBuff > 1)
  412. {
  413. pszPath[0] = _X('\\');
  414. pszPath[1] = _X('\0');
  415. }
  416. else
  417. {
  418. hr = STRSAFE_E_INSUFFICIENT_BUFFER;
  419. }
  420. goto done;
  421. }
  422. // if all we have is the suffix, just copy it
  423. if (pszPrefix == NULL || *pszPrefix == _X('\0'))
  424. {
  425. hr = StringCchCopyExX(pszPath, cchPathBuff, pszSuffix,
  426. NULL, NULL, SAFEPATH_STRING_FLAGS);
  427. if (FAILED(hr))
  428. goto done;
  429. }
  430. else
  431. {
  432. // if all we have is the prefix, just copy it
  433. if (pszSuffix == NULL || *pszSuffix == _X('\0'))
  434. {
  435. hr = StringCchCopyExX(pszPath, cchPathBuff, pszPrefix,
  436. NULL, NULL, SAFEPATH_STRING_FLAGS);
  437. if (FAILED(hr))
  438. goto done;
  439. }
  440. // if we have a relative path for the suffix, then we just combine
  441. // the two and insert a backslash between them if necessary
  442. else if (PathIsRelativeX(pszSuffix))
  443. {
  444. hr = StringCchCopyExX(pszPath, cchPathBuff, pszPrefix,
  445. NULL, NULL, SAFEPATH_STRING_FLAGS);
  446. if (FAILED(hr))
  447. goto done;
  448. hr = PathCchAddBackslashX(pszPath, cchPathBuff);
  449. if (FAILED(hr))
  450. goto done;
  451. hr = StringCchCatExX(pszPath, cchPathBuff, pszSuffix,
  452. NULL, NULL, SAFEPATH_STRING_FLAGS);
  453. if (FAILED(hr))
  454. goto done;
  455. }
  456. // if the suffix starts with a backslash then just strip off
  457. // everything except for the root of the prefix and append the
  458. // suffix
  459. else if (*pszSuffix == CH_WHACK && PathIsUNCX(pszSuffix) == FALSE)
  460. {
  461. hr = StringCchCopyExX(pszPath, cchPathBuff, pszPrefix,
  462. NULL, NULL, SAFEPATH_STRING_FLAGS);
  463. if (FAILED(hr))
  464. goto done;
  465. // this is safe to call as it will only reduce the size of the
  466. // string
  467. PathStripToRootX(pszPath);
  468. hr = PathCchAddBackslashX(pszPath, cchPathBuff);
  469. if (FAILED(hr))
  470. goto done;
  471. // make sure to skip the backslash while appending
  472. hr = StringCchCatExX(pszPath, cchPathBuff, pszSuffix + 1,
  473. NULL, NULL, SAFEPATH_STRING_FLAGS);
  474. if (FAILED(hr))
  475. goto done;
  476. }
  477. // we'll, likely the suffix is a full path (local or UNC), so
  478. // ignore the prefix
  479. else
  480. {
  481. hr = StringCchCopyExX(pszPath, cchPathBuff, pszSuffix,
  482. NULL, NULL, SAFEPATH_STRING_FLAGS);
  483. if (FAILED(hr))
  484. goto done;
  485. }
  486. }
  487. hr = PathCchCanonicalizeX(pszPath, cchPathBuff, pszPath);
  488. done:
  489. return hr;
  490. }
  491. // **************************************************************************
  492. HRESULT PathCchAppendX(LPXSTR pszPath, DWORD cchPathBuff, LPCXSTR pszNew)
  493. {
  494. HRESULT hr = NOERROR;
  495. DWORD dwOffset = 0;
  496. DWORD cch, cchNew;
  497. if (pszPath == NULL)
  498. {
  499. hr = STRSAFE_E_INVALID_PARAMETER;
  500. goto done;
  501. }
  502. if (pszNew != NULL)
  503. {
  504. // skip all initial backslashes in pszNew
  505. while (*pszNew == CH_WHACK)
  506. {
  507. pszNew++;
  508. }
  509. hr = PathCchCombineX(pszPath, cchPathBuff, pszPath, pszNew);
  510. }
  511. else
  512. {
  513. hr = E_FAIL;
  514. }
  515. done:
  516. return hr;
  517. }
  518. // make the unicode pass through the file
  519. #if !defined(SAFEPATH_UNICODEPASS)
  520. #define SAFEPATH_UNICODEPASS
  521. #include "safepath.cpp"
  522. #endif