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