1285 lines
33 KiB
C
1285 lines
33 KiB
C
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
i_sysshu.c
|
||
|
||
Abstract:
|
||
|
||
This module performs actions related to workstation shutdown
|
||
control.
|
||
|
||
Workstation shutdown has several different facets:
|
||
|
||
1) Should the [SHUTDOWN] button be displayed at the logon
|
||
screen, allowing anyone to shut the system down - even
|
||
those that don't have accounts?
|
||
|
||
2) Once logged on, who should be allowed to shut the system
|
||
down? Anyone? Only administrators?
|
||
|
||
The first facet is controlled by the following registry key
|
||
value:
|
||
|
||
Key: \Hkey_local_machine\Software\Microsoft\
|
||
Windows NT\CurrentVersion\Winlogon
|
||
Value: [REG_SZ] ShutdownWithoutLogon<integer value>
|
||
|
||
Where the defined "<integer value>"'s are:
|
||
|
||
0 - Do not display the [SHUTDOWN] button in the
|
||
logon dialog.
|
||
1 - Display the [SHUTDOWN] button in the logon dialog.
|
||
|
||
All other values are undefined and will default to "0".
|
||
|
||
The second facet is controlled by assignment of two privileges
|
||
(SE_SHUTDOWN_PRIVILEGE and SE_REMOTE_SHUTDOWN_PRIVILEGE).
|
||
Assignment of these privileges is tracked by LSA.
|
||
|
||
Note that this utility does not distinguish between those who
|
||
may shut the system down locally vs. remotely. If you may
|
||
shut the system down, you may do it either remotely or locally.
|
||
|
||
|
||
|
||
|
||
Author:
|
||
|
||
Jim Kelly (JimK) 22-Sep-1994
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "Missyp.h"
|
||
#include "ntlsa.h"
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Module-Private Definitions //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////
|
||
|
||
VOID
|
||
MissypMarkShutdownValueCurrent( VOID );
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Module-wide variables //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////
|
||
|
||
PSECMGR_AREA_DESCRIPTOR
|
||
MissypShutdownArea;
|
||
|
||
PSECMGR_ITEM_DESCRIPTOR
|
||
MissypShutdownItem;
|
||
|
||
|
||
WCHAR
|
||
MissypShutdownName[SECMGR_MAX_ITEM_NAME_LENGTH],
|
||
MissypShutdownDesc[SECMGR_MAX_ITEM_DESC_LENGTH];
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Module-Private Prototypes //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////
|
||
|
||
NTSTATUS
|
||
MissypGetShutdownPrivs(
|
||
IN HWND hwnd,
|
||
OUT PBOOLEAN NonStandard,
|
||
OUT PBOOLEAN AnyoneLoggedOn,
|
||
OUT PBOOLEAN OpersAndAdmins,
|
||
OUT PBOOLEAN Administrators
|
||
);
|
||
|
||
NTSTATUS
|
||
MissypSetShutdownPrivs(
|
||
IN PMISSYP_ACCOUNTS Accounts
|
||
);
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Externally callable functions //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
BOOL
|
||
MissypShutdownInitialize(
|
||
IN PSECMGR_AREA_DESCRIPTOR Area,
|
||
IN PSECMGR_ITEM_DESCRIPTOR Item,
|
||
IN ULONG ItemIndex
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to initialize this module and fill in the passed Item
|
||
control block.
|
||
|
||
|
||
Arguments
|
||
|
||
Area - the security area which this item is part of.
|
||
|
||
Item - points to the item to initialize.
|
||
|
||
ItemIndex - The index of the item in the array of items for this area.
|
||
|
||
|
||
|
||
Return Values:
|
||
|
||
TRUE - This item has been successfully initialized. However, the
|
||
item value is not yet valid.
|
||
|
||
FALSE - we ran into trouble initializing.
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// Save away pointers to the area and our item
|
||
//
|
||
|
||
MissypShutdownArea = Area;
|
||
MissypShutdownItem = Item;
|
||
|
||
//
|
||
// Get the name and description strings
|
||
//
|
||
|
||
LoadString( MissyphInstance,
|
||
MISSYP_STRING_ITEM_SHUTDOWN_NAME,
|
||
&MissypShutdownName[0],
|
||
SECMGR_MAX_ITEM_NAME_LENGTH
|
||
);
|
||
LoadString( MissyphInstance,
|
||
MISSYP_STRING_ITEM_SHUTDOWN_DESC,
|
||
&MissypShutdownDesc[0],
|
||
SECMGR_MAX_ITEM_DESC_LENGTH
|
||
);
|
||
|
||
//
|
||
// Set the flags as such:
|
||
//
|
||
// Item view editing is allowed.
|
||
// Area view editing IS allowed.
|
||
// The value is NOT complex - may be displayed in spreadsheet mode.
|
||
// The value is NOT current (has not be retrieved yet).
|
||
//
|
||
|
||
Item->Flags = (SECMGR_ITEM_FLAG_AREA_VIEW |
|
||
SECMGR_ITEM_FLAG_ITEM_VIEW);
|
||
|
||
//
|
||
// Now the rest of the fields.
|
||
// Value and RecommendedValue are not yet available.
|
||
//
|
||
|
||
Item->Area = Area;
|
||
Item->ItemIndex = ItemIndex;
|
||
Item->SecMgrContext = NULL; // Not for our use
|
||
Item->SmedlyContext = NULL; // We don't use this
|
||
Item->Name = &MissypShutdownName[0];
|
||
Item->Description = &MissypShutdownDesc[0];
|
||
Item->Type = SecMgrTypeWho;
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
|
||
BOOL
|
||
MissypInvokeShutdown(
|
||
IN HWND hwnd,
|
||
IN BOOL AllowChanges,
|
||
IN PSECMGR_AREA_DESCRIPTOR Area,
|
||
IN PSECMGR_ITEM_DESCRIPTOR Item
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to obtain a view of just the system shutdown item.
|
||
|
||
|
||
Arguments
|
||
|
||
hwnd - The caller's window. We will put up a child window.
|
||
|
||
AllowChanges - If TRUE, value changes are allowed. Otherwise,
|
||
value changes are not allowed.
|
||
|
||
Area - Pointer to this Area's descriptor.
|
||
|
||
Item - Pointer to our item's descriptor. This will be filled in
|
||
with current values upon exit of this routine.
|
||
|
||
|
||
|
||
Return Values:
|
||
|
||
TRUE - The dialog was displayed without error.
|
||
|
||
FALSE - we ran into trouble.
|
||
|
||
If an error is encountered, then an error popup will be
|
||
displayed by this routine.
|
||
|
||
--*/
|
||
{
|
||
|
||
DialogBoxParam(MissyphInstance,
|
||
MAKEINTRESOURCE(MISSYP_ID_DLG_ITEM_SHUTDOWN_SYSTEM),
|
||
hwnd,
|
||
(DLGPROC)MissypDlgProcShutdown,
|
||
(LONG)AllowChanges
|
||
);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
MissypGetShutdownSetting(
|
||
IN HWND hwnd,
|
||
OUT PSECMGR_WHO Value,
|
||
IN BOOL Interactive
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to get the current settings of who may
|
||
shutdown the system.
|
||
|
||
|
||
Arguments
|
||
|
||
hwnd - The caller's window. This is used if we need to put
|
||
up an error popup.
|
||
|
||
Value - Receives a value indicating who may currently shutdown
|
||
the system.
|
||
|
||
Interactive - Indicates whether or not we should attempt to present
|
||
any UI (such as popups informing non-standard setting). TRUE
|
||
means it is OK to present UI, FALSE indicates we should not
|
||
put up any UI.
|
||
|
||
Return Values:
|
||
|
||
TRUE - The value has been successfully retrieved.
|
||
|
||
FALSE - we ran into trouble querying the current setting.
|
||
|
||
If an error is encountered, then an error popup will be
|
||
displayed by this routine.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS
|
||
NtStatus;
|
||
|
||
UINT
|
||
ShutdownByAnyone;
|
||
|
||
BOOLEAN
|
||
NonStandard,
|
||
AnyoneLoggedOn,
|
||
OpersAndAdmins,
|
||
Administrators;
|
||
|
||
|
||
|
||
ShutdownByAnyone =
|
||
GetProfileInt( TEXT("Winlogon"), TEXT("ShutdownWithoutLogon"), 0);
|
||
|
||
|
||
|
||
//
|
||
// See who is assigned either of the shutdown privileges.
|
||
// Do this even if we already know the setting will be
|
||
// SecMgrAnyone just so we can cause the
|
||
// "non-standard assignment" popup to be displayed, if
|
||
// necessary.
|
||
//
|
||
|
||
NtStatus = MissypGetShutdownPrivs( hwnd,
|
||
&NonStandard,
|
||
&AnyoneLoggedOn,
|
||
&OpersAndAdmins,
|
||
&Administrators
|
||
);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// Check to see if we have conflicting (non-standard) settings
|
||
//
|
||
|
||
if ( NonStandard || ((ShutdownByAnyone == 1) && (!AnyoneLoggedOn)) ) {
|
||
|
||
if (Interactive) {
|
||
|
||
//
|
||
// Put up the pop-up
|
||
//
|
||
|
||
MissypPopUp( hwnd, MISSYP_POP_UP_NONSTANDARD_SHUTDOWN, 0);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
if (ShutdownByAnyone == 1) {
|
||
(*Value) = SecMgrAnyone;
|
||
} else if (AnyoneLoggedOn) {
|
||
(*Value) = SecMgrAnyoneLoggedOn;
|
||
} else if (OpersAndAdmins) {
|
||
(*Value) = SecMgrOpersAndAdmins;
|
||
} else {
|
||
(*Value) = SecMgrAdminsOnly;
|
||
}
|
||
|
||
|
||
//
|
||
// Set the value in our item control block
|
||
//
|
||
|
||
MissypShutdownItem->Value.Who = (*Value);
|
||
|
||
|
||
//
|
||
// Mark the value as current (updating our recommendation in the process).
|
||
//
|
||
|
||
MissypMarkShutdownValueCurrent();
|
||
|
||
return( TRUE );
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
MissypSetShutdownSetting(
|
||
IN HWND hwnd,
|
||
IN SECMGR_WHO Value
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to set a new workstation shutdown setting.
|
||
|
||
Arguments
|
||
|
||
hwnd - The caller's window. This is used if we need to put
|
||
up an error popup.
|
||
|
||
Value - Indicates who may shutdown the system.
|
||
|
||
|
||
Return Values:
|
||
|
||
TRUE - The value has been successfully set
|
||
|
||
FALSE - we ran into trouble setting the new setting.
|
||
|
||
If an error is encountered, then an error popup will be
|
||
displayed by this routine.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS
|
||
NtStatus;
|
||
|
||
ULONG
|
||
ProfileValue;
|
||
|
||
BOOLEAN
|
||
Result;
|
||
|
||
PMISSYP_ACCOUNTS
|
||
Accounts;
|
||
|
||
|
||
//
|
||
// Allow shutdown at the logon screen, if asked for
|
||
//
|
||
|
||
if (Value == SecMgrAnyone) {
|
||
ProfileValue = 1;
|
||
} else {
|
||
ProfileValue = 0;
|
||
}
|
||
|
||
Result = MissypSetProfileInt(
|
||
TEXT("Winlogon"),
|
||
TEXT("ShutdownWithoutLogon"),
|
||
ProfileValue );
|
||
if (!Result) {
|
||
|
||
//
|
||
// Put up a pop-up
|
||
//
|
||
|
||
MissypPopUp( hwnd, MISSYP_POP_UP_CANT_SET_SHUTDOWN, MISSYP_STRING_TITLE_ERROR);
|
||
|
||
|
||
return(FALSE);
|
||
|
||
}
|
||
|
||
|
||
switch (Value) {
|
||
case SecMgrAnyone:
|
||
case SecMgrAnyoneLoggedOn:
|
||
Accounts = &MissypAnyoneSids;
|
||
break;
|
||
|
||
case SecMgrOpersAndAdmins:
|
||
Accounts = &MissypOpersAndAdminsSids;
|
||
break;
|
||
|
||
|
||
case SecMgrAdminsOnly:
|
||
Accounts = &MissypAdminsSids;
|
||
break;
|
||
} //end_switch
|
||
|
||
//
|
||
// Apply the privileges to the specified accounts
|
||
//
|
||
|
||
NtStatus = MissypSetShutdownPrivs( Accounts );
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// Update our Item Control Block
|
||
//
|
||
|
||
MissypShutdownItem->Value.Who = Value;
|
||
MissypMarkShutdownValueCurrent();
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Put up a popup
|
||
//
|
||
|
||
MissypPopUp( hwnd, MISSYP_POP_UP_CANT_SET_SHUTDOWN, MISSYP_STRING_TITLE_ERROR);
|
||
return(FALSE);
|
||
}
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
MissypGetShutdownPrivs(
|
||
IN HWND hwnd,
|
||
OUT PBOOLEAN NonStandard,
|
||
OUT PBOOLEAN AnyoneLoggedOn,
|
||
OUT PBOOLEAN OpersAndAdmins,
|
||
OUT PBOOLEAN Administrators
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to get an indication of who is assigned
|
||
the shutdown privileges.
|
||
|
||
The boolean parameters will be returned as true if EITHER of
|
||
the shutdown privileges are assigned to ANY of the accounts
|
||
in the corresponding set of accounts.
|
||
|
||
|
||
|
||
A flag indicating shutdown rights are currently assigned in a
|
||
non-standard fashion will be set upon return if any of the
|
||
following conditions are found to be true:
|
||
|
||
1) Any account not in one of the lists of accounts is
|
||
found to have either or both of the shutdown privileges.
|
||
|
||
2) In the lowest group to have shutdown rights, some, but not
|
||
all, of the accounts have both shutdown rights assigned.
|
||
|
||
3) No accounts in any of the groups have shutdown rights
|
||
assigned.
|
||
|
||
|
||
Arguments
|
||
|
||
hwnd - The caller's window. This is used if we need to put
|
||
up an error popup.
|
||
|
||
NonStandard - Returned as TRUE if system is configured in a non-standard
|
||
fashion. Otherwise, returned as FALSE.
|
||
|
||
AnyoneLoggedOn - must be returned as TRUE if any of the SIDs in
|
||
MissypAnyoneSids are assigned either of the shutdown
|
||
privileges.
|
||
|
||
OpersAndAdmins - must be returned as TRUE if any of the SIDs in
|
||
MissypOpersAndAdminsSids are assigned either of the shutdown
|
||
privileges.
|
||
|
||
Administrators - must be returned as TRUE if any of the SIDs in
|
||
MissypAdminsSids are assigned either of the shutdown
|
||
privileges.
|
||
|
||
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - Everything went fine.
|
||
|
||
Other - status returned by an LSA cxall. In this case, a pop-up
|
||
has been displayed indicating the problem.
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS
|
||
NtStatus,
|
||
IgnoreStatus;
|
||
|
||
LSA_HANDLE
|
||
Policy,
|
||
Account;
|
||
|
||
OBJECT_ATTRIBUTES
|
||
ObjectAttributes;
|
||
|
||
ACCESS_MASK
|
||
Access = ACCOUNT_VIEW;
|
||
|
||
PSID
|
||
*Sids = NULL;
|
||
|
||
ULONG
|
||
i,
|
||
j,
|
||
m,
|
||
n,
|
||
Count;
|
||
|
||
PPRIVILEGE_SET
|
||
Privileges;
|
||
|
||
LSA_ENUMERATION_HANDLE
|
||
EnumerationContext;
|
||
|
||
BOOLEAN
|
||
MoreEntries,
|
||
AdminsPrivileged,
|
||
ShutdownPriv = FALSE,
|
||
RemoteShutdownPriv = FALSE;
|
||
|
||
LUID
|
||
ShutdownPrivLuid = {SE_SHUTDOWN_PRIVILEGE, 0},
|
||
RemoteShutdownPrivLuid = {SE_REMOTE_SHUTDOWN_PRIVILEGE, 0};
|
||
|
||
PLUID
|
||
NextPriv;
|
||
|
||
|
||
PMISSYP_ACCOUNTS
|
||
Accounts[3];
|
||
|
||
ULONG
|
||
LevelPrivileged[3],
|
||
PrivilegesAssigned[3][MISSYP_MAX_WELL_KNOWN_ACCOUNTS];
|
||
|
||
|
||
(*NonStandard) = FALSE;
|
||
|
||
//
|
||
// Set up the account tracking structures
|
||
// The PrivilegesAssigned flags will get incremented as
|
||
// we go through the enumeration.
|
||
// The LevelPrivileged[] will indicate how many accounts at each
|
||
// level have at least one of the privileges.
|
||
//
|
||
|
||
Accounts[0] = &MissypAnyoneSids;
|
||
Accounts[1] = &MissypOpersAndAdminsSids;
|
||
Accounts[2] = &MissypAdminsSids;
|
||
|
||
for (i=0; i<3; i++) {
|
||
for (j=0; j<Accounts[i]->Accounts; j++) {
|
||
PrivilegesAssigned[i][j] = 0;
|
||
}
|
||
}
|
||
|
||
LevelPrivileged[0] = 0;
|
||
LevelPrivileged[1] = 0;
|
||
LevelPrivileged[2] = 0;
|
||
|
||
|
||
//
|
||
// Open LSA
|
||
//
|
||
|
||
InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL );
|
||
NtStatus = LsaOpenPolicy(
|
||
NULL, // Local system
|
||
&ObjectAttributes,
|
||
(POLICY_READ | POLICY_EXECUTE),
|
||
&Policy
|
||
);
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// Enumerate all the privileged accounts
|
||
//
|
||
//
|
||
|
||
EnumerationContext = 0;
|
||
MoreEntries = TRUE;
|
||
while (MoreEntries) {
|
||
|
||
NtStatus = LsaEnumerateAccounts( Policy,
|
||
&EnumerationContext,
|
||
(PVOID *)&Sids, //Return buffer
|
||
8000, //prefered max length
|
||
&Count
|
||
);
|
||
if ( NT_SUCCESS(NtStatus) ||
|
||
(NtStatus == STATUS_NO_MORE_ENTRIES)) {
|
||
|
||
if (NtStatus == STATUS_NO_MORE_ENTRIES) {
|
||
NtStatus = STATUS_SUCCESS;
|
||
MoreEntries = FALSE;
|
||
}
|
||
//
|
||
// Open each account
|
||
//
|
||
|
||
for (i=0; i<Count; i++) {
|
||
|
||
NtStatus = LsaOpenAccount( Policy,
|
||
Sids[i],
|
||
Access,
|
||
&Account );
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
NtStatus = LsaEnumeratePrivilegesOfAccount(
|
||
Account,
|
||
&Privileges );
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// Don't yet know if either of the privileges
|
||
// are assigned.
|
||
//
|
||
|
||
ShutdownPriv = FALSE;
|
||
RemoteShutdownPriv = FALSE;
|
||
|
||
|
||
//
|
||
// Loop through these to see if the shutdown
|
||
// privileges have been assigned.
|
||
//
|
||
|
||
for (j=0; j<Privileges->PrivilegeCount; j++) {
|
||
|
||
NextPriv = &Privileges->Privilege[j].Luid;
|
||
if (RtlEqualLuid(&ShutdownPrivLuid, NextPriv)) {
|
||
|
||
ShutdownPriv = TRUE;
|
||
}
|
||
if (RtlEqualLuid(&RemoteShutdownPrivLuid, NextPriv)) {
|
||
RemoteShutdownPriv = TRUE;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure either both privs were assigned
|
||
// or neither priv was assigned. Otherwise,
|
||
// it is non-standard.
|
||
//
|
||
|
||
if (ShutdownPriv != RemoteShutdownPriv) {
|
||
(*NonStandard) = TRUE;
|
||
}
|
||
|
||
//
|
||
// If either privilege is assigned, then we
|
||
// need to go through each of our accounts and
|
||
// increment the assigned privileges count on
|
||
// a matching account (if there is one).
|
||
// If there isn't one, then we have a non-standard
|
||
// configuration.
|
||
//
|
||
|
||
if (ShutdownPriv || RemoteShutdownPriv) {
|
||
|
||
for (m=0; m<3; m++) {
|
||
for (n=0; n<Accounts[m]->Accounts; n++) {
|
||
|
||
if (RtlEqualSid(Sids[i],
|
||
Accounts[m]->Sid[n])) {
|
||
//
|
||
// To get here, at least one
|
||
// priv had to have been assigned.
|
||
// Assume both were and fix the
|
||
// count if we were wrong.
|
||
//
|
||
|
||
LevelPrivileged[m]++;
|
||
PrivilegesAssigned[m][n] += 2;
|
||
|
||
if (ShutdownPriv != RemoteShutdownPriv) {
|
||
PrivilegesAssigned[m][n]--;
|
||
(*NonStandard) = TRUE;
|
||
}
|
||
} //end_if (EqualSid)
|
||
} //end_for (n)
|
||
} //end_for (m)
|
||
|
||
|
||
}
|
||
|
||
|
||
IgnoreStatus = LsaFreeMemory( Privileges );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
|
||
IgnoreStatus = LsaClose( Account );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
} //end_for (i)
|
||
|
||
if (Sids != NULL) {
|
||
IgnoreStatus = LsaFreeMemory( Sids );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
Sids = NULL;
|
||
}
|
||
}
|
||
} //end_while
|
||
|
||
IgnoreStatus = LsaClose( Policy );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Now we have to figure out if the lowest level to have
|
||
// any privileges has a complete set of privileges to
|
||
// determine if that might be cause for a non-standard
|
||
// configuration. We can also set the ouput parameters
|
||
// while we are at it.
|
||
//
|
||
|
||
if (LevelPrivileged[2] != 0) {
|
||
//
|
||
// The Administrators account has the privileges.
|
||
// This will effect our assessment of Opers and Admins.
|
||
//
|
||
|
||
AdminsPrivileged = TRUE;
|
||
} else {
|
||
AdminsPrivileged = FALSE;
|
||
}
|
||
|
||
if (LevelPrivileged[0] == 0) {
|
||
(*AnyoneLoggedOn) = FALSE;
|
||
} else {
|
||
(*AnyoneLoggedOn) = TRUE;
|
||
if (LevelPrivileged[0] != Accounts[0]->Accounts) {
|
||
(*NonStandard) = TRUE;
|
||
}
|
||
}
|
||
|
||
if ( (LevelPrivileged[1] == 0) ||
|
||
(AdminsPrivileged && (LevelPrivileged[1] == 1)) ) {
|
||
(*OpersAndAdmins) = FALSE;
|
||
} else {
|
||
(*OpersAndAdmins) = TRUE;
|
||
if ( !(*AnyoneLoggedOn) && //No privs at lowest level
|
||
(LevelPrivileged[1] != Accounts[1]->Accounts) ) {
|
||
(*NonStandard) = TRUE;
|
||
}
|
||
}
|
||
|
||
if (LevelPrivileged[2] == 0) {
|
||
(*Administrators) = FALSE;
|
||
} else {
|
||
(*Administrators) = TRUE;
|
||
if ( !(*AnyoneLoggedOn) && //No privs at lowest level
|
||
!(*OpersAndAdmins) && //Or next level
|
||
(LevelPrivileged[2] != Accounts[2]->Accounts) ) {
|
||
(*NonStandard) = TRUE;
|
||
}
|
||
}
|
||
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
MissypSetShutdownPrivs(
|
||
IN PMISSYP_ACCOUNTS Accounts
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to assign the SHUTDOWN and REMOTE_SHUTDOWN
|
||
privileges to a specified list of accounts.
|
||
|
||
Arguments
|
||
|
||
Accounts - Points to a counted array of SID pointers. These
|
||
are the accounts to be assigned the shutdown privileges.
|
||
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - The privileges were successfully assigned.
|
||
|
||
Other - error status from one of the lsa or other api called.
|
||
|
||
If an error is encountered, the caller is expected to put
|
||
up a popup indicating what the problem is.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS
|
||
NtStatus,
|
||
IgnoreStatus;
|
||
|
||
LSA_HANDLE
|
||
Policy,
|
||
Account;
|
||
|
||
OBJECT_ATTRIBUTES
|
||
ObjectAttributes;
|
||
|
||
ACCESS_MASK
|
||
PolicyAccess = (POLICY_READ | POLICY_WRITE | POLICY_EXECUTE),
|
||
AccountAccess = (ACCOUNT_WRITE);
|
||
|
||
LSA_ENUMERATION_HANDLE
|
||
EnumerationContext;
|
||
|
||
PSID
|
||
*Sids = NULL;
|
||
|
||
BOOLEAN
|
||
MoreEntries,
|
||
Assigned,
|
||
PrivilegesAssigned[MISSYP_MAX_WELL_KNOWN_ACCOUNTS];
|
||
|
||
ULONG
|
||
i,
|
||
j,
|
||
Count;
|
||
|
||
LUID
|
||
ShutdownPrivLuid = {SE_SHUTDOWN_PRIVILEGE, 0},
|
||
RemoteShutdownPrivLuid = {SE_REMOTE_SHUTDOWN_PRIVILEGE, 0};
|
||
|
||
CHAR
|
||
PrivilegesBuffer[sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES)];
|
||
|
||
PPRIVILEGE_SET
|
||
PrivilegeSet = (PPRIVILEGE_SET)((PVOID)PrivilegesBuffer);
|
||
|
||
|
||
|
||
|
||
//
|
||
// Initialize to indicate none of the accounts have had
|
||
// privileges assigned yet.
|
||
//
|
||
|
||
for (i=0; i<Accounts->Accounts; i++) {
|
||
PrivilegesAssigned[i] = FALSE;
|
||
}
|
||
|
||
//
|
||
// Set up a privilege set with the required privileges
|
||
//
|
||
|
||
PrivilegeSet->PrivilegeCount = 2;
|
||
PrivilegeSet->Control = 0;
|
||
PrivilegeSet->Privilege[0].Luid = ShutdownPrivLuid;
|
||
PrivilegeSet->Privilege[0].Attributes = 0;
|
||
PrivilegeSet->Privilege[1].Luid = RemoteShutdownPrivLuid;
|
||
PrivilegeSet->Privilege[1].Attributes = 0;
|
||
|
||
//
|
||
// Open LSA
|
||
//
|
||
|
||
InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL );
|
||
NtStatus = LsaOpenPolicy(
|
||
NULL, // Local system
|
||
&ObjectAttributes,
|
||
PolicyAccess,
|
||
&Policy
|
||
);
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
|
||
//
|
||
// Enumerate the accounts
|
||
// For each account, we will either assign or remove the
|
||
// shutdown privileges.
|
||
//
|
||
|
||
EnumerationContext = 0;
|
||
MoreEntries = TRUE;
|
||
while (MoreEntries) {
|
||
|
||
NtStatus = LsaEnumerateAccounts( Policy,
|
||
&EnumerationContext,
|
||
(PVOID *)&Sids, //Return buffer
|
||
8000, //prefered max length
|
||
&Count
|
||
);
|
||
if ( NT_SUCCESS(NtStatus) ||
|
||
(NtStatus == STATUS_NO_MORE_ENTRIES)) {
|
||
|
||
if (NtStatus == STATUS_NO_MORE_ENTRIES) {
|
||
NtStatus = STATUS_SUCCESS;
|
||
MoreEntries = FALSE;
|
||
}
|
||
|
||
//
|
||
// Open each account
|
||
//
|
||
|
||
for (i=0; i<Count; i++) {
|
||
|
||
NtStatus = LsaOpenAccount( Policy,
|
||
Sids[i],
|
||
AccountAccess,
|
||
&Account );
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// See if this is one of the accounts we must
|
||
// assign privileges to.
|
||
//
|
||
|
||
Assigned = FALSE;
|
||
for (j=0; j<Accounts->Accounts; j++) {
|
||
if (RtlEqualSid(Sids[i], Accounts->Sid[j])) {
|
||
NtStatus = LsaAddPrivilegesToAccount(
|
||
Account,
|
||
PrivilegeSet);
|
||
PrivilegesAssigned[j] = TRUE;
|
||
Assigned = TRUE;
|
||
break; //for loop
|
||
}
|
||
} //end_for
|
||
|
||
if (!Assigned) {
|
||
NtStatus = LsaRemovePrivilegesFromAccount(
|
||
Account,
|
||
FALSE, // Don't remove all privs
|
||
PrivilegeSet);
|
||
if (NtStatus = STATUS_INVALID_PARAMETER) {
|
||
//
|
||
// Don't worry about it. It just means we
|
||
// tried to remove privileges from someone
|
||
// with no privileges.
|
||
//
|
||
|
||
NtStatus = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// Put up a popup
|
||
//
|
||
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
IgnoreStatus = LsaClose( Account );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
}
|
||
|
||
} //end_for (i)
|
||
|
||
if (Sids != NULL) {
|
||
IgnoreStatus = LsaFreeMemory( Sids );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
Sids = NULL;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Couldn't enumerate - put up a popup
|
||
//
|
||
|
||
return(NtStatus);
|
||
}
|
||
} //end_while
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Now make sure all the necessary accounts existed.
|
||
// If not, create them and assign the privileges.
|
||
//
|
||
|
||
for (i=0; i<Accounts->Accounts; i++) {
|
||
|
||
if (!PrivilegesAssigned[i]) {
|
||
|
||
//
|
||
// This account must not have existed and so didn't
|
||
// show up in the enumeration.
|
||
//
|
||
|
||
NtStatus = LsaCreateAccount( Policy,
|
||
Accounts->Sid[i],
|
||
AccountAccess,
|
||
&Account
|
||
);
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// Put up a popup
|
||
//
|
||
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
//
|
||
// Assign the privileges
|
||
//
|
||
|
||
NtStatus = LsaAddPrivilegesToAccount( Account,
|
||
PrivilegeSet);
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// Put up a popup
|
||
//
|
||
|
||
return(NtStatus);
|
||
}
|
||
|
||
IgnoreStatus = LsaClose( Account );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
} //end_for
|
||
|
||
|
||
|
||
IgnoreStatus = LsaClose( Policy );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
SECMGR_WHO
|
||
MissypGetShutdownRecommendation(
|
||
IN ULONG SecurityLevel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the recommended shutdown setting for this
|
||
workstation given a specified security level.
|
||
|
||
Arguments
|
||
|
||
SecurityLevel - If this value is SECMGR_LEVEL_CURRENT, then the current
|
||
security level known to Missy will be used. Otherwise, the provided
|
||
security level will be used.
|
||
|
||
|
||
Return Values:
|
||
|
||
The recommended setting.
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
ULONG
|
||
EffectiveLevel = SecurityLevel;
|
||
|
||
if (SecurityLevel == SECMGR_LEVEL_CURRENT) {
|
||
EffectiveLevel = MissypSecMgrControl->SecurityLevel;
|
||
}
|
||
|
||
|
||
//
|
||
// Figure out what our recommendation is
|
||
//
|
||
// WinNt running Standard or lower ==> Anyone
|
||
// Otherwise ==> Anyone logged on
|
||
//
|
||
|
||
if ((MissypProductType == NtProductWinNt) &&
|
||
( EffectiveLevel <= SECMGR_LEVEL_STANDARD) ) {
|
||
return(SecMgrAnyone);
|
||
} else {
|
||
return(SecMgrAnyoneLoggedOn);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
MissypUpdateShutdownRecommendation( VOID )
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function updates our recommended value field and flag and sets
|
||
the Shutdown value as CURRENT.
|
||
|
||
Arguments
|
||
|
||
None.
|
||
|
||
|
||
Return Values:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// set our recommendation in the item control block.
|
||
//
|
||
|
||
MissypShutdownItem->RecommendedValue.Who = MissypGetShutdownRecommendation( SECMGR_LEVEL_CURRENT );
|
||
|
||
//
|
||
// Indicate whether the current value matches the recommended value
|
||
//
|
||
|
||
if (MissypShutdownItem->Value.Who == MissypShutdownItem->RecommendedValue.Who) {
|
||
MissypShutdownItem->Flags |= SECMGR_ITEM_FLAG_VALUE_RECOMMENDED; //Recommended value
|
||
} else {
|
||
MissypShutdownItem->Flags &= (~SECMGR_ITEM_FLAG_VALUE_RECOMMENDED); //Not recommended value
|
||
|
||
//
|
||
// Is it stronger or weaker than the recommended value
|
||
//
|
||
|
||
if (MissypShutdownItem->Value.Who > MissypShutdownItem->RecommendedValue.Who) {
|
||
MissypShutdownItem->Flags |= SECMGR_ITEM_FLAG_VALUE_STRONGER;
|
||
} else {
|
||
MissypShutdownItem->Flags &= (~SECMGR_ITEM_FLAG_VALUE_STRONGER);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Locally callable functions //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
VOID
|
||
MissypMarkShutdownValueCurrent( VOID )
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function updates our recommended value field and flag and sets
|
||
the Shutdown value as CURRENT.
|
||
|
||
Arguments
|
||
|
||
None.
|
||
|
||
|
||
Return Values:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// set our recommendation in the item control block.
|
||
//
|
||
|
||
MissypUpdateShutdownRecommendation();
|
||
|
||
|
||
//
|
||
// Indicate we have a value
|
||
//
|
||
|
||
MissypShutdownItem->Flags |= SECMGR_ITEM_FLAG_VALUE_CURRENT;
|
||
|
||
return;
|
||
}
|
||
|
||
|