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.

326 lines
9.6 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. SafeExt.c (WinSAFER File Extension)
  5. Abstract:
  6. This module implements the WinSAFER APIs that evaluate the system
  7. policies to determine if a given file extension is an "executable"
  8. file that needs to have different enforcement policies considered.
  9. Author:
  10. Jeffrey Lawson (JLawson) - Nov 1999
  11. Environment:
  12. User mode only.
  13. Exported Functions:
  14. Revision History:
  15. Created - Jul 2000
  16. --*/
  17. #include "pch.h"
  18. #pragma hdrstop
  19. #include <winsafer.h>
  20. #include <winsaferp.h>
  21. #include "saferp.h"
  22. NTSTATUS NTAPI
  23. __CodeAuthzIsExecutableFileTypeHelper(
  24. IN PUNICODE_STRING UnicodeFullPathname,
  25. IN DWORD dwScopeId,
  26. IN BOOLEAN bFromShellExecute,
  27. OUT PBOOLEAN pbResult
  28. )
  29. {
  30. NTSTATUS Status;
  31. LPCWSTR szExtension, szPtr, szEnd;
  32. ULONG ulExtensionLength;
  33. HANDLE hKeyBadTypes;
  34. DWORD dwAllocatedSize = 0, dwActualSize;
  35. PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo = NULL;
  36. const static UNICODE_STRING UnicodeValueName =
  37. RTL_CONSTANT_STRING(SAFER_EXETYPES_REGVALUE);
  38. if (!ARGUMENT_PRESENT(UnicodeFullPathname) ||
  39. UnicodeFullPathname->Buffer == NULL ||
  40. UnicodeFullPathname->Length == 0)
  41. {
  42. Status = STATUS_INVALID_PARAMETER;
  43. goto ExitHandler;
  44. }
  45. if (!ARGUMENT_PRESENT(pbResult)) {
  46. Status = STATUS_ACCESS_VIOLATION;
  47. goto ExitHandler;
  48. }
  49. //
  50. // Start from the end of the string and scan backwards to
  51. // look for the period separator and find the extension.
  52. //
  53. szExtension = UnicodeFullPathname->Buffer +
  54. (UnicodeFullPathname->Length / sizeof(WCHAR));
  55. ASSERT(szExtension >= UnicodeFullPathname->Buffer);
  56. for (;;) {
  57. if (szExtension < UnicodeFullPathname->Buffer ||
  58. *szExtension == L'\\' || *szExtension == L'/') {
  59. // We scanned back too far, but did not find the extension.
  60. Status = STATUS_NOT_FOUND;
  61. goto ExitHandler;
  62. }
  63. if (*szExtension == L'.') {
  64. // We found the period that marks the extension.
  65. szExtension++;
  66. break;
  67. }
  68. szExtension--;
  69. }
  70. ulExtensionLength = (UnicodeFullPathname->Length / sizeof(WCHAR)) -
  71. (ULONG) (szExtension - UnicodeFullPathname->Buffer);
  72. if (ulExtensionLength == 0) {
  73. // We found a period, but there was no extension.
  74. Status = STATUS_NOT_FOUND;
  75. goto ExitHandler;
  76. }
  77. if (bFromShellExecute) {
  78. if ( _wcsicmp(szExtension, L"exe") == 0 ){
  79. *pbResult = FALSE;
  80. Status = STATUS_SUCCESS;
  81. goto ExitHandler;
  82. }
  83. }
  84. //
  85. // Open and query the registry value containing the list of extensions.
  86. //
  87. Status = CodeAuthzpOpenPolicyRootKey(
  88. dwScopeId,
  89. NULL,
  90. SAFER_CODEIDS_REGSUBKEY,
  91. KEY_READ,
  92. FALSE,
  93. &hKeyBadTypes
  94. );
  95. if (!NT_SUCCESS(Status)) {
  96. goto ExitHandler;
  97. }
  98. for (;;) {
  99. Status = NtQueryValueKey(
  100. hKeyBadTypes,
  101. (PUNICODE_STRING) &UnicodeValueName,
  102. KeyValuePartialInformation,
  103. pKeyValueInfo, dwAllocatedSize, &dwActualSize);
  104. if (NT_SUCCESS(Status)) {
  105. break;
  106. }
  107. else if ((Status == STATUS_BUFFER_OVERFLOW ||
  108. Status == STATUS_BUFFER_TOO_SMALL) &&
  109. dwActualSize > dwAllocatedSize)
  110. {
  111. if (pKeyValueInfo != NULL) {
  112. RtlFreeHeap(RtlProcessHeap(), 0, pKeyValueInfo);
  113. }
  114. dwAllocatedSize = dwActualSize;
  115. pKeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)
  116. RtlAllocateHeap(RtlProcessHeap(), 0, dwAllocatedSize);
  117. if (!pKeyValueInfo) {
  118. Status = STATUS_NO_MEMORY;
  119. goto ExitHandler2;
  120. }
  121. }
  122. else {
  123. goto ExitHandler3;
  124. }
  125. }
  126. //
  127. // See if the extension is in one of those specified in the list.
  128. //
  129. szEnd = (LPCWSTR) ( ((LPBYTE) pKeyValueInfo->Data) +
  130. pKeyValueInfo->DataLength);
  131. for (szPtr = (LPCWSTR) pKeyValueInfo->Data; szPtr < szEnd; ) {
  132. ULONG ulOneExtension = wcslen(szPtr);
  133. if (szPtr + ulOneExtension > szEnd) {
  134. ulOneExtension = (ULONG) (szEnd - szPtr);
  135. }
  136. if (ulOneExtension == ulExtensionLength &&
  137. _wcsnicmp(szExtension, szPtr, ulExtensionLength) == 0) {
  138. *pbResult = TRUE;
  139. Status = STATUS_SUCCESS;
  140. goto ExitHandler3;
  141. }
  142. szPtr += ulOneExtension + 1;
  143. }
  144. *pbResult = FALSE;
  145. Status = STATUS_SUCCESS;
  146. ExitHandler3:
  147. if (pKeyValueInfo != NULL) {
  148. RtlFreeHeap(RtlProcessHeap(), 0, pKeyValueInfo);
  149. }
  150. ExitHandler2:
  151. NtClose(hKeyBadTypes);
  152. ExitHandler:
  153. return Status;
  154. }
  155. NTSTATUS NTAPI
  156. CodeAuthzIsExecutableFileType(
  157. IN PUNICODE_STRING UnicodeFullPathname,
  158. IN BOOLEAN bFromShellExecute,
  159. OUT PBOOLEAN pbResult
  160. )
  161. /*++
  162. Routine Description:
  163. This API determines if a specified filename has an extension that
  164. is considered an "executable" extension. Applications can take
  165. special precautions to avoid invoking untrusted files that might
  166. be considered executable.
  167. Common examples of extensions that are considered executable include:
  168. EXE, COM, BAT, CMD, VBS, JS, URL, LNK, SHS, PIF, PL, and others.
  169. Arguments:
  170. UnicodeFullPathname - pointer to a Unicode string of the
  171. full path and/or filename to evaluate. Only the file's extension
  172. (the portion of the specified path following the last period)
  173. is used in this evaluation. File extension comparisons are done
  174. case-insensitively, without regard to case.
  175. An error will be returned if this pointer is NULL, or if the length
  176. of the path is zero, or if the file does not have an extension.
  177. Although applications are encouraged to supply the entire,
  178. fully-qualified pathname to this API, the szFullPathname argument
  179. will also accept only the file extension, by ensuring that the
  180. file extension is preceeded by a period (for example: L".exe")
  181. bFromShellExecute - for performance reasons, if this is being called
  182. from ShellExecute, we'd like to skip exe checking since CreateProcess
  183. will do the check
  184. pbResult - pointer to a variable that will receive a TRUE or FALSE
  185. result value if this API executes successfully. An error will
  186. be returned if this pointer is NULL.
  187. Return Value:
  188. Returns STATUS_SUCCESS if the API executes successfully, otherwise a
  189. valid NTSTATUS error code is returned. If the return value indicates
  190. success, then the argument 'pbResult' will also receive a boolean
  191. value indicating whether or not the pathname represented an executable
  192. file type.
  193. --*/
  194. {
  195. NTSTATUS Status;
  196. Status = __CodeAuthzIsExecutableFileTypeHelper(
  197. UnicodeFullPathname,
  198. SAFER_SCOPEID_MACHINE,
  199. bFromShellExecute,
  200. pbResult
  201. );
  202. if (!NT_SUCCESS(Status)) {
  203. Status = __CodeAuthzIsExecutableFileTypeHelper(
  204. UnicodeFullPathname,
  205. SAFER_SCOPEID_USER,
  206. bFromShellExecute,
  207. pbResult
  208. );
  209. }
  210. return Status;
  211. }
  212. BOOL WINAPI
  213. SaferiIsExecutableFileType(
  214. IN LPCWSTR szFullPathname,
  215. IN BOOLEAN bFromShellExecute
  216. )
  217. /*++
  218. Routine Description:
  219. This API determines if a specified filename has an extension that
  220. is considered an "executable" extension. Applications can take
  221. special precautions to avoid invoking untrusted files that might
  222. be considered executable.
  223. Common examples of extensions that are considered executable include:
  224. EXE, COM, BAT, CMD, VBS, JS, URL, LNK, SHS, PIF, PL, and others.
  225. Arguments:
  226. szFullPathname - pointer to a Null-terminated Unicode string of the
  227. full path and/or filename to evaluate. Only the file's extension
  228. (the portion of the specified path following the last period)
  229. is used in this evaluation. File extension comparisons are done
  230. case-insensitively, without regard to case.
  231. An error will be returned if this pointer is NULL, or if the length
  232. of the path is zero, or if the file does not have an extension.
  233. Although applications are encouraged to supply the entire,
  234. fully-qualified pathname to this API, the szFullPathname argument
  235. will also accept only the file extension, by ensuring that the
  236. file extension is preceeded by a period (for example: L".exe")
  237. bFromShellExecute - for performance reasons, if this is being called
  238. from ShellExecute, we'd like to skip exe checking since CreateProcess
  239. will do the check
  240. Return Value:
  241. Returns TRUE if the API executes successfully and the filepath's
  242. extension was recognized as one of the "executable extensions".
  243. Otherwise a return value of FALSE will either indicate unsuccessful
  244. API execution, or identification of a non-executable extension.
  245. --*/
  246. {
  247. NTSTATUS Status;
  248. UNICODE_STRING UnicodePathname;
  249. BOOLEAN bResult;
  250. RtlInitUnicodeString(&UnicodePathname, szFullPathname);
  251. Status = CodeAuthzIsExecutableFileType(
  252. &UnicodePathname, bFromShellExecute, &bResult);
  253. if (!NT_SUCCESS(Status)) {
  254. BaseSetLastNTError(Status);
  255. return FALSE;
  256. }
  257. if (!bResult) {
  258. BaseSetLastNTError(STATUS_NOT_FOUND);
  259. return FALSE;
  260. } else {
  261. return TRUE;
  262. }
  263. }