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