r26520: More Python updates.
[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(setup_dir, ldif, session_info, credentials, subobj, lp, dbname, 
234               erase=True):
235     assert dbname is not None
236     ldb = open_ldb(session_info, credentials, lp, dbname)
237     assert ldb is not None
238     ldb.transaction_start()
239     try:
240         if erase:
241             ldb.erase();    
242         setup_add_ldif(setup_dir, ldif, subobj, ldb)
243     except:
244         ldb.transaction_cancel()
245         raise
246     ldb.transaction_commit()
247
248
249 def setup_ldb_modify(setup_dir, ldif, subobj, ldb):
250     """Modify a ldb in the private dir."""
251     src = os.path.join(setup_dir, ldif)
252
253     data = open(src, 'r').read()
254     data = substitute_var(data, subobj.subst_vars())
255     assert not "${" in data
256
257     for (changetype, msg) in ldb.parse_ldif(data):
258         ldb.modify(msg)
259
260
261 def setup_file(setup_dir, template, message, fname, subobj):
262     """Setup a file in the private dir."""
263     f = fname
264     src = os.path.join(setup_dir, template)
265
266     os.unlink(f)
267
268     data = open(src, 'r').read()
269     data = substitute_var(data, subobj.subst_vars())
270     assert not "${" in data
271
272     open(f, 'w').write(data)
273
274
275 def provision_default_paths(lp, subobj):
276     """Set the default paths for provisioning.
277
278     :param lp: Loadparm context.
279     :param subobj: Object
280     """
281     paths = ProvisionPaths()
282     private_dir = lp.get("private dir")
283     paths.shareconf = os.path.join(private_dir, "share.ldb")
284     paths.samdb = lp.get("sam database") or os.path.join(private_dir, "samdb.ldb")
285     paths.secrets = lp.get("secrets database") or os.path.join(private_dir, "secrets.ldb")
286     paths.templates = os.path.join(private_dir, "templates.ldb")
287     paths.keytab = os.path.join(private_dir, "secrets.keytab")
288     paths.dns = os.path.join(private_dir, subobj.dnsdomain + ".zone")
289     paths.winsdb = os.path.join(private_dir, "wins.ldb")
290     paths.ldap_basedn_ldif = os.path.join(private_dir, 
291                                           subobj.dnsdomain + ".ldif")
292     paths.ldap_config_basedn_ldif = os.path.join(private_dir, 
293                                              subobj.dnsdomain + "-config.ldif")
294     paths.ldap_schema_basedn_ldif = os.path.join(private_dir, 
295                                               subobj.dnsdomain + "-schema.ldif")
296     paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
297     paths.phpldapadminconfig = os.path.join(private_dir, 
298                                             "phpldapadmin-config.php")
299     paths.hklm = os.path.join(private_dir, "hklm.ldb")
300     return paths
301
302
303 def setup_name_mappings(subobj, ldb):
304     """setup reasonable name mappings for sam names to unix names."""
305     res = ldb.search(Dn(ldb, subobj.domaindn), SCOPE_BASE, "objectSid=*", 
306                      ["objectSid"])
307     assert len(res) == 1
308     assert "objectSid" in res[0]
309     sid = str(list(res[0]["objectSid"])[0])
310
311     # add some foreign sids if they are not present already
312     ldb.add_foreign(subobj.domaindn, "S-1-5-7", "Anonymous")
313     ldb.add_foreign(subobj.domaindn, "S-1-1-0", "World")
314     ldb.add_foreign(subobj.domaindn, "S-1-5-2", "Network")
315     ldb.add_foreign(subobj.domaindn, "S-1-5-18", "System")
316     ldb.add_foreign(subobj.domaindn, "S-1-5-11", "Authenticated Users")
317
318     # some well known sids
319     ldb.setup_name_mapping(subobj.domaindn, "S-1-5-7", subobj.nobody)
320     ldb.setup_name_mapping(subobj.domaindn, "S-1-1-0", subobj.nogroup)
321     ldb.setup_name_mapping(subobj.domaindn, "S-1-5-2", subobj.nogroup)
322     ldb.setup_name_mapping(subobj.domaindn, "S-1-5-18", subobj.root)
323     ldb.setup_name_mapping(subobj.domaindn, "S-1-5-11", subobj.users)
324     ldb.setup_name_mapping(subobj.domaindn, "S-1-5-32-544", subobj.wheel)
325     ldb.setup_name_mapping(subobj.domaindn, "S-1-5-32-545", subobj.users)
326     ldb.setup_name_mapping(subobj.domaindn, "S-1-5-32-546", subobj.nogroup)
327     ldb.setup_name_mapping(subobj.domaindn, "S-1-5-32-551", subobj.backup)
328
329     # and some well known domain rids
330     ldb.setup_name_mapping(subobj.domaindn, sid + "-500", subobj.root)
331     ldb.setup_name_mapping(subobj.domaindn, sid + "-518", subobj.wheel)
332     ldb.setup_name_mapping(subobj.domaindn, sid + "-519", subobj.wheel)
333     ldb.setup_name_mapping(subobj.domaindn, sid + "-512", subobj.wheel)
334     ldb.setup_name_mapping(subobj.domaindn, sid + "-513", subobj.users)
335     ldb.setup_name_mapping(subobj.domaindn, sid + "-520", subobj.wheel)
336
337
338 def provision_become_dc(setup_dir, subobj, message, paths, lp, session_info, 
339                         credentials):
340     assert session_info is not None
341     subobj.fix(paths)
342
343     message("Setting up templates into %s" % paths.templates)
344     setup_ldb(setup_dir, "provision_templates.ldif", session_info,
345               credentials, subobj, lp, paths.templates)
346
347     # Also wipes the database
348     message("Setting up %s partitions" % paths.samdb)
349     setup_ldb(setup_dir, "provision_partitions.ldif", session_info, 
350               credentials, subobj, lp, paths.samdb)
351
352     samdb = SamDB(paths.samdb, session_info=session_info, 
353                   credentials=credentials, lp=lp)
354     ldb.transaction_start()
355     try:
356         message("Setting up %s attributes" % paths.samdb)
357         setup_add_ldif(setup_dir, "provision_init.ldif", subobj, samdb)
358
359         message("Setting up %s rootDSE" % paths.samdb)
360         setup_add_ldif(setup_dir, "provision_rootdse_add.ldif", subobj, samdb)
361
362         message("Erasing data from partitions")
363         ldb_erase_partitions(subobj, message, samdb, None)
364
365         message("Setting up %s indexes" % paths.samdb)
366         setup_add_ldif(setup_dir, "provision_index.ldif", subobj, samdb)
367     except:
368         samdb.transaction_cancel()
369         raise
370
371     samdb.transaction_commit()
372
373     message("Setting up %s" % paths.secrets)
374     setup_ldb(setup_dir, "secrets_init.ldif", session_info, credentials, 
375               subobj, lp, paths.secrets)
376
377     setup_ldb(setup_dir, "secrets.ldif", session_info, credentials, subobj, 
378               lp, paths.secrets, False)
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         setup_ldb(setup_dir, "share.ldif", session_info, credentials, subobj, 
412                   lp, paths.shareconf)
413
414     message("Setting up %s" % paths.secrets)
415     setup_ldb(setup_dir, "secrets_init.ldif", session_info, credentials, 
416               subobj, lp, paths.secrets)
417     setup_ldb(setup_dir, "secrets.ldif", session_info, credentials, subobj, 
418               lp, paths.secrets, False)
419
420     message("Setting up registry")
421     reg = registry.Registry()
422     #hive = registry.Hive(paths.hklm, session_info=session_info, 
423     #                     credentials=credentials, lp_ctx=lp)
424     #reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
425     provision_reg = os.path.join(setup_dir, "provision.reg")
426     assert os.path.exists(provision_reg)
427     #reg.apply_patchfile(provision_reg)
428
429     message("Setting up templates into %s" % paths.templates)
430     setup_ldb(setup_dir, "provision_templates.ldif", session_info, 
431               credentials, subobj, lp, paths.templates)
432
433     message("Setting up sam.ldb partitions")
434     setup_ldb(setup_dir, "provision_partitions.ldif", session_info, 
435               credentials, subobj, lp, paths.samdb)
436
437     samdb = SamDB(paths.samdb, session_info=session_info, 
438                   credentials=credentials, lp=lp)
439     samdb.transaction_start()
440     try:
441         message("Setting up sam.ldb attributes")
442         setup_add_ldif(setup_dir, "provision_init.ldif", subobj, samdb)
443
444         message("Setting up sam.ldb rootDSE")
445         setup_add_ldif(setup_dir, "provision_rootdse_add.ldif", subobj, samdb)
446
447         message("Erasing data from partitions")
448         ldb_erase_partitions(subobj, message, samdb, ldapbackend)
449     except:
450         samdb.transaction_cancel()
451         raise
452
453     samdb.transaction_commit()
454
455     message("Pre-loading the Samba 4 and AD schema")
456
457     samdb = SamDB(paths.samdb, session_info=session_info, 
458                   credentials=credentials, lp=lp)
459     samdb.set_domain_sid(subobj.domainsid)
460     load_schema(setup_dir, subobj, samdb)
461
462     samdb.transaction_start()
463         
464     try:
465         message("Adding DomainDN: %s (permitted to fail)" % subobj.domaindn)
466         setup_add_ldif(setup_dir, "provision_basedn.ldif", subobj, samdb)
467         message("Modifying DomainDN: " + subobj.domaindn + "")
468         setup_ldb_modify(setup_dir, "provision_basedn_modify.ldif", subobj, samdb)
469
470         message("Adding configuration container (permitted to fail)")
471         setup_add_ldif(setup_dir, "provision_configuration_basedn.ldif", subobj, samdb)
472         message("Modifying configuration container")
473         setup_ldb_modify(setup_dir, "provision_configuration_basedn_modify.ldif", subobj, samdb)
474
475         message("Adding schema container (permitted to fail)")
476         setup_add_ldif(setup_dir, "provision_schema_basedn.ldif", subobj, samdb)
477         message("Modifying schema container")
478         setup_ldb_modify(setup_dir, "provision_schema_basedn_modify.ldif", subobj, samdb)
479         message("Setting up sam.ldb Samba4 schema")
480         setup_add_ldif(setup_dir, "schema_samba4.ldif", subobj, samdb)
481         message("Setting up sam.ldb AD schema")
482         setup_add_ldif(setup_dir, "schema.ldif", subobj, samdb)
483
484         message("Setting up sam.ldb configuration data")
485         setup_add_ldif(setup_dir, "provision_configuration.ldif", subobj, samdb)
486
487         message("Setting up display specifiers")
488         setup_add_ldif(setup_dir, "display_specifiers.ldif", subobj, samdb)
489
490         message("Adding users container (permitted to fail)")
491         setup_add_ldif(setup_dir, "provision_users_add.ldif", subobj, samdb)
492         message("Modifying users container")
493         setup_ldb_modify(setup_dir, "provision_users_modify.ldif", subobj, samdb)
494         message("Adding computers container (permitted to fail)")
495         setup_add_ldif(setup_dir, "provision_computers_add.ldif", subobj, samdb)
496         message("Modifying computers container")
497         setup_ldb_modify(setup_dir, "provision_computers_modify.ldif", subobj, samdb)
498         message("Setting up sam.ldb data")
499         setup_add_ldif(setup_dir, "provision.ldif", subobj, samdb)
500
501         if blank:
502             message("Setting up sam.ldb index")
503             setup_add_ldif(setup_dir, "provision_index.ldif", subobj, samdb)
504
505             message("Setting up sam.ldb rootDSE marking as syncronized")
506             setup_modify_ldif(setup_dir, "provision_rootdse_modify.ldif", subobj, samdb)
507
508             samdb.transaction_commit()
509             return
510
511     #    message("Activate schema module")
512     #    setup_modify_ldif("schema_activation.ldif", info, samdb, False)
513     #
514     #    // (hack) Reload, now we have the schema loaded.  
515     #    commit_ok = samdb.transaction_commit()
516     #    if (!commit_ok) {
517     #        message("samdb commit failed: " + samdb.errstring() + "\n")
518     #        assert(commit_ok)
519     #    }
520     #    samdb.close()
521     #
522     #    samdb = open_ldb(info, paths.samdb, False)
523     #
524         message("Setting up sam.ldb users and groups")
525         setup_add_ldif(setup_dir, "provision_users.ldif", subobj, samdb)
526
527         setup_name_mappings(subobj, samdb)
528
529         message("Setting up sam.ldb index")
530         setup_add_ldif(setup_dir, "provision_index.ldif", subobj, samdb)
531
532         message("Setting up sam.ldb rootDSE marking as syncronized")
533         setup_modify_ldif(setup_dir, "provision_rootdse_modify.ldif", subobj, samdb)
534     except:
535         samdb.transaction_cancel()
536         raise
537
538     samdb.transaction_commit()
539
540     message("Setting up phpLDAPadmin configuration")
541     setup_file(setup_dir, "phpldapadmin-config.php", message, 
542                paths.phpldapadminconfig, subobj)
543     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
544
545
546 def provision_dns(setup_dir, subobj, message, paths, session_info, credentials):
547     """Write out a DNS zone file, from the info in the current database."""
548     message("Setting up DNS zone: %s" % subobj.dnsdomain)
549     # connect to the sam
550     ldb = SamDB(paths.samdb, session_info=session_info, credentials=credentials,
551                 lp=lp)
552
553     # These values may have changed, due to an incoming SamSync,
554     # or may not have been specified, so fetch them from the database
555
556     res = ldb.search(Dn(ldb, subobj.domaindn), SCOPE_BASE, "objectGUID=*", 
557                      ["objectGUID"])
558     assert(len(res) == 1)
559     assert(res[0]["objectGUID"] is not None)
560     subobj.domainguid = res[0]["objectGUID"]
561
562     subobj.host_guid = ldb.searchone(subobj.domaindn, 
563                                  "(&(objectClass=computer)(cn=%s))" % subobj.netbiosname, "objectGUID")
564     assert subobj.host_guid is not None
565
566     setup_file(setup_dir, "provision.zone", message, paths.dns, subobj)
567
568     message("Please install the zone located in %s into your DNS server" % paths.dns)
569
570
571 def provision_ldapbase(setup_dir, subobj, message, paths):
572     """Write out a DNS zone file, from the info in the current database."""
573     message("Setting up LDAP base entry: %s" % subobj.domaindn)
574     rdns = subobj.domaindn.split(",")
575     subobj.extensibleobject = "objectClass: extensibleObject"
576
577     subobj.rdn_dc = rdns[0][len("DC="):]
578
579     setup_file(setup_dir, "provision_basedn.ldif", 
580            message, paths.ldap_basedn_ldif, 
581            subobj)
582
583     setup_file(setup_dir, "provision_configuration_basedn.ldif", 
584            message, paths.ldap_config_basedn_ldif, 
585            subobj)
586
587     setup_file(setup_dir, "provision_schema_basedn.ldif", 
588            message, paths.ldap_schema_basedn_ldif, 
589            subobj)
590
591     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")
592
593
594 def provision_guess(lp):
595     """guess reasonably default options for provisioning."""
596     subobj = ProvisionSettings(realm=lp.get("realm").upper(),
597                                domain=lp.get("workgroup"),
598                                hostname=hostname(), 
599                                hostip=hostip())
600
601     assert subobj.realm is not None
602     assert subobj.domain is not None
603     assert subobj.hostname is not None
604     
605     subobj.domainsid    = security.random_sid()
606     subobj.invocationid = uuid.random()
607     subobj.policyguid   = uuid.random()
608     subobj.krbtgtpass   = misc.random_password(12)
609     subobj.machinepass  = misc.random_password(12)
610     subobj.adminpass    = misc.random_password(12)
611     subobj.dnspass      = misc.random_password(12)
612     subobj.datestring   = time.strftime("%Y%m%d%H")
613     subobj.root         = findnss(pwd.getpwnam, "root")[4]
614     subobj.nobody       = findnss(pwd.getpwnam, "nobody")[4]
615     subobj.nogroup      = findnss(grp.getgrnam, "nogroup", "nobody")[2]
616     subobj.wheel        = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
617     subobj.backup       = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
618     subobj.users        = findnss(grp.getgrnam, "users", "guest", "other", "unknown", "usr")[2]
619
620     subobj.dnsdomain    = subobj.realm.lower()
621     subobj.dnsname      = "%s.%s" % (subobj.hostname.lower(), subobj.dnsdomain)
622     subobj.domaindn     = "DC=" + subobj.dnsdomain.replace(".", ",DC=")
623     subobj.domaindn_ldb = "users.ldb"
624     subobj.rootdn       = subobj.domaindn
625     subobj.configdn     = "CN=Configuration," + subobj.rootdn
626     subobj.configdn_ldb = "configuration.ldb"
627     subobj.schemadn     = "CN=Schema," + subobj.configdn
628     subobj.schemadn_ldb = "schema.ldb"
629
630     #Add modules to the list to activate them by default
631     #beware often order is important
632     #
633     # Some Known ordering constraints:
634     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
635     # - objectclass must be before password_hash, because password_hash checks
636     #   that the objectclass is of type person (filled in by objectclass
637     #   module when expanding the objectclass list)
638     # - partition must be last
639     # - each partition has its own module list then
640     subobj.modules_list = ["rootdse",
641                     "paged_results",
642                     "ranged_results",
643                     "anr",
644                     "server_sort",
645                     "extended_dn",
646                     "asq",
647                     "samldb",
648                     "rdn_name",
649                     "objectclass",
650                     "kludge_acl",
651                     "operational"]
652     subobj.tdb_modules_list = [
653                     "subtree_rename",
654                     "subtree_delete",
655                     "linked_attributes"]
656     subobj.modules_list2 = ["show_deleted",
657                     "partition"]
658
659     subobj.extensibleobject = "# no objectClass: extensibleObject for local ldb"
660     subobj.aci        = "# no aci for local ldb"
661     return subobj
662
663
664 def load_schema(setup_dir, subobj, samdb):
665     """Load schema."""
666     src = os.path.join(setup_dir, "schema.ldif")
667     schema_data = open(src, 'r').read()
668     src = os.path.join(setup_dir, "schema_samba4.ldif")
669     schema_data += open(src, 'r').read()
670     schema_data = substitute_var(schema_data, subobj.subst_vars())
671     src = os.path.join(setup_dir, "provision_schema_basedn_modify.ldif")
672     head_data = open(src, 'r').read()
673     head_data = substitute_var(head_data, subobj.subst_vars())
674     samdb.attach_schema_from_ldif(head_data, schema_data)
675
676
677 def join_domain(domain, netbios_name, join_type, creds, message):
678     ctx = NetContext(creds)
679     joindom = object()
680     joindom.domain = domain
681     joindom.join_type = join_type
682     joindom.netbios_name = netbios_name
683     if not ctx.JoinDomain(joindom):
684         raise Exception("Domain Join failed: " + joindom.error_string)
685
686
687 def vampire(domain, session_info, credentials, message):
688     """Vampire a remote domain.  
689     
690     Session info and credentials are required for for
691     access to our local database (might be remote ldap)
692     """
693     ctx = NetContext(credentials)
694     vampire_ctx = object()
695     machine_creds = credentials_init()
696     machine_creds.set_domain(form.domain)
697     if not machine_creds.set_machine_account():
698         raise Exception("Failed to access domain join information!")
699     vampire_ctx.machine_creds = machine_creds
700     vampire_ctx.session_info = session_info
701     if not ctx.SamSyncLdb(vampire_ctx):
702         raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string)
703
704
705 def ldb_erase_partitions(subobj, message, ldb, ldapbackend):
706     """Erase an ldb, removing all records."""
707     assert ldb is not None
708     res = ldb.search(Dn(ldb, ""), SCOPE_BASE, "(objectClass=*)", 
709                      ["namingContexts"])
710     assert len(res) == 1
711     if not "namingContexts" in res[0]:
712         return
713     for basedn in res[0]["namingContexts"]:
714         anything = "(|(objectclass=*)(dn=*))"
715         previous_remaining = 1
716         current_remaining = 0
717
718         if ldapbackend and (basedn == subobj.domaindn):
719             # Only delete objects that were created by provision
720             anything = "(objectcategory=*)"
721
722         k = 0
723         while ++k < 10 and (previous_remaining != current_remaining):
724             # and the rest
725             res2 = ldb.search(Dn(ldb, basedn), SCOPE_SUBTREE, anything, ["dn"])
726             previous_remaining = current_remaining
727             current_remaining = len(res2)
728             for msg in res2:
729                 try:
730                     ldb.delete(msg.dn)
731                 except LdbError, (_, text):
732                     message("Unable to delete %s: %s" % (msg.dn, text))
733
734