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.

2954 lines
98 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name:
  4. SafeIden.c (WinSAFER SaferIdentifyLevel)
  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. SaferiSearchMatchingHashRules (privately exported)
  15. SaferIdentifyLevel
  16. Revision History:
  17. Created - Nov 1999
  18. --*/
  19. #include "pch.h"
  20. #pragma hdrstop
  21. #include <md5.h>
  22. #include <wchar.h> //for swprintf
  23. #pragma warning(push, 3)
  24. #include <wintrust.h> // WinVerifyTrust
  25. #include <softpub.h> // WINTRUST_ACTION_GENERIC_VERIFY_V2
  26. #pragma warning(pop)
  27. #include <winsafer.h>
  28. #include <winsaferp.h>
  29. #include <wintrust.h>
  30. #include <crypto\wintrustp.h>
  31. #include "saferp.h"
  32. #define EXPAND_REGPATH
  33. //#define VERBOSE_IDENTIFICATIONS
  34. #ifdef VERBOSE_IDENTIFICATIONS
  35. #define OUTPUTDEBUGSTRING(v) OutputDebugStringW(v)
  36. #else
  37. #define OUTPUTDEBUGSTRING(v)
  38. #endif
  39. const static GUID guidTrustedCert = SAFER_GUID_RESULT_TRUSTED_CERT;
  40. const static GUID guidDefaultRule = SAFER_GUID_RESULT_DEFAULT_LEVEL;
  41. NTSTATUS NTAPI
  42. __CodeAuthzpEnsureMapped(
  43. IN OUT PLOCALIDENTITYCONTEXT pIdentContext
  44. )
  45. /*++
  46. Routine Description:
  47. Evaluates the supplied identification context structure and
  48. attempts to gain access to a mapped memory region of the entity
  49. being identified. It does the following steps:
  50. 1) if the identification context already has a non-NULL memory
  51. pointer then returns successfully.
  52. 2) if the identification context has a non-NULL file handle
  53. then that handle is memory mapped into memory and
  54. returns successfully.
  55. 3) if the identification context has a non-NULL image filename
  56. then that filename is opened for read access and memory
  57. mapped into memory.
  58. Otherwise the function call is not successful.
  59. The caller must be sure to call CodeAuthzpEnsureUnmapped later.
  60. Arguments:
  61. pIdentContext = pointer to the identification context structure.
  62. After this function call succeeds, the caller can assume
  63. that pIdentContext->pImageMemory and pIdentContext->ImageSize
  64. are valid and can be used.
  65. Return Value:
  66. Returns STATUS_SUCCESS if a memory-mapped image pointer and size
  67. are now available, otherwise a failure occurred trying to map them.
  68. --*/
  69. {
  70. HANDLE hMapping;
  71. ASSERT(ARGUMENT_PRESENT(pIdentContext) &&
  72. pIdentContext->CodeProps != NULL);
  73. if (pIdentContext->pImageMemory == NULL ||
  74. pIdentContext->ImageSize.QuadPart == 0)
  75. {
  76. //
  77. // If a memory pointer and imagesize were supplied to us
  78. // in the CodeProperties, then just use them directly.
  79. //
  80. if (pIdentContext->CodeProps->ImageSize.QuadPart != 0 &&
  81. pIdentContext->CodeProps->pByteBlock != NULL)
  82. {
  83. pIdentContext->pImageMemory =
  84. pIdentContext->CodeProps->pByteBlock;
  85. pIdentContext->ImageSize.QuadPart =
  86. pIdentContext->CodeProps->ImageSize.QuadPart;
  87. pIdentContext->bImageMemoryNeedUnmap = FALSE;
  88. return STATUS_SUCCESS;
  89. }
  90. //
  91. // Ensure that we have an open file handle, by using the
  92. // handle supplied to us in the CodeProperties if possible,
  93. // otherwise by opening the supplied ImagePath.
  94. //
  95. if (pIdentContext->hFileHandle == NULL) {
  96. // no file handle supplied.
  97. return STATUS_INVALID_PARAMETER; // failed.
  98. }
  99. //
  100. // Get the size of the file. We assume that if we had to
  101. // open the file ourself that the ImageSize cannot be used.
  102. //
  103. if (!GetFileSizeEx(pIdentContext->hFileHandle,
  104. &pIdentContext->ImageSize)) {
  105. return STATUS_ACCESS_DENIED; // failure
  106. }
  107. if (pIdentContext->ImageSize.HighPart != 0) {
  108. //BLACKCOMB TODO: maybe later handle very large files.
  109. return STATUS_NO_MEMORY; // failure--too large.
  110. }
  111. if (pIdentContext->ImageSize.QuadPart == 0) {
  112. return STATUS_INVALID_PARAMETER; // failure--zero file size.
  113. }
  114. //
  115. // Now that we have an open file handle, open it up
  116. // as a memory-mapped file mapping.
  117. //
  118. hMapping = CreateFileMapping(
  119. pIdentContext->hFileHandle,
  120. NULL,
  121. PAGE_READONLY,
  122. (DWORD) 0, // highword zero
  123. (DWORD) pIdentContext->ImageSize.LowPart,
  124. NULL);
  125. if (hMapping == NULL || hMapping == INVALID_HANDLE_VALUE) {
  126. return STATUS_ACCESS_DENIED;
  127. }
  128. //
  129. // View map the file into memory.
  130. //
  131. pIdentContext->pImageMemory =
  132. MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0,
  133. pIdentContext->ImageSize.LowPart);
  134. CloseHandle(hMapping);
  135. if (pIdentContext->pImageMemory == NULL) {
  136. return STATUS_ACCESS_DENIED;
  137. }
  138. pIdentContext->bImageMemoryNeedUnmap = TRUE;
  139. }
  140. return STATUS_SUCCESS;
  141. }
  142. NTSTATUS NTAPI
  143. __CodeAuthzpEnsureUnmapped(
  144. IN OUT PLOCALIDENTITYCONTEXT pIdentContext
  145. )
  146. /*++
  147. Routine Description:
  148. Reverses the effects of __CodeAuthzEnsureMapped and closes and
  149. frees any handles that were opened to the specified file.
  150. Arguments:
  151. pIdentContext - pointer to the context structure.
  152. Return Value:
  153. Returns STATUS_SUCCESS if no errors occurred.
  154. --*/
  155. {
  156. ASSERT(pIdentContext != NULL);
  157. if (pIdentContext->bImageMemoryNeedUnmap &&
  158. pIdentContext->pImageMemory != NULL)
  159. {
  160. UnmapViewOfFile((LPCVOID) pIdentContext->pImageMemory);
  161. pIdentContext->pImageMemory = NULL;
  162. pIdentContext->bImageMemoryNeedUnmap = FALSE;
  163. }
  164. return STATUS_SUCCESS;
  165. }
  166. NTSTATUS NTAPI
  167. CodeAuthzpComputeImageHash(
  168. IN PVOID pImageMemory,
  169. IN DWORD dwImageSize,
  170. OUT PBYTE pComputedHash OPTIONAL,
  171. IN OUT PDWORD pdwHashSize OPTIONAL,
  172. OUT ALG_ID *pHashAlgorithm OPTIONAL
  173. )
  174. /*++
  175. Routine Description:
  176. Computes an MD5 image hash of a specified region of memory.
  177. Note, MD5 hashes are always 16 bytes in length.
  178. Arguments:
  179. pImageMemory - Pointer to a memory buffer to compute the hash of.
  180. dwImageSize - Total size of the pImageMemory buffer in bytes.
  181. pComputedHash - Pointer that receives the computed hash.
  182. pdwHashSize - Pointer to a DWORD value. On input, this DWORD should
  183. specify the maximum size of the pComputedHash buffer.
  184. On successful execution of this function, the length of the
  185. resulting hash is written to this pointer.
  186. pHashAlgorithm - pointer to a variable that will receive the hash
  187. algorithm that was used to compute the hash. This will
  188. always be the constant CALG_MD5.
  189. Return Value:
  190. Returns STATUS_SUCCESS on successful execution.
  191. --*/
  192. {
  193. MD5_CTX md5ctx;
  194. //
  195. // Check the validity of the arguments supplied to us.
  196. //
  197. if (!ARGUMENT_PRESENT(pImageMemory) ||
  198. dwImageSize == 0) {
  199. return STATUS_INVALID_PARAMETER;
  200. }
  201. if (!ARGUMENT_PRESENT(pComputedHash) ||
  202. !ARGUMENT_PRESENT(pdwHashSize) ||
  203. *pdwHashSize < 16) {
  204. return STATUS_BUFFER_TOO_SMALL;
  205. }
  206. //
  207. // Compute the MD5 hash of it.
  208. // (this could also be done with CryptCreateHash+CryptHashData)
  209. //
  210. MD5Init(&md5ctx);
  211. MD5Update(&md5ctx, (LPBYTE) pImageMemory, dwImageSize);
  212. MD5Final(&md5ctx);
  213. //
  214. // Copy the hash to the user's buffer.
  215. //
  216. RtlCopyMemory(pComputedHash, &md5ctx.digest[0], 16);
  217. *pdwHashSize = 16;
  218. if (ARGUMENT_PRESENT(pHashAlgorithm)) {
  219. *pHashAlgorithm = CALG_MD5;
  220. }
  221. return STATUS_SUCCESS;
  222. }
  223. NTSTATUS NTAPI
  224. __CodeAuthzpCheckIdentityPathRules(
  225. IN OUT PLOCALIDENTITYCONTEXT pIdentStruct,
  226. OUT PAUTHZLEVELTABLERECORD *pFoundLevel,
  227. OUT PBOOL pbExactMatch,
  228. OUT PAUTHZIDENTSTABLERECORD *pFoundIdentity
  229. )
  230. /*++
  231. Routine Description:
  232. Evaluates a wildcard pattern against a specified pathname and
  233. indicates if they match.
  234. Arguments:
  235. pIdentStruct -
  236. pFoundLevel - receives a pointer to the authorization Level record
  237. indicated by the best matching rule.
  238. pbExactMatch - receives a boolean value indicating if the match
  239. was against an exact fully qualified path rule.
  240. pFoundIdentity - receives a pointer to the identifier entry rule
  241. that best matched.
  242. Return Value:
  243. Returns STATUS_SUCCESS if a WinSafer Level has been found,
  244. or STATUS_NOT_FOUND if not. Otherwise an error code.
  245. --*/
  246. {
  247. NTSTATUS Status;
  248. PVOID RestartKey;
  249. UNICODE_STRING UnicodePath;
  250. WCHAR ExpandedPath[MAX_PATH];
  251. WCHAR szLongPath[MAX_PATH];
  252. PAUTHZIDENTSTABLERECORD pAuthzIdentRecord, pBestIdentRecord;
  253. PAUTHZLEVELTABLERECORD pAuthzLevelRecord;
  254. LPWSTR lpKeyname = NULL;
  255. LONG lBestLevelDepth;
  256. DWORD dwBestLevelId;
  257. BOOLEAN bFirstPass;
  258. LONG bPathIdentIsBadType = -1; // represents uninit'd state
  259. //
  260. // Verify that our input arguments all make sense.
  261. //
  262. if (!ARGUMENT_PRESENT(pIdentStruct) ||
  263. pIdentStruct->CodeProps == NULL ||
  264. !ARGUMENT_PRESENT(pFoundLevel) ||
  265. !ARGUMENT_PRESENT(pbExactMatch))
  266. {
  267. Status = STATUS_INVALID_PARAMETER;
  268. goto ExitHandler;
  269. }
  270. if ((pIdentStruct->dwCheckFlags & SAFER_CRITERIA_IMAGEPATH) == 0 ||
  271. pIdentStruct->UnicodeFullyQualfiedLongFileName.Buffer == NULL ||
  272. RtlIsGenericTableEmpty(&g_CodeIdentitiesTable))
  273. {
  274. // We're not supposed to evaluate image paths.
  275. Status = STATUS_NOT_FOUND;
  276. goto ExitHandler;
  277. }
  278. ASSERT(g_TableCritSec.OwningThread == UlongToHandle(GetCurrentThreadId()));
  279. //
  280. // Enumerate through all path subkey GUIDs.
  281. //
  282. bFirstPass = TRUE;
  283. RestartKey = NULL;
  284. for (pAuthzIdentRecord = (PAUTHZIDENTSTABLERECORD)
  285. RtlEnumerateGenericTableWithoutSplaying(
  286. &g_CodeIdentitiesTable, &RestartKey);
  287. pAuthzIdentRecord != NULL;
  288. pAuthzIdentRecord = (PAUTHZIDENTSTABLERECORD)
  289. RtlEnumerateGenericTableWithoutSplaying(
  290. &g_CodeIdentitiesTable, &RestartKey)
  291. )
  292. {
  293. if (pAuthzIdentRecord->dwIdentityType ==
  294. SaferIdentityTypeImageName)
  295. {//begin reg key lookup block
  296. LONG lMatchDepth;
  297. //
  298. // Explicitly expand environmental variables.
  299. //
  300. if (pAuthzIdentRecord->ImageNameInfo.bExpandVars) {
  301. #ifdef EXPAND_REGPATH
  302. //This code attempts to expand "path" entries that are really reg keys.
  303. //For example, some paths are install dependent. These paths are commonly written into
  304. //the registry. You can specify a regkey that is a path.
  305. //For example see the following regkeys:
  306. //HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
  307. //HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
  308. //HKEY_CURRENT_USER\Software\Microsoft\Office\9.0\Outlook\Security\OutlookSecureTempFolder
  309. {
  310. LPWSTR lpzRegKey = pAuthzIdentRecord->ImageNameInfo.ImagePath.Buffer;
  311. HKEY hKey=NULL, hKeyHive=NULL;
  312. BOOL bIsCurrentUser = FALSE;
  313. //leading percent does two things:
  314. //1. The rules created will be the Expandable String Type (REG_EXPAND_SZ)
  315. //2. Reduces the chance of a real path name conflict.
  316. LPCWSTR LP_CU_HIVE = L"%HKEY_CURRENT_USER";
  317. LPCWSTR LP_LM_HIVE = L"%HKEY_LOCAL_MACHINE";
  318. BYTE buffer[MAX_PATH *2 + 80];
  319. LPWSTR lpValue=NULL;
  320. DWORD dwBufferSize = sizeof(buffer);
  321. LPWSTR lpHivename;
  322. LPWSTR lpLastPercentSign;
  323. LONG retval;
  324. BOOL bIsRegKey=TRUE;
  325. DWORD dwKeyLength;
  326. //We expect a string like the following:
  327. //%HKEY_CURRENT_USER\Software\Microsoft\Office\9.0\Outlook\Security\OutlookSecureTempFolder%
  328. //We need to break it into three parts for the registry query:
  329. //1. The hive: HKEY_CURRENT_USER
  330. //2. The key name: Software\Microsoft\Office\9.0\Outlook\Security
  331. //3. The value name: OutlookSecureTempFolder
  332. lpKeyname=NULL;
  333. lpValue=NULL;
  334. lpHivename=NULL;
  335. lpLastPercentSign=NULL;
  336. memset(buffer, 0, dwBufferSize);
  337. lpHivename = wcsstr(lpzRegKey, LP_CU_HIVE);
  338. OUTPUTDEBUGSTRING(L"\n");
  339. OUTPUTDEBUGSTRING(L"$");
  340. OUTPUTDEBUGSTRING(lpzRegKey);
  341. OUTPUTDEBUGSTRING(L"\n");
  342. lpLastPercentSign = wcsrchr(lpzRegKey, '%');
  343. //if (lpLastPercentSign != &lpzRegKey[wcslen(lpzRegKey) - 1]) { //needs to end in a '%' as well
  344. //
  345. // we allow %key+valuename%OLK* type paths now
  346. // but there still has to be a matching %
  347. //
  348. if (!lpLastPercentSign) {
  349. bIsRegKey = FALSE;
  350. }
  351. if (bIsRegKey) {
  352. if (lpHivename != NULL) {
  353. hKeyHive = HKEY_CURRENT_USER;
  354. bIsCurrentUser = TRUE;
  355. dwKeyLength = (wcslen(&lpzRegKey[wcslen(LP_CU_HIVE)+1]) +1) * sizeof (WCHAR);
  356. lpKeyname = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, dwKeyLength);
  357. if ( lpKeyname == NULL ) {
  358. Status = STATUS_NO_MEMORY;
  359. goto ForLoopCleanup;
  360. }
  361. wcscpy(lpKeyname, &lpzRegKey[wcslen(LP_CU_HIVE)+1] );
  362. OUTPUTDEBUGSTRING(L"HKEY_CURRENT_USER");
  363. OUTPUTDEBUGSTRING(L"\n");
  364. } else {
  365. lpHivename = wcsstr(lpzRegKey, LP_LM_HIVE);
  366. if (lpHivename != NULL) {
  367. hKeyHive = HKEY_LOCAL_MACHINE;
  368. dwKeyLength = (wcslen(&lpzRegKey[wcslen(LP_LM_HIVE)+1]) +1) * sizeof (WCHAR);
  369. lpKeyname = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, dwKeyLength);
  370. if ( lpKeyname == NULL ) {
  371. Status = STATUS_NO_MEMORY;
  372. goto ForLoopCleanup;
  373. }
  374. wcscpy(lpKeyname, &lpzRegKey[wcslen(LP_LM_HIVE)+1] );
  375. OUTPUTDEBUGSTRING(L"HKEY_LOCAL_MACHINE");
  376. OUTPUTDEBUGSTRING(L"\n");
  377. } else {
  378. //The string is either a path or bogus data
  379. bIsRegKey = FALSE;
  380. }
  381. }
  382. }
  383. if (bIsRegKey) {
  384. lpValue = wcsrchr(lpKeyname, '\\');
  385. if (lpValue==NULL) {
  386. Status = STATUS_NOT_FOUND;
  387. goto ForLoopCleanup;
  388. }
  389. //Take the regkey and value and stick a null terminator in between them.
  390. *lpValue = '\0';
  391. lpValue++;
  392. //lpValue[wcslen(lpValue)-1] = '\0';
  393. //lpLastPercentSign[0] = L'\0'; //replace the final '%' char with a null terminator
  394. lpLastPercentSign = wcsrchr(lpValue, '%');
  395. if (lpLastPercentSign == NULL) {
  396. Status = STATUS_NOT_FOUND;
  397. goto ForLoopCleanup;
  398. }
  399. *lpLastPercentSign = '\0';
  400. //
  401. // Bug# 416461 - causes handle leak so use a different API
  402. // for the user hive
  403. //
  404. if ( bIsCurrentUser ) {
  405. if (retval = RegOpenCurrentUser( KEY_READ, &hKeyHive ) ) {
  406. if ( retval == ERROR_FILE_NOT_FOUND ||
  407. retval == ERROR_NOT_FOUND ||
  408. retval == ERROR_PATH_NOT_FOUND) {
  409. if (lpKeyname) {
  410. HeapFree(GetProcessHeap(), 0,lpKeyname);
  411. lpKeyname = NULL;
  412. }
  413. continue;
  414. }
  415. Status = STATUS_NOT_FOUND;
  416. goto ForLoopCleanup;
  417. }
  418. }
  419. retval = RegOpenKeyEx(hKeyHive,
  420. lpKeyname,
  421. 0,
  422. KEY_READ,
  423. &hKey);
  424. if ( bIsCurrentUser ) {
  425. RegCloseKey(hKeyHive);
  426. }
  427. if (retval)
  428. {
  429. if ( retval == ERROR_FILE_NOT_FOUND ||
  430. retval == ERROR_NOT_FOUND ||
  431. retval == ERROR_PATH_NOT_FOUND) {
  432. if (lpKeyname) {
  433. HeapFree(GetProcessHeap(), 0,lpKeyname);
  434. lpKeyname = NULL;
  435. }
  436. continue;
  437. }
  438. Status = STATUS_NOT_FOUND;
  439. goto ForLoopCleanup;
  440. } else {
  441. OUTPUTDEBUGSTRING(lpKeyname);
  442. OUTPUTDEBUGSTRING(L"\n");
  443. OUTPUTDEBUGSTRING(lpValue);
  444. OUTPUTDEBUGSTRING(L"\n");
  445. if (retval = RegQueryValueEx(hKey,
  446. lpValue,
  447. NULL,
  448. NULL,
  449. buffer,
  450. &dwBufferSize))
  451. {
  452. RegCloseKey(hKey);
  453. Status = STATUS_NOT_FOUND;
  454. goto ForLoopCleanup;
  455. } else {
  456. #ifdef VERBOSE_IDENTIFICATIONS
  457. UNICODE_STRING UnicodeDebug;
  458. WCHAR DebugBuffer[MAX_PATH*2 + 80];
  459. #endif
  460. UNICODE_STRING NewPath;
  461. PUNICODE_STRING pPathFromRule;
  462. //
  463. // if it exists, concatenate the filename after
  464. // i.e. the OLK in %HKEY\somekey\somevalue%OLK
  465. //
  466. if (lpLastPercentSign[1] != L'\0') {
  467. //
  468. // there is some stuff after %HKEY\somekey\somevalue%
  469. //
  470. if (sizeof(buffer) >
  471. ((wcslen((WCHAR*)buffer) + wcslen(lpLastPercentSign+1))* sizeof(WCHAR))) {
  472. WCHAR *pwcBuffer = (WCHAR *)buffer;
  473. if (pwcBuffer[0] != L'\0' &&
  474. pwcBuffer[wcslen(pwcBuffer)-1] != L'\\') {
  475. wcscat((WCHAR*)buffer, L"\\");
  476. }
  477. wcscat((WCHAR*)buffer, lpLastPercentSign+1);
  478. }
  479. }
  480. pPathFromRule=&(pAuthzIdentRecord->ImageNameInfo.ImagePath);
  481. NewPath.Length = (USHORT)wcslen((WCHAR*)buffer) * sizeof(WCHAR);
  482. NewPath.MaximumLength = (USHORT)wcslen((WCHAR*)buffer) * sizeof(WCHAR);
  483. NewPath.Buffer = (PWCHAR)buffer;
  484. #ifdef VERBOSE_IDENTIFICATIONS
  485. RtlInitEmptyUnicodeString(&UnicodeDebug, DebugBuffer, sizeof(DebugBuffer));
  486. swprintf(UnicodeDebug.Buffer, L"pPathFromRule(L,ML,Buffer)=(%d,%d,%s)\n",
  487. pPathFromRule->Length,
  488. pPathFromRule->MaximumLength,
  489. pPathFromRule->Buffer);
  490. OUTPUTDEBUGSTRING(UnicodeDebug.Buffer);
  491. memset(DebugBuffer, '0', sizeof(DebugBuffer));
  492. swprintf(UnicodeDebug.Buffer, L"NewPath(L,ML,Buffer)=(%d,%d,%s)\n",
  493. NewPath.Length,
  494. NewPath.MaximumLength,
  495. NewPath.Buffer);
  496. OUTPUTDEBUGSTRING(UnicodeDebug.Buffer);
  497. #endif
  498. //The new path may be bigger than the current UNICODE_STRING can store. Reallocate if necessary.
  499. if (pPathFromRule->MaximumLength >=
  500. NewPath.Length + sizeof(UNICODE_NULL)) {
  501. RtlCopyUnicodeString(
  502. pPathFromRule,
  503. &NewPath);
  504. } else {
  505. UNICODE_STRING UnicodeExpandedCopy;
  506. Status = RtlDuplicateUnicodeString(
  507. RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
  508. RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING,
  509. &NewPath,
  510. &UnicodeExpandedCopy);
  511. if (NT_SUCCESS(Status)) {
  512. RtlFreeUnicodeString(&pAuthzIdentRecord->ImageNameInfo.ImagePath);
  513. pAuthzIdentRecord->ImageNameInfo.ImagePath = UnicodeExpandedCopy;
  514. }
  515. }
  516. #ifdef VERBOSE_IDENTIFICATIONS
  517. memset(DebugBuffer, '0', sizeof(DebugBuffer));
  518. swprintf(UnicodeDebug.Buffer, L"pPathFromRule after copy(L,ML,Buffer)=(%d,%d,%s)\n",
  519. pPathFromRule->Length,
  520. pPathFromRule->MaximumLength,
  521. pPathFromRule->Buffer);
  522. OUTPUTDEBUGSTRING(UnicodeDebug.Buffer);
  523. #endif
  524. }
  525. }
  526. RegCloseKey(hKey);
  527. }
  528. if (lpKeyname) {
  529. HeapFree(GetProcessHeap(), 0,lpKeyname);
  530. lpKeyname = NULL;
  531. }
  532. } //end reg key lookup block
  533. #endif
  534. // Attempt to expand now.
  535. RtlInitEmptyUnicodeString(
  536. &UnicodePath,
  537. &ExpandedPath[0],
  538. sizeof(ExpandedPath) );
  539. Status = RtlExpandEnvironmentStrings_U(
  540. NULL, // environment
  541. &pAuthzIdentRecord->ImageNameInfo.ImagePath, // unexpanded path
  542. &UnicodePath, // resulting path
  543. NULL); // needed buffer size.
  544. if (!NT_SUCCESS(Status)) {
  545. // Failed to expand environment strings.
  546. continue;
  547. }
  548. // Perf optimization: If the expansion was successful,
  549. // update the table to keep the expanded version, eliminating
  550. // the need to expand the string for any future comparisons.
  551. if (pAuthzIdentRecord->ImageNameInfo.ImagePath.MaximumLength >=
  552. UnicodePath.Length + sizeof(UNICODE_NULL)) {
  553. RtlCopyUnicodeString(
  554. &pAuthzIdentRecord->ImageNameInfo.ImagePath,
  555. &UnicodePath);
  556. pAuthzIdentRecord->ImageNameInfo.bExpandVars = FALSE;
  557. } else {
  558. UNICODE_STRING UnicodeExpandedCopy;
  559. Status = RtlDuplicateUnicodeString(
  560. RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
  561. RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING,
  562. &UnicodePath,
  563. &UnicodeExpandedCopy);
  564. if (NT_SUCCESS(Status)) {
  565. RtlFreeUnicodeString(
  566. &pAuthzIdentRecord->ImageNameInfo.ImagePath);
  567. pAuthzIdentRecord->ImageNameInfo.ImagePath =
  568. UnicodeExpandedCopy;
  569. pAuthzIdentRecord->ImageNameInfo.bExpandVars = FALSE;
  570. }
  571. }
  572. } else {
  573. UnicodePath.Buffer = pAuthzIdentRecord->ImageNameInfo.ImagePath.Buffer;
  574. UnicodePath.Length = pAuthzIdentRecord->ImageNameInfo.ImagePath.Length;
  575. UnicodePath.MaximumLength = pAuthzIdentRecord->ImageNameInfo.ImagePath.MaximumLength;
  576. }
  577. //
  578. // Attempt short -> long filename expansion (if there is a need)
  579. //
  580. szLongPath[0] = L'\0';
  581. //
  582. // unicode buffer is guaranteed to be < MAX_PATH
  583. //
  584. wcsncpy(szLongPath,
  585. pAuthzIdentRecord->ImageNameInfo.ImagePath.Buffer,
  586. pAuthzIdentRecord->ImageNameInfo.ImagePath.Length/sizeof(WCHAR));
  587. szLongPath[pAuthzIdentRecord->ImageNameInfo.ImagePath.Length/sizeof(WCHAR)] = L'\0';
  588. if ( wcschr(szLongPath, L'~') ) {
  589. if (!GetLongPathNameW(szLongPath,
  590. szLongPath,
  591. sizeof(szLongPath) / sizeof(WCHAR))) {
  592. Status = STATUS_VARIABLE_NOT_FOUND;
  593. continue;
  594. }
  595. RtlInitUnicodeString(&UnicodePath, szLongPath);
  596. if (pAuthzIdentRecord->ImageNameInfo.ImagePath.MaximumLength >=
  597. UnicodePath.Length + sizeof(UNICODE_NULL)) {
  598. RtlCopyUnicodeString(
  599. &pAuthzIdentRecord->ImageNameInfo.ImagePath,
  600. &UnicodePath);
  601. } else {
  602. UNICODE_STRING UnicodeExpandedCopy;
  603. Status = RtlDuplicateUnicodeString(
  604. RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
  605. RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING,
  606. &UnicodePath,
  607. &UnicodeExpandedCopy);
  608. if (NT_SUCCESS(Status)) {
  609. RtlFreeUnicodeString(
  610. &pAuthzIdentRecord->ImageNameInfo.ImagePath);
  611. pAuthzIdentRecord->ImageNameInfo.ImagePath =
  612. UnicodeExpandedCopy;
  613. }
  614. }
  615. }
  616. //
  617. // Compute the quality of which the wildcard path identity
  618. // matches the ImagePath property we were asked to evaluate.
  619. //
  620. ASSERT(UnicodePath.Buffer[UnicodePath.Length / sizeof(WCHAR)] == UNICODE_NULL);
  621. ASSERT(pIdentStruct->UnicodeFullyQualfiedLongFileName.Buffer[
  622. pIdentStruct->UnicodeFullyQualfiedLongFileName.Length / sizeof(WCHAR)] == UNICODE_NULL);
  623. lMatchDepth = CodeAuthzpCompareImagePath(UnicodePath.Buffer,
  624. pIdentStruct->UnicodeFullyQualfiedLongFileName.Buffer);
  625. if (!lMatchDepth) continue;
  626. //
  627. // If this path identity is configured to only apply to
  628. // file extensions on the "bad list" then check to see if
  629. // the ImagePath specifies one of the bad extensions.
  630. //
  631. #ifdef AUTHZPOL_SAFERFLAGS_ONLY_EXES
  632. if (lMatchDepth > 0 &&
  633. (pAuthzIdentRecord->ImageNameInfo.dwSaferFlags &
  634. AUTHZPOL_SAFERFLAGS_ONLY_EXES) != 0)
  635. {
  636. if (bPathIdentIsBadType == -1) {
  637. BOOLEAN bResult;
  638. Status = CodeAuthzIsExecutableFileType(
  639. &pIdentStruct->UnicodeFullyQualfiedLongFileName, FALSE,
  640. &bResult );
  641. if (!NT_SUCCESS(Status) || !bResult) {
  642. bPathIdentIsBadType = FALSE;
  643. } else {
  644. bPathIdentIsBadType = TRUE;
  645. }
  646. }
  647. if (!bPathIdentIsBadType) {
  648. // This identity matches against only the "bad"
  649. // extensions, so pretend that this didn't match.
  650. continue;
  651. }
  652. }
  653. #endif
  654. //
  655. // Emit some diagnostic debugging code to show the result
  656. // of all of the path evaluations and their match depths.
  657. //
  658. #ifdef VERBOSE_IDENTIFICATIONS
  659. {
  660. UNICODE_STRING UnicodeDebug;
  661. WCHAR DebugBuffer[MAX_PATH*2 + 80];
  662. // sprintf is for wimps.
  663. RtlInitEmptyUnicodeString(&UnicodeDebug, DebugBuffer, sizeof(DebugBuffer));
  664. RtlAppendUnicodeToString(&UnicodeDebug, L"Safer pattern ");
  665. RtlAppendUnicodeStringToString(&UnicodeDebug, &UnicodePath);
  666. RtlAppendUnicodeToString(&UnicodeDebug, L" matched ");
  667. RtlAppendUnicodeStringToString(&UnicodeDebug, &(pIdentStruct->UnicodeFullyQualfiedLongFileName));
  668. RtlAppendUnicodeToString(&UnicodeDebug, L" with value ");
  669. UnicodeDebug.Buffer += UnicodeDebug.Length / sizeof(WCHAR);
  670. UnicodeDebug.MaximumLength -= UnicodeDebug.Length;
  671. RtlIntegerToUnicodeString(lMatchDepth, 10, &UnicodeDebug);
  672. RtlAppendUnicodeToString(&UnicodeDebug, L"\n");
  673. OUTPUTDEBUGSTRING(DebugBuffer);
  674. }
  675. #endif
  676. //
  677. // Evaluate if this path identity matches better than whatever
  678. // best path identity that we previously had, and keep it if so.
  679. //
  680. if (lMatchDepth < 0) // an exact fully-qualified path!
  681. {
  682. if (bFirstPass ||
  683. lBestLevelDepth >= 0 ||
  684. pAuthzIdentRecord->dwLevelId < dwBestLevelId)
  685. {
  686. pBestIdentRecord = pAuthzIdentRecord;
  687. dwBestLevelId = pAuthzIdentRecord->dwLevelId;
  688. lBestLevelDepth = lMatchDepth;
  689. bFirstPass = FALSE;
  690. }
  691. }
  692. else // an inexact leading prefix path match.
  693. {
  694. ASSERT(lMatchDepth > 0);
  695. if (bFirstPass ||
  696. (lBestLevelDepth >= 0 &&
  697. (lMatchDepth > lBestLevelDepth ||
  698. (lMatchDepth == lBestLevelDepth &&
  699. pAuthzIdentRecord->dwLevelId < dwBestLevelId)
  700. )
  701. )
  702. )
  703. {
  704. pBestIdentRecord = pAuthzIdentRecord;
  705. dwBestLevelId = pAuthzIdentRecord->dwLevelId;
  706. lBestLevelDepth = lMatchDepth;
  707. bFirstPass = FALSE;
  708. }
  709. }
  710. ForLoopCleanup:
  711. if (lpKeyname)
  712. {
  713. HeapFree(GetProcessHeap(), 0,lpKeyname);
  714. lpKeyname = NULL;
  715. }
  716. }
  717. }
  718. //
  719. // If we have identified a matching WinSafer Level then
  720. // look up the Level record for it and return success.
  721. //
  722. if (bFirstPass) {
  723. Status = STATUS_NOT_FOUND;
  724. goto ExitHandler;
  725. }
  726. pAuthzLevelRecord = CodeAuthzLevelObjpLookupByLevelId(
  727. &g_CodeLevelObjTable, dwBestLevelId);
  728. if (!pAuthzLevelRecord) {
  729. Status = STATUS_NOT_FOUND;
  730. goto ExitHandler;
  731. }
  732. *pFoundLevel = pAuthzLevelRecord;
  733. *pbExactMatch = (lBestLevelDepth < 0 ? TRUE : FALSE);
  734. *pFoundIdentity = pBestIdentRecord;
  735. Status = STATUS_SUCCESS;
  736. ExitHandler:
  737. if (lpKeyname)
  738. HeapFree(GetProcessHeap(), 0,lpKeyname);
  739. return Status;
  740. }
  741. NTSTATUS NTAPI
  742. __CodeAuthzpCheckIdentityCertificateRules(
  743. IN OUT PLOCALIDENTITYCONTEXT pIdentStruct,
  744. OUT DWORD *dwExtendedError,
  745. OUT PAUTHZLEVELTABLERECORD *pFoundLevel,
  746. IN DWORD dwUIChoice
  747. )
  748. /*++
  749. Routine Description:
  750. Calls WinVerifyTrust to determine the trust level of the code
  751. signer that has signed a piece of code.
  752. Arguments:
  753. pIdentStruct - context state structure.
  754. dwExtendedError - To return extended error returned by WinVerifyTrust.
  755. pFoundLevel - receives a pointer to the authorization Level record
  756. indicated by the best matching rule.
  757. dwUIChoice - optionally specifies the amount of UI that WinVerifyTrust
  758. is allowed to display. If this argument is 0, then it is treated
  759. as if WTD_UI_ALL had been supplied.
  760. Return Value:
  761. Returns STATUS_SUCCESS if a WinSafer Level has been found,
  762. or STATUS_RETRY if the publisher was unknown and UIflags blocked prompting,
  763. or STATUS_NOT_FOUND if not. Otherwise an error code.
  764. --*/
  765. {
  766. NTSTATUS Status;
  767. PAUTHZLEVELTABLERECORD pLevelRecord;
  768. GUID wvtFileActionID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
  769. WINTRUST_FILE_INFO wvtFileInfo;
  770. WINTRUST_DATA wvtData;
  771. LONG lStatus;
  772. DWORD dwLastError;
  773. DWORD LocalHandleSequenceNumber;
  774. //
  775. // Verify that our input arguments all make sense.
  776. //
  777. if (!ARGUMENT_PRESENT(pIdentStruct) ||
  778. pIdentStruct->CodeProps == NULL ) {
  779. Status = STATUS_INVALID_PARAMETER;
  780. goto ExitHandler;
  781. }
  782. if ((pIdentStruct->dwCheckFlags & SAFER_CRITERIA_AUTHENTICODE) == 0 ||
  783. !ARGUMENT_PRESENT(pIdentStruct->UnicodeFullyQualfiedLongFileName.Buffer)) {
  784. // We're not supposed to evaluate certificates, or the
  785. // filename was not supplied (WinVerifyTrust requires a
  786. // filename, even if an opened handle to is also supplied).
  787. Status = STATUS_NOT_FOUND;
  788. goto ExitHandler;
  789. }
  790. if ( !ARGUMENT_PRESENT(pFoundLevel) ) {
  791. Status = STATUS_ACCESS_VIOLATION;
  792. goto ExitHandler;
  793. }
  794. ASSERT(g_TableCritSec.OwningThread == UlongToHandle(GetCurrentThreadId()));
  795. //
  796. // Prepare the input data structure that WinVerifyTrust expects.
  797. //
  798. RtlZeroMemory(&wvtData, sizeof(WINTRUST_DATA));
  799. wvtData.cbStruct = sizeof(WINTRUST_DATA);
  800. if ((wvtData.dwUIChoice = dwUIChoice) == 0) {
  801. // If the UI choice element was left zero, then assume all UI.
  802. wvtData.dwUIChoice = WTD_UI_ALL;
  803. }
  804. wvtData.dwProvFlags = WTD_SAFER_FLAG; // our magic flag.
  805. wvtData.dwUnionChoice = WTD_CHOICE_FILE;
  806. wvtData.pFile = &wvtFileInfo;
  807. //
  808. // Prepare the input file data structure used by WinVerifyTrust.
  809. //
  810. RtlZeroMemory(&wvtFileInfo, sizeof(WINTRUST_FILE_INFO));
  811. wvtFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
  812. wvtFileInfo.hFile = pIdentStruct->hFileHandle;
  813. wvtFileInfo.pcwszFilePath = pIdentStruct->UnicodeFullyQualfiedLongFileName.Buffer;
  814. //
  815. // Save the global state number.
  816. //
  817. LocalHandleSequenceNumber = g_dwLevelHandleSequence;
  818. //
  819. // Leave the critical section to prevent deadlock with the LoaderLock.
  820. //
  821. RtlLeaveCriticalSection(&g_TableCritSec);
  822. //
  823. // Actually call WinVerifyTrust and save off the return code
  824. // and last error code.
  825. //
  826. lStatus = WinVerifyTrust(
  827. pIdentStruct->CodeProps->hWndParent, // hwnd
  828. &wvtFileActionID,
  829. &wvtData
  830. );
  831. dwLastError = GetLastError();
  832. *dwExtendedError = dwLastError;
  833. //
  834. // Reacquire the lock and check global state.
  835. //
  836. RtlEnterCriticalSection(&g_TableCritSec);
  837. //
  838. // Check the global state and make sure that the tables were not reloaded
  839. // when we were not looking.
  840. //
  841. if (LocalHandleSequenceNumber != g_dwLevelHandleSequence) {
  842. ASSERT(FALSE);
  843. Status = STATUS_INTERNAL_ERROR;
  844. goto ExitHandler;
  845. }
  846. //
  847. // Process the WinVerifyTrust errors per PhilH
  848. //
  849. pLevelRecord = NULL;
  850. if (S_OK == lStatus && TRUST_E_SUBJECT_NOT_TRUSTED != dwLastError) {
  851. //
  852. // The file is signed. The publisher or hash is explicitly trusted
  853. //
  854. pLevelRecord = CodeAuthzLevelObjpLookupByLevelId(
  855. &g_CodeLevelObjTable, SAFER_LEVELID_FULLYTRUSTED);
  856. } else if (TRUST_E_EXPLICIT_DISTRUST == lStatus || TRUST_E_SUBJECT_NOT_TRUSTED == lStatus) {
  857. //
  858. // The publisher is revoked or explicitly untrusted. Alternatively, the hash is
  859. // explicitly untrusted.
  860. //
  861. pLevelRecord = CodeAuthzLevelObjpLookupByLevelId(
  862. &g_CodeLevelObjTable, SAFER_LEVELID_DISALLOWED);
  863. } else {
  864. //
  865. // we won't be too conservative in any of the following cases.
  866. // No explicit trust or untrust. Continue on to other SAFER checks.
  867. //
  868. // TRUST_E_NOSIGNATURE == lStatus
  869. // The file isn't signed. Alternatively for TRUST_E_BAD_DIGEST == dwLastError,
  870. // a signed file has been modified.
  871. // CRYPT_E_SECURITY_SETTINGS == lStatus
  872. // For authenticode downloads, the admin has disabled user UI and trust.
  873. // any other combination of lStatus and dwLastError
  874. // The file is signed. WVT has already called safer to check the hash rules.
  875. Status = STATUS_NOT_FOUND;
  876. goto ExitHandler;
  877. }
  878. if (!pLevelRecord) {
  879. Status = STATUS_ACCESS_DENIED;
  880. } else {
  881. *pFoundLevel = pLevelRecord;
  882. Status = STATUS_SUCCESS;
  883. }
  884. ExitHandler:
  885. return Status;
  886. }
  887. BOOL WINAPI
  888. SaferiSearchMatchingHashRules(
  889. IN ALG_ID HashAlgorithm OPTIONAL,
  890. IN PBYTE pHashBytes,
  891. IN DWORD dwHashSize,
  892. IN DWORD dwOriginalImageSize OPTIONAL,
  893. OUT PDWORD pdwFoundLevel,
  894. OUT PDWORD pdwSaferFlags
  895. )
  896. /*++
  897. Routine Description:
  898. This is a private function that is exported for WinVerifyTrust
  899. to call to determine if a given hash has a WinSafer policy
  900. associated with it.
  901. Because this is a private function that is directly called by
  902. outside code, there is extra work needed to enter the critical
  903. section, reload the policy if needed, and set the value returned
  904. by GetLastError.
  905. Arguments:
  906. HashAlgorithm - specifies the algorithm in which the hash
  907. was computed (CALG_MD5, CALG_SHA, etc).
  908. pHashBytes - pointer to a buffer containing the pre-computed
  909. hash value of the file's contents.
  910. dwHashSize - length indicating the size of the hash value that
  911. is referenced by the pHashBytes argument. For example,
  912. a 128-bit MD5 hash should have a dwHashSize length of 16.
  913. dwOriginalImageSize - Specifies the size of the original file's
  914. contents that are being hashed. This value is used as a
  915. heuristic to minimize the number of comparisons that must
  916. be done to identify a match. If this parameter is 0, then
  917. this heuristic will not be used.
  918. pdwFoundLevel - pointer that receives a DWORD indicating the
  919. WinSafer LevelId that is found. This value is only written
  920. when TRUE is returned.
  921. pdwSaferFlags - pointer that receives a DWORD value containing flags
  922. that control the supression of User-Interface dialogs.
  923. This value is only written when TRUE is returned.
  924. Return Value:
  925. Returns TRUE if a WinSafer Level has been found, or FALSE if not.
  926. If FALSE is returned, GetLastError() may be used to find out
  927. specifics about why no match was found (possibly argument errors).
  928. --*/
  929. {
  930. NTSTATUS Status;
  931. PVOID RestartKey;
  932. PAUTHZIDENTSTABLERECORD pAuthzIdentRecord;
  933. DWORD dwBestLevelId;
  934. DWORD dwBestSaferFlags;
  935. BOOLEAN bFirstPass;
  936. //
  937. // Verify that our input arguments all make sense.
  938. //
  939. if (!ARGUMENT_PRESENT(pHashBytes) ||
  940. dwHashSize < 1) {
  941. Status = STATUS_INVALID_PARAMETER;
  942. goto ExitHandler;
  943. }
  944. if (!ARGUMENT_PRESENT(pdwFoundLevel) ||
  945. !ARGUMENT_PRESENT(pdwSaferFlags)) {
  946. Status = STATUS_ACCESS_VIOLATION;
  947. goto ExitHandler;
  948. }
  949. //
  950. // Enter the critical section and reload the tables if needed.
  951. // Notice that a potential reload is needed here because this
  952. // function is externally called directly.
  953. //
  954. if (!g_bInitializedFirstTime) {
  955. Status = STATUS_UNSUCCESSFUL;
  956. goto ExitHandler;
  957. }
  958. RtlEnterCriticalSection(&g_TableCritSec);
  959. if (g_bNeedCacheReload) {
  960. Status = CodeAuthzpImmediateReloadCacheTables();
  961. if (!NT_SUCCESS(Status)) {
  962. goto ExitHandler2;
  963. }
  964. }
  965. //
  966. // Enumerate through all hash subkey GUIDs.
  967. //
  968. bFirstPass = TRUE;
  969. RestartKey = NULL;
  970. for (pAuthzIdentRecord = (PAUTHZIDENTSTABLERECORD)
  971. RtlEnumerateGenericTableWithoutSplaying(
  972. &g_CodeIdentitiesTable, &RestartKey);
  973. pAuthzIdentRecord != NULL;
  974. pAuthzIdentRecord = (PAUTHZIDENTSTABLERECORD)
  975. RtlEnumerateGenericTableWithoutSplaying(
  976. &g_CodeIdentitiesTable, &RestartKey)
  977. )
  978. {
  979. if (pAuthzIdentRecord->dwIdentityType == SaferIdentityTypeImageHash)
  980. {
  981. //
  982. // Ensure that the hash algorithm is the same type between
  983. // what we were supplied and what we are matching against.
  984. //
  985. if (HashAlgorithm != 0 &&
  986. pAuthzIdentRecord->ImageHashInfo.HashAlgorithm !=
  987. HashAlgorithm) {
  988. continue;
  989. }
  990. //
  991. // If the actual filesize does not match the filesize stored
  992. // with the hash identity then there is no need to perform
  993. // any comparisons involving the hash.
  994. //
  995. if ( dwOriginalImageSize != 0 && dwOriginalImageSize !=
  996. pAuthzIdentRecord->ImageHashInfo.ImageSize.QuadPart ) {
  997. continue;
  998. }
  999. //
  1000. // If the hash doesn't match at all, then go onto the next one.
  1001. //
  1002. if ( dwHashSize != pAuthzIdentRecord->ImageHashInfo.HashSize ||
  1003. !RtlEqualMemory(
  1004. &pAuthzIdentRecord->ImageHashInfo.ImageHash[0],
  1005. &pHashBytes[0], dwHashSize))
  1006. {
  1007. continue;
  1008. }
  1009. //
  1010. // Evaluate if this identity matches better than whatever
  1011. // best path identity that we previously had, and keep it if so.
  1012. //
  1013. if ( bFirstPass ||
  1014. // we didn't have anything before.
  1015. pAuthzIdentRecord->dwLevelId < dwBestLevelId
  1016. // or specifies a less-privileged level.
  1017. )
  1018. {
  1019. dwBestLevelId = pAuthzIdentRecord->dwLevelId;
  1020. dwBestSaferFlags = pAuthzIdentRecord->ImageHashInfo.dwSaferFlags;
  1021. bFirstPass = FALSE;
  1022. }
  1023. }
  1024. }
  1025. //
  1026. // If we have identified a matching WinSafer Level then
  1027. // pass it back and return success.
  1028. //
  1029. if (bFirstPass) {
  1030. Status = STATUS_NOT_FOUND;
  1031. goto ExitHandler2;
  1032. }
  1033. *pdwFoundLevel = dwBestLevelId;
  1034. *pdwSaferFlags = dwBestSaferFlags;
  1035. Status = STATUS_SUCCESS;
  1036. ExitHandler2:
  1037. RtlLeaveCriticalSection(&g_TableCritSec);
  1038. ExitHandler:
  1039. if (NT_SUCCESS(Status)) {
  1040. return TRUE;
  1041. } else {
  1042. BaseSetLastNTError(Status);
  1043. return FALSE;
  1044. }
  1045. }
  1046. NTSTATUS NTAPI
  1047. __CodeAuthzpCheckIdentityHashRules(
  1048. IN OUT PLOCALIDENTITYCONTEXT pIdentStruct,
  1049. OUT PAUTHZLEVELTABLERECORD *pFoundLevel,
  1050. OUT PAUTHZIDENTSTABLERECORD *pFoundIdentity
  1051. )
  1052. /*++
  1053. Routine Description:
  1054. Assumes that the global table lock has already been acquired.
  1055. Arguments:
  1056. pIdentStruct -
  1057. pFoundLevel - receives a pointer to the authorization Level record
  1058. indicated by the best matching rule.
  1059. pFoundIdentity - receives a pointer to the identifier entry rule
  1060. that best matched.
  1061. Return Value:
  1062. Returns STATUS_SUCCESS if a WinSafer Level has been found,
  1063. or STATUS_NOT_FOUND if not. Otherwise an error code.
  1064. --*/
  1065. {
  1066. NTSTATUS Status;
  1067. PVOID RestartKey;
  1068. PAUTHZIDENTSTABLERECORD pAuthzIdentRecord, pBestIdentRecord;
  1069. PAUTHZLEVELTABLERECORD pAuthzLevelRecord;
  1070. DWORD dwBestLevelId;
  1071. BOOLEAN bFirstPass;
  1072. //
  1073. // Verify that our input arguments all make sense.
  1074. //
  1075. if (!ARGUMENT_PRESENT(pIdentStruct) ||
  1076. pIdentStruct->CodeProps == NULL ) {
  1077. Status = STATUS_INVALID_PARAMETER;
  1078. goto ExitHandler;
  1079. }
  1080. if ((pIdentStruct->dwCheckFlags & SAFER_CRITERIA_IMAGEHASH) == 0 ||
  1081. RtlIsGenericTableEmpty(&g_CodeIdentitiesTable)) {
  1082. // We're not supposed to evaluate hashes.
  1083. Status = STATUS_NOT_FOUND;
  1084. goto ExitHandler;
  1085. }
  1086. if ( !ARGUMENT_PRESENT(pFoundLevel) ||
  1087. !ARGUMENT_PRESENT(pFoundIdentity) ) {
  1088. Status = STATUS_ACCESS_VIOLATION;
  1089. goto ExitHandler;
  1090. }
  1091. ASSERT(g_TableCritSec.OwningThread == UlongToHandle(GetCurrentThreadId()));
  1092. //
  1093. // Enumerate through all hash subkey GUIDs.
  1094. //
  1095. bFirstPass = TRUE;
  1096. RestartKey = NULL;
  1097. for (pAuthzIdentRecord = (PAUTHZIDENTSTABLERECORD)
  1098. RtlEnumerateGenericTableWithoutSplaying(
  1099. &g_CodeIdentitiesTable, &RestartKey);
  1100. pAuthzIdentRecord != NULL;
  1101. pAuthzIdentRecord = (PAUTHZIDENTSTABLERECORD)
  1102. RtlEnumerateGenericTableWithoutSplaying(
  1103. &g_CodeIdentitiesTable, &RestartKey)
  1104. )
  1105. {
  1106. if (pAuthzIdentRecord->dwIdentityType == SaferIdentityTypeImageHash)
  1107. {
  1108. HRESULT hr = S_OK;
  1109. //
  1110. // If the user already supplied the pre-computed hash to us,
  1111. // but not the file size, then assume that we do not need
  1112. // to consider the file size when making a comparison.
  1113. //
  1114. if (pIdentStruct->bHaveHash) {
  1115. goto HashComputed;
  1116. }
  1117. //
  1118. // Get the signed hash for the file. If this fails then compute the
  1119. // MD5 hash.
  1120. //
  1121. if ((pIdentStruct->dwCheckFlags & SAFER_CRITERIA_NOSIGNEDHASH) == 0) {
  1122. //
  1123. // Leave the critical section to prevent deadlock with the LoaderLock.
  1124. //
  1125. RtlLeaveCriticalSection(&g_TableCritSec);
  1126. hr = WTHelperGetFileHash(
  1127. pIdentStruct->UnicodeFullyQualfiedLongFileName.Buffer,
  1128. 0,
  1129. NULL,
  1130. &pIdentStruct->FinalHash[0],
  1131. &pIdentStruct->FinalHashSize,
  1132. &pIdentStruct->FinalHashAlgorithm);
  1133. //
  1134. // Reacquire the lock and check global state.
  1135. // Since this is the first thing that we do, it is ok to reacquire
  1136. // the lock safely.
  1137. //
  1138. RtlEnterCriticalSection(&g_TableCritSec);
  1139. if (SUCCEEDED(hr)) {
  1140. pIdentStruct->bHaveHash = TRUE;
  1141. goto HashComputed;
  1142. }
  1143. }
  1144. //
  1145. // If the actual filesize does not match the filesize stored
  1146. // with the hash identity then there is no need to perform
  1147. // any comparisons involving the hash.
  1148. //
  1149. if ( pIdentStruct->ImageSize.QuadPart == 0 )
  1150. {
  1151. // If we don't have the ImageSize yet, then try to
  1152. // open the file and memory map it to find the size.
  1153. Status = __CodeAuthzpEnsureMapped(pIdentStruct);
  1154. if (!NT_SUCCESS(Status)) {
  1155. // If we failed to compute the MD5 sum of this, then that is
  1156. // actually rather bad, but we'll proceed to evaluate any
  1157. // non-MD5 identity rules, since ignoring them could be worse.
  1158. pIdentStruct->dwCheckFlags &= ~SAFER_CRITERIA_IMAGEHASH;
  1159. goto ExitHandler;
  1160. }
  1161. ASSERTMSG("EnsureMapped failed but did not return error",
  1162. pIdentStruct->pImageMemory != NULL &&
  1163. pIdentStruct->ImageSize.QuadPart != 0);
  1164. }
  1165. if ( pAuthzIdentRecord->ImageHashInfo.ImageSize.QuadPart !=
  1166. pIdentStruct->ImageSize.QuadPart) {
  1167. continue;
  1168. }
  1169. //
  1170. // Dynamically compute the MD5 hash of the item if needed.
  1171. //
  1172. if (!pIdentStruct->bHaveHash)
  1173. {
  1174. // Otherwise hash was not supplied, so we must compute it now.
  1175. // Open the file and memory map it.
  1176. Status = __CodeAuthzpEnsureMapped(pIdentStruct);
  1177. if (!NT_SUCCESS(Status)) {
  1178. // If we failed to compute the MD5 sum of this, then
  1179. // that is actually rather bad, but we'll proceed to
  1180. // evaluate any non-MD5 identity rules, since ignoring
  1181. // them could be worse.
  1182. pIdentStruct->dwCheckFlags &= ~SAFER_CRITERIA_IMAGEHASH;
  1183. goto ExitHandler;
  1184. }
  1185. ASSERTMSG("EnsureMapped failed but did not return error",
  1186. pIdentStruct->pImageMemory != NULL &&
  1187. pIdentStruct->ImageSize.QuadPart != 0);
  1188. // We now have a MD5 hash to use.
  1189. pIdentStruct->FinalHashSize =
  1190. sizeof(pIdentStruct->FinalHash);
  1191. Status = CodeAuthzpComputeImageHash(
  1192. pIdentStruct->pImageMemory,
  1193. pIdentStruct->ImageSize.LowPart,
  1194. &pIdentStruct->FinalHash[0],
  1195. &pIdentStruct->FinalHashSize,
  1196. &pIdentStruct->FinalHashAlgorithm);
  1197. if (!NT_SUCCESS(Status)) {
  1198. goto ExitHandler;
  1199. }
  1200. pIdentStruct->bHaveHash = TRUE;
  1201. }
  1202. HashComputed:
  1203. //
  1204. // Ensure that the hash algorithm is the same type between
  1205. // what we were supplied and what we are matching against.
  1206. //
  1207. if ( pIdentStruct->FinalHashAlgorithm != 0 &&
  1208. pAuthzIdentRecord->ImageHashInfo.HashAlgorithm !=
  1209. pIdentStruct->FinalHashAlgorithm) {
  1210. continue;
  1211. }
  1212. //
  1213. // If the hash doesn't match at all, then go onto the next one.
  1214. //
  1215. if ( pIdentStruct->FinalHashSize !=
  1216. pAuthzIdentRecord->ImageHashInfo.HashSize ||
  1217. !RtlEqualMemory(
  1218. &pIdentStruct->FinalHash[0],
  1219. &pAuthzIdentRecord->ImageHashInfo.ImageHash[0],
  1220. pIdentStruct->FinalHashSize))
  1221. {
  1222. continue;
  1223. }
  1224. //
  1225. // Evaluate if this identity matches better than whatever
  1226. // best path identity that we previously had, and keep it if so.
  1227. //
  1228. if ( bFirstPass ||
  1229. // we didn't have anything before.
  1230. pAuthzIdentRecord->dwLevelId < dwBestLevelId
  1231. // same scope, but specifies a less-privileged level.
  1232. )
  1233. {
  1234. pBestIdentRecord = pAuthzIdentRecord;
  1235. dwBestLevelId = pAuthzIdentRecord->dwLevelId;
  1236. bFirstPass = FALSE;
  1237. }
  1238. }
  1239. }
  1240. //
  1241. // If we have identified a matching WinSafer Level then
  1242. // look up the Level record for it and return success.
  1243. //
  1244. if (bFirstPass) {
  1245. Status = STATUS_NOT_FOUND;
  1246. goto ExitHandler;
  1247. }
  1248. pAuthzLevelRecord = CodeAuthzLevelObjpLookupByLevelId(
  1249. &g_CodeLevelObjTable, dwBestLevelId);
  1250. if (!pAuthzLevelRecord) {
  1251. Status = STATUS_NOT_FOUND;
  1252. goto ExitHandler;
  1253. }
  1254. *pFoundLevel = pAuthzLevelRecord;
  1255. *pFoundIdentity = pBestIdentRecord;
  1256. Status = STATUS_SUCCESS;
  1257. ExitHandler:
  1258. return Status;
  1259. }
  1260. NTSTATUS NTAPI
  1261. __CodeAuthzpCheckIdentityUrlZoneRules(
  1262. IN OUT PLOCALIDENTITYCONTEXT pIdentStruct,
  1263. OUT PAUTHZLEVELTABLERECORD *pFoundLevel,
  1264. OUT PAUTHZIDENTSTABLERECORD *pFoundIdentity
  1265. )
  1266. /*++
  1267. Routine Description:
  1268. Arguments:
  1269. pIdentStruct -
  1270. pFoundLevel - receives a pointer to the authorization Level record
  1271. indicated by the best matching rule.
  1272. pFoundIdentity - receives a pointer to the identifier entry rule
  1273. that best matched.
  1274. Return Value:
  1275. Returns STATUS_SUCCESS if a WinSafer Level has been found,
  1276. or STATUS_NOT_FOUND if not. Otherwise an error code.
  1277. --*/
  1278. {
  1279. NTSTATUS Status;
  1280. PVOID RestartKey;
  1281. PAUTHZIDENTSTABLERECORD pAuthzIdentRecord, pBestIdentRecord;
  1282. PAUTHZLEVELTABLERECORD pAuthzLevelRecord;
  1283. DWORD dwBestLevelId;
  1284. BOOLEAN bFirstPass;
  1285. //
  1286. // Verify that our input arguments all make sense.
  1287. //
  1288. if (!ARGUMENT_PRESENT(pIdentStruct) ||
  1289. pIdentStruct->CodeProps == NULL) {
  1290. Status = STATUS_INVALID_PARAMETER;
  1291. goto ExitHandler;
  1292. }
  1293. if ((pIdentStruct->dwCheckFlags & SAFER_CRITERIA_URLZONE) == 0 ||
  1294. RtlIsGenericTableEmpty(&g_CodeIdentitiesTable)) {
  1295. // We're not supposed to evaluate zones.
  1296. Status = STATUS_NOT_FOUND;
  1297. goto ExitHandler;
  1298. }
  1299. if (!ARGUMENT_PRESENT(pFoundLevel) ||
  1300. !ARGUMENT_PRESENT(pFoundIdentity) ) {
  1301. Status = STATUS_ACCESS_VIOLATION;
  1302. goto ExitHandler;
  1303. }
  1304. ASSERT(g_TableCritSec.OwningThread == UlongToHandle(GetCurrentThreadId()));
  1305. //
  1306. // Enumerate through all UrlZone subkey GUIDs.
  1307. //
  1308. RestartKey = NULL;
  1309. bFirstPass = TRUE;
  1310. for (pAuthzIdentRecord = (PAUTHZIDENTSTABLERECORD)
  1311. RtlEnumerateGenericTableWithoutSplaying(
  1312. &g_CodeIdentitiesTable, &RestartKey);
  1313. pAuthzIdentRecord != NULL;
  1314. pAuthzIdentRecord = (PAUTHZIDENTSTABLERECORD)
  1315. RtlEnumerateGenericTableWithoutSplaying(
  1316. &g_CodeIdentitiesTable, &RestartKey)
  1317. )
  1318. {
  1319. if (pAuthzIdentRecord->dwIdentityType ==
  1320. SaferIdentityTypeUrlZone)
  1321. {
  1322. //
  1323. // Compare the identity with what was supplied to us.
  1324. //
  1325. if (pAuthzIdentRecord->ImageZone.UrlZoneId !=
  1326. pIdentStruct->CodeProps->UrlZoneId) {
  1327. // this zone does not match, so ignore it.
  1328. continue;
  1329. }
  1330. //
  1331. // Evaluate if this path identity matches better than whatever
  1332. // best path identity that we previously had, and keep it if so.
  1333. //
  1334. if (bFirstPass ||
  1335. // we didn't have anything better before.
  1336. pAuthzIdentRecord->dwLevelId < dwBestLevelId)
  1337. // this also matches, but specifies a less-privileged level.
  1338. {
  1339. pBestIdentRecord = pAuthzIdentRecord;
  1340. dwBestLevelId = pAuthzIdentRecord->dwLevelId;
  1341. bFirstPass = FALSE;
  1342. }
  1343. }
  1344. }
  1345. //
  1346. // If we have identified a matching WinSafer Level then
  1347. // look up the Level record for it and return success.
  1348. //
  1349. if (bFirstPass) {
  1350. Status = STATUS_NOT_FOUND;
  1351. goto ExitHandler;
  1352. }
  1353. pAuthzLevelRecord = CodeAuthzLevelObjpLookupByLevelId(
  1354. &g_CodeLevelObjTable, dwBestLevelId);
  1355. if (!pAuthzLevelRecord) {
  1356. Status = STATUS_NOT_FOUND;
  1357. goto ExitHandler;
  1358. }
  1359. *pFoundLevel = pAuthzLevelRecord;
  1360. *pFoundIdentity = pBestIdentRecord;
  1361. Status = STATUS_SUCCESS;
  1362. ExitHandler:
  1363. return Status;
  1364. }
  1365. NTSTATUS NTAPI
  1366. __CodeAuthzpIdentifyOneCodeAuthzLevel(
  1367. IN PSAFER_CODE_PROPERTIES pCodeProperties OPTIONAL,
  1368. OUT DWORD *dwExtendedError,
  1369. OUT PAUTHZLEVELTABLERECORD *pBestLevelRecord,
  1370. OUT GUID *pBestIdentGuid
  1371. )
  1372. /*++
  1373. Routine Description:
  1374. Performs the code identification process.
  1375. Assumes that the caller has already locked the global critsec.
  1376. Arguments:
  1377. pCodeProperties - pointer the single CODE_PROPERTIESW structure
  1378. that should be analyzed and evaluated. This parameter
  1379. may be specified as NULL to indicate that there are
  1380. no specific properties that should be evaluated and that
  1381. only the configured Default Level should be used.
  1382. dwExtendedError - In case of certificate rule match, return the extended
  1383. error from WinVerifyTrust.
  1384. pBestLevelRecord - returns the matching WinSafer Level record.
  1385. The value written to this parameter should only be
  1386. considered valid when STATUS_SUCCESS is also returned.
  1387. pBestIdentGuid - returns the matching Code Identity guid from
  1388. which the resulting WinSafer Level was determined.
  1389. The value written to this parameter should only be
  1390. considered valid when STATUS_SUCCESS is also returned.
  1391. This GUID may also be SAFER_GUID_RESULT_TRUSTED_CERT or
  1392. SAFER_GUID_RESULT_DEFAULT_LEVEL to indicate that the result
  1393. was from a publisher cert or a default rule match.
  1394. Note that a cert hash match will also
  1395. Return Value:
  1396. Returns STATUS_SUCCESS if a WinSafer Level has been found,
  1397. or STATUS_NOT_FOUND or another error code if not.
  1398. --*/
  1399. {
  1400. NTSTATUS Status;
  1401. LOCALIDENTITYCONTEXT identStruct = {0};
  1402. //
  1403. // Verify our input state and perform any explicit
  1404. // policy loading, if it hasn't been loaded yet.
  1405. //
  1406. if (!ARGUMENT_PRESENT(pBestLevelRecord) ||
  1407. !ARGUMENT_PRESENT(pBestIdentGuid)) {
  1408. Status = STATUS_ACCESS_VIOLATION;
  1409. goto ExitHandler;
  1410. }
  1411. ASSERT(g_TableCritSec.OwningThread == UlongToHandle(GetCurrentThreadId()));
  1412. //
  1413. // Star the identification process. If no code properties were
  1414. // supplied to us, then we can immediately skip to only
  1415. // considering the default WinSafer Level configurations.
  1416. //
  1417. if (ARGUMENT_PRESENT(pCodeProperties))
  1418. {
  1419. BOOLEAN bRetryCertRuleCheck = FALSE;
  1420. BOOLEAN bPathIsNtNamespace;
  1421. // Current best identity match.
  1422. PAUTHZLEVELTABLERECORD pAuthzLevelRecord = NULL;
  1423. PAUTHZIDENTSTABLERECORD pAuthzIdentRecord;
  1424. // Temporary evaluation identity match.
  1425. BOOL bExactPath;
  1426. PAUTHZLEVELTABLERECORD pTempLevelRecord;
  1427. PAUTHZIDENTSTABLERECORD pTempIdentRecord;
  1428. //
  1429. // Check that the CODE_PROPERTIES structure is the right size.
  1430. //
  1431. if (pCodeProperties->cbSize != sizeof(SAFER_CODE_PROPERTIES)) {
  1432. Status = STATUS_INFO_LENGTH_MISMATCH;
  1433. goto ExitHandler;
  1434. }
  1435. //
  1436. // Initialize the structure that we use to store our
  1437. // stateful information during policy evaluation.
  1438. // We don't copy everything from the CODE_PROPERTIES
  1439. // structure into the identStruct, since some of it
  1440. // is dynamically loaded/copied within "EnsureMapped".
  1441. //
  1442. RtlZeroMemory(&identStruct, sizeof(LOCALIDENTITYCONTEXT));
  1443. identStruct.CodeProps = pCodeProperties;
  1444. identStruct.dwCheckFlags = pCodeProperties->dwCheckFlags;
  1445. identStruct.ImageSize.QuadPart =
  1446. pCodeProperties->ImageSize.QuadPart;
  1447. if (identStruct.ImageSize.QuadPart != 0 &&
  1448. pCodeProperties->dwImageHashSize > 0 &&
  1449. pCodeProperties->dwImageHashSize <= SAFER_MAX_HASH_SIZE)
  1450. {
  1451. // The image hash and filesize were both supplied, therefore
  1452. // we have a valid hash and don't need to compute it ourself.
  1453. RtlCopyMemory(&identStruct.FinalHash[0],
  1454. &pCodeProperties->ImageHash[0],
  1455. pCodeProperties->dwImageHashSize);
  1456. identStruct.FinalHashSize = pCodeProperties->dwImageHashSize;
  1457. identStruct.bHaveHash = TRUE;
  1458. }
  1459. bPathIsNtNamespace = ((identStruct.dwCheckFlags &
  1460. SAFER_CRITERIA_IMAGEPATH_NT) != 0 ? TRUE : FALSE);
  1461. //
  1462. // Copy over the file handle into the context structure, if a
  1463. // handle was supplied, otherwise try to open the filepath.
  1464. //
  1465. if (pCodeProperties->hImageFileHandle != NULL &&
  1466. pCodeProperties->hImageFileHandle != INVALID_HANDLE_VALUE)
  1467. {
  1468. identStruct.hFileHandle = pCodeProperties->hImageFileHandle;
  1469. identStruct.bCloseFileHandle = FALSE;
  1470. }
  1471. else if (pCodeProperties->ImagePath != NULL)
  1472. {
  1473. HANDLE hFile;
  1474. if (bPathIsNtNamespace) {
  1475. UNICODE_STRING UnicodeFilename;
  1476. IO_STATUS_BLOCK IoStatusBlock;
  1477. OBJECT_ATTRIBUTES ObjectAttributes;
  1478. RtlInitUnicodeString(&UnicodeFilename, pCodeProperties->ImagePath);
  1479. InitializeObjectAttributes(
  1480. &ObjectAttributes, &UnicodeFilename,
  1481. OBJ_CASE_INSENSITIVE, NULL, NULL);
  1482. Status = NtOpenFile(&hFile, FILE_GENERIC_READ, &ObjectAttributes,
  1483. &IoStatusBlock, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE);
  1484. if (!NT_SUCCESS(Status)) {
  1485. hFile = NULL;
  1486. }
  1487. } else {
  1488. hFile = CreateFileW(
  1489. pCodeProperties->ImagePath,
  1490. GENERIC_READ,
  1491. FILE_SHARE_READ,
  1492. NULL,
  1493. OPEN_EXISTING,
  1494. FILE_ATTRIBUTE_NORMAL,
  1495. NULL);
  1496. }
  1497. if (hFile != NULL && hFile != INVALID_HANDLE_VALUE) {
  1498. identStruct.hFileHandle = hFile;
  1499. identStruct.bCloseFileHandle = TRUE;
  1500. }
  1501. }
  1502. //
  1503. // Reconstruct the fully qualified pathname from the handle
  1504. // or from the supplied filename.
  1505. //
  1506. Status = CodeAuthzFullyQualifyFilename(
  1507. identStruct.hFileHandle,
  1508. bPathIsNtNamespace,
  1509. pCodeProperties->ImagePath,
  1510. &identStruct.UnicodeFullyQualfiedLongFileName);
  1511. if (!NT_SUCCESS(Status) &&
  1512. pCodeProperties->ImagePath != NULL &&
  1513. !bPathIsNtNamespace)
  1514. {
  1515. // Otherwise just live with what was passed in to us.
  1516. // If allocation fails, then path criteria will just be ignored.
  1517. Status = RtlCreateUnicodeString(
  1518. &identStruct.UnicodeFullyQualfiedLongFileName,
  1519. pCodeProperties->ImagePath);
  1520. }
  1521. //
  1522. // Perform the WinVerifyTrust sequence to see if the signing
  1523. // certificate matches any of the publishers that are in the
  1524. // trusted or distrusted publisher stores. This also has the
  1525. // additional effect of checking the "signed hashes".
  1526. //
  1527. Status = __CodeAuthzpCheckIdentityCertificateRules(
  1528. &identStruct,
  1529. dwExtendedError,
  1530. &pAuthzLevelRecord,
  1531. WTD_UI_NONE);
  1532. if (NT_SUCCESS(Status)) {
  1533. // An exact publisher was found, so return immediately.
  1534. ASSERT(pAuthzLevelRecord != NULL);
  1535. *pBestLevelRecord = pAuthzLevelRecord;
  1536. RtlCopyMemory(pBestIdentGuid,
  1537. &guidTrustedCert, sizeof(GUID));
  1538. goto ExitHandler2;
  1539. } else if (Status != STATUS_NOT_FOUND) {
  1540. goto ExitHandler2;
  1541. }
  1542. //
  1543. // Search hash rules defined for this level/scope.
  1544. // Note that hashes match exactly or not at all,
  1545. // so if we get a positive match, then that level
  1546. // is absolutely returned.
  1547. //
  1548. Status = __CodeAuthzpCheckIdentityHashRules(
  1549. &identStruct,
  1550. &pAuthzLevelRecord,
  1551. &pAuthzIdentRecord);
  1552. if (NT_SUCCESS(Status)) {
  1553. // An exact hash identity was found, so return immediately.
  1554. ASSERT(pAuthzLevelRecord != NULL);
  1555. *pBestLevelRecord = pAuthzLevelRecord;
  1556. RtlCopyMemory(pBestIdentGuid,
  1557. &pAuthzIdentRecord->IdentGuid, sizeof(GUID));
  1558. goto ExitHandler2;
  1559. } else if (Status != STATUS_NOT_FOUND) {
  1560. goto ExitHandler2;
  1561. }
  1562. ASSERT(pAuthzLevelRecord == NULL);
  1563. //
  1564. // Search file path rules defined for this level/scope.
  1565. // Note that file paths can either be an exact match
  1566. // or a partial match. If we find an exact match, then
  1567. // it should be absolutely returned. Otherwise the
  1568. // path was a "grouping match" and we must compare the
  1569. // Level with all of the remaining "grouping checks".
  1570. //
  1571. Status = __CodeAuthzpCheckIdentityPathRules(
  1572. &identStruct,
  1573. &pAuthzLevelRecord,
  1574. &bExactPath,
  1575. &pAuthzIdentRecord);
  1576. if (NT_SUCCESS(Status)) {
  1577. ASSERT(pAuthzLevelRecord != NULL);
  1578. pTempLevelRecord = pAuthzLevelRecord;
  1579. pTempIdentRecord = pAuthzIdentRecord;
  1580. if (bExactPath) {
  1581. *pBestLevelRecord = pTempLevelRecord;
  1582. RtlCopyMemory(pBestIdentGuid,
  1583. &pTempIdentRecord->IdentGuid, sizeof(GUID));
  1584. goto ExitHandler2;
  1585. }
  1586. } else if (Status != STATUS_NOT_FOUND) {
  1587. goto ExitHandler2;
  1588. }
  1589. //
  1590. // Search URL Zone identity rules.
  1591. // Note that zones are always "grouping matches",
  1592. // so they must be compared against all of the remaining
  1593. // "grouping checks".
  1594. //
  1595. Status = __CodeAuthzpCheckIdentityUrlZoneRules(
  1596. &identStruct,
  1597. &pTempLevelRecord,
  1598. &pTempIdentRecord);
  1599. if (NT_SUCCESS(Status)) {
  1600. ASSERT(pTempLevelRecord != NULL);
  1601. if (pAuthzLevelRecord == NULL ||
  1602. pTempLevelRecord->dwLevelId <
  1603. pAuthzLevelRecord->dwLevelId)
  1604. {
  1605. pAuthzLevelRecord = pTempLevelRecord;
  1606. pAuthzIdentRecord = pTempIdentRecord;
  1607. }
  1608. } else if (Status != STATUS_NOT_FOUND) {
  1609. goto ExitHandler2;
  1610. }
  1611. #ifdef SAFER_PROMPT_USER_FOR_DECISION_MAKING
  1612. #error "Prompting user in WinVerifyTrust"
  1613. //
  1614. // We were originally passed UI flag, but we supressed
  1615. // the UI display the first time. Call WinVerifyTrust
  1616. // again and see if user choice would allow code to run.
  1617. //
  1618. if (bRetryCertRuleCheck)
  1619. {
  1620. if (pAuthzLevelRecord != NULL) {
  1621. //If we have a rule match and the rule match is FULLYTRUSTED skip retry.
  1622. if (pAuthzLevelRecord->dwLevelId == SAFER_LEVELID_FULLYTRUSTED) {
  1623. bRetryCertRuleCheck = FALSE;
  1624. }
  1625. } else if (g_DefaultCodeLevel != NULL) {
  1626. //No rule match so far. Check default level.
  1627. //If default level is FULLY_TRUSTED skip retry
  1628. if (g_DefaultCodeLevel->dwLevelId == SAFER_LEVELID_FULLYTRUSTED) {
  1629. bRetryCertRuleCheck = FALSE;
  1630. }
  1631. }
  1632. //
  1633. // Perform the WinVerifyTrust sequence again to see if the signing
  1634. // certificate matches any of the publishers that are in the
  1635. // trusted or distrusted publisher stores.
  1636. //
  1637. if (bRetryCertRuleCheck) {
  1638. Status = __CodeAuthzpCheckIdentityCertificateRules(
  1639. &identStruct,
  1640. &pTempLevelRecord,
  1641. identStruct.CodeProps->dwWVTUIChoice);
  1642. if (NT_SUCCESS(Status)) {
  1643. // User clicked Yes or No. Run it as such.
  1644. ASSERT(pTempLevelRecord != NULL);
  1645. *pBestLevelRecord = pTempLevelRecord;
  1646. RtlCopyMemory(pBestIdentGuid,
  1647. &guidTrustedCert, sizeof(GUID));
  1648. goto ExitHandler2;
  1649. }
  1650. }
  1651. }
  1652. #endif
  1653. //
  1654. // If we found any Level matches at this point, then we
  1655. // should simply return that match. The identified Level
  1656. // will be the MIN() of all "grouping matches" found.
  1657. //
  1658. if (pAuthzLevelRecord != NULL) {
  1659. Status = STATUS_SUCCESS;
  1660. *pBestLevelRecord = pAuthzLevelRecord;
  1661. ASSERT(pAuthzIdentRecord != NULL);
  1662. RtlCopyMemory(pBestIdentGuid,
  1663. &pAuthzIdentRecord->IdentGuid, sizeof(GUID));
  1664. goto ExitHandler2;
  1665. }
  1666. }
  1667. //
  1668. // Now we need to consider the default WinSafer Level and
  1669. // return it if one was defined. If there was no default
  1670. // defined, then we should simply return STATUS_NOT_FOUND.
  1671. //
  1672. if (g_DefaultCodeLevel != NULL) {
  1673. *pBestLevelRecord = g_DefaultCodeLevel;
  1674. RtlCopyMemory(pBestIdentGuid, &guidDefaultRule, sizeof(GUID));
  1675. Status = STATUS_SUCCESS;
  1676. goto ExitHandler2;
  1677. }
  1678. Status = STATUS_NOT_FOUND;
  1679. ExitHandler2:
  1680. __CodeAuthzpEnsureUnmapped(&identStruct);
  1681. if (identStruct.UnicodeFullyQualfiedLongFileName.Buffer != NULL) {
  1682. RtlFreeUnicodeString(&identStruct.UnicodeFullyQualfiedLongFileName);
  1683. }
  1684. if (identStruct.bCloseFileHandle && identStruct.hFileHandle != NULL) {
  1685. NtClose(identStruct.hFileHandle);
  1686. }
  1687. ExitHandler:
  1688. return Status;
  1689. }
  1690. BOOL
  1691. SaferpSkipPolicyForAdmins(VOID)
  1692. /*++
  1693. Routine Description:
  1694. Decides whether or not Safer policy should be skipped.
  1695. Policy is skipped if
  1696. 1. The caller is an Admin AND
  1697. 2. The registry key specifies that the policy should be skipped
  1698. for Admins.
  1699. Arguments:
  1700. Return Value:
  1701. Returns TRUE if a policy should be skipped for admins.
  1702. Returns FALSE otherwise or in case of any intermediate errors.
  1703. --*/
  1704. {
  1705. static BOOL gSaferSkipPolicy = 2;
  1706. BOOL bIsAdmin = FALSE;
  1707. DWORD AdminSid[] = {0x201, 0x5000000, 0x20, 0x220};
  1708. NTSTATUS Status = STATUS_SUCCESS;
  1709. // If we have already evaluated policy once, return the cached value.
  1710. if (2 != gSaferSkipPolicy)
  1711. {
  1712. return gSaferSkipPolicy;
  1713. }
  1714. // Set the default to "will not skip policy"
  1715. gSaferSkipPolicy = 0;
  1716. // Check if the caller is an admin.
  1717. if (CheckTokenMembership(NULL, (PSID) AdminSid, &bIsAdmin))
  1718. {
  1719. // The caller is an Admin. Let's check whether the regkey says it's ok
  1720. // to skip the policy for admins.
  1721. if (bIsAdmin)
  1722. {
  1723. const static UNICODE_STRING SaferUnicodeKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers");
  1724. const static OBJECT_ATTRIBUTES SaferObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&SaferUnicodeKeyName, OBJ_CASE_INSENSITIVE);
  1725. const static UNICODE_STRING SaferPolicyScope = RTL_CONSTANT_STRING(SAFER_POLICY_SCOPE);
  1726. HANDLE hKeyEnabled = NULL;
  1727. BYTE QueryBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 64];
  1728. PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION) QueryBuffer;
  1729. DWORD dwActualSize = 0;
  1730. // Open the CodeIdentifiers key.
  1731. Status = NtOpenKey(
  1732. &hKeyEnabled,
  1733. KEY_QUERY_VALUE,
  1734. (POBJECT_ATTRIBUTES) &SaferObjectAttributes
  1735. );
  1736. if (NT_SUCCESS(Status)) {
  1737. // Read the Policy Scope value.
  1738. Status = NtQueryValueKey(
  1739. hKeyEnabled,
  1740. (PUNICODE_STRING) &SaferPolicyScope,
  1741. KeyValuePartialInformation,
  1742. pKeyValueInfo,
  1743. sizeof(QueryBuffer),
  1744. &dwActualSize
  1745. );
  1746. NtClose(hKeyEnabled);
  1747. // Skip policy if the flag is set to 1.
  1748. if (NT_SUCCESS(Status)) {
  1749. if ((pKeyValueInfo->Type == REG_DWORD) &&
  1750. (pKeyValueInfo->DataLength == sizeof(DWORD)) &&
  1751. (*((PDWORD) pKeyValueInfo->Data) & 0x1)) {
  1752. gSaferSkipPolicy = 1;
  1753. }
  1754. }
  1755. }
  1756. }
  1757. }
  1758. return gSaferSkipPolicy;
  1759. }
  1760. VOID
  1761. SaferpLogResultsToFile(
  1762. LPWSTR InputImageName,
  1763. LPWSTR LevelName,
  1764. LPWSTR RuleTypeName,
  1765. GUID *Guid
  1766. )
  1767. /*++
  1768. Routine Description:
  1769. Logs a message to a file specified in
  1770. HKLM\Software\Policies\Microsoft\Windows\Safer\CodeIdentifiers LogFileName.
  1771. The format of the message is:
  1772. TLIST.EXE (PID = 1076) identified C:\SAFERTEST\TEST.VBS as FULLY TRUSTED
  1773. using CERTIFICATE rul, Guid = {abcdef00-abcd-abcd-abcdefabcdef00}
  1774. Arguments:
  1775. Return Value:
  1776. --*/
  1777. {
  1778. #define SAFER_LOG_NAME1 L" (PID = "
  1779. #define SAFER_LOG_NAME2 L") identified "
  1780. #define SAFER_LOG_NAME3 L" as "
  1781. #define SAFER_LOG_NAME4 L" using "
  1782. #define SAFER_LOG_NAME5 L" rule, Guid = "
  1783. #define SAFER_INTEGER_LENGTH 20
  1784. #define SAFER_MAX_RULE_DESCRIPTION_LENGTH 12
  1785. #define SAFER_GUID_LENGTH 38
  1786. NTSTATUS Status = STATUS_SUCCESS;
  1787. HANDLE hFile = NULL;
  1788. HANDLE hKey = NULL;
  1789. ULONG ProcessNameLength = 0;
  1790. PWCHAR Buffer = NULL;
  1791. ULONG BasicInfoLength = 0;
  1792. ULONG BytesWritten = 0;
  1793. UCHAR TmpBuf[] = {0xFF, 0xFE};
  1794. const static UNICODE_STRING SaferUnicodeKeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers");
  1795. const static OBJECT_ATTRIBUTES SaferObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&SaferUnicodeKeyName, OBJ_CASE_INSENSITIVE);
  1796. const static UNICODE_STRING SaferPolicyScope = RTL_CONSTANT_STRING(SAFER_LOGFILE_NAME);
  1797. PROCESS_BASIC_INFORMATION ProcInfo = {0};
  1798. ULONG TotalSize = sizeof(SAFER_LOG_NAME1) +
  1799. sizeof(SAFER_LOG_NAME2) +
  1800. sizeof(SAFER_LOG_NAME3) +
  1801. sizeof(SAFER_LOG_NAME4) +
  1802. sizeof(SAFER_LOG_NAME5) +
  1803. ((SAFER_INTEGER_LENGTH +
  1804. SAFER_MAX_RULE_DESCRIPTION_LENGTH +
  1805. SAFER_GUID_LENGTH) * sizeof(WCHAR));
  1806. UCHAR QueryBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + MAX_PATH * sizeof(WCHAR)];
  1807. PWCHAR ProcessImageName = NULL;
  1808. PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION) QueryBuffer;
  1809. DWORD dwActualSize = 0;
  1810. // Open the CodeIdentifiers key.
  1811. Status = NtOpenKey(
  1812. &hKey,
  1813. KEY_QUERY_VALUE,
  1814. (POBJECT_ATTRIBUTES) &SaferObjectAttributes
  1815. );
  1816. if (!NT_SUCCESS(Status)) {
  1817. return;
  1818. }
  1819. // Read the name of the file for logging.
  1820. Status = NtQueryValueKey(
  1821. hKey,
  1822. (PUNICODE_STRING) &SaferPolicyScope,
  1823. KeyValuePartialInformation,
  1824. pKeyValueInfo,
  1825. sizeof(QueryBuffer),
  1826. &dwActualSize
  1827. );
  1828. NtClose(hKey);
  1829. // We do not care if the buffer size was too small to retrieve the logfile
  1830. // name since this is for troubleshooting.
  1831. if (!NT_SUCCESS(Status)) {
  1832. return;
  1833. }
  1834. // This was not a string.
  1835. if (pKeyValueInfo->Type != REG_SZ) {
  1836. return;
  1837. }
  1838. hFile = CreateFileW((LPCWSTR) pKeyValueInfo->Data, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
  1839. if (hFile == INVALID_HANDLE_VALUE) {
  1840. return;
  1841. }
  1842. SetFilePointer (hFile, 0, NULL, FILE_BEGIN);
  1843. WriteFile (hFile, (LPCVOID)TmpBuf, 2, &BytesWritten, NULL);
  1844. SetFilePointer (hFile, 0, NULL, FILE_END);
  1845. Status = NtQueryInformationProcess(NtCurrentProcess(), ProcessImageFileName, QueryBuffer, sizeof(QueryBuffer), &ProcessNameLength);
  1846. if (!NT_SUCCESS(Status)) {
  1847. goto Cleanup;
  1848. }
  1849. ProcessImageName = (PWCHAR) (QueryBuffer + ProcessNameLength - sizeof(WCHAR));
  1850. ProcessNameLength = 1;
  1851. while (((PUCHAR) ProcessImageName >= QueryBuffer) && (*(ProcessImageName - 1) != L'\\')) {
  1852. ProcessImageName--;
  1853. ProcessNameLength++;
  1854. }
  1855. TotalSize += (ProcessNameLength + (wcslen(InputImageName) + wcslen(LevelName)) * sizeof(WCHAR));
  1856. Status = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, (PVOID) &ProcInfo, sizeof(PROCESS_BASIC_INFORMATION), &BasicInfoLength);
  1857. if (!NT_SUCCESS(Status)) {
  1858. goto Cleanup;
  1859. }
  1860. Buffer = (PWCHAR) RtlAllocateHeap(RtlProcessHeap(), 0, TotalSize);
  1861. if (Buffer == NULL) {
  1862. goto Cleanup;
  1863. }
  1864. swprintf(Buffer, L"%s%s%d%s%s%s%s%s%s%s{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\r\n",
  1865. ProcessImageName,
  1866. SAFER_LOG_NAME1,
  1867. ProcInfo.UniqueProcessId,
  1868. SAFER_LOG_NAME2,
  1869. InputImageName,
  1870. SAFER_LOG_NAME3,
  1871. LevelName,
  1872. SAFER_LOG_NAME4,
  1873. RuleTypeName,
  1874. SAFER_LOG_NAME5,
  1875. Guid->Data1, Guid->Data2, Guid->Data3, Guid->Data4[0], Guid->Data4[1], Guid->Data4[2], Guid->Data4[3], Guid->Data4[4], Guid->Data4[5], Guid->Data4[6], Guid->Data4[7]);
  1876. ASSERT((wcslen(Buffer) + 1) * sizeof(WCHAR) < TotalSize);
  1877. WriteFile (hFile, (LPCVOID)Buffer, (wcslen(Buffer) * sizeof(WCHAR)), &BytesWritten, NULL);
  1878. RtlFreeHeap(RtlProcessHeap(), 0, Buffer);
  1879. Cleanup:
  1880. CloseHandle(hFile);
  1881. }
  1882. #define USER_REGISTRY_EXT_MUTEX TEXT("userenv: User Registry policy mutex")
  1883. #define MACH_REGISTRY_EXT_MUTEX TEXT("Global\\userenv: Machine Registry policy mutex")
  1884. HANDLE
  1885. SaferpEnterCriticalPolicySection(
  1886. IN PWCHAR wszMutex,
  1887. IN DWORD dwTimeOut
  1888. )
  1889. /*++
  1890. Routine Description:
  1891. Acquires the machine/user mutex.
  1892. Arguments:
  1893. wszMutex - Name of the mutex to acquire.
  1894. dwTimeOut - Time for we will wait in case policy processing is going on.
  1895. Return Value:
  1896. Returns the handle to the mutex. In case of errors, the return value in ZERO.
  1897. --*/
  1898. {
  1899. HANDLE hSection;
  1900. DWORD dwRet;
  1901. //
  1902. // Open the mutex to wait on.
  1903. //
  1904. hSection = OpenMutex(SYNCHRONIZE, FALSE, wszMutex);
  1905. if (!hSection) {
  1906. return NULL;
  1907. }
  1908. //
  1909. // Claim the mutex
  1910. //
  1911. dwRet = WaitForSingleObject(hSection, dwTimeOut);
  1912. if ((dwRet == WAIT_FAILED) || (dwRet == WAIT_TIMEOUT)) {
  1913. CloseHandle(hSection);
  1914. return NULL;
  1915. }
  1916. return hSection;
  1917. }
  1918. BOOL
  1919. SaferpCheckKeyStamp(
  1920. IN HKEY Key
  1921. )
  1922. /*++
  1923. Routine Description:
  1924. Compare the last write stamp on the CodeIdentifiers key vs the last time
  1925. we loaded Safer policy.
  1926. Arguments:
  1927. Key - Handle to HKLM or HKCU.
  1928. Return Value:
  1929. Returns TRUE if policy needs to be reloaded.
  1930. --*/
  1931. {
  1932. #define SAFERP_KEY_NAME L"Software\\Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers"
  1933. NTSTATUS Status;
  1934. HKEY SubKey = NULL;
  1935. DWORD SizeIgnore = 0;
  1936. WCHAR Buffer[MAX_PATH];
  1937. PKEY_BASIC_INFORMATION KeyInfo = (PKEY_BASIC_INFORMATION) Buffer;
  1938. //
  1939. // Open the CodeIdentifiers key.
  1940. //
  1941. DWORD dwErr = RegOpenKeyW(Key, SAFERP_KEY_NAME, &SubKey);
  1942. if (dwErr != ERROR_SUCCESS) {
  1943. return FALSE;
  1944. }
  1945. //
  1946. // Query the key for basic information.
  1947. //
  1948. Status = NtQueryKey(SubKey, KeyBasicInformation, KeyInfo, sizeof(Buffer), &SizeIgnore);
  1949. CloseHandle(SubKey);
  1950. if (!NT_SUCCESS(Status)) {
  1951. return FALSE;
  1952. }
  1953. //
  1954. // Compare the timestmaps.
  1955. //
  1956. if (-1 == CompareFileTime((FILETIME *) &g_SaferPolicyTimeStamp, (FILETIME *) &KeyInfo->LastWriteTime)) {
  1957. return TRUE;
  1958. }
  1959. return FALSE;
  1960. }
  1961. NTSTATUS
  1962. SaferpGrabGroupPolicyLocks(
  1963. OUT PHANDLE phMachine,
  1964. OUT PHANDLE phUser
  1965. )
  1966. /*++
  1967. Routine Description:
  1968. Acquire both user and machine locks.
  1969. Arguments:
  1970. phMachine - To return the handle to machine mutex.
  1971. phUser - To return the handle to user mutex.
  1972. Return Value:
  1973. Returns STATUS_SUCCESS if both mutexes have been acquired.
  1974. --*/
  1975. {
  1976. //
  1977. // Observe the locking order - machine first, then user.
  1978. //
  1979. *phMachine = SaferpEnterCriticalPolicySection(MACH_REGISTRY_EXT_MUTEX, 0);
  1980. if (*phMachine != NULL) {
  1981. //
  1982. // We got the machine lock. Now let's get the user lock.
  1983. //
  1984. *phUser = SaferpEnterCriticalPolicySection(USER_REGISTRY_EXT_MUTEX, 0);
  1985. if (*phUser != NULL) {
  1986. return STATUS_SUCCESS;
  1987. }
  1988. //
  1989. // If we could not get the User lock, release the machine lock.
  1990. //
  1991. ReleaseMutex (*phMachine);
  1992. CloseHandle (*phMachine);
  1993. *phMachine = NULL;
  1994. }
  1995. return STATUS_ACCESS_DENIED;
  1996. }
  1997. VOID
  1998. SaferpReleaseGroupPolicyLocks(
  1999. IN HANDLE hMachine,
  2000. IN HANDLE hUser
  2001. )
  2002. /*++
  2003. Routine Description:
  2004. Release both user and machine locks.
  2005. Arguments:
  2006. hMachine - Machine handle to release.
  2007. hUser - User handle to release.
  2008. Return Value:
  2009. None.
  2010. --*/
  2011. {
  2012. if (hUser)
  2013. {
  2014. ReleaseMutex(hUser);
  2015. CloseHandle(hUser);
  2016. }
  2017. if (hMachine)
  2018. {
  2019. ReleaseMutex(hMachine);
  2020. CloseHandle(hMachine);
  2021. }
  2022. }
  2023. NTSTATUS
  2024. SaferpReloadPolicyIfNeeded()
  2025. /*++
  2026. Routine Description:
  2027. Reload Safer policy in case the time stamp on Safer keys is more recent than
  2028. the last time we loaded policy.
  2029. Arguments:
  2030. None.
  2031. Return Value:
  2032. Returns STATUS_SUCCESS in case we fail to load the policy after we have decided
  2033. to do so. No failure is returned if we encounter an error before that since
  2034. we already have a cached one.
  2035. --*/
  2036. {
  2037. NTSTATUS Status;
  2038. HANDLE hUser = NULL;
  2039. HANDLE hMachine = NULL;
  2040. //
  2041. // Check if Machine policy has changed.
  2042. //
  2043. if (!SaferpCheckKeyStamp(HKEY_LOCAL_MACHINE))
  2044. {
  2045. //
  2046. // Machine policy has not changed.
  2047. // Check if User policy has changed.
  2048. //
  2049. if (!SaferpCheckKeyStamp(HKEY_CURRENT_USER))
  2050. {
  2051. //
  2052. // Neither policy has changed. Just return.
  2053. //
  2054. return STATUS_SUCCESS;
  2055. }
  2056. }
  2057. //
  2058. // Check if it is ok to load policy.
  2059. //
  2060. Status = SaferpGrabGroupPolicyLocks(&hMachine, &hUser);
  2061. if (!NT_SUCCESS(Status)) {
  2062. //
  2063. // Failure to grab the locks is ok here. We will just return.
  2064. //
  2065. Status = STATUS_SUCCESS;
  2066. goto Cleanup;
  2067. }
  2068. //
  2069. // Release the current set of rules.
  2070. //
  2071. CodeAuthzGuidIdentsEntireTableFree(&g_CodeIdentitiesTable);
  2072. CodeAuthzLevelObjpEntireTableFree(&g_CodeLevelObjTable);
  2073. //
  2074. // And reload the policy.
  2075. //
  2076. g_bNeedCacheReload = TRUE;
  2077. Status = CodeAuthzpImmediateReloadCacheTables();
  2078. if (!NT_SUCCESS(Status)) {
  2079. ASSERT(FALSE);
  2080. }
  2081. Cleanup:
  2082. //
  2083. // Leave the group policy locks.
  2084. //
  2085. SaferpReleaseGroupPolicyLocks(hMachine, hUser);
  2086. return Status;
  2087. }
  2088. BOOL WINAPI
  2089. SaferIdentifyLevel(
  2090. IN DWORD dwNumProperties,
  2091. IN PSAFER_CODE_PROPERTIES pCodeProperties,
  2092. OUT SAFER_LEVEL_HANDLE *pLevelHandle,
  2093. IN LPVOID lpReserved
  2094. )
  2095. /*++
  2096. Routine Description:
  2097. Performs the code identification process. Accepts an array of
  2098. CODE_PROPERTIES structures that supply all of the identification
  2099. criteria. The final result is the least privileged match resulting
  2100. from each element of the array.
  2101. Arguments:
  2102. dwNumProperties - indicates the number of CODE_PROPERTIES structures
  2103. pointed to by the CodeProperties argument.
  2104. pCodeProperties - pointer to one or more structures that specify
  2105. all of the input criteria that will be used to identify level.
  2106. pLevelHandle - pointer that will receive the opened Level object
  2107. handle when the identification operation is successful.
  2108. lpReserved - unused, must be zero.
  2109. Return Value:
  2110. Returns TRUE if a Level was identified and an opened handle
  2111. to it stored in the 'LevelHandle' argument. Otherwise this
  2112. function returns FALSE on error and GetLastError() can be used
  2113. to obtain additional information about the error.
  2114. --*/
  2115. {
  2116. DWORD Index;
  2117. NTSTATUS Status;
  2118. BOOL ReturnValue = FALSE;
  2119. PAUTHZLEVELTABLERECORD pBestLevelRecord;
  2120. GUID BestIdentGuid;
  2121. PWCHAR LocalLevelName = L"\"default\"";
  2122. PWCHAR LocalRuleName = L"default";
  2123. PWCHAR LocalImageName = L"Default";
  2124. DWORD dwExtendedError = ERROR_SUCCESS;
  2125. BOOL bCheckPolicyPropagation = TRUE;
  2126. //
  2127. // Validate the input parameters.
  2128. //
  2129. UNREFERENCED_PARAMETER(lpReserved);
  2130. if (!ARGUMENT_PRESENT(pLevelHandle)) {
  2131. Status = STATUS_ACCESS_VIOLATION;
  2132. goto ExitHandler;
  2133. }
  2134. if (!g_bInitializedFirstTime) {
  2135. Status = STATUS_UNSUCCESSFUL;
  2136. goto ExitHandler;
  2137. }
  2138. RtlEnterCriticalSection(&g_TableCritSec);
  2139. if (g_bNeedCacheReload) {
  2140. Status = CodeAuthzpImmediateReloadCacheTables();
  2141. if (!NT_SUCCESS(Status)) {
  2142. goto ExitHandler2;
  2143. }
  2144. bCheckPolicyPropagation = FALSE;
  2145. }
  2146. if (RtlIsGenericTableEmpty(&g_CodeLevelObjTable)) {
  2147. // There are no levels defined! Should not happen.
  2148. Status = STATUS_NOT_FOUND;
  2149. goto ExitHandler2;
  2150. }
  2151. //
  2152. // Do not allow filehandles unless filename is specified.
  2153. //
  2154. for (Index = 0; Index < dwNumProperties; Index++)
  2155. {
  2156. if (pCodeProperties[Index].hImageFileHandle != NULL &&
  2157. pCodeProperties[Index].hImageFileHandle != INVALID_HANDLE_VALUE &&
  2158. pCodeProperties[Index].ImagePath == NULL)
  2159. {
  2160. Status = STATUS_INVALID_PARAMETER;
  2161. goto ExitHandler2;
  2162. }
  2163. }
  2164. if (SaferpSkipPolicyForAdmins())
  2165. {
  2166. pBestLevelRecord = CodeAuthzLevelObjpLookupByLevelId(
  2167. &g_CodeLevelObjTable, SAFER_LEVELID_FULLYTRUSTED);
  2168. RtlCopyMemory(&BestIdentGuid, &guidDefaultRule, sizeof(GUID));
  2169. goto GotMatchingRule;
  2170. }
  2171. if (!ARGUMENT_PRESENT(pCodeProperties) || dwNumProperties == 0) {
  2172. // We were given no criteria to evaluate, so just return
  2173. // the default level. If there was no default defined,
  2174. // then we should simply return STATUS_NOT_FOUND.
  2175. if (g_DefaultCodeLevel != NULL) {
  2176. pBestLevelRecord = g_DefaultCodeLevel;
  2177. RtlCopyMemory(&BestIdentGuid, &guidDefaultRule, sizeof(GUID));
  2178. goto GotMatchingRule;
  2179. } else {
  2180. Status = STATUS_NOT_FOUND;
  2181. goto ExitHandler2;
  2182. }
  2183. }
  2184. //
  2185. // If we did not reload policy in this call, check if we need to.
  2186. //
  2187. if (bCheckPolicyPropagation)
  2188. {
  2189. //
  2190. // Make sure that we do not have any outstanding handles.
  2191. //
  2192. if (0 == g_dwNumHandlesAllocated)
  2193. {
  2194. //
  2195. // Get the last time policy was propagated.
  2196. //
  2197. Status = SaferpReloadPolicyIfNeeded();
  2198. if (!NT_SUCCESS(Status))
  2199. {
  2200. goto ExitHandler2;
  2201. }
  2202. }
  2203. }
  2204. //
  2205. // Iterate through the list of CODE_PROPERTIES supplied
  2206. // and determine the final code Level that should be used.
  2207. //
  2208. pBestLevelRecord = NULL;
  2209. for (Index = 0; Index < dwNumProperties; Index++)
  2210. {
  2211. PAUTHZLEVELTABLERECORD pOneLevelRecord;
  2212. GUID OneIdentGuid;
  2213. Status = __CodeAuthzpIdentifyOneCodeAuthzLevel(
  2214. &pCodeProperties[Index],
  2215. &dwExtendedError,
  2216. &pOneLevelRecord,
  2217. &OneIdentGuid);
  2218. if (NT_SUCCESS(Status)) {
  2219. ASSERT(pOneLevelRecord != NULL);
  2220. if (!pBestLevelRecord ||
  2221. pOneLevelRecord->dwLevelId <
  2222. pBestLevelRecord->dwLevelId )
  2223. {
  2224. pBestLevelRecord = pOneLevelRecord;
  2225. RtlCopyMemory(&BestIdentGuid, &OneIdentGuid, sizeof(GUID));
  2226. }
  2227. } else if (Status != STATUS_NOT_FOUND) {
  2228. // An unexpected error occurred, so return that.
  2229. goto ExitHandler2;
  2230. }
  2231. }
  2232. if (pBestLevelRecord == NULL) {
  2233. Status = STATUS_NOT_FOUND;
  2234. goto ExitHandler2;
  2235. }
  2236. //
  2237. // Now we have the result so pass back a handle to the
  2238. // identified WinSafer Level.
  2239. // Allocate a handle to represent this level.
  2240. //
  2241. GotMatchingRule:
  2242. ASSERT(pBestLevelRecord != NULL);
  2243. if (IsEqualGUID(&guidDefaultRule, &BestIdentGuid))
  2244. {
  2245. // The resulting level match came from the default rule.
  2246. // Now we have to try to guess whether the default actually
  2247. // came from the Machine or User scope.
  2248. DWORD dwScopeId;
  2249. if (g_hKeyCustomRoot != NULL) {
  2250. dwScopeId = SAFER_SCOPEID_REGISTRY;
  2251. } else if (g_DefaultCodeLevelUser != NULL &&
  2252. g_DefaultCodeLevelUser->dwLevelId ==
  2253. pBestLevelRecord->dwLevelId) {
  2254. dwScopeId = SAFER_SCOPEID_USER;
  2255. } else {
  2256. dwScopeId = SAFER_SCOPEID_MACHINE;
  2257. }
  2258. Status = CodeAuthzpCreateLevelHandleFromRecord(
  2259. pBestLevelRecord, // pLevelRecord
  2260. dwScopeId, // dwScopeId
  2261. 0, // dwSaferFlags
  2262. dwExtendedError,
  2263. SaferIdentityDefault,
  2264. &BestIdentGuid, // pIdentRecord
  2265. pLevelHandle // pLevelHandle
  2266. );
  2267. }
  2268. else if (IsEqualGUID(&guidTrustedCert, &BestIdentGuid))
  2269. {
  2270. // Note that when the result is from a certificate, we have
  2271. // no way of actually knowing whether the certificate was
  2272. // defined within the Machine or User scope, so we'll just
  2273. // arbitrarily pick the Machine scope for the handle to be
  2274. // based out of. Additionally, there are no SaferFlags
  2275. // persisted for certificates so we just assume 0.
  2276. Status = CodeAuthzpCreateLevelHandleFromRecord(
  2277. pBestLevelRecord, // pLevelRecord
  2278. SAFER_SCOPEID_MACHINE, // dwScopeId
  2279. 0, // dwSaferFlags
  2280. dwExtendedError,
  2281. SaferIdentityTypeCertificate,
  2282. &BestIdentGuid, // pIdentRecord
  2283. pLevelHandle // pLevelHandle
  2284. );
  2285. LocalRuleName = L"certificate";
  2286. }
  2287. else
  2288. {
  2289. // Otherwise the result must have come from a path, hash,
  2290. // or zone rule, so we must look up the resulting GUID in our
  2291. // identity table and retrieve the SaferFlags that were stored
  2292. // along with that Identity record. But we won't panic if we
  2293. // can't actually find the GUID anymore (even though that should
  2294. // not ever be the case while we have the critical section).
  2295. PAUTHZIDENTSTABLERECORD pBestIdentRecord;
  2296. DWORD dwSaferFlags = 0;
  2297. SAFER_IDENTIFICATION_TYPES LocalIdentificationType = SaferIdentityDefault;
  2298. pBestIdentRecord = CodeAuthzIdentsLookupByGuid(
  2299. &g_CodeIdentitiesTable, &BestIdentGuid);
  2300. if (pBestIdentRecord != NULL) {
  2301. // we identified a level, and the match came from a Identity.
  2302. switch (pBestIdentRecord->dwIdentityType) {
  2303. case SaferIdentityTypeImageName:
  2304. dwSaferFlags = pBestIdentRecord->ImageNameInfo.dwSaferFlags;
  2305. LocalRuleName = L"path";
  2306. LocalIdentificationType = SaferIdentityTypeImageName;
  2307. break;
  2308. case SaferIdentityTypeImageHash:
  2309. dwSaferFlags = pBestIdentRecord->ImageHashInfo.dwSaferFlags;
  2310. LocalRuleName = L"hash";
  2311. LocalIdentificationType = SaferIdentityTypeImageHash;
  2312. break;
  2313. case SaferIdentityTypeUrlZone:
  2314. dwSaferFlags = pBestIdentRecord->ImageZone.dwSaferFlags;
  2315. LocalRuleName = L"zone";
  2316. LocalIdentificationType = SaferIdentityTypeUrlZone;
  2317. break;
  2318. default: break;
  2319. }
  2320. Status = CodeAuthzpCreateLevelHandleFromRecord(
  2321. pBestLevelRecord, // pLevelRecord
  2322. pBestIdentRecord->dwScopeId,
  2323. dwSaferFlags, // dwSaferFlags
  2324. dwExtendedError,
  2325. LocalIdentificationType,
  2326. &BestIdentGuid, // pIdentRecord
  2327. pLevelHandle // pLevelHandle
  2328. );
  2329. }
  2330. else
  2331. {
  2332. Status = STATUS_ACCESS_DENIED;
  2333. }
  2334. }
  2335. if (NT_SUCCESS(Status)) {
  2336. ReturnValue = TRUE; // success.
  2337. }
  2338. switch(pBestLevelRecord->dwLevelId)
  2339. {
  2340. case SAFER_LEVELID_FULLYTRUSTED:
  2341. LocalLevelName = L"Unrestricted";
  2342. break;
  2343. case SAFER_LEVELID_NORMALUSER:
  2344. LocalLevelName = L"Basic User";
  2345. break;
  2346. case SAFER_LEVELID_CONSTRAINED:
  2347. LocalLevelName = L"Restricted";
  2348. break;
  2349. case SAFER_LEVELID_UNTRUSTED:
  2350. LocalLevelName = L"Untrusted";
  2351. break;
  2352. case SAFER_LEVELID_DISALLOWED:
  2353. LocalLevelName = L"Disallowed";
  2354. break;
  2355. default:
  2356. ASSERT(FALSE);
  2357. break;
  2358. }
  2359. if (pCodeProperties->ImagePath != NULL) {
  2360. LocalImageName = (PWSTR) pCodeProperties->ImagePath;
  2361. }
  2362. SaferpLogResultsToFile(
  2363. LocalImageName,
  2364. LocalLevelName,
  2365. LocalRuleName,
  2366. &BestIdentGuid);
  2367. ExitHandler2:
  2368. RtlLeaveCriticalSection(&g_TableCritSec);
  2369. ExitHandler:
  2370. if (!ReturnValue) {
  2371. BaseSetLastNTError(Status);
  2372. }
  2373. return ReturnValue;
  2374. }