d9863420b694238b56f31ec16ad11e3c415402e8
[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 = 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, registry.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 = open_ldb(session_info, credentials, lp, paths.samdb)
438     samdb.transaction_start()
439     try:
440         message("Setting up sam.ldb attributes")
441         setup_add_ldif(setup_dir, "provision_init.ldif", subobj, samdb)
442
443         message("Setting up sam.ldb rootDSE")
444         setup_add_ldif(setup_dir, "provision_rootdse_add.ldif", subobj, samdb)
445
446         message("Erasing data from partitions")
447         ldb_erase_partitions(subobj, message, samdb, ldapbackend)
448     except:
449         samdb.transaction_cancel()
450         raise
451
452     samdb.transaction_commit()
453
454     message("Pre-loading the Samba 4 and AD schema")
455
456     samdb = SamDB(paths.samdb, session_info=session_info, 
457                   credentials=credentials, lp=lp)
458     samdb.set_domain_sid(subobj.domainsid)
459     load_schema(setup_dir, subobj, samdb)
460
461     samdb.transaction_start()
462         
463     try:
464         message("Adding DomainDN: %s (permitted to fail)" % subobj.domaindn)
465         setup_add_ldif(setup_dir, "provision_basedn.ldif", subobj, samdb)
466         message("Modifying DomainDN: " + subobj.domaindn + "")
467         setup_ldb_modify(setup_dir, "provision_basedn_modify.ldif", subobj, samdb)
468
469         message("Adding configuration container (permitted to fail)")
470         setup_add_ldif(setup_dir, "provision_configuration_basedn.ldif", subobj, samdb)
471         message("Modifying configuration container")
472         setup_ldb_modify(setup_dir, "provision_configuration_basedn_modify.ldif", subobj, samdb)
473
474         message("Adding schema container (permitted to fail)")
475         setup_add_ldif(setup_dir, "provision_schema_basedn.ldif", subobj, samdb)
476         message("Modifying schema container")
477         setup_ldb_modify(setup_dir, "provision_schema_basedn_modify.ldif", subobj, samdb)
478         message("Setting up sam.ldb Samba4 schema")
479         setup_add_ldif(setup_dir, "schema_samba4.ldif", subobj, samdb)
480         message("Setting up sam.ldb AD schema")
481         setup_add_ldif(setup_dir, "schema.ldif", subobj, samdb)
482
483         message("Setting up sam.ldb configuration data")
484         setup_add_ldif(setup_dir, "provision_configuration.ldif", subobj, samdb)
485
486         message("Setting up display specifiers")
487         setup_add_ldif(setup_dir, "display_specifiers.ldif", subobj, samdb)
488
489         message("Adding users container (permitted to fail)")
490         setup_add_ldif(setup_dir, "provision_users_add.ldif", subobj, samdb)
491         message("Modifying users container")
492         setup_ldb_modify(setup_dir, "provision_users_modify.ldif", subobj, samdb)
493         message("Adding computers container (permitted to fail)")
494         setup_add_ldif(setup_dir, "provision_computers_add.ldif", subobj, samdb)
495         message("Modifying computers container")
496         setup_ldb_modify(setup_dir, "provision_computers_modify.ldif", subobj, samdb)
497         message("Setting up sam.ldb data")
498         setup_add_ldif(setup_dir, "provision.ldif", subobj, samdb)
499
500         if blank:
501             message("Setting up sam.ldb index")
502             setup_add_ldif(setup_dir, "provision_index.ldif", subobj, samdb)
503
504             message("Setting up sam.ldb rootDSE marking as syncronized")
505             setup_modify_ldif(setup_dir, "provision_rootdse_modify.ldif", subobj, samdb)
506
507             samdb.transaction_commit()
508             return
509
510     #    message("Activate schema module")
511     #    setup_modify_ldif("schema_activation.ldif", info, samdb, False)
512     #
513     #    // (hack) Reload, now we have the schema loaded.  
514     #    commit_ok = samdb.transaction_commit()
515     #    if (!commit_ok) {
516     #        message("samdb commit failed: " + samdb.errstring() + "\n")
517     #        assert(commit_ok)
518     #    }
519     #    samdb.close()
520     #
521     #    samdb = open_ldb(info, paths.samdb, False)
522     #
523         message("Setting up sam.ldb users and groups")
524         setup_add_ldif(setup_dir, "provision_users.ldif", subobj, samdb)
525
526         setup_name_mappings(subobj, samdb)
527
528         message("Setting up sam.ldb index")
529         setup_add_ldif(setup_dir, "provision_index.ldif", subobj, samdb)
530
531         message("Setting up sam.ldb rootDSE marking as syncronized")
532         setup_modify_ldif(setup_dir, "provision_rootdse_modify.ldif", subobj, samdb)
533     except:
534         samdb.transaction_cancel()
535         raise
536
537     samdb.transaction_commit()
538
539     message("Setting up phpLDAPadmin configuration")
540     setup_file(setup_dir, "phpldapadmin-config.php", message, 
541                paths.phpldapadminconfig, subobj)
542     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
543
544
545 def provision_dns(setup_dir, subobj, message, paths, session_info, credentials):
546     """Write out a DNS zone file, from the info in the current database."""
547     message("Setting up DNS zone: %s" % subobj.dnsdomain)
548     # connect to the sam
549     ldb = SamDB(paths.samdb, session_info=session_info, credentials=credentials,
550                 lp=lp)
551
552     # These values may have changed, due to an incoming SamSync,
553     # or may not have been specified, so fetch them from the database
554
555     res = ldb.search(Dn(ldb, subobj.domaindn), SCOPE_BASE, "objectGUID=*", 
556                      ["objectGUID"])
557     assert(len(res) == 1)
558     assert(res[0]["objectGUID"] is not None)
559     subobj.domainguid = res[0]["objectGUID"]
560
561     subobj.host_guid = ldb.searchone(subobj.domaindn, 
562                                  "(&(objectClass=computer)(cn=%s))" % subobj.netbiosname, "objectGUID")
563     assert subobj.host_guid is not None
564
565     setup_file(setup_dir, "provision.zone", message, paths.dns, subobj)
566
567     message("Please install the zone located in %s into your DNS server" % paths.dns)
568
569
570 def provision_ldapbase(setup_dir, subobj, message, paths):
571     """Write out a DNS zone file, from the info in the current database."""
572     message("Setting up LDAP base entry: %s" % subobj.domaindn)
573     rdns = subobj.domaindn.split(",")
574     subobj.extensibleobject = "objectClass: extensibleObject"
575
576     subobj.rdn_dc = rdns[0][len("DC="):]
577
578     setup_file(setup_dir, "provision_basedn.ldif", 
579            message, paths.ldap_basedn_ldif, 
580            subobj)
581
582     setup_file(setup_dir, "provision_configuration_basedn.ldif", 
583            message, paths.ldap_config_basedn_ldif, 
584            subobj)
585
586     setup_file(setup_dir, "provision_schema_basedn.ldif", 
587            message, paths.ldap_schema_basedn_ldif, 
588            subobj)
589
590     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")
591
592
593 def provision_guess(lp):
594     """guess reasonably default options for provisioning."""
595     subobj = ProvisionSettings(realm=lp.get("realm").upper(),
596                                domain=lp.get("workgroup"),
597                                hostname=hostname(), 
598                                hostip=hostip())
599
600     assert subobj.realm is not None
601     assert subobj.domain is not None
602     assert subobj.hostname is not None
603     
604     subobj.domainsid    = security.random_sid()
605     subobj.invocationid = uuid.random()
606     subobj.policyguid   = uuid.random()
607     subobj.krbtgtpass   = misc.random_password(12)
608     subobj.machinepass  = misc.random_password(12)
609     subobj.adminpass    = misc.random_password(12)
610     subobj.dnspass      = misc.random_password(12)
611     subobj.datestring   = time.strftime("%Y%m%d%H")
612     subobj.root         = findnss(pwd.getpwnam, "root")[4]
613     subobj.nobody       = findnss(pwd.getpwnam, "nobody")[4]
614     subobj.nogroup      = findnss(grp.getgrnam, "nogroup", "nobody")[2]
615     subobj.wheel        = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
616     subobj.backup       = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
617     subobj.users        = findnss(grp.getgrnam, "users", "guest", "other", "unknown", "usr")[2]
618
619     subobj.dnsdomain    = subobj.realm.lower()
620     subobj.dnsname      = "%s.%s" % (subobj.hostname.lower(), subobj.dnsdomain)
621     subobj.domaindn     = "DC=" + subobj.dnsdomain.replace(".", ",DC=")
622     subobj.domaindn_ldb = "users.ldb"
623     subobj.rootdn       = subobj.domaindn
624     subobj.configdn     = "CN=Configuration," + subobj.rootdn
625     subobj.configdn_ldb = "configuration.ldb"
626     subobj.schemadn     = "CN=Schema," + subobj.configdn
627     subobj.schemadn_ldb = "schema.ldb"
628
629     #Add modules to the list to activate them by default
630     #beware often order is important
631     #
632     # Some Known ordering constraints:
633     # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
634     # - objectclass must be before password_hash, because password_hash checks
635     #   that the objectclass is of type person (filled in by objectclass
636     #   module when expanding the objectclass list)
637     # - partition must be last
638     # - each partition has its own module list then
639     subobj.modules_list = ["rootdse",
640                     "paged_results",
641                     "ranged_results",
642                     "anr",
643                     "server_sort",
644                     "extended_dn",
645                     "asq",
646                     "samldb",
647                     "rdn_name",
648                     "objectclass",
649                     "kludge_acl",
650                     "operational"]
651     subobj.tdb_modules_list = [
652                     "subtree_rename",
653                     "subtree_delete",
654                     "linked_attributes"]
655     subobj.modules_list2 = ["show_deleted",
656                     "partition"]
657
658     subobj.extensibleobject = "# no objectClass: extensibleObject for local ldb"
659     subobj.aci        = "# no aci for local ldb"
660     return subobj
661
662
663 def load_schema(setup_dir, subobj, samdb):
664     """Load schema."""
665     src = os.path.join(setup_dir, "schema.ldif")
666     schema_data = open(src, 'r').read()
667     src = os.path.join(setup_dir, "schema_samba4.ldif")
668     schema_data += open(src, 'r').read()
669     schema_data = substitute_var(schema_data, subobj.subst_vars())
670     src = os.path.join(setup_dir, "provision_schema_basedn_modify.ldif")
671     head_data = open(src, 'r').read()
672     head_data = substitute_var(head_data, subobj.subst_vars())
673     samdb.attach_schema_from_ldif(head_data, schema_data)
674
675
676 def join_domain(domain, netbios_name, join_type, creds, message):
677     ctx = NetContext(creds)
678     joindom = object()
679     joindom.domain = domain
680     joindom.join_type = join_type
681     joindom.netbios_name = netbios_name
682     if not ctx.JoinDomain(joindom):
683         raise Exception("Domain Join failed: " + joindom.error_string)
684
685
686 def vampire(domain, session_info, credentials, message):
687     """Vampire a remote domain.  
688     
689     Session info and credentials are required for for
690     access to our local database (might be remote ldap)
691     """
692     ctx = NetContext(credentials)
693     vampire_ctx = object()
694     machine_creds = credentials_init()
695     machine_creds.set_domain(form.domain)
696     if not machine_creds.set_machine_account():
697         raise Exception("Failed to access domain join information!")
698     vampire_ctx.machine_creds = machine_creds
699     vampire_ctx.session_info = session_info
700     if not ctx.SamSyncLdb(vampire_ctx):
701         raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string)
702
703
704 def ldb_erase_partitions(subobj, message, ldb, ldapbackend):
705     """Erase an ldb, removing all records."""
706     assert ldb is not None
707     res = ldb.search(Dn(ldb, ""), SCOPE_BASE, "(objectClass=*)", 
708                      ["namingContexts"])
709     assert len(res) == 1
710     if not "namingContexts" in res[0]:
711         return
712     for basedn in res[0]["namingContexts"]:
713         anything = "(|(objectclass=*)(dn=*))"
714         previous_remaining = 1
715         current_remaining = 0
716
717         if ldapbackend and (basedn == subobj.domaindn):
718             # Only delete objects that were created by provision
719             anything = "(objectcategory=*)"
720
721         k = 0
722         while ++k < 10 and (previous_remaining != current_remaining):
723             # and the rest
724             res2 = ldb.search(Dn(ldb, basedn), SCOPE_SUBTREE, anything, ["dn"])
725             previous_remaining = current_remaining
726             current_remaining = len(res2)
727             for msg in res2:
728                 try:
729                     ldb.delete(msg.dn)
730                 except LdbError, (_, text):
731                     message("Unable to delete %s: %s" % (msg.dn, text))
732
733