r26538: Pass path generation function around rather than base directory.
[idra/samba.git] / source4 / 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.load_ldif_add(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     for (changetype, msg) in ldb.parse_ldif(data):
115         assert changetype == CHANGETYPE_MODIFY
116         ldb.modify(msg)
117
118
119 def setup_ldb(ldb, ldif_path, subst_vars):
120     assert ldb is not None
121     ldb.transaction_start()
122     try:
123         setup_add_ldif(ldb, ldif_path, subst_vars)
124     except:
125         ldb.transaction_cancel()
126         raise
127     ldb.transaction_commit()
128
129
130 def setup_file(template, fname, substvars):
131     """Setup a file in the private dir."""
132     f = fname
133
134     if os.path.exists(f):
135         os.unlink(f)
136
137     data = open(template, 'r').read()
138     if substvars:
139         data = substitute_var(data, substvars)
140     assert not "${" in data
141
142     open(f, 'w').write(data)
143
144
145 def provision_default_paths(lp, dnsdomain):
146     """Set the default paths for provisioning.
147
148     :param lp: Loadparm context.
149     :param dnsdomain: DNS Domain name
150     """
151     paths = ProvisionPaths()
152     private_dir = lp.get("private dir")
153     paths.shareconf = os.path.join(private_dir, "share.ldb")
154     paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
155     paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
156     paths.templates = os.path.join(private_dir, "templates.ldb")
157     paths.keytab = os.path.join(private_dir, "secrets.keytab")
158     paths.dns_keytab = os.path.join(private_dir, "dns.keytab")
159     paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
160     paths.winsdb = os.path.join(private_dir, "wins.ldb")
161     paths.ldap_basedn_ldif = os.path.join(private_dir, 
162                                           dnsdomain + ".ldif")
163     paths.ldap_config_basedn_ldif = os.path.join(private_dir, 
164                                              dnsdomain + "-config.ldif")
165     paths.ldap_schema_basedn_ldif = os.path.join(private_dir, 
166                                               dnsdomain + "-schema.ldif")
167     paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
168     paths.phpldapadminconfig = os.path.join(private_dir, 
169                                             "phpldapadmin-config.php")
170     paths.hklm = os.path.join(private_dir, "hklm.ldb")
171     paths.sysvol = lp.get("sysvol", "path")
172     if paths.sysvol is None:
173         paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
174
175     paths.netlogon = lp.get("netlogon", "path")
176     if paths.netlogon is None:
177         paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
178
179     return paths
180
181
182 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users, 
183                         wheel, backup):
184     """setup reasonable name mappings for sam names to unix names."""
185     # add some foreign sids if they are not present already
186     ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
187     ldb.add_foreign(domaindn, "S-1-1-0", "World")
188     ldb.add_foreign(domaindn, "S-1-5-2", "Network")
189     ldb.add_foreign(domaindn, "S-1-5-18", "System")
190     ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
191
192     # some well known sids
193     ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
194     ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
195     ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
196     ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
197     ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
198     ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
199     ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
200     ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
201     ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
202
203     # and some well known domain rids
204     ldb.setup_name_mapping(domaindn, sid + "-500", root)
205     ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
206     ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
207     ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
208     ldb.setup_name_mapping(domaindn, sid + "-513", users)
209     ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
210
211
212 def provision_become_dc(setup_dir, message, paths, lp, session_info, 
213                         credentials):
214     assert session_info is not None
215     erase = False
216
217     def setup_path(file):
218         return os.path.join(setup_dir, file)
219     os.path.unlink(paths.samdb)
220
221     message("Setting up templates db")
222     setup_templatesdb(paths.templates, setup_path, session_info, 
223                       credentials, lp)
224
225     # Also wipes the database
226     message("Setting up sam.ldb")
227     samdb = SamDB(paths.samdb, credentials=credentials, 
228                   session_info=session_info, lp=lp)
229
230     message("Setting up sam.ldb partitions")
231     setup_samdb_partitions(samdb, setup_path, schemadn, 
232                        configdn, domaindn)
233
234     samdb = SamDB(paths.samdb, credentials=credentials, 
235                   session_info=session_info, lp=lp)
236
237     ldb.transaction_start()
238     try:
239         message("Setting up sam.ldb attributes")
240         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
241
242         message("Setting up sam.ldb rootDSE")
243         setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, 
244                             hostname, dnsdomain, realm, rootdn, configdn, 
245                             netbiosname)
246
247         if erase:
248             message("Erasing data from partitions")
249             samdb.erase_partitions()
250
251         message("Setting up sam.ldb indexes")
252         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
253     except:
254         samdb.transaction_cancel()
255         raise
256
257     samdb.transaction_commit()
258
259     message("Setting up %s" % paths.secrets)
260     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, session_info, credentials, lp)
261     setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), 
262               { "MACHINEPASS_B64": b64encode(machinepass) })
263
264
265 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
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.Hive(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.apply_patchfile(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
356 def provision(lp, setup_dir, message, blank, paths, session_info, 
357               credentials, ldapbackend, realm=None, domain=None, hostname=None, 
358               hostip=None, domainsid=None, hostguid=None, adminpass=None, 
359               krbtgtpass=None, domainguid=None, policyguid=None, 
360               invocationid=None, machinepass=None, dnspass=None, root=None,
361               nobody=None, nogroup=None, users=None, wheel=None, backup=None, 
362               aci=None, serverrole=None):
363     """Provision samba4
364     
365     :note: caution, this wipes all existing data!
366     """
367
368     def setup_path(file):
369         return os.path.join(setup_dir, file)
370
371     erase = False
372
373     if domainsid is None:
374         domainsid = security.random_sid()
375     if policyguid is None:
376         policyguid = uuid.random()
377     if invocationid is None:
378         invocationid = uuid.random()
379     if adminpass is None:
380         adminpass = misc.random_password(12)
381     if krbtgtpass is None:
382         krbtgtpass = misc.random_password(12)
383     if machinepass is None:
384         machinepass  = misc.random_password(12)
385     if dnspass is None:
386         dnspass = misc.random_password(12)
387     if root is None:
388         root = findnss(pwd.getpwnam, "root")[4]
389     if nobody is None:
390         nobody = findnss(pwd.getpwnam, "nobody")[4]
391     if nogroup is None:
392         nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]
393     if users is None:
394         users = findnss(grp.getgrnam, "users", "guest", "other", "unknown", "usr")[2]
395     if wheel is None:
396         wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
397     if backup is None:
398         backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
399     if aci is None:
400         aci = "# no aci for local ldb"
401     if serverrole is None:
402         serverrole = lp.get("server role")
403
404     if realm is None:
405         realm = lp.get("realm")
406     else:
407         if lp.get("realm").upper() != realm.upper():
408             raise Error("realm '%s' in smb.conf must match chosen realm '%s'\n" %
409                 (lp.get("realm"), realm))
410
411     assert realm is not None
412     realm = realm.upper()
413
414     if domain is None:
415         domain = lp.get("workgroup")
416     else:
417         if lp.get("workgroup").upper() != domain.upper():
418             raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'\n",
419                 lp.get("workgroup"), domain)
420
421     assert domain is not None
422     domain = domain.upper()
423     if not valid_netbios_name(domain):
424         raise InvalidNetbiosName(domain)
425
426     if hostname is None:
427         hostname = gethostname().split(".")[0].lower()
428
429     if hostip is None:
430         hostip = gethostbyname(hostname)
431
432     netbiosname = hostname.upper()
433     if not valid_netbios_name(netbiosname):
434         raise InvalidNetbiosName(netbiosname)
435
436     dnsdomain    = realm.lower()
437     domaindn     = "DC=" + dnsdomain.replace(".", ",DC=")
438     rootdn       = domaindn
439     configdn     = "CN=Configuration," + rootdn
440     schemadn     = "CN=Schema," + configdn
441
442     rdn_dc = domaindn.split(",")[0][len("DC="):]
443
444     message("set DOMAIN SID: %s" % str(domainsid))
445     message("Provisioning for %s in realm %s" % (domain, realm))
446     message("Using administrator password: %s" % adminpass)
447
448     assert paths.smbconf is not None
449
450     # only install a new smb.conf if there isn't one there already
451     if not os.path.exists(paths.smbconf):
452         message("Setting up smb.conf")
453         if serverrole == "domain controller":
454             smbconfsuffix = "dc"
455         elif serverrole == "member":
456             smbconfsuffix = "member"
457         else:
458             assert "Invalid server role setting: %s" % serverrole
459         setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), paths.smbconf, {
460             "HOSTNAME": hostname,
461             "DOMAIN_CONF": domain,
462             "REALM_CONF": realm,
463             "SERVERROLE": serverrole,
464             "NETLOGONPATH": paths.netlogon,
465             "SYSVOLPATH": paths.sysvol,
466             })
467         lp.reload()
468
469     # only install a new shares config db if there is none
470     if not os.path.exists(paths.shareconf):
471         message("Setting up share.ldb")
472         share_ldb = Ldb(paths.shareconf, session_info=session_info, 
473                         credentials=credentials, lp=lp)
474         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
475
476     message("Setting up secrets.ldb")
477     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, session_info=session_info, 
478                     credentials=credentials, lp=lp)
479
480     message("Setting up the registry")
481     # FIXME: Still fails for some reason
482     #setup_registry(paths.hklm, setup_path, session_info, 
483     #               credentials=credentials, lp=lp)
484
485     message("Setting up templates db")
486     setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
487                       credentials=credentials, lp=lp)
488
489     samdb = SamDB(paths.samdb, session_info=session_info, 
490                   credentials=credentials, lp=lp)
491
492     message("Setting up sam.ldb partitions")
493     setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn)
494
495     samdb = SamDB(paths.samdb, session_info=session_info, 
496                   credentials=credentials, lp=lp)
497
498     samdb.transaction_start()
499     try:
500         message("Setting up sam.ldb attributes")
501         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
502
503         message("Setting up sam.ldb rootDSE")
504         setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, 
505                             hostname, dnsdomain, realm, rootdn, configdn, 
506                             netbiosname)
507
508         if erase:
509             message("Erasing data from partitions")
510             samdb.erase_partitions()
511     except:
512         samdb.transaction_cancel()
513         raise
514
515     samdb.transaction_commit()
516
517     message("Pre-loading the Samba 4 and AD schema")
518     samdb = SamDB(paths.samdb, session_info=session_info, 
519                   credentials=credentials, lp=lp)
520     samdb.set_domain_sid(domainsid)
521     load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
522
523     samdb.transaction_start()
524         
525     try:
526         message("Adding DomainDN: %s (permitted to fail)" % domaindn)
527         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
528             "DOMAINDN": domaindn,
529             "ACI": aci,
530             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
531             "RDN_DC": rdn_dc,
532             })
533
534         message("Modifying DomainDN: " + domaindn + "")
535         if domainguid is not None:
536             domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
537         else:
538             domainguid_mod = ""
539
540         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
541             "RDN_DC": rdn_dc,
542             "LDAPTIME": timestring(int(time.time())),
543             "DOMAINSID": str(domainsid),
544             "SCHEMADN": schemadn, 
545             "NETBIOSNAME": netbiosname,
546             "DEFAULTSITE": DEFAULTSITE,
547             "CONFIGDN": configdn,
548             "POLICYGUID": policyguid,
549             "DOMAINDN": domaindn,
550             "DOMAINGUID_MOD": domainguid_mod,
551             })
552
553         message("Adding configuration container (permitted to fail)")
554         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
555             "CONFIGDN": configdn, 
556             "ACI": aci,
557             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
558             })
559         message("Modifying configuration container")
560         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
561             "CONFIGDN": configdn, 
562             "SCHEMADN": schemadn,
563             })
564
565         message("Adding schema container (permitted to fail)")
566         setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
567             "SCHEMADN": schemadn,
568             "ACI": aci,
569             "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
570             })
571         message("Modifying schema container")
572         setup_modify_ldif(samdb, setup_path("provision_schema_basedn_modify.ldif"), {
573             "SCHEMADN": schemadn,
574             "NETBIOSNAME": netbiosname,
575             "DEFAULTSITE": DEFAULTSITE,
576             "CONFIGDN": configdn,
577             })
578
579         message("Setting up sam.ldb Samba4 schema")
580         setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
581                        {"SCHEMADN": schemadn })
582         message("Setting up sam.ldb AD schema")
583         setup_add_ldif(samdb, setup_path("schema.ldif"), 
584                        {"SCHEMADN": schemadn})
585
586         message("Setting up sam.ldb configuration data")
587         setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
588             "CONFIGDN": configdn,
589             "NETBIOSNAME": netbiosname,
590             "DEFAULTSITE": DEFAULTSITE,
591             "DNSDOMAIN": dnsdomain,
592             "DOMAIN": domain,
593             "SCHEMADN": schemadn,
594             "DOMAINDN": domaindn,
595             })
596
597         message("Setting up display specifiers")
598         setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
599                        {"CONFIGDN": configdn})
600
601         message("Adding users container (permitted to fail)")
602         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
603             "DOMAINDN": domaindn})
604         message("Modifying users container")
605         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
606             "DOMAINDN": domaindn})
607         message("Adding computers container (permitted to fail)")
608         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
609             "DOMAINDN": domaindn})
610         message("Modifying computers container")
611         setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
612             "DOMAINDN": domaindn})
613         message("Setting up sam.ldb data")
614         setup_add_ldif(samdb, setup_path("provision.ldif"), {
615             "DOMAINDN": domaindn,
616             "NETBIOSNAME": netbiosname,
617             "DEFAULTSITE": DEFAULTSITE,
618             "CONFIGDN": configdn,
619             })
620
621         if not blank:
622
623     #    message("Activate schema module")
624     #    setup_modify_ldif("schema_activation.ldif", info, samdb, False)
625     #
626     #    // (hack) Reload, now we have the schema loaded.  
627     #    commit_ok = samdb.transaction_commit()
628     #    if (!commit_ok) {
629     #        message("samdb commit failed: " + samdb.errstring() + "\n")
630     #        assert(commit_ok)
631     #    }
632     #    samdb.close()
633     #
634     #    samdb = open_ldb(info, paths.samdb, False)
635     #
636             message("Setting up sam.ldb users and groups")
637             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
638                 "DOMAINDN": domaindn,
639                 "DOMAINSID": str(domainsid),
640                 "CONFIGDN": configdn,
641                 "ADMINPASS_B64": b64encode(adminpass),
642                 "KRBTGTPASS_B64": b64encode(krbtgtpass),
643                 })
644
645             if lp.get("server role") == "domain controller":
646                 message("Setting up self join")
647                 if hostguid is not None:
648                     hostguid_add = "objectGUID: %s" % hostguid
649                 else:
650                     hostguid_add = ""
651
652                 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
653                           "CONFIGDN": configdn, 
654                           "SCHEMADN": schemadn,
655                           "DOMAINDN": domaindn,
656                           "INVOCATIONID": invocationid,
657                           "NETBIOSNAME": netbiosname,
658                           "DEFAULTSITE": DEFAULTSITE,
659                           "DNSNAME": "%s.%s" % (hostname, dnsdomain),
660                           "MACHINEPASS_B64": b64encode(machinepass),
661                           "DNSPASS_B64": b64encode(dnspass),
662                           "REALM": realm,
663                           "DOMAIN": domain,
664                           "HOSTGUID_ADD": hostguid_add,
665                           "DNSDOMAIN": dnsdomain})
666                 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
667                           "POLICYGUID": policyguid,
668                           "DNSDOMAIN": dnsdomain,
669                           "DOMAINSID": str(domainsid),
670                           "DOMAINDN": domaindn})
671
672                 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}"), 0755)
673                 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "Machine"), 0755)
674                 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "User"), 0755)
675                 if not os.path.isdir(paths.netlogon):
676                     os.makedirs(paths.netlogon, 0755)
677                 setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), { 
678                     "MACHINEPASS_B64": b64encode(machinepass),
679                     "DOMAIN": domain,
680                     "REALM": realm,
681                     "LDAPTIME": timestring(int(time.time())),
682                     "DNSDOMAIN": dnsdomain,
683                     "DOMAINSID": str(domainsid),
684                     "SECRETS_KEYTAB": paths.keytab,
685                     "NETBIOSNAME": netbiosname,
686                     "SAM_LDB": paths.samdb,
687                     "DNS_KEYTAB": paths.dns_keytab,
688                     "DNSPASS_B64": b64encode(dnspass),
689                     })
690
691             setup_name_mappings(samdb, str(domainsid), 
692                                 domaindn, root=root, nobody=nobody, 
693                                 nogroup=nogroup, wheel=wheel, users=users,
694                                 backup=backup)
695
696         message("Setting up sam.ldb index")
697         samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
698
699         message("Setting up sam.ldb rootDSE marking as syncronized")
700         setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
701     except:
702         samdb.transaction_cancel()
703         raise
704
705     samdb.transaction_commit()
706
707     message("Setting up phpLDAPadmin configuration")
708     create_phplpapdadmin_config(paths.phpldapadminconfig, setup_path, paths.s4_ldapi_path)
709
710     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
711
712     message("Setting up DNS zone: %s" % dnsdomain)
713     create_zone_file(paths.dns, setup_path, samdb, 
714                   hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
715                   domaindn=domaindn, dnspass=dnspass, realm=realm)
716     message("Please install the zone located in %s into your DNS server" % paths.dns)
717
718 def create_phplpapdadmin_config(path, setup_path, s4_ldapi_path):
719     setup_file(setup_path("phpldapadmin-config.php"), 
720                path, {"S4_LDAPI_URI": "ldapi://%s" % s4_ldapi_path.replace("/", "%2F")})
721
722
723 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, 
724                   hostip, hostname, dnspass, realm):
725     """Write out a DNS zone file, from the info in the current database."""
726
727     # connect to the sam
728     # These values may have changed, due to an incoming SamSync,
729     # or may not have been specified, so fetch them from the database
730     domainguid = samdb.searchone(Dn(samdb, domaindn), "objectGUID")
731     hostguid = samdb.searchone(Dn(samdb, domaindn), "objectGUID" ,
732                      expression="(&(objectClass=computer)(cn=%s))" % hostname)
733
734     setup_file(setup_path("provision.zone"), path, {
735             "DNSPASS_B64": b64encode(dnspass),
736             "HOSTNAME": hostname,
737             "DNSDOMAIN": dnsdomain,
738             "REALM": realm,
739             "HOSTIP": hostip,
740             "DOMAINGUID": domainguid,
741             "DATESTRING": time.strftime("%Y%m%d%H"),
742             "DEFAULTSITE": DEFAULTSITE,
743             "HOSTGUID": hostguid,
744         })
745
746
747 def provision_ldapbase(setup_dir, message, paths):
748     """Write out a DNS zone file, from the info in the current database."""
749     message("Setting up LDAP base entry: %s" % domaindn)
750     rdns = domaindn.split(",")
751
752     rdn_dc = rdns[0][len("DC="):]
753
754     def setup_path(file):
755         return os.path.join(setup_dir, file)
756
757     setup_file(setup_path("provision_basedn.ldif"), 
758            paths.ldap_basedn_ldif)
759
760     setup_file(setup_path("provision_configuration_basedn.ldif"), 
761            paths.ldap_config_basedn_ldif)
762
763     setup_file(setup_path("provision_schema_basedn.ldif"), 
764            paths.ldap_schema_basedn_ldif, {
765             "SCHEMADN": schemadn,
766             "ACI": "# no aci for local ldb",
767             "EXTENSIBLEOBJECT": "objectClass: extensibleObject"})
768
769     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")
770
771
772 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
773     """Load schema."""
774     schema_data = open(setup_path("schema.ldif"), 'r').read()
775     schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
776     schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
777     head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
778     head_data = substitute_var(head_data, {
779                     "SCHEMADN": schemadn,
780                     "NETBIOSNAME": netbiosname,
781                     "CONFIGDN": configdn,
782                     "DEFAULTSITE": DEFAULTSITE})
783     samdb.attach_schema_from_ldif(head_data, schema_data)
784
785
786 def join_domain(domain, netbios_name, join_type, creds):
787     ctx = NetContext(creds)
788     joindom = object()
789     joindom.domain = domain
790     joindom.join_type = join_type
791     joindom.netbios_name = netbios_name
792     if not ctx.JoinDomain(joindom):
793         raise Exception("Domain Join failed: " + joindom.error_string)
794
795
796 def vampire(domain, session_info, credentials, message):
797     """Vampire a remote domain.  
798     
799     Session info and credentials are required for for
800     access to our local database (might be remote ldap)
801     """
802     ctx = NetContext(credentials)
803     machine_creds = Credentials()
804     machine_creds.set_domain(form.domain)
805     if not machine_creds.set_machine_account():
806         raise Exception("Failed to access domain join information!")
807     vampire_ctx.machine_creds = machine_creds
808     vampire_ctx.session_info = session_info
809     if not ctx.SamSyncLdb(vampire_ctx):
810         raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string)
811
812
813