2010-08-26 08:35:30 +04:00
# DRS utility code
#
# Copyright Andrew Tridgell 2010
# Copyright Andrew Bartlett 2010
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from samba . dcerpc import drsuapi , misc
from samba . net import Net
import samba , ldb
2010-09-29 07:46:15 +04:00
2011-12-08 23:23:57 +04:00
class drsException ( Exception ) :
2011-11-28 23:48:59 +04:00
""" Base element for drs errors """
def __init__ ( self , value ) :
self . value = value
def __str__ ( self ) :
return " drsException: " + self . value
def drsuapi_connect ( server , lp , creds ) :
2011-12-08 23:23:57 +04:00
""" Make a DRSUAPI connection to the server.
2011-11-28 23:48:59 +04:00
: param server : the name of the server to connect to
: param lp : a samba line parameter object
: param creds : credential used for the connection
: return : A tuple with the drsuapi bind object , the drsuapi handle
and the supported extensions .
: raise drsException : if the connection fails
"""
binding_options = " seal "
if int ( lp . get ( " log level " ) ) > = 5 :
binding_options + = " ,print "
binding_string = " ncacn_ip_tcp: %s [ %s ] " % ( server , binding_options )
try :
drsuapiBind = drsuapi . drsuapi ( binding_string , lp , creds )
( drsuapiHandle , bindSupportedExtensions ) = drs_DsBind ( drsuapiBind )
except Exception , e :
2011-12-05 02:00:57 +04:00
raise drsException ( " DRS connection to %s failed: %s " % ( server , e ) )
2011-11-28 23:48:59 +04:00
return ( drsuapiBind , drsuapiHandle , bindSupportedExtensions )
2011-12-08 23:23:57 +04:00
def sendDsReplicaSync ( drsuapiBind , drsuapi_handle , source_dsa_guid ,
naming_context , req_option ) :
""" Send DS replica sync request.
2011-11-28 23:48:59 +04:00
: param drsuapiBind : a drsuapi Bind object
: param drsuapi_handle : a drsuapi hanle on the drsuapi connection
: param source_dsa_guid : the guid of the source dsa for the replication
: param naming_context : the DN of the naming context to replicate
: param req_options : replication options for the DsReplicaSync call
2011-12-08 23:23:57 +04:00
: raise drsException : if any error occur while sending and receiving the
reply for the dsReplicaSync
2011-11-28 23:48:59 +04:00
"""
nc = drsuapi . DsReplicaObjectIdentifier ( )
nc . dn = naming_context
req1 = drsuapi . DsReplicaSyncRequest1 ( )
req1 . naming_context = nc ;
req1 . options = req_option
req1 . source_dsa_guid = misc . GUID ( source_dsa_guid )
try :
drsuapiBind . DsReplicaSync ( drsuapi_handle , 1 , req1 )
except Exception , estr :
raise drsException ( " DsReplicaSync failed %s " % estr )
2011-12-08 23:23:57 +04:00
2011-12-05 02:00:57 +04:00
def sendRemoveDsServer ( drsuapiBind , drsuapi_handle , server_dsa_dn , domain ) :
2011-12-08 23:23:57 +04:00
""" Send RemoveDSServer request.
2011-12-05 02:00:57 +04:00
: param drsuapiBind : a drsuapi Bind object
: param drsuapi_handle : a drsuapi hanle on the drsuapi connection
2011-12-08 23:23:57 +04:00
: param server_dsa_dn : a DN object of the server ' s dsa that we want to
demote
2011-12-05 02:00:57 +04:00
: param domain : a DN object of the server ' s domain
2011-12-08 23:23:57 +04:00
: raise drsException : if any error occur while sending and receiving the
reply for the DsRemoveDSServer
2011-12-05 02:00:57 +04:00
"""
try :
req1 = drsuapi . DsRemoveDSServerRequest1 ( )
req1 . server_dn = str ( server_dsa_dn )
req1 . domain_dn = str ( domain )
req1 . commit = 1
drsuapiBind . DsRemoveDSServer ( drsuapi_handle , 1 , req1 )
except Exception , estr :
raise drsException ( " DsRemoveDSServer failed %s " % estr )
2011-12-08 23:23:57 +04:00
2010-09-29 07:46:15 +04:00
def drs_DsBind ( drs ) :
''' make a DsBind call, returning the binding handle '''
bind_info = drsuapi . DsBindInfoCtr ( )
bind_info . length = 28
bind_info . info = drsuapi . DsBindInfo28 ( )
2010-11-28 06:22:46 +03:00
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_BASE
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
bind_info . info . supported_extensions | = drsuapi . DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
2010-09-29 07:46:15 +04:00
( info , handle ) = drs . DsBind ( misc . GUID ( drsuapi . DRSUAPI_DS_BIND_GUID ) , bind_info )
2010-11-07 05:55:20 +03:00
return ( handle , info . info . supported_extensions )
2010-09-29 07:46:15 +04:00
2010-11-28 06:22:46 +03:00
2011-12-08 23:23:57 +04:00
class drs_Replicate ( object ) :
2010-08-26 08:35:30 +04:00
''' DRS replication calls '''
def __init__ ( self , binding_string , lp , creds , samdb ) :
self . drs = drsuapi . drsuapi ( binding_string , lp , creds )
2010-11-07 05:55:20 +03:00
( self . drs_handle , self . supported_extensions ) = drs_DsBind ( self . drs )
2010-08-26 08:35:30 +04:00
self . net = Net ( creds = creds , lp = lp )
self . samdb = samdb
self . replication_state = self . net . replicate_init ( self . samdb , lp , self . drs )
def drs_get_rodc_partial_attribute_set ( self ) :
''' get a list of attributes for RODC replication '''
partial_attribute_set = drsuapi . DsPartialAttributeSet ( )
partial_attribute_set . version = 1
attids = [ ]
# the exact list of attids we send is quite critical. Note that
2010-11-18 10:47:21 +03:00
# we do ask for the secret attributes, but set SPECIAL_SECRET_PROCESSING
2010-08-26 08:35:30 +04:00
# to zero them out
schema_dn = self . samdb . get_schema_basedn ( )
res = self . samdb . search ( base = schema_dn , scope = ldb . SCOPE_SUBTREE ,
expression = " objectClass=attributeSchema " ,
attrs = [ " lDAPDisplayName " , " systemFlags " ,
" searchFlags " ] )
for r in res :
ldap_display_name = r [ " lDAPDisplayName " ] [ 0 ]
if " systemFlags " in r :
system_flags = r [ " systemFlags " ] [ 0 ]
if ( int ( system_flags ) & ( samba . dsdb . DS_FLAG_ATTR_NOT_REPLICATED |
samba . dsdb . DS_FLAG_ATTR_IS_CONSTRUCTED ) ) :
continue
2010-09-09 15:38:01 +04:00
if " searchFlags " in r :
search_flags = r [ " searchFlags " ] [ 0 ]
if ( int ( search_flags ) & samba . dsdb . SEARCH_FLAG_RODC_ATTRIBUTE ) :
continue
2010-08-26 08:35:30 +04:00
attid = self . samdb . get_attid_from_lDAPDisplayName ( ldap_display_name )
attids . append ( int ( attid ) )
# the attids do need to be sorted, or windows doesn't return
# all the attributes we need
attids . sort ( )
partial_attribute_set . attids = attids
partial_attribute_set . num_attids = len ( attids )
return partial_attribute_set
def replicate ( self , dn , source_dsa_invocation_id , destination_dsa_guid ,
2010-11-07 05:53:13 +03:00
schema = False , exop = drsuapi . DRSUAPI_EXOP_NONE , rodc = False ,
replica_flags = None ) :
2010-08-26 08:35:30 +04:00
''' replicate a single DN '''
# setup for a GetNCChanges call
req8 = drsuapi . DsGetNCChangesRequest8 ( )
req8 . destination_dsa_guid = destination_dsa_guid
2010-11-28 06:22:46 +03:00
req8 . source_dsa_invocation_id = source_dsa_invocation_id
req8 . naming_context = drsuapi . DsReplicaObjectIdentifier ( )
2010-09-23 09:35:28 +04:00
req8 . naming_context . dn = dn
2010-08-26 08:35:30 +04:00
req8 . highwatermark = drsuapi . DsReplicaHighWaterMark ( )
req8 . highwatermark . tmp_highest_usn = 0
2010-11-28 06:22:46 +03:00
req8 . highwatermark . reserved_usn = 0
req8 . highwatermark . highest_usn = 0
req8 . uptodateness_vector = None
2010-11-07 05:53:13 +03:00
if replica_flags is not None :
req8 . replica_flags = replica_flags
elif exop == drsuapi . DRSUAPI_EXOP_REPL_SECRET :
2010-11-28 06:22:46 +03:00
req8 . replica_flags = 0
2010-08-26 08:35:30 +04:00
else :
2010-11-28 06:22:46 +03:00
req8 . replica_flags = ( drsuapi . DRSUAPI_DRS_INIT_SYNC |
drsuapi . DRSUAPI_DRS_PER_SYNC |
drsuapi . DRSUAPI_DRS_GET_ANC |
drsuapi . DRSUAPI_DRS_NEVER_SYNCED )
2010-11-05 06:08:49 +03:00
if rodc :
req8 . replica_flags | = drsuapi . DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
2010-11-07 05:53:13 +03:00
else :
req8 . replica_flags | = drsuapi . DRSUAPI_DRS_WRIT_REP
2011-09-13 02:19:24 +04:00
req8 . max_object_count = 402
req8 . max_ndr_size = 402116
req8 . extended_op = exop
req8 . fsmo_info = 0
req8 . partial_attribute_set = None
req8 . partial_attribute_set_ex = None
req8 . mapping_ctr . num_mappings = 0
req8 . mapping_ctr . mappings = None
2010-08-26 08:35:30 +04:00
2010-11-05 06:08:49 +03:00
if not schema and rodc :
2010-08-26 08:35:30 +04:00
req8 . partial_attribute_set = self . drs_get_rodc_partial_attribute_set ( )
2010-11-07 05:55:20 +03:00
if self . supported_extensions & drsuapi . DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8 :
req_level = 8
req = req8
else :
req_level = 5
req5 = drsuapi . DsGetNCChangesRequest5 ( )
for a in dir ( req5 ) :
if a [ 0 ] != ' _ ' :
setattr ( req5 , a , getattr ( req8 , a ) )
req = req5
2010-08-26 08:35:30 +04:00
while True :
2010-11-07 05:55:20 +03:00
( level , ctr ) = self . drs . DsGetNCChanges ( self . drs_handle , req_level , req )
2012-09-27 20:30:47 +04:00
if ctr . first_object is None and ctr . object_count != 0 :
2010-11-08 02:14:50 +03:00
raise RuntimeError ( " DsGetNCChanges: NULL first_object with object_count= %u " % ( ctr . object_count ) )
2011-08-08 12:47:12 +04:00
self . net . replicate_chunk ( self . replication_state , level , ctr ,
2011-09-13 02:19:24 +04:00
schema = schema , req_level = req_level , req = req )
2010-08-26 08:35:30 +04:00
if ctr . more_data == 0 :
break
2012-12-18 17:46:23 +04:00
req . highwatermark = ctr . new_highwatermark