Source code of Windows XP (NT5)
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.

532 lines
15 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. SafePath.c (WinSAFER Path Comparison)
  5. Abstract:
  6. This module implements the WinSAFER APIs that evaluate the system
  7. policies to determine which Authorization Level has been configured
  8. to apply restrictions for a specified application or code library.
  9. Author:
  10. Jeffrey Lawson (JLawson) - Nov 1999
  11. Environment:
  12. User mode only.
  13. Exported Functions:
  14. CodeAuthzpCompareImagePath
  15. Revision History:
  16. Created - Nov 1999
  17. --*/
  18. #include "pch.h"
  19. #pragma hdrstop
  20. #include <winsafer.h>
  21. #include <winsaferp.h>
  22. #include "saferp.h"
  23. //
  24. // Define the following value to use the "new" comparison logic,
  25. // that includes asterick and question mark matching.
  26. //
  27. #define USE_NEW_WILDCARD_EVALUATION
  28. //
  29. // Convenient macros for doing filename pattern matching.
  30. //
  31. #define IS_UCASE_CHARS_EQUAL_U(ch1, ch2) (((ch1) == (ch2)) || (RtlUpcaseUnicodeChar(ch1) == RtlUpcaseUnicodeChar(ch2)))
  32. #define IS_PATH_SEPARATOR_U(ch) (((ch) == L'\\') || ((ch) == L'/'))
  33. #define IS_WILDCARD_CHAR_U(ch) ((ch) == L'*')
  34. #define IS_QUESTION_CHAR_U(ch) ((ch) == L'?')
  35. #define IS_DOT_CHAR_U(ch) ((ch) == L'.')
  36. FORCEINLINE LPCWSTR CodeAuthzpFindSlash (
  37. IN LPCWSTR string,
  38. IN USHORT length
  39. )
  40. /*++
  41. Routine Description:
  42. Returns a pointer to the first instance of a forward or
  43. backward slash within the specified string buffer.
  44. Arguments:
  45. string -
  46. length -
  47. Return Value:
  48. Returns NULL if no backslashes or forward-slashes were found within
  49. the string. Otherwise returns a pointer to the matching char.
  50. --*/
  51. {
  52. while (length-- > 0) {
  53. if (IS_PATH_SEPARATOR_U(*string)) return string;
  54. string++;
  55. }
  56. return NULL;
  57. }
  58. #ifdef USE_NEW_WILDCARD_EVALUATION
  59. LONG NTAPI
  60. __CodeAuthzpCompareImagePathHelper(
  61. IN LPCWSTR wild,
  62. IN USHORT wildlen,
  63. IN LPCWSTR actual,
  64. IN USHORT actuallen
  65. )
  66. /*++
  67. Routine Description:
  68. Evaluates a wildcard pattern against a specified pathname and
  69. indicates if they match.
  70. Arguments:
  71. wild -
  72. wildlen -
  73. actual -
  74. actuallen -
  75. Return Value:
  76. 0 = no match
  77. -1 = match exactly
  78. 1 = match with wildcard
  79. --*/
  80. {
  81. LONG lMatchResult = -1;
  82. ASSERT(ARGUMENT_PRESENT(wild) &&
  83. !CodeAuthzpFindSlash(wild, wildlen));
  84. ASSERT(ARGUMENT_PRESENT(actual) &&
  85. !CodeAuthzpFindSlash(actual, actuallen));
  86. for (;;) {
  87. // Check for terminating conditions.
  88. if (wildlen == 0) {
  89. if (actuallen == 0) {
  90. return lMatchResult;
  91. } else {
  92. ASSERT(actuallen > 0);
  93. return 0;
  94. }
  95. }
  96. // Evaluate the wildcard pattern.
  97. if (IS_WILDCARD_CHAR_U(*wild)) {
  98. USHORT matchcount;
  99. // Skip past the asterick (possibly multiple).
  100. do {
  101. wild++; wildlen--;
  102. } while ( wildlen > 0 && IS_WILDCARD_CHAR_U(*wild) );
  103. // Try expanding the asterick to be zero or more chars.
  104. for (matchcount = 0; ; matchcount++) {
  105. if (matchcount > actuallen) {
  106. return 0; // match failed.
  107. }
  108. if (0 != __CodeAuthzpCompareImagePathHelper(
  109. wild, wildlen,
  110. &actual[matchcount], actuallen - matchcount))
  111. {
  112. actual += matchcount;
  113. actuallen -= matchcount;
  114. break;
  115. }
  116. }
  117. // We've encountered a wildcard char, so remember
  118. // that this is no longer an "exact" match.
  119. lMatchResult = 1;
  120. } else if (IS_QUESTION_CHAR_U(*wild)) {
  121. // Question marks will match any single character, except
  122. // periods. Question marks will also match nothing when
  123. // we are already at the end of the filename or segment.
  124. if (actuallen > 0 && !IS_DOT_CHAR_U(*actual)) {
  125. actual++; actuallen--;
  126. }
  127. wild++; wildlen--;
  128. // We've encountered a wildcard char, so remember
  129. // that this is no longer an "exact" match.
  130. lMatchResult = 1;
  131. } else {
  132. if (actuallen < 1 ||
  133. !IS_UCASE_CHARS_EQUAL_U(*wild, *actual)) {
  134. return 0;
  135. }
  136. wild++; wildlen--;
  137. actual++; actuallen--;
  138. }
  139. }
  140. }
  141. LONG NTAPI
  142. CodeAuthzpCompareUnicodeImagePath(
  143. IN PCUNICODE_STRING wildcard,
  144. IN PCUNICODE_STRING actual
  145. )
  146. /*++
  147. Routine Description:
  148. Evaluates a wildcard pattern against a specified pathname and
  149. indicates if they match.
  150. Arguments:
  151. wildcard -
  152. actual -
  153. Return Value:
  154. Returns 0 if the path fragment does not match the specified imagepath.
  155. Returns -1 if the fragment matches the imagepath _exactly_!
  156. Otherwise returns a postive integer representing the "depth" of the
  157. the match (number of matching subdirectories). Greater values
  158. indicate a "deeper" directory match.
  159. --*/
  160. {
  161. USHORT wildindex = 0, actualindex = 0;
  162. LONG matchquality = 0;
  163. BOOLEAN bNoWildcardsFound = TRUE;
  164. ASSERT(ARGUMENT_PRESENT(wildcard) && wildcard->Buffer != NULL);
  165. ASSERT(ARGUMENT_PRESENT(actual) && actual->Buffer != NULL);
  166. for (;;)
  167. {
  168. ASSERT(wildindex <= wildcard->Length / sizeof(WCHAR));
  169. ASSERT(actualindex <= actual->Length / sizeof(WCHAR));
  170. if (wildindex == wildcard->Length / sizeof(WCHAR))
  171. {
  172. // We've reached the end of the wildcard but the actual string has
  173. // not ended.
  174. if (actualindex < actual->Length / sizeof(WCHAR)) {
  175. return matchquality;
  176. }
  177. // The wildcard matched the filename but with inexact matches.
  178. // Return one more than the actual depth so that we can handle
  179. // non-qualified path matches as worse then these.
  180. if (!bNoWildcardsFound) {
  181. return (matchquality + 1);
  182. }
  183. ASSERT(wildindex == wildcard->Length / sizeof(WCHAR));
  184. return -1; // exact match.
  185. }
  186. else if (IS_PATH_SEPARATOR_U(wildcard->Buffer[wildindex]))
  187. {
  188. if (!IS_PATH_SEPARATOR_U(actual->Buffer[actualindex])) {
  189. return 0; // no match
  190. }
  191. // Skip forward to the start of the next component.
  192. do {
  193. wildindex++;
  194. } while ( wildindex < wildcard->Length / sizeof(WCHAR) &&
  195. IS_PATH_SEPARATOR_U(wildcard->Buffer[wildindex]) );
  196. // Skip forward to the start of the next component.
  197. do {
  198. actualindex++;
  199. } while ( actualindex < actual->Length / sizeof(WCHAR) &&
  200. IS_PATH_SEPARATOR_U(actual->Buffer[actualindex]) );
  201. }
  202. else
  203. {
  204. USHORT wildlen = 0, actuallen = 0;
  205. // Count the length of this component of the wildcard.
  206. while (wildindex + wildlen < (USHORT) (wildcard->Length / sizeof(WCHAR)) &&
  207. !IS_PATH_SEPARATOR_U(wildcard->Buffer[wildindex + wildlen])) {
  208. wildlen++;
  209. }
  210. ASSERT(wildlen > 0);
  211. // Count the length of this component of the actual path.
  212. while (actualindex + actuallen < (USHORT) (actual->Length / sizeof(WCHAR)) &&
  213. !IS_PATH_SEPARATOR_U(actual->Buffer[actualindex + actuallen])) {
  214. actuallen++;
  215. }
  216. // Otherwise require that this component matches.
  217. switch (__CodeAuthzpCompareImagePathHelper(
  218. &wildcard->Buffer[wildindex], wildlen,
  219. &actual->Buffer[actualindex], actuallen)) {
  220. case 0: // fails to match
  221. return 0;
  222. case -1: // matches exactly without wildcards
  223. break;
  224. default: // matches with wildcard expansion.
  225. bNoWildcardsFound = FALSE; break;
  226. }
  227. // Increment pointers for next component.
  228. wildindex += wildlen;
  229. actualindex += actuallen;
  230. matchquality++;
  231. }
  232. }
  233. }
  234. LONG NTAPI
  235. CodeAuthzpCompareImagePath(
  236. IN LPCWSTR szPathFragment,
  237. IN LPCWSTR szFullImagePath
  238. )
  239. /*++
  240. Routine Description:
  241. Evaluates a wildcard pattern against a specified pathname and
  242. indicates if they match.
  243. Arguments:
  244. szPathFragment -
  245. szFullImagePath -
  246. Return Value:
  247. Returns 0 if the path fragment does not match the specified imagepath.
  248. Returns -1 if the fragment matches the imagepath _exactly_!
  249. Otherwise returns a postive integer representing the "depth" of the
  250. the match (number of matching subdirectories). Greater values
  251. indicate a "deeper" directory match.
  252. --*/
  253. {
  254. UNICODE_STRING UnicodePathFragment;
  255. UNICODE_STRING UnicodeFullImagePath;
  256. ULONG i = 0;
  257. USHORT Len = 0;
  258. LONG lMatchDepth = 0;
  259. RtlInitUnicodeString(&UnicodePathFragment, szPathFragment);
  260. RtlInitUnicodeString(&UnicodeFullImagePath, szFullImagePath);
  261. lMatchDepth = CodeAuthzpCompareUnicodeImagePath(
  262. &UnicodePathFragment, &UnicodeFullImagePath);
  263. // We did not get a match for fully qualified name. Let's try for just a
  264. // basename match.
  265. if (!lMatchDepth) {
  266. // if the rule has a '\' in it, it's not a basename match rule.
  267. // We only check for filename.ext rules allowing wildcards.
  268. if (wcschr(szPathFragment, L'\\')) {
  269. return 0;
  270. }
  271. Len = (UnicodeFullImagePath.Length/sizeof(WCHAR)) -1;
  272. // Skip from rightmost character to the the character just after the
  273. // last '\', if any or to the beginning of the string in absence of '\'.
  274. while (Len > 0 && szFullImagePath[Len] != L'\\') {
  275. Len--;
  276. }
  277. // A '\' exists. Move one character to the right.
  278. if (szFullImagePath[Len] == L'\\') {
  279. Len++;
  280. }
  281. // Check if there is a match of the file basename with the rule. We have
  282. // already checked that the rule does not have '\'.
  283. switch (__CodeAuthzpCompareImagePathHelper(
  284. szPathFragment, UnicodePathFragment.Length/sizeof(WCHAR),
  285. szFullImagePath+Len, (UnicodeFullImagePath.Length/sizeof(WCHAR))-Len)) {
  286. case 0: // fails to match
  287. return 0;
  288. case -1: // matches exactly without wildcards
  289. default: // matches with wildcard expansion.
  290. // We treat exact matches the same as inexact matches here.
  291. // Thus, abc.exe is == a*.exe = *.exe.
  292. // Skip to the first non-'\' character.
  293. while ((szFullImagePath[i] == L'\\') && (szFullImagePath[i] != L'\0')) {
  294. i++;
  295. }
  296. // This string is bogus. It only has 0 or more '\'s in it.
  297. if (szFullImagePath[i] == L'\0') {
  298. return 0;
  299. }
  300. // Return the depth of the tree.
  301. lMatchDepth = 1;
  302. while (TRUE) {
  303. // Skip to the first '\' while not end of string.
  304. while ((szFullImagePath[i] != L'\\') && (szFullImagePath[i] != L'\0')) {
  305. i++;
  306. }
  307. // We are at the end of the string. Return the depth.
  308. if (szFullImagePath[i] == L'\0') {
  309. return lMatchDepth;
  310. }
  311. // Skip to the first non-'\' while not end of string.
  312. while ((szFullImagePath[i] == L'\\') && (szFullImagePath[i] != L'\0')) {
  313. i++;
  314. }
  315. // We are at a non-'\' character. Increment the depth.
  316. lMatchDepth++;
  317. }
  318. // Should never get here.
  319. ASSERT(FALSE);
  320. }
  321. }
  322. return lMatchDepth;
  323. }
  324. #else // #ifdef USE_NEW_WILDCARD_EVALUATION
  325. LONG NTAPI
  326. CodeAuthzpCompareImagePath(
  327. IN LPCWSTR szPathFragment,
  328. IN LPCWSTR szFullImagePath
  329. )
  330. /*++
  331. Routine Description:
  332. Evaluates a wildcard pattern against a specified pathname and
  333. indicates if they match.
  334. Arguments:
  335. szPathFragment -
  336. szFullImagePath -
  337. Return Value:
  338. Returns 0 if the path fragment does not match the specified imagepath.
  339. Returns -1 if the fragment matches the imagepath _exactly_!
  340. Otherwise returns a postive integer representing the "depth" of the
  341. the match (number of matching subdirectories). Greater values
  342. indicate a "deeper" directory match.
  343. --*/
  344. {
  345. LONG MatchDepth = 0;
  346. BOOLEAN bLastWasSlash = TRUE;
  347. LPCWSTR pFragment = szPathFragment;
  348. LPCWSTR pImage = szFullImagePath;
  349. //
  350. // Verify that our arguments were all supplied.
  351. //
  352. ASSERT(ARGUMENT_PRESENT(pFragment) && ARGUMENT_PRESENT(pImage));
  353. if (!*pFragment || !*pImage) return 0; // empty strings.
  354. //
  355. // Perform the actual comparison loop.
  356. //
  357. for (;;) {
  358. if (!*pFragment)
  359. {
  360. // We have reached the string terminator at the end of the
  361. // wildcard fragment. If this was also the end of the
  362. // actual filename, then this is a precise match. Otherwise
  363. // we'll only consider this a positive partial match if this
  364. // occurred on a path-separator boundary.
  365. if (!*pImage) return -1; // matched exactly.
  366. else if (bLastWasSlash) break;
  367. else if (IS_PATH_SEPARATOR_U(*pImage)) break;
  368. else return 0; // did not match.
  369. }
  370. else if (!*pImage)
  371. {
  372. // We have reached the end of the actual filename, but have
  373. // not yet found the end of the wildcard fragment.
  374. return 0; // did not match.
  375. }
  376. else if (!IS_UCASE_CHARS_EQUAL_U(*pFragment, *pImage))
  377. {
  378. // The two characters were unequal. However, this condition
  379. // might occur if multiple path separators occur in one and
  380. // not the other, so explicitly absorb multiple slashes.
  381. if (bLastWasSlash) {
  382. if (IS_PATH_SEPARATOR_U(*pFragment)) { pFragment++; continue; }
  383. else if (IS_PATH_SEPARATOR_U(*pImage)) { pImage++; continue; }
  384. }
  385. return 0; // did not match.
  386. }
  387. else
  388. {
  389. // Both characters matched. Remember if they were slashes.
  390. // If this is a transition to a non-separator portion of the
  391. // filename, then increment our depth counter.
  392. if (IS_PATH_SEPARATOR_U(*pFragment)) {
  393. bLastWasSlash = TRUE;
  394. } else {
  395. if (bLastWasSlash) {
  396. MatchDepth++;
  397. bLastWasSlash = FALSE;
  398. }
  399. }
  400. }
  401. pFragment++;
  402. pImage++;
  403. }
  404. return MatchDepth;
  405. }
  406. #endif //#ifdef USE_NEW_WILDCARD_EVALUATION