windows-nt/Source/XPSP1/NT/ds/security/tools/dsacls/refresh.cpp

921 lines
23 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1998 - 1998 Microsoft Corporation
Module Name:
refresh.c
Abstract:
This Module implements the delegation tool, which allows for the management
of access to DS objects
Author:
Mac McLain (MacM) 10-15-96
Environment:
User Mode
Revision History:
--*/
#include "stdafx.h"
#include "utils.h"
#include "dsace.h"
#include "dsacls.h"
typedef struct _DEFAULT_SD_NODE {
PWSTR ObjectClass;
PSECURITY_DESCRIPTOR DefaultSd;
struct _DEFAULT_SD_NODE *Next;
} DEFAULT_SD_NODE, *PDEFAULT_SD_NODE;
typedef struct _DEFAULT_SD_INFO {
LDAP *Ldap;
PWSTR SchemaPath;
PSID DomainSid;
PDEFAULT_SD_NODE SdList;
} DEFAULT_SD_INFO, *PDEFAULT_SD_INFO;
#define DSACL_ALL_FILTER L"(ObjectClass=*)"
#define DSACL_SCHEMA_NC L"schemaNamingContext"
#define DSACL_OBJECT_CLASS L"objectClass"
#define DSACL_LDAP_DN L"(ldapDisplayName="
#define DSACL_LDAP_DN_CLOSE L")"
#define DSACL_DEFAULT_SD L"defaultSecurityDescriptor"
DWORD
FindDefaultSdForClass(
IN PWSTR ClassId,
IN PDEFAULT_SD_INFO SdInfo,
IN OUT PDEFAULT_SD_NODE *DefaultSdNode
)
/*++
Routine Description:
This routine will search the SD_INFO list for an existing entry that matches the current
class type. If no such entry is found, one will be created from information from the schema
Arguments:
ClassId - ClassId to find the default SD node for
SdInfo - Current list of default SDs and associated information
DefaultSdNode - Where the locted node is returned
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PWSTR Attributes[] = {
NULL,
NULL
};
LDAPMessage *Message = NULL, *Entry;
PWSTR Filter = NULL, SchemaObjectDn = NULL, DefaultSd = NULL, *DefaultSdList = NULL;
PDEFAULT_SD_NODE Node;
*DefaultSdNode = NULL;
Node = SdInfo->SdList;
while ( Node ) {
if ( !_wcsicmp( Node->ObjectClass, ClassId ) ) {
*DefaultSdNode = Node;
break;
}
Node = Node->Next;
}
//
// If it wasn't found, we'll have to go out and load it out of the Ds.
//
if ( !Node ) {
Filter = (LPWSTR)LocalAlloc( LMEM_FIXED,
sizeof( DSACL_LDAP_DN ) - sizeof( WCHAR ) +
( wcslen( ClassId ) * sizeof( WCHAR ) ) +
sizeof( DSACL_LDAP_DN_CLOSE ) );
if ( !Filter ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
goto FindDefaultExit;
}
swprintf( Filter,
L"%ws%ws%ws",
DSACL_LDAP_DN,
ClassId,
DSACL_LDAP_DN_CLOSE );
//
// Now, do the search
//
Win32Err = LdapMapErrorToWin32( ldap_search_s( SdInfo->Ldap,
SdInfo->SchemaPath,
LDAP_SCOPE_SUBTREE,
Filter,
Attributes,
0,
&Message ) );
if ( Win32Err != ERROR_SUCCESS ) {
goto FindDefaultExit;
}
Entry = ldap_first_entry( SdInfo->Ldap, Message );
if ( Entry ) {
SchemaObjectDn = ldap_get_dn( SdInfo->Ldap, Entry );
ldap_msgfree( Message );
if ( !SchemaObjectDn ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
goto FindDefaultExit;
}
} else {
Win32Err = LdapMapErrorToWin32( SdInfo->Ldap->ld_errno );
goto FindDefaultExit;
}
//
// Ok, now we can read the default security descriptor
//
Attributes[ 0 ] = DSACL_DEFAULT_SD;
Win32Err = LdapMapErrorToWin32( ldap_search_s( SdInfo->Ldap,
SchemaObjectDn,
LDAP_SCOPE_BASE,
DSACL_ALL_FILTER,
Attributes,
0,
&Message ) );
Entry = ldap_first_entry( SdInfo->Ldap, Message );
if ( Entry ) {
//
// Now, we'll have to get the values
//
DefaultSdList = ldap_get_values( SdInfo->Ldap, Entry, Attributes[ 0 ] );
if ( DefaultSdList ) {
DefaultSd = DefaultSdList[ 0 ];
} else {
Win32Err = LdapMapErrorToWin32( SdInfo->Ldap->ld_errno );
goto FindDefaultExit;
}
ldap_msgfree( Message );
}
//
// Find a new node and insert it
//
Node = (DEFAULT_SD_NODE*)LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
sizeof( DEFAULT_SD_NODE ) );
if ( !Node ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
goto FindDefaultExit;
}
if ( !ConvertStringSDToSDRootDomain( SdInfo->DomainSid,
DefaultSd,
SDDL_REVISION,
&Node->DefaultSd,
NULL ) ) {
Win32Err = GetLastError();
}
if ( Win32Err == ERROR_SUCCESS ) {
Node->ObjectClass =(LPWSTR) LocalAlloc( LMEM_FIXED,
( wcslen( ClassId ) + 1 ) * sizeof( WCHAR ) );
if ( Node->ObjectClass == NULL ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
} else {
wcscpy( Node->ObjectClass, ClassId );
Node->Next = SdInfo->SdList;
SdInfo->SdList = Node;
}
}
if ( Win32Err != ERROR_SUCCESS ) {
LocalFree( Node->DefaultSd );
LocalFree( Node->ObjectClass );
LocalFree( Node );
} else {
*DefaultSdNode = Node;
}
}
FindDefaultExit:
LocalFree( Filter );
if ( SchemaObjectDn ) {
ldap_memfree( SchemaObjectDn );
}
if ( DefaultSdList ) {
ldap_value_free( DefaultSdList );
}
return( Win32Err );
}
DWORD
SetDefaultSdForObject(
IN LDAP *Ldap,
IN PWSTR ObjectPath,
IN PDEFAULT_SD_INFO SdInfo,
IN SECURITY_INFORMATION Protection
)
/*++
Routine Description:
This routine set the default security descriptor on the indicated object
Arguments:
Ldap - Ldap connect to the server holding the object
ObjectPath - 1779 style path to the object
SdInfo - Current list of default SDs and associated information
Returns:
ERROR_SUCCESS - Success
ERROR_DS_NAME_TYPE_UNKNOWN - Unable to determine the class id of the object
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PWSTR Attributes[] = {
DSACL_OBJECT_CLASS,
NULL
};
LDAPMessage *Message = NULL, *Entry;
PWSTR ClassId = NULL;
PWSTR *ClassList = NULL;
ULONG i;
PDEFAULT_SD_NODE DefaultSdNode = NULL;
PACTRL_ACCESS NewAccess = NULL;
PACTRL_AUDIT NewAudit = NULL;
//
// First, get the class id off of the object
//
Win32Err = LdapMapErrorToWin32( ldap_search_s( Ldap,
ObjectPath,
LDAP_SCOPE_BASE,
DSACL_ALL_FILTER,
Attributes,
0,
&Message ) );
if ( Win32Err != ERROR_SUCCESS ) {
goto SetDefaultExit;
}
Entry = ldap_first_entry( Ldap, Message );
if ( Entry ) {
//
// Now, we'll have to get the values
//
ClassList = ldap_get_values( Ldap, Entry, Attributes[ 0 ] );
if ( ClassList ) {
//
// Get the class id
//
i = 0;
while ( TRUE ) {
if ( ClassList[ i ] ) {
i++;
} else {
break;
}
}
// ASSERT( i > 0 );
if ( i == 0 ) {
Win32Err = ERROR_DS_NAME_TYPE_UNKNOWN;
goto SetDefaultExit;
}
ClassId = ClassList[ i - 1 ];
} else {
Win32Err = LdapMapErrorToWin32( Ldap->ld_errno );
goto SetDefaultExit;
}
ldap_msgfree( Message );
Message = NULL;
}
if ( !ClassId ) {
Win32Err = ERROR_DS_NAME_TYPE_UNKNOWN;
goto SetDefaultExit;
}
//
// Now, see if we have a cache entry for that...
//
Win32Err = FindDefaultSdForClass( ClassId,
SdInfo,
&DefaultSdNode );
if ( Win32Err != ERROR_SUCCESS ) {
goto SetDefaultExit;
}
//
// Ok, we have everything we need, so let's go ahead and set it all
//
/* Win32Err = ConvertSecurityDescriptorToAccessNamed( ObjectPath,
SE_DS_OBJECT_ALL,
DefaultSdNode->DefaultSd,
&NewAccess,
&NewAudit,
NULL,
NULL );
*/
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = WriteObjectSecurity(ObjectPath,
DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | Protection,
DefaultSdNode->DefaultSd
);
}
if ( Win32Err == ERROR_SUCCESS ) {
DisplayMessageEx( 0, MSG_DSACLS_PROCESSED, ObjectPath );
}
SetDefaultExit:
if ( ClassList ) {
ldap_value_free( ClassList );
}
if ( Message ) {
ldap_msgfree( Message );
}
LocalFree( NewAccess );
LocalFree( NewAudit );
return( Win32Err );
}
DWORD
SetDefaultSdForObjectAndChildren(
IN LDAP *Ldap,
IN PWSTR ObjectPath,
IN PDEFAULT_SD_INFO SdInfo,
IN BOOLEAN Propagate,
IN SECURITY_INFORMATION Protection
)
/*++
Routine Description:
This routine will set the security descriptor on the object and potentially all of its
children to the default security as obtained from the schema
Arguments:
Ldap - Ldap connect to the server holding the object
ObjectPath - 1779 style path to the object
SdInfo - Current list of default SDs and associated information
Propagate - If TRUE, reset the security on the children as well
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PWSTR Attributes[] = {
NULL
};
LDAPMessage *Message = NULL, *Entry;
PWSTR ChildName = NULL;
PLDAPSearch SearchHandle = NULL;
ULONG Count;
//
// First, get the class id off of the object
//
SearchHandle = ldap_search_init_pageW( Ldap,
ObjectPath,
Propagate ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_BASE,
DSACL_ALL_FILTER,
Attributes,
FALSE,
NULL,
NULL,
0,
2000,
NULL );
if ( SearchHandle == NULL ) {
Win32Err = LdapMapErrorToWin32( LdapGetLastError( ) );
} else {
while ( Win32Err == ERROR_SUCCESS ) {
Count = 0;
//
// Get the next page
//
Win32Err = ldap_get_next_page_s( Ldap,
SearchHandle,
NULL,
100,
&Count,
&Message );
if ( Message ) {
Entry = ldap_first_entry( Ldap, Message );
while ( Entry ) {
ChildName = ldap_get_dn( SdInfo->Ldap, Entry );
if ( !ChildName ) {
Win32Err = ERROR_NOT_ENOUGH_MEMORY;
break;
}
Win32Err = SetDefaultSdForObject( Ldap,
ChildName,
SdInfo,
Protection);
ldap_memfree( ChildName );
if ( Win32Err != ERROR_SUCCESS ) {
break;
}
Entry = ldap_next_entry( Ldap, Entry );
}
Win32Err = Ldap->ld_errno;
ldap_msgfree( Message );
Message = NULL;
}
if ( Win32Err == LDAP_NO_RESULTS_RETURNED ) {
Win32Err = ERROR_SUCCESS;
break;
}
}
ldap_search_abandon_page( Ldap,
SearchHandle );
}
return( Win32Err );
}
DWORD
BindToDsObject(
IN PWSTR ObjectPath,
OUT PLDAP *Ldap,
OUT PSID *DomainSid OPTIONAL
)
/*++
Routine Description:
This routine will bind to the ldap server on a domain controller that holds the specified
object path. Optionally, the sid of the domain hosted by that domain controller is returned
Arguments:
ObjectPath - 1779 style path to the object
Ldap - Where the ldap connection handle is returned
DomainSid - Sid of the domain hosted by the domain controller.
Returns:
ERROR_SUCCESS - Success
ERROR_PATH_NOT_FOUND - A domain controller for this path could not be located
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PWSTR ServerName = NULL;
PWSTR Separator = NULL;
PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
PWSTR Path = NULL;
HANDLE DsHandle = NULL;
PDS_NAME_RESULT NameRes = NULL;
BOOLEAN NamedServer = FALSE;
UNICODE_STRING ServerNameU;
OBJECT_ATTRIBUTES ObjectAttributes;
LSA_HANDLE LsaHandle;
PPOLICY_PRIMARY_DOMAIN_INFO PolicyPDI = NULL;
NTSTATUS Status;
//
// Get a server name
//
/* if ( wcslen( ObjectPath ) > 2 && *ObjectPath == L'\\' && *( ObjectPath + 1 ) == L'\\' ) {
Separator = wcschr( ObjectPath + 2, L'\\' );
if ( Separator ) {
*Separator = L'\0';
Path = Separator + 1;
}
ServerName = ObjectPath + 2;
NamedServer = TRUE;
} else {
Path = ObjectPath;
Win32Err = DsGetDcName( NULL,
NULL,
NULL,
NULL,
DS_IP_REQUIRED |
DS_DIRECTORY_SERVICE_REQUIRED,
&DcInfo );
if ( Win32Err == ERROR_SUCCESS ) {
ServerName = DcInfo[ 0 ].DomainControllerName + 2;
}
}
//
// Do the bind and crack
//
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = DsBind( ServerName,
NULL,
&DsHandle );
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = DsCrackNames( DsHandle,
DS_NAME_NO_FLAGS,
DS_FQDN_1779_NAME,
DS_FQDN_1779_NAME,
1,
&Path,
&NameRes );
if ( Win32Err == ERROR_SUCCESS ) {
if ( NameRes->cItems != 0 && !NamedServer &&
NameRes->rItems[ 0 ].status == DS_NAME_ERROR_DOMAIN_ONLY ) {
NetApiBufferFree( DcInfo );
DcInfo = NULL;
Win32Err = DsGetDcNameW( NULL,
NameRes->rItems[ 0 ].pDomain,
NULL,
NULL,
DS_IP_REQUIRED |
DS_DIRECTORY_SERVICE_REQUIRED,
&DcInfo );
if ( Win32Err == ERROR_SUCCESS ) {
DsUnBindW( &DsHandle );
DsHandle = NULL;
ServerName = DcInfo->DomainControllerName + 2;
//Win32Err = DsBind( DcInfo->DomainControllerAddress,
// NULL,
// &DsHandle );
//
Win32Err = DsBind( ServerName,
NULL,
&DsHandle );
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = DsCrackNames( DsHandle,
DS_NAME_NO_FLAGS,
DS_FQDN_1779_NAME,
DS_FQDN_1779_NAME,
1,
&Path,
&NameRes);
}
}
}
}
}
}
*/
//
// Now, do the bind
//
*Ldap = ldap_open( g_szServerName,
LDAP_PORT );
if ( *Ldap == NULL ) {
Win32Err = ERROR_PATH_NOT_FOUND;
} else {
Win32Err = LdapMapErrorToWin32( ldap_bind_s( *Ldap,
NULL,
NULL,
LDAP_AUTH_SSPI ) );
}
//
// If specified, get the sid for the domain
//
if ( DomainSid ) {
RtlInitUnicodeString( &ServerNameU, g_szServerName );
InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
//
// Get the sid of the domain
//
Status = LsaOpenPolicy( &ServerNameU,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&LsaHandle );
if ( NT_SUCCESS( Status ) ) {
Status = LsaQueryInformationPolicy( LsaHandle,
PolicyPrimaryDomainInformation,
( PVOID * )&PolicyPDI );
if ( NT_SUCCESS( Status ) ) {
*DomainSid = (PSID)LocalAlloc( LMEM_FIXED,
RtlLengthSid( PolicyPDI->Sid ) );
if ( *DomainSid == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlCopySid( RtlLengthSid( PolicyPDI->Sid ), *DomainSid, PolicyPDI->Sid );
}
LsaFreeMemory( PolicyPDI );
}
LsaClose( LsaHandle );
}
if ( !NT_SUCCESS( Status ) ) {
Win32Err = RtlNtStatusToDosError( Status );
ldap_unbind( *Ldap );
*Ldap = NULL;
}
}
return( Win32Err );
}
DWORD
SetDefaultSecurityOnObjectTree(
IN PWSTR ObjectPath,
IN BOOLEAN Propagate,
IN SECURITY_INFORMATION Protection
)
/*++
Routine Description:
This routine will set the security descriptor on the object and potentially all of its
children to the default security as obtained from the schema
Arguments:
ObjectPath - 1779 style path to the object
Propagate - If TRUE, reset the security on the children as well
Returns:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
PWSTR Attributes[] = {
DSACL_SCHEMA_NC,
NULL
};
LDAPMessage *Message, *Entry;
PWSTR *PathList = NULL;
DEFAULT_SD_INFO SdInfo = {
NULL,
NULL,
NULL,
NULL
};
PDEFAULT_SD_NODE CleanupNode;
//
// Bind to the ds object
//
Win32Err = BindToDsObject( ObjectPath,
&SdInfo.Ldap,
&SdInfo.DomainSid );
if ( Win32Err != ERROR_SUCCESS ) {
goto SetDefaultExit;
}
//
// Get the schema path
//
Win32Err = LdapMapErrorToWin32( ldap_search_s( SdInfo.Ldap,
NULL,
LDAP_SCOPE_BASE,
DSACL_ALL_FILTER,
Attributes,
0,
&Message ) );
if ( Win32Err == ERROR_SUCCESS ) {
Entry = ldap_first_entry( SdInfo.Ldap, Message );
if ( Entry ) {
//
// Now, we'll have to get the values
//
PathList = ldap_get_values( SdInfo.Ldap, Entry, Attributes[ 0 ] );
if ( PathList ) {
SdInfo.SchemaPath = PathList[ 0 ];
} else {
Win32Err = LdapMapErrorToWin32( SdInfo.Ldap->ld_errno );
}
ldap_msgfree( Message );
}
}
if( SdInfo.Ldap )
{
Win32Err = SetDefaultSdForObjectAndChildren( SdInfo.Ldap,
ObjectPath,
&SdInfo,
Propagate,
Protection);
}
SetDefaultExit:
//
// Unbind from the DS
//
if ( SdInfo.Ldap ) {
ldap_unbind( SdInfo.Ldap );
}
if ( PathList ) {
ldap_value_free( PathList );
}
//
// Clean up the Default SD Info list
//
LocalFree( SdInfo.DomainSid );
while ( SdInfo.SdList ) {
CleanupNode = SdInfo.SdList;
LocalFree( CleanupNode->ObjectClass );
LocalFree( CleanupNode->DefaultSd );
SdInfo.SdList = SdInfo.SdList->Next;
LocalFree( CleanupNode );
}
return( Win32Err );
}