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.
3028 lines
93 KiB
3028 lines
93 KiB
/*++
|
|
|
|
Copyright (c) 1990 - 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
security.c
|
|
|
|
Abstract:
|
|
|
|
This module interfaces with the security system
|
|
|
|
Author:
|
|
|
|
Andrew Bell (andrewbe) June 1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <precomp.h>
|
|
|
|
/******************************************************************************
|
|
|
|
|
|
The printing security model
|
|
---------------------------
|
|
|
|
In printing we define a hierarchy of three objects:
|
|
|
|
|
|
SERVER
|
|
|
|
/ \
|
|
/ \
|
|
/ ...
|
|
/
|
|
|
|
PRINTER
|
|
|
|
/ \
|
|
/ \
|
|
/ ...
|
|
/
|
|
|
|
DOCUMENT
|
|
|
|
|
|
|
|
|
|
The following types of operation may be performed on each of these objects:
|
|
|
|
|
|
SERVER: Install/Deinstall Driver
|
|
Create Printer
|
|
Enumerate Printers
|
|
|
|
PRINTER: Pause/Resume
|
|
Delete
|
|
Connect to/Disconnect
|
|
Set
|
|
Enumerate Documents
|
|
|
|
DOCUMENT: Pause/Resume
|
|
Delete
|
|
Set Attributes
|
|
|
|
|
|
|
|
For product LanMan NT, five classes of user are defined, and,
|
|
for Windows NT, four classes are defined.
|
|
The following privileges are assigned to each class:
|
|
|
|
|
|
|
|
Administrators
|
|
|
|
Print Operators
|
|
|
|
System Operators
|
|
|
|
Power Users
|
|
|
|
Owners
|
|
|
|
Everyone (World)
|
|
|
|
|
|
|
|
|
|
******************************************************************************/
|
|
|
|
#define DBGCHK( Condition, ErrorInfo ) \
|
|
if( Condition ) DBGMSG( DBG_WARNING, ErrorInfo )
|
|
|
|
#define TOKENLENGTH( Token ) ( *( ( (PDWORD)Token ) - 1 ) )
|
|
|
|
|
|
|
|
/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
|
|
Came from \\orville\razzle\src\private\newsam\server\bldsam3.c
|
|
|
|
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
|
|
|
|
|
|
GENERIC_MAPPING GenericMapping[SPOOLER_OBJECT_COUNT] =
|
|
{
|
|
{ SERVER_READ, SERVER_WRITE, SERVER_EXECUTE, SERVER_ALL_ACCESS },
|
|
{ PRINTER_READ, PRINTER_WRITE, PRINTER_EXECUTE, PRINTER_ALL_ACCESS },
|
|
{ JOB_READ, JOB_WRITE, JOB_EXECUTE, JOB_ALL_ACCESS }
|
|
};
|
|
|
|
/* !!! Should these be translatable??? */
|
|
|
|
LPWSTR ObjectTypeName[SPOOLER_OBJECT_COUNT] =
|
|
{
|
|
L"Server", L"Printer", L"Document"
|
|
};
|
|
|
|
WCHAR *szSpooler = L"Spooler";
|
|
|
|
|
|
LUID AuditValue;
|
|
|
|
PSECURITY_DESCRIPTOR pServerSecurityDescriptor;
|
|
LUID gLoadDriverPrivilegeLuid;
|
|
PSID pLocalSystemSid;
|
|
PSID pGuestsSid;
|
|
PSID pNetworkLogonSid;
|
|
|
|
BOOL ServerGenerateOnClose; /* Do we need this for the server? */
|
|
|
|
#if DBG
|
|
#define DBG_ACCESS_TYPE_SERVER_ALL_ACCESS 0
|
|
#define DBG_ACCESS_TYPE_SERVER_READ 1
|
|
#define DBG_ACCESS_TYPE_SERVER_WRITE 2
|
|
#define DBG_ACCESS_TYPE_SERVER_EXECUTE 3
|
|
#define DBG_ACCESS_TYPE_PRINTER_ALL_ACCESS 4
|
|
#define DBG_ACCESS_TYPE_PRINTER_READ 5
|
|
#define DBG_ACCESS_TYPE_PRINTER_WRITE 6
|
|
#define DBG_ACCESS_TYPE_PRINTER_EXECUTE 7
|
|
#define DBG_ACCESS_TYPE_JOB_ALL_ACCESS 8
|
|
#define DBG_ACCESS_TYPE_JOB_READ 9
|
|
#define DBG_ACCESS_TYPE_JOB_WRITE 10
|
|
#define DBG_ACCESS_TYPE_JOB_EXECUTE 11
|
|
#define DBG_ACCESS_TYPE_PRINTER_ACCESS_USE 12
|
|
#define DBG_ACCESS_TYPE_PRINTER_ACCESS_ADMINISTER 13
|
|
#define DBG_ACCESS_TYPE_SERVER_ACCESS_ENUMERATE 14
|
|
#define DBG_ACCESS_TYPE_SERVER_ACCESS_ADMINISTER 15
|
|
#define DBG_ACCESS_TYPE_JOB_ACCESS_ADMINISTER 16
|
|
#define DBG_ACCESS_TYPE_DELETE 17
|
|
#define DBG_ACCESS_TYPE_WRITE_DAC 18
|
|
#define DBG_ACCESS_TYPE_WRITE_OWNER 19
|
|
#define DBG_ACCESS_TYPE_ACCESS_SYSTEM_SECURITY 20
|
|
// These two should come last:
|
|
#define DBG_ACCESS_TYPE_UNKNOWN 21
|
|
#define DBG_ACCESS_TYPE_COUNT 22
|
|
|
|
typedef struct _DBG_ACCESS_TYPE_MAPPING
|
|
{
|
|
DWORD Type;
|
|
LPWSTR Name;
|
|
}
|
|
DBG_ACCESS_TYPE_MAPPING, *PDBG_ACCESS_TYPE_MAPPING;
|
|
|
|
DBG_ACCESS_TYPE_MAPPING DbgAccessTypeMapping[DBG_ACCESS_TYPE_COUNT] =
|
|
{
|
|
{ SERVER_ALL_ACCESS, L"SERVER_ALL_ACCESS" },
|
|
{ SERVER_READ, L"SERVER_READ" },
|
|
{ SERVER_WRITE, L"SERVER_WRITE" },
|
|
{ SERVER_EXECUTE, L"SERVER_EXECUTE" },
|
|
{ PRINTER_ALL_ACCESS, L"PRINTER_ALL_ACCESS" },
|
|
{ PRINTER_READ, L"PRINTER_READ" },
|
|
{ PRINTER_WRITE, L"PRINTER_WRITE" },
|
|
{ PRINTER_EXECUTE, L"PRINTER_EXECUTE" },
|
|
{ JOB_ALL_ACCESS, L"JOB_ALL_ACCESS" },
|
|
{ JOB_READ, L"JOB_READ" },
|
|
{ JOB_WRITE, L"JOB_WRITE" },
|
|
{ JOB_EXECUTE, L"JOB_EXECUTE" },
|
|
{ PRINTER_ACCESS_USE, L"PRINTER_ACCESS_USE" },
|
|
{ PRINTER_ACCESS_ADMINISTER, L"PRINTER_ACCESS_ADMINISTER" },
|
|
{ SERVER_ACCESS_ENUMERATE, L"SERVER_ACCESS_ENUMERATE" },
|
|
{ SERVER_ACCESS_ADMINISTER, L"SERVER_ACCESS_ADMINISTER" },
|
|
{ JOB_ACCESS_ADMINISTER, L"JOB_ACCESS_ADMINISTER" },
|
|
{ DELETE, L"DELETE" },
|
|
{ WRITE_DAC, L"WRITE_DAC" },
|
|
{ WRITE_OWNER, L"WRITE_OWNER" },
|
|
{ ACCESS_SYSTEM_SECURITY, L"ACCESS_SYSTEM_SECURITY" },
|
|
{ 0, L"UNKNOWN" }
|
|
};
|
|
|
|
|
|
LPWSTR DbgGetAccessTypeName( DWORD AccessType )
|
|
{
|
|
PDBG_ACCESS_TYPE_MAPPING pMapping;
|
|
DWORD i;
|
|
|
|
pMapping = DbgAccessTypeMapping;
|
|
i = 0;
|
|
|
|
while( ( i < ( DBG_ACCESS_TYPE_COUNT - 1 ) ) && ( pMapping[i].Type != AccessType ) )
|
|
i++;
|
|
|
|
return pMapping[i].Name;
|
|
}
|
|
|
|
#endif /* DBG */
|
|
|
|
|
|
BOOL
|
|
BuildJobOwnerSecurityDescriptor(
|
|
IN HANDLE hToken,
|
|
OUT PSECURITY_DESCRIPTOR *ppSD
|
|
);
|
|
|
|
VOID
|
|
DestroyJobOwnerSecurityDescriptor(
|
|
IN PSECURITY_DESCRIPTOR pSD
|
|
);
|
|
|
|
BOOL
|
|
SetRequiredPrivileges(
|
|
IN HANDLE TokenHandle,
|
|
OUT PTOKEN_PRIVILEGES *ppPreviousTokenPrivileges,
|
|
OUT PDWORD pPreviousTokenPrivilegesLength
|
|
);
|
|
|
|
BOOL
|
|
ResetRequiredPrivileges(
|
|
IN HANDLE TokenHandle,
|
|
IN PTOKEN_PRIVILEGES pPreviousTokenPrivileges,
|
|
IN DWORD PreviousTokenPrivilegesLength
|
|
);
|
|
|
|
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
AllocateLocalSD(
|
|
PSECURITY_DESCRIPTOR pSystemAllocatedSD
|
|
);
|
|
|
|
DWORD
|
|
GetHackOutAce(
|
|
PACL pDacl
|
|
);
|
|
|
|
#define MAX_ACE 20
|
|
|
|
|
|
#if DBG
|
|
|
|
typedef struct _STANDARD_ACE {
|
|
ACE_HEADER Header;
|
|
ACCESS_MASK Mask;
|
|
PSID Sid;
|
|
} STANDARD_ACE;
|
|
typedef STANDARD_ACE *PSTANDARD_ACE;
|
|
|
|
//
|
|
// The following macros used by DumpAcl(), these macros and DumpAcl() are
|
|
// stolen from private\ntos\se\ctaccess.c (written by robertre) for
|
|
// debugging purposes.
|
|
//
|
|
|
|
//
|
|
// Returns a pointer to the first Ace in an Acl (even if the Acl is empty).
|
|
//
|
|
|
|
#define FirstAce(Acl) ((PVOID)((LPBYTE)(Acl) + sizeof(ACL)))
|
|
|
|
//
|
|
// Returns a pointer to the next Ace in a sequence (even if the input
|
|
// Ace is the one in the sequence).
|
|
//
|
|
|
|
#define NextAce(Ace) ((PVOID)((LPBYTE)(Ace) + ((PACE_HEADER)(Ace))->AceSize))
|
|
|
|
|
|
VOID
|
|
DumpAcl(
|
|
IN PACL Acl
|
|
);
|
|
|
|
#endif //if DBG
|
|
|
|
|
|
/* Dummy access mask which will never be checked, but required
|
|
* by the ACL editor, so that Document Properties is not undefined
|
|
* for containers (i.e. printers).
|
|
* This mask alone must not be used for any other ACE, since it
|
|
* will be used to find the no-inherit ACE which propagates
|
|
* onto printers.
|
|
*/
|
|
#define DUMMY_ACE_ACCESS_MASK READ_CONTROL
|
|
|
|
|
|
/* CreateServerSecurityDescriptor
|
|
*
|
|
* Arguments: None
|
|
*
|
|
* Return: The security descriptor returned by BuildPrintObjectProtection.
|
|
*
|
|
*/
|
|
PSECURITY_DESCRIPTOR
|
|
CreateServerSecurityDescriptor(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD ObjectType = SPOOLER_OBJECT_SERVER;
|
|
NT_PRODUCT_TYPE NtProductType;
|
|
PSID AceSid[MAX_ACE]; // Don't expect more than MAX_ACE ACEs in any of these.
|
|
ACCESS_MASK AceMask[MAX_ACE]; // Access masks corresponding to Sids
|
|
BYTE InheritFlags[MAX_ACE]; //
|
|
UCHAR AceType[MAX_ACE];
|
|
DWORD AceCount;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
|
|
PSID WorldSid = NULL;
|
|
PSID AdminsAliasSid = NULL;
|
|
PSID PrintOpsAliasSid = NULL;
|
|
PSID SystemOpsAliasSid = NULL;
|
|
PSID PowerUsersAliasSid = NULL;
|
|
PSID CreatorOwnerSid = NULL;
|
|
PSECURITY_DESCRIPTOR ServerSD = NULL;
|
|
BOOL OK;
|
|
|
|
|
|
//
|
|
// Printer SD
|
|
//
|
|
|
|
AceCount = 0;
|
|
|
|
/* Creator-Owner SID: */
|
|
|
|
OK = AllocateAndInitializeSid( &CreatorSidAuthority, 1,
|
|
SECURITY_CREATOR_OWNER_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&CreatorOwnerSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
/* The following is a dummy ACE needed for the ACL editor.
|
|
* Note this is a gross hack, and will result in two ACEs
|
|
* being propagated onto printers when they are created,
|
|
* one of which must be deleted.
|
|
*/
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = CreatorOwnerSid;
|
|
AceMask[AceCount] = DUMMY_ACE_ACCESS_MASK;
|
|
InheritFlags[AceCount] = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE;
|
|
AceCount++;
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = CreatorOwnerSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE;
|
|
AceCount++;
|
|
|
|
|
|
/* World SID */
|
|
|
|
OK = AllocateAndInitializeSid( &WorldSidAuthority, 1,
|
|
SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&WorldSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = WorldSid;
|
|
AceMask[AceCount] = SERVER_EXECUTE;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = WorldSid;
|
|
AceMask[AceCount] = GENERIC_EXECUTE;
|
|
InheritFlags[AceCount] = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE;
|
|
AceCount++;
|
|
|
|
|
|
/* Admins alias SID */
|
|
|
|
OK = AllocateAndInitializeSid( &NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&AdminsAliasSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = AdminsAliasSid;
|
|
AceMask[AceCount] = SERVER_ALL_ACCESS;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = AdminsAliasSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
AceCount++;
|
|
|
|
OK = RtlGetNtProductType( &NtProductType );
|
|
DBGCHK( !OK, ( "Couldn't get product type" ) );
|
|
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
switch (NtProductType) {
|
|
case NtProductLanManNt:
|
|
// case NtProductMember:
|
|
|
|
/* Print Ops alias SID */
|
|
|
|
OK = AllocateAndInitializeSid( &NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_PRINT_OPS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&PrintOpsAliasSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = PrintOpsAliasSid;
|
|
AceMask[AceCount] = SERVER_ALL_ACCESS;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = PrintOpsAliasSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
AceCount++;
|
|
|
|
/* System Ops alias SID */
|
|
|
|
OK = AllocateAndInitializeSid( &NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_SYSTEM_OPS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&SystemOpsAliasSid );
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = SystemOpsAliasSid;
|
|
AceMask[AceCount] = SERVER_ALL_ACCESS;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = SystemOpsAliasSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
AceCount++;
|
|
|
|
break;
|
|
|
|
case NtProductWinNt:
|
|
default:
|
|
|
|
{
|
|
OSVERSIONINFOEX OsVersion = {0};
|
|
|
|
OsVersion.dwOSVersionInfoSize = sizeof(OsVersion);
|
|
|
|
//
|
|
// Whistler Personal does not have the Power Users group.
|
|
//
|
|
if (GetVersionEx((LPOSVERSIONINFO)&OsVersion) &&
|
|
!(OsVersion.wProductType==VER_NT_WORKSTATION && OsVersion.wSuiteMask & VER_SUITE_PERSONAL)) {
|
|
|
|
OK = AllocateAndInitializeSid( &NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_POWER_USERS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&PowerUsersAliasSid );
|
|
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = PowerUsersAliasSid;
|
|
AceMask[AceCount] = SERVER_ALL_ACCESS;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = PowerUsersAliasSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
AceCount++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
DBGCHK( ( AceCount > MAX_ACE ), ( "ACE count exceeded" ) );
|
|
|
|
if ( AceCount > MAX_ACE ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
OK = BuildPrintObjectProtection( AceType,
|
|
AceCount,
|
|
AceSid,
|
|
AceMask,
|
|
InheritFlags,
|
|
AdminsAliasSid,
|
|
AdminsAliasSid,
|
|
&GenericMapping[ObjectType],
|
|
&ServerSD );
|
|
|
|
DBGCHK( !OK, ( "BuildPrintObjectProtection failed" ) );
|
|
|
|
CleanUp:
|
|
|
|
if (WorldSid) {
|
|
FreeSid( WorldSid );
|
|
}
|
|
if (AdminsAliasSid) {
|
|
FreeSid( AdminsAliasSid );
|
|
}
|
|
if (CreatorOwnerSid) {
|
|
FreeSid( CreatorOwnerSid );
|
|
}
|
|
if (PrintOpsAliasSid) {
|
|
FreeSid( PrintOpsAliasSid );
|
|
}
|
|
if (SystemOpsAliasSid) {
|
|
FreeSid( SystemOpsAliasSid );
|
|
}
|
|
if (PowerUsersAliasSid) {
|
|
FreeSid( PowerUsersAliasSid );
|
|
}
|
|
|
|
pServerSecurityDescriptor = ServerSD;
|
|
|
|
return ServerSD;
|
|
}
|
|
|
|
|
|
/* CreatePrinterSecurityDescriptor
|
|
*
|
|
* Creates a default security descriptor for a printer by inheritance
|
|
* of the access flags in the local spooler's security descriptor.
|
|
* The resulting descriptor is allocated from the process' heap
|
|
* and should be freed by DeletePrinterSecurityDescriptor.
|
|
*
|
|
* Argument: pCreatorSecurityDescriptor - if the creator supplies
|
|
* a security descriptor, this should point to it. Otherwise
|
|
* it should be NULL.
|
|
*
|
|
* Return: The printer's security descriptor
|
|
*
|
|
*/
|
|
PSECURITY_DESCRIPTOR
|
|
CreatePrinterSecurityDescriptor(
|
|
PSECURITY_DESCRIPTOR pCreatorSecurityDescriptor
|
|
)
|
|
{
|
|
HANDLE ClientToken;
|
|
PSECURITY_DESCRIPTOR pPrivateObjectSecurity;
|
|
PSECURITY_DESCRIPTOR pPrinterSecurityDescriptor;
|
|
DWORD ObjectType = SPOOLER_OBJECT_PRINTER;
|
|
BOOL OK;
|
|
HANDLE hToken;
|
|
BOOL DaclPresent;
|
|
PACL pDacl;
|
|
BOOL DaclDefaulted = FALSE;
|
|
DWORD HackOutAce;
|
|
|
|
|
|
if( GetTokenHandle( &ClientToken ) )
|
|
{
|
|
hToken = RevertToPrinterSelf( );
|
|
|
|
OK = CreatePrivateObjectSecurity( pServerSecurityDescriptor,
|
|
pCreatorSecurityDescriptor,
|
|
&pPrivateObjectSecurity,
|
|
TRUE, // This is a container
|
|
ClientToken,
|
|
&GenericMapping[ObjectType] );
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
|
|
CloseHandle(ClientToken);
|
|
|
|
DBGCHK( !OK, ( "CreatePrivateObjectSecurity failed: Error %d", GetLastError() ) );
|
|
|
|
if( !OK )
|
|
return NULL;
|
|
|
|
pPrinterSecurityDescriptor = pPrivateObjectSecurity;
|
|
|
|
if( !pCreatorSecurityDescriptor )
|
|
{
|
|
GetSecurityDescriptorDacl( pPrinterSecurityDescriptor,
|
|
&DaclPresent,
|
|
&pDacl,
|
|
&DaclDefaulted );
|
|
|
|
|
|
|
|
/* HACK HACK HACK HACK HACK
|
|
*
|
|
* We defined an extra ACE for the benefit of the ACL editor.
|
|
* This is container-inherit,
|
|
* and we want it to propagate onto documents.
|
|
* However, this means it will also propagate onto printers,
|
|
* which we definitely don't want.
|
|
*/
|
|
HackOutAce = GetHackOutAce( pDacl );
|
|
|
|
if( HackOutAce != (DWORD)-1 )
|
|
DeleteAce( pDacl, HackOutAce );
|
|
|
|
|
|
#if DBG
|
|
if( MODULE_DEBUG & DBG_SECURITY ){
|
|
DBGMSG( DBG_SECURITY, ( "Printer security descriptor DACL:\n" ));
|
|
DumpAcl( pDacl );
|
|
}
|
|
#endif /* DBG */
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
OK = FALSE;
|
|
}
|
|
|
|
|
|
return ( OK ? pPrinterSecurityDescriptor : NULL );
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
DWORD GetHackOutAce( PACL pDacl )
|
|
{
|
|
DWORD i;
|
|
PACCESS_ALLOWED_ACE pAce;
|
|
BOOL OK = TRUE;
|
|
|
|
i = 0;
|
|
|
|
while( OK )
|
|
{
|
|
OK = GetAce( pDacl, i, (LPVOID *)&pAce );
|
|
|
|
DBGCHK( !OK, ( "Failed to get ACE. Error %d", GetLastError() ) );
|
|
|
|
/* Find the dummy ace that isn't inherit-only:
|
|
*/
|
|
if( OK && ( pAce->Mask == DUMMY_ACE_ACCESS_MASK )
|
|
&&( !( pAce->Header.AceFlags & INHERIT_ONLY_ACE ) ) )
|
|
return i;
|
|
}
|
|
|
|
return (DWORD)-1;
|
|
}
|
|
|
|
|
|
/* SetPrinterSecurityDescriptor
|
|
*
|
|
* Arguments:
|
|
*
|
|
* SecurityInformation - Type of security information to be applied,
|
|
* typically DACL_SECURITY_INFORMATION. (This is a 32-bit array.)
|
|
*
|
|
* pModificationDescriptor - A pointer to a pointer to a security
|
|
* descriptor to be applied to the previous descriptor.
|
|
*
|
|
* pObjectSecurityDescriptor - The previous descriptor which is to be
|
|
* modified.
|
|
*
|
|
*
|
|
* Return:
|
|
*
|
|
* Boolean indicating success or otherwise.
|
|
*
|
|
*/
|
|
BOOL
|
|
SetPrinterSecurityDescriptor(
|
|
SECURITY_INFORMATION SecurityInformation,
|
|
PSECURITY_DESCRIPTOR pModificationDescriptor,
|
|
PSECURITY_DESCRIPTOR *ppObjectsSecurityDescriptor
|
|
)
|
|
{
|
|
HANDLE ClientToken;
|
|
DWORD ObjectType = SPOOLER_OBJECT_PRINTER;
|
|
BOOL OK = FALSE;
|
|
HANDLE hToken;
|
|
|
|
if( GetTokenHandle( &ClientToken ) )
|
|
{
|
|
/* SetPrivateObjectSecurity should not be called when we are
|
|
* impersonating a client, since it may need to allocate memory:
|
|
*/
|
|
hToken = RevertToPrinterSelf( );
|
|
|
|
OK = SetPrivateObjectSecurity( SecurityInformation,
|
|
pModificationDescriptor,
|
|
ppObjectsSecurityDescriptor,
|
|
&GenericMapping[ObjectType],
|
|
ClientToken );
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
|
|
DBGCHK( !OK, ( "SetPrivateObjectSecurity failed: Error %d", GetLastError() ) );
|
|
|
|
CloseHandle(ClientToken);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
/* CreateDocumentSecurityDescriptor
|
|
*
|
|
* Creates a default security descriptor for a document by inheritance
|
|
* of the access flags in the supplied printer security descriptor.
|
|
* The resulting descriptor is allocated from the process' heap
|
|
* and should be freed by DeleteDocumentSecurityDescriptor.
|
|
*
|
|
* Argument: The printer's security descriptor
|
|
*
|
|
* Return: The document's security descriptor
|
|
*
|
|
*/
|
|
PSECURITY_DESCRIPTOR
|
|
CreateDocumentSecurityDescriptor(
|
|
PSECURITY_DESCRIPTOR pPrinterSecurityDescriptor
|
|
)
|
|
{
|
|
HANDLE ClientToken;
|
|
PSECURITY_DESCRIPTOR pPrivateObjectSecurity;
|
|
PSECURITY_DESCRIPTOR pDocumentSecurityDescriptor;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
DWORD ObjectType = SPOOLER_OBJECT_DOCUMENT;
|
|
BOOL OK = FALSE;
|
|
HANDLE hToken;
|
|
|
|
if( GetTokenHandle( &ClientToken ) )
|
|
{
|
|
hToken = RevertToPrinterSelf( );
|
|
|
|
//
|
|
// The function CreateDocumentSecurityDescriptor does not preserve
|
|
// the last error correctly. If CreatePrivateObjectSecurityEx fails,
|
|
// it sets the last error. But after that,
|
|
//
|
|
OK = BuildJobOwnerSecurityDescriptor(ClientToken, &pSD) &&
|
|
CreatePrivateObjectSecurityEx(pPrinterSecurityDescriptor,
|
|
pSD,
|
|
&pPrivateObjectSecurity,
|
|
NULL,
|
|
FALSE, // This is not a container
|
|
SEF_DACL_AUTO_INHERIT | SEF_SACL_AUTO_INHERIT,
|
|
ClientToken,
|
|
&GenericMapping[ObjectType] );
|
|
|
|
DestroyJobOwnerSecurityDescriptor(pSD);
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
|
|
CloseHandle(ClientToken);
|
|
|
|
DBGCHK( !OK, ( "CreatePrivateObjectSecurity failed: Error %d", GetLastError() ) );
|
|
|
|
if( !OK )
|
|
return NULL;
|
|
|
|
pDocumentSecurityDescriptor = pPrivateObjectSecurity;
|
|
|
|
#if DBG
|
|
if( MODULE_DEBUG & DBG_SECURITY )
|
|
{
|
|
BOOL DaclPresent;
|
|
PACL pDacl;
|
|
BOOL DaclDefaulted = FALSE;
|
|
|
|
GetSecurityDescriptorDacl( pDocumentSecurityDescriptor,
|
|
&DaclPresent,
|
|
&pDacl,
|
|
&DaclDefaulted );
|
|
|
|
DBGMSG( DBG_SECURITY, ( "Document security descriptor DACL:\n" ));
|
|
|
|
DumpAcl( pDacl );
|
|
}
|
|
#endif /* DBG */
|
|
|
|
}
|
|
else
|
|
{
|
|
OK = FALSE;
|
|
}
|
|
|
|
return ( OK ? pDocumentSecurityDescriptor : NULL );
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
BOOL DeletePrinterSecurity(
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
{
|
|
BOOL OK;
|
|
|
|
OK = DestroyPrivateObjectSecurity( &pIniPrinter->pSecurityDescriptor );
|
|
pIniPrinter->pSecurityDescriptor = NULL;
|
|
|
|
DBGCHK( !OK, ( "DestroyPrivateObjectSecurity failed. Error %d", GetLastError() ) );
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
BOOL DeleteDocumentSecurity(
|
|
PINIJOB pIniJob
|
|
)
|
|
{
|
|
BOOL OK;
|
|
|
|
OK = DestroyPrivateObjectSecurity( &pIniJob->pSecurityDescriptor );
|
|
|
|
DBGCHK( !OK, ( "DestroyPrivateObjectSecurity failed. Error %d", GetLastError() ) );
|
|
|
|
OK = ObjectCloseAuditAlarm( szSpooler, pIniJob,
|
|
pIniJob->GenerateOnClose );
|
|
|
|
DBGCHK( !OK, ( "ObjectCloseAuditAlarm failed. Error %d", GetLastError() ) );
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef OLDSTUFF
|
|
|
|
/* AllocateLocalSD
|
|
*
|
|
* Makes a copy of a security descriptor, allocating it out of the local heap.
|
|
* The source descriptor MUST be in self-relative format.
|
|
*
|
|
* Argument
|
|
*
|
|
* pSourceSD - Pointer to a self-relative security descriptor
|
|
*
|
|
*
|
|
* Returns
|
|
*
|
|
* A pointer to a locally allocated security descriptor.
|
|
*
|
|
* If the function fails to allocate the memory, NULL is returned.
|
|
*
|
|
* Note, if an invalid security descriptor is passed to
|
|
* GetSecurityDescriptorLength, the return value is undefined,
|
|
* therefore the caller should ensure that the source is valid.
|
|
*/
|
|
PSECURITY_DESCRIPTOR AllocateLocalSD( PSECURITY_DESCRIPTOR pSourceSD )
|
|
{
|
|
DWORD Length;
|
|
PSECURITY_DESCRIPTOR pLocalSD;
|
|
|
|
Length = GetSecurityDescriptorLength( pSourceSD );
|
|
|
|
pLocalSD = AllocSplMem( Length );
|
|
|
|
if( pLocalSD )
|
|
{
|
|
memcpy( pLocalSD, pSourceSD, Length );
|
|
}
|
|
|
|
return pLocalSD;
|
|
}
|
|
|
|
#endif /* OLDSTUFF */
|
|
|
|
|
|
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
|
|
|
|
|
|
BOOL
|
|
BuildPrintObjectProtection(
|
|
IN PUCHAR AceType,
|
|
IN DWORD AceCount,
|
|
IN PSID *AceSid,
|
|
IN ACCESS_MASK *AceMask,
|
|
IN BYTE *InheritFlags,
|
|
IN PSID OwnerSid,
|
|
IN PSID GroupSid,
|
|
IN PGENERIC_MAPPING GenericMap,
|
|
OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a self-relative security descriptor ready
|
|
to be applied to one of the print manager objects.
|
|
|
|
If so indicated, a pointer to the last RID of the SID in the last
|
|
ACE of the DACL is returned and a flag set indicating that the RID
|
|
must be replaced before the security descriptor is applied to an object.
|
|
This is to support USER object protection, which must grant some
|
|
access to the user represented by the object.
|
|
|
|
|
|
The SACL of each of these objects will be set to:
|
|
|
|
|
|
Audit
|
|
Success | Fail
|
|
WORLD
|
|
(Write | Delete | WriteDacl | AccessSystemSecurity)
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
AceType - Array of AceTypes.
|
|
Must be ACCESS_ALLOWED_ACE_TYPE or ACCESS_DENIED_ACE_TYPE.
|
|
|
|
AceCount - The number of ACEs to be included in the DACL.
|
|
|
|
AceSid - Points to an array of SIDs to be granted access by the DACL.
|
|
If the target SAM object is a User object, then the last entry
|
|
in this array is expected to be the SID of an account within the
|
|
domain with the last RID not yet set. The RID will be set during
|
|
actual account creation.
|
|
|
|
AceMask - Points to an array of accesses to be granted by the DACL.
|
|
The n'th entry of this array corresponds to the n'th entry of
|
|
the AceSid array. These masks should not include any generic
|
|
access types.
|
|
|
|
InheritFlags - Pointer to an array of inherit flags.
|
|
The n'th entry of this array corresponds to the n'th entry of
|
|
the AceSid array.
|
|
|
|
OwnerSid - The SID of the owner to be assigned to the descriptor.
|
|
|
|
GroupSid - The SID of the group to be assigned to the descriptor.
|
|
|
|
GenericMap - Points to a generic mapping for the target object type.
|
|
|
|
ppSecurityDescriptor - Receives a pointer to the security descriptor.
|
|
This will be allcated from the process' heap, not the spooler's,
|
|
and should therefore be freed with LocalFree().
|
|
|
|
|
|
IN DWORD AceCount,
|
|
IN PSID *AceSid,
|
|
IN ACCESS_MASK *AceMask,
|
|
IN BYTE *InheritFlags,
|
|
IN PSID OwnerSid,
|
|
IN PSID GroupSid,
|
|
IN PGENERIC_MAPPING GenericMap,
|
|
OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor
|
|
|
|
Return Value:
|
|
|
|
TBS.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
|
|
SECURITY_DESCRIPTOR Absolute;
|
|
PSECURITY_DESCRIPTOR Relative = NULL;
|
|
PACL TmpAcl= NULL;
|
|
PACCESS_ALLOWED_ACE TmpAce;
|
|
DWORD SDLength;
|
|
DWORD DaclLength;
|
|
DWORD i;
|
|
BOOL bReturn = FALSE;
|
|
|
|
//
|
|
// The approach is to set up an absolute security descriptor that
|
|
// looks like what we want and then copy it to make a self-relative
|
|
// security descriptor.
|
|
//
|
|
|
|
if (InitializeSecurityDescriptor( &Absolute,
|
|
SECURITY_DESCRIPTOR_REVISION1 ) &&
|
|
|
|
SetSecurityDescriptorOwner( &Absolute, OwnerSid, FALSE ) &&
|
|
|
|
SetSecurityDescriptorGroup( &Absolute, GroupSid, FALSE ) ) {
|
|
|
|
//
|
|
// Discretionary ACL
|
|
//
|
|
// Calculate its length,
|
|
// Allocate it,
|
|
// Initialize it,
|
|
// Add each ACE
|
|
// Set ACE as InheritOnly if necessary
|
|
// Add it to the security descriptor
|
|
//
|
|
|
|
DaclLength = (DWORD)sizeof(ACL);
|
|
for (i=0; i<AceCount; i++) {
|
|
|
|
DaclLength += GetLengthSid( AceSid[i] ) +
|
|
(DWORD)sizeof(ACCESS_ALLOWED_ACE) -
|
|
(DWORD)sizeof(DWORD); //Subtract out SidStart field length
|
|
}
|
|
|
|
TmpAcl = AllocSplMem( DaclLength );
|
|
|
|
if (TmpAcl && InitializeAcl( TmpAcl, DaclLength, ACL_REVISION2 )) {
|
|
|
|
BOOL bLoop = TRUE;
|
|
for (i=0; bLoop && i < AceCount; i++)
|
|
{
|
|
if( AceType[i] == ACCESS_ALLOWED_ACE_TYPE ) {
|
|
bLoop = AddAccessAllowedAce ( TmpAcl, ACL_REVISION2, AceMask[i], AceSid[i] );
|
|
} else {
|
|
bLoop = AddAccessDeniedAce ( TmpAcl, ACL_REVISION2, AceMask[i], AceSid[i] );
|
|
}
|
|
|
|
if (bLoop) {
|
|
if (InheritFlags[i] != 0)
|
|
{
|
|
if ( bLoop = GetAce( TmpAcl, i, (LPVOID *)&TmpAce )) {
|
|
TmpAce->Header.AceFlags = InheritFlags[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bLoop) {
|
|
#if DBG
|
|
DBGMSG( DBG_SECURITY, ( "Server security descriptor DACL:\n" ) );
|
|
|
|
DumpAcl(TmpAcl);
|
|
#endif /* DBG */
|
|
|
|
if (SetSecurityDescriptorDacl (&Absolute, TRUE, TmpAcl, FALSE )) {
|
|
|
|
//
|
|
// Convert the Security Descriptor to Self-Relative
|
|
//
|
|
// Get the length needed
|
|
// Allocate that much memory
|
|
// Copy it
|
|
// Free the generated absolute ACLs
|
|
//
|
|
|
|
SDLength = GetSecurityDescriptorLength( &Absolute );
|
|
|
|
Relative = LocalAlloc( 0, SDLength );
|
|
|
|
if (Relative) {
|
|
bReturn = MakeSelfRelativeSD(&Absolute, Relative, &SDLength );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bReturn) {
|
|
*ppSecurityDescriptor = Relative;
|
|
} else {
|
|
|
|
*ppSecurityDescriptor = NULL;
|
|
if (Relative) {
|
|
LocalFree(Relative);
|
|
}
|
|
}
|
|
|
|
if (TmpAcl){
|
|
FreeSplMem(TmpAcl);
|
|
}
|
|
|
|
return(bReturn);
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
ValidateObjectAccess
|
|
|
|
Routine Description:
|
|
|
|
Validates whether the currently impersonated user has access to the given
|
|
printer object or not.
|
|
|
|
Arguments:
|
|
|
|
ObjectType - SPOOLER_OBJECT_* value, which is an index into the global
|
|
mapping for spooler objects.
|
|
DesiredAccess - The type of access requested.
|
|
ObjectHandle - If ObjectType == SPOOLER_OBJECT_PRINTER, this must be a printer
|
|
handle, if SPOOLER_OBJECT_DOCUMENT, a pointer to a INIJOB
|
|
structure. For SPOOLER_OBJECT_SERVER this is ignored.
|
|
pGrantedAccess - The access that we granted to the client.
|
|
pIniSpooler - The inispooler on which the obhect is located.
|
|
|
|
Return Value:
|
|
|
|
TRUE if requested access is granted.
|
|
|
|
|
|
--*/
|
|
BOOL
|
|
ValidateObjectAccess(
|
|
IN DWORD ObjectType,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN LPVOID ObjectHandle,
|
|
OUT PACCESS_MASK pGrantedAccess,
|
|
IN PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HANDLE hClientToken = NULL;
|
|
|
|
bRet = GetTokenHandle(&hClientToken);
|
|
|
|
if (bRet)
|
|
{
|
|
bRet = ValidateObjectAccessWithToken(hClientToken, ObjectType, DesiredAccess, ObjectHandle, pGrantedAccess, pIniSpooler);
|
|
}
|
|
else if (pGrantedAccess)
|
|
{
|
|
*pGrantedAccess = 0;
|
|
}
|
|
|
|
if (hClientToken)
|
|
{
|
|
CloseHandle(hClientToken);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
BOOL
|
|
ValidateObjectAccessWithToken(
|
|
IN HANDLE hClientToken,
|
|
IN DWORD ObjectType,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN LPVOID ObjectHandle,
|
|
OUT PACCESS_MASK pGrantedAccess,
|
|
IN PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
LPWSTR pObjectName;
|
|
PSECURITY_DESCRIPTOR pSecurityDescriptor;
|
|
PSPOOL pSpool = NULL;
|
|
PINIPRINTER pIniPrinter;
|
|
PINIJOB pIniJob;
|
|
BOOL AccessCheckOK;
|
|
BOOL OK;
|
|
BOOL AccessStatus = TRUE;
|
|
ACCESS_MASK MappedDesiredAccess;
|
|
DWORD GrantedAccess = 0;
|
|
PBOOL pGenerateOnClose;
|
|
BYTE PrivilegeSetBuffer[256];
|
|
DWORD PrivilegeSetBufferLength = 256;
|
|
PPRIVILEGE_SET pPrivilegeSet;
|
|
BOOL HackForNoImpersonationToken = FALSE;
|
|
DWORD dwRetCode;
|
|
|
|
PTOKEN_PRIVILEGES pPreviousTokenPrivileges;
|
|
DWORD PreviousTokenPrivilegesLength;
|
|
|
|
//
|
|
// Some Print Providers may not require Security
|
|
//
|
|
|
|
if ( (pIniSpooler->SpoolerFlags & SPL_SECURITY_CHECK) == FALSE ) return TRUE;
|
|
|
|
|
|
switch( ObjectType )
|
|
{
|
|
case SPOOLER_OBJECT_SERVER:
|
|
case SPOOLER_OBJECT_XCV:
|
|
if( ObjectHandle )
|
|
pSpool = ObjectHandle;
|
|
ObjectHandle = pIniSpooler;
|
|
pObjectName = pIniSpooler->pMachineName;
|
|
pSecurityDescriptor = pServerSecurityDescriptor;
|
|
pGenerateOnClose = &ServerGenerateOnClose;
|
|
break;
|
|
|
|
case SPOOLER_OBJECT_PRINTER:
|
|
pSpool = ObjectHandle;
|
|
pIniPrinter = pSpool->pIniPrinter;
|
|
pObjectName = pIniPrinter->pName;
|
|
pSecurityDescriptor = pIniPrinter->pSecurityDescriptor;
|
|
pGenerateOnClose = &pSpool->GenerateOnClose;
|
|
break;
|
|
|
|
case SPOOLER_OBJECT_DOCUMENT:
|
|
pIniJob = (PINIJOB)ObjectHandle;
|
|
pObjectName = pIniJob->pDocument;
|
|
pSecurityDescriptor = pIniJob->pSecurityDescriptor;
|
|
pGenerateOnClose = &pIniJob->GenerateOnClose;
|
|
break;
|
|
}
|
|
|
|
MapGenericToSpecificAccess( ObjectType, DesiredAccess, &MappedDesiredAccess );
|
|
|
|
if (MappedDesiredAccess & ACCESS_SYSTEM_SECURITY) {
|
|
if (!SetRequiredPrivileges( hClientToken,
|
|
&pPreviousTokenPrivileges,
|
|
&PreviousTokenPrivilegesLength
|
|
)) {
|
|
|
|
if (pGrantedAccess) {
|
|
*pGrantedAccess = 0;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
pPrivilegeSet = (PPRIVILEGE_SET)PrivilegeSetBuffer;
|
|
|
|
|
|
/* Call AccessCheck followed by ObjectOpenAuditAlarm rather than
|
|
* AccessCheckAndAuditAlarm, because we may need to enable
|
|
* SeSecurityPrivilege in order to check for ACCESS_SYSTEM_SECURITY
|
|
* privilege. We must ensure that the security access-checking
|
|
* API has the actual token whose security privilege we have enabled.
|
|
* AccessCheckAndAuditAlarm is no good for this, because it opens
|
|
* the client's token again, which may not have the privilege enabled.
|
|
*/
|
|
AccessCheckOK = AccessCheck( pSecurityDescriptor,
|
|
hClientToken,
|
|
MappedDesiredAccess,
|
|
&GenericMapping[ObjectType],
|
|
pPrivilegeSet,
|
|
&PrivilegeSetBufferLength,
|
|
&GrantedAccess,
|
|
&AccessStatus );
|
|
|
|
if (!AccessCheckOK) {
|
|
|
|
if (GetLastError() == ERROR_NO_IMPERSONATION_TOKEN) {
|
|
DBGMSG(DBG_TRACE, ("No impersonation token. Access will be granted.\n"));
|
|
HackForNoImpersonationToken = TRUE;
|
|
dwRetCode = ERROR_SUCCESS;
|
|
GrantedAccess = MappedDesiredAccess;
|
|
} else {
|
|
dwRetCode = GetLastError();
|
|
|
|
}
|
|
pPrivilegeSet = NULL;
|
|
} else {
|
|
|
|
if (!AccessStatus) {
|
|
|
|
dwRetCode = GetLastError();
|
|
}
|
|
}
|
|
|
|
OK = ObjectOpenAuditAlarm( szSpooler,
|
|
ObjectHandle,
|
|
ObjectTypeName[ObjectType],
|
|
pObjectName,
|
|
pSecurityDescriptor,
|
|
hClientToken,
|
|
MappedDesiredAccess,
|
|
GrantedAccess,
|
|
pPrivilegeSet,
|
|
FALSE, /* No new object creation */
|
|
AccessStatus,
|
|
pGenerateOnClose );
|
|
|
|
|
|
if( MappedDesiredAccess & ACCESS_SYSTEM_SECURITY )
|
|
ResetRequiredPrivileges( hClientToken,
|
|
pPreviousTokenPrivileges,
|
|
PreviousTokenPrivilegesLength );
|
|
|
|
if( !pSpool )
|
|
ObjectCloseAuditAlarm( szSpooler, ObjectHandle, *pGenerateOnClose );
|
|
|
|
|
|
//
|
|
// Allowing power users to install printer drivers or other dlls into the
|
|
// trusted component base is a security hole. We now require that administrators
|
|
// and power users have the load driver privilege present in the token in order
|
|
// to be able to preform administrative tasks on the spooler. See bug 352856
|
|
// for more details.
|
|
//
|
|
if (AccessCheckOK &&
|
|
AccessStatus &&
|
|
ObjectType == SPOOLER_OBJECT_SERVER &&
|
|
GrantedAccess & SERVER_ACCESS_ADMINISTER)
|
|
{
|
|
BOOL bPrivPresent;
|
|
DWORD Attributes;
|
|
|
|
dwRetCode = CheckPrivilegePresent(hClientToken,
|
|
&gLoadDriverPrivilegeLuid,
|
|
&bPrivPresent,
|
|
&Attributes);
|
|
|
|
if (dwRetCode == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// The reason why we check if the load driver privilege is present and
|
|
// not present AND enabled is the following. Let's assume you have been
|
|
// granted the privilege to load drivers.
|
|
// When you logon on interactively SeLoadDriverPrivilege is enabled.
|
|
// When you logon on via the secondary logon (runas which calls
|
|
// CreateProcessWithLogonW) then the privilege is disabled. We do not want
|
|
// to have inconsistent behavior regarding administering the spooler server.
|
|
//
|
|
if (!bPrivPresent)
|
|
{
|
|
//
|
|
// The caller has been granted SERVER_ACCESS_ADMINISTER permission but
|
|
// the caller doesn't have the privilege to load drivers. We do not
|
|
// grant the desired access in this case.
|
|
//
|
|
GrantedAccess = 0;
|
|
AccessStatus = FALSE;
|
|
dwRetCode = ERROR_ACCESS_DENIED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We cannot determine if the privilege is held, so we need to fail
|
|
// the AccessCheck function.
|
|
//
|
|
GrantedAccess = 0;
|
|
AccessCheckOK = FALSE;
|
|
dwRetCode = GetLastError();
|
|
}
|
|
}
|
|
|
|
if( pGrantedAccess )
|
|
*pGrantedAccess = GrantedAccess;
|
|
|
|
//
|
|
// we do the setlasterror here because we may have failed the AccessCheck
|
|
// or we succeeded but are denied access but the ObjectOpenAuditAlarm went
|
|
// thru smoothly and now there is no error code to return on the function
|
|
// so we specify the dwRetCode if we did fail.
|
|
//
|
|
|
|
if (!AccessCheckOK || !AccessStatus) {
|
|
SetLastError(dwRetCode);
|
|
}
|
|
|
|
return ( ( OK && AccessCheckOK && AccessStatus ) || HackForNoImpersonationToken );
|
|
}
|
|
|
|
|
|
/* AccessGranted
|
|
*
|
|
* Checks whether the desired access is granted, by comparing the GrantedAccess
|
|
* mask pointed to by pSpool.
|
|
*
|
|
* Parameters
|
|
*
|
|
* ObjectType - SPOOLER_OBJECT_* value, which is an index into the global
|
|
* mapping for spooler objects. This will not be SPOOLER_OBJECT_DOCUMENT,
|
|
* since we don't have document handles. It could potentially be
|
|
* SPOOLER_OBJECT_SERVER.
|
|
*
|
|
* DesiredAccess - The type of access requested.
|
|
*
|
|
* pSpool - A pointer to the SPOOL structure
|
|
*
|
|
* Returns
|
|
*
|
|
* TRUE - Access is granted
|
|
* FALSE - Access is not granted
|
|
*/
|
|
BOOL
|
|
AccessGranted(
|
|
DWORD ObjectType,
|
|
ACCESS_MASK DesiredAccess,
|
|
PSPOOL pSpool
|
|
)
|
|
{
|
|
|
|
if ( (pSpool->pIniSpooler->SpoolerFlags & SPL_SECURITY_CHECK) == FALSE ) return TRUE;
|
|
|
|
MapGenericMask( &DesiredAccess,
|
|
&GenericMapping[ObjectType] );
|
|
|
|
return AreAllAccessesGranted( pSpool->GrantedAccess, DesiredAccess );
|
|
}
|
|
|
|
|
|
// Stolen from windows\base\username.c
|
|
// !!! Must close the handle that is returned
|
|
BOOL
|
|
GetTokenHandle(
|
|
PHANDLE pTokenHandle
|
|
)
|
|
{
|
|
if (!OpenThreadToken(GetCurrentThread(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
TRUE,
|
|
pTokenHandle)) {
|
|
|
|
if (GetLastError() == ERROR_NO_TOKEN) {
|
|
|
|
// This means we are not impersonating anybody.
|
|
// Instead, lets get the token out of the process.
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
pTokenHandle)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
} else
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
VOID MapGenericToSpecificAccess(
|
|
DWORD ObjectType,
|
|
DWORD GenericAccess,
|
|
PDWORD pSpecificAccess
|
|
)
|
|
{
|
|
*pSpecificAccess = GenericAccess;
|
|
|
|
MapGenericMask( pSpecificAccess,
|
|
&GenericMapping[ObjectType] );
|
|
}
|
|
|
|
|
|
/* GetSecurityInformation
|
|
*
|
|
* Fills in the security information with a mask specifying the contents
|
|
* of the security descriptor.
|
|
*
|
|
* Parameters
|
|
*
|
|
* pSecurityDescriptor - A pointer to a security descriptor
|
|
* that the caller wishes to set. This may be NULL.
|
|
*
|
|
* pSecurityInformation - A pointer to a buffer to receive
|
|
* the security information flags.
|
|
*
|
|
*
|
|
* Warning: This is an egregious hack.
|
|
* We need to find out what is being set so we can verify the caller
|
|
* has the required privileges.
|
|
* There's no way an app like Print Manager can tell us what bits
|
|
* of the security descriptor it wants to set.
|
|
*
|
|
* The following flags may be set:
|
|
*
|
|
* OWNER_SECURITY_INFORMATION
|
|
* GROUP_SECURITY_INFORMATION
|
|
* DACL_SECURITY_INFORMATION
|
|
* SACL_SECURITY_INFORMATION
|
|
*
|
|
* Returns
|
|
*
|
|
* TRUE - No errors occurred
|
|
* FALSE - An error occurred
|
|
*
|
|
*/
|
|
BOOL
|
|
GetSecurityInformation(
|
|
PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
PSECURITY_INFORMATION pSecurityInformation
|
|
)
|
|
{
|
|
SECURITY_INFORMATION SecurityInformation = 0;
|
|
BOOL Defaulted;
|
|
PSID pSidOwner;
|
|
PSID pSidGroup;
|
|
BOOL DaclPresent;
|
|
PACL pDacl;
|
|
BOOL SaclPresent;
|
|
PACL pSacl;
|
|
BOOL rc = TRUE;
|
|
|
|
|
|
if( pSecurityDescriptor
|
|
&& IsValidSecurityDescriptor( pSecurityDescriptor ) )
|
|
{
|
|
if( GetSecurityDescriptorOwner( pSecurityDescriptor, &pSidOwner, &Defaulted )
|
|
&& GetSecurityDescriptorGroup( pSecurityDescriptor, &pSidGroup, &Defaulted )
|
|
&& GetSecurityDescriptorDacl( pSecurityDescriptor, &DaclPresent, &pDacl, &Defaulted )
|
|
&& GetSecurityDescriptorSacl( pSecurityDescriptor, &SaclPresent, &pSacl, &Defaulted ) )
|
|
{
|
|
if( pSidOwner )
|
|
SecurityInformation |= OWNER_SECURITY_INFORMATION;
|
|
if( pSidGroup )
|
|
SecurityInformation |= GROUP_SECURITY_INFORMATION;
|
|
if( DaclPresent )
|
|
SecurityInformation |= DACL_SECURITY_INFORMATION;
|
|
if( SaclPresent )
|
|
SecurityInformation |= SACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
else
|
|
rc = FALSE;
|
|
}else {
|
|
DBGMSG(DBG_TRACE, ("Either NULL pSecurityDescriptor or !IsValidSecurityDescriptor %.8x\n", pSecurityDescriptor));
|
|
rc = FALSE;
|
|
}
|
|
DBGMSG( DBG_TRACE, ("GetSecurityInformation returns %d with SecurityInformation = %08x\n", rc, SecurityInformation) );
|
|
|
|
*pSecurityInformation = SecurityInformation;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* GetPrivilegeRequired
|
|
*
|
|
* Returns a mask containing the privileges required to set the specified
|
|
* security information.
|
|
*
|
|
* Parameter
|
|
*
|
|
* SecurityInformation - Flags specifying the security information
|
|
* that the caller wishes to set. This may be 0.
|
|
*
|
|
* Returns
|
|
*
|
|
* An access mask specifying the privileges required.
|
|
*
|
|
*/
|
|
ACCESS_MASK
|
|
GetPrivilegeRequired(
|
|
SECURITY_INFORMATION SecurityInformation
|
|
)
|
|
{
|
|
ACCESS_MASK PrivilegeRequired = 0;
|
|
|
|
if( SecurityInformation & OWNER_SECURITY_INFORMATION )
|
|
PrivilegeRequired |= WRITE_OWNER;
|
|
if( SecurityInformation & GROUP_SECURITY_INFORMATION )
|
|
PrivilegeRequired |= WRITE_OWNER;
|
|
if( SecurityInformation & DACL_SECURITY_INFORMATION )
|
|
PrivilegeRequired |= WRITE_DAC;
|
|
if( SecurityInformation & SACL_SECURITY_INFORMATION )
|
|
PrivilegeRequired |= ACCESS_SYSTEM_SECURITY;
|
|
|
|
return PrivilegeRequired;
|
|
}
|
|
|
|
|
|
/* BuildPartialSecurityDescriptor
|
|
*
|
|
* Creates a copy of the source security descriptor, omitting those
|
|
* parts of the descriptor which the AccessGranted mask doesn't give
|
|
* read access to.
|
|
*
|
|
* Parameters
|
|
*
|
|
* AccessMask - Defines the permissions held by the client.
|
|
* This may include READ_CONTROL or ACCESS_SYSTEM_SECURITY.
|
|
*
|
|
* pSourceSecurityDescriptor - A pointer to the security descriptor
|
|
* upon which the partial security descriptor will be based.
|
|
* Its Owner, Group, DACL and SACL will be copied appropriately,
|
|
*
|
|
* ppPartialSecurityDescriptor - A pointer to a variable which
|
|
* will receive the address of the newly created descriptor.
|
|
* If the AccessMask parameter contains neither READ_CONTROL
|
|
* nor ACCESS_SYSTEM_SECURITY, the descriptor will be empty.
|
|
* The descriptor will be in self-relative format, and must
|
|
* be freed by the caller using FreeSplMem().
|
|
*
|
|
* pPartialSecurityDescriptorLength - A pointer to a variable
|
|
* to receive the length of the security descriptor.
|
|
* This should be passed as the second parameter to FreeSplMem()
|
|
* when the descriptor is freed.
|
|
*
|
|
* Returns
|
|
*
|
|
* TRUE - No error was detected
|
|
* FALSE - An error was detected
|
|
*
|
|
*/
|
|
BOOL
|
|
BuildPartialSecurityDescriptor(
|
|
ACCESS_MASK AccessGranted,
|
|
PSECURITY_DESCRIPTOR pSourceSecurityDescriptor,
|
|
PSECURITY_DESCRIPTOR *ppPartialSecurityDescriptor,
|
|
PDWORD pPartialSecurityDescriptorLength
|
|
)
|
|
{
|
|
SECURITY_DESCRIPTOR AbsolutePartialSecurityDescriptor;
|
|
BOOL Defaulted = FALSE;
|
|
PSID pOwnerSid = NULL;
|
|
PSID pGroupSid = NULL;
|
|
BOOL DaclPresent = FALSE;
|
|
PACL pDacl = NULL;
|
|
BOOL SaclPresent = FALSE;
|
|
PACL pSacl = NULL;
|
|
BOOL ErrorOccurred = FALSE;
|
|
DWORD Length = 0;
|
|
PSECURITY_DESCRIPTOR pSelfRelativePartialSecurityDescriptor = NULL;
|
|
|
|
/* When we've initialized the security descriptor,
|
|
* it will have no owner, no primary group, no DACL and no SACL:
|
|
*/
|
|
if( InitializeSecurityDescriptor( &AbsolutePartialSecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION1 ) )
|
|
{
|
|
/* If the caller has READ_CONTROL permission,
|
|
* set the Owner, Group and DACL:
|
|
*/
|
|
if( AreAllAccessesGranted( AccessGranted, READ_CONTROL ) )
|
|
{
|
|
if( GetSecurityDescriptorOwner( pSourceSecurityDescriptor,
|
|
&pOwnerSid, &Defaulted ) )
|
|
SetSecurityDescriptorOwner( &AbsolutePartialSecurityDescriptor,
|
|
pOwnerSid, Defaulted );
|
|
else
|
|
ErrorOccurred = TRUE;
|
|
|
|
if( GetSecurityDescriptorGroup( pSourceSecurityDescriptor,
|
|
&pGroupSid, &Defaulted ) )
|
|
SetSecurityDescriptorGroup( &AbsolutePartialSecurityDescriptor,
|
|
pGroupSid, Defaulted );
|
|
else
|
|
ErrorOccurred = TRUE;
|
|
|
|
if( GetSecurityDescriptorDacl( pSourceSecurityDescriptor,
|
|
&DaclPresent, &pDacl, &Defaulted ) )
|
|
SetSecurityDescriptorDacl( &AbsolutePartialSecurityDescriptor,
|
|
DaclPresent, pDacl, Defaulted );
|
|
else
|
|
ErrorOccurred = TRUE;
|
|
}
|
|
|
|
/* If the caller has ACCESS_SYSTEM_SECURITY permission,
|
|
* set the SACL:
|
|
*/
|
|
if( AreAllAccessesGranted( AccessGranted, ACCESS_SYSTEM_SECURITY ) )
|
|
{
|
|
if( GetSecurityDescriptorSacl( pSourceSecurityDescriptor,
|
|
&SaclPresent, &pSacl, &Defaulted ) )
|
|
SetSecurityDescriptorSacl( &AbsolutePartialSecurityDescriptor,
|
|
SaclPresent, pSacl, Defaulted );
|
|
else
|
|
ErrorOccurred = TRUE;
|
|
}
|
|
|
|
if( !ErrorOccurred )
|
|
{
|
|
Length = 0;
|
|
|
|
if( !MakeSelfRelativeSD( &AbsolutePartialSecurityDescriptor,
|
|
pSelfRelativePartialSecurityDescriptor,
|
|
&Length ) )
|
|
{
|
|
if( GetLastError( ) == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
pSelfRelativePartialSecurityDescriptor = AllocSplMem( Length );
|
|
|
|
if( !pSelfRelativePartialSecurityDescriptor
|
|
|| !MakeSelfRelativeSD( &AbsolutePartialSecurityDescriptor,
|
|
pSelfRelativePartialSecurityDescriptor,
|
|
&Length ) )
|
|
{
|
|
ErrorOccurred = TRUE;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
ErrorOccurred = TRUE;
|
|
|
|
DBGMSG(DBG_WARNING, ("MakeSelfRelativeSD failed: Error %d\n",
|
|
GetLastError()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGMSG(DBG_WARNING, ("Expected MakeSelfRelativeSD to fail!\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
ErrorOccurred = TRUE;
|
|
|
|
|
|
if( !ErrorOccurred )
|
|
{
|
|
*ppPartialSecurityDescriptor = pSelfRelativePartialSecurityDescriptor;
|
|
*pPartialSecurityDescriptorLength = Length;
|
|
}
|
|
|
|
return !ErrorOccurred;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
SetRequiredPrivileges(
|
|
IN HANDLE TokenHandle,
|
|
OUT PTOKEN_PRIVILEGES *ppPreviousTokenPrivileges,
|
|
OUT PDWORD pPreviousTokenPrivilegesLength
|
|
)
|
|
/*++
|
|
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
TokenHandle - A token associated with the current thread or process
|
|
|
|
ppPreviousTokenPrivileges - This will be filled with the address of the
|
|
buffer allocated to hold the previously existing privileges for this
|
|
process or thread.
|
|
|
|
pPreviousTokenPrivilegesLength - This will be filled with the length of the
|
|
buffer allocated.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful.
|
|
|
|
|
|
--*/
|
|
{
|
|
/* Make enough room for TOKEN_PRIVILEGES with an array of 2 Privileges
|
|
* (there's 1 by default):
|
|
*/
|
|
#define PRIV_SECURITY 0
|
|
#define PRIV_COUNT 1
|
|
|
|
LUID SecurityValue;
|
|
|
|
BYTE TokenPrivilegesBuffer[ sizeof( TOKEN_PRIVILEGES ) +
|
|
( ( PRIV_COUNT - 1 ) *
|
|
sizeof( LUID_AND_ATTRIBUTES ) ) ];
|
|
PTOKEN_PRIVILEGES pTokenPrivileges;
|
|
DWORD FirstTryBufferLength = 256;
|
|
DWORD BytesNeeded;
|
|
|
|
//
|
|
// First, assert Audit privilege
|
|
//
|
|
|
|
memset( &SecurityValue, 0, sizeof SecurityValue );
|
|
|
|
if( !LookupPrivilegeValue( NULL, SE_SECURITY_NAME, &SecurityValue ) )
|
|
{
|
|
DBGMSG( DBG_WARNING,
|
|
( "LookupPrivilegeValue failed: Error %d\n", GetLastError( ) ) );
|
|
return FALSE;
|
|
}
|
|
|
|
/* Allocate a buffer of a reasonable length to hold the current privileges,
|
|
* so we can restore them later:
|
|
*/
|
|
*pPreviousTokenPrivilegesLength = FirstTryBufferLength;
|
|
if( !( *ppPreviousTokenPrivileges = AllocSplMem( FirstTryBufferLength ) ) )
|
|
return FALSE;
|
|
|
|
memset( &TokenPrivilegesBuffer, 0, sizeof TokenPrivilegesBuffer );
|
|
pTokenPrivileges = (PTOKEN_PRIVILEGES)&TokenPrivilegesBuffer;
|
|
|
|
/*
|
|
* Set up the privilege set we will need
|
|
*/
|
|
pTokenPrivileges->PrivilegeCount = PRIV_COUNT;
|
|
pTokenPrivileges->Privileges[PRIV_SECURITY].Luid = SecurityValue;
|
|
pTokenPrivileges->Privileges[PRIV_SECURITY].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
if (!AdjustTokenPrivileges( TokenHandle,
|
|
FALSE,
|
|
pTokenPrivileges,
|
|
*pPreviousTokenPrivilegesLength,
|
|
*ppPreviousTokenPrivileges,
|
|
&BytesNeeded )) {
|
|
|
|
if( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
*pPreviousTokenPrivilegesLength = BytesNeeded;
|
|
*ppPreviousTokenPrivileges = ReallocSplMem(
|
|
*ppPreviousTokenPrivileges,
|
|
0,
|
|
*pPreviousTokenPrivilegesLength );
|
|
|
|
if( *ppPreviousTokenPrivileges )
|
|
{
|
|
if (!AdjustTokenPrivileges( TokenHandle,
|
|
FALSE,
|
|
pTokenPrivileges,
|
|
*pPreviousTokenPrivilegesLength,
|
|
*ppPreviousTokenPrivileges,
|
|
&BytesNeeded )) {
|
|
|
|
DBGMSG( DBG_WARNING, ("AdjustTokenPrivileges failed: Error %d\n", GetLastError()));
|
|
goto Fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pPreviousTokenPrivilegesLength = 0;
|
|
goto Fail;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DBGMSG( DBG_WARNING, ("AdjustTokenPrivileges failed: Error %d\n", GetLastError()));
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
Fail:
|
|
if (*ppPreviousTokenPrivileges) {
|
|
|
|
FreeSplMem(*ppPreviousTokenPrivileges);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ResetRequiredPrivileges(
|
|
IN HANDLE TokenHandle,
|
|
IN PTOKEN_PRIVILEGES pPreviousTokenPrivileges,
|
|
IN DWORD PreviousTokenPrivilegesLength
|
|
)
|
|
/*++
|
|
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
TokenHandle - A token associated with the current thread or process
|
|
|
|
pPreviousTokenPrivileges - The address of the buffer holding the previous
|
|
privileges to be reinstated.
|
|
|
|
PreviousTokenPrivilegesLength - Length of the buffer for deallocation.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful.
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOL OK;
|
|
|
|
OK = AdjustTokenPrivileges ( TokenHandle,
|
|
FALSE,
|
|
pPreviousTokenPrivileges,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
FreeSplMem( pPreviousTokenPrivileges );
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
|
|
/* CreateEverybodySecurityDescriptor
|
|
*
|
|
* Creates a security descriptor giving everyone access
|
|
*
|
|
* Arguments: None
|
|
*
|
|
* Return: The security descriptor returned by BuildPrintObjectProtection.
|
|
*
|
|
*/
|
|
#undef MAX_ACE
|
|
#define MAX_ACE 5
|
|
#define DBGCHK( Condition, ErrorInfo ) \
|
|
if( Condition ) DBGMSG( DBG_WARNING, ErrorInfo )
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
CreateEverybodySecurityDescriptor(
|
|
VOID
|
|
)
|
|
{
|
|
UCHAR AceType[MAX_ACE];
|
|
PSID AceSid[MAX_ACE]; // Don't expect more than MAX_ACE ACEs in any of these.
|
|
DWORD AceCount;
|
|
//
|
|
// For Code optimization we replace 5 individaul
|
|
// SID_IDENTIFIER_AUTHORITY with an array of
|
|
// SID_IDENTIFIER_AUTHORITYs
|
|
// where
|
|
// SidAuthority[0] = UserSidAuthority
|
|
// SidAuthority[1] = PowerSidAuthority
|
|
// SidAuthority[2] = CreatorSidAuthority
|
|
// SidAuthority[3] = SystemSidAuthority
|
|
// SidAuthority[4] = AdminSidAuthority
|
|
//
|
|
SID_IDENTIFIER_AUTHORITY SidAuthority[MAX_ACE] = {
|
|
SECURITY_NT_AUTHORITY,
|
|
SECURITY_NT_AUTHORITY,
|
|
SECURITY_CREATOR_SID_AUTHORITY,
|
|
SECURITY_NT_AUTHORITY,
|
|
SECURITY_NT_AUTHORITY
|
|
};
|
|
//
|
|
// For code optimization we replace 5 individual Sids with
|
|
// an array of Sids
|
|
// where
|
|
// Sid[0] = UserSid
|
|
// Sid[1] = PowerSid
|
|
// Sid[2] = CreatorSid
|
|
// Sid[3] = SystemSid
|
|
// Sid[4] = AdminSid
|
|
//
|
|
PSID Sids[MAX_ACE] = {NULL,NULL,NULL,NULL,NULL};
|
|
//
|
|
// Access masks corresponding to Sids
|
|
//
|
|
ACCESS_MASK AceMask[MAX_ACE] = {
|
|
(FILE_GENERIC_EXECUTE | SYNCHRONIZE | FILE_GENERIC_WRITE | FILE_GENERIC_READ) &
|
|
~READ_CONTROL & ~FILE_WRITE_ATTRIBUTES &
|
|
~FILE_WRITE_EA&~FILE_READ_DATA&~FILE_LIST_DIRECTORY ,
|
|
(FILE_GENERIC_EXECUTE | SYNCHRONIZE | FILE_GENERIC_WRITE | FILE_GENERIC_READ) &
|
|
~READ_CONTROL & ~FILE_WRITE_ATTRIBUTES &
|
|
~FILE_WRITE_EA&~FILE_READ_DATA&~FILE_LIST_DIRECTORY ,
|
|
STANDARD_RIGHTS_ALL | FILE_GENERIC_EXECUTE | FILE_GENERIC_WRITE |
|
|
FILE_GENERIC_READ | FILE_ALL_ACCESS ,
|
|
STANDARD_RIGHTS_ALL | FILE_GENERIC_EXECUTE | FILE_GENERIC_WRITE |
|
|
FILE_GENERIC_READ | FILE_ALL_ACCESS ,
|
|
STANDARD_RIGHTS_ALL | FILE_GENERIC_EXECUTE | FILE_GENERIC_WRITE |
|
|
FILE_GENERIC_READ | FILE_ALL_ACCESS ,
|
|
};
|
|
//
|
|
// SubAuthorities leading to the proper Group
|
|
//
|
|
DWORD SubAuthorities[3*MAX_ACE] = {
|
|
2 , SECURITY_BUILTIN_DOMAIN_RID , DOMAIN_ALIAS_RID_USERS ,
|
|
2 , SECURITY_BUILTIN_DOMAIN_RID , DOMAIN_ALIAS_RID_POWER_USERS ,
|
|
1 , SECURITY_CREATOR_OWNER_RID , 0 ,
|
|
1 , SECURITY_LOCAL_SYSTEM_RID , 0 ,
|
|
2 , SECURITY_BUILTIN_DOMAIN_RID , DOMAIN_ALIAS_RID_ADMINS
|
|
};
|
|
|
|
//
|
|
// CONTAINER_INHERIT_ACE -> This folder and subfolders
|
|
// OBJECT_INHERIT_ACE -> Files
|
|
//
|
|
BYTE InheritFlags[MAX_ACE] = {
|
|
CONTAINER_INHERIT_ACE,
|
|
CONTAINER_INHERIT_ACE,
|
|
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
|
|
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
|
|
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
|
|
};
|
|
|
|
|
|
PSECURITY_DESCRIPTOR ServerSD = NULL;
|
|
|
|
//
|
|
// Printer SD
|
|
//
|
|
|
|
|
|
for(AceCount = 0;
|
|
( (AceCount < MAX_ACE) &&
|
|
AllocateAndInitializeSid(&SidAuthority[AceCount],
|
|
(BYTE)SubAuthorities[AceCount*3],
|
|
SubAuthorities[AceCount*3+1],
|
|
SubAuthorities[AceCount*3+2],
|
|
0, 0, 0, 0, 0, 0,
|
|
&Sids[AceCount]));
|
|
AceCount++)
|
|
{
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = Sids[AceCount];
|
|
}
|
|
|
|
if(AceCount == MAX_ACE)
|
|
{
|
|
if(!BuildPrintObjectProtection( AceType,
|
|
AceCount,
|
|
AceSid,
|
|
AceMask,
|
|
InheritFlags,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ServerSD ) )
|
|
{
|
|
DBGMSG( DBG_WARNING,( "Couldn't buidl Print Object protection" ) );
|
|
ServerSD = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGMSG( DBG_WARNING,( "Couldn't Allocate and initialize SIDs" ) );
|
|
}
|
|
|
|
for(AceCount=0;AceCount<MAX_ACE;AceCount++)
|
|
{
|
|
if(Sids[AceCount])
|
|
FreeSid( Sids[AceCount] );
|
|
}
|
|
return ServerSD;
|
|
}
|
|
|
|
|
|
|
|
/* CreateDriversShareSecurityDescriptor
|
|
*
|
|
* Creates a security descriptor for the drivers$ share.
|
|
* This reflects the security descriptor applied to the print server,
|
|
* in that Everyone is given GENERIC_READ | GENERIC_EXECUTE,
|
|
* and everyone with SERVER_ACCESS_ADMINISTER (Administrators,
|
|
* Power Users etc.) is given GENERIC_ALL access to the share,
|
|
*
|
|
* If in future releases we support changes to the print server
|
|
* security descriptor (e.g. allowing the ability to deny
|
|
* SERVER_ACCESS_ENUMERATE), this routine will have to become more
|
|
* sophisticated, as the access to the share will probably need
|
|
* to be modified accordingly.
|
|
*
|
|
* Arguments: None
|
|
*
|
|
* Return: The security descriptor returned by BuildPrintObjectProtection.
|
|
*
|
|
*/
|
|
#undef MAX_ACE
|
|
#define MAX_ACE 20
|
|
#define DBGCHK( Condition, ErrorInfo ) \
|
|
if( Condition ) DBGMSG( DBG_WARNING, ErrorInfo )
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
CreateDriversShareSecurityDescriptor(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD ObjectType = SPOOLER_OBJECT_SERVER;
|
|
NT_PRODUCT_TYPE NtProductType;
|
|
PSID AceSid[MAX_ACE]; // Don't expect more than MAX_ACE ACEs in any of these.
|
|
ACCESS_MASK AceMask[MAX_ACE]; // Access masks corresponding to Sids
|
|
BYTE InheritFlags[MAX_ACE]; //
|
|
UCHAR AceType[MAX_ACE];
|
|
DWORD AceCount;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
|
|
PSID WorldSid = NULL;
|
|
PSID AdminsAliasSid = NULL;
|
|
PSID PrintOpsAliasSid = NULL;
|
|
PSID SystemOpsAliasSid = NULL;
|
|
PSID PowerUsersAliasSid = NULL;
|
|
PSID CreatorOwnerSid = NULL;
|
|
PSECURITY_DESCRIPTOR pDriversShareSD = NULL;
|
|
BOOL OK;
|
|
|
|
|
|
//
|
|
// Printer SD
|
|
//
|
|
|
|
AceCount = 0;
|
|
|
|
/* Creator-Owner SID: */
|
|
|
|
OK = AllocateAndInitializeSid( &CreatorSidAuthority, 1,
|
|
SECURITY_CREATOR_OWNER_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&CreatorOwnerSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
/* World SID */
|
|
|
|
OK = AllocateAndInitializeSid( &WorldSidAuthority, 1,
|
|
SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&WorldSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = WorldSid;
|
|
AceMask[AceCount] = GENERIC_READ | GENERIC_EXECUTE;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
/* Admins alias SID */
|
|
|
|
OK = AllocateAndInitializeSid( &NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&AdminsAliasSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = AdminsAliasSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
|
|
OK = RtlGetNtProductType( &NtProductType );
|
|
DBGCHK( !OK, ( "Couldn't get product type" ) );
|
|
|
|
if (NtProductType == NtProductLanManNt) {
|
|
|
|
/* Print Ops alias SID */
|
|
|
|
OK = AllocateAndInitializeSid( &NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_PRINT_OPS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&PrintOpsAliasSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = PrintOpsAliasSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
/* System Ops alias SID */
|
|
|
|
OK = AllocateAndInitializeSid( &NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_SYSTEM_OPS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&SystemOpsAliasSid );
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = SystemOpsAliasSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
} else {
|
|
|
|
//
|
|
// LanManNT product
|
|
//
|
|
|
|
OK = AllocateAndInitializeSid( &NtAuthority, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_POWER_USERS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&PowerUsersAliasSid );
|
|
|
|
DBGCHK( !OK, ( "Couldn't Allocate and initialize SID" ) );
|
|
if ( !OK ) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = PowerUsersAliasSid;
|
|
AceMask[AceCount] = GENERIC_ALL;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
}
|
|
|
|
|
|
DBGCHK( ( AceCount > MAX_ACE ), ( "ACE count exceeded" ) );
|
|
|
|
|
|
OK = BuildPrintObjectProtection( AceType,
|
|
AceCount,
|
|
AceSid,
|
|
AceMask,
|
|
InheritFlags,
|
|
AdminsAliasSid,
|
|
AdminsAliasSid,
|
|
&GenericMapping[ObjectType],
|
|
&pDriversShareSD );
|
|
|
|
CleanUp:
|
|
|
|
if (WorldSid) {
|
|
FreeSid( WorldSid );
|
|
}
|
|
if (AdminsAliasSid) {
|
|
FreeSid( AdminsAliasSid );
|
|
}
|
|
if (CreatorOwnerSid) {
|
|
FreeSid( CreatorOwnerSid );
|
|
}
|
|
if (PrintOpsAliasSid) {
|
|
FreeSid( PrintOpsAliasSid );
|
|
}
|
|
if (SystemOpsAliasSid) {
|
|
FreeSid( SystemOpsAliasSid );
|
|
}
|
|
if (PowerUsersAliasSid) {
|
|
FreeSid( PowerUsersAliasSid );
|
|
}
|
|
|
|
return pDriversShareSD;
|
|
}
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
DumpAcl(
|
|
IN PACL Acl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dumps via (NetpDbgPrint) an Acl for debug purposes. It is
|
|
specialized to dump standard aces.
|
|
|
|
Arguments:
|
|
|
|
Acl - Supplies the Acl to dump
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
PSTANDARD_ACE Ace;
|
|
|
|
if( MODULE_DEBUG & DBG_SECURITY ) {
|
|
|
|
DBGMSG( DBG_SECURITY, ( " DumpAcl @%08lx\n", Acl ));
|
|
|
|
//
|
|
// Check if the Acl is null
|
|
//
|
|
|
|
if (Acl == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Dump the Acl header
|
|
//
|
|
|
|
DBGMSG( DBG_SECURITY,
|
|
( " Revision: %02x, Size: %04x, AceCount: %04x\n",
|
|
Acl->AclRevision, Acl->AclSize, Acl->AceCount ));
|
|
|
|
//
|
|
// Now for each Ace we want do dump it
|
|
//
|
|
|
|
for (i = 0, Ace = FirstAce(Acl);
|
|
i < Acl->AceCount;
|
|
i++, Ace = NextAce(Ace) ) {
|
|
|
|
//
|
|
// print out the ace header
|
|
//
|
|
|
|
DBGMSG( DBG_SECURITY, ( " AceHeader: %08lx\n", *(PDWORD)Ace ));
|
|
|
|
//
|
|
// special case on the standard ace types
|
|
//
|
|
|
|
if ((Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ||
|
|
(Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE) ||
|
|
(Ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE) ||
|
|
(Ace->Header.AceType == SYSTEM_ALARM_ACE_TYPE)) {
|
|
|
|
//
|
|
// The following array is indexed by ace types and must
|
|
// follow the allowed, denied, audit, alarm seqeuence
|
|
//
|
|
|
|
static LPSTR AceTypes[] = { "Access Allowed",
|
|
"Access Denied ",
|
|
"System Audit ",
|
|
"System Alarm "
|
|
};
|
|
|
|
DBGMSG( DBG_SECURITY,
|
|
( " %s Access Mask: %08lx\n",
|
|
AceTypes[Ace->Header.AceType], Ace->Mask ));
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_SECURITY, (" Unknown Ace Type\n" ));
|
|
|
|
}
|
|
|
|
DBGMSG( DBG_SECURITY,
|
|
( " AceSize = %d\n AceFlags = ", Ace->Header.AceSize ));
|
|
|
|
if (Ace->Header.AceFlags & OBJECT_INHERIT_ACE) {
|
|
DBGMSG( DBG_SECURITY, ( " OBJECT_INHERIT_ACE\n" ));
|
|
DBGMSG( DBG_SECURITY, ( " " ));
|
|
}
|
|
if (Ace->Header.AceFlags & CONTAINER_INHERIT_ACE) {
|
|
DBGMSG( DBG_SECURITY, ( " CONTAINER_INHERIT_ACE\n" ));
|
|
DBGMSG( DBG_SECURITY, ( " " ));
|
|
}
|
|
|
|
if (Ace->Header.AceFlags & NO_PROPAGATE_INHERIT_ACE) {
|
|
DBGMSG( DBG_SECURITY, ( " NO_PROPAGATE_INHERIT_ACE\n" ));
|
|
DBGMSG( DBG_SECURITY, ( " " ));
|
|
}
|
|
|
|
if (Ace->Header.AceFlags & INHERIT_ONLY_ACE) {
|
|
DBGMSG( DBG_SECURITY, ( " INHERIT_ONLY_ACE\n" ));
|
|
DBGMSG( DBG_SECURITY, ( " " ));
|
|
}
|
|
|
|
if (Ace->Header.AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) {
|
|
DBGMSG( DBG_SECURITY, ( " SUCCESSFUL_ACCESS_ACE_FLAG\n" ));
|
|
DBGMSG( DBG_SECURITY, ( " " ));
|
|
}
|
|
|
|
if (Ace->Header.AceFlags & FAILED_ACCESS_ACE_FLAG) {
|
|
DBGMSG( DBG_SECURITY, ( " FAILED_ACCESS_ACE_FLAG\n" ));
|
|
DBGMSG( DBG_SECURITY, ( " " ));
|
|
}
|
|
|
|
DBGMSG( DBG_SECURITY, ( "\n" ));
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif // if DBG
|
|
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
BuildJobOwnerSecurityDescriptor
|
|
|
|
Routine Description:
|
|
|
|
This routine builds a SD that will be passed as CreatorDescriptor argument to
|
|
CreatePrivateObjectSecurityEx. The SD on any new job will be created using
|
|
the SD returned by this function and will inherit from the SD from the print
|
|
queue.
|
|
|
|
BuildJobOwnerSecurityDescriptor --> SD Print queue SD
|
|
\ /
|
|
\ / Inheritance
|
|
\ /
|
|
Job SD
|
|
|
|
The SD created in this function will have as owner the user from the
|
|
hToken argument. (The user impersonated by the thread from where we have the
|
|
hToken). The ACL grants full access on the job to the local system.
|
|
|
|
The reason why we need this special SD is the following. If you remove the
|
|
creatorowner from the print queue SD and no user has manage docs permissions
|
|
CreatePrivateObjectSecurity won't find any inheritable ACEs in the parent.
|
|
Thus it grants full permissions to the owner and to the local system. This
|
|
leads to a random behavior where according to the UI the user should not be
|
|
able to manage his docs, but the SD on the job will grant manage docs rights.
|
|
|
|
We don't want that. We want the local system to have full privileges on the job
|
|
and the user who submitted the job should be granted permissions only if:
|
|
- the user has manage doc rights
|
|
- creator owner is present in the print queue SD
|
|
|
|
Arguments:
|
|
|
|
hToken - impersonation token of the user who creates a new job
|
|
ppSD - pointer to recieve SD
|
|
|
|
Return Value:
|
|
|
|
Win32 error code
|
|
|
|
--*/
|
|
BOOL
|
|
BuildJobOwnerSecurityDescriptor(
|
|
IN HANDLE hToken,
|
|
OUT PSECURITY_DESCRIPTOR *ppSD
|
|
)
|
|
{
|
|
DWORD Error = ERROR_INVALID_PARAMETER;
|
|
|
|
if (hToken && ppSD)
|
|
{
|
|
PVOID pUserInfo = NULL;
|
|
DWORD cbUserInfo = 0;
|
|
|
|
//
|
|
// Get the owner from the thread token
|
|
//
|
|
Error = GetTokenInformation(hToken,
|
|
TokenUser,
|
|
NULL,
|
|
0,
|
|
&cbUserInfo) ? ERROR_SUCCESS : GetLastError();
|
|
|
|
//
|
|
// Allocate buffer and try getting the owner again
|
|
//
|
|
if (Error == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
if (pUserInfo = AllocSplMem(cbUserInfo))
|
|
{
|
|
Error = GetTokenInformation(hToken,
|
|
TokenUser,
|
|
pUserInfo,
|
|
cbUserInfo,
|
|
&cbUserInfo) ? ERROR_SUCCESS : GetLastError();
|
|
|
|
//
|
|
// Build the SD. We grant read control to the owner of the job
|
|
//
|
|
if (Error == ERROR_SUCCESS)
|
|
{
|
|
DWORD ObjectType = SPOOLER_OBJECT_DOCUMENT;
|
|
PSID AceSid[2];
|
|
ACCESS_MASK AceMask[2];
|
|
BYTE InheritFlags[2];
|
|
UCHAR AceType[2];
|
|
DWORD AceCount = 0;
|
|
PSID pUserSid;
|
|
|
|
pUserSid = ((((TOKEN_USER *)pUserInfo)->User)).Sid;
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = ((((TOKEN_USER *)pUserInfo)->User)).Sid;
|
|
AceMask[AceCount] = JOB_READ;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
|
|
AceSid[AceCount] = pLocalSystemSid;
|
|
AceMask[AceCount] = JOB_ALL_ACCESS;
|
|
InheritFlags[AceCount] = 0;
|
|
AceCount++;
|
|
|
|
Error = BuildPrintObjectProtection(AceType,
|
|
AceCount,
|
|
AceSid,
|
|
AceMask,
|
|
InheritFlags,
|
|
pUserSid,
|
|
NULL,
|
|
&GenericMapping[ObjectType],
|
|
ppSD) ? ERROR_SUCCESS : GetLastError();
|
|
|
|
}
|
|
|
|
FreeSplMem(pUserInfo);
|
|
}
|
|
else
|
|
{
|
|
Error = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
|
|
SetLastError(Error);
|
|
|
|
return Error == ERROR_SUCCESS;
|
|
}
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
DestroyJobOwnerSecurityDescriptor
|
|
|
|
Routine Description:
|
|
|
|
This routine frees a SD allocated by CreatejobOwnerSecurityDescriptor
|
|
|
|
Arguments:
|
|
|
|
pSD - pointer to SD
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
VOID
|
|
DestroyJobOwnerSecurityDescriptor(
|
|
IN PSECURITY_DESCRIPTOR pSD
|
|
)
|
|
{
|
|
if (pSD)
|
|
{
|
|
LocalFree(pSD);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
InitializeSecurityStructures
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes security structures.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE - function succeeded
|
|
FALSE - function failed, GetLastError() returns the reason
|
|
|
|
--*/
|
|
BOOL
|
|
InitializeSecurityStructures(
|
|
VOID
|
|
)
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
return !!CreateServerSecurityDescriptor() &&
|
|
LookupPrivilegeValue(NULL,
|
|
SE_LOAD_DRIVER_NAME,
|
|
&gLoadDriverPrivilegeLuid) &&
|
|
AllocateAndInitializeSid(&NtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pLocalSystemSid) &&
|
|
AllocateAndInitializeSid(&NtAuthority,
|
|
1,
|
|
SECURITY_NETWORK_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pNetworkLogonSid) &&
|
|
AllocateAndInitializeSid(&NtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_GUESTS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&pGuestsSid);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
PrincipalIsRemoteGuest
|
|
|
|
Routine Description:
|
|
|
|
This routine checks whether remote guest is present in a token.
|
|
Remote guest = network + guest
|
|
|
|
Arguments:
|
|
|
|
hToken - handle to token, NULL is ok (see CheckTokenMemberShip)
|
|
pbRemoteGuest - pointer to receive BOOL. true means remote guest
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - pbRemoteGuest is reliable
|
|
other win32 error code, do not use pbRemoteGuest
|
|
|
|
--*/
|
|
DWORD
|
|
PrincipalIsRemoteGuest(
|
|
IN HANDLE hToken,
|
|
OUT BOOL *pbRemoteGuest
|
|
)
|
|
{
|
|
DWORD Error = ERROR_INVALID_PARAMETER;
|
|
|
|
if (pbRemoteGuest)
|
|
{
|
|
BOOL bNetwork = FALSE;
|
|
BOOL bGuests = FALSE;
|
|
|
|
if (CheckTokenMembership(hToken, pNetworkLogonSid, &bNetwork) &&
|
|
CheckTokenMembership(hToken, pGuestsSid, &bGuests))
|
|
{
|
|
*pbRemoteGuest = bNetwork && bGuests;
|
|
|
|
Error = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
*pbRemoteGuest = FALSE;
|
|
|
|
Error = GetLastError();
|
|
}
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
CheckPrivilegePresent
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if a certain privilege is present in a token
|
|
|
|
Arguments:
|
|
|
|
hToken - thread or process token
|
|
pLuid - pointer to luid for the privilege to be searched for
|
|
pbPresent - will be set to true is the privilege is present in the token
|
|
pAttributes - will be set to the attributes of the privilege. It is a mask
|
|
indicating if the privilege is disabled, enabled, enabled by
|
|
default.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - the function executed successfully and the caller can use
|
|
pbPresent and pAttributes
|
|
other Win32 error
|
|
|
|
--*/
|
|
DWORD
|
|
CheckPrivilegePresent(
|
|
IN HANDLE hToken,
|
|
IN PLUID pLuid,
|
|
IN LPBOOL pbPresent,
|
|
IN LPDWORD pAttributes OPTIONAL
|
|
)
|
|
{
|
|
DWORD Error = ERROR_INVALID_PARAMETER;
|
|
PVOID pPrivInfo = NULL;
|
|
DWORD cbPrivInfo = kGuessTokenPrivileges;
|
|
|
|
if (pLuid && pbPresent)
|
|
{
|
|
*pbPresent = FALSE;
|
|
|
|
pPrivInfo = AllocSplMem(cbPrivInfo);
|
|
|
|
Error = pPrivInfo ? ERROR_SUCCESS : GetLastError();
|
|
|
|
if (Error == ERROR_SUCCESS)
|
|
{
|
|
Error = GetTokenInformation(hToken,
|
|
TokenPrivileges,
|
|
pPrivInfo,
|
|
cbPrivInfo,
|
|
&cbPrivInfo) ? ERROR_SUCCESS : GetLastError();
|
|
}
|
|
|
|
//
|
|
// Reallocate buffer and try getting the privileges
|
|
//
|
|
if (Error == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
FreeSplMem(pPrivInfo);
|
|
|
|
pPrivInfo = AllocSplMem(cbPrivInfo);
|
|
|
|
Error = pPrivInfo ? ERROR_SUCCESS : GetLastError();
|
|
|
|
if (Error == ERROR_SUCCESS)
|
|
{
|
|
Error = GetTokenInformation(hToken,
|
|
TokenPrivileges,
|
|
pPrivInfo,
|
|
cbPrivInfo,
|
|
&cbPrivInfo) ? ERROR_SUCCESS : GetLastError();
|
|
}
|
|
}
|
|
|
|
if (Error == ERROR_SUCCESS)
|
|
{
|
|
TOKEN_PRIVILEGES *pTokenPrivileges = (TOKEN_PRIVILEGES *)pPrivInfo;
|
|
DWORD uCount;
|
|
|
|
//
|
|
// Search the privilege in the list of privileges present in the token
|
|
//
|
|
for (uCount = 0; uCount < pTokenPrivileges->PrivilegeCount; uCount++)
|
|
{
|
|
if (pTokenPrivileges->Privileges[uCount].Luid.HighPart == pLuid->HighPart &&
|
|
pTokenPrivileges->Privileges[uCount].Luid.LowPart == pLuid->LowPart)
|
|
{
|
|
//
|
|
// We found the privilege
|
|
//
|
|
*pbPresent = TRUE;
|
|
|
|
if (pAttributes)
|
|
{
|
|
*pAttributes = pTokenPrivileges->Privileges[uCount].Attributes;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeSplMem(pPrivInfo);
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
CreateSelfRelativeSD
|
|
|
|
Routine Description:
|
|
|
|
Creates a self relative SD. The function does not modifiy *pAbsoluteSD
|
|
|
|
Arguments:
|
|
|
|
pAbsoluteSD - pointer to SD in absolute form
|
|
ppRelativeSD - pointer where to receive a pointer to the SD in self relative form.
|
|
The caller needs to free the returned memory using LocalFree
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - *ppRelativeSD points to the SD in relative form. Must be freed with LocalFree
|
|
other Win32 error
|
|
|
|
--*/
|
|
DWORD
|
|
CreateSelfRelativeSD(
|
|
IN PSECURITY_DESCRIPTOR pAbsoluteSD,
|
|
OUT PSECURITY_DESCRIPTOR *ppRelativeSD
|
|
)
|
|
{
|
|
DWORD cbNeeded = 0;
|
|
DWORD Error = ERROR_SUCCESS;
|
|
|
|
*ppRelativeSD = NULL;
|
|
|
|
if (!MakeSelfRelativeSD(pAbsoluteSD, NULL, &cbNeeded) && (Error = GetLastError()) == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
PSECURITY_DESCRIPTOR pSelfRelativeSD = LocalAlloc(LMEM_FIXED, cbNeeded);
|
|
|
|
if (pSelfRelativeSD)
|
|
{
|
|
if (MakeSelfRelativeSD(pAbsoluteSD, pSelfRelativeSD, &cbNeeded))
|
|
{
|
|
*ppRelativeSD = pSelfRelativeSD;
|
|
|
|
pSelfRelativeSD = NULL;
|
|
|
|
Error = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Error = GetLastError();
|
|
}
|
|
|
|
LocalFree(pSelfRelativeSD);
|
|
}
|
|
else
|
|
{
|
|
Error = GetLastError();
|
|
}
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
AddEntriesToSecurityDescriptor
|
|
|
|
Routine Description:
|
|
|
|
Adds ACEs to an existing SD. *ppSecurityDescriptor is assumed to be not null and valid.
|
|
|
|
Arguments:
|
|
|
|
cCountOfExplicitEntries - number of entries in array
|
|
pListOfExplicitEntries - array of EXPLICIT_ACCESS
|
|
ppSecurityDescriptor - on input, pointer to SD in self relative form, allocated with LocalAlloc
|
|
on output, pointer to SD in self relative form with added ACEs
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - the function freed the original *ppSecurityDescriptor and stored a pointer to
|
|
a SD equivalent to the original + added ACEs. Caller must free it with LocalFree
|
|
other Win32 error - *ppSecurityDescriptor is not modified
|
|
|
|
--*/
|
|
DWORD
|
|
AddEntriesToSecurityDescriptor(
|
|
IN ULONG cCountOfExplicitEntries,
|
|
IN PEXPLICIT_ACCESS pListOfExplicitEntries,
|
|
IN OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor
|
|
)
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
SECURITY_DESCRIPTOR AbsoluteSD = {0};
|
|
DWORD AbsoluteSDSize = sizeof(SECURITY_DESCRIPTOR);
|
|
PACL pDacl = NULL;
|
|
DWORD DaclSize = 0;
|
|
PACL pSacl = NULL;
|
|
DWORD SaclSize = 0;
|
|
PSID pOwner = NULL;
|
|
DWORD OwnerSize = 0;
|
|
PSID pGroup = NULL;
|
|
DWORD GroupSize = 0;
|
|
|
|
if (!MakeAbsoluteSD(*ppSecurityDescriptor,
|
|
&AbsoluteSD,
|
|
&AbsoluteSDSize,
|
|
pDacl,
|
|
&DaclSize,
|
|
pSacl,
|
|
&SaclSize,
|
|
pOwner,
|
|
&OwnerSize,
|
|
pGroup,
|
|
&GroupSize) && (Error = GetLastError()) == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
if (DaclSize == 0)
|
|
{
|
|
//
|
|
// Nothing to do, return the same SD back to the caller
|
|
//
|
|
Error = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
if (!(pDacl = LocalAlloc(LMEM_FIXED, DaclSize)) ||
|
|
SaclSize > 0 && !(pSacl = LocalAlloc(LMEM_FIXED, SaclSize)) ||
|
|
OwnerSize > 0 && !(pOwner = LocalAlloc(LMEM_FIXED, OwnerSize)) ||
|
|
GroupSize > 0 && !(pGroup = LocalAlloc(LMEM_FIXED, GroupSize)))
|
|
{
|
|
Error = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
if (MakeAbsoluteSD(*ppSecurityDescriptor,
|
|
&AbsoluteSD,
|
|
&AbsoluteSDSize,
|
|
pDacl,
|
|
&DaclSize,
|
|
pSacl,
|
|
&SaclSize,
|
|
pOwner,
|
|
&OwnerSize,
|
|
pGroup,
|
|
&GroupSize))
|
|
{
|
|
PACL pNewDacl = NULL;
|
|
|
|
if ((Error = SetEntriesInAcl(cCountOfExplicitEntries,
|
|
pListOfExplicitEntries,
|
|
pDacl,
|
|
&pNewDacl)) == ERROR_SUCCESS)
|
|
{
|
|
if (SetSecurityDescriptorDacl(&AbsoluteSD,
|
|
TRUE,
|
|
pNewDacl,
|
|
TRUE))
|
|
{
|
|
PSECURITY_DESCRIPTOR pTempSD = NULL;
|
|
|
|
if ((Error = CreateSelfRelativeSD(&AbsoluteSD, &pTempSD)) == ERROR_SUCCESS)
|
|
{
|
|
LocalFree(*ppSecurityDescriptor);
|
|
|
|
*ppSecurityDescriptor = pTempSD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error = GetLastError();
|
|
}
|
|
|
|
LocalFree(pNewDacl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error = GetLastError();
|
|
}
|
|
}
|
|
|
|
LocalFree(pDacl);
|
|
LocalFree(pSacl);
|
|
LocalFree(pOwner);
|
|
LocalFree(pGroup);
|
|
}
|
|
}
|
|
|
|
return Error;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
GrantJobReadPermissionToLocalSystem
|
|
|
|
Routine Description:
|
|
|
|
Adds an ACE that grants job_read permission to local system on an existing SD
|
|
|
|
Arguments:
|
|
|
|
ppSecurityDescriptor - on input, pointer to SD in self relative form, allocated with LocalAlloc
|
|
on output, pointer to SD in self relative form with added ACE for local system
|
|
|
|
Return Value:
|
|
|
|
TRUE - the function freed the original *ppSecurityDescriptor and stored a pointer to
|
|
a SD equivalent to the original + added ACE. Caller must free it with LocalFree
|
|
FALSE - *ppSecurityDescriptor is not modified, use GetLastError
|
|
|
|
--*/
|
|
BOOL
|
|
GrantJobReadPermissionToLocalSystem(
|
|
IN OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor
|
|
)
|
|
{
|
|
DWORD Error;
|
|
EXPLICIT_ACCESS ExplicitAccess[1];
|
|
|
|
ExplicitAccess[0].grfAccessMode = GRANT_ACCESS;
|
|
ExplicitAccess[0].grfAccessPermissions = JOB_READ;
|
|
ExplicitAccess[0].grfInheritance = NO_INHERITANCE;
|
|
ExplicitAccess[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
|
|
ExplicitAccess[0].Trustee.pMultipleTrustee = NULL;
|
|
ExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
|
ExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ExplicitAccess[0].Trustee.ptstrName = pLocalSystemSid;
|
|
|
|
Error = AddEntriesToSecurityDescriptor(1, ExplicitAccess, ppSecurityDescriptor);
|
|
|
|
SetLastError(Error);
|
|
|
|
return Error == ERROR_SUCCESS;
|
|
}
|