windows-nt/Source/XPSP1/NT/base/ntsetup/legacy/dll/security.c
2020-09-26 16:20:57 +08:00

1120 lines
23 KiB
C

#include "precomp.h"
#pragma hdrstop
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
security.c
Abstract:
Security related functions in the setupdll
Detect Routines:
1. GetUserAccounts. Lists down all the user accounts in the system
2. GetComputerName. Finds the computer name.
Install Routines Workers:
1. CheckPrivilegeExistsWorker.
2. EnablePrivilegeWorker.
General Subroutines:
1. AdjustPrivilege.
2. RestorePrivilege.
Author:
Sunil Pai (sunilp) April 1992
--*/
//
// Routines which are used to force deletion of a file by taking ownership
// of the file
//
BOOL
AssertTakeOwnership(
HANDLE TokenHandle,
PTOKEN_PRIVILEGES OldPrivs
);
BOOL
GetTokenHandle(
PHANDLE TokenHandle
);
VOID
RestoreTakeOwnership(
HANDLE TokenHandle,
PTOKEN_PRIVILEGES OldPrivs
);
BOOL
FForceDeleteFile(
LPSTR szPath
);
//======================
// DETECT ROUTINES
//=======================
//
// Get current users account name
//
CB
GetMyUserName(
IN RGSZ Args,
IN USHORT cArgs,
OUT SZ ReturnBuffer,
IN CB cbReturnBuffer
)
/*++
Routine Description:
DetectRoutine for GetUserName. This finds out the username of
the logged in account.
Arguments:
Args - C argument list to this detect routine (None exist)
cArgs - Number of arguments.
ReturnBuffer - Buffer in which detected value is returned.
cbReturnBuffer - Buffer Size.
Return value:
Returns length of detected value.
--*/
{
CHAR UserName[MAX_PATH];
CHAR DomainName[MAX_PATH];
DWORD cbUserName = MAX_PATH;
DWORD cbDomainName = MAX_PATH;
SID_NAME_USE peUse;
BOOL bStatus = FALSE;
HANDLE hToken = NULL;
TOKEN_USER *ptu = NULL;
DWORD cbTokenBuffer = 0;
CB Length;
#define DEFAULT_USERNAME ""
Unused(Args);
Unused(cArgs);
Unused(cbReturnBuffer);
if( !OpenProcessToken( GetCurrentProcess(), TOKEN_READ, &hToken ) ) {
goto err;
}
//
// Get space needed for process token information
//
if( !GetTokenInformation( hToken, TokenUser, (LPVOID)NULL, 0, &cbTokenBuffer) ) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
goto err;
}
}
//
// Allocate space for token user information
//
if ( (ptu = (TOKEN_USER *)SAlloc(cbTokenBuffer)) == NULL ) {
goto err;
}
//
// Query token user information again
//
if( !GetTokenInformation( hToken, TokenUser, (LPVOID)ptu, cbTokenBuffer, &cbTokenBuffer) ) {
goto err;
}
//
// Query the user name and return it
//
if( LookupAccountSid( NULL, ptu->User.Sid, UserName, &cbUserName , DomainName, &cbDomainName, &peUse) ) {
lstrcpy( ReturnBuffer, DomainName );
lstrcat( ReturnBuffer, "\\" );
lstrcat( ReturnBuffer, UserName );
Length = lstrlen( ReturnBuffer ) + 1;
bStatus = TRUE;
}
err:
if( !bStatus ) {
lstrcpy( ReturnBuffer, DEFAULT_USERNAME );
Length = lstrlen( DEFAULT_USERNAME ) + 1;
}
if( hToken != NULL ) {
CloseHandle( hToken );
}
if( ptu ) {
SFree( ptu );
}
return( Length );
}
//
// Get User Accounts
//
CB
GetUserAccounts(
IN RGSZ Args,
IN USHORT cArgs,
OUT SZ ReturnBuffer,
IN CB cbReturnBuffer
)
/*++
Routine Description:
DetectRoutine for UserAccounts. This routine enumerates all the
user accounts under HKEY_USERS and returns the <sid-string key, username>
tuplet for every user found. The detected value will have the
following form:
{ {sid-string1, user-name1}, {sid-string2, user-name2} ... }
Arguments:
Args - C argument list to this detect routine (None exist)
cArgs - Number of arguments.
ReturnBuffer - Buffer in which detected value is returned.
cbReturnBuffer - Buffer Size.
Return value:
Returns length of detected value.
--*/
{
HKEY hProfile, hSubKey;
CHAR SubKeyName[MAX_PATH];
CHAR UserName[MAX_PATH];
CHAR DomainName[MAX_PATH];
CHAR ProfilePath[MAX_PATH];
CHAR Class[MAX_PATH];
DWORD cbSubKeyName;
DWORD cbUserName;
DWORD cbDomainName;
DWORD cbProfilePath;
DWORD cbClass;
FILETIME FileTime;
UINT Index;
LONG Status;
BOOL bStatus;
RGSZ rgszUsers, rgszCurrent;
SZ sz;
CB Length;
CHAR UnknownUser[MAX_PATH];
DWORD UnknownUserNum;
CHAR UnknownUserChar[10];
PSID pSid;
SID_NAME_USE peUse;
Unused(Args);
Unused(cArgs);
Unused(cbReturnBuffer);
#define NO_ACCOUNTS "{}"
//
// Load the string to use as the unknown user string. We will append
// it with numbers
//
LoadString( MyDllModuleHandle, IDS_STRING_UNKNOWN_USER, UnknownUser, MAX_PATH );
UnknownUserNum = 1;
//
// Enumerate keys under HKEY_USERS, for each user see if it is a
// a .Default (reject this), get the sid value and convert the
// sid to a user name. Add the subkey (this is a sid-string) and
// the username as an account.
//
//
// Intialise users list to no users
//
rgszUsers = RgszAlloc(1);
//
// open the key to the profile tree
//
Status = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
0,
KEY_READ,
&hProfile
);
if (Status == ERROR_SUCCESS) {
//
// Profile key exists, enumerate the profiles under this key
//
for ( Index = 0 ; ; Index++ ) {
//
// Get the current sub-key
//
cbSubKeyName = MAX_PATH;
cbClass = MAX_PATH;
cbUserName = MAX_PATH;
cbDomainName = MAX_PATH;
cbProfilePath = MAX_PATH;
Status = RegEnumKeyEx(
hProfile,
Index,
SubKeyName,
&cbSubKeyName,
NULL,
Class,
&cbClass,
&FileTime
);
if ( Status != ERROR_SUCCESS ) {
break;
}
//
// Open the subkey
//
Status = RegOpenKeyEx(
hProfile,
SubKeyName,
0,
KEY_READ,
&hSubKey
);
if ( Status != ERROR_SUCCESS) {
continue;
}
if( !lstrcmpi( SubKeyName, "THE_USER" ) ) {
lstrcpy( UserName, "THE_USER" );
goto skip_1;
}
//
// Get the User name for this profile, by looking up the sid
// value in the user key and then looking up the sid.
//
pSid = (PSID)GetValueEntry(
hSubKey,
"Sid"
);
if (!pSid) {
RegCloseKey( hSubKey );
continue;
}
//
// Convert the Sid into Username
//
bStatus = LookupAccountSid(
NULL,
pSid,
UserName,
&cbUserName,
DomainName,
&cbDomainName,
&peUse
);
SFree( pSid );
if( !bStatus ) {
RegCloseKey( hSubKey );
continue;
}
lstrcat( DomainName, "\\" );
if(!lstrcmpi(UserName, "")) {
lstrcat(DomainName, UnknownUser);
_ultoa( UnknownUserNum, UnknownUserChar, 10 );
lstrcat(DomainName, UnknownUserChar);
UnknownUserNum++;
}
else {
lstrcat(DomainName, UserName);
}
skip_1:
//
// Get the profilepath for this subkey, check to see if profilepath
// exists
//
bStatus = HUserKeyToProfilePath(
hSubKey,
ProfilePath,
&cbProfilePath
);
if( !bStatus ) {
RegCloseKey( hSubKey );
continue;
}
RegCloseKey( hSubKey );
//
// Form the list entry for this user
//
rgszCurrent = RgszAlloc(4);
rgszCurrent[0] = SzDup ( SubKeyName );
rgszCurrent[1] = SzDup ( DomainName );
rgszCurrent[2] = SzDup ( ProfilePath );
rgszCurrent[3] = NULL;
//
// Add this user to the list of users
//
sz = SzListValueFromRgsz( rgszCurrent );
if ( sz ) {
if( !RgszAdd ( &rgszUsers, sz ) ) {
SFree( sz );
}
}
RgszFree ( rgszCurrent );
}
RegCloseKey( hProfile );
}
sz = SzListValueFromRgsz( rgszUsers );
RgszFree( rgszUsers );
if ( sz ) {
lstrcpy( ReturnBuffer, sz );
Length = lstrlen( sz ) + 1;
SFree ( sz );
}
else {
lstrcpy( ReturnBuffer, NO_ACCOUNTS );
Length = lstrlen( NO_ACCOUNTS ) + 1;
}
return ( Length );
}
//========================
// INSTALL ROUTINE WORKERS
//========================
BOOL
CheckPrivilegeExistsWorker(
IN LPSTR PrivilegeType
)
/*++
Routine Description:
Routine to determine whether we have a particular privilege
Arguments:
PrivilegeType - Name of the privilege to enable / disable
Return value:
TRUE if CheckTakeOwnerPrivilege succeeds, FALSE otherwise.
If TRUE: ReturnTextBuffer has "YES" if privilege exists, "NO" otherwise.
If FALSE: ReturnTextBuffer has error text.
--*/
{
LONG Privilege;
TOKEN_PRIVILEGES PrevState;
ULONG ReturnLength = sizeof( TOKEN_PRIVILEGES );
if ( !lstrcmpi( PrivilegeType, "SeTakeOwnershipPrivilege" ) ) {
Privilege = SE_TAKE_OWNERSHIP_PRIVILEGE;
}
else if ( !lstrcmpi( PrivilegeType, "SeSystemEnvironmentPrivilege" ) ) {
Privilege = SE_SYSTEM_ENVIRONMENT_PRIVILEGE;
}
else {
SetErrorText(IDS_ERROR_UNSUPPORTEDPRIV);
return ( FALSE );
}
if ( AdjustPrivilege(
Privilege,
ENABLE_PRIVILEGE,
&PrevState,
&ReturnLength
)
) {
SetReturnText( "YES" );
RestorePrivilege( &PrevState );
return ( TRUE );
}
else {
SetReturnText( "NO" );
return ( TRUE );
}
}
BOOL
EnablePrivilegeWorker(
LPSTR PrivilegeType,
LPSTR Action
)
/*++
Routine Description:
Install routine to enable / disable the SE_SYSTEM_ENVIRONMENT_PRIVILEGE
Arguments:
PrivilegeType - Name of the privilege to enable / disable
Action - Whether to enable / disable
Return value:
TRUE if Enable / Disable succeeds, FALSE otherwise. ReturnTextBuffer
gets initialised to error text if FALSE.
--*/
{
ULONG Privilege;
INT AdjustAction;
if ( !lstrcmpi( PrivilegeType, "SeTakeOwnershipPrivilege" ) ) {
Privilege = SE_TAKE_OWNERSHIP_PRIVILEGE;
}
else if ( !lstrcmpi( PrivilegeType, "SeSystemEnvironmentPrivilege" ) ) {
Privilege = SE_SYSTEM_ENVIRONMENT_PRIVILEGE;
}
else {
SetErrorText(IDS_ERROR_UNSUPPORTEDPRIV);
return ( FALSE );
}
//
// Check Arg[1] .. Whether to enable / disable
//
if (!lstrcmpi(Action, "ENABLE")) {
AdjustAction = ENABLE_PRIVILEGE;
}
else if (!lstrcmpi(Action, "DISABLE")) {
AdjustAction = DISABLE_PRIVILEGE;
}
else {
SetErrorText(IDS_ERROR_BADARGS);
return(FALSE);
}
if ( !AdjustPrivilege(
Privilege,
AdjustAction,
NULL,
NULL
)
) {
SetErrorText(IDS_ERROR_ADJUSTPRIVILEGE);
return ( FALSE );
}
else {
return ( TRUE );
}
}
//======================================================================
// General security subroutines
//======================================================================
BOOL
AdjustPrivilege(
IN LONG PrivilegeType,
IN INT Action,
IN PTOKEN_PRIVILEGES PrevState, OPTIONAL
IN PULONG ReturnLength OPTIONAL
)
/*++
Routine Description:
Routine to enable or disable a particular privilege
Arguments:
PrivilegeType - Name of the privilege to enable / disable
Action - ENABLE_PRIVILEGE | DISABLE_PRIVILEGE
PrevState - Optional pointer to TOKEN_PRIVILEGES structure
to receive the previous state of privilege.
ReturnLength - Optional pointer to a ULONG to receive the length
of the PrevState returned.
Return value:
TRUE if succeeded, FALSE otherwise.
--*/
{
NTSTATUS NtStatus;
HANDLE Token;
LUID Privilege;
TOKEN_PRIVILEGES NewState;
ULONG BufferLength = 0;
//
// Get Privilege LUID
//
Privilege = RtlConvertLongToLuid(PrivilegeType);
NewState.PrivilegeCount = 1;
NewState.Privileges[0].Luid = Privilege;
//
// Look at action and determine the attributes
//
switch( Action ) {
case ENABLE_PRIVILEGE:
NewState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
break;
case DISABLE_PRIVILEGE:
NewState.Privileges[0].Attributes = 0;
break;
default:
return ( FALSE );
}
//
// Open our own token
//
NtStatus = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&Token
);
if (!NT_SUCCESS(NtStatus)) {
return( FALSE );
}
//
// See if return buffer is present and accordingly set the parameter
// of buffer length
//
if ( PrevState && ReturnLength ) {
BufferLength = *ReturnLength;
}
//
// Set the state of the privilege
//
NtStatus = NtAdjustPrivilegesToken(
Token, // TokenHandle
FALSE, // DisableAllPrivileges
&NewState, // NewState
BufferLength, // BufferLength
PrevState, // PreviousState (OPTIONAL)
ReturnLength // ReturnLength (OPTIONAL)
);
if ( NT_SUCCESS( NtStatus ) ) {
NtClose( Token );
return( TRUE );
}
else {
NtClose( Token );
return( FALSE );
}
}
BOOL
RestorePrivilege(
IN PTOKEN_PRIVILEGES PrevState
)
/*++
Routine Description:
To restore a privilege to its previous state
Arguments:
PrevState - Pointer to token privileges returned from an earlier
AdjustPrivileges call.
Return value:
TRUE on success, FALSE otherwise
--*/
{
NTSTATUS NtStatus;
HANDLE Token;
//
// Parameter checking
//
if ( !PrevState ) {
return ( FALSE );
}
//
// Open our own token
//
NtStatus = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&Token
);
if (!NT_SUCCESS(NtStatus)) {
return( FALSE );
}
//
// Set the state of the privilege
//
NtStatus = NtAdjustPrivilegesToken(
Token, // TokenHandle
FALSE, // DisableAllPrivileges
PrevState, // NewState
0, // BufferLength
NULL, // PreviousState (OPTIONAL)
NULL // ReturnLength (OPTIONAL)
);
if ( NT_SUCCESS( NtStatus ) ) {
NtClose( Token );
return( TRUE );
}
else {
NtClose( Token );
return( FALSE );
}
}
BOOL
HUserKeyToProfilePath(
IN HKEY hUserKey,
OUT LPSTR lpProfilePath,
IN OUT LPDWORD lpcbProfilePath
)
/*++
Routine Description:
This finds out the ProfilePath corresponding to a user account key handle
sees if the file exists and then returns the path to the profile.
Arguments:
hUserKey - Handle to a user account profile key
lpProfilePath - Pointer to a profile path buffer which will receive the
queried path.
lpcbProfilePath - Pointer to the size of the profile path buffer. Input
value is the size of the name buffer. Output value is the
actual size of the username queried
Return value:
Returns TRUE for success, FALSE for failure. If TRUE lpProfilePath and
lpcbProfilePath are initialized.
--*/
{
LONG Status;
CHAR szValue[ MAX_PATH ];
DWORD dwSize = MAX_PATH;
//
// Get the profile path value
//
Status = RegQueryValueEx(
hUserKey,
"ProfileImagePath",
NULL,
NULL,
szValue,
&dwSize
);
if( Status != ERROR_SUCCESS ) {
return( FALSE );
}
*lpcbProfilePath = ExpandEnvironmentStrings(
(LPCSTR)szValue,
lpProfilePath,
*lpcbProfilePath
);
//
// Check if profile path exists
//
if ( FFileExist( lpProfilePath ) ) {
return( TRUE );
}
else {
return( FALSE );
}
}
BOOL
FForceDeleteFile(
LPSTR szPath
)
{
BOOL Result;
SECURITY_DESCRIPTOR SecurityDescriptor;
HANDLE TokenHandle;
TOKEN_PRIVILEGES OldPrivs;
PSID AliasAdminsSid = NULL;
SID_IDENTIFIER_AUTHORITY SepNtAuthority = SECURITY_NT_AUTHORITY;
Result = AllocateAndInitializeSid(
&SepNtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&AliasAdminsSid
);
if ( !Result ) {
return( FALSE );
}
Result = GetTokenHandle( &TokenHandle );
if ( !Result ) {
return( FALSE );
}
//
// Create the security descritor with NULL DACL and Administrator as owner
//
InitializeSecurityDescriptor( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
Result = SetSecurityDescriptorDacl (
&SecurityDescriptor,
TRUE,
NULL,
FALSE
);
if ( !Result ) {
CloseHandle( TokenHandle );
return( FALSE );
}
Result = SetSecurityDescriptorOwner (
&SecurityDescriptor,
AliasAdminsSid,
FALSE
);
if ( !Result ) {
CloseHandle( TokenHandle );
return FALSE;
}
//
// Assert TakeOwnership privilege.
//
Result = AssertTakeOwnership( TokenHandle, &OldPrivs );
if ( !Result ) {
CloseHandle( TokenHandle );
return FALSE;
}
//
// Make Administrator the owner of the file.
//
Result = SetFileSecurity(
szPath,
OWNER_SECURITY_INFORMATION,
&SecurityDescriptor
);
RestoreTakeOwnership( TokenHandle, &OldPrivs );
if ( !Result ) {
CloseHandle( TokenHandle );
return( FALSE );
}
//
// We are now the owner, put a benign DACL onto the file
//
Result = SetFileSecurity(
szPath,
DACL_SECURITY_INFORMATION,
&SecurityDescriptor
);
if ( !Result ) {
CloseHandle( TokenHandle );
return( FALSE );
}
return( TRUE );
}
BOOL
GetTokenHandle(
PHANDLE TokenHandle
)
//
// This routine will open the current process and return
// a handle to its token.
//
// These handles will be closed for us when the process
// exits.
//
{
HANDLE ProcessHandle;
BOOL Result;
ProcessHandle = OpenProcess(
PROCESS_QUERY_INFORMATION,
FALSE,
GetCurrentProcessId()
);
if ( ProcessHandle == NULL ) {
//
// This should not happen
//
return( FALSE );
}
Result = OpenProcessToken (
ProcessHandle,
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
TokenHandle
);
if ( !Result ) {
//
// This should not happen
//
return( FALSE );
}
return( TRUE );
}
BOOL
AssertTakeOwnership(
HANDLE TokenHandle,
PTOKEN_PRIVILEGES OldPrivs
)
//
// This routine turns on SeTakeOwnershipPrivilege in the current
// token. Once that has been accomplished, we can open the file
// for WRITE_OWNER even if we are denied that access by the ACL
// on the file.
{
LUID TakeOwnershipValue;
BOOL Result;
TOKEN_PRIVILEGES TokenPrivileges;
DWORD ReturnLength;
//
// First, find out the value of TakeOwnershipPrivilege
//
Result = LookupPrivilegeValue(
NULL,
"SeTakeOwnershipPrivilege",
&TakeOwnershipValue
);
if ( !Result ) {
//
// This should not happen
//
return FALSE;
}
//
// Set up the privilege set we will need
//
TokenPrivileges.PrivilegeCount = 1;
TokenPrivileges.Privileges[0].Luid = TakeOwnershipValue;
TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
ReturnLength = sizeof( TOKEN_PRIVILEGES );
(VOID) AdjustTokenPrivileges (
TokenHandle,
FALSE,
&TokenPrivileges,
sizeof( TOKEN_PRIVILEGES ),
OldPrivs,
&ReturnLength
);
if ( GetLastError() != NO_ERROR ) {
return( FALSE );
} else {
return( TRUE );
}
}
VOID
RestoreTakeOwnership(
HANDLE TokenHandle,
PTOKEN_PRIVILEGES OldPrivs
)
{
(VOID) AdjustTokenPrivileges (
TokenHandle,
FALSE,
OldPrivs,
sizeof( TOKEN_PRIVILEGES ),
NULL,
NULL
);
}