Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1005 lines
30 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
security.c
Abstract:
Security addons to the tree-manipulation module
for the RPL service.
Author:
Jon Newman (jonn) 16 - February - 1994
Revision History:
Jon Newman (jonn) 16 - February - 1994
Added RplGrant*Perms primitives
Vladimir Z. Vulovic (vladimv) 06 - May - 1994
Security fix: added new api + major reorg.
--*/
#include "local.h"
#include "rpldb.h" // Call(), CallM()
#include "db.h"
#include "dblib.h" // RplFilterFirst()
#include "tree.h"
#include "treei.h"
#include "wksta.h" // WKSTA_WkstaName
#include "profile.h" // PROFILE_ProfileName
#include <ntseapi.h>
#include <ntrtl.h> // RtlAllocateAndInitializeSid
#include <ntlsa.h>
#include <ntsam.h>
#include <nturtl.h> // see IsNTFS
#include <lmserver.h> // NetServerGetInfo
typedef enum _RPL_SD_BLOCK_TYPE {
RPL_SD_BLOCK_TYPE_ADMINONLY = 0,// Only RPLADMIN gets access
RPL_SD_BLOCK_TYPE_ALLUSERS, // RPLADMIN gets full access, RPLUSER
// gets RX access
RPL_SD_BLOCK_TYPE_ONEUSER // RPLADMIN gets full access, one
// wksta account gets RWXCDA access
} RPL_SD_BLOCK_TYPE;
/*
* This is the block of information which is retained between the recursive
* calls in a single RplDoTree run. It remembers the SECURITY_DESCRIPTOR
* which was assigned to the last file/directory, so if the next
* file/directory wants the same permissions (as is usually the case)
* we do not have to fetch everything all over again. It also contains
* some information about what kind of SECURITY_DESCRIPTOR is currently
* stored, plus items needed to get a different SECURITY_DESCRIPTOR.
*/
typedef struct _RPL_SD_BLOCK {
BOOL SkipThisTree; // must be the first element
PSECURITY_DESCRIPTOR psd;
PACL paclDacl;
BOOL MonitorSecurityType;
RPL_SD_BLOCK_TYPE SecurityType;
//
// CODEWORK do we need to keep these sids around
//
PSID psidSystem;
PSID psidAdministrators;
POLICY_ACCOUNT_DOMAIN_INFO * pinfoRPLUSERAccountDomain;
SAM_HANDLE hsamRPLUSERAccountDomain;
POLICY_ACCOUNT_DOMAIN_INFO * pinfoWkstaAccountDomain;
SAM_HANDLE hsamWkstaAccountDomain;
} RPL_SD_BLOCK, *PRPL_SD_BLOCK;
//
// The DACL we assign to files has 2 or 3 ACEs.
//
#define RPL_INDEX_SYSTEM_ACE 0
#define RPL_INDEX_ADMINS_ACE 1
#define RPL_INDEX_CHANGEABLE_ACE 2
DWORD IsNTFS(
IN PWCHAR Resource,
OUT BOOL * pfIsNTFS
)
/*++
NAME: IsNTFS
SYNOPSIS: This function checks the given file resource and attempts to
determine if it is on an NTFS partition.
ENTRY: Resource - Pointer to file/directory name in the form
"X:\aaa\bbb\ccc
pfIsNTFS - Pointer to BOOL that will receive the results
RETURNS: NO_ERROR if successful, error code otherwise
NOTES:
HISTORY:
JonN 23-Feb-1994 Copied from acledit\ntfsacl.cxx
--*/
{
DWORD Error = NO_ERROR ;
HANDLE hDrive = NULL ;
WCHAR awchDosDriveName[4];
WCHAR awchNtDriveName[40];
UNICODE_STRING unistrNtDriveName;
BYTE buffFsInfo[ sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 200 ] ;
PFILE_FS_ATTRIBUTE_INFORMATION FsInfo =
(PFILE_FS_ATTRIBUTE_INFORMATION)buffFsInfo ;
OBJECT_ATTRIBUTES oa ;
IO_STATUS_BLOCK StatusBlock ;
RPL_ASSERT( Resource != NULL
&& wcslen(Resource) >= 3
&& pfIsNTFS != NULL ) ;
*pfIsNTFS = FALSE ;
//
// Determine the NT drive name
//
awchDosDriveName[0] = Resource[0];
awchDosDriveName[1] = Resource[1];
awchDosDriveName[2] = Resource[2];
awchDosDriveName[3] = L'\0';
RPL_ASSERT( awchDosDriveName[1] == L':'
&& awchDosDriveName[2] == L'\\' );
unistrNtDriveName.Buffer = awchNtDriveName;
unistrNtDriveName.Length = sizeof(awchNtDriveName);
unistrNtDriveName.MaximumLength = sizeof(awchNtDriveName);
if (!RtlDosPathNameToNtPathName_U( awchDosDriveName,
&unistrNtDriveName,
NULL,
NULL)){
RPL_ASSERT( FALSE ) ;
Error = ERROR_NOT_ENOUGH_MEMORY ;
goto cleanup;
}
RPL_ASSERT( unistrNtDriveName.Length < unistrNtDriveName.MaximumLength );
unistrNtDriveName.Buffer[unistrNtDriveName.Length/sizeof(WCHAR)] = L'\0';
//
// Open the NT volume and query volume information
//
InitializeObjectAttributes( &oa,
&unistrNtDriveName,
OBJ_CASE_INSENSITIVE,
0,
0 );
Error = NtOpenFile( &hDrive,
SYNCHRONIZE | FILE_READ_DATA,
&oa,
&StatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_ALERT);
if (Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
Error = NtQueryVolumeInformationFile(
hDrive,
&StatusBlock,
(PVOID) FsInfo,
sizeof(buffFsInfo),
FileFsAttributeInformation );
if (Error != NO_ERROR) {
if (Error == ERROR_ACCESS_DENIED) {
RplDump( ++RG_Assert, ( "(access denied) assuming the file system is NTFS"));
Error = NO_ERROR ;
*pfIsNTFS = TRUE ;
} else {
RplDump( ++RG_Assert, ( "Error=%d", Error));
}
goto cleanup;
}
if ( FsInfo->FileSystemAttributes & FILE_PERSISTENT_ACLS){
*pfIsNTFS = TRUE ;
}
cleanup:
/* Close the volume if we ever opened it
*/
if ( hDrive != NULL){
NtClose( hDrive );
}
return( Error) ;
}
DWORD AddInheritableAce(
IN OUT ACL * pacl,
IN ACCESS_MASK mask,
IN SID * psid
)
/*++
Add an ACCESS_ALLOWED_ACE to the ACL, with all inheritance flags set
--*/
{
DWORD Error = NO_ERROR;
ACCESS_ALLOWED_ACE * pace = NULL;
if ( !AddAccessAllowedAce( pacl, ACL_REVISION, mask, psid )) {
Error = GetLastError();
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
//
// new ACE should be the last ACE
//
if ( !GetAce( pacl, (pacl->AceCount) - 1, &pace )) {
Error = GetLastError();
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
RPL_ASSERT( EqualSid( psid, &(pace->SidStart)) );
(pace->Header.AceFlags) |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
cleanup:
return( Error);
}
VOID DeInitSdBlock( IN RPL_SD_BLOCK * psdblock)
{
if (psdblock->psd != NULL) {
RplMemFree( RG_MemoryHandle, psdblock->psd );
}
if (psdblock->psidSystem != NULL) {
RtlFreeSid( psdblock->psidSystem );
}
if (psdblock->psidAdministrators != NULL) {
RtlFreeSid( psdblock->psidAdministrators );
}
if ( psdblock->pinfoRPLUSERAccountDomain != NULL) {
LsaFreeMemory( psdblock->pinfoRPLUSERAccountDomain);
}
if (psdblock->hsamRPLUSERAccountDomain != NULL) {
SamCloseHandle( psdblock->hsamRPLUSERAccountDomain);
}
if ( psdblock->pinfoWkstaAccountDomain != NULL
&& psdblock->pinfoWkstaAccountDomain !=
psdblock->pinfoRPLUSERAccountDomain) {
LsaFreeMemory( psdblock->pinfoWkstaAccountDomain);
}
if ( psdblock->hsamWkstaAccountDomain != NULL
&& psdblock->hsamWkstaAccountDomain !=
psdblock->hsamRPLUSERAccountDomain) {
SamCloseHandle( psdblock->hsamWkstaAccountDomain);
}
}
DWORD GetDomains(
OUT SAM_HANDLE * phsamWkstaAccountDomain,
OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoWkstaAccountDomain,
OUT SAM_HANDLE * phsamRPLUSERAccountDomain,
OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoRPLUSERAccountDomain,
IN ACCESS_MASK DesiredAccess
)
/*--
Get a SAM_HANDLE to the account domain, and other information about
the account domain. This routine uses SAM and LSA directly.
--*/
{
DWORD Error = NO_ERROR;
SAM_HANDLE hsamAccountServer = NULL;
SAM_HANDLE hsamLocalServer = NULL;
LSA_HANDLE hlsaLocal = NULL;
LSA_HANDLE hlsaPDC = NULL;
OBJECT_ATTRIBUTES oa;
SECURITY_QUALITY_OF_SERVICE sqos;
BOOL fIsPrimaryDC = FALSE;
BOOL fIsBackupDC = FALSE;
BOOL fIsNotDC = FALSE;
SERVER_INFO_101 * psi101 = NULL;
POLICY_PRIMARY_DOMAIN_INFO * pinfoPrimaryDomain = NULL;
WCHAR awchPrimaryDomain[ DNLEN+1 ];
WCHAR * pwchPDC = NULL;
UNICODE_STRING unistrPDC;
RPL_ASSERT( phsamWkstaAccountDomain != NULL );
RPL_ASSERT( ppinfoWkstaAccountDomain != NULL );
RPL_ASSERT( phsamRPLUSERAccountDomain != NULL );
RPL_ASSERT( ppinfoRPLUSERAccountDomain != NULL );
//
// Set up object attributes (borrowed from uintlsa.cxx)
//
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sqos.ImpersonationLevel = SecurityImpersonation;
sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
sqos.EffectiveOnly = FALSE;
InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
oa.SecurityQualityOfService = &sqos;
//
// Determine whether this is a PDC, BDC or other server.
//
Error = NetServerGetInfo( NULL, 101, (LPBYTE *)&psi101 );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
RPL_ASSERT( psi101 != NULL );
fIsPrimaryDC = !!(psi101->sv101_type & SV_TYPE_DOMAIN_CTRL);
fIsBackupDC = !!(psi101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL);
fIsNotDC = !(psi101->sv101_type & (SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL));
//
// Get a local LSA handle
//
Error = LsaOpenPolicy( NULL,
&oa,
GENERIC_EXECUTE,
&hlsaLocal );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
//
//
// If this is not a DC, get the name of the primary domain and
// determine whether this is a ServerNt/WinNt machine on a workgroup.
//
if (!fIsPrimaryDC) {
Error = LsaQueryInformationPolicy( hlsaLocal,
PolicyPrimaryDomainInformation,
&pinfoPrimaryDomain );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
if (pinfoPrimaryDomain->Sid == NULL) {
//
// This is a ServerNt/WinNt machine on a workgroup. Pretend
// this is a Primary DC from now on.
//
fIsPrimaryDC = TRUE;
fIsBackupDC = fIsNotDC = FALSE;
}
}
//
// If this is not a PDC, get the name of a primary domain PDC
// and reload the LSA handle
//
if (!fIsPrimaryDC) {
RPL_ASSERT( DNLEN*sizeof(WCHAR) >= pinfoPrimaryDomain->Name.Length );
memcpy( awchPrimaryDomain,
pinfoPrimaryDomain->Name.Buffer,
pinfoPrimaryDomain->Name.Length ); // this is in bytes
awchPrimaryDomain[ pinfoPrimaryDomain->Name.Length / sizeof(WCHAR) ]
= L'\0';
Error = NetGetDCName( NULL,
awchPrimaryDomain,
(LPBYTE *)&pwchPDC );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
unistrPDC.Buffer = pwchPDC;
unistrPDC.Length = wcslen( pwchPDC ) * sizeof(WCHAR);
unistrPDC.MaximumLength = unistrPDC.Length + sizeof(WCHAR);
Error = LsaOpenPolicy( &unistrPDC,
&oa,
GENERIC_EXECUTE,
&hlsaPDC );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
}
//
// Load information on the RPLUSERAccount domain
//
Error = LsaQueryInformationPolicy( (fIsBackupDC) ? hlsaPDC : hlsaLocal,
PolicyAccountDomainInformation,
ppinfoRPLUSERAccountDomain );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
//
// Load a SAM handle on the RPLUSERAccount domain
//
Error = SamConnect( (fIsBackupDC) ? &unistrPDC : NULL,
&hsamLocalServer,
SAM_SERVER_LOOKUP_DOMAIN,
NULL );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
Error = SamOpenDomain( hsamLocalServer,
DesiredAccess,
(*ppinfoRPLUSERAccountDomain)->DomainSid,
phsamRPLUSERAccountDomain );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
//
// If this is not a DC, load information and a SAM handle on
// the RPLUSERAccount domain
//
if (!fIsNotDC) {
*phsamWkstaAccountDomain = *phsamRPLUSERAccountDomain;
*ppinfoWkstaAccountDomain = *ppinfoRPLUSERAccountDomain;
} else {
Error = LsaQueryInformationPolicy( hlsaPDC,
PolicyAccountDomainInformation,
ppinfoWkstaAccountDomain );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
//
// Get server handle
//
Error = SamConnect( &unistrPDC,
&hsamAccountServer,
SAM_SERVER_LOOKUP_DOMAIN,
NULL );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
Error = SamOpenDomain( hsamAccountServer,
DesiredAccess,
(*ppinfoWkstaAccountDomain)->DomainSid,
phsamWkstaAccountDomain );
if ( Error != NERR_Success) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
}
cleanup:
if ( hsamLocalServer != NULL) {
SamCloseHandle( hsamLocalServer);
}
if ( hsamAccountServer != NULL) {
SamCloseHandle( hsamAccountServer);
}
if ( hlsaLocal != NULL) {
LsaClose( hlsaLocal);
}
if ( hlsaPDC != NULL) {
LsaClose( hlsaPDC);
}
if ( pinfoPrimaryDomain != NULL) {
LsaFreeMemory( pinfoPrimaryDomain);
pinfoPrimaryDomain = NULL;
}
if ( pwchPDC != NULL ) {
NetApiBufferFree( (LPBYTE)pwchPDC );
pwchPDC = NULL;
}
if ( psi101 != NULL ) {
NetApiBufferFree( (LPBYTE)psi101 );
psi101 = NULL;
}
if ( Error != NO_ERROR
&& (*ppinfoWkstaAccountDomain) != NULL
&& (*ppinfoWkstaAccountDomain) != (*ppinfoRPLUSERAccountDomain) ) {
LsaFreeMemory( (*ppinfoWkstaAccountDomain));
*ppinfoWkstaAccountDomain = NULL;
}
if ( Error != NO_ERROR
&& (*phsamWkstaAccountDomain) != NULL
&& (*phsamWkstaAccountDomain) != (*phsamRPLUSERAccountDomain) ) {
SamCloseHandle( *phsamWkstaAccountDomain);
*phsamWkstaAccountDomain = NULL;
}
if ( Error != NO_ERROR && (*ppinfoRPLUSERAccountDomain) != NULL) {
LsaFreeMemory( (*ppinfoRPLUSERAccountDomain));
*ppinfoRPLUSERAccountDomain = NULL;
}
if ( Error != NO_ERROR && (*phsamRPLUSERAccountDomain) != NULL) {
SamCloseHandle( *phsamRPLUSERAccountDomain);
*phsamRPLUSERAccountDomain = NULL;
}
return( Error);
}
DWORD MyBuildSid(
OUT PSID * ppsidAccountSid,
IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
IN DWORD ulRid
)
{
DWORD Error = 0;
DWORD cbNewSid = 0;
PUCHAR pcSubAuthorityCount = NULL;
PDWORD pdwLastSubAuthority = NULL;
RPL_ASSERT( ppsidAccountSid != NULL && pinfoAccountDomain != NULL && ulRid != 0 );
cbNewSid = GetLengthSid( pinfoAccountDomain->DomainSid) + sizeof(DWORD);
*ppsidAccountSid = RplMemAlloc( RG_MemoryHandle, cbNewSid );
if ( *ppsidAccountSid == NULL) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto cleanup;
}
if ( !CopySid( cbNewSid, *ppsidAccountSid, pinfoAccountDomain->DomainSid)) {
Error = GetLastError();
goto cleanup;
}
pcSubAuthorityCount = GetSidSubAuthorityCount( *ppsidAccountSid );
RPL_ASSERT( pcSubAuthorityCount != NULL );
(*pcSubAuthorityCount)++;
pdwLastSubAuthority = GetSidSubAuthority(
*ppsidAccountSid, (DWORD)((*pcSubAuthorityCount)-1) );
RPL_ASSERT( pdwLastSubAuthority != NULL );
*pdwLastSubAuthority = ulRid;
cleanup:
if ( Error != NO_ERROR) {
if ( *ppsidAccountSid != NULL) {
RplMemFree( RG_MemoryHandle, *ppsidAccountSid);
}
}
return( Error);
}
SID_IDENTIFIER_AUTHORITY IDAuthorityNT = SECURITY_NT_AUTHORITY;
DWORD InitSdBlock( OUT RPL_SD_BLOCK * psdblock)
/*++
Initialize SdBlock.
--*/
{
DWORD Error;
DWORD cbNewDacl;
memset( psdblock, 0, sizeof(*psdblock));
//
// Get System SID
//
Error = RtlAllocateAndInitializeSid( &IDAuthorityNT, 1,
SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
&(psdblock->psidSystem));
if (Error != NO_ERROR) {
return( Error);
}
//
// Get Administrators SID
//
Error = RtlAllocateAndInitializeSid(
&IDAuthorityNT,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&(psdblock->psidAdministrators)
);
if (Error != NO_ERROR) {
return( Error);
}
//
// Get information about local and (if not PDC) remote domain
//
Error = GetDomains( &(psdblock->hsamWkstaAccountDomain),
&(psdblock->pinfoWkstaAccountDomain),
&(psdblock->hsamRPLUSERAccountDomain),
&(psdblock->pinfoRPLUSERAccountDomain),
GENERIC_EXECUTE );
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
return( Error);
}
//
// Start assembling DACL
//
cbNewDacl = sizeof(ACL) + (3*sizeof(ACCESS_ALLOWED_ACE))
+ (3*GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES))
- sizeof(DWORD);
psdblock->paclDacl = RplMemAlloc( RG_MemoryHandle, cbNewDacl);
if ( psdblock->paclDacl == NULL) {
Error = ERROR_NOT_ENOUGH_MEMORY;
return( Error);
}
if ( !InitializeAcl( psdblock->paclDacl, cbNewDacl, ACL_REVISION)) {
Error = GetLastError();
RplDump( ++RG_Assert, ( "Error=%d", Error));
return( Error);
}
//
// Add System ACE
//
Error = AddInheritableAce( psdblock->paclDacl, GENERIC_ALL, psdblock->psidSystem);
if (Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
return( Error);
}
//
// Add Administrators ACE
//
Error = AddInheritableAce( psdblock->paclDacl, GENERIC_ALL, psdblock->psidAdministrators);
if (Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
return( Error);
}
//
// Build security descriptor
//
psdblock->psd = RplMemAlloc( RG_MemoryHandle, sizeof(SECURITY_DESCRIPTOR) );
if ( psdblock->psd == NULL) {
Error = ERROR_NOT_ENOUGH_MEMORY;
return( Error);
}
if ( !InitializeSecurityDescriptor( psdblock->psd,
SECURITY_DESCRIPTOR_REVISION )) {
Error = GetLastError();
RplDump( ++RG_Assert, ( "Error=%d", Error));
return( Error);
}
if ( !SetSecurityDescriptorDacl( psdblock->psd, TRUE, psdblock->paclDacl, FALSE)) {
Error = GetLastError();
RplDump( ++RG_Assert, ( "Error=%d", Error));
return( Error);
}
//
// Set SecurityType in RPL_SD_BLOCK
//
psdblock->MonitorSecurityType = TRUE;
psdblock->SecurityType = RPL_SD_BLOCK_TYPE_ADMINONLY;
return( NO_ERROR);
}
DWORD UpdateSdBlock(
IN OUT RPL_SD_BLOCK * psdblock,
IN RPL_SD_BLOCK_TYPE SecurityType,
IN DWORD GrantToRid,
IN BOOL MonitorSecurityType
)
/*--
Updates the changeable ace in the security descriptor
to reflect GrantToRid.
MonitorSecurityType - do we need to monitor (keep evaluating) security type
while working on this subtree
--*/
{
DWORD Error = NO_ERROR;
PSID pGrantToSid = NULL;
//
// Delete the changeable ACE if present
//
if ( psdblock->SecurityType == RPL_SD_BLOCK_TYPE_ONEUSER
|| psdblock->SecurityType == RPL_SD_BLOCK_TYPE_ALLUSERS) {
if ( !DeleteAce( psdblock->paclDacl, RPL_INDEX_CHANGEABLE_ACE)) {
Error = GetLastError();
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
psdblock->SecurityType = RPL_SD_BLOCK_TYPE_ADMINONLY;
}
//
// Rewrite the changeable ACE if needed
//
if ( GrantToRid != 0) {
Error = MyBuildSid( &pGrantToSid,
(SecurityType == RPL_SD_BLOCK_TYPE_ALLUSERS)
? psdblock->pinfoRPLUSERAccountDomain
: psdblock->pinfoWkstaAccountDomain,
GrantToRid);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
switch( SecurityType) {
case RPL_SD_BLOCK_TYPE_ALLUSERS:
Error = AddInheritableAce( psdblock->paclDacl,
GENERIC_READ | GENERIC_EXECUTE,
pGrantToSid);
break;
case RPL_SD_BLOCK_TYPE_ONEUSER:
Error = AddInheritableAce( psdblock->paclDacl,
GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
pGrantToSid);
break;
default:
RPL_ASSERT( FALSE);
Error = NERR_RplInternal;
break;
}
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
psdblock->SecurityType = SecurityType;
}
cleanup:
if ( Error == NO_ERROR) {
psdblock->MonitorSecurityType = MonitorSecurityType;
}
if ( pGrantToSid != NULL) {
RplMemFree( RG_MemoryHandle, pGrantToSid);
}
return( Error);
}
RPL_SD_BLOCK_TYPE SecurityType( IN PWCHAR File)
/*++
Based on a position of File in %RplRoot% tree, determines what
SECURITY_DESCRIPTOR type is appropriate for this file.
--*/
{
#define WCSLEN( _x_) ( (DWORD) (sizeof(_x_)/sizeof(WCHAR) - 1) )
#define SEC_RPLFILES L"RPLFILES\\"
#define SEC_BINFILES L"BINFILES"
#define SEC_PROFILES L"PROFILES"
#define SEC_MACHINES L"MACHINES\\"
#define SEC_TMPFILES L"TMPFILES\\"
RPL_ASSERT( _wcsnicmp( File, RG_Directory, RG_DirectoryLength) == 0);
File += RG_DirectoryLength;
//
// If file is not in rplfiles, then it should be accessible to
// admins only.
//
if ( _wcsnicmp( File, SEC_RPLFILES, WCSLEN( SEC_RPLFILES)) != 0) {
return( RPL_SD_BLOCK_TYPE_ADMINONLY);
}
File += WCSLEN( SEC_RPLFILES);
//
// If file is in rplfiles\binfiles or rplfiles\profiles, then it
// should be accessible to admins & RPLUSER group.
//
if ( _wcsnicmp( File, SEC_BINFILES, WCSLEN( SEC_BINFILES)) == 0 ||
_wcsnicmp( File, SEC_PROFILES, WCSLEN( SEC_PROFILES)) == 0) {
return( RPL_SD_BLOCK_TYPE_ALLUSERS);
}
//
// If file is in rplfiles\machines\* or rplfiles\tmpfiles\*,
// then it should be accessible to admins & a particular workstation
// account.
//
if ( _wcsnicmp( File, SEC_MACHINES, WCSLEN( SEC_MACHINES)) == 0 ||
_wcsnicmp( File, SEC_TMPFILES, WCSLEN( SEC_TMPFILES)) == 0) {
return( RPL_SD_BLOCK_TYPE_ONEUSER);
}
//
// Return the default value. Files in rplfiles\configs will take
// this code path.
//
return( RPL_SD_BLOCK_TYPE_ADMINONLY);
}
DWORD SetRplPerms(
IN PWCHAR File,
IN OUT PBOOL pAuxiliaryBlock
)
/*--
This is the callback routine called by RplDoTree to set permissions
on a specific file/directory.
File Path to file or directory.
pAuxiliaryBlock Pointer to RPL_SD_BLOCK the first element
of which must be boolean SkipThisTree
--*/
{
PRPL_SD_BLOCK psdblock = (PRPL_SD_BLOCK)pAuxiliaryBlock;
psdblock->SkipThisTree = FALSE; // by default we do not skip
if ( psdblock->MonitorSecurityType) {
//
// We skip subtrees that require different security information.
//
if ( SecurityType( File) != psdblock->SecurityType) {
psdblock->SkipThisTree = TRUE;
return( NO_ERROR);
}
}
//
// Set the actual file security. Note that this call will succeed
// even if the volume is not NTFS, thus we must check for
// partition type elsewhere.
//
if ( !SetFileSecurity( File, DACL_SECURITY_INFORMATION, psdblock->psd)) {
DWORD Error = GetLastError();
RplDump( ++RG_Assert, ( "Error=%d", Error));
return( Error);
}
return( NO_ERROR);
}
NET_API_STATUS NET_API_FUNCTION
NetrRplSetSecurity(
IN RPL_RPC_HANDLE ServerHandle,
IN LPWSTR WkstaName,
IN DWORD WkstaRid,
IN DWORD RplUserRid
)
/*++
There are 3 types of permissions: ADMINONLY, RPLUSER & ONEUSER.
For each type we first construct the appropriate security
description then use it.
--*/
{
DWORD Error;
WCHAR Directory[ MAX_PATH];
BOOL IsNtfs;
RPL_SD_BLOCK sdblock;
//
// Make working copy of RG_Directory, then verify it is NTFS.
// If it is not NTFS then there is nothing for us to do here.
//
wcscpy( Directory, RG_Directory);
Error = IsNTFS( Directory, &IsNtfs);
if (Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
return( Error);
} else if (!IsNtfs) {
return( NO_ERROR);
}
//
// Initialize to ADMINONLY permissions only.
//
Error = InitSdBlock( &sdblock);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
//
// If RplUserRid is given, set both ADMINONLY & RPLUSER permissions
// (different permissions on different trees).
//
if ( RplUserRid != 0) {
//
// Set ADMINONLY permissions.
//
Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
SetRplPerms, (PVOID)&sdblock);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
//
// Initialize to RPLUSER permissions, then set them.
//
Error = UpdateSdBlock( &sdblock, RPL_SD_BLOCK_TYPE_ALLUSERS, RplUserRid, FALSE);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
wcscpy( Directory, RG_Directory);
wcscat( Directory, L"RPLFILES\\BINFILES");
Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
SetRplPerms, (PVOID)&sdblock);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
wcscpy( Directory, RG_Directory);
wcscat( Directory, L"RPLFILES\\PROFILES");
Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
SetRplPerms, (PVOID)&sdblock);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
}
//
// If WkstaRid is given, initilize then set ONEUSER permissions.
//
if ( WkstaRid != 0) {
if ( !ValidName( WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
return( ERROR_INVALID_PARAMETER);
}
Error = UpdateSdBlock( &sdblock, RPL_SD_BLOCK_TYPE_ONEUSER, WkstaRid, FALSE);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
wcscpy( Directory, RG_Directory);
wcscat( Directory, L"RPLFILES\\MACHINES\\");
wcscat( Directory, WkstaName);
Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
SetRplPerms, (PVOID)&sdblock);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
wcscpy( Directory, RG_Directory);
wcscat( Directory, L"RPLFILES\\TMPFILES\\");
wcscat( Directory, WkstaName);
Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
SetRplPerms, (PVOID)&sdblock);
if ( Error != NO_ERROR) {
RplDump( ++RG_Assert, ( "Error=%d", Error));
goto cleanup;
}
}
cleanup:
DeInitSdBlock( &sdblock);
if (Error != NO_ERROR) {
//
// CODEWORK Need a more appropriate event log ?!
//
RplReportEvent( NELOG_RplCheckSecurity, NULL, sizeof(DWORD), &Error);
}
return( Error);
}