windows-nt/Source/XPSP1/NT/termsrv/admtools/appsec/dll/appsecdll.c

923 lines
22 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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