c4a3bb7fd60f7ad11e00ec1a8f23b6ee1912b6a7
[jelmer/samba4-debian.git] / source / scripting / python / samba / provision.py
1 #
2 #    backend code for provisioning a Samba4 server
3 #    Released under the GNU GPL v2 or later
4 #    Copyright Jelmer Vernooij 2007
5 #
6 # Based on the original in EJS:
7 #    Copyright Andrew Tridgell 2005
8 #
9
10 from base64 import b64encode
11 import os
12 import pwd
13 import grp
14 import time
15 import uuid, misc
16 from socket import gethostname, gethostbyname
17 import param
18 import registry
19 import samba
20 from samba import Ldb, substitute_var, valid_netbios_name
21 from samba.samdb import SamDB
22 import security
23 from ldb import Dn, SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
24         LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
25
26
27 DEFAULTSITE = "Default-First-Site-Name"
28
29 class InvalidNetbiosName(Exception):
30     def __init__(self, name):
31         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
32
33
34 class ProvisionPaths:
35     def __init__(self):
36         self.smbconf = None
37         self.shareconf = None
38         self.hklm = None
39         self.hkcu = None
40         self.hkcr = None
41         self.hku = None
42         self.hkpd = None
43         self.hkpt = None
44         self.samdb = None
45         self.secrets = None
46         self.keytab = None
47         self.dns_keytab = None
48         self.dns = None
49         self.winsdb = None
50         self.ldap_basedn_ldif = None
51         self.ldap_config_basedn_ldif = None
52         self.ldap_schema_basedn_ldif = None
53
54
55 def install_ok(lp, session_info, credentials):
56     """Check whether the current install seems ok."""
57     if lp.get("realm") == "":
58         return False
59     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
60             credentials=credentials, lp=lp)
61     if len(ldb.search(ldb.Dn("(cn=Administrator)"))) != 1:
62         return False
63     return True
64
65
66 def findnss(nssfn, *names):
67     """Find a user or group from a list of possibilities."""
68     for name in names:
69         try:
70             return nssfn(name)
71         except KeyError:
72             pass
73     raise Exception("Unable to find user/group for %s" % arguments[1])
74
75
76 def open_ldb(session_info, credentials, lp, dbname):
77     assert session_info is not None
78     try:
79         return Ldb(dbname, session_info=session_info, credentials=credentials, 
80                    lp=lp)
81     except LdbError, e:
82         print e
83         os.unlink(dbname)
84         return Ldb(dbname, session_info=session_info, credentials=credentials,
85                    lp=lp)
86
87
88 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
89     """Setup a ldb in the private dir."""
90     assert isinstance(ldif_path, str)
91
92     data = open(ldif_path, 'r').read()
93     if subst_vars is not None:
94         data = substitute_var(data, subst_vars)
95
96     assert "${" not in data
97
98     ldb.add_ldif(data)
99
100
101 def setup_modify_ldif(ldb, ldif_path, substvars=None):
102     """Modify a ldb in the private dir.
103     
104     :param ldb: LDB object.
105     :param ldif_path: LDIF file path.
106     :param substvars: Optional dictionary with substitution variables.
107     """
108     data = open(ldif_path, 'r').read()
109     if substvars is not None:
110         data = substitute_var(data, substvars)
111
112     assert "${" not in data
113
114     ldb.modify_ldif(data)
115
116
117 def setup_ldb(ldb, ldif_path, subst_vars):
118     assert ldb is not None
119     ldb.transaction_start()
120     try:
121         setup_add_ldif(ldb, ldif_path, subst_vars)
122     except:
123         ldb.transaction_cancel()
124         raise
125     ldb.transaction_commit()
126
127
128 def setup_file(template, fname, substvars):
129     """Setup a file in the private dir."""
130     f = fname
131
132     if os.path.exists(f):
133         os.unlink(f)
134
135     data = open(template, 'r').read()
136     if substvars:
137         data = substitute_var(data, substvars)
138     assert not "${" in data
139
140     open(f, 'w').write(data)
141
142
143 def provision_paths_from_lp(lp, dnsdomain):
144     """Set the default paths for provisioning.
145
146     :param lp: Loadparm context.
147     :param dnsdomain: DNS Domain name
148     """
149     paths = ProvisionPaths()
150     private_dir = lp.get("private dir")
151     paths.shareconf = os.path.join(private_dir, "share.ldb")
152     paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
153     paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
154     paths.templates = os.path.join(private_dir, "templates.ldb")
155     paths.keytab = os.path.join(private_dir, "secrets.keytab")
156     paths.dns_keytab = os.path.join(private_dir, "dns.keytab")
157     paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
158     paths.winsdb = os.path.join(private_dir, "wins.ldb")
159     paths.ldap_basedn_ldif = os.path.join(private_dir, 
160                                           dnsdomain + ".ldif")
161     paths.ldap_config_basedn_ldif = os.path.join(private_dir, 
162                                              dnsdomain + "-config.ldif")
163     paths.ldap_schema_basedn_ldif = os.path.join(private_dir, 
164                                               dnsdomain + "-schema.ldif")
165     paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
166     paths.phpldapadminconfig = os.path.join(private_dir, 
167                                             "phpldapadmin-config.php")
168     paths.hklm = os.path.join(private_dir, "hklm.ldb")
169     paths.sysvol = lp.get("sysvol", "path")
170     if paths.sysvol is None:
171         paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
172
173     paths.netlogon = lp.get("netlogon", "path")
174     if paths.netlogon is None:
175         paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
176
177     return paths
178
179
180 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users, 
181                         wheel, backup):
182     """setup reasonable name mappings for sam names to unix names."""
183     # add some foreign sids if they are not present already
184     ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
185     ldb.add_foreign(domaindn, "S-1-1-0", "World")
186     ldb.add_foreign(domaindn, "S-1-5-2", "Network")
187     ldb.add_foreign(domaindn, "S-1-5-18", "System")
188     ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
189
190     # some well known sids
191     ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
192     ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
193     ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
194     ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
195     ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
196     ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
197     ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
198     ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
199     ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
200
201     # and some well known domain rids
202     ldb.setup_name_mapping(domaindn, sid + "-500", root)
203     ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
204     ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
205     ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
206     ldb.setup_name_mapping(domaindn, sid + "-513", users)
207     ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
208
209
210 def provision_become_dc(setup_dir, message, paths, lp, session_info, 
211                         credentials):
212     assert session_info is not None
213     erase = False
214
215     def setup_path(file):
216         return os.path.join(setup_dir, file)
217     os.path.unlink(paths.samdb)
218
219     message("Setting up templates db")
220     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
221                       credentials=credentials, lp=lp)
222
223     # Also wipes the database
224     message("Setting up sam.ldb")
225     samdb = SamDB(paths.samdb, session_info=session_info, 
226                   credentials=credentials, lp=lp)
227
228     message("Setting up sam.ldb partitions")
229     setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn)
230
231     samdb = SamDB(paths.samdb, session_info=session_info, 
232                   credentials=credentials, lp=lp)
233
234     ldb.transaction_start()
235     try:
236         message("Setting up sam.ldb attributes")
237         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
238
239         message("Setting up sam.ldb rootDSE")
240         setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, 
241                             hostname, dnsdomain, realm, rootdn, configdn, 
242                             netbiosname)
243
244         if erase:
245             message("Erasing data from partitions")
246             samdb.erase_partitions()
247
248         message("Setting up sam.ldb indexes")
249         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
250     except:
251         samdb.transaction_cancel()
252         raise
253
254     samdb.transaction_commit()
255
256     message("Setting up %s" % paths.secrets)
257     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, session_info, 
258                                   credentials, lp)
259     setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), 
260               { "MACHINEPASS_B64": b64encode(machinepass) })
261
262
263 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
264     if os.path.exists(path):
265         os.unlink(path)
266     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, lp=lp)
267     secrets_ldb.erase()
268     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
269     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
270     return secrets_ldb
271
272
273 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
274     templates_ldb = SamDB(path, session_info=session_info,
275                         credentials=credentials, lp=lp)
276     templates_ldb.erase()
277     templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
278
279
280 def setup_registry(path, setup_path, session_info, credentials, lp):
281     reg = registry.Registry()
282     hive = registry.open_ldb(path, session_info=session_info, 
283                          credentials=credentials, lp_ctx=lp)
284     reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
285     provision_reg = setup_path("provision.reg")
286     assert os.path.exists(provision_reg)
287     reg.diff_apply(provision_reg)
288
289
290 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname, 
291                         dnsdomain, realm, rootdn, configdn, netbiosname):
292     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
293         "SCHEMADN": schemadn, 
294         "NETBIOSNAME": netbiosname,
295         "DNSDOMAIN": dnsdomain,
296         "DEFAULTSITE": DEFAULTSITE,
297         "REALM": realm,
298         "DNSNAME": "%s.%s" % (hostname, dnsdomain),
299         "DOMAINDN": domaindn,
300         "ROOTDN": rootdn,
301         "CONFIGDN": configdn,
302         "VERSION": samba.version(),
303         })
304
305
306 def setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn):
307     #Add modules to the list to activate them by default
308     #beware often order is important
309     #
310     # Some Known ordering constraints:
311     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
312     # - objectclass must be before password_hash, because password_hash checks
313     #   that the objectclass is of type person (filled in by objectclass
314     #   module when expanding the objectclass list)
315     # - partition must be last
316     # - each partition has its own module list then
317     modules_list = ["rootdse",
318                     "paged_results",
319                     "ranged_results",
320                     "anr",
321                     "server_sort",
322                     "extended_dn",
323                     "asq",
324                     "samldb",
325                     "rdn_name",
326                     "objectclass",
327                     "kludge_acl",
328                     "operational"]
329     tdb_modules_list = [
330                     "subtree_rename",
331                     "subtree_delete",
332                     "linked_attributes"]
333     modules_list2 = ["show_deleted",
334                     "partition"]
335  
336     setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
337         "SCHEMADN": schemadn, 
338         "SCHEMADN_LDB": "schema.ldb",
339         "SCHEMADN_MOD2": ",objectguid",
340         "CONFIGDN": configdn,
341         "CONFIGDN_LDB": "configuration.ldb",
342         "DOMAINDN": domaindn,
343         "DOMAINDN_LDB": "users.ldb",
344         "SCHEMADN_MOD": "schema_fsmo",
345         "CONFIGDN_MOD": "naming_fsmo",
346         "CONFIGDN_MOD2": ",objectguid",
347         "DOMAINDN_MOD": "pdc_fsmo,password_hash",
348         "DOMAINDN_MOD2": ",objectguid",
349         "MODULES_LIST": ",".join(modules_list),
350         "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
351         "MODULES_LIST2": ",".join(modules_list2),
352         })
353
354
355 def setup_self_join(samdb, configdn, schemadn, domaindn, 
356                     netbiosname, hostname, dnsdomain, machinepass, dnspass, 
357                     realm, domainname, domainsid, invocationid, setup_path,
358                     policyguid, hostguid=None):
359     if hostguid is not None:
360         hostguid_add = "objectGUID: %s" % hostguid
361     else:
362         hostguid_add = ""
363
364     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
365               "CONFIGDN": configdn, 
366               "SCHEMADN": schemadn,
367               "DOMAINDN": domaindn,
368               "INVOCATIONID": invocationid,
369               "NETBIOSNAME": netbiosname,
370               "DEFAULTSITE": DEFAULTSITE,
371               "DNSNAME": "%s.%s" % (hostname, dnsdomain),
372               "MACHINEPASS_B64": b64encode(machinepass),
373               "DNSPASS_B64": b64encode(dnspass),
374               "REALM": realm,
375               "DOMAIN": domainname,
376               "HOSTGUID_ADD": hostguid_add,
377               "DNSDOMAIN": dnsdomain})
378     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
379               "POLICYGUID": policyguid,
380               "DNSDOMAIN": dnsdomain,
381               "DOMAINSID": str(domainsid),
382               "DOMAINDN": domaindn})
383
384
385 def setup_samdb(path, setup_path, session_info, credentials, lp, 
386                 schemadn, configdn, domaindn, dnsdomain, realm, 
387                 netbiosname, message, hostname, rootdn, erase, 
388                 domainsid, aci, rdn_dc, domainguid, policyguid, 
389                 domainname, blank, adminpass, krbtgtpass, 
390                 machinepass, hostguid, invocationid, dnspass):
391     # Also wipes the database
392     message("Setting up sam.ldb")
393     samdb = SamDB(path, session_info=session_info, 
394                   credentials=credentials, lp=lp)
395
396     message("Setting up sam.ldb partitions")
397     setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn)
398
399     samdb = SamDB(path, session_info=session_info, 
400                   credentials=credentials, lp=lp)
401
402     samdb.transaction_start()
403     try:
404         message("Setting up sam.ldb attributes")
405         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
406
407         message("Setting up sam.ldb rootDSE")
408         setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, 
409                             hostname, dnsdomain, realm, rootdn, configdn, 
410                             netbiosname)
411
412         if erase:
413             message("Erasing data from partitions")
414             samdb.erase_partitions()
415     except:
416         samdb.transaction_cancel()
417         raise
418
419     samdb.transaction_commit()
420
421     message("Pre-loading the Samba 4 and AD schema")
422     samdb = SamDB(path, session_info=session_info, 
423                   credentials=credentials, lp=lp)
424     samdb.set_domain_sid(domainsid)
425     load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
426
427     samdb.transaction_start()
428         
429     try:
430         message("Adding DomainDN: %s (permitted to fail)" % domaindn)
431         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
432             "DOMAINDN": domaindn,
433             "ACI": aci,
434             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
435             "RDN_DC": rdn_dc,
436             })
437
438         message("Modifying DomainDN: " + domaindn + "")
439         if domainguid is not None:
440             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
441         else:
442             domainguid_mod = ""
443
444         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
445             "RDN_DC": rdn_dc,
446             "LDAPTIME": timestring(int(time.time())),
447             "DOMAINSID": str(domainsid),
448             "SCHEMADN": schemadn, 
449             "NETBIOSNAME": netbiosname,
450             "DEFAULTSITE": DEFAULTSITE,
451             "CONFIGDN": configdn,
452             "POLICYGUID": policyguid,
453             "DOMAINDN": domaindn,
454             "DOMAINGUID_MOD": domainguid_mod,
455             })
456
457         message("Adding configuration container (permitted to fail)")
458         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
459             "CONFIGDN": configdn, 
460             "ACI": aci,
461             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
462             })
463         message("Modifying configuration container")
464         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
465             "CONFIGDN": configdn, 
466             "SCHEMADN": schemadn,
467             })
468
469         message("Adding schema container (permitted to fail)")
470         setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
471             "SCHEMADN": schemadn,
472             "ACI": aci,
473             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
474             })
475         message("Modifying schema container")
476         setup_modify_ldif(samdb, setup_path("provision_schema_basedn_modify.ldif"), {
477             "SCHEMADN": schemadn,
478             "NETBIOSNAME": netbiosname,
479             "DEFAULTSITE": DEFAULTSITE,
480             "CONFIGDN": configdn,
481             })
482
483         message("Setting up sam.ldb Samba4 schema")
484         setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
485                        {"SCHEMADN": schemadn })
486         message("Setting up sam.ldb AD schema")
487         setup_add_ldif(samdb, setup_path("schema.ldif"), 
488                        {"SCHEMADN": schemadn})
489
490         message("Setting up sam.ldb configuration data")
491         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
492             "CONFIGDN": configdn,
493             "NETBIOSNAME": netbiosname,
494             "DEFAULTSITE": DEFAULTSITE,
495             "DNSDOMAIN": dnsdomain,
496             "DOMAIN": domainname,
497             "SCHEMADN": schemadn,
498             "DOMAINDN": domaindn,
499             })
500
501         message("Setting up display specifiers")
502         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
503                        {"CONFIGDN": configdn})
504
505         message("Adding users container (permitted to fail)")
506         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
507             "DOMAINDN": domaindn})
508         message("Modifying users container")
509         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
510             "DOMAINDN": domaindn})
511         message("Adding computers container (permitted to fail)")
512         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
513             "DOMAINDN": domaindn})
514         message("Modifying computers container")
515         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
516             "DOMAINDN": domaindn})
517         message("Setting up sam.ldb data")
518         setup_add_ldif(samdb, setup_path("provision.ldif"), {
519             "DOMAINDN": domaindn,
520             "NETBIOSNAME": netbiosname,
521             "DEFAULTSITE": DEFAULTSITE,
522             "CONFIGDN": configdn,
523             })
524
525         if not blank:
526             message("Setting up sam.ldb users and groups")
527             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
528                 "DOMAINDN": domaindn,
529                 "DOMAINSID": str(domainsid),
530                 "CONFIGDN": configdn,
531                 "ADMINPASS_B64": b64encode(adminpass),
532                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
533                 })
534
535             if lp.get("server role") == "domain controller":
536                 message("Setting up self join")
537                 setup_self_join(samdb, configdn=configdn, schemadn=schemadn, domaindn=domaindn, 
538                                 invocationid=invocationid, dnspass=dnspass, netbiosname=netbiosname,
539                                 dnsdomain=dnsdomain, realm=realm, machinepass=machinepass, 
540                                 domainname=domainname, domainsid=domainsid, policyguid=policyguid,
541                                 hostname=hostname, hostguid=hostguid, setup_path=setup_path)
542
543         message("Setting up sam.ldb index")
544         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
545
546         message("Setting up sam.ldb rootDSE marking as synchronized")
547         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
548     except:
549         samdb.transaction_cancel()
550         raise
551
552     samdb.transaction_commit()
553     return samdb
554
555
556 def provision(lp, setup_dir, message, blank, paths, session_info, 
557               credentials, ldapbackend, realm=None, domain=None, hostname=None, 
558               hostip=None, domainsid=None, hostguid=None, adminpass=None, 
559               krbtgtpass=None, domainguid=None, policyguid=None, 
560               invocationid=None, machinepass=None, dnspass=None, root=None,
561               nobody=None, nogroup=None, users=None, wheel=None, backup=None, 
562               aci=None, serverrole=None):
563     """Provision samba4
564     
565     :note: caution, this wipes all existing data!
566     """
567
568     def setup_path(file):
569         return os.path.join(setup_dir, file)
570
571     erase = False
572
573     if domainsid is None:
574         domainsid = security.random_sid()
575     if policyguid is None:
576         policyguid = uuid.random()
577     if invocationid is None:
578         invocationid = uuid.random()
579     if adminpass is None:
580         adminpass = misc.random_password(12)
581     if krbtgtpass is None:
582         krbtgtpass = misc.random_password(12)
583     if machinepass is None:
584         machinepass  = misc.random_password(12)
585     if dnspass is None:
586         dnspass = misc.random_password(12)
587     if root is None:
588         root = findnss(pwd.getpwnam, "root")[4]
589     if nobody is None:
590         nobody = findnss(pwd.getpwnam, "nobody")[4]
591     if nogroup is None:
592         nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]
593     if users is None:
594         users = findnss(grp.getgrnam, "users", "guest", "other", "unknown", "usr")[2]
595     if wheel is None:
596         wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
597     if backup is None:
598         backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
599     if aci is None:
600         aci = "# no aci for local ldb"
601     if serverrole is None:
602         serverrole = lp.get("server role")
603
604     if realm is None:
605         realm = lp.get("realm")
606     else:
607         if lp.get("realm").upper() != realm.upper():
608             raise Exception("realm '%s' in smb.conf must match chosen realm '%s'\n" %
609                 (lp.get("realm"), realm))
610
611     assert realm is not None
612     realm = realm.upper()
613
614     if domain is None:
615         domain = lp.get("workgroup")
616     else:
617         if lp.get("workgroup").upper() != domain.upper():
618             raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'\n",
619                 lp.get("workgroup"), domain)
620
621     assert domain is not None
622     domain = domain.upper()
623     if not valid_netbios_name(domain):
624         raise InvalidNetbiosName(domain)
625
626     if hostname is None:
627         hostname = gethostname().split(".")[0].lower()
628
629     if hostip is None:
630         hostip = gethostbyname(hostname)
631
632     netbiosname = hostname.upper()
633     if not valid_netbios_name(netbiosname):
634         raise InvalidNetbiosName(netbiosname)
635
636     dnsdomain    = realm.lower()
637     domaindn     = "DC=" + dnsdomain.replace(".", ",DC=")
638     rootdn       = domaindn
639     configdn     = "CN=Configuration," + rootdn
640     schemadn     = "CN=Schema," + configdn
641
642     rdn_dc = domaindn.split(",")[0][len("DC="):]
643
644     message("set DOMAIN SID: %s" % str(domainsid))
645     message("Provisioning for %s in realm %s" % (domain, realm))
646     message("Using administrator password: %s" % adminpass)
647
648     assert paths.smbconf is not None
649
650     # only install a new smb.conf if there isn't one there already
651     if not os.path.exists(paths.smbconf):
652         message("Setting up smb.conf")
653         if serverrole == "domain controller":
654             smbconfsuffix = "dc"
655         elif serverrole == "member":
656             smbconfsuffix = "member"
657         else:
658             assert "Invalid server role setting: %s" % serverrole
659         setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), paths.smbconf, {
660             "HOSTNAME": hostname,
661             "DOMAIN_CONF": domain,
662             "REALM_CONF": realm,
663             "SERVERROLE": serverrole,
664             "NETLOGONPATH": paths.netlogon,
665             "SYSVOLPATH": paths.sysvol,
666             })
667         lp.reload()
668
669     # only install a new shares config db if there is none
670     if not os.path.exists(paths.shareconf):
671         message("Setting up share.ldb")
672         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
673                         credentials=credentials, lp=lp)
674         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
675
676     message("Setting up secrets.ldb")
677     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
678                                   session_info=session_info, 
679                                   credentials=credentials, lp=lp)
680
681     message("Setting up the registry")
682     setup_registry(paths.hklm, setup_path, session_info, 
683                    credentials=credentials, lp=lp)
684
685     message("Setting up templates db")
686     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
687                       credentials=credentials, lp=lp)
688
689     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, credentials=credentials,
690                         lp=lp, schemadn=schemadn, configdn=configdn, domaindn=domaindn,
691                         dnsdomain=dnsdomain, netbiosname=netbiosname, realm=realm, message=message,
692                         hostname=hostname, rootdn=rootdn, erase=erase, domainsid=domainsid, aci=aci,
693                         rdn_dc=rdn_dc, domainguid=domainguid, policyguid=policyguid, 
694                         domainname=domain, blank=blank, adminpass=adminpass, krbtgtpass=krbtgtpass,
695                         hostguid=hostguid, invocationid=invocationid, machinepass=machinepass,
696                         dnspass=dnspass)
697
698     if lp.get("server role") == "domain controller":
699         os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}"), 0755)
700         os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "Machine"), 0755)
701         os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "User"), 0755)
702         if not os.path.isdir(paths.netlogon):
703             os.makedirs(paths.netlogon, 0755)
704         secrets_ldb = Ldb(paths.secrets, session_info=session_info, credentials=credentials, lp=lp)
705         setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), { 
706             "MACHINEPASS_B64": b64encode(machinepass),
707             "DOMAIN": domain,
708             "REALM": realm,
709             "LDAPTIME": timestring(int(time.time())),
710             "DNSDOMAIN": dnsdomain,
711             "DOMAINSID": str(domainsid),
712             "SECRETS_KEYTAB": paths.keytab,
713             "NETBIOSNAME": netbiosname,
714             "SAM_LDB": paths.samdb,
715             "DNS_KEYTAB": paths.dns_keytab,
716             "DNSPASS_B64": b64encode(dnspass),
717             })
718
719     if not blank:
720         setup_name_mappings(samdb, str(domainsid), 
721                         domaindn, root=root, nobody=nobody, 
722                         nogroup=nogroup, wheel=wheel, users=users,
723                         backup=backup)
724
725     message("Setting up phpLDAPadmin configuration")
726     create_phplpapdadmin_config(paths.phpldapadminconfig, setup_path, paths.s4_ldapi_path)
727
728     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
729
730     if lp.get("server role") == "domain controller":
731         samdb = SamDB(paths.samdb, session_info=session_info, 
732                       credentials=credentials, lp=lp)
733
734         domainguid = samdb.searchone(Dn(samdb, domaindn), "objectGUID")
735         assert isinstance(domainguid, str)
736         hostguid = samdb.searchone(Dn(samdb, domaindn), "objectGUID",
737                 expression="(&(objectClass=computer)(cn=%s))" % hostname,
738                 scope=SCOPE_SUBTREE)
739         assert isinstance(hostguid, str)
740
741         message("Setting up DNS zone: %s" % dnsdomain)
742         create_zone_file(paths.dns, setup_path, samdb, 
743                       hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
744                       domaindn=domaindn, dnspass=dnspass, realm=realm, 
745                       domainguid=domainguid, hostguid=hostguid)
746         message("Please install the zone located in %s into your DNS server" % paths.dns)
747
748 def create_phplpapdadmin_config(path, setup_path, s4_ldapi_path):
749     setup_file(setup_path("phpldapadmin-config.php"), 
750                path, {"S4_LDAPI_URI": "ldapi://%s" % s4_ldapi_path.replace("/", "%2F")})
751
752
753 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
754                   hostip, hostname, dnspass, realm, domainguid, hostguid):
755     """Write out a DNS zone file, from the info in the current database."""
756
757     setup_file(setup_path("provision.zone"), path, {
758             "DNSPASS_B64": b64encode(dnspass),
759             "HOSTNAME": hostname,
760             "DNSDOMAIN": dnsdomain,
761             "REALM": realm,
762             "HOSTIP": hostip,
763             "DOMAINGUID": domainguid,
764             "DATESTRING": time.strftime("%Y%m%d%H"),
765             "DEFAULTSITE": DEFAULTSITE,
766             "HOSTGUID": hostguid,
767         })
768
769
770 def provision_ldapbase(setup_dir, message, paths):
771     """Write out a DNS zone file, from the info in the current database."""
772     message("Setting up LDAP base entry: %s" % domaindn)
773     rdns = domaindn.split(",")
774
775     rdn_dc = rdns[0][len("DC="):]
776
777     def setup_path(file):
778         return os.path.join(setup_dir, file)
779
780     setup_file(setup_path("provision_basedn.ldif"), 
781            paths.ldap_basedn_ldif)
782
783     setup_file(setup_path("provision_configuration_basedn.ldif"), 
784            paths.ldap_config_basedn_ldif)
785
786     setup_file(setup_path("provision_schema_basedn.ldif"), 
787            paths.ldap_schema_basedn_ldif, {
788             "SCHEMADN": schemadn,
789             "ACI": "# no aci for local ldb",
790             "EXTENSIBLEOBJECT": "objectClass: extensibleObject"})
791
792     message("Please install the LDIF located in " + paths.ldap_basedn_ldif + ", " + paths.ldap_config_basedn_ldif + " and " + paths.ldap_schema_basedn_ldif + " into your LDAP server, and re-run with --ldap-backend=ldap://my.ldap.server")
793
794
795 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
796     """Load schema."""
797     schema_data = open(setup_path("schema.ldif"), 'r').read()
798     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
799     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
800     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
801     head_data = substitute_var(head_data, {
802                     "SCHEMADN": schemadn,
803                     "NETBIOSNAME": netbiosname,
804                     "CONFIGDN": configdn,
805                     "DEFAULTSITE": DEFAULTSITE})
806     samdb.attach_schema_from_ldif(head_data, schema_data)
807
808
809 def join_domain(domain, netbios_name, join_type, creds):
810     ctx = NetContext(creds)
811     joindom = object()
812     joindom.domain = domain
813     joindom.join_type = join_type
814     joindom.netbios_name = netbios_name
815     if not ctx.JoinDomain(joindom):
816         raise Exception("Domain Join failed: " + joindom.error_string)
817
818
819 def vampire(domain, session_info, credentials, message):
820     """Vampire a remote domain.  
821     
822     Session info and credentials are required for for
823     access to our local database (might be remote ldap)
824     """
825     ctx = NetContext(credentials)
826     machine_creds = Credentials()
827     machine_creds.set_domain(form.domain)
828     if not machine_creds.set_machine_account():
829         raise Exception("Failed to access domain join information!")
830     vampire_ctx.machine_creds = machine_creds
831     vampire_ctx.session_info = session_info
832     if not ctx.SamSyncLdb(vampire_ctx):
833         raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string)
834
835
836