2010-08-23 05:31:48 +04:00
# python join 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/>.
#
2010-11-28 16:09:30 +03:00
""" Joining a domain. """
2010-08-23 05:31:48 +04:00
from samba . auth import system_session
from samba . samdb import SamDB
2016-01-28 16:00:38 +03:00
from samba import gensec , Ldb , drs_utils , arcfour_encrypt , string_to_byte_array
2011-11-04 20:34:47 +04:00
import ldb , samba , sys , uuid
2017-02-17 08:23:23 +03:00
from samba . ndr import ndr_pack , ndr_unpack
from samba . dcerpc import security , drsuapi , misc , nbt , lsa , drsblobs , dnsserver , dnsp
2013-09-09 03:54:23 +04:00
from samba . dsdb import DS_DOMAIN_FUNCTION_2003
2010-08-23 05:31:48 +04:00
from samba . credentials import Credentials , DONT_USE_KERBEROS
2011-08-24 09:39:51 +04:00
from samba . provision import secretsdb_self_join , provision , provision_fill , FILL_DRS , FILL_SUBDOMAIN
2012-12-24 01:56:50 +04:00
from samba . provision . common import setup_path
2010-11-05 06:09:49 +03:00
from samba . schema import Schema
2014-08-22 06:16:30 +04:00
from samba import descriptor
2010-08-24 09:42:54 +04:00
from samba . net import Net
2012-06-24 15:10:34 +04:00
from samba . provision . sambadns import setup_bind9_dns
2012-12-24 01:56:50 +04:00
from samba import read_and_sub_file
2017-02-13 01:12:54 +03:00
from samba import werror
2012-12-24 01:56:50 +04:00
from base64 import b64encode
2017-02-17 08:23:23 +03:00
from samba import WERRORError
from samba . dnsserver import ARecord , AAAARecord , PTRRecord , CNameRecord , NSRecord , MXRecord , SOARecord , SRVRecord , TXTRecord
from samba import sd_utils
2010-08-24 09:42:54 +04:00
import logging
2010-11-05 05:00:45 +03:00
import talloc
2011-08-26 07:23:41 +04:00
import random
2011-09-07 11:21:07 +04:00
import time
2010-08-23 05:31:48 +04:00
2011-05-13 16:04:07 +04:00
class DCJoinException ( Exception ) :
def __init__ ( self , msg ) :
super ( DCJoinException , self ) . __init__ ( " Can ' t join, error: %s " % msg )
2011-01-06 16:39:40 +03:00
class dc_join ( object ) :
2012-09-16 16:18:51 +04:00
""" Perform a DC join. """
2010-11-05 06:09:49 +03:00
2013-09-09 01:53:37 +04:00
def __init__ ( ctx , logger = None , server = None , creds = None , lp = None , site = None ,
2012-07-06 09:38:06 +04:00
netbios_name = None , targetdir = None , domain = None ,
machinepass = None , use_ntvfs = False , dns_backend = None ,
2017-12-14 21:24:14 +03:00
promote_existing = False , clone_only = False ,
plaintext_secrets = False ) :
2017-06-06 06:21:50 +03:00
if site is None :
site = " Default-First-Site-Name "
2015-08-17 06:33:31 +03:00
ctx . clone_only = clone_only
2013-09-09 01:53:37 +04:00
ctx . logger = logger
2010-11-05 06:09:49 +03:00
ctx . creds = creds
ctx . lp = lp
ctx . site = site
ctx . targetdir = targetdir
2012-06-05 02:32:42 +04:00
ctx . use_ntvfs = use_ntvfs
2017-12-14 21:24:14 +03:00
ctx . plaintext_secrets = plaintext_secrets
2012-06-21 17:46:21 +04:00
2012-07-06 09:38:06 +04:00
ctx . promote_existing = promote_existing
ctx . promote_from_dn = None
2012-06-21 17:46:21 +04:00
ctx . nc_list = [ ]
2013-10-16 05:34:43 +04:00
ctx . full_nc_list = [ ]
2010-11-05 06:09:49 +03:00
ctx . creds . set_gensec_features ( creds . get_gensec_features ( ) | gensec . FEATURE_SEAL )
ctx . net = Net ( creds = ctx . creds , lp = ctx . lp )
if server is not None :
ctx . server = server
else :
2013-09-09 01:53:37 +04:00
ctx . logger . info ( " Finding a writeable DC for domain ' %s ' " % domain )
2010-11-05 06:09:49 +03:00
ctx . server = ctx . find_dc ( domain )
2013-09-09 01:53:37 +04:00
ctx . logger . info ( " Found DC %s " % ctx . server )
2010-11-05 06:09:49 +03:00
ctx . samdb = SamDB ( url = " ldap:// %s " % ctx . server ,
session_info = system_session ( ) ,
credentials = ctx . creds , lp = ctx . lp )
2011-05-13 16:04:07 +04:00
try :
ctx . samdb . search ( scope = ldb . SCOPE_ONELEVEL , attrs = [ " dn " ] )
except ldb . LdbError , ( enum , estr ) :
raise DCJoinException ( estr )
2010-11-05 06:09:49 +03:00
ctx . base_dn = str ( ctx . samdb . get_default_basedn ( ) )
ctx . root_dn = str ( ctx . samdb . get_root_basedn ( ) )
ctx . schema_dn = str ( ctx . samdb . get_schema_basedn ( ) )
ctx . config_dn = str ( ctx . samdb . get_config_basedn ( ) )
2014-08-11 03:23:57 +04:00
ctx . domsid = security . dom_sid ( ctx . samdb . get_domain_sid ( ) )
2014-08-08 10:43:47 +04:00
ctx . forestsid = ctx . domsid
2010-11-05 06:09:49 +03:00
ctx . domain_name = ctx . get_domain_name ( )
2011-10-01 04:58:52 +04:00
ctx . forest_domain_name = ctx . get_forest_domain_name ( )
2011-09-02 09:12:11 +04:00
ctx . invocation_id = misc . GUID ( str ( uuid . uuid4 ( ) ) )
2010-11-05 06:09:49 +03:00
2011-12-04 17:23:34 +04:00
ctx . dc_ntds_dn = ctx . samdb . get_dsServiceName ( )
2010-11-05 06:09:49 +03:00
ctx . dc_dnsHostName = ctx . get_dnsHostName ( )
2010-11-07 05:55:20 +03:00
ctx . behavior_version = ctx . get_behavior_version ( )
2011-11-28 23:03:11 +04:00
if machinepass is not None :
ctx . acct_pass = machinepass
else :
2016-08-23 13:37:37 +03:00
ctx . acct_pass = samba . generate_random_machine_password ( 128 , 255 )
2010-11-05 06:09:49 +03:00
2015-08-17 06:33:31 +03:00
ctx . dnsdomain = ctx . samdb . domain_dns_name ( )
if clone_only :
# As we don't want to create or delete these DNs, we set them to None
ctx . server_dn = None
ctx . ntds_dn = None
ctx . acct_dn = None
ctx . myname = ctx . server . split ( ' . ' ) [ 0 ]
ctx . ntds_guid = None
2016-10-31 06:48:33 +03:00
ctx . rid_manager_dn = None
2016-03-27 07:29:35 +03:00
# Save this early
ctx . remote_dc_ntds_guid = ctx . samdb . get_ntds_GUID ( )
2010-11-07 05:55:20 +03:00
else :
2015-08-17 06:33:31 +03:00
# work out the DNs of all the objects we will be adding
ctx . myname = netbios_name
ctx . samname = " %s $ " % ctx . myname
ctx . server_dn = " CN= %s ,CN=Servers,CN= %s ,CN=Sites, %s " % ( ctx . myname , ctx . site , ctx . config_dn )
ctx . ntds_dn = " CN=NTDS Settings, %s " % ctx . server_dn
ctx . acct_dn = " CN= %s ,OU=Domain Controllers, %s " % ( ctx . myname , ctx . base_dn )
ctx . dnshostname = " %s . %s " % ( ctx . myname . lower ( ) , ctx . dnsdomain )
ctx . dnsforest = ctx . samdb . forest_dns_name ( )
topology_base = " CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System, %s " % ctx . base_dn
if ctx . dn_exists ( topology_base ) :
ctx . topology_dn = " CN= %s , %s " % ( ctx . myname , topology_base )
else :
ctx . topology_dn = None
ctx . SPNs = [ " HOST/ %s " % ctx . myname ,
" HOST/ %s " % ctx . dnshostname ,
" GC/ %s / %s " % ( ctx . dnshostname , ctx . dnsforest ) ]
2010-11-05 06:09:49 +03:00
2016-10-31 06:48:33 +03:00
res_rid_manager = ctx . samdb . search ( scope = ldb . SCOPE_BASE ,
attrs = [ " rIDManagerReference " ] ,
base = ctx . base_dn )
ctx . rid_manager_dn = res_rid_manager [ 0 ] [ " rIDManagerReference " ] [ 0 ]
2012-09-06 04:37:18 +04:00
ctx . domaindns_zone = ' DC=DomainDnsZones, %s ' % ctx . base_dn
2013-10-11 00:36:53 +04:00
ctx . forestdns_zone = ' DC=ForestDnsZones, %s ' % ctx . root_dn
2012-09-06 04:37:18 +04:00
2016-07-20 04:37:47 +03:00
expr = " (&(objectClass=crossRef)(ncName= %s )) " % ldb . binary_encode ( ctx . domaindns_zone )
2012-09-06 04:37:18 +04:00
res_domaindns = ctx . samdb . search ( scope = ldb . SCOPE_ONELEVEL ,
attrs = [ ] ,
base = ctx . samdb . get_partitions_dn ( ) ,
2016-07-20 04:37:47 +03:00
expression = expr )
2012-09-06 04:37:18 +04:00
if dns_backend is None :
ctx . dns_backend = " NONE "
else :
if len ( res_domaindns ) == 0 :
ctx . dns_backend = " NONE "
print " NO DNS zone information found in source domain, not replicating DNS "
else :
ctx . dns_backend = dns_backend
2010-11-05 06:09:49 +03:00
ctx . realm = ctx . dnsdomain
2010-11-07 05:55:20 +03:00
2010-11-05 06:09:49 +03:00
ctx . tmp_samdb = None
2016-11-29 16:27:57 +03:00
ctx . replica_flags = ( drsuapi . DRSUAPI_DRS_INIT_SYNC |
drsuapi . DRSUAPI_DRS_PER_SYNC |
drsuapi . DRSUAPI_DRS_GET_ANC |
2016-11-29 16:29:59 +03:00
drsuapi . DRSUAPI_DRS_GET_NC_SIZE |
2016-11-29 16:27:57 +03:00
drsuapi . DRSUAPI_DRS_NEVER_SYNCED )
2010-11-05 06:09:49 +03:00
# these elements are optional
ctx . never_reveal_sid = None
ctx . reveal_sid = None
ctx . connection_dn = None
ctx . RODC = False
ctx . krbtgt_dn = None
ctx . drsuapi = None
ctx . managedby = None
2011-09-05 10:43:26 +04:00
ctx . subdomain = False
2013-09-06 07:46:05 +04:00
ctx . adminpass = None
2016-05-31 05:54:45 +03:00
ctx . partition_dn = None
2010-11-05 06:09:49 +03:00
2017-02-17 08:23:23 +03:00
ctx . dns_a_dn = None
ctx . dns_cname_dn = None
# Do not normally register 127. addresses but allow override for selftest
ctx . force_all_ips = False
2010-11-05 06:09:49 +03:00
def del_noerror ( ctx , dn , recursive = False ) :
if recursive :
try :
res = ctx . samdb . search ( base = dn , scope = ldb . SCOPE_ONELEVEL , attrs = [ " dn " ] )
2010-11-29 06:10:57 +03:00
except Exception :
2010-11-05 06:09:49 +03:00
return
for r in res :
ctx . del_noerror ( r . dn , recursive = True )
2010-08-23 05:31:48 +04:00
try :
2010-11-05 06:09:49 +03:00
ctx . samdb . delete ( dn )
2010-08-23 05:31:48 +04:00
print " Deleted %s " % dn
2010-11-29 06:10:57 +03:00
except Exception :
2010-08-23 05:31:48 +04:00
pass
2017-06-06 06:22:35 +03:00
def cleanup_old_accounts ( ctx , force = False ) :
2016-05-31 05:54:45 +03:00
res = ctx . samdb . search ( base = ctx . samdb . get_default_basedn ( ) ,
expression = ' sAMAccountName= %s ' % ldb . binary_encode ( ctx . samname ) ,
attrs = [ " msDS-krbTgtLink " , " objectSID " ] )
if len ( res ) == 0 :
return
2017-06-06 06:22:35 +03:00
if not force :
creds = Credentials ( )
creds . guess ( ctx . lp )
try :
creds . set_machine_account ( ctx . lp )
creds . set_kerberos_state ( ctx . creds . get_kerberos_state ( ) )
machine_samdb = SamDB ( url = " ldap:// %s " % ctx . server ,
session_info = system_session ( ) ,
credentials = creds , lp = ctx . lp )
except :
pass
else :
token_res = machine_samdb . search ( scope = ldb . SCOPE_BASE , base = " " , attrs = [ " tokenGroups " ] )
if token_res [ 0 ] [ " tokenGroups " ] [ 0 ] \
== res [ 0 ] [ " objectSID " ] [ 0 ] :
raise DCJoinException ( " Not removing account %s which "
" looks like a Samba DC account "
" maching the password we already have. "
" To override, remove secrets.ldb and secrets.tdb "
% ctx . samname )
2016-05-31 05:54:45 +03:00
ctx . del_noerror ( res [ 0 ] . dn , recursive = True )
if " msDS-Krbtgtlink " in res [ 0 ] :
new_krbtgt_dn = res [ 0 ] [ " msDS-Krbtgtlink " ] [ 0 ]
2017-03-09 04:50:14 +03:00
ctx . del_noerror ( ctx . new_krbtgt_dn )
2016-05-31 05:54:45 +03:00
res = ctx . samdb . search ( base = ctx . samdb . get_default_basedn ( ) ,
expression = ' (&(sAMAccountName= %s )(servicePrincipalName= %s )) ' %
( ldb . binary_encode ( " dns- %s " % ctx . myname ) ,
ldb . binary_encode ( " dns/ %s " % ctx . dnshostname ) ) ,
attrs = [ ] )
if res :
ctx . del_noerror ( res [ 0 ] . dn , recursive = True )
res = ctx . samdb . search ( base = ctx . samdb . get_default_basedn ( ) ,
expression = ' (sAMAccountName= %s ) ' % ldb . binary_encode ( " dns- %s " % ctx . myname ) ,
attrs = [ ] )
if res :
raise DCJoinException ( " Not removing account %s which looks like "
" a Samba DNS service account but does not "
" have servicePrincipalName= %s " %
( ldb . binary_encode ( " dns- %s " % ctx . myname ) ,
ldb . binary_encode ( " dns/ %s " % ctx . dnshostname ) ) )
2017-06-06 06:22:35 +03:00
def cleanup_old_join ( ctx , force = False ) :
2012-09-16 16:18:51 +04:00
""" Remove any DNs from a previous join. """
2016-05-31 05:54:45 +03:00
# find the krbtgt link
if not ctx . subdomain :
2017-06-06 06:22:35 +03:00
ctx . cleanup_old_accounts ( force = force )
2016-05-31 05:54:45 +03:00
if ctx . connection_dn is not None :
ctx . del_noerror ( ctx . connection_dn )
if ctx . krbtgt_dn is not None :
ctx . del_noerror ( ctx . krbtgt_dn )
ctx . del_noerror ( ctx . ntds_dn )
ctx . del_noerror ( ctx . server_dn , recursive = True )
if ctx . topology_dn :
ctx . del_noerror ( ctx . topology_dn )
if ctx . partition_dn :
ctx . del_noerror ( ctx . partition_dn )
if ctx . subdomain :
binding_options = " sign "
lsaconn = lsa . lsarpc ( " ncacn_ip_tcp: %s [ %s ] " % ( ctx . server , binding_options ) ,
ctx . lp , ctx . creds )
objectAttr = lsa . ObjectAttribute ( )
objectAttr . sec_qos = lsa . QosInfo ( )
pol_handle = lsaconn . OpenPolicy2 ( ' ' . decode ( ' utf-8 ' ) ,
objectAttr , security . SEC_FLAG_MAXIMUM_ALLOWED )
name = lsa . String ( )
name . string = ctx . realm
info = lsaconn . QueryTrustedDomainInfoByName ( pol_handle , name , lsa . LSA_TRUSTED_DOMAIN_INFO_FULL_INFO )
lsaconn . DeleteTrustedDomain ( pol_handle , info . info_ex . sid )
name = lsa . String ( )
name . string = ctx . forest_domain_name
info = lsaconn . QueryTrustedDomainInfoByName ( pol_handle , name , lsa . LSA_TRUSTED_DOMAIN_INFO_FULL_INFO )
lsaconn . DeleteTrustedDomain ( pol_handle , info . info_ex . sid )
2011-08-26 07:23:41 +04:00
2017-02-17 08:23:23 +03:00
if ctx . dns_a_dn :
ctx . del_noerror ( ctx . dns_a_dn )
if ctx . dns_cname_dn :
ctx . del_noerror ( ctx . dns_cname_dn )
2010-08-23 05:31:48 +04:00
2012-07-06 09:38:06 +04:00
def promote_possible ( ctx ) :
2012-09-16 16:18:51 +04:00
""" confirm that the account is just a bare NT4 BDC or a member server, so can be safely promoted """
2012-07-06 09:38:06 +04:00
if ctx . subdomain :
# This shouldn't happen
raise Exception ( " Can not promote into a subdomain " )
res = ctx . samdb . search ( base = ctx . samdb . get_default_basedn ( ) ,
expression = ' sAMAccountName= %s ' % ldb . binary_encode ( ctx . samname ) ,
attrs = [ " msDS-krbTgtLink " , " userAccountControl " , " serverReferenceBL " , " rIDSetReferences " ] )
if len ( res ) == 0 :
raise Exception ( " Could not find domain member account ' %s ' to promote to a DC, use ' samba-tool domain join ' instead ' " % ctx . samname )
if " msDS-krbTgtLink " in res [ 0 ] or " serverReferenceBL " in res [ 0 ] or " rIDSetReferences " in res [ 0 ] :
raise Exception ( " Account ' %s ' appears to be an active DC, use ' samba-tool domain join ' if you must re-create this account " % ctx . samname )
if ( int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] ) & ( samba . dsdb . UF_WORKSTATION_TRUST_ACCOUNT | samba . dsdb . UF_SERVER_TRUST_ACCOUNT ) == 0 ) :
raise Exception ( " Account %s is not a domain member or a bare NT4 BDC, use ' samba-tool domain join ' instead ' " % ctx . samname )
2012-09-16 16:18:51 +04:00
2012-07-06 09:38:06 +04:00
ctx . promote_from_dn = res [ 0 ] . dn
2010-09-14 12:22:13 +04:00
def find_dc ( ctx , domain ) :
2012-09-16 16:18:51 +04:00
""" find a writeable DC for the given domain """
2010-11-05 06:09:49 +03:00
try :
2011-11-23 01:26:06 +04:00
ctx . cldap_ret = ctx . net . finddc ( domain = domain , flags = nbt . NBT_SERVER_LDAP | nbt . NBT_SERVER_DS | nbt . NBT_SERVER_WRITABLE )
2010-11-29 06:10:57 +03:00
except Exception :
raise Exception ( " Failed to find a writeable DC for domain ' %s ' " % domain )
2010-09-20 00:20:33 +04:00
if ctx . cldap_ret . client_site is not None and ctx . cldap_ret . client_site != " " :
ctx . site = ctx . cldap_ret . client_site
2010-09-29 05:31:02 +04:00
return ctx . cldap_ret . pdc_dns_name
2010-09-14 12:22:13 +04:00
2010-11-07 05:55:20 +03:00
def get_behavior_version ( ctx ) :
res = ctx . samdb . search ( base = ctx . base_dn , scope = ldb . SCOPE_BASE , attrs = [ " msDS-Behavior-Version " ] )
if " msDS-Behavior-Version " in res [ 0 ] :
return int ( res [ 0 ] [ " msDS-Behavior-Version " ] [ 0 ] )
else :
return samba . dsdb . DS_DOMAIN_FUNCTION_2000
2010-11-05 06:09:49 +03:00
def get_dnsHostName ( ctx ) :
res = ctx . samdb . search ( base = " " , scope = ldb . SCOPE_BASE , attrs = [ " dnsHostName " ] )
2010-08-23 05:31:48 +04:00
return res [ 0 ] [ " dnsHostName " ] [ 0 ]
2010-11-05 06:09:49 +03:00
def get_domain_name ( ctx ) :
2010-09-09 12:02:08 +04:00
''' get netbios name of the domain from the partitions record '''
2010-11-05 06:09:49 +03:00
partitions_dn = ctx . samdb . get_partitions_dn ( )
res = ctx . samdb . search ( base = partitions_dn , scope = ldb . SCOPE_ONELEVEL , attrs = [ " nETBIOSName " ] ,
2016-07-20 04:37:47 +03:00
expression = ' ncName= %s ' % ldb . binary_encode ( str ( ctx . samdb . get_default_basedn ( ) ) ) )
2010-09-09 12:02:08 +04:00
return res [ 0 ] [ " nETBIOSName " ] [ 0 ]
2011-10-01 04:58:52 +04:00
def get_forest_domain_name ( ctx ) :
''' get netbios name of the domain from the partitions record '''
partitions_dn = ctx . samdb . get_partitions_dn ( )
res = ctx . samdb . search ( base = partitions_dn , scope = ldb . SCOPE_ONELEVEL , attrs = [ " nETBIOSName " ] ,
2016-07-20 04:37:47 +03:00
expression = ' ncName= %s ' % ldb . binary_encode ( str ( ctx . samdb . get_root_basedn ( ) ) ) )
2011-10-01 04:58:52 +04:00
return res [ 0 ] [ " nETBIOSName " ] [ 0 ]
2011-08-26 07:23:41 +04:00
def get_parent_partition_dn ( ctx ) :
''' get the parent domain partition DN from parent DNS name '''
res = ctx . samdb . search ( base = ctx . config_dn , attrs = [ ] ,
expression = ' (&(objectclass=crossRef)(dnsRoot= %s )(systemFlags: %s := %u )) ' %
2016-07-20 04:37:47 +03:00
( ldb . binary_encode ( ctx . parent_dnsdomain ) ,
ldb . OID_COMPARATOR_AND , samba . dsdb . SYSTEM_FLAG_CR_NTDS_DOMAIN ) )
2011-08-26 07:23:41 +04:00
return str ( res [ 0 ] . dn )
2011-10-01 04:58:52 +04:00
def get_naming_master ( ctx ) :
''' get the parent domain partition DN from parent DNS name '''
res = ctx . samdb . search ( base = ' CN=Partitions, %s ' % ctx . config_dn , attrs = [ ' fSMORoleOwner ' ] ,
scope = ldb . SCOPE_BASE , controls = [ " extended_dn:1:1 " ] )
if not ' fSMORoleOwner ' in res [ 0 ] :
2013-09-04 05:03:37 +04:00
raise DCJoinException ( " Can ' t find naming master on partition DN %s in %s " % ( ctx . partition_dn , ctx . samdb . url ) )
2013-09-03 09:41:42 +04:00
try :
master_guid = str ( misc . GUID ( ldb . Dn ( ctx . samdb , res [ 0 ] [ ' fSMORoleOwner ' ] [ 0 ] ) . get_extended_component ( ' GUID ' ) ) )
except KeyError :
raise DCJoinException ( " Can ' t find GUID in naming master on partition DN %s " % res [ 0 ] [ ' fSMORoleOwner ' ] [ 0 ] )
2011-10-01 04:58:52 +04:00
master_host = ' %s ._msdcs. %s ' % ( master_guid , ctx . dnsforest )
return master_host
2010-11-05 06:09:49 +03:00
def get_mysid ( ctx ) :
2010-11-07 05:55:20 +03:00
''' get the SID of the connected user. Only works with w2k8 and later,
so only used for RODC join '''
2010-11-05 06:09:49 +03:00
res = ctx . samdb . search ( base = " " , scope = ldb . SCOPE_BASE , attrs = [ " tokenGroups " ] )
2010-08-23 05:31:48 +04:00
binsid = res [ 0 ] [ " tokenGroups " ] [ 0 ]
2010-11-05 06:09:49 +03:00
return ctx . samdb . schema_format_value ( " objectSID " , binsid )
2010-08-24 17:37:25 +04:00
2010-11-07 05:55:20 +03:00
def dn_exists ( ctx , dn ) :
''' check if a DN exists '''
try :
res = ctx . samdb . search ( base = dn , scope = ldb . SCOPE_BASE , attrs = [ ] )
2010-11-29 06:10:57 +03:00
except ldb . LdbError , ( enum , estr ) :
if enum == ldb . ERR_NO_SUCH_OBJECT :
return False
raise
2010-11-07 05:55:20 +03:00
return True
2010-11-05 06:09:49 +03:00
def add_krbtgt_account ( ctx ) :
''' RODCs need a special krbtgt account '''
2010-08-24 17:37:25 +04:00
print " Adding %s " % ctx . krbtgt_dn
rec = {
" dn " : ctx . krbtgt_dn ,
" objectclass " : " user " ,
" useraccountcontrol " : str ( samba . dsdb . UF_NORMAL_ACCOUNT |
samba . dsdb . UF_ACCOUNTDISABLE ) ,
" showinadvancedviewonly " : " TRUE " ,
2010-09-30 23:44:39 +04:00
" description " : " krbtgt for %s " % ctx . samname }
2010-08-24 17:37:25 +04:00
ctx . samdb . add ( rec , [ " rodc_join:1:1 " ] )
# now we need to search for the samAccountName attribute on the krbtgt DN,
# as this will have been magically set to the krbtgt number
res = ctx . samdb . search ( base = ctx . krbtgt_dn , scope = ldb . SCOPE_BASE , attrs = [ " samAccountName " ] )
ctx . krbtgt_name = res [ 0 ] [ " samAccountName " ] [ 0 ]
print " Got krbtgt_name= %s " % ctx . krbtgt_name
m = ldb . Message ( )
m . dn = ldb . Dn ( ctx . samdb , ctx . acct_dn )
m [ " msDS-krbTgtLink " ] = ldb . MessageElement ( ctx . krbtgt_dn ,
ldb . FLAG_MOD_REPLACE , " msDS-krbTgtLink " )
ctx . samdb . modify ( m )
ctx . new_krbtgt_dn = " CN= %s ,CN=Users, %s " % ( ctx . krbtgt_name , ctx . base_dn )
print " Renaming %s to %s " % ( ctx . krbtgt_dn , ctx . new_krbtgt_dn )
ctx . samdb . rename ( ctx . krbtgt_dn , ctx . new_krbtgt_dn )
2010-11-05 06:09:49 +03:00
def drsuapi_connect ( ctx ) :
2011-10-01 04:58:52 +04:00
''' make a DRSUAPI connection to the naming master '''
2010-11-17 03:08:59 +03:00
binding_options = " seal "
2017-09-06 07:40:05 +03:00
if ctx . lp . log_level ( ) > = 9 :
2010-11-17 03:08:59 +03:00
binding_options + = " ,print "
binding_string = " ncacn_ip_tcp: %s [ %s ] " % ( ctx . server , binding_options )
2010-11-05 06:09:49 +03:00
ctx . drsuapi = drsuapi . drsuapi ( binding_string , ctx . lp , ctx . creds )
2010-11-07 05:55:20 +03:00
( ctx . drsuapi_handle , ctx . bind_supported_extensions ) = drs_utils . drs_DsBind ( ctx . drsuapi )
2010-11-05 06:09:49 +03:00
def create_tmp_samdb ( ctx ) :
''' create a temporary samdb object for schema queries '''
2014-08-11 03:23:57 +04:00
ctx . tmp_schema = Schema ( ctx . domsid ,
2010-11-05 06:09:49 +03:00
schemadn = ctx . schema_dn )
ctx . tmp_samdb = SamDB ( session_info = system_session ( ) , url = None , auto_connect = False ,
credentials = ctx . creds , lp = ctx . lp , global_schema = False ,
am_rodc = False )
ctx . tmp_samdb . set_schema ( ctx . tmp_schema )
def build_DsReplicaAttribute ( ctx , attrname , attrvalue ) :
''' build a DsReplicaAttributeCtr object '''
r = drsuapi . DsReplicaAttribute ( )
r . attid = ctx . tmp_samdb . get_attid_from_lDAPDisplayName ( attrname )
r . value_ctr = 1
2011-08-24 09:39:51 +04:00
def DsAddEntry ( ctx , recs ) :
2010-11-05 06:09:49 +03:00
''' add a record via the DRSUAPI DsAddEntry call '''
if ctx . drsuapi is None :
ctx . drsuapi_connect ( )
if ctx . tmp_samdb is None :
ctx . create_tmp_samdb ( )
2011-08-24 09:39:51 +04:00
objects = [ ]
for rec in recs :
id = drsuapi . DsReplicaObjectIdentifier ( )
id . dn = rec [ ' dn ' ]
attrs = [ ]
for a in rec :
if a == ' dn ' :
continue
if not isinstance ( rec [ a ] , list ) :
v = [ rec [ a ] ]
else :
v = rec [ a ]
rattr = ctx . tmp_samdb . dsdb_DsReplicaAttribute ( ctx . tmp_samdb , a , v )
attrs . append ( rattr )
attribute_ctr = drsuapi . DsReplicaAttributeCtr ( )
attribute_ctr . num_attributes = len ( attrs )
attribute_ctr . attributes = attrs
object = drsuapi . DsReplicaObject ( )
object . identifier = id
object . attribute_ctr = attribute_ctr
list_object = drsuapi . DsReplicaObjectListItem ( )
list_object . object = object
objects . append ( list_object )
2010-11-05 06:09:49 +03:00
req2 = drsuapi . DsAddEntryRequest2 ( )
2011-08-24 09:39:51 +04:00
req2 . first_object = objects [ 0 ]
prev = req2 . first_object
for o in objects [ 1 : ] :
prev . next_object = o
prev = o
2010-11-05 06:09:49 +03:00
( level , ctr ) = ctx . drsuapi . DsAddEntry ( ctx . drsuapi_handle , 2 , req2 )
2011-11-09 12:32:55 +04:00
if level == 2 :
if ctr . dir_err != drsuapi . DRSUAPI_DIRERR_OK :
print ( " DsAddEntry failed with dir_err %u " % ctr . dir_err )
raise RuntimeError ( " DsAddEntry failed " )
2017-02-13 01:12:54 +03:00
if ctr . extended_err [ 0 ] != werror . WERR_SUCCESS :
2011-11-09 12:32:55 +04:00
print ( " DsAddEntry failed with status %s info %s " % ( ctr . extended_err ) )
raise RuntimeError ( " DsAddEntry failed " )
if level == 3 :
if ctr . err_ver != 1 :
raise RuntimeError ( " expected err_ver 1, got %u " % ctr . err_ver )
2017-02-13 01:12:54 +03:00
if ctr . err_data . status [ 0 ] != werror . WERR_SUCCESS :
2017-02-11 09:34:09 +03:00
if ctr . err_data . info is None :
print ( " DsAddEntry failed with status %s , info omitted " % ( ctr . err_data . status [ 1 ] ) )
else :
print ( " DsAddEntry failed with status %s info %s " % ( ctr . err_data . status [ 1 ] ,
ctr . err_data . info . extended_err ) )
2011-11-09 12:32:55 +04:00
raise RuntimeError ( " DsAddEntry failed " )
if ctr . err_data . dir_err != drsuapi . DRSUAPI_DIRERR_OK :
print ( " DsAddEntry failed with dir_err %u " % ctr . err_data . dir_err )
raise RuntimeError ( " DsAddEntry failed " )
2011-08-24 09:39:51 +04:00
return ctr . objects
2011-09-07 11:21:07 +04:00
2013-10-16 05:34:43 +04:00
def join_ntdsdsa_obj ( ctx ) :
''' return the ntdsdsa object to add '''
2012-10-08 08:52:25 +04:00
2011-08-26 07:23:41 +04:00
print " Adding %s " % ctx . ntds_dn
rec = {
" dn " : ctx . ntds_dn ,
" objectclass " : " nTDSDSA " ,
" systemFlags " : str ( samba . dsdb . SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE ) ,
" dMDLocation " : ctx . schema_dn }
2011-08-24 09:39:51 +04:00
nc_list = [ ctx . base_dn , ctx . config_dn , ctx . schema_dn ]
2011-08-26 07:23:41 +04:00
if ctx . behavior_version > = samba . dsdb . DS_DOMAIN_FUNCTION_2003 :
2011-12-12 15:44:33 +04:00
rec [ " msDS-Behavior-Version " ] = str ( samba . dsdb . DS_DOMAIN_FUNCTION_2008_R2 )
2011-08-26 07:23:41 +04:00
2011-08-24 09:39:51 +04:00
if ctx . behavior_version > = samba . dsdb . DS_DOMAIN_FUNCTION_2003 :
2011-08-26 07:23:41 +04:00
rec [ " msDS-HasDomainNCs " ] = ctx . base_dn
if ctx . RODC :
rec [ " objectCategory " ] = " CN=NTDS-DSA-RO, %s " % ctx . schema_dn
2013-10-16 05:34:43 +04:00
rec [ " msDS-HasFullReplicaNCs " ] = ctx . full_nc_list
2011-08-26 07:23:41 +04:00
rec [ " options " ] = " 37 "
else :
rec [ " objectCategory " ] = " CN=NTDS-DSA, %s " % ctx . schema_dn
2013-10-16 05:34:43 +04:00
rec [ " HasMasterNCs " ] = [ ]
for nc in nc_list :
if nc in ctx . full_nc_list :
rec [ " HasMasterNCs " ] . append ( nc )
2011-08-26 07:23:41 +04:00
if ctx . behavior_version > = samba . dsdb . DS_DOMAIN_FUNCTION_2003 :
2013-10-16 05:34:43 +04:00
rec [ " msDS-HasMasterNCs " ] = ctx . full_nc_list
2011-08-26 07:23:41 +04:00
rec [ " options " ] = " 1 "
2011-09-02 09:12:11 +04:00
rec [ " invocationId " ] = ndr_pack ( ctx . invocation_id )
2013-10-16 05:34:43 +04:00
return rec
def join_add_ntdsdsa ( ctx ) :
''' add the ntdsdsa object '''
rec = ctx . join_ntdsdsa_obj ( )
if ctx . RODC :
ctx . samdb . add ( rec , [ " rodc_join:1:1 " ] )
else :
2011-08-24 09:39:51 +04:00
ctx . DsAddEntry ( [ rec ] )
2011-08-26 07:23:41 +04:00
# find the GUID of our NTDS DN
res = ctx . samdb . search ( base = ctx . ntds_dn , scope = ldb . SCOPE_BASE , attrs = [ " objectGUID " ] )
ctx . ntds_guid = misc . GUID ( ctx . samdb . schema_format_value ( " objectGUID " , res [ 0 ] [ " objectGUID " ] [ 0 ] ) )
2010-11-05 06:09:49 +03:00
def join_add_objects ( ctx ) :
''' add the various objects needed for the join '''
2011-08-24 09:39:51 +04:00
if ctx . acct_dn :
print " Adding %s " % ctx . acct_dn
rec = {
" dn " : ctx . acct_dn ,
" objectClass " : " computer " ,
" displayname " : ctx . samname ,
" samaccountname " : ctx . samname ,
" userAccountControl " : str ( ctx . userAccountControl | samba . dsdb . UF_ACCOUNTDISABLE ) ,
" dnshostname " : ctx . dnshostname }
if ctx . behavior_version > = samba . dsdb . DS_DOMAIN_FUNCTION_2008 :
rec [ ' msDS-SupportedEncryptionTypes ' ] = str ( samba . dsdb . ENC_ALL_TYPES )
2012-07-06 09:38:06 +04:00
elif ctx . promote_existing :
rec [ ' msDS-SupportedEncryptionTypes ' ] = [ ]
2011-08-24 09:39:51 +04:00
if ctx . managedby :
rec [ " managedby " ] = ctx . managedby
2012-07-06 09:38:06 +04:00
elif ctx . promote_existing :
rec [ " managedby " ] = [ ]
2011-08-24 09:39:51 +04:00
if ctx . never_reveal_sid :
rec [ " msDS-NeverRevealGroup " ] = ctx . never_reveal_sid
2012-07-06 09:38:06 +04:00
elif ctx . promote_existing :
rec [ " msDS-NeverRevealGroup " ] = [ ]
2012-09-16 16:18:51 +04:00
2011-08-24 09:39:51 +04:00
if ctx . reveal_sid :
rec [ " msDS-RevealOnDemandGroup " ] = ctx . reveal_sid
2012-07-06 09:38:06 +04:00
elif ctx . promote_existing :
rec [ " msDS-RevealOnDemandGroup " ] = [ ]
if ctx . promote_existing :
if ctx . promote_from_dn != ctx . acct_dn :
ctx . samdb . rename ( ctx . promote_from_dn , ctx . acct_dn )
ctx . samdb . modify ( ldb . Message . from_dict ( ctx . samdb , rec , ldb . FLAG_MOD_REPLACE ) )
else :
ctx . samdb . add ( rec )
2010-11-05 06:09:49 +03:00
if ctx . krbtgt_dn :
ctx . add_krbtgt_account ( )
2015-08-17 06:33:31 +03:00
if ctx . server_dn :
print " Adding %s " % ctx . server_dn
rec = {
" dn " : ctx . server_dn ,
" objectclass " : " server " ,
# windows uses 50000000 decimal for systemFlags. A windows hex/decimal mixup bug?
" systemFlags " : str ( samba . dsdb . SYSTEM_FLAG_CONFIG_ALLOW_RENAME |
samba . dsdb . SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE |
samba . dsdb . SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE ) ,
# windows seems to add the dnsHostName later
" dnsHostName " : ctx . dnshostname }
if ctx . acct_dn :
rec [ " serverReference " ] = ctx . acct_dn
2011-08-24 09:39:51 +04:00
2015-08-17 06:33:31 +03:00
ctx . samdb . add ( rec )
2010-08-24 17:37:25 +04:00
2011-08-26 10:07:05 +04:00
if ctx . subdomain :
2011-08-26 07:23:41 +04:00
# the rest is done after replication
ctx . ntds_guid = None
return
2011-08-26 10:07:05 +04:00
2015-08-17 06:33:31 +03:00
if ctx . ntds_dn :
ctx . join_add_ntdsdsa ( )
2010-08-24 17:37:25 +04:00
2016-07-18 04:09:59 +03:00
# Add the Replica-Locations or RO-Replica-Locations attributes
# TODO Is this supposed to be for the schema partition too?
expr = " (&(objectClass=crossRef)(ncName= %s )) " % ldb . binary_encode ( ctx . domaindns_zone )
2016-07-21 07:01:20 +03:00
domain = ( ctx . samdb . search ( scope = ldb . SCOPE_ONELEVEL ,
2016-07-18 04:09:59 +03:00
attrs = [ ] ,
base = ctx . samdb . get_partitions_dn ( ) ,
2016-07-21 07:01:20 +03:00
expression = expr ) , ctx . domaindns_zone )
2016-07-18 04:09:59 +03:00
expr = " (&(objectClass=crossRef)(ncName= %s )) " % ldb . binary_encode ( ctx . forestdns_zone )
2016-07-21 07:01:20 +03:00
forest = ( ctx . samdb . search ( scope = ldb . SCOPE_ONELEVEL ,
2016-07-18 04:09:59 +03:00
attrs = [ ] ,
base = ctx . samdb . get_partitions_dn ( ) ,
2016-07-21 07:01:20 +03:00
expression = expr ) , ctx . forestdns_zone )
for part , zone in ( domain , forest ) :
if zone not in ctx . nc_list :
continue
2016-07-18 04:09:59 +03:00
if len ( part ) == 1 :
m = ldb . Message ( )
m . dn = part [ 0 ] . dn
attr = " msDS-NC-Replica-Locations "
if ctx . RODC :
attr = " msDS-NC-RO-Replica-Locations "
m [ attr ] = ldb . MessageElement ( ctx . ntds_dn ,
ldb . FLAG_MOD_ADD , attr )
ctx . samdb . modify ( m )
2010-11-05 06:09:49 +03:00
if ctx . connection_dn is not None :
print " Adding %s " % ctx . connection_dn
rec = {
" dn " : ctx . connection_dn ,
" objectclass " : " nTDSConnection " ,
" enabledconnection " : " TRUE " ,
" options " : " 65 " ,
" fromServer " : ctx . dc_ntds_dn }
ctx . samdb . add ( rec )
2010-08-24 17:37:25 +04:00
2011-08-24 09:39:51 +04:00
if ctx . acct_dn :
print " Adding SPNs to %s " % ctx . acct_dn
m = ldb . Message ( )
m . dn = ldb . Dn ( ctx . samdb , ctx . acct_dn )
for i in range ( len ( ctx . SPNs ) ) :
ctx . SPNs [ i ] = ctx . SPNs [ i ] . replace ( " $NTDSGUID " , str ( ctx . ntds_guid ) )
m [ " servicePrincipalName " ] = ldb . MessageElement ( ctx . SPNs ,
2012-07-06 09:38:06 +04:00
ldb . FLAG_MOD_REPLACE ,
2011-08-24 09:39:51 +04:00
" servicePrincipalName " )
ctx . samdb . modify ( m )
2011-11-14 20:53:39 +04:00
# The account password set operation should normally be done over
# LDAP. Windows 2000 DCs however allow this only with SSL
# connections which are hard to set up and otherwise refuse with
# ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
# over SAMR.
2011-08-24 09:39:51 +04:00
print " Setting account password for %s " % ctx . samname
2011-11-14 20:53:39 +04:00
try :
ctx . samdb . setpassword ( " (&(objectClass=user)(sAMAccountName= %s )) "
% ldb . binary_encode ( ctx . samname ) ,
ctx . acct_pass ,
force_change_at_next_login = False ,
username = ctx . samname )
except ldb . LdbError , ( num , _ ) :
if num != ldb . ERR_UNWILLING_TO_PERFORM :
pass
ctx . net . set_password ( account_name = ctx . samname ,
domain_name = ctx . domain_name ,
2016-08-23 13:37:37 +03:00
newpassword = ctx . acct_pass . encode ( ' utf-8 ' ) )
2011-11-14 20:53:39 +04:00
2012-01-30 20:20:28 +04:00
res = ctx . samdb . search ( base = ctx . acct_dn , scope = ldb . SCOPE_BASE ,
2017-02-17 08:23:23 +03:00
attrs = [ " msDS-KeyVersionNumber " ,
" objectSID " ] )
2012-01-30 20:20:28 +04:00
if " msDS-KeyVersionNumber " in res [ 0 ] :
ctx . key_version_number = int ( res [ 0 ] [ " msDS-KeyVersionNumber " ] [ 0 ] )
else :
ctx . key_version_number = None
2011-08-24 09:39:51 +04:00
2017-02-17 08:23:23 +03:00
ctx . new_dc_account_sid = ndr_unpack ( security . dom_sid ,
res [ 0 ] [ " objectSid " ] [ 0 ] )
2011-08-24 09:39:51 +04:00
print ( " Enabling account " )
m = ldb . Message ( )
m . dn = ldb . Dn ( ctx . samdb , ctx . acct_dn )
m [ " userAccountControl " ] = ldb . MessageElement ( str ( ctx . userAccountControl ) ,
ldb . FLAG_MOD_REPLACE ,
" userAccountControl " )
ctx . samdb . modify ( m )
2010-08-24 17:37:25 +04:00
2012-12-24 01:56:50 +04:00
if ctx . dns_backend . startswith ( " BIND9_ " ) :
ctx . dnspass = samba . generate_random_password ( 128 , 255 )
recs = ctx . samdb . parse_ldif ( read_and_sub_file ( setup_path ( " provision_dns_add_samba.ldif " ) ,
{ " DNSDOMAIN " : ctx . dnsdomain ,
" DOMAINDN " : ctx . base_dn ,
" HOSTNAME " : ctx . myname ,
2017-02-17 02:10:12 +03:00
" DNSPASS_B64 " : b64encode ( ctx . dnspass . encode ( ' utf-16-le ' ) ) ,
2012-12-24 01:56:50 +04:00
" DNSNAME " : ctx . dnshostname } ) )
for changetype , msg in recs :
assert changetype == ldb . CHANGETYPE_NONE
2013-10-24 19:37:06 +04:00
dns_acct_dn = msg [ " dn " ]
2012-12-24 01:56:50 +04:00
print " Adding DNS account %s with dns/ SPN " % msg [ " dn " ]
# Remove dns password (we will set it as a modify, as we can't do clearTextPassword over LDAP)
del msg [ " clearTextPassword " ]
# Remove isCriticalSystemObject for similar reasons, it cannot be set over LDAP
del msg [ " isCriticalSystemObject " ]
2013-10-24 19:37:06 +04:00
# Disable account until password is set
msg [ " userAccountControl " ] = str ( samba . dsdb . UF_NORMAL_ACCOUNT |
samba . dsdb . UF_ACCOUNTDISABLE )
2012-12-24 01:56:50 +04:00
try :
ctx . samdb . add ( msg )
except ldb . LdbError , ( num , _ ) :
if num != ldb . ERR_ENTRY_ALREADY_EXISTS :
raise
# The account password set operation should normally be done over
# LDAP. Windows 2000 DCs however allow this only with SSL
# connections which are hard to set up and otherwise refuse with
# ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
# over SAMR.
2013-10-24 19:37:06 +04:00
print " Setting account password for dns- %s " % ctx . myname
2012-12-24 01:56:50 +04:00
try :
ctx . samdb . setpassword ( " (&(objectClass=user)(samAccountName=dns- %s )) "
% ldb . binary_encode ( ctx . myname ) ,
ctx . dnspass ,
force_change_at_next_login = False ,
username = ctx . samname )
except ldb . LdbError , ( num , _ ) :
if num != ldb . ERR_UNWILLING_TO_PERFORM :
2013-10-24 19:37:06 +04:00
raise
ctx . net . set_password ( account_name = " dns- %s " % ctx . myname ,
2012-12-24 01:56:50 +04:00
domain_name = ctx . domain_name ,
newpassword = ctx . dnspass )
res = ctx . samdb . search ( base = dns_acct_dn , scope = ldb . SCOPE_BASE ,
attrs = [ " msDS-KeyVersionNumber " ] )
if " msDS-KeyVersionNumber " in res [ 0 ] :
ctx . dns_key_version_number = int ( res [ 0 ] [ " msDS-KeyVersionNumber " ] [ 0 ] )
else :
ctx . dns_key_version_number = None
2011-08-26 07:23:41 +04:00
def join_add_objects2 ( ctx ) :
2012-09-16 16:18:51 +04:00
""" add the various objects needed for the join, for subdomains post replication """
2011-08-26 07:23:41 +04:00
print " Adding %s " % ctx . partition_dn
2014-08-22 06:16:30 +04:00
name_map = { ' SubdomainAdmins ' : " %s - %s " % ( str ( ctx . domsid ) , security . DOMAIN_RID_ADMINS ) }
sd_binary = descriptor . get_paritions_crossref_subdomain_descriptor ( ctx . forestsid , name_map = name_map )
2011-08-26 07:23:41 +04:00
rec = {
" dn " : ctx . partition_dn ,
" objectclass " : " crossRef " ,
" objectCategory " : " CN=Cross-Ref, %s " % ctx . schema_dn ,
" nCName " : ctx . base_dn ,
" nETBIOSName " : ctx . domain_name ,
" dnsRoot " : ctx . dnsdomain ,
" trustParent " : ctx . parent_partition_dn ,
2014-08-22 06:16:30 +04:00
" systemFlags " : str ( samba . dsdb . SYSTEM_FLAG_CR_NTDS_NC | samba . dsdb . SYSTEM_FLAG_CR_NTDS_DOMAIN ) ,
" ntSecurityDescriptor " : sd_binary ,
}
2011-09-02 09:12:11 +04:00
if ctx . behavior_version > = samba . dsdb . DS_DOMAIN_FUNCTION_2003 :
rec [ " msDS-Behavior-Version " ] = str ( ctx . behavior_version )
2011-08-26 07:23:41 +04:00
2013-10-16 05:34:43 +04:00
rec2 = ctx . join_ntdsdsa_obj ( )
2011-08-24 09:39:51 +04:00
objects = ctx . DsAddEntry ( [ rec , rec2 ] )
if len ( objects ) != 2 :
raise DCJoinException ( " Expected 2 objects from DsAddEntry " )
ctx . ntds_guid = objects [ 1 ] . guid
print ( " Replicating partition DN " )
ctx . repl . replicate ( ctx . partition_dn ,
misc . GUID ( " 00000000-0000-0000-0000-000000000000 " ) ,
ctx . ntds_guid ,
exop = drsuapi . DRSUAPI_EXOP_REPL_OBJ ,
replica_flags = drsuapi . DRSUAPI_DRS_WRIT_REP )
print ( " Replicating NTDS DN " )
ctx . repl . replicate ( ctx . ntds_dn ,
misc . GUID ( " 00000000-0000-0000-0000-000000000000 " ) ,
ctx . ntds_guid ,
exop = drsuapi . DRSUAPI_EXOP_REPL_OBJ ,
replica_flags = drsuapi . DRSUAPI_DRS_WRIT_REP )
2011-08-26 07:23:41 +04:00
2010-08-24 17:37:25 +04:00
def join_provision ( ctx ) :
2012-09-16 16:18:51 +04:00
""" Provision the local SAM. """
2010-08-24 17:37:25 +04:00
print " Calling bare provision "
2010-11-05 06:09:49 +03:00
smbconf = ctx . lp . configfile
2010-08-24 17:37:25 +04:00
2013-09-26 21:19:18 +04:00
presult = provision ( ctx . logger , system_session ( ) , smbconf = smbconf ,
2012-02-26 19:25:56 +04:00
targetdir = ctx . targetdir , samdb_fill = FILL_DRS , realm = ctx . realm ,
rootdn = ctx . root_dn , domaindn = ctx . base_dn ,
schemadn = ctx . schema_dn , configdn = ctx . config_dn ,
serverdn = ctx . server_dn , domain = ctx . domain_name ,
hostname = ctx . myname , domainsid = ctx . domsid ,
2013-09-09 04:15:36 +04:00
machinepass = ctx . acct_pass , serverrole = " active directory domain controller " ,
2012-02-26 19:25:56 +04:00
sitename = ctx . site , lp = ctx . lp , ntdsguid = ctx . ntds_guid ,
2017-12-14 21:24:14 +03:00
use_ntvfs = ctx . use_ntvfs , dns_backend = ctx . dns_backend ,
plaintext_secrets = ctx . plaintext_secrets )
2010-08-24 17:37:25 +04:00
print " Provision OK for domain DN %s " % presult . domaindn
ctx . local_samdb = presult . samdb
ctx . lp = presult . lp
2010-08-25 06:34:15 +04:00
ctx . paths = presult . paths
2011-08-24 09:39:51 +04:00
ctx . names = presult . names
2014-08-08 10:43:47 +04:00
# Fix up the forestsid, it may be different if we are joining as a subdomain
ctx . names . forestsid = ctx . forestsid
2011-08-24 09:39:51 +04:00
def join_provision_own_domain ( ctx ) :
2012-09-16 16:18:51 +04:00
""" Provision the local SAM. """
2011-08-24 09:39:51 +04:00
2011-09-02 09:12:11 +04:00
# we now operate exclusively on the local database, which
# we need to reopen in order to get the newly created schema
print ( " Reconnecting to local samdb " )
ctx . samdb = SamDB ( url = ctx . local_samdb . url ,
session_info = system_session ( ) ,
lp = ctx . local_samdb . lp ,
global_schema = False )
ctx . samdb . set_invocation_id ( str ( ctx . invocation_id ) )
ctx . local_samdb = ctx . samdb
2011-08-26 07:23:41 +04:00
2013-09-09 01:53:37 +04:00
ctx . logger . info ( " Finding domain GUID from ncName " )
2011-08-24 09:39:51 +04:00
res = ctx . local_samdb . search ( base = ctx . partition_dn , scope = ldb . SCOPE_BASE , attrs = [ ' ncName ' ] ,
2013-09-16 21:23:07 +04:00
controls = [ " extended_dn:1:1 " , " reveal_internals:0 " ] )
2013-09-06 07:38:36 +04:00
if ' nCName ' not in res [ 0 ] :
raise DCJoinException ( " Can ' t find naming context on partition DN %s in %s " % ( ctx . partition_dn , ctx . samdb . url ) )
try :
2014-08-08 10:43:47 +04:00
ctx . names . domainguid = str ( misc . GUID ( ldb . Dn ( ctx . samdb , res [ 0 ] [ ' ncName ' ] [ 0 ] ) . get_extended_component ( ' GUID ' ) ) )
2013-09-06 07:38:36 +04:00
except KeyError :
raise DCJoinException ( " Can ' t find GUID in naming master on partition DN %s " % res [ 0 ] [ ' ncName ' ] [ 0 ] )
2014-08-08 10:43:47 +04:00
ctx . logger . info ( " Got domain GUID %s " % ctx . names . domainguid )
2011-09-07 11:21:07 +04:00
2013-09-09 01:53:37 +04:00
ctx . logger . info ( " Calling own domain provision " )
2011-08-24 09:39:51 +04:00
secrets_ldb = Ldb ( ctx . paths . secrets , session_info = system_session ( ) , lp = ctx . lp )
presult = provision_fill ( ctx . local_samdb , secrets_ldb ,
2014-08-08 10:43:47 +04:00
ctx . logger , ctx . names , ctx . paths ,
2013-09-09 03:54:23 +04:00
dom_for_fun_level = DS_DOMAIN_FUNCTION_2003 ,
2011-08-24 09:39:51 +04:00
targetdir = ctx . targetdir , samdb_fill = FILL_SUBDOMAIN ,
2013-09-09 04:15:36 +04:00
machinepass = ctx . acct_pass , serverrole = " active directory domain controller " ,
2011-10-15 15:54:45 +04:00
lp = ctx . lp , hostip = ctx . names . hostip , hostip6 = ctx . names . hostip6 ,
2013-09-09 01:53:37 +04:00
dns_backend = ctx . dns_backend , adminpass = ctx . adminpass )
2011-08-26 07:23:41 +04:00
print ( " Provision OK for domain %s " % ctx . names . dnsdomain )
2010-08-24 17:37:25 +04:00
def join_replicate ( ctx ) :
2012-09-16 16:18:51 +04:00
""" Replicate the SAM. """
2010-08-24 17:37:25 +04:00
print " Starting replication "
ctx . local_samdb . transaction_start ( )
2010-12-15 18:40:59 +03:00
try :
source_dsa_invocation_id = misc . GUID ( ctx . samdb . get_invocation_id ( ) )
2011-08-26 07:23:41 +04:00
if ctx . ntds_guid is None :
2011-08-24 09:39:51 +04:00
print ( " Using DS_BIND_GUID_W2K3 " )
2011-08-26 07:23:41 +04:00
destination_dsa_guid = misc . GUID ( drsuapi . DRSUAPI_DS_BIND_GUID_W2K3 )
else :
destination_dsa_guid = ctx . ntds_guid
2010-12-15 18:40:59 +03:00
if ctx . RODC :
repl_creds = Credentials ( )
repl_creds . guess ( ctx . lp )
repl_creds . set_kerberos_state ( DONT_USE_KERBEROS )
repl_creds . set_username ( ctx . samname )
2017-02-14 00:34:06 +03:00
repl_creds . set_password ( ctx . acct_pass . encode ( ' utf-8 ' ) )
2010-12-15 18:40:59 +03:00
else :
repl_creds = ctx . creds
binding_options = " seal "
2017-09-06 07:40:05 +03:00
if ctx . lp . log_level ( ) > = 9 :
2010-12-15 18:40:59 +03:00
binding_options + = " ,print "
repl = drs_utils . drs_Replicate (
" ncacn_ip_tcp: %s [ %s ] " % ( ctx . server , binding_options ) ,
2013-09-19 01:27:26 +04:00
ctx . lp , repl_creds , ctx . local_samdb , ctx . invocation_id )
2010-12-15 18:40:59 +03:00
repl . replicate ( ctx . schema_dn , source_dsa_invocation_id ,
destination_dsa_guid , schema = True , rodc = ctx . RODC ,
replica_flags = ctx . replica_flags )
repl . replicate ( ctx . config_dn , source_dsa_invocation_id ,
destination_dsa_guid , rodc = ctx . RODC ,
replica_flags = ctx . replica_flags )
2011-08-24 09:39:51 +04:00
if not ctx . subdomain :
2011-10-25 22:13:00 +04:00
# Replicate first the critical object for the basedn
if not ctx . domain_replica_flags & drsuapi . DRSUAPI_DRS_CRITICAL_ONLY :
print " Replicating critical objects from the base DN of the domain "
2016-11-29 16:27:57 +03:00
ctx . domain_replica_flags | = drsuapi . DRSUAPI_DRS_CRITICAL_ONLY
2011-10-25 22:13:00 +04:00
repl . replicate ( ctx . base_dn , source_dsa_invocation_id ,
destination_dsa_guid , rodc = ctx . RODC ,
replica_flags = ctx . domain_replica_flags )
2015-12-09 07:04:14 +03:00
ctx . domain_replica_flags ^ = drsuapi . DRSUAPI_DRS_CRITICAL_ONLY
2011-08-24 09:39:51 +04:00
repl . replicate ( ctx . base_dn , source_dsa_invocation_id ,
destination_dsa_guid , rodc = ctx . RODC ,
replica_flags = ctx . domain_replica_flags )
2012-09-29 21:15:05 +04:00
print " Done with always replicated NC (base, config, schema) "
2012-06-21 17:46:21 +04:00
2016-07-18 04:09:59 +03:00
# At this point we should already have an entry in the ForestDNS
# and DomainDNS NC (those under CN=Partions,DC=...) in order to
# indicate that we hold a replica for this NC.
2012-09-29 21:15:05 +04:00
for nc in ( ctx . domaindns_zone , ctx . forestdns_zone ) :
2012-10-11 19:23:13 +04:00
if nc in ctx . nc_list :
2012-09-29 21:15:05 +04:00
print " Replicating %s " % ( str ( nc ) )
repl . replicate ( nc , source_dsa_invocation_id ,
destination_dsa_guid , rodc = ctx . RODC ,
replica_flags = ctx . replica_flags )
2012-06-21 17:46:21 +04:00
2010-12-15 18:40:59 +03:00
if ctx . RODC :
repl . replicate ( ctx . acct_dn , source_dsa_invocation_id ,
destination_dsa_guid ,
exop = drsuapi . DRSUAPI_EXOP_REPL_SECRET , rodc = True )
repl . replicate ( ctx . new_krbtgt_dn , source_dsa_invocation_id ,
destination_dsa_guid ,
exop = drsuapi . DRSUAPI_EXOP_REPL_SECRET , rodc = True )
2016-10-31 06:48:33 +03:00
elif ctx . rid_manager_dn != None :
# Try and get a RID Set if we can. This is only possible against the RID Master. Warn otherwise.
try :
repl . replicate ( ctx . rid_manager_dn , source_dsa_invocation_id ,
destination_dsa_guid ,
exop = drsuapi . DRSUAPI_EXOP_FSMO_RID_ALLOC )
except samba . DsExtendedError , ( enum , estr ) :
if enum == drsuapi . DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER :
print " WARNING: Unable to replicate own RID Set, as server %s (the server we joined) is not the RID Master. " % ctx . server
print " NOTE: This is normal and expected, Samba will be able to create users after it contacts the RID Master at first startup. "
else :
raise
2011-08-24 09:39:51 +04:00
ctx . repl = repl
ctx . source_dsa_invocation_id = source_dsa_invocation_id
ctx . destination_dsa_guid = destination_dsa_guid
2010-12-15 18:40:59 +03:00
print " Committing SAM database "
2012-02-25 18:56:25 +04:00
except :
2010-12-15 18:40:59 +03:00
ctx . local_samdb . transaction_cancel ( )
raise
2010-11-07 05:55:20 +03:00
else :
2010-12-15 18:40:59 +03:00
ctx . local_samdb . transaction_commit ( )
2010-08-24 17:37:25 +04:00
2011-10-25 22:16:38 +04:00
def send_DsReplicaUpdateRefs ( ctx , dn ) :
r = drsuapi . DsReplicaUpdateRefsRequest1 ( )
r . naming_context = drsuapi . DsReplicaObjectIdentifier ( )
r . naming_context . dn = str ( dn )
r . naming_context . guid = misc . GUID ( " 00000000-0000-0000-0000-000000000000 " )
r . naming_context . sid = security . dom_sid ( " S-0-0 " )
r . dest_dsa_guid = ctx . ntds_guid
r . dest_dsa_dns_name = " %s ._msdcs. %s " % ( str ( ctx . ntds_guid ) , ctx . dnsforest )
r . options = drsuapi . DRSUAPI_DRS_ADD_REF | drsuapi . DRSUAPI_DRS_DEL_REF
if not ctx . RODC :
r . options | = drsuapi . DRSUAPI_DRS_WRIT_REP
2017-03-28 04:29:26 +03:00
if ctx . drsuapi is None :
ctx . drsuapi_connect ( )
ctx . drsuapi . DsReplicaUpdateRefs ( ctx . drsuapi_handle , 1 , r )
2010-08-24 17:37:25 +04:00
2017-02-17 08:23:23 +03:00
def join_add_dns_records ( ctx ) :
""" Remotely Add a DNS record to the target DC. We assume that if we
replicate DNS that the server holds the DNS roles and can accept
updates .
This avoids issues getting replication going after the DC
first starts as the rest of the domain does not have to
wait for samba_dnsupdate to run successfully .
Specifically , we add the records implied by the DsReplicaUpdateRefs
call above .
We do not just run samba_dnsupdate as we want to strictly
operate against the DC we just joined :
- We do not want to query another DNS server
- We do not want to obtain a Kerberos ticket
( as the KDC we select may not be the DC we just joined ,
and so may not be in sync with the password we just set )
- We do not wish to set the _ldap records until we have started
- We do not wish to use NTLM ( the - - use - samba - tool mode forces
NTLM )
"""
client_version = dnsserver . DNS_CLIENT_VERSION_LONGHORN
record_type = dnsp . DNS_TYPE_A
select_flags = dnsserver . DNS_RPC_VIEW_AUTHORITY_DATA | \
dnsserver . DNS_RPC_VIEW_NO_CHILDREN
zone = ctx . dnsdomain
msdcs_zone = " _msdcs. %s " % ctx . dnsforest
name = ctx . myname
msdcs_cname = str ( ctx . ntds_guid )
cname_target = " %s . %s " % ( name , zone )
IPs = samba . interface_ips ( ctx . lp , ctx . force_all_ips )
ctx . logger . info ( " Adding %d remote DNS records for %s . %s " % \
( len ( IPs ) , name , zone ) )
binding_options = " sign "
dns_conn = dnsserver . dnsserver ( " ncacn_ip_tcp: %s [ %s ] " % ( ctx . server , binding_options ) ,
ctx . lp , ctx . creds )
name_found = True
sd_helper = samba . sd_utils . SDUtils ( ctx . samdb )
change_owner_sd = security . descriptor ( )
change_owner_sd . owner_sid = ctx . new_dc_account_sid
change_owner_sd . group_sid = security . dom_sid ( " %s - %d " %
( str ( ctx . domsid ) ,
security . DOMAIN_RID_DCS ) )
# TODO: Remove any old records from the primary DNS name
try :
( buflen , res ) \
= dns_conn . DnssrvEnumRecords2 ( client_version ,
0 ,
ctx . server ,
zone ,
name ,
None ,
dnsp . DNS_TYPE_ALL ,
select_flags ,
None ,
None )
except WERRORError as e :
if e . args [ 0 ] == werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST :
name_found = False
pass
if name_found :
for rec in res . rec :
for record in rec . records :
if record . wType == dnsp . DNS_TYPE_A or \
record . wType == dnsp . DNS_TYPE_AAAA :
# delete record
del_rec_buf = dnsserver . DNS_RPC_RECORD_BUF ( )
del_rec_buf . rec = record
try :
dns_conn . DnssrvUpdateRecord2 ( client_version ,
0 ,
ctx . server ,
zone ,
name ,
None ,
del_rec_buf )
except WERRORError as e :
if e . args [ 0 ] == werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST :
pass
else :
raise
for IP in IPs :
if IP . find ( ' : ' ) != - 1 :
ctx . logger . info ( " Adding DNS AAAA record %s . %s for IPv6 IP: %s "
% ( name , zone , IP ) )
rec = AAAARecord ( IP )
else :
ctx . logger . info ( " Adding DNS A record %s . %s for IPv4 IP: %s "
% ( name , zone , IP ) )
rec = ARecord ( IP )
# Add record
add_rec_buf = dnsserver . DNS_RPC_RECORD_BUF ( )
add_rec_buf . rec = rec
dns_conn . DnssrvUpdateRecord2 ( client_version ,
0 ,
ctx . server ,
zone ,
name ,
add_rec_buf ,
None )
if ( len ( IPs ) > 0 ) :
domaindns_zone_dn = ldb . Dn ( ctx . samdb , ctx . domaindns_zone )
( ctx . dns_a_dn , ldap_record ) \
= ctx . samdb . dns_lookup ( " %s . %s " % ( name , zone ) ,
dns_partition = domaindns_zone_dn )
# Make the DC own the DNS record, not the administrator
sd_helper . modify_sd_on_dn ( ctx . dns_a_dn , change_owner_sd ,
controls = [ " sd_flags:1: %d "
% ( security . SECINFO_OWNER
| security . SECINFO_GROUP ) ] )
# Add record
ctx . logger . info ( " Adding DNS CNAME record %s . %s for %s "
% ( msdcs_cname , msdcs_zone , cname_target ) )
add_rec_buf = dnsserver . DNS_RPC_RECORD_BUF ( )
rec = CNameRecord ( cname_target )
add_rec_buf . rec = rec
dns_conn . DnssrvUpdateRecord2 ( client_version ,
0 ,
ctx . server ,
msdcs_zone ,
msdcs_cname ,
add_rec_buf ,
None )
forestdns_zone_dn = ldb . Dn ( ctx . samdb , ctx . forestdns_zone )
( ctx . dns_cname_dn , ldap_record ) \
= ctx . samdb . dns_lookup ( " %s . %s " % ( msdcs_cname , msdcs_zone ) ,
dns_partition = forestdns_zone_dn )
# Make the DC own the DNS record, not the administrator
sd_helper . modify_sd_on_dn ( ctx . dns_cname_dn , change_owner_sd ,
controls = [ " sd_flags:1: %d "
% ( security . SECINFO_OWNER
| security . SECINFO_GROUP ) ] )
ctx . logger . info ( " All other DNS records (like _ldap SRV records) " +
" will be created samba_dnsupdate on first startup " )
def join_replicate_new_dns_records ( ctx ) :
for nc in ( ctx . domaindns_zone , ctx . forestdns_zone ) :
if nc in ctx . nc_list :
ctx . logger . info ( " Replicating new DNS records in %s " % ( str ( nc ) ) )
ctx . repl . replicate ( nc , ctx . source_dsa_invocation_id ,
ctx . ntds_guid , rodc = ctx . RODC ,
replica_flags = ctx . replica_flags ,
full_sync = False )
2010-08-25 06:34:15 +04:00
def join_finalise ( ctx ) :
2012-09-16 16:18:51 +04:00
""" Finalise the join, mark us synchronised and setup secrets db. """
2010-08-25 06:34:15 +04:00
2012-10-08 08:52:25 +04:00
# FIXME we shouldn't do this in all cases
2015-08-17 06:33:31 +03:00
2012-10-08 08:52:25 +04:00
# If for some reasons we joined in another site than the one of
# DC we just replicated from then we don't need to send the updatereplicateref
# as replication between sites is time based and on the initiative of the
# requesting DC
2015-08-17 06:33:31 +03:00
if not ctx . clone_only :
ctx . logger . info ( " Sending DsReplicaUpdateRefs for all the replicated partitions " )
for nc in ctx . nc_list :
ctx . send_DsReplicaUpdateRefs ( nc )
2011-10-25 22:16:38 +04:00
2015-08-17 06:33:31 +03:00
if not ctx . clone_only and ctx . RODC :
2012-08-03 14:47:11 +04:00
print " Setting RODC invocationId "
ctx . local_samdb . set_invocation_id ( str ( ctx . invocation_id ) )
ctx . local_samdb . set_opaque_integer ( " domainFunctionality " ,
ctx . behavior_version )
m = ldb . Message ( )
m . dn = ldb . Dn ( ctx . local_samdb , " %s " % ctx . ntds_dn )
m [ " invocationId " ] = ldb . MessageElement ( ndr_pack ( ctx . invocation_id ) ,
ldb . FLAG_MOD_REPLACE ,
" invocationId " )
ctx . local_samdb . modify ( m )
# Note: as RODC the invocationId is only stored
# on the RODC itself, the other DCs never see it.
#
# Thats is why we fix up the replPropertyMetaData stamp
# for the 'invocationId' attribute, we need to change
# the 'version' to '0', this is what windows 2008r2 does as RODC
#
# This means if the object on a RWDC ever gets a invocationId
# attribute, it will have version '1' (or higher), which will
# will overwrite the RODC local value.
ctx . local_samdb . set_attribute_replmetadata_version ( m . dn ,
" invocationId " ,
0 )
2013-09-09 01:53:37 +04:00
ctx . logger . info ( " Setting isSynchronized and dsServiceName " )
2010-08-25 06:34:15 +04:00
m = ldb . Message ( )
2011-08-11 10:05:11 +04:00
m . dn = ldb . Dn ( ctx . local_samdb , ' @ROOTDSE ' )
2010-08-25 06:34:15 +04:00
m [ " isSynchronized " ] = ldb . MessageElement ( " TRUE " , ldb . FLAG_MOD_REPLACE , " isSynchronized " )
2015-08-17 06:33:31 +03:00
# We want to appear to be the server we just cloned
if ctx . clone_only :
2016-03-27 07:29:35 +03:00
guid = ctx . remote_dc_ntds_guid
2015-08-17 06:33:31 +03:00
else :
guid = ctx . ntds_guid
m [ " dsServiceName " ] = ldb . MessageElement ( " <GUID= %s > " % str ( guid ) ,
2011-08-11 10:05:11 +04:00
ldb . FLAG_MOD_REPLACE , " dsServiceName " )
ctx . local_samdb . modify ( m )
2010-08-25 06:34:15 +04:00
2015-08-17 06:33:31 +03:00
if ctx . clone_only or ctx . subdomain :
2011-08-26 07:23:41 +04:00
return
2010-08-25 06:34:15 +04:00
secrets_ldb = Ldb ( ctx . paths . secrets , session_info = system_session ( ) , lp = ctx . lp )
2013-09-09 01:53:37 +04:00
ctx . logger . info ( " Setting up secrets database " )
2010-08-25 06:34:15 +04:00
secretsdb_self_join ( secrets_ldb , domain = ctx . domain_name ,
realm = ctx . realm ,
dnsdomain = ctx . dnsdomain ,
netbiosname = ctx . myname ,
2014-08-11 03:23:57 +04:00
domainsid = ctx . domsid ,
2010-08-25 06:34:15 +04:00
machinepass = ctx . acct_pass ,
2010-11-05 11:09:45 +03:00
secure_channel_type = ctx . secure_channel_type ,
2010-09-30 23:44:39 +04:00
key_version_number = ctx . key_version_number )
2010-08-25 06:34:15 +04:00
2012-06-24 15:10:34 +04:00
if ctx . dns_backend . startswith ( " BIND9_ " ) :
2014-08-08 10:43:47 +04:00
setup_bind9_dns ( ctx . local_samdb , secrets_ldb ,
2013-09-09 01:53:37 +04:00
ctx . names , ctx . paths , ctx . lp , ctx . logger ,
2012-06-24 15:10:34 +04:00
dns_backend = ctx . dns_backend ,
2012-12-24 01:56:50 +04:00
dnspass = ctx . dnspass , os_level = ctx . behavior_version ,
targetdir = ctx . targetdir ,
key_version_number = ctx . dns_key_version_number )
2012-06-24 15:10:34 +04:00
2011-08-26 07:23:41 +04:00
def join_setup_trusts ( ctx ) :
2012-09-16 16:18:51 +04:00
""" provision the local SAM. """
2011-08-26 07:23:41 +04:00
print " Setup domain trusts with server %s " % ctx . server
2011-11-14 00:13:59 +04:00
binding_options = " " # why doesn't signing work here? w2k8r2 claims no session key
2011-08-26 07:23:41 +04:00
lsaconn = lsa . lsarpc ( " ncacn_np: %s [ %s ] " % ( ctx . server , binding_options ) ,
ctx . lp , ctx . creds )
objectAttr = lsa . ObjectAttribute ( )
objectAttr . sec_qos = lsa . QosInfo ( )
pol_handle = lsaconn . OpenPolicy2 ( ' ' . decode ( ' utf-8 ' ) ,
objectAttr , security . SEC_FLAG_MAXIMUM_ALLOWED )
info = lsa . TrustDomainInfoInfoEx ( )
info . domain_name . string = ctx . dnsdomain
info . netbios_name . string = ctx . domain_name
2014-08-11 03:23:57 +04:00
info . sid = ctx . domsid
2011-08-26 07:23:41 +04:00
info . trust_direction = lsa . LSA_TRUST_DIRECTION_INBOUND | lsa . LSA_TRUST_DIRECTION_OUTBOUND
info . trust_type = lsa . LSA_TRUST_TYPE_UPLEVEL
info . trust_attributes = lsa . LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
try :
oldname = lsa . String ( )
oldname . string = ctx . dnsdomain
oldinfo = lsaconn . QueryTrustedDomainInfoByName ( pol_handle , oldname ,
lsa . LSA_TRUSTED_DOMAIN_INFO_FULL_INFO )
print ( " Removing old trust record for %s (SID %s ) " % ( ctx . dnsdomain , oldinfo . info_ex . sid ) )
lsaconn . DeleteTrustedDomain ( pol_handle , oldinfo . info_ex . sid )
except RuntimeError :
pass
2016-01-28 16:00:38 +03:00
password_blob = string_to_byte_array ( ctx . trustdom_pass . encode ( ' utf-16-le ' ) )
2011-08-26 07:23:41 +04:00
clear_value = drsblobs . AuthInfoClear ( )
clear_value . size = len ( password_blob )
clear_value . password = password_blob
clear_authentication_information = drsblobs . AuthenticationInformation ( )
2011-09-07 11:22:49 +04:00
clear_authentication_information . LastUpdateTime = samba . unix2nttime ( int ( time . time ( ) ) )
2011-08-26 07:23:41 +04:00
clear_authentication_information . AuthType = lsa . TRUST_AUTH_TYPE_CLEAR
clear_authentication_information . AuthInfo = clear_value
authentication_information_array = drsblobs . AuthenticationInformationArray ( )
2011-09-07 11:22:49 +04:00
authentication_information_array . count = 1
authentication_information_array . array = [ clear_authentication_information ]
2011-08-26 07:23:41 +04:00
outgoing = drsblobs . trustAuthInOutBlob ( )
outgoing . count = 1
outgoing . current = authentication_information_array
trustpass = drsblobs . trustDomainPasswords ( )
confounder = [ 3 ] * 512
for i in range ( 512 ) :
confounder [ i ] = random . randint ( 0 , 255 )
trustpass . confounder = confounder
trustpass . outgoing = outgoing
trustpass . incoming = outgoing
trustpass_blob = ndr_pack ( trustpass )
encrypted_trustpass = arcfour_encrypt ( lsaconn . session_key , trustpass_blob )
auth_blob = lsa . DATA_BUF2 ( )
auth_blob . size = len ( encrypted_trustpass )
2016-01-28 16:00:38 +03:00
auth_blob . data = string_to_byte_array ( encrypted_trustpass )
2011-08-26 07:23:41 +04:00
auth_info = lsa . TrustDomainInfoAuthInfoInternal ( )
auth_info . auth_blob = auth_blob
trustdom_handle = lsaconn . CreateTrustedDomainEx2 ( pol_handle ,
info ,
auth_info ,
security . SEC_STD_DELETE )
rec = {
2011-10-01 04:58:52 +04:00
" dn " : " cn= %s ,cn=system, %s " % ( ctx . dnsforest , ctx . base_dn ) ,
2011-08-26 07:23:41 +04:00
" objectclass " : " trustedDomain " ,
" trustType " : str ( info . trust_type ) ,
" trustAttributes " : str ( info . trust_attributes ) ,
" trustDirection " : str ( info . trust_direction ) ,
2011-10-01 04:58:52 +04:00
" flatname " : ctx . forest_domain_name ,
" trustPartner " : ctx . dnsforest ,
2011-08-26 07:23:41 +04:00
" trustAuthIncoming " : ndr_pack ( outgoing ) ,
2014-08-11 03:46:51 +04:00
" trustAuthOutgoing " : ndr_pack ( outgoing ) ,
" securityIdentifier " : ndr_pack ( ctx . forestsid )
2011-08-26 07:23:41 +04:00
}
ctx . local_samdb . add ( rec )
rec = {
2011-10-01 04:58:52 +04:00
" dn " : " cn= %s $,cn=users, %s " % ( ctx . forest_domain_name , ctx . base_dn ) ,
2011-08-26 07:23:41 +04:00
" objectclass " : " user " ,
" userAccountControl " : str ( samba . dsdb . UF_INTERDOMAIN_TRUST_ACCOUNT ) ,
2014-08-22 09:49:06 +04:00
" clearTextPassword " : ctx . trustdom_pass . encode ( ' utf-16-le ' ) ,
" samAccountName " : " %s $ " % ctx . forest_domain_name
2011-08-26 07:23:41 +04:00
}
ctx . local_samdb . add ( rec )
2010-11-05 06:09:49 +03:00
def do_join ( ctx ) :
2013-10-16 05:34:43 +04:00
# nc_list is the list of naming context (NC) for which we will
# replicate in and send a updateRef command to the partner DC
# full_nc_list is the list of naming context (NC) we hold
# read/write copies of. These are not subsets of each other.
2012-06-21 17:46:21 +04:00
ctx . nc_list = [ ctx . config_dn , ctx . schema_dn ]
2013-10-16 05:34:43 +04:00
ctx . full_nc_list = [ ctx . base_dn , ctx . config_dn , ctx . schema_dn ]
2012-06-21 17:46:21 +04:00
2013-10-16 05:34:43 +04:00
if ctx . subdomain and ctx . dns_backend != " NONE " :
ctx . full_nc_list + = [ ctx . domaindns_zone ]
elif not ctx . subdomain :
2012-06-21 17:46:21 +04:00
ctx . nc_list + = [ ctx . base_dn ]
2013-10-16 05:34:43 +04:00
2012-06-21 17:46:21 +04:00
if ctx . dns_backend != " NONE " :
2012-09-06 04:37:18 +04:00
ctx . nc_list + = [ ctx . domaindns_zone ]
2013-10-16 05:34:43 +04:00
ctx . nc_list + = [ ctx . forestdns_zone ]
ctx . full_nc_list + = [ ctx . domaindns_zone ]
ctx . full_nc_list + = [ ctx . forestdns_zone ]
2012-06-21 17:46:21 +04:00
2015-08-17 06:33:31 +03:00
if not ctx . clone_only :
if ctx . promote_existing :
ctx . promote_possible ( )
else :
ctx . cleanup_old_join ( )
2012-09-16 16:18:51 +04:00
2010-11-05 06:09:49 +03:00
try :
2015-08-17 06:33:31 +03:00
if not ctx . clone_only :
ctx . join_add_objects ( )
2010-11-05 06:09:49 +03:00
ctx . join_provision ( )
2011-09-02 09:12:11 +04:00
ctx . join_replicate ( )
2015-08-17 06:33:31 +03:00
if ( not ctx . clone_only and ctx . subdomain ) :
2011-08-24 09:39:51 +04:00
ctx . join_add_objects2 ( )
2011-08-24 09:39:51 +04:00
ctx . join_provision_own_domain ( )
2011-08-26 07:23:41 +04:00
ctx . join_setup_trusts ( )
2017-02-17 08:23:23 +03:00
if not ctx . clone_only and ctx . dns_backend != " NONE " :
ctx . join_add_dns_records ( )
ctx . join_replicate_new_dns_records ( )
2011-08-26 07:23:41 +04:00
ctx . join_finalise ( )
2012-02-25 18:56:25 +04:00
except :
2016-05-05 00:39:58 +03:00
try :
print " Join failed - cleaning up "
except IOError :
pass
2015-08-17 06:33:31 +03:00
if not ctx . clone_only :
ctx . cleanup_old_join ( )
2010-11-05 06:09:49 +03:00
raise
2010-08-25 06:34:15 +04:00
2013-09-09 01:53:37 +04:00
def join_RODC ( logger = None , server = None , creds = None , lp = None , site = None , netbios_name = None ,
2011-11-28 23:03:11 +04:00
targetdir = None , domain = None , domain_critical_only = False ,
2012-07-06 09:38:06 +04:00
machinepass = None , use_ntvfs = False , dns_backend = None ,
2017-12-14 21:24:14 +03:00
promote_existing = False , plaintext_secrets = False ) :
2012-09-16 16:18:51 +04:00
""" Join as a RODC. """
2010-09-15 12:52:11 +04:00
2013-09-09 01:53:37 +04:00
ctx = dc_join ( logger , server , creds , lp , site , netbios_name , targetdir , domain ,
2017-12-14 21:24:14 +03:00
machinepass , use_ntvfs , dns_backend , promote_existing ,
plaintext_secrets )
2010-08-23 05:31:48 +04:00
2011-08-24 09:39:51 +04:00
lp . set ( " workgroup " , ctx . domain_name )
2013-09-09 01:53:37 +04:00
logger . info ( " workgroup is %s " % ctx . domain_name )
2011-08-24 09:39:51 +04:00
lp . set ( " realm " , ctx . realm )
2013-09-09 01:53:37 +04:00
logger . info ( " realm is %s " % ctx . realm )
2011-08-24 09:39:51 +04:00
2010-08-24 09:42:54 +04:00
ctx . krbtgt_dn = " CN=krbtgt_ %s ,CN=Users, %s " % ( ctx . myname , ctx . base_dn )
2010-08-23 05:31:48 +04:00
2010-08-24 17:37:25 +04:00
# setup some defaults for accounts that should be replicated to this RODC
2012-09-16 16:18:51 +04:00
ctx . never_reveal_sid = [
" <SID= %s - %s > " % ( ctx . domsid , security . DOMAIN_RID_RODC_DENY ) ,
" <SID= %s > " % security . SID_BUILTIN_ADMINISTRATORS ,
" <SID= %s > " % security . SID_BUILTIN_SERVER_OPERATORS ,
" <SID= %s > " % security . SID_BUILTIN_BACKUP_OPERATORS ,
" <SID= %s > " % security . SID_BUILTIN_ACCOUNT_OPERATORS ]
2010-08-25 06:34:15 +04:00
ctx . reveal_sid = " <SID= %s - %s > " % ( ctx . domsid , security . DOMAIN_RID_RODC_ALLOW )
2010-08-23 05:31:48 +04:00
2010-11-07 05:55:20 +03:00
mysid = ctx . get_mysid ( )
admin_dn = " <SID= %s > " % mysid
ctx . managedby = admin_dn
2010-08-23 05:31:48 +04:00
2010-11-05 06:09:49 +03:00
ctx . userAccountControl = ( samba . dsdb . UF_WORKSTATION_TRUST_ACCOUNT |
samba . dsdb . UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION |
samba . dsdb . UF_PARTIAL_SECRETS_ACCOUNT )
2010-08-23 05:31:48 +04:00
2010-11-05 06:09:49 +03:00
ctx . SPNs . extend ( [ " RestrictedKrbHost/ %s " % ctx . myname ,
" RestrictedKrbHost/ %s " % ctx . dnshostname ] )
2010-08-24 17:37:25 +04:00
2010-11-05 06:09:49 +03:00
ctx . connection_dn = " CN=RODC Connection (FRS), %s " % ctx . ntds_dn
2010-11-05 11:09:45 +03:00
ctx . secure_channel_type = misc . SEC_CHAN_RODC
2010-11-05 06:09:49 +03:00
ctx . RODC = True
2016-11-29 16:27:57 +03:00
ctx . replica_flags | = ( drsuapi . DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
2010-11-18 10:48:42 +03:00
drsuapi . DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP )
2011-08-08 13:01:21 +04:00
ctx . domain_replica_flags = ctx . replica_flags
if domain_critical_only :
ctx . domain_replica_flags | = drsuapi . DRSUAPI_DRS_CRITICAL_ONLY
2010-11-05 06:09:49 +03:00
ctx . do_join ( )
2010-11-07 05:55:20 +03:00
2013-09-09 01:53:37 +04:00
logger . info ( " Joined domain %s (SID %s ) as an RODC " % ( ctx . domain_name , ctx . domsid ) )
2010-08-23 05:31:48 +04:00
2010-11-05 06:09:49 +03:00
2013-09-09 01:53:37 +04:00
def join_DC ( logger = None , server = None , creds = None , lp = None , site = None , netbios_name = None ,
2011-11-28 23:03:11 +04:00
targetdir = None , domain = None , domain_critical_only = False ,
2012-07-06 09:38:06 +04:00
machinepass = None , use_ntvfs = False , dns_backend = None ,
2017-12-14 21:24:14 +03:00
promote_existing = False , plaintext_secrets = False ) :
2012-09-16 16:18:51 +04:00
""" Join as a DC. """
2013-09-09 01:53:37 +04:00
ctx = dc_join ( logger , server , creds , lp , site , netbios_name , targetdir , domain ,
2017-12-14 21:24:14 +03:00
machinepass , use_ntvfs , dns_backend , promote_existing ,
plaintext_secrets )
2010-11-05 06:09:49 +03:00
2011-08-24 09:39:51 +04:00
lp . set ( " workgroup " , ctx . domain_name )
2013-09-09 01:53:37 +04:00
logger . info ( " workgroup is %s " % ctx . domain_name )
2011-08-24 09:39:51 +04:00
lp . set ( " realm " , ctx . realm )
2013-09-09 01:53:37 +04:00
logger . info ( " realm is %s " % ctx . realm )
2011-08-24 09:39:51 +04:00
2010-11-05 06:09:49 +03:00
ctx . userAccountControl = samba . dsdb . UF_SERVER_TRUST_ACCOUNT | samba . dsdb . UF_TRUSTED_FOR_DELEGATION
ctx . SPNs . append ( ' E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/ %s ' % ctx . dnsdomain )
2010-11-05 11:09:45 +03:00
ctx . secure_channel_type = misc . SEC_CHAN_BDC
2010-11-05 06:09:49 +03:00
2016-11-29 16:27:57 +03:00
ctx . replica_flags | = ( drsuapi . DRSUAPI_DRS_WRIT_REP |
drsuapi . DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS )
2011-08-08 13:01:21 +04:00
ctx . domain_replica_flags = ctx . replica_flags
if domain_critical_only :
ctx . domain_replica_flags | = drsuapi . DRSUAPI_DRS_CRITICAL_ONLY
2010-11-07 05:55:20 +03:00
2010-11-05 06:09:49 +03:00
ctx . do_join ( )
2013-09-09 01:53:37 +04:00
logger . info ( " Joined domain %s (SID %s ) as a DC " % ( ctx . domain_name , ctx . domsid ) )
2011-08-24 09:39:51 +04:00
2015-08-17 06:33:31 +03:00
def join_clone ( logger = None , server = None , creds = None , lp = None ,
2015-08-19 04:29:35 +03:00
targetdir = None , domain = None , include_secrets = False ) :
2015-08-17 06:33:31 +03:00
""" Join as a DC. """
ctx = dc_join ( logger , server , creds , lp , site = None , netbios_name = None , targetdir = targetdir , domain = domain ,
machinepass = None , use_ntvfs = False , dns_backend = " NONE " , promote_existing = False , clone_only = True )
lp . set ( " workgroup " , ctx . domain_name )
logger . info ( " workgroup is %s " % ctx . domain_name )
lp . set ( " realm " , ctx . realm )
logger . info ( " realm is %s " % ctx . realm )
2016-11-29 16:27:57 +03:00
ctx . replica_flags | = ( drsuapi . DRSUAPI_DRS_WRIT_REP |
drsuapi . DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS )
2015-08-19 04:29:35 +03:00
if not include_secrets :
ctx . replica_flags | = drsuapi . DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
2015-08-17 06:33:31 +03:00
ctx . domain_replica_flags = ctx . replica_flags
ctx . do_join ( )
logger . info ( " Cloned domain %s (SID %s ) " % ( ctx . domain_name , ctx . domsid ) )
2013-09-09 01:53:37 +04:00
def join_subdomain ( logger = None , server = None , creds = None , lp = None , site = None ,
2012-09-16 16:18:51 +04:00
netbios_name = None , targetdir = None , parent_domain = None , dnsdomain = None ,
2013-09-06 07:46:05 +04:00
netbios_domain = None , machinepass = None , adminpass = None , use_ntvfs = False ,
2017-12-14 21:24:14 +03:00
dns_backend = None , plaintext_secrets = False ) :
2012-09-16 16:18:51 +04:00
""" Join as a DC. """
2013-09-09 01:53:37 +04:00
ctx = dc_join ( logger , server , creds , lp , site , netbios_name , targetdir , parent_domain ,
2017-12-14 21:24:14 +03:00
machinepass , use_ntvfs , dns_backend , plaintext_secrets )
2011-08-24 09:39:51 +04:00
ctx . subdomain = True
2013-09-06 07:46:05 +04:00
if adminpass is None :
ctx . adminpass = samba . generate_random_password ( 12 , 32 )
else :
ctx . adminpass = adminpass
2011-08-26 07:23:41 +04:00
ctx . parent_domain_name = ctx . domain_name
2011-08-24 09:39:51 +04:00
ctx . domain_name = netbios_domain
ctx . realm = dnsdomain
2011-08-26 07:23:41 +04:00
ctx . parent_dnsdomain = ctx . dnsdomain
ctx . parent_partition_dn = ctx . get_parent_partition_dn ( )
2011-08-24 09:39:51 +04:00
ctx . dnsdomain = dnsdomain
2011-08-26 10:07:05 +04:00
ctx . partition_dn = " CN= %s ,CN=Partitions, %s " % ( ctx . domain_name , ctx . config_dn )
2011-10-01 04:58:52 +04:00
ctx . naming_master = ctx . get_naming_master ( )
if ctx . naming_master != ctx . server :
2013-09-09 01:53:37 +04:00
logger . info ( " Reconnecting to naming master %s " % ctx . naming_master )
2011-10-01 04:58:52 +04:00
ctx . server = ctx . naming_master
ctx . samdb = SamDB ( url = " ldap:// %s " % ctx . server ,
session_info = system_session ( ) ,
credentials = ctx . creds , lp = ctx . lp )
2013-09-26 04:09:30 +04:00
res = ctx . samdb . search ( base = " " , scope = ldb . SCOPE_BASE , attrs = [ ' dnsHostName ' ] ,
controls = [ ] )
ctx . server = res [ 0 ] [ " dnsHostName " ]
logger . info ( " DNS name of new naming master is %s " % ctx . server )
2011-10-01 04:58:52 +04:00
2011-08-24 09:39:51 +04:00
ctx . base_dn = samba . dn_from_dns_name ( dnsdomain )
2014-08-08 10:43:47 +04:00
ctx . forestsid = ctx . domsid
2014-08-11 03:23:57 +04:00
ctx . domsid = security . random_sid ( )
2011-08-24 09:39:51 +04:00
ctx . acct_dn = None
2014-08-19 12:33:11 +04:00
ctx . dnshostname = " %s . %s " % ( ctx . myname . lower ( ) , ctx . dnsdomain )
2016-08-23 13:27:19 +03:00
# Windows uses 240 bytes as UTF16 so we do
ctx . trustdom_pass = samba . generate_random_machine_password ( 120 , 120 )
2011-08-24 09:39:51 +04:00
ctx . userAccountControl = samba . dsdb . UF_SERVER_TRUST_ACCOUNT | samba . dsdb . UF_TRUSTED_FOR_DELEGATION
ctx . SPNs . append ( ' E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/ %s ' % ctx . dnsdomain )
ctx . secure_channel_type = misc . SEC_CHAN_BDC
2016-11-29 16:27:57 +03:00
ctx . replica_flags | = ( drsuapi . DRSUAPI_DRS_WRIT_REP |
drsuapi . DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS )
2011-08-24 09:39:51 +04:00
ctx . domain_replica_flags = ctx . replica_flags
ctx . do_join ( )
2013-09-09 01:53:37 +04:00
ctx . logger . info ( " Created domain %s (SID %s ) as a DC " % ( ctx . domain_name , ctx . domsid ) )