r26499: Allow testing python provision by setting the PROVISION_PYTHON environment...
[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 from samba import Ldb, substitute_var, valid_netbios_name
18 from samba.samdb import SamDB
19 from ldb import Dn, SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
20         LDB_ERR_NO_SUCH_OBJECT, timestring
21
22
23 class InvalidNetbiosName(Exception):
24     def __init__(self, name):
25         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
26
27
28 class ProvisionSettings(object):
29     def __init__(self, realm=None, domain=None, hostname=None, hostip=None):
30         self.realm = realm
31         self.domain = domain
32         self.hostname = hostname
33         self.hostip = hostip
34         self.domainsid = None
35         self.invocationid = None
36         self.krbtgtpass = None
37         self.machinepass = None
38         self.adminpass = None
39         self.defaultsite  = "Default-First-Site-Name"
40         self.datestring = None
41         self.root = None
42         self.nobody = None
43         self.nogroup = None
44         self.wheel = None
45         self.backup = None
46         self.users = None
47         self.dnsdomain = None
48         self.dnsname = None
49         self.domaindn = None
50         self.domaindn_ldb = None
51         self.rootdn = None
52         self.configdn = None
53         self.configdn_ldb = None
54         self.schemedn = None
55         self.schemedn_ldb = None
56         self.s4_ldapi_path = None
57         self.policyguid = None
58
59     def subst_vars(self):
60         return {"SCHEMADN": self.schemadn,
61                 "SCHEMADN_LDB": self.schemadn_ldb,
62                 "SCHEMADN_MOD": "schema_fsmo",
63                 "SCHEMADN_MOD2": ",objectguid",
64                 "CONFIGDN": self.configdn,
65                 "TDB_MODULES_LIST": ","+",".join(self.tdb_modules_list),
66                 "MODULES_LIST2": ",".join(self.modules_list2),
67                 "CONFIGDN_LDB": self.configdn_ldb,
68                 "DOMAINDN": self.domaindn,
69                 "DOMAINDN_LDB": self.domaindn_ldb,
70                 "DOMAINDN_MOD": "pdc_fsmo,password_hash",
71                 "DOMAINDN_MOD2": ",objectguid",
72                 "DOMAINSID": self.domainsid,
73                 "MODULES_LIST": ",".join(self.modules_list),
74                 "CONFIGDN_MOD": "naming_fsmo",
75                 "CONFIGDN_MOD2": ",objectguid",
76                 "NETBIOSNAME": self.netbiosname,
77                 "DNSNAME": self.dnsname,
78                 "ROOTDN": self.rootdn,
79                 "DNSDOMAIN": self.dnsdomain,
80                 "REALM": self.realm,
81                 "DEFAULTSITE": self.defaultsite,
82                 "MACHINEPASS_B64": b64encode(self.machinepass),
83                 "ADMINPASS_B64": b64encode(self.adminpass),
84                 "DNSPASS_B64": b64encode(self.dnspass),
85                 "KRBTGTPASS_B64": b64encode(self.krbtgtpass),
86                 "S4_LDAPI_URI": "ldapi://%s" % self.s4_ldapi_path.replace("/", "%2F"),
87                 "LDAPTIME": timestring(int(time.time())),
88                 "POLICYGUID": self.policyguid,
89                 "RDN_DC": self.rdn_dc,
90                 "DOMAINGUID_MOD": self.domainguid_mod,
91                 }
92
93     def fix(self, paths):
94         self.realm       = self.realm.upper()
95         self.hostname    = self.hostname.lower()
96         self.domain      = self.domain.upper()
97         if not valid_netbios_name(self.domain):
98             raise InvalidNetbiosName(self.domain)
99         self.netbiosname = self.hostname.upper()
100         if not valid_netbios_name(self.netbiosname):
101             raise InvalidNetbiosName(self.netbiosname)
102         rdns = self.domaindn.split(",")
103         self.rdn_dc = rdns[0][len("DC="):]
104
105         self.sam_ldb        = paths.samdb
106         self.secrets_ldb    = paths.secrets
107         self.secrets_keytab    = paths.keytab
108         
109         self.s4_ldapi_path = paths.s4_ldapi_path
110
111     def validate(self, lp):
112         if not valid_netbios_name(self.domain):
113             raise InvalidNetbiosName(self.domain)
114
115         if not valid_netbios_name(self.netbiosname):
116             raise InvalidNetbiosName(self.netbiosname)
117
118         if lp.get("workgroup").upper() != self.domain.upper():
119             raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'\n",
120                 lp.get("workgroup"), self.domain)
121
122         if lp.get("realm").upper() != self.realm.upper():
123             raise Error("realm '%s' in smb.conf must match chosen realm '%s'\n" %
124                 (lp.get("realm"), self.realm))
125
126
127 class ProvisionPaths:
128     def __init__(self):
129         self.smbconf = None
130         self.shareconf = None
131         self.hklm = None
132         self.hkcu = None
133         self.hkcr = None
134         self.hku = None
135         self.hkpd = None
136         self.hkpt = None
137         self.samdb = None
138         self.secrets = None
139         self.keytab = None
140         self.dns = None
141         self.winsdb = None
142         self.ldap_basedn_ldif = None
143         self.ldap_config_basedn_ldif = None
144         self.ldap_schema_basedn_ldif = None
145         self.s4_ldapi_path = None
146
147
148 def install_ok(lp, session_info, credentials):
149     """Check whether the current install seems ok."""
150     if lp.get("realm") == "":
151         return False
152     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
153             credentials=credentials)
154     if len(ldb.search("(cn=Administrator)")) != 1:
155         return False
156     return True
157
158
159 def findnss(nssfn, *names):
160     """Find a user or group from a list of possibilities."""
161     for name in names:
162         try:
163             return nssfn(name)
164         except KeyError:
165             pass
166     raise Exception("Unable to find user/group for %s" % arguments[1])
167
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(ldb);    
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("private dir")
275     paths.shareconf = os.path.join(private_dir, "share.ldb")
276     paths.samdb = lp.get("sam database") or os.path.join(private_dir, "samdb.ldb")
277     paths.secrets = lp.get("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 = open_ldb(session_info, credentials, paths.samdb)
437
438     samdb.set_domain_sid(subobj.domainsid)
439
440     load_schema(setup_dir, subobj, samdb)
441
442     samdb.transaction_start()
443         
444     try:
445         message("Adding DomainDN: %s (permitted to fail)" % subobj.domaindn)
446         setup_add_ldif(setup_dir, "provision_basedn.ldif", subobj, samdb)
447         message("Modifying DomainDN: " + subobj.domaindn + "")
448         setup_ldb_modify(setup_dir, "provision_basedn_modify.ldif", subobj, samdb)
449
450         message("Adding configuration container (permitted to fail)")
451         setup_add_ldif(setup_dir, "provision_configuration_basedn.ldif", subobj, samdb)
452         message("Modifying configuration container")
453         setup_ldb_modify(setup_dir, "provision_configuration_basedn_modify.ldif", subobj, samdb)
454
455         message("Adding schema container (permitted to fail)")
456         setup_add_ldif(setup_dir, "provision_schema_basedn.ldif", subobj, samdb)
457         message("Modifying schema container")
458         setup_ldb_modify(setup_dir, "provision_schema_basedn_modify.ldif", subobj, samdb)
459         message("Setting up sam.ldb Samba4 schema")
460         setup_add_ldif(setup_dir, "schema_samba4.ldif", subobj, samdb)
461         message("Setting up sam.ldb AD schema")
462         setup_add_ldif(setup_dir, "schema.ldif", subobj, samdb)
463
464         message("Setting up sam.ldb configuration data")
465         setup_add_ldif(setup_dir, "provision_configuration.ldif", subobj, samdb)
466
467         message("Setting up display specifiers")
468         setup_add_ldif(setup_dir, "display_specifiers.ldif", subobj, samdb)
469
470         message("Adding users container (permitted to fail)")
471         setup_add_ldif(setup_dir, "provision_users_add.ldif", subobj, samdb)
472         message("Modifying users container")
473         setup_ldb_modify(setup_dir, "provision_users_modify.ldif", subobj, samdb)
474         message("Adding computers container (permitted to fail)")
475         setup_add_ldif(setup_dir, "provision_computers_add.ldif", subobj, samdb)
476         message("Modifying computers container")
477         setup_ldb_modify(setup_dir, "provision_computers_modify.ldif", subobj, samdb)
478         message("Setting up sam.ldb data")
479         setup_add_ldif(setup_dir, "provision.ldif", subobj, samdb)
480
481         if blank:
482             message("Setting up sam.ldb index")
483             setup_add_ldif(setup_dir, "provision_index.ldif", subobj, samdb)
484
485             message("Setting up sam.ldb rootDSE marking as syncronized")
486             setup_modify_ldif(setup_dir, "provision_rootdse_modify.ldif", subobj, samdb)
487
488             samdb.transaction_commit()
489             return
490
491     #    message("Activate schema module")
492     #    setup_modify_ldif("schema_activation.ldif", info, samdb, False)
493     #
494     #    // (hack) Reload, now we have the schema loaded.  
495     #    commit_ok = samdb.transaction_commit()
496     #    if (!commit_ok) {
497     #        message("samdb commit failed: " + samdb.errstring() + "\n")
498     #        assert(commit_ok)
499     #    }
500     #    samdb.close()
501     #
502     #    samdb = open_ldb(info, paths.samdb, False)
503     #
504         message("Setting up sam.ldb users and groups")
505         setup_add_ldif(setup_dir, "provision_users.ldif", subobj, samdb)
506
507         setup_name_mappings(subobj, samdb)
508
509         message("Setting up sam.ldb index")
510         setup_add_ldif(setup_dir, "provision_index.ldif", subobj, samdb)
511
512         message("Setting up sam.ldb rootDSE marking as syncronized")
513         setup_modify_ldif(setup_dir, "provision_rootdse_modify.ldif", subobj, samdb)
514     except:
515         samdb.transaction_cancel()
516         raise
517
518     samdb.transaction_commit()
519
520     message("Setting up phpLDAPadmin configuration")
521     setup_file(setup_dir, "phpldapadmin-config.php", message, 
522                paths.phpldapadminconfig, subobj)
523     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
524
525
526 def provision_dns(setup_dir, subobj, message, paths, session_info, credentials):
527     """Write out a DNS zone file, from the info in the current database."""
528     message("Setting up DNS zone: %s" % subobj.dnsdomain)
529     # connect to the sam
530     ldb = SamDB(paths.samdb, session_info=session_info, credentials=credentials)
531
532     # These values may have changed, due to an incoming SamSync,
533     # or may not have been specified, so fetch them from the database
534
535     res = ldb.search(Dn(ldb, subobj.domaindn), SCOPE_BASE, "objectGUID=*", 
536                      ["objectGUID"])
537     assert(len(res) == 1)
538     assert(res[0]["objectGUID"] is not None)
539     subobj.domainguid = res[0]["objectGUID"]
540
541     subobj.host_guid = ldb.searchone(subobj.domaindn, 
542                                  "(&(objectClass=computer)(cn=%s))" % subobj.netbiosname, "objectGUID")
543     assert subobj.host_guid is not None
544
545     setup_file(setup_dir, "provision.zone", message, paths.dns, subobj)
546
547     message("Please install the zone located in %s into your DNS server" % paths.dns)
548
549
550 def provision_ldapbase(setup_dir, subobj, message, paths):
551     """Write out a DNS zone file, from the info in the current database."""
552     message("Setting up LDAP base entry: %s" % subobj.domaindn)
553     rdns = subobj.domaindn.split(",")
554     subobj.extensibleobject = "objectClass: extensibleObject"
555
556     subobj.rdn_dc = rdns[0][len("DC="):]
557
558     setup_file(setup_dir, "provision_basedn.ldif", 
559            message, paths.ldap_basedn_ldif, 
560            subobj)
561
562     setup_file(setup_dir, "provision_configuration_basedn.ldif", 
563            message, paths.ldap_config_basedn_ldif, 
564            subobj)
565
566     setup_file(setup_dir, "provision_schema_basedn.ldif", 
567            message, paths.ldap_schema_basedn_ldif, 
568            subobj)
569
570     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")
571
572
573 def provision_guess(lp):
574     """guess reasonably default options for provisioning."""
575     subobj = ProvisionSettings(realm=lp.get("realm").upper(),
576                                domain=lp.get("workgroup"),
577                                hostname=hostname(), 
578                                hostip=hostip())
579
580     assert subobj.realm is not None
581     assert subobj.domain is not None
582     assert subobj.hostname is not None
583     
584     subobj.domainsid    = sid.random()
585     subobj.invocationid = uuid.random()
586     subobj.policyguid   = uuid.random()
587     subobj.krbtgtpass   = misc.random_password(12)
588     subobj.machinepass  = misc.random_password(12)
589     subobj.adminpass    = misc.random_password(12)
590     subobj.dnspass      = misc.random_password(12)
591     subobj.datestring   = time.strftime("%Y%m%d%H")
592     subobj.root         = findnss(pwd.getpwnam, "root")[4]
593     subobj.nobody       = findnss(pwd.getpwnam, "nobody")[4]
594     subobj.nogroup      = findnss(grp.getgrnam, "nogroup", "nobody")[2]
595     subobj.wheel        = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
596     subobj.backup       = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
597     subobj.users        = findnss(grp.getgrnam, "users", "guest", "other", "unknown", "usr")[2]
598
599     subobj.dnsdomain    = subobj.realm.lower()
600     subobj.dnsname      = "%s.%s" % (subobj.hostname.lower(), subobj.dnsdomain)
601     subobj.domaindn     = "DC=" + subobj.dnsdomain.replace(".", ",DC=")
602     subobj.domaindn_ldb = "users.ldb"
603     subobj.rootdn       = subobj.domaindn
604     subobj.configdn     = "CN=Configuration," + subobj.rootdn
605     subobj.configdn_ldb = "configuration.ldb"
606     subobj.schemadn     = "CN=Schema," + subobj.configdn
607     subobj.schemadn_ldb = "schema.ldb"
608
609     #Add modules to the list to activate them by default
610     #beware often order is important
611     #
612     # Some Known ordering constraints:
613     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
614     # - objectclass must be before password_hash, because password_hash checks
615     #   that the objectclass is of type person (filled in by objectclass
616     #   module when expanding the objectclass list)
617     # - partition must be last
618     # - each partition has its own module list then
619     subobj.modules_list = ["rootdse",
620                     "paged_results",
621                     "ranged_results",
622                     "anr",
623                     "server_sort",
624                     "extended_dn",
625                     "asq",
626                     "samldb",
627                     "rdn_name",
628                     "objectclass",
629                     "kludge_acl",
630                     "operational"]
631     subobj.tdb_modules_list = [
632                     "subtree_rename",
633                     "subtree_delete",
634                     "linked_attributes"]
635     subobj.modules_list2 = ["show_deleted",
636                     "partition"]
637
638     subobj.extensibleobject = "# no objectClass: extensibleObject for local ldb"
639     subobj.aci        = "# no aci for local ldb"
640     return subobj
641
642
643 def load_schema(setup_dir, subobj, samdb):
644     """Load schema."""
645     src = os.path.join(setup_dir, "schema.ldif")
646     schema_data = open(src, 'r').read()
647     src = os.path.join(setup_dir, "schema_samba4.ldif")
648     schema_data += open(src, 'r').read()
649     schema_data = substitute_var(schema_data, subobj.subst_vars())
650     src = os.path.join(setup_dir, "provision_schema_basedn_modify.ldif")
651     head_data = open(src, 'r').read()
652     head_data = substitute_var(head_data, subobj.subst_vars())
653     samdb.attach_dsdb_schema_from_ldif(head_data, schema_data)
654
655
656 def join_domain(domain, netbios_name, join_type, creds, message):
657     ctx = NetContext(creds)
658     joindom = object()
659     joindom.domain = domain
660     joindom.join_type = join_type
661     joindom.netbios_name = netbios_name
662     if not ctx.JoinDomain(joindom):
663         raise Exception("Domain Join failed: " + joindom.error_string)
664
665
666 def vampire(domain, session_info, credentials, message):
667     """Vampire a remote domain.  
668     
669     Session info and credentials are required for for
670     access to our local database (might be remote ldap)
671     """
672     ctx = NetContext(credentials)
673     vampire_ctx = object()
674     machine_creds = credentials_init()
675     machine_creds.set_domain(form.domain)
676     if not machine_creds.set_machine_account():
677         raise Exception("Failed to access domain join information!")
678     vampire_ctx.machine_creds = machine_creds
679     vampire_ctx.session_info = session_info
680     if not ctx.SamSyncLdb(vampire_ctx):
681         raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string)
682
683
684 def ldb_erase_partitions(subobj, message, ldb, ldapbackend):
685     """Erase an ldb, removing all records."""
686     assert ldb is not None
687     res = ldb.search(Dn(ldb, ""), SCOPE_BASE, "(objectClass=*)", 
688                      ["namingContexts"])
689     assert len(res) == 1
690     if not "namingContexts" in res[0]:
691         return
692     for basedn in res[0]["namingContexts"]:
693         anything = "(|(objectclass=*)(dn=*))"
694         previous_remaining = 1
695         current_remaining = 0
696
697         if ldapbackend and (basedn == subobj.domaindn):
698             # Only delete objects that were created by provision
699             anything = "(objectcategory=*)"
700
701         k = 0
702         while ++k < 10 and (previous_remaining != current_remaining):
703             # and the rest
704             res2 = ldb.search(Dn(ldb, basedn), SCOPE_SUBTREE, anything, ["dn"])
705             previous_remaining = current_remaining
706             current_remaining = len(res2)
707             for msg in res2:
708                 try:
709                     ldb.delete(msg.dn)
710                 except LdbError, (_, text):
711                     message("Unable to delete %s: %s" % (msg.dn, text))
712
713