/*++ 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 ); }