|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name : appsecdll.c Abstract :
Exports a function CreateProcessNotify - this function decides whether the new process can be created. Revision History :
Sep 2000 - added support for Short File Names; PowerUsers not affected by AppSec - SriramSa Author :
Sriram Sampath (SriramSa) June 1999 --*/
#include "pch.h"
#pragma hdrstop
#include "appsecdll.h"
BOOL APIENTRY DllMain ( HANDLE hInst, DWORD ul_reason, LPVOID lpReserved ) {
switch (ul_reason) { case DLL_PROCESS_ATTACH :
// Disable Thread Lib calls - performance optimisation
DisableThreadLibraryCalls (hInst); break ; case DLL_PROCESS_DETACH : break ; } // end of switch
return 1 ;
UNREFERENCED_PARAMETER(hInst) ; UNREFERENCED_PARAMETER(lpReserved) ;
}
/*++
Routine Description :
This routine determines if a process can be created based on whether it is a system process and if the user is an admin or not. Arguments :
lpApplicationName - process name Reason - the reason this CreateProcessNotify is called
Return Value :
STATUS_SUCCESS if the process can be created ; STATUS_ACCESS_DEINIED if the process cannot be created. --*/
NTSTATUS CreateProcessNotify ( LPCWSTR lpApplicationName, ULONG Reason ) { INT size ; HKEY TSkey, list_key, learn_key ; WCHAR g_szSystemRoot[MAX_PATH] ; WCHAR CurrentProcessName[MAX_PATH] ; WCHAR LongApplicationName[MAX_PATH] ; WCHAR CorrectAppName[MAX_PATH] ; WCHAR ResolvedAppName[MAX_PATH] ; BOOL is_taskman = FALSE , is_system = FALSE ; BOOL check_flag = FALSE, taskman_flag = FALSE, add_status ; BOOL IsAppSecEnabled = TRUE ; DWORD is_enabled = 0, learn_enabled = 0, PowerUserEnabled = 0; DWORD dw, disp, error_code, CurrentSessionId, RetValue, dwTimeOut = 1000; HANDLE TokenHandle; UCHAR TokenInformation[ sizeof( TOKEN_STATISTICS ) ]; ULONG ReturnLength; LUID CurrentLUID = { 0, 0 }; LUID SystemLUID = SYSTEM_LUID; NTSTATUS Status, QueryStatus; BOOL IsMember, IsAnAdmin = FALSE; SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY; PSID AdminSid = FALSE ; if ( Reason != APPCERT_IMAGE_OK_TO_RUN ) { return STATUS_SUCCESS ; }
// First Check if the fEnabled key to see if Security is Enabled
// This is done by checking the fEnabled key in the Registry
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, APPS_REGKEY, 0, KEY_READ, &TSkey ) != ERROR_SUCCESS ) {
return STATUS_SUCCESS ; } size = sizeof(DWORD) ;
if ( RegQueryValueEx( TSkey, FENABLED_KEY, NULL, NULL, (LPBYTE) &is_enabled, &size ) != ERROR_SUCCESS ) { goto error_cleanup ; } if (is_enabled == 0) { // Security is not Enabled
IsAppSecEnabled = FALSE ; }
// Check if the PowerUsers key in the registry is Enabled or not
if ( RegQueryValueEx( TSkey, POWER_USERS_KEY, NULL, NULL, (LPBYTE) &PowerUserEnabled, &size ) != ERROR_SUCCESS ) {
PowerUserEnabled = 0; } //
// Check if the process which is trying to launch the new process is a system process.
// This is done by querying the Token information of the current process and
// comparing it's LUID with the LUID of a Process running under system context.
//
Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_QUERY, &TokenHandle ); if ( !NT_SUCCESS(Status) ) { is_system = TRUE ; }
if ( ! is_system ) {
QueryStatus = NtQueryInformationToken( TokenHandle, TokenStatistics, &TokenInformation, sizeof(TokenInformation), &ReturnLength ); if ( !NT_SUCCESS(QueryStatus) ) { goto error_cleanup ; }
NtClose(TokenHandle);
RtlCopyLuid( &CurrentLUID, &(((PTOKEN_STATISTICS)TokenInformation)->AuthenticationId) );
//
// If the process is running in System context,
// we allow it to be created without further check
// The only exception to this is, we do not allow WinLogon to launch TaskManager
// unless it is in the authorized list
//
if ( RtlEqualLuid( &CurrentLUID, &SystemLUID ) ) { is_system = TRUE ;
}
} // Check if Task Manager is spawned by a System Process
if (is_system) {
GetEnvironmentVariable( L"SystemRoot", g_szSystemRoot, MAX_PATH ) ; swprintf(CurrentProcessName, L"%s\\System32\\taskmgr.exe", g_szSystemRoot ) ;
if ( _wcsicmp( CurrentProcessName, lpApplicationName ) != 0 ) { goto error_cleanup ;
}
} //
// if not a system Process check if the user is a Administrator
// This is done by comparing the SID of the current user to that of an Admin
//
if ( NT_SUCCESS( RtlAllocateAndInitializeSid( &SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminSid ) ) ) { if ( CheckTokenMembership( NULL, AdminSid, &IsAnAdmin ) == 0 ) { goto error_cleanup ; } RtlFreeSid(AdminSid); }
//
// If the user is an Admin, see if we are in the Tracking mode
// We are in Tracking mode if the LearnEnabled Flag in Registry contains the Current Session ID
//
if (IsAnAdmin == TRUE ) {
// Check the LearnEnabled flag to see if Tracking mode
if ( RegOpenKeyEx( HKEY_CURRENT_USER, LIST_REGKEY, 0, KEY_READ, &learn_key ) != ERROR_SUCCESS ) { goto error_cleanup ; } if ( RegQueryValueEx( learn_key, LEARN_ENABLED_KEY, NULL, NULL, (LPBYTE) &learn_enabled, &size ) != ERROR_SUCCESS ) { RegCloseKey(learn_key) ; goto error_cleanup ; } RegCloseKey(learn_key) ; if (learn_enabled == -1) { // Tracking is not enabled
goto error_cleanup ; } else { // Tracking is enabled
// now get current session and see if it is the same as
// the one in which tracking is enabled
// Get CurrentSessionId
if ( ProcessIdToSessionId( GetCurrentProcessId(), &CurrentSessionId ) == 0 ) {
goto error_cleanup ; } if (learn_enabled != CurrentSessionId) {
// dont add to the list of tracked applications
goto error_cleanup ; } // Tracking phase is enabled - build the list
// add this process name to the AppList registry
// Create the Mutex for Synchronization when adding to list
g_hMutex = CreateMutex( NULL, FALSE, MUTEX_NAME ) ; if (g_hMutex == NULL) { goto error_cleanup ; } // Wait to Enter the Critical Section - wait for a max of 1 minute
dw = WaitForSingleObject(g_hMutex, dwTimeOut) ; if (dw == WAIT_OBJECT_0) { //
// Create the Registry Key which will hold the applications tracked
// during tracking period
//
if ( RegCreateKeyEx( HKEY_CURRENT_USER, LIST_REGKEY, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &list_key, &disp ) != ERROR_SUCCESS) {
ReleaseMutex(g_hMutex) ; CloseHandle(g_hMutex) ; goto error_cleanup ; } // Add this application name to the list in registry
add_status = add_to_list ( list_key, lpApplicationName ) ; } // Done adding to the list
ReleaseMutex(g_hMutex) ; // Out of the Critical Section
CloseHandle(g_hMutex) ; RegCloseKey(list_key) ; goto error_cleanup ; } // ending of Tracking phase
} // User is an admin
// Check if user is a PowerUser
if ((PowerUserEnabled == 1) && (IsPowerUser())) { goto error_cleanup ; }
// User is not an admin - also it is not a system process
// Check if AppSec is enabled - if yes check the authorized list of apps
if (IsAppSecEnabled == FALSE) { // AppSec is not enabled - so no need to check the authorized list of apps
goto error_cleanup ;
} // The filename may be in a short form - first convert it into the long form
RetValue = GetLongPathNameW( (LPCWSTR) lpApplicationName, LongApplicationName, MAX_PATH) ; if (RetValue == 0) { // error - so use the original app name, not the long one
wcscpy(CorrectAppName, lpApplicationName) ; } else { wcscpy(CorrectAppName, LongApplicationName) ; } //
// Resolve Application name - if may reside in a remote server and share
//
ResolveName( CorrectAppName, ResolvedAppName ); // Read the AuthorizedApplications List and compare with current Appname
check_flag = check_list( TSkey, ResolvedAppName ) ; RegCloseKey(TSkey) ;
//
// If the current AppName is not in authorized list return ACCESS_DENIED
if (check_flag == FALSE) { return STATUS_ACCESS_DENIED ; } else {
return STATUS_SUCCESS ;
} //
// Error cleanup code
// Close the Registry Key where we store authorized apps and return SUCCESS
//
error_cleanup : RegCloseKey(TSkey) ; return STATUS_SUCCESS; } // end of CreateProcessNotify
/*++
Routine Description :
This routine checks if a process name is in a specified list of authorised applications in the registry. Arguments : hkey - The handle to the registry key which has the list of authorised applications. appname - name of the process
Return Value :
TRUE if process is in the list of authorised applications. FALSE otherwise. --*/
BOOL check_list( HKEY hkey, LPWSTR appname )
{ WCHAR c ; INT i, j = 0 ; DWORD error_code ; DWORD RetValue ; LONG value,size = 0 ; BOOL found = FALSE ; WCHAR *buffer_sent, *app ; WCHAR LongAppName[MAX_PATH] ; WCHAR AppToCompare[MAX_PATH] ;
// First find out size of buffer to allocate
// This buffer will hold the authorized list of apps
if ( RegQueryValueEx( hkey, AUTHORIZED_APPS_LIST_KEY, NULL, NULL, (LPBYTE) NULL, &size ) != ERROR_SUCCESS ) {
return TRUE ; } buffer_sent = (WCHAR *) malloc ( size * sizeof(WCHAR)) ; if (buffer_sent == NULL) { return TRUE ; } app = (WCHAR *) malloc ( size * sizeof(WCHAR)) ; if (app == NULL) { free(buffer_sent) ; return TRUE ; } memset(buffer_sent, 0, size * sizeof(WCHAR) ) ; memset(app, 0, size * sizeof(WCHAR) ) ;
// Get the List of Authorized applications from the Registry
if ( RegQueryValueEx( hkey, AUTHORIZED_APPS_LIST_KEY, NULL, NULL, (LPBYTE) buffer_sent, &size ) != ERROR_SUCCESS ) {
free(buffer_sent) ; free(app) ; return TRUE ; } // check if the process is present in the Authorized List
for(i=0 ; i <= size-1 ; i++ ) {
// check for end of list
if ( (buffer_sent[i] == L'\0') && (buffer_sent[i+1] == L'\0') ) { break ; } while ( buffer_sent[i] != L'\0' ) { app[j++] = buffer_sent[i++] ; } app[j++] = L'\0' ; // The filename may be in a short form - first convert it into the long form
RetValue = GetLongPathNameW( (LPCWSTR) app, LongAppName, MAX_PATH) ; if (RetValue == 0) { // GetLongPathNameW failed for an app in the authorized list
// maybe the file in the authorized list doesn't exist anymore
wcscpy( AppToCompare, app) ; } else { wcscpy(AppToCompare, LongAppName) ; }
// Compare if this app is the one that is being queried now
if ( _wcsicmp(appname, AppToCompare) == 0 ) { // this process is present in the Authorized List
found = TRUE ; break ; } j = 0 ; } // end of for loop
free(buffer_sent) ; free(app) ; return(found) ; } // end of function
/*++
Routine Description :
This routine appends a process name to a list maintained in Registry Key - used in Tracking mode. Arguments : hkey - The handle to the registry key which has the list of applications tracked. appname - name of the process
Return Value :
TRUE if process is appended successfully. FALSE otherwise. --*/
BOOL add_to_list( HKEY hkey, LPCWSTR appname ) { WCHAR c ; INT i, j = 0 ; UINT k ; DWORD error_code ; BOOL status = FALSE ; LONG value, size = 0, new_size ; WCHAR *buffer_got, *buffer_sent ;
// First find out size of buffer to allocate
// This buffer will hold the applications which are tracked
if ( RegQueryValueEx( hkey, TRACK_LIST_KEY, NULL, NULL, (LPBYTE) NULL, &size ) != ERROR_SUCCESS ) { return (status) ; }
buffer_got = (WCHAR *) malloc ( size * sizeof(WCHAR)) ; if (buffer_got == NULL) { return (status); } memset(buffer_got, 0, size * sizeof(WCHAR) ) ; // Get the present list of tracked processes in buffer_got
if ( RegQueryValueEx( hkey, TRACK_LIST_KEY, NULL, NULL, (LPBYTE) buffer_got, &size ) != ERROR_SUCCESS ) { free(buffer_got) ; return (status) ; } // Append the present process to the track list
// Prepare buffer to hold it
// Size of new buffer will be the sum of the old buffer size
// and the size of the new application + one byte for the terminating NULL char (in bytes)
//
new_size = size + (wcslen(appname) + 1) * sizeof(WCHAR) ; buffer_sent = (WCHAR *) malloc (new_size) ; if (buffer_sent == NULL) { free(buffer_got) ; return (status); } memset( buffer_sent, 0, new_size ) ; // check if this is the FIRST entry
// If so size will be 2 - corresponding to 2 NULL chars in a empty list
if ( size == 2 ) { // this is the first entry
wcscpy(buffer_sent,appname) ; j = wcslen(buffer_sent) ; j++ ; buffer_sent[j] = L'\0' ; } else { // size > 2 - append this process to the end of track list
for(i=0 ; i <= size-1 ; i++ ) {
if ( (buffer_got[i] == L'\0') && (buffer_got[i+1] == L'\0') ) { break ; } buffer_sent[j++] = buffer_got[i] ; } // end of for loop
buffer_sent[j++] = L'\0' ; for(k=0 ; k <= wcslen(appname) - 1 ; k++) { buffer_sent[j++] = (WCHAR) appname[k] ; } buffer_sent[j++] = L'\0' ; buffer_sent[j] = L'\0' ; } // size > 2
// write the new track list into registry
if ( RegSetValueEx( hkey, L"ApplicationList", 0, REG_MULTI_SZ, (CONST BYTE *) buffer_sent, (j+1) * sizeof(WCHAR) ) != ERROR_SUCCESS ) { // Free all the buffers which were allocated
free(buffer_got) ; free(buffer_sent) ; return (status) ;
} status = TRUE ; // Free the buffers allocated
free(buffer_got) ; free(buffer_sent) ; return(status) ; } // end of function
/*++
Routine Description :
This Routine checks if the application resides in a local drive or a remote network share. If it is a remote share, the UNC path of the application is returned. Arguments : appname - name of the application
Return Value :
The UNC path of the appname if it resides in a remote server share. The same appname if it resides in a local drive. --*/
VOID ResolveName( LPCWSTR appname, WCHAR *ResolvedName ) {
UINT i ; INT length ; WCHAR LocalName[3] ; WCHAR RootPathName[4] ; WCHAR RemoteName[MAX_PATH] ; DWORD size = MAX_PATH ; DWORD DriveType, error_status ; //
// ResolvedName will hold the name of the UNC path of the appname if it is in
// a remote server and share
memset(ResolvedName, 0, MAX_PATH * sizeof(WCHAR)) ; // check if appname is a app in local drive or remote server share
// Parse the first 3 chars in appname to get the root directory of the drive
// where it resides
wcsncpy(RootPathName, appname, 3 ) ; RootPathName[3] = L'\0'; // Find the type of the Drive where the app is
DriveType = GetDriveType(RootPathName) ;
if (DriveType == DRIVE_REMOTE) { // Use WNetGetConnection to get the name of the remote share
// Parse the first two chars of the appname to get the local drive
// which is mapped onto the remote server and share
wcsncpy(LocalName, appname, 2 ) ; LocalName[2] = L'\0' ;
error_status = WNetGetConnection ( LocalName, RemoteName, &size ) ;
if (error_status != NO_ERROR) { wcscpy(ResolvedName,appname) ; return ; } //
// Prepare ResolvedName - it will contain the Remote Server and Share name
// followed by a \ and then the appname
//
wcscpy( ResolvedName, RemoteName ) ; length = wcslen(ResolvedName) ;
ResolvedName[length++] = L'\\' ; for (i = 3 ; i <= wcslen(appname) ; i++ ) { ResolvedName[length++] = appname[i] ; } ResolvedName[length] = L'\0' ; return ;
} else { // This application is in local drive and not in a remote server and share
// Just send the appname back to the calling function
wcscpy(ResolvedName,appname) ; return ; } }
/*++
Routine Description - This function checks if the present User belongs to the group of PowerUser.
Arguments - none
Return Value - TRUE is the User belongs to the Group of PowerUser FALSE if not.
--*/
BOOL IsPowerUser(VOID) { BOOL IsMember, IsAnPower; SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY; PSID PowerSid;
if (RtlAllocateAndInitializeSid( &SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &PowerSid ) != STATUS_SUCCESS) { IsAnPower = FALSE; } else { if (!CheckTokenMembership( NULL, PowerSid, &IsMember)) { IsAnPower = FALSE; } else { IsAnPower = IsMember; } RtlFreeSid(PowerSid); } return IsAnPower;
}// end of function IsPowerUser
|