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.

922 lines
23 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. appsecdll.c
  5. Abstract :
  6. Exports a function CreateProcessNotify - this function decides whether
  7. the new process can be created.
  8. Revision History :
  9. Sep 2000 - added support for Short File Names; PowerUsers not affected by AppSec - SriramSa
  10. Author :
  11. Sriram Sampath (SriramSa) June 1999
  12. --*/
  13. #include "pch.h"
  14. #pragma hdrstop
  15. #include "appsecdll.h"
  16. BOOL APIENTRY
  17. DllMain (
  18. HANDLE hInst,
  19. DWORD ul_reason,
  20. LPVOID lpReserved
  21. )
  22. {
  23. switch (ul_reason) {
  24. case DLL_PROCESS_ATTACH :
  25. // Disable Thread Lib calls - performance optimisation
  26. DisableThreadLibraryCalls (hInst);
  27. break ;
  28. case DLL_PROCESS_DETACH :
  29. break ;
  30. } // end of switch
  31. return 1 ;
  32. UNREFERENCED_PARAMETER(hInst) ;
  33. UNREFERENCED_PARAMETER(lpReserved) ;
  34. }
  35. /*++
  36. Routine Description :
  37. This routine determines if a process can be created based on whether
  38. it is a system process and if the user is an admin or not.
  39. Arguments :
  40. lpApplicationName - process name
  41. Reason - the reason this CreateProcessNotify is called
  42. Return Value :
  43. STATUS_SUCCESS if the process can be created ;
  44. STATUS_ACCESS_DEINIED if the process cannot be created.
  45. --*/
  46. NTSTATUS
  47. CreateProcessNotify (
  48. LPCWSTR lpApplicationName,
  49. ULONG Reason
  50. )
  51. {
  52. INT size ;
  53. HKEY TSkey, list_key, learn_key ;
  54. WCHAR g_szSystemRoot[MAX_PATH] ;
  55. WCHAR CurrentProcessName[MAX_PATH] ;
  56. WCHAR LongApplicationName[MAX_PATH] ;
  57. WCHAR CorrectAppName[MAX_PATH] ;
  58. WCHAR ResolvedAppName[MAX_PATH] ;
  59. BOOL is_taskman = FALSE , is_system = FALSE ;
  60. BOOL check_flag = FALSE, taskman_flag = FALSE, add_status ;
  61. BOOL IsAppSecEnabled = TRUE ;
  62. DWORD is_enabled = 0, learn_enabled = 0, PowerUserEnabled = 0;
  63. DWORD dw, disp, error_code, CurrentSessionId, RetValue, dwTimeOut = 1000;
  64. HANDLE TokenHandle;
  65. UCHAR TokenInformation[ sizeof( TOKEN_STATISTICS ) ];
  66. ULONG ReturnLength;
  67. LUID CurrentLUID = { 0, 0 };
  68. LUID SystemLUID = SYSTEM_LUID;
  69. NTSTATUS Status, QueryStatus;
  70. BOOL IsMember, IsAnAdmin = FALSE;
  71. SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
  72. PSID AdminSid = FALSE ;
  73. if ( Reason != APPCERT_IMAGE_OK_TO_RUN ) {
  74. return STATUS_SUCCESS ;
  75. }
  76. // First Check if the fEnabled key to see if Security is Enabled
  77. // This is done by checking the fEnabled key in the Registry
  78. if ( RegOpenKeyEx(
  79. HKEY_LOCAL_MACHINE,
  80. APPS_REGKEY,
  81. 0,
  82. KEY_READ,
  83. &TSkey
  84. ) != ERROR_SUCCESS ) {
  85. return STATUS_SUCCESS ;
  86. }
  87. size = sizeof(DWORD) ;
  88. if ( RegQueryValueEx(
  89. TSkey,
  90. FENABLED_KEY,
  91. NULL,
  92. NULL,
  93. (LPBYTE) &is_enabled,
  94. &size
  95. ) != ERROR_SUCCESS ) {
  96. goto error_cleanup ;
  97. }
  98. if (is_enabled == 0) {
  99. // Security is not Enabled
  100. IsAppSecEnabled = FALSE ;
  101. }
  102. // Check if the PowerUsers key in the registry is Enabled or not
  103. if ( RegQueryValueEx(
  104. TSkey,
  105. POWER_USERS_KEY,
  106. NULL,
  107. NULL,
  108. (LPBYTE) &PowerUserEnabled,
  109. &size
  110. ) != ERROR_SUCCESS ) {
  111. PowerUserEnabled = 0;
  112. }
  113. //
  114. // Check if the process which is trying to launch the new process is a system process.
  115. // This is done by querying the Token information of the current process and
  116. // comparing it's LUID with the LUID of a Process running under system context.
  117. //
  118. Status = NtOpenProcessToken(
  119. NtCurrentProcess(),
  120. TOKEN_QUERY,
  121. &TokenHandle
  122. );
  123. if ( !NT_SUCCESS(Status) ) {
  124. is_system = TRUE ;
  125. }
  126. if ( ! is_system ) {
  127. QueryStatus = NtQueryInformationToken(
  128. TokenHandle,
  129. TokenStatistics,
  130. &TokenInformation,
  131. sizeof(TokenInformation),
  132. &ReturnLength
  133. );
  134. if ( !NT_SUCCESS(QueryStatus) ) {
  135. goto error_cleanup ;
  136. }
  137. NtClose(TokenHandle);
  138. RtlCopyLuid(
  139. &CurrentLUID,
  140. &(((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId)
  141. );
  142. //
  143. // If the process is running in System context,
  144. // we allow it to be created without further check
  145. // The only exception to this is, we do not allow WinLogon to launch TaskManager
  146. // unless it is in the authorized list
  147. //
  148. if ( RtlEqualLuid(
  149. &CurrentLUID,
  150. &SystemLUID
  151. ) ) {
  152. is_system = TRUE ;
  153. }
  154. }
  155. // Check if Task Manager is spawned by a System Process
  156. if (is_system) {
  157. GetEnvironmentVariable( L"SystemRoot", g_szSystemRoot, MAX_PATH ) ;
  158. swprintf(CurrentProcessName, L"%s\\System32\\taskmgr.exe", g_szSystemRoot ) ;
  159. if ( _wcsicmp( CurrentProcessName, lpApplicationName ) != 0 ) {
  160. goto error_cleanup ;
  161. }
  162. }
  163. //
  164. // if not a system Process check if the user is a Administrator
  165. // This is done by comparing the SID of the current user to that of an Admin
  166. //
  167. if ( NT_SUCCESS(
  168. RtlAllocateAndInitializeSid(
  169. &SystemSidAuthority,
  170. 2,
  171. SECURITY_BUILTIN_DOMAIN_RID,
  172. DOMAIN_ALIAS_RID_ADMINS,
  173. 0, 0, 0, 0, 0, 0,
  174. &AdminSid
  175. )
  176. ) ) {
  177. if ( CheckTokenMembership(
  178. NULL,
  179. AdminSid,
  180. &IsAnAdmin
  181. ) == 0 ) {
  182. goto error_cleanup ;
  183. }
  184. RtlFreeSid(AdminSid);
  185. }
  186. //
  187. // If the user is an Admin, see if we are in the Tracking mode
  188. // We are in Tracking mode if the LearnEnabled Flag in Registry contains the Current Session ID
  189. //
  190. if (IsAnAdmin == TRUE ) {
  191. // Check the LearnEnabled flag to see if Tracking mode
  192. if ( RegOpenKeyEx(
  193. HKEY_CURRENT_USER,
  194. LIST_REGKEY,
  195. 0,
  196. KEY_READ,
  197. &learn_key
  198. ) != ERROR_SUCCESS ) {
  199. goto error_cleanup ;
  200. }
  201. if ( RegQueryValueEx(
  202. learn_key,
  203. LEARN_ENABLED_KEY,
  204. NULL,
  205. NULL,
  206. (LPBYTE) &learn_enabled,
  207. &size
  208. ) != ERROR_SUCCESS ) {
  209. RegCloseKey(learn_key) ;
  210. goto error_cleanup ;
  211. }
  212. RegCloseKey(learn_key) ;
  213. if (learn_enabled == -1) {
  214. // Tracking is not enabled
  215. goto error_cleanup ;
  216. } else {
  217. // Tracking is enabled
  218. // now get current session and see if it is the same as
  219. // the one in which tracking is enabled
  220. // Get CurrentSessionId
  221. if ( ProcessIdToSessionId(
  222. GetCurrentProcessId(),
  223. &CurrentSessionId
  224. ) == 0 ) {
  225. goto error_cleanup ;
  226. }
  227. if (learn_enabled != CurrentSessionId) {
  228. // dont add to the list of tracked applications
  229. goto error_cleanup ;
  230. }
  231. // Tracking phase is enabled - build the list
  232. // add this process name to the AppList registry
  233. // Create the Mutex for Synchronization when adding to list
  234. g_hMutex = CreateMutex(
  235. NULL,
  236. FALSE,
  237. MUTEX_NAME
  238. ) ;
  239. if (g_hMutex == NULL) {
  240. goto error_cleanup ;
  241. }
  242. // Wait to Enter the Critical Section - wait for a max of 1 minute
  243. dw = WaitForSingleObject(g_hMutex, dwTimeOut) ;
  244. if (dw == WAIT_OBJECT_0) {
  245. //
  246. // Create the Registry Key which will hold the applications tracked
  247. // during tracking period
  248. //
  249. if ( RegCreateKeyEx(
  250. HKEY_CURRENT_USER,
  251. LIST_REGKEY,
  252. 0,
  253. NULL,
  254. REG_OPTION_VOLATILE,
  255. KEY_ALL_ACCESS,
  256. NULL,
  257. &list_key,
  258. &disp
  259. ) != ERROR_SUCCESS) {
  260. ReleaseMutex(g_hMutex) ;
  261. CloseHandle(g_hMutex) ;
  262. goto error_cleanup ;
  263. }
  264. // Add this application name to the list in registry
  265. add_status = add_to_list (
  266. list_key,
  267. lpApplicationName
  268. ) ;
  269. } // Done adding to the list
  270. ReleaseMutex(g_hMutex) ;
  271. // Out of the Critical Section
  272. CloseHandle(g_hMutex) ;
  273. RegCloseKey(list_key) ;
  274. goto error_cleanup ;
  275. } // ending of Tracking phase
  276. } // User is an admin
  277. // Check if user is a PowerUser
  278. if ((PowerUserEnabled == 1) && (IsPowerUser())) {
  279. goto error_cleanup ;
  280. }
  281. // User is not an admin - also it is not a system process
  282. // Check if AppSec is enabled - if yes check the authorized list of apps
  283. if (IsAppSecEnabled == FALSE) {
  284. // AppSec is not enabled - so no need to check the authorized list of apps
  285. goto error_cleanup ;
  286. }
  287. // The filename may be in a short form - first convert it into the long form
  288. RetValue = GetLongPathNameW( (LPCWSTR) lpApplicationName, LongApplicationName, MAX_PATH) ;
  289. if (RetValue == 0) {
  290. // error - so use the original app name, not the long one
  291. wcscpy(CorrectAppName, lpApplicationName) ;
  292. } else {
  293. wcscpy(CorrectAppName, LongApplicationName) ;
  294. }
  295. //
  296. // Resolve Application name - if may reside in a remote server and share
  297. //
  298. ResolveName(
  299. CorrectAppName,
  300. ResolvedAppName
  301. );
  302. // Read the AuthorizedApplications List and compare with current Appname
  303. check_flag = check_list(
  304. TSkey,
  305. ResolvedAppName
  306. ) ;
  307. RegCloseKey(TSkey) ;
  308. //
  309. // If the current AppName is not in authorized list return ACCESS_DENIED
  310. if (check_flag == FALSE) {
  311. return STATUS_ACCESS_DENIED ;
  312. } else {
  313. return STATUS_SUCCESS ;
  314. }
  315. //
  316. // Error cleanup code
  317. // Close the Registry Key where we store authorized apps and return SUCCESS
  318. //
  319. error_cleanup :
  320. RegCloseKey(TSkey) ;
  321. return STATUS_SUCCESS;
  322. } // end of CreateProcessNotify
  323. /*++
  324. Routine Description :
  325. This routine checks if a process name is in a specified list
  326. of authorised applications in the registry.
  327. Arguments :
  328. hkey - The handle to the registry key which has the list of
  329. authorised applications.
  330. appname - name of the process
  331. Return Value :
  332. TRUE if process is in the list of authorised applications.
  333. FALSE otherwise.
  334. --*/
  335. BOOL
  336. check_list(
  337. HKEY hkey,
  338. LPWSTR appname
  339. )
  340. {
  341. WCHAR c ;
  342. INT i, j = 0 ;
  343. DWORD error_code ;
  344. DWORD RetValue ;
  345. LONG value,size = 0 ;
  346. BOOL found = FALSE ;
  347. WCHAR *buffer_sent, *app ;
  348. WCHAR LongAppName[MAX_PATH] ;
  349. WCHAR AppToCompare[MAX_PATH] ;
  350. // First find out size of buffer to allocate
  351. // This buffer will hold the authorized list of apps
  352. if ( RegQueryValueEx(
  353. hkey,
  354. AUTHORIZED_APPS_LIST_KEY,
  355. NULL,
  356. NULL,
  357. (LPBYTE) NULL,
  358. &size
  359. ) != ERROR_SUCCESS ) {
  360. return TRUE ;
  361. }
  362. buffer_sent = (WCHAR *) malloc ( size * sizeof(WCHAR)) ;
  363. if (buffer_sent == NULL) {
  364. return TRUE ;
  365. }
  366. app = (WCHAR *) malloc ( size * sizeof(WCHAR)) ;
  367. if (app == NULL) {
  368. free(buffer_sent) ;
  369. return TRUE ;
  370. }
  371. memset(buffer_sent, 0, size * sizeof(WCHAR) ) ;
  372. memset(app, 0, size * sizeof(WCHAR) ) ;
  373. // Get the List of Authorized applications from the Registry
  374. if ( RegQueryValueEx(
  375. hkey,
  376. AUTHORIZED_APPS_LIST_KEY,
  377. NULL,
  378. NULL,
  379. (LPBYTE) buffer_sent,
  380. &size
  381. ) != ERROR_SUCCESS ) {
  382. free(buffer_sent) ;
  383. free(app) ;
  384. return TRUE ;
  385. }
  386. // check if the process is present in the Authorized List
  387. for(i=0 ; i <= size-1 ; i++ ) {
  388. // check for end of list
  389. if ( (buffer_sent[i] == L'\0') &&
  390. (buffer_sent[i+1] == L'\0') ) {
  391. break ;
  392. }
  393. while ( buffer_sent[i] != L'\0' ) {
  394. app[j++] = buffer_sent[i++] ;
  395. }
  396. app[j++] = L'\0' ;
  397. // The filename may be in a short form - first convert it into the long form
  398. RetValue = GetLongPathNameW( (LPCWSTR) app, LongAppName, MAX_PATH) ;
  399. if (RetValue == 0) {
  400. // GetLongPathNameW failed for an app in the authorized list
  401. // maybe the file in the authorized list doesn't exist anymore
  402. wcscpy( AppToCompare, app) ;
  403. } else {
  404. wcscpy(AppToCompare, LongAppName) ;
  405. }
  406. // Compare if this app is the one that is being queried now
  407. if ( _wcsicmp(appname, AppToCompare) == 0 ) {
  408. // this process is present in the Authorized List
  409. found = TRUE ;
  410. break ;
  411. }
  412. j = 0 ;
  413. } // end of for loop
  414. free(buffer_sent) ;
  415. free(app) ;
  416. return(found) ;
  417. } // end of function
  418. /*++
  419. Routine Description :
  420. This routine appends a process name to a list maintained in
  421. Registry Key - used in Tracking mode.
  422. Arguments :
  423. hkey - The handle to the registry key which has the list of
  424. applications tracked.
  425. appname - name of the process
  426. Return Value :
  427. TRUE if process is appended successfully.
  428. FALSE otherwise.
  429. --*/
  430. BOOL
  431. add_to_list(
  432. HKEY hkey,
  433. LPCWSTR appname
  434. )
  435. {
  436. WCHAR c ;
  437. INT i, j = 0 ;
  438. UINT k ;
  439. DWORD error_code ;
  440. BOOL status = FALSE ;
  441. LONG value, size = 0, new_size ;
  442. WCHAR *buffer_got, *buffer_sent ;
  443. // First find out size of buffer to allocate
  444. // This buffer will hold the applications which are tracked
  445. if ( RegQueryValueEx(
  446. hkey,
  447. TRACK_LIST_KEY,
  448. NULL,
  449. NULL,
  450. (LPBYTE) NULL,
  451. &size
  452. ) != ERROR_SUCCESS ) {
  453. return (status) ;
  454. }
  455. buffer_got = (WCHAR *) malloc ( size * sizeof(WCHAR)) ;
  456. if (buffer_got == NULL) {
  457. return (status);
  458. }
  459. memset(buffer_got, 0, size * sizeof(WCHAR) ) ;
  460. // Get the present list of tracked processes in buffer_got
  461. if ( RegQueryValueEx(
  462. hkey,
  463. TRACK_LIST_KEY,
  464. NULL,
  465. NULL,
  466. (LPBYTE) buffer_got,
  467. &size
  468. ) != ERROR_SUCCESS ) {
  469. free(buffer_got) ;
  470. return (status) ;
  471. }
  472. // Append the present process to the track list
  473. // Prepare buffer to hold it
  474. // Size of new buffer will be the sum of the old buffer size
  475. // and the size of the new application + one byte for the terminating NULL char (in bytes)
  476. //
  477. new_size = size + (wcslen(appname) + 1) * sizeof(WCHAR) ;
  478. buffer_sent = (WCHAR *) malloc (new_size) ;
  479. if (buffer_sent == NULL) {
  480. free(buffer_got) ;
  481. return (status);
  482. }
  483. memset( buffer_sent, 0, new_size ) ;
  484. // check if this is the FIRST entry
  485. // If so size will be 2 - corresponding to 2 NULL chars in a empty list
  486. if ( size == 2 ) {
  487. // this is the first entry
  488. wcscpy(buffer_sent,appname) ;
  489. j = wcslen(buffer_sent) ;
  490. j++ ;
  491. buffer_sent[j] = L'\0' ;
  492. } else {
  493. // size > 2 - append this process to the end of track list
  494. for(i=0 ; i <= size-1 ; i++ ) {
  495. if ( (buffer_got[i] == L'\0') &&
  496. (buffer_got[i+1] == L'\0') ) {
  497. break ;
  498. }
  499. buffer_sent[j++] = buffer_got[i] ;
  500. } // end of for loop
  501. buffer_sent[j++] = L'\0' ;
  502. for(k=0 ; k <= wcslen(appname) - 1 ; k++) {
  503. buffer_sent[j++] = (WCHAR) appname[k] ;
  504. }
  505. buffer_sent[j++] = L'\0' ;
  506. buffer_sent[j] = L'\0' ;
  507. } // size > 2
  508. // write the new track list into registry
  509. if ( RegSetValueEx(
  510. hkey,
  511. L"ApplicationList",
  512. 0,
  513. REG_MULTI_SZ,
  514. (CONST BYTE *) buffer_sent,
  515. (j+1) * sizeof(WCHAR)
  516. ) != ERROR_SUCCESS ) {
  517. // Free all the buffers which were allocated
  518. free(buffer_got) ;
  519. free(buffer_sent) ;
  520. return (status) ;
  521. }
  522. status = TRUE ;
  523. // Free the buffers allocated
  524. free(buffer_got) ;
  525. free(buffer_sent) ;
  526. return(status) ;
  527. } // end of function
  528. /*++
  529. Routine Description :
  530. This Routine checks if the application resides in a local drive
  531. or a remote network share. If it is a remote share, the UNC path
  532. of the application is returned.
  533. Arguments :
  534. appname - name of the application
  535. Return Value :
  536. The UNC path of the appname if it resides in a remote server share.
  537. The same appname if it resides in a local drive.
  538. --*/
  539. VOID
  540. ResolveName(
  541. LPCWSTR appname,
  542. WCHAR *ResolvedName
  543. )
  544. {
  545. UINT i ;
  546. INT length ;
  547. WCHAR LocalName[3] ;
  548. WCHAR RootPathName[4] ;
  549. WCHAR RemoteName[MAX_PATH] ;
  550. DWORD size = MAX_PATH ;
  551. DWORD DriveType, error_status ;
  552. //
  553. // ResolvedName will hold the name of the UNC path of the appname if it is in
  554. // a remote server and share
  555. memset(ResolvedName, 0, MAX_PATH * sizeof(WCHAR)) ;
  556. // check if appname is a app in local drive or remote server share
  557. // Parse the first 3 chars in appname to get the root directory of the drive
  558. // where it resides
  559. wcsncpy(RootPathName, appname, 3 ) ;
  560. RootPathName[3] = L'\0';
  561. // Find the type of the Drive where the app is
  562. DriveType = GetDriveType(RootPathName) ;
  563. if (DriveType == DRIVE_REMOTE) {
  564. // Use WNetGetConnection to get the name of the remote share
  565. // Parse the first two chars of the appname to get the local drive
  566. // which is mapped onto the remote server and share
  567. wcsncpy(LocalName, appname, 2 ) ;
  568. LocalName[2] = L'\0' ;
  569. error_status = WNetGetConnection (
  570. LocalName,
  571. RemoteName,
  572. &size
  573. ) ;
  574. if (error_status != NO_ERROR) {
  575. wcscpy(ResolvedName,appname) ;
  576. return ;
  577. }
  578. //
  579. // Prepare ResolvedName - it will contain the Remote Server and Share name
  580. // followed by a \ and then the appname
  581. //
  582. wcscpy( ResolvedName, RemoteName ) ;
  583. length = wcslen(ResolvedName) ;
  584. ResolvedName[length++] = L'\\' ;
  585. for (i = 3 ; i <= wcslen(appname) ; i++ ) {
  586. ResolvedName[length++] = appname[i] ;
  587. }
  588. ResolvedName[length] = L'\0' ;
  589. return ;
  590. } else {
  591. // This application is in local drive and not in a remote server and share
  592. // Just send the appname back to the calling function
  593. wcscpy(ResolvedName,appname) ;
  594. return ;
  595. }
  596. }
  597. /*++
  598. Routine Description - This function checks if the present User belongs to the
  599. group of PowerUser.
  600. Arguments - none
  601. Return Value - TRUE is the User belongs to the Group of PowerUser
  602. FALSE if not.
  603. --*/
  604. BOOL
  605. IsPowerUser(VOID)
  606. {
  607. BOOL IsMember, IsAnPower;
  608. SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
  609. PSID PowerSid;
  610. if (RtlAllocateAndInitializeSid(
  611. &SystemSidAuthority,
  612. 2,
  613. SECURITY_BUILTIN_DOMAIN_RID,
  614. DOMAIN_ALIAS_RID_POWER_USERS,
  615. 0, 0, 0, 0, 0, 0,
  616. &PowerSid
  617. ) != STATUS_SUCCESS) {
  618. IsAnPower = FALSE;
  619. } else {
  620. if (!CheckTokenMembership(
  621. NULL,
  622. PowerSid,
  623. &IsMember)) {
  624. IsAnPower = FALSE;
  625. } else {
  626. IsAnPower = IsMember;
  627. }
  628. RtlFreeSid(PowerSid);
  629. }
  630. return IsAnPower;
  631. }// end of function IsPowerUser