s4-provision Add initial support for joining as a new subdomain
[mdw/samba.git] / source4 / scripting / python / samba / join.py
1 #!/usr/bin/env python
2 #
3 # python join code
4 # Copyright Andrew Tridgell 2010
5 # Copyright Andrew Bartlett 2010
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 """Joining a domain."""
22
23 from samba.auth import system_session
24 from samba.samdb import SamDB
25 from samba import gensec, Ldb, drs_utils
26 import ldb, samba, sys, os, uuid
27 from samba.ndr import ndr_pack
28 from samba.dcerpc import security, drsuapi, misc, nbt
29 from samba.credentials import Credentials, DONT_USE_KERBEROS
30 from samba.provision import secretsdb_self_join, provision, provision_fill, FILL_DRS, FILL_SUBDOMAIN
31 from samba.schema import Schema
32 from samba.net import Net
33 from samba.dcerpc import security
34 import logging
35 import talloc
36
37 # this makes debugging easier
38 talloc.enable_null_tracking()
39
40 class DCJoinException(Exception):
41
42     def __init__(self, msg):
43         super(DCJoinException, self).__init__("Can't join, error: %s" % msg)
44
45
46 class dc_join(object):
47     '''perform a DC join'''
48
49     def __init__(ctx, server=None, creds=None, lp=None, site=None,
50             netbios_name=None, targetdir=None, domain=None):
51         ctx.creds = creds
52         ctx.lp = lp
53         ctx.site = site
54         ctx.netbios_name = netbios_name
55         ctx.targetdir = targetdir
56
57         ctx.creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
58         ctx.net = Net(creds=ctx.creds, lp=ctx.lp)
59
60         if server is not None:
61             ctx.server = server
62         else:
63             print("Finding a writeable DC for domain '%s'" % domain)
64             ctx.server = ctx.find_dc(domain)
65             print("Found DC %s" % ctx.server)
66
67         ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
68                           session_info=system_session(),
69                           credentials=ctx.creds, lp=ctx.lp)
70
71         try:
72             ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
73         except ldb.LdbError, (enum, estr):
74             raise DCJoinException(estr)
75
76
77         ctx.myname = netbios_name
78         ctx.samname = "%s$" % ctx.myname
79         ctx.base_dn = str(ctx.samdb.get_default_basedn())
80         ctx.root_dn = str(ctx.samdb.get_root_basedn())
81         ctx.schema_dn = str(ctx.samdb.get_schema_basedn())
82         ctx.config_dn = str(ctx.samdb.get_config_basedn())
83         ctx.domsid = ctx.samdb.get_domain_sid()
84         ctx.domain_name = ctx.get_domain_name()
85
86         ctx.dc_ntds_dn = ctx.get_dsServiceName()
87         ctx.dc_dnsHostName = ctx.get_dnsHostName()
88         ctx.behavior_version = ctx.get_behavior_version()
89
90         ctx.acct_pass = samba.generate_random_password(32, 40)
91
92         # work out the DNs of all the objects we will be adding
93         ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn)
94         ctx.ntds_dn = "CN=NTDS Settings,%s" % ctx.server_dn
95         topology_base = "CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % ctx.base_dn
96         if ctx.dn_exists(topology_base):
97             ctx.topology_dn = "CN=%s,%s" % (ctx.myname, topology_base)
98         else:
99             ctx.topology_dn = None
100
101         ctx.dnsdomain = ctx.samdb.domain_dns_name()
102         ctx.dnsforest = ctx.samdb.forest_dns_name()
103         ctx.dnshostname = "%s.%s" % (ctx.myname, ctx.dnsdomain)
104
105         ctx.realm = ctx.dnsdomain
106
107         ctx.acct_dn = "CN=%s,OU=Domain Controllers,%s" % (ctx.myname, ctx.base_dn)
108
109         ctx.tmp_samdb = None
110
111         ctx.SPNs = [ "HOST/%s" % ctx.myname,
112                      "HOST/%s" % ctx.dnshostname,
113                      "GC/%s/%s" % (ctx.dnshostname, ctx.dnsforest) ]
114
115         # these elements are optional
116         ctx.never_reveal_sid = None
117         ctx.reveal_sid = None
118         ctx.connection_dn = None
119         ctx.RODC = False
120         ctx.krbtgt_dn = None
121         ctx.drsuapi = None
122         ctx.managedby = None
123
124
125     def del_noerror(ctx, dn, recursive=False):
126         if recursive:
127             try:
128                 res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
129             except Exception:
130                 return
131             for r in res:
132                 ctx.del_noerror(r.dn, recursive=True)
133         try:
134             ctx.samdb.delete(dn)
135             print "Deleted %s" % dn
136         except Exception:
137             pass
138
139     def cleanup_old_join(ctx):
140         '''remove any DNs from a previous join'''
141         try:
142             # find the krbtgt link
143             print("checking samaccountname")
144             res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
145                                    expression='samAccountName=%s' % ldb.binary_encode(ctx.samname),
146                                    attrs=["msDS-krbTgtLink"])
147             if res:
148                 ctx.del_noerror(res[0].dn, recursive=True)
149             if ctx.connection_dn is not None:
150                 ctx.del_noerror(ctx.connection_dn)
151             if ctx.krbtgt_dn is not None:
152                 ctx.del_noerror(ctx.krbtgt_dn)
153             ctx.del_noerror(ctx.ntds_dn)
154             ctx.del_noerror(ctx.server_dn, recursive=True)
155             if ctx.topology_dn:
156                 ctx.del_noerror(ctx.topology_dn)
157             if res:
158                 ctx.new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
159                 ctx.del_noerror(ctx.new_krbtgt_dn)
160         except Exception:
161             pass
162
163     def find_dc(ctx, domain):
164         '''find a writeable DC for the given domain'''
165         try:
166             ctx.cldap_ret = ctx.net.finddc(domain, nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE)
167         except Exception:
168             raise Exception("Failed to find a writeable DC for domain '%s'" % domain)
169         if ctx.cldap_ret.client_site is not None and ctx.cldap_ret.client_site != "":
170             ctx.site = ctx.cldap_ret.client_site
171         return ctx.cldap_ret.pdc_dns_name
172
173
174     def get_dsServiceName(ctx):
175         res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
176         return res[0]["dsServiceName"][0]
177
178     def get_behavior_version(ctx):
179         res = ctx.samdb.search(base=ctx.base_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
180         if "msDS-Behavior-Version" in res[0]:
181             return int(res[0]["msDS-Behavior-Version"][0])
182         else:
183             return samba.dsdb.DS_DOMAIN_FUNCTION_2000
184
185     def get_dnsHostName(ctx):
186         res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"])
187         return res[0]["dnsHostName"][0]
188
189     def get_domain_name(ctx):
190         '''get netbios name of the domain from the partitions record'''
191         partitions_dn = ctx.samdb.get_partitions_dn()
192         res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
193                                expression='ncName=%s' % ctx.samdb.get_default_basedn())
194         return res[0]["nETBIOSName"][0]
195
196     def get_mysid(ctx):
197         '''get the SID of the connected user. Only works with w2k8 and later,
198            so only used for RODC join'''
199         res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"])
200         binsid = res[0]["tokenGroups"][0]
201         return ctx.samdb.schema_format_value("objectSID", binsid)
202
203     def dn_exists(ctx, dn):
204         '''check if a DN exists'''
205         try:
206             res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[])
207         except ldb.LdbError, (enum, estr):
208             if enum == ldb.ERR_NO_SUCH_OBJECT:
209                 return False
210             raise
211         return True
212
213     def add_krbtgt_account(ctx):
214         '''RODCs need a special krbtgt account'''
215         print "Adding %s" % ctx.krbtgt_dn
216         rec = {
217             "dn" : ctx.krbtgt_dn,
218             "objectclass" : "user",
219             "useraccountcontrol" : str(samba.dsdb.UF_NORMAL_ACCOUNT |
220                                        samba.dsdb.UF_ACCOUNTDISABLE),
221             "showinadvancedviewonly" : "TRUE",
222             "description" : "krbtgt for %s" % ctx.samname}
223         ctx.samdb.add(rec, ["rodc_join:1:1"])
224
225         # now we need to search for the samAccountName attribute on the krbtgt DN,
226         # as this will have been magically set to the krbtgt number
227         res = ctx.samdb.search(base=ctx.krbtgt_dn, scope=ldb.SCOPE_BASE, attrs=["samAccountName"])
228         ctx.krbtgt_name = res[0]["samAccountName"][0]
229
230         print "Got krbtgt_name=%s" % ctx.krbtgt_name
231
232         m = ldb.Message()
233         m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
234         m["msDS-krbTgtLink"] = ldb.MessageElement(ctx.krbtgt_dn,
235                                                   ldb.FLAG_MOD_REPLACE, "msDS-krbTgtLink")
236         ctx.samdb.modify(m)
237
238         ctx.new_krbtgt_dn = "CN=%s,CN=Users,%s" % (ctx.krbtgt_name, ctx.base_dn)
239         print "Renaming %s to %s" % (ctx.krbtgt_dn, ctx.new_krbtgt_dn)
240         ctx.samdb.rename(ctx.krbtgt_dn, ctx.new_krbtgt_dn)
241
242     def drsuapi_connect(ctx):
243         '''make a DRSUAPI connection to the server'''
244         binding_options = "seal"
245         if int(ctx.lp.get("log level")) >= 5:
246             binding_options += ",print"
247         binding_string = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options)
248         ctx.drsuapi = drsuapi.drsuapi(binding_string, ctx.lp, ctx.creds)
249         (ctx.drsuapi_handle, ctx.bind_supported_extensions) = drs_utils.drs_DsBind(ctx.drsuapi)
250
251     def create_tmp_samdb(ctx):
252         '''create a temporary samdb object for schema queries'''
253         ctx.tmp_schema = Schema(security.dom_sid(ctx.domsid),
254                                 schemadn=ctx.schema_dn)
255         ctx.tmp_samdb = SamDB(session_info=system_session(), url=None, auto_connect=False,
256                               credentials=ctx.creds, lp=ctx.lp, global_schema=False,
257                               am_rodc=False)
258         ctx.tmp_samdb.set_schema(ctx.tmp_schema)
259
260     def build_DsReplicaAttribute(ctx, attrname, attrvalue):
261         '''build a DsReplicaAttributeCtr object'''
262         r = drsuapi.DsReplicaAttribute()
263         r.attid = ctx.tmp_samdb.get_attid_from_lDAPDisplayName(attrname)
264         r.value_ctr = 1
265
266
267     def DsAddEntry(ctx, rec):
268         '''add a record via the DRSUAPI DsAddEntry call'''
269         if ctx.drsuapi is None:
270             ctx.drsuapi_connect()
271         if ctx.tmp_samdb is None:
272             ctx.create_tmp_samdb()
273
274         id = drsuapi.DsReplicaObjectIdentifier()
275         id.dn = rec['dn']
276
277         attrs = []
278         for a in rec:
279             if a == 'dn':
280                 continue
281             if not isinstance(rec[a], list):
282                 v = [rec[a]]
283             else:
284                 v = rec[a]
285             rattr = ctx.tmp_samdb.dsdb_DsReplicaAttribute(ctx.tmp_samdb, a, v)
286             attrs.append(rattr)
287
288         attribute_ctr = drsuapi.DsReplicaAttributeCtr()
289         attribute_ctr.num_attributes = len(attrs)
290         attribute_ctr.attributes = attrs
291
292         object = drsuapi.DsReplicaObject()
293         object.identifier = id
294         object.attribute_ctr = attribute_ctr
295
296         first_object = drsuapi.DsReplicaObjectListItem()
297         first_object.object = object
298
299         req2 = drsuapi.DsAddEntryRequest2()
300         req2.first_object = first_object
301
302         (level, ctr) = ctx.drsuapi.DsAddEntry(ctx.drsuapi_handle, 2, req2)
303         if ctr.err_ver != 1:
304             raise RuntimeError("expected err_ver 1, got %u" % ctr.err_ver)
305         if ctr.err_data.status != (0, 'WERR_OK'):
306             print("DsAddEntry failed with status %s info %s" % (ctr.err_data.status,
307                                                                 ctr.err_data.info.extended_err))
308             raise RuntimeError("DsAddEntry failed")
309
310     def join_add_objects(ctx):
311         '''add the various objects needed for the join'''
312         if ctx.acct_dn:
313             print "Adding %s" % ctx.acct_dn
314             rec = {
315                 "dn" : ctx.acct_dn,
316                 "objectClass": "computer",
317                 "displayname": ctx.samname,
318                 "samaccountname" : ctx.samname,
319                 "userAccountControl" : str(ctx.userAccountControl | samba.dsdb.UF_ACCOUNTDISABLE),
320                 "dnshostname" : ctx.dnshostname}
321             if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2008:
322                 rec['msDS-SupportedEncryptionTypes'] = str(samba.dsdb.ENC_ALL_TYPES)
323             if ctx.managedby:
324                 rec["managedby"] = ctx.managedby
325             if ctx.never_reveal_sid:
326                 rec["msDS-NeverRevealGroup"] = ctx.never_reveal_sid
327             if ctx.reveal_sid:
328                 rec["msDS-RevealOnDemandGroup"] = ctx.reveal_sid
329             ctx.samdb.add(rec)
330
331         if ctx.krbtgt_dn:
332             ctx.add_krbtgt_account()
333
334         print "Adding %s" % ctx.server_dn
335         rec = {
336             "dn": ctx.server_dn,
337             "objectclass" : "server",
338             "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME |
339                                 samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE |
340                                 samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
341             "dnsHostName" : ctx.dnshostname}
342
343         if ctx.acct_dn:
344             rec["serverReference"] = ctx.acct_dn
345
346         ctx.samdb.add(rec)
347
348         # FIXME: the partition (NC) assignment has to be made dynamic
349         print "Adding %s" % ctx.ntds_dn
350         rec = {
351             "dn" : ctx.ntds_dn,
352             "objectclass" : "nTDSDSA",
353             "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
354             "dMDLocation" : ctx.schema_dn}
355
356         if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
357             rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
358
359         if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
360             rec["msDS-HasDomainNCs"] = ctx.base_dn
361
362         if ctx.RODC:
363             rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
364             rec["msDS-HasFullReplicaNCs"] = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
365             rec["options"] = "37"
366             ctx.samdb.add(rec, ["rodc_join:1:1"])
367         else:
368             rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
369             rec["HasMasterNCs"]      = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
370             if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
371                 rec["msDS-HasMasterNCs"] = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
372             rec["options"] = "1"
373             rec["invocationId"] = ndr_pack(misc.GUID(str(uuid.uuid4())))
374             ctx.DsAddEntry(rec)
375
376         # find the GUID of our NTDS DN
377         res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
378         ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0]))
379
380         if ctx.connection_dn is not None:
381             print "Adding %s" % ctx.connection_dn
382             rec = {
383                 "dn" : ctx.connection_dn,
384                 "objectclass" : "nTDSConnection",
385                 "enabledconnection" : "TRUE",
386                 "options" : "65",
387                 "fromServer" : ctx.dc_ntds_dn}
388             ctx.samdb.add(rec)
389
390         if ctx.topology_dn and ctx.acct_dn:
391             print "Adding %s" % ctx.topology_dn
392             rec = {
393                 "dn" : ctx.topology_dn,
394                 "objectclass" : "msDFSR-Member",
395                 "msDFSR-ComputerReference" : ctx.acct_dn,
396                 "serverReference" : ctx.ntds_dn}
397             ctx.samdb.add(rec)
398
399         if ctx.acct_dn:
400             print "Adding SPNs to %s" % ctx.acct_dn
401             m = ldb.Message()
402             m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
403             for i in range(len(ctx.SPNs)):
404                 ctx.SPNs[i] = ctx.SPNs[i].replace("$NTDSGUID", str(ctx.ntds_guid))
405             m["servicePrincipalName"] = ldb.MessageElement(ctx.SPNs,
406                                                            ldb.FLAG_MOD_ADD,
407                                                            "servicePrincipalName")
408             ctx.samdb.modify(m)
409
410             print "Setting account password for %s" % ctx.samname
411             ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))" % ldb.binary_encode(ctx.samname),
412                                   ctx.acct_pass,
413                                   force_change_at_next_login=False,
414                                   username=ctx.samname)
415             res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-keyVersionNumber"])
416             ctx.key_version_number = int(res[0]["msDS-keyVersionNumber"][0])
417
418             print("Enabling account")
419             m = ldb.Message()
420             m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
421             m["userAccountControl"] = ldb.MessageElement(str(ctx.userAccountControl),
422                                                          ldb.FLAG_MOD_REPLACE,
423                                                          "userAccountControl")
424             ctx.samdb.modify(m)
425
426     def join_provision(ctx):
427         '''provision the local SAM'''
428
429         print "Calling bare provision"
430
431         logger = logging.getLogger("provision")
432         logger.addHandler(logging.StreamHandler(sys.stdout))
433         smbconf = ctx.lp.configfile
434
435         presult = provision(logger, system_session(), None,
436                             smbconf=smbconf, targetdir=ctx.targetdir, samdb_fill=FILL_DRS,
437                             realm=ctx.realm, rootdn=ctx.root_dn, domaindn=ctx.base_dn,
438                             schemadn=ctx.schema_dn,
439                             configdn=ctx.config_dn,
440                             serverdn=ctx.server_dn, domain=ctx.domain_name,
441                             hostname=ctx.myname, domainsid=ctx.domsid,
442                             machinepass=ctx.acct_pass, serverrole="domain controller",
443                             sitename=ctx.site, lp=ctx.lp)
444         print "Provision OK for domain DN %s" % presult.domaindn
445         ctx.local_samdb = presult.samdb
446         ctx.lp          = presult.lp
447         ctx.paths       = presult.paths
448         ctx.names       = presult.names
449
450     def join_provision_own_domain(ctx):
451         '''provision the local SAM'''
452
453         print "Calling bare provision"
454
455         logger = logging.getLogger("provision")
456         logger.addHandler(logging.StreamHandler(sys.stdout))
457
458         secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
459
460         presult = provision_fill(ctx.local_samdb, secrets_ldb,
461                                  logger, ctx.names, ctx.paths, domainsid=security.dom_sid(ctx.domsid),
462                                  targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
463                                  machinepass=ctx.acct_pass, serverrole="domain controller",
464                                  lp=ctx.lp)
465         print "Provision OK for domain %s" % ctx.names.dnsdomain
466
467
468     def join_replicate(ctx):
469         '''replicate the SAM'''
470
471         print "Starting replication"
472         ctx.local_samdb.transaction_start()
473         try:
474             source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id())
475             destination_dsa_guid = ctx.ntds_guid
476
477             if ctx.RODC:
478                 repl_creds = Credentials()
479                 repl_creds.guess(ctx.lp)
480                 repl_creds.set_kerberos_state(DONT_USE_KERBEROS)
481                 repl_creds.set_username(ctx.samname)
482                 repl_creds.set_password(ctx.acct_pass)
483             else:
484                 repl_creds = ctx.creds
485
486             binding_options = "seal"
487             if int(ctx.lp.get("log level")) >= 5:
488                 binding_options += ",print"
489             repl = drs_utils.drs_Replicate(
490                 "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
491                 ctx.lp, repl_creds, ctx.local_samdb)
492
493             repl.replicate(ctx.schema_dn, source_dsa_invocation_id,
494                     destination_dsa_guid, schema=True, rodc=ctx.RODC,
495                     replica_flags=ctx.replica_flags)
496             repl.replicate(ctx.config_dn, source_dsa_invocation_id,
497                     destination_dsa_guid, rodc=ctx.RODC,
498                     replica_flags=ctx.replica_flags)
499             if not ctx.subdomain:
500                 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
501                                destination_dsa_guid, rodc=ctx.RODC,
502                                replica_flags=ctx.domain_replica_flags)
503             if ctx.RODC:
504                 repl.replicate(ctx.acct_dn, source_dsa_invocation_id,
505                         destination_dsa_guid,
506                         exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
507                 repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id,
508                         destination_dsa_guid,
509                         exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
510
511             print "Committing SAM database"
512         except:
513             ctx.local_samdb.transaction_cancel()
514             raise
515         else:
516             ctx.local_samdb.transaction_commit()
517
518
519     def join_finalise(ctx):
520         '''finalise the join, mark us synchronised and setup secrets db'''
521
522         print "Setting isSynchronized and dsServiceName"
523         m = ldb.Message()
524         m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
525         m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized")
526         m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(ctx.ntds_guid),
527                                                 ldb.FLAG_MOD_REPLACE, "dsServiceName")
528         ctx.local_samdb.modify(m)
529
530         secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
531
532         print "Setting up secrets database"
533         secretsdb_self_join(secrets_ldb, domain=ctx.domain_name,
534                             realm=ctx.realm,
535                             dnsdomain=ctx.dnsdomain,
536                             netbiosname=ctx.myname,
537                             domainsid=security.dom_sid(ctx.domsid),
538                             machinepass=ctx.acct_pass,
539                             secure_channel_type=ctx.secure_channel_type,
540                             key_version_number=ctx.key_version_number)
541
542     def do_join(ctx):
543         ctx.cleanup_old_join()
544         try:
545             ctx.join_add_objects()
546             ctx.join_provision()
547             ctx.join_replicate()
548             if ctx.subdomain:
549                 ctx.join_provision_own_domain()
550             else:
551                 ctx.join_finalise()
552         except Exception:
553             print "Join failed - cleaning up"
554             ctx.cleanup_old_join()
555             raise
556
557
558 def join_RODC(server=None, creds=None, lp=None, site=None, netbios_name=None,
559               targetdir=None, domain=None, domain_critical_only=False):
560     """join as a RODC"""
561
562     ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain)
563
564     lp.set("workgroup", ctx.domain_name)
565     print("workgroup is %s" % ctx.domain_name)
566
567     lp.set("realm", ctx.realm)
568     print("realm is %s" % ctx.realm)
569
570     ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn)
571
572     # setup some defaults for accounts that should be replicated to this RODC
573     ctx.never_reveal_sid = [ "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY),
574                              "<SID=%s>" % security.SID_BUILTIN_ADMINISTRATORS,
575                              "<SID=%s>" % security.SID_BUILTIN_SERVER_OPERATORS,
576                              "<SID=%s>" % security.SID_BUILTIN_BACKUP_OPERATORS,
577                              "<SID=%s>" % security.SID_BUILTIN_ACCOUNT_OPERATORS ]
578     ctx.reveal_sid = "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW)
579
580     mysid = ctx.get_mysid()
581     admin_dn = "<SID=%s>" % mysid
582     ctx.managedby = admin_dn
583
584     ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
585                               samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION |
586                               samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT)
587
588     ctx.SPNs.extend([ "RestrictedKrbHost/%s" % ctx.myname,
589                       "RestrictedKrbHost/%s" % ctx.dnshostname ])
590
591     ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn
592     ctx.secure_channel_type = misc.SEC_CHAN_RODC
593     ctx.RODC = True
594     ctx.replica_flags  =  (drsuapi.DRSUAPI_DRS_INIT_SYNC |
595                            drsuapi.DRSUAPI_DRS_PER_SYNC |
596                            drsuapi.DRSUAPI_DRS_GET_ANC |
597                            drsuapi.DRSUAPI_DRS_NEVER_SYNCED |
598                            drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
599                            drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP)
600     ctx.domain_replica_flags = ctx.replica_flags
601     if domain_critical_only:
602         ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
603
604     ctx.do_join()
605
606
607     print "Joined domain %s (SID %s) as an RODC" % (ctx.domain_name, ctx.domsid)
608
609
610 def join_DC(server=None, creds=None, lp=None, site=None, netbios_name=None,
611             targetdir=None, domain=None, domain_critical_only=False):
612     """join as a DC"""
613     ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain)
614
615     lp.set("workgroup", ctx.domain_name)
616     print("workgroup is %s" % ctx.domain_name)
617
618     lp.set("realm", ctx.realm)
619     print("realm is %s" % ctx.realm)
620
621     ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
622
623     ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
624     ctx.secure_channel_type = misc.SEC_CHAN_BDC
625
626     ctx.replica_flags = (drsuapi.DRSUAPI_DRS_WRIT_REP |
627                          drsuapi.DRSUAPI_DRS_INIT_SYNC |
628                          drsuapi.DRSUAPI_DRS_PER_SYNC |
629                          drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS |
630                          drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
631     ctx.domain_replica_flags = ctx.replica_flags
632     if domain_critical_only:
633         ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
634
635     ctx.do_join()
636     print "Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid)
637
638 def join_subdomain(server=None, creds=None, lp=None, site=None, netbios_name=None,
639                    targetdir=None, parent_domain=None, dnsdomain=None, netbios_domain=None):
640     """join as a DC"""
641     ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, parent_domain)
642     ctx.subdomain = True
643     ctx.domain_name = netbios_domain
644     ctx.realm = dnsdomain
645     ctx.dnsdomain = dnsdomain
646     ctx.base_dn = samba.dn_from_dns_name(dnsdomain)
647     ctx.domsid = str(security.random_sid())
648     ctx.acct_dn = None
649     ctx.dnshostname = "%s.%s" % (ctx.myname, ctx.dnsdomain)
650
651     ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
652
653     ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
654     ctx.secure_channel_type = misc.SEC_CHAN_BDC
655
656     ctx.replica_flags = (drsuapi.DRSUAPI_DRS_WRIT_REP |
657                          drsuapi.DRSUAPI_DRS_INIT_SYNC |
658                          drsuapi.DRSUAPI_DRS_PER_SYNC |
659                          drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS |
660                          drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
661     ctx.domain_replica_flags = ctx.replica_flags
662
663     ctx.do_join()
664     print "Created domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid)