-#!/usr/bin/env python
-#
# python join code
# Copyright Andrew Tridgell 2010
# Copyright Andrew Bartlett 2010
from samba.provision import secretsdb_self_join, provision, provision_fill, FILL_DRS, FILL_SUBDOMAIN
from samba.schema import Schema
from samba.net import Net
+from samba.provision.sambadns import setup_bind9_dns
import logging
import talloc
import random
'''perform a DC join'''
def __init__(ctx, server=None, creds=None, lp=None, site=None,
- netbios_name=None, targetdir=None, domain=None):
+ netbios_name=None, targetdir=None, domain=None,
+ machinepass=None, use_ntvfs=False, dns_backend=None):
ctx.creds = creds
ctx.lp = lp
ctx.site = site
ctx.netbios_name = netbios_name
ctx.targetdir = targetdir
+ ctx.use_ntvfs = use_ntvfs
+ if dns_backend is None:
+ ctx.dns_backend = "NONE"
+ else:
+ ctx.dns_backend = dns_backend
+
+ ctx.nc_list = []
+ ctx.full_nc_list = []
ctx.creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
ctx.net = Net(creds=ctx.creds, lp=ctx.lp)
ctx.forest_domain_name = ctx.get_forest_domain_name()
ctx.invocation_id = misc.GUID(str(uuid.uuid4()))
- ctx.dc_ntds_dn = ctx.get_dsServiceName()
+ ctx.dc_ntds_dn = ctx.samdb.get_dsServiceName()
ctx.dc_dnsHostName = ctx.get_dnsHostName()
ctx.behavior_version = ctx.get_behavior_version()
- ctx.acct_pass = samba.generate_random_password(32, 40)
+ if machinepass is not None:
+ ctx.acct_pass = machinepass
+ else:
+ ctx.acct_pass = samba.generate_random_password(32, 40)
# work out the DNs of all the objects we will be adding
ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn)
'''remove any DNs from a previous join'''
try:
# find the krbtgt link
- print("checking samaccountname")
+ print("checking sAMAccountName")
if ctx.subdomain:
res = None
else:
res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
- expression='samAccountName=%s' % ldb.binary_encode(ctx.samname),
+ expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
attrs=["msDS-krbTgtLink"])
if res:
ctx.del_noerror(res[0].dn, recursive=True)
return ctx.cldap_ret.pdc_dns_name
- def get_dsServiceName(ctx):
- res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
- return res[0]["dsServiceName"][0]
-
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]:
nc_list = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
- rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
+ rec["msDS-Behavior-Version"] = str(samba.dsdb.DS_DOMAIN_FUNCTION_2008_R2)
if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
rec["msDS-HasDomainNCs"] = ctx.base_dn
if ctx.RODC:
rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
- rec["msDS-HasFullReplicaNCs"] = nc_list
+ rec["msDS-HasFullReplicaNCs"] = ctx.nc_list
rec["options"] = "37"
ctx.samdb.add(rec, ["rodc_join:1:1"])
else:
rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
rec["HasMasterNCs"] = nc_list
if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
- rec["msDS-HasMasterNCs"] = nc_list
+ rec["msDS-HasMasterNCs"] = ctx.nc_list
rec["options"] = "1"
rec["invocationId"] = ndr_pack(ctx.invocation_id)
ctx.DsAddEntry([rec])
"servicePrincipalName")
ctx.samdb.modify(m)
+ # 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.
print "Setting account password for %s" % ctx.samname
- ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))" % ldb.binary_encode(ctx.samname),
- ctx.acct_pass,
- force_change_at_next_login=False,
- username=ctx.samname)
- res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-keyVersionNumber"])
- ctx.key_version_number = int(res[0]["msDS-keyVersionNumber"][0])
+ 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,
+ newpassword=ctx.acct_pass)
+
+ res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE,
+ attrs=["msDS-KeyVersionNumber"])
+ if "msDS-KeyVersionNumber" in res[0]:
+ ctx.key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
+ else:
+ ctx.key_version_number = None
print("Enabling account")
m = ldb.Message()
rec2["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
rec2["HasMasterNCs"] = nc_list
if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
- rec2["msDS-HasMasterNCs"] = nc_list
+ rec2["msDS-HasMasterNCs"] = ctx.nc_list
rec2["options"] = "1"
rec2["invocationId"] = ndr_pack(ctx.invocation_id)
logger.addHandler(logging.StreamHandler(sys.stdout))
smbconf = ctx.lp.configfile
- presult = provision(logger, system_session(), None,
- smbconf=smbconf, 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,
- machinepass=ctx.acct_pass, serverrole="domain controller",
- sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
- dns_backend="NONE")
+ presult = provision(logger, system_session(), None, smbconf=smbconf,
+ 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,
+ machinepass=ctx.acct_pass, serverrole="domain controller",
+ sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
+ use_ntvfs=ctx.use_ntvfs, dns_backend=ctx.dns_backend)
print "Provision OK for domain DN %s" % presult.domaindn
ctx.local_samdb = presult.samdb
ctx.lp = presult.lp
targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
machinepass=ctx.acct_pass, serverrole="domain controller",
lp=ctx.lp, hostip=ctx.names.hostip, hostip6=ctx.names.hostip6,
- dns_backend="BIND9_DLZ")
+ dns_backend=ctx.dns_backend)
print("Provision OK for domain %s" % ctx.names.dnsdomain)
def join_replicate(ctx):
destination_dsa_guid, rodc=ctx.RODC,
replica_flags=ctx.replica_flags)
if not ctx.subdomain:
+ # 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"
+ ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | drsuapi.DRSUAPI_DRS_GET_ANC
+ repl.replicate(ctx.base_dn, source_dsa_invocation_id,
+ destination_dsa_guid, rodc=ctx.RODC,
+ replica_flags=ctx.domain_replica_flags)
+ ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | drsuapi.DRSUAPI_DRS_GET_ANC
+ else:
+ ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_GET_ANC
repl.replicate(ctx.base_dn, source_dsa_invocation_id,
destination_dsa_guid, rodc=ctx.RODC,
replica_flags=ctx.domain_replica_flags)
+
+ if 'DC=DomainDnsZones,%s' % ctx.base_dn in ctx.nc_list:
+ repl.replicate('DC=DomainDnsZones,%s' % ctx.base_dn, source_dsa_invocation_id,
+ destination_dsa_guid, rodc=ctx.RODC,
+ replica_flags=ctx.replica_flags)
+
+ if 'DC=ForestDnsZones,%s' % ctx.root_dn in ctx.nc_list:
+ repl.replicate('DC=ForestDnsZones,%s' % ctx.root_dn, source_dsa_invocation_id,
+ destination_dsa_guid, rodc=ctx.RODC,
+ replica_flags=ctx.replica_flags)
+
if ctx.RODC:
repl.replicate(ctx.acct_dn, source_dsa_invocation_id,
destination_dsa_guid,
def join_finalise(ctx):
'''finalise the join, mark us synchronised and setup secrets db'''
+ logger = logging.getLogger("provision")
+ logger.addHandler(logging.StreamHandler(sys.stdout))
+
print "Sending DsReplicateUpdateRefs for all the partitions"
- ctx.send_DsReplicaUpdateRefs(ctx.schema_dn)
- ctx.send_DsReplicaUpdateRefs(ctx.config_dn)
- ctx.send_DsReplicaUpdateRefs(ctx.base_dn)
+ for nc in ctx.full_nc_list:
+ ctx.send_DsReplicaUpdateRefs(nc)
print "Setting isSynchronized and dsServiceName"
m = ldb.Message()
secure_channel_type=ctx.secure_channel_type,
key_version_number=ctx.key_version_number)
+ if ctx.dns_backend.startswith("BIND9_"):
+ dnspass = samba.generate_random_password(128, 255)
+
+ setup_bind9_dns(ctx.local_samdb, secrets_ldb, security.dom_sid(ctx.domsid),
+ ctx.names, ctx.paths, ctx.lp, logger,
+ dns_backend=ctx.dns_backend,
+ dnspass=dnspass, os_level=ctx.behavior_version,
+ targetdir=ctx.targetdir)
+
def join_setup_trusts(ctx):
'''provision the local SAM'''
def do_join(ctx):
+ ctx.nc_list = [ ctx.config_dn, ctx.schema_dn ]
+ ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
+
+ if not ctx.subdomain:
+ ctx.nc_list += [ctx.base_dn]
+ if ctx.dns_backend != "NONE":
+ ctx.nc_list += ['DC=DomainDnsZones,%s' % ctx.base_dn]
+
+ if ctx.dns_backend != "NONE":
+ ctx.full_nc_list += ['DC=DomainDnsZones,%s' % ctx.base_dn]
+ ctx.full_nc_list += ['DC=ForestDnsZones,%s' % ctx.root_dn]
+ ctx.nc_list += ['DC=ForestDnsZones,%s' % ctx.root_dn]
+
+
ctx.cleanup_old_join()
try:
ctx.join_add_objects()
ctx.join_provision_own_domain()
ctx.join_setup_trusts()
ctx.join_finalise()
- except Exception:
+ except:
print "Join failed - cleaning up"
ctx.cleanup_old_join()
raise
def join_RODC(server=None, creds=None, lp=None, site=None, netbios_name=None,
- targetdir=None, domain=None, domain_critical_only=False):
+ targetdir=None, domain=None, domain_critical_only=False,
+ machinepass=None, use_ntvfs=False, dns_backend=None):
"""join as a RODC"""
- ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain)
+ ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain,
+ machinepass, use_ntvfs, dns_backend)
lp.set("workgroup", ctx.domain_name)
print("workgroup is %s" % ctx.domain_name)
def join_DC(server=None, creds=None, lp=None, site=None, netbios_name=None,
- targetdir=None, domain=None, domain_critical_only=False):
+ targetdir=None, domain=None, domain_critical_only=False,
+ machinepass=None, use_ntvfs=False, dns_backend=None):
"""join as a DC"""
- ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain)
+ ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain,
+ machinepass, use_ntvfs, dns_backend)
lp.set("workgroup", ctx.domain_name)
print("workgroup is %s" % ctx.domain_name)
print "Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid)
def join_subdomain(server=None, creds=None, lp=None, site=None, netbios_name=None,
- targetdir=None, parent_domain=None, dnsdomain=None, netbios_domain=None):
+ targetdir=None, parent_domain=None, dnsdomain=None, netbios_domain=None,
+ machinepass=None, use_ntvfs=False, dns_backend=None):
"""join as a DC"""
- ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, parent_domain)
+ ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, parent_domain,
+ machinepass, use_ntvfs, dns_backend)
ctx.subdomain = True
ctx.parent_domain_name = ctx.domain_name
ctx.domain_name = netbios_domain