provision: Split out result reporting for backends.
[nivanova/samba-autobuild/.git] / source4 / scripting / python / samba / provision / __init__.py
1
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
4
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
8 #
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 #
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 """Functions for setting up a Samba configuration."""
27
28 __docformat__ = "restructuredText"
29
30 from base64 import b64encode
31 import os
32 import re
33 import pwd
34 import grp
35 import logging
36 import time
37 import uuid
38 import socket
39 import urllib
40 import string
41
42 import ldb
43
44 from samba.auth import system_session, admin_session
45 import samba
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
47 from samba import (
48     Ldb,
49     check_all_substituted,
50     setup_file,
51     substitute_var,
52     valid_netbios_name,
53     version,
54     )
55 from samba.dcerpc import security, misc
56 from samba.dcerpc.misc import (
57     SEC_CHAN_BDC,
58     SEC_CHAN_WKSTA,
59     )
60 from samba.dsdb import (
61     DS_DOMAIN_FUNCTION_2003,
62     DS_DOMAIN_FUNCTION_2008_R2,
63     ENC_ALL_TYPES,
64     )
65 from samba.idmap import IDmapDB
66 from samba.ms_display_specifiers import read_ms_ldif
67 from samba.ntacls import setntacl, dsacl2fsacl
68 from samba.ndr import ndr_pack, ndr_unpack
69 from samba.provision.backend import (
70     ExistingBackend,
71     FDSBackend,
72     LDBBackend,
73     OpenLDAPBackend,
74     )
75 from samba.provision.descriptor import (
76     get_config_descriptor,
77     get_domain_descriptor
78     )
79 from samba.provision.common import (
80     setup_path,
81     setup_add_ldif,
82     setup_modify_ldif,
83     )
84 from samba.provision.sambadns import (
85     setup_ad_dns,
86     create_dns_update_list
87     )
88
89 import samba.param
90 import samba.registry
91 from samba.schema import Schema
92 from samba.samdb import SamDB
93 from samba.dbchecker import dbcheck
94
95
96 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
97 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
98 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
99 DEFAULTSITE = "Default-First-Site-Name"
100 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
101
102
103 class ProvisionPaths(object):
104
105     def __init__(self):
106         self.shareconf = None
107         self.hklm = None
108         self.hkcu = None
109         self.hkcr = None
110         self.hku = None
111         self.hkpd = None
112         self.hkpt = None
113         self.samdb = None
114         self.idmapdb = None
115         self.secrets = None
116         self.keytab = None
117         self.dns_keytab = None
118         self.dns = None
119         self.winsdb = None
120         self.private_dir = None
121         self.phpldapadminconfig = None
122
123
124 class ProvisionNames(object):
125
126     def __init__(self):
127         self.rootdn = None
128         self.domaindn = None
129         self.configdn = None
130         self.schemadn = None
131         self.ldapmanagerdn = None
132         self.dnsdomain = None
133         self.realm = None
134         self.netbiosname = None
135         self.domain = None
136         self.hostname = None
137         self.sitename = None
138         self.smbconf = None
139
140 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
141     """Get key provision parameters (realm, domain, ...) from a given provision
142
143     :param samdb: An LDB object connected to the sam.ldb file
144     :param secretsdb: An LDB object connected to the secrets.ldb file
145     :param idmapdb: An LDB object connected to the idmap.ldb file
146     :param paths: A list of path to provision object
147     :param smbconf: Path to the smb.conf file
148     :param lp: A LoadParm object
149     :return: A list of key provision parameters
150     """
151     names = ProvisionNames()
152     names.adminpass = None
153
154     # NT domain, kerberos realm, root dn, domain dn, domain dns name
155     names.domain = string.upper(lp.get("workgroup"))
156     names.realm = lp.get("realm")
157     names.dnsdomain = names.realm.lower()
158     basedn = samba.dn_from_dns_name(names.dnsdomain)
159     names.realm = string.upper(names.realm)
160     # netbiosname
161     # Get the netbiosname first (could be obtained from smb.conf in theory)
162     res = secretsdb.search(expression="(flatname=%s)" %
163                             names.domain,base="CN=Primary Domains",
164                             scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
165     names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
166
167     names.smbconf = smbconf
168
169     # That's a bit simplistic but it's ok as long as we have only 3
170     # partitions
171     current = samdb.search(expression="(objectClass=*)",
172         base="", scope=ldb.SCOPE_BASE,
173         attrs=["defaultNamingContext", "schemaNamingContext",
174                "configurationNamingContext","rootDomainNamingContext"])
175
176     names.configdn = current[0]["configurationNamingContext"]
177     configdn = str(names.configdn)
178     names.schemadn = current[0]["schemaNamingContext"]
179     if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
180                                        current[0]["defaultNamingContext"][0]))):
181         raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
182                                  "is not the same ..." % (paths.samdb,
183                                     str(current[0]["defaultNamingContext"][0]),
184                                     paths.smbconf, basedn)))
185
186     names.domaindn=current[0]["defaultNamingContext"]
187     names.rootdn=current[0]["rootDomainNamingContext"]
188     # default site name
189     res3 = samdb.search(expression="(objectClass=site)",
190         base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
191     names.sitename = str(res3[0]["cn"])
192
193     # dns hostname and server dn
194     res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
195                             base="OU=Domain Controllers,%s" % basedn,
196                             scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
197     names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
198
199     server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
200                                 attrs=[], base=configdn)
201     names.serverdn = server_res[0].dn
202
203     # invocation id/objectguid
204     res5 = samdb.search(expression="(objectClass=*)",
205             base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
206             attrs=["invocationID", "objectGUID"])
207     names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
208     names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
209
210     # domain guid/sid
211     res6 = samdb.search(expression="(objectClass=*)", base=basedn,
212             scope=ldb.SCOPE_BASE, attrs=["objectGUID",
213                 "objectSid","msDS-Behavior-Version" ])
214     names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
215     names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
216     if res6[0].get("msDS-Behavior-Version") is None or \
217         int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
218         names.domainlevel = DS_DOMAIN_FUNCTION_2000
219     else:
220         names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
221
222     # policy guid
223     res7 = samdb.search(expression="(displayName=Default Domain Policy)",
224                         base="CN=Policies,CN=System," + basedn,
225                         scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
226     names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
227     # dc policy guid
228     res8 = samdb.search(expression="(displayName=Default Domain Controllers"
229                                    " Policy)",
230                             base="CN=Policies,CN=System," + basedn,
231                             scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
232     if len(res8) == 1:
233         names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
234     else:
235         names.policyid_dc = None
236     res9 = idmapdb.search(expression="(cn=%s)" %
237                             (security.SID_BUILTIN_ADMINISTRATORS),
238                             attrs=["xidNumber"])
239     if len(res9) == 1:
240         names.wheel_gid = res9[0]["xidNumber"]
241     else:
242         raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
243     return names
244
245
246 def update_provision_usn(samdb, low, high, id, replace=False):
247     """Update the field provisionUSN in sam.ldb
248
249     This field is used to track range of USN modified by provision and
250     upgradeprovision.
251     This value is used afterward by next provision to figure out if
252     the field have been modified since last provision.
253
254     :param samdb: An LDB object connect to sam.ldb
255     :param low: The lowest USN modified by this upgrade
256     :param high: The highest USN modified by this upgrade
257     :param id: The invocation id of the samba's dc
258     :param replace: A boolean indicating if the range should replace any
259                     existing one or appended (default)
260     """
261
262     tab = []
263     if not replace:
264         entry = samdb.search(base="@PROVISION",
265                              scope=ldb.SCOPE_BASE,
266                              attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
267         for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
268             if not re.search(';', e):
269                 e = "%s;%s" % (e, id)
270             tab.append(str(e))
271
272     tab.append("%s-%s;%s" % (low, high, id))
273     delta = ldb.Message()
274     delta.dn = ldb.Dn(samdb, "@PROVISION")
275     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
276         ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
277     entry = samdb.search(expression='provisionnerID=*',
278                          base="@PROVISION", scope=ldb.SCOPE_BASE,
279                          attrs=["provisionnerID"])
280     if len(entry) == 0 or len(entry[0]) == 0:
281         delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
282     samdb.modify(delta)
283
284
285 def set_provision_usn(samdb, low, high, id):
286     """Set the field provisionUSN in sam.ldb
287     This field is used to track range of USN modified by provision and
288     upgradeprovision.
289     This value is used afterward by next provision to figure out if
290     the field have been modified since last provision.
291
292     :param samdb: An LDB object connect to sam.ldb
293     :param low: The lowest USN modified by this upgrade
294     :param high: The highest USN modified by this upgrade
295     :param id: The invocationId of the provision"""
296
297     tab = []
298     tab.append("%s-%s;%s" % (low, high, id))
299
300     delta = ldb.Message()
301     delta.dn = ldb.Dn(samdb, "@PROVISION")
302     delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
303         ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
304     samdb.add(delta)
305
306
307 def get_max_usn(samdb,basedn):
308     """ This function return the biggest USN present in the provision
309
310     :param samdb: A LDB object pointing to the sam.ldb
311     :param basedn: A string containing the base DN of the provision
312                     (ie. DC=foo, DC=bar)
313     :return: The biggest USN in the provision"""
314
315     res = samdb.search(expression="objectClass=*",base=basedn,
316                          scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
317                          controls=["search_options:1:2",
318                                    "server_sort:1:1:uSNChanged",
319                                    "paged_results:1:1"])
320     return res[0]["uSNChanged"]
321
322
323 def get_last_provision_usn(sam):
324     """Get USNs ranges modified by a provision or an upgradeprovision
325
326     :param sam: An LDB object pointing to the sam.ldb
327     :return: a dictionnary which keys are invocation id and values are an array
328              of integer representing the different ranges
329     """
330     try:
331         entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
332                            base="@PROVISION", scope=ldb.SCOPE_BASE,
333                            attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
334     except ldb.LdbError, (ecode, emsg):
335         if ecode == ldb.ERR_NO_SUCH_OBJECT:
336             return None
337         raise
338     if len(entry):
339         myids = []
340         range = {}
341         p = re.compile(r'-')
342         if entry[0].get("provisionnerID"):
343             for e in entry[0]["provisionnerID"]:
344                 myids.append(str(e))
345         for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
346             tab1 = str(r).split(';')
347             if len(tab1) == 2:
348                 id = tab1[1]
349             else:
350                 id = "default"
351             if (len(myids) > 0 and id not in myids):
352                 continue
353             tab2 = p.split(tab1[0])
354             if range.get(id) == None:
355                 range[id] = []
356             range[id].append(tab2[0])
357             range[id].append(tab2[1])
358         return range
359     else:
360         return None
361
362
363 class ProvisionResult(object):
364     """Result of a provision.
365
366     :ivar server_role: The server role
367     :ivar paths: ProvisionPaths instance
368     :ivar domaindn: The domain dn, as string
369     """
370
371     def __init__(self):
372         self.server_role = None
373         self.paths = None
374         self.domaindn = None
375         self.lp = None
376         self.samdb = None
377         self.idmap = None
378         self.names = None
379         self.domainsid = None
380         self.adminpass_generated = None
381         self.adminpass = None
382         self.backend_result = None
383
384     def report_logger(self, logger):
385         """Report this provision result to a logger."""
386         logger.info(
387             "Once the above files are installed, your Samba4 server will "
388             "be ready to use")
389         if self.adminpass_generated:
390             logger.info("Admin password:        %s", self.adminpass)
391         logger.info("Server Role:           %s", self.server_role)
392         logger.info("Hostname:              %s", self.names.hostname)
393         logger.info("NetBIOS Domain:        %s", self.names.domain)
394         logger.info("DNS Domain:            %s", self.names.dnsdomain)
395         logger.info("DOMAIN SID:            %s", self.domainsid)
396
397         if self.paths.phpldapadminconfig is not None:
398             logger.info(
399                 "A phpLDAPadmin configuration file suitable for administering "
400                 "the Samba 4 LDAP server has been created in %s.",
401                 self.paths.phpldapadminconfig)
402
403         if self.backend_result:
404             self.backend_result.report_logger(logger)
405
406
407 def check_install(lp, session_info, credentials):
408     """Check whether the current install seems ok.
409
410     :param lp: Loadparm context
411     :param session_info: Session information
412     :param credentials: Credentials
413     """
414     if lp.get("realm") == "":
415         raise Exception("Realm empty")
416     samdb = Ldb(lp.samdb_url(), session_info=session_info,
417             credentials=credentials, lp=lp)
418     if len(samdb.search("(cn=Administrator)")) != 1:
419         raise ProvisioningError("No administrator account found")
420
421
422 def findnss(nssfn, names):
423     """Find a user or group from a list of possibilities.
424
425     :param nssfn: NSS Function to try (should raise KeyError if not found)
426     :param names: Names to check.
427     :return: Value return by first names list.
428     """
429     for name in names:
430         try:
431             return nssfn(name)
432         except KeyError:
433             pass
434     raise KeyError("Unable to find user/group in %r" % names)
435
436
437 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
438 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
439
440
441 def provision_paths_from_lp(lp, dnsdomain):
442     """Set the default paths for provisioning.
443
444     :param lp: Loadparm context.
445     :param dnsdomain: DNS Domain name
446     """
447     paths = ProvisionPaths()
448     paths.private_dir = lp.get("private dir")
449
450     # This is stored without path prefix for the "privateKeytab" attribute in
451     # "secrets_dns.ldif".
452     paths.dns_keytab = "dns.keytab"
453     paths.keytab = "secrets.keytab"
454
455     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
456     paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
457     paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
458     paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
459     paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
460     paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
461     paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
462     paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
463     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
464     paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
465     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
466     paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
467     paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
468     paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
469     paths.phpldapadminconfig = os.path.join(paths.private_dir,
470                                             "phpldapadmin-config.php")
471     paths.hklm = "hklm.ldb"
472     paths.hkcr = "hkcr.ldb"
473     paths.hkcu = "hkcu.ldb"
474     paths.hku = "hku.ldb"
475     paths.hkpd = "hkpd.ldb"
476     paths.hkpt = "hkpt.ldb"
477     paths.sysvol = lp.get("path", "sysvol")
478     paths.netlogon = lp.get("path", "netlogon")
479     paths.smbconf = lp.configfile
480     return paths
481
482
483 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
484                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
485                 schemadn=None, serverdn=None, sitename=None):
486     """Guess configuration settings to use."""
487
488     if hostname is None:
489         hostname = socket.gethostname().split(".")[0]
490
491     netbiosname = lp.get("netbios name")
492     if netbiosname is None:
493         netbiosname = hostname
494         # remove forbidden chars
495         newnbname = ""
496         for x in netbiosname:
497             if x.isalnum() or x in VALID_NETBIOS_CHARS:
498                 newnbname = "%s%c" % (newnbname, x)
499         # force the length to be <16
500         netbiosname = newnbname[0:15]
501     assert netbiosname is not None
502     netbiosname = netbiosname.upper()
503     if not valid_netbios_name(netbiosname):
504         raise InvalidNetbiosName(netbiosname)
505
506     if dnsdomain is None:
507         dnsdomain = lp.get("realm")
508         if dnsdomain is None or dnsdomain == "":
509             raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
510
511     dnsdomain = dnsdomain.lower()
512
513     if serverrole is None:
514         serverrole = lp.get("server role")
515         if serverrole is None:
516             raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
517
518     serverrole = serverrole.lower()
519
520     realm = dnsdomain.upper()
521
522     if lp.get("realm") == "":
523         raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s.  Please remove the smb.conf file and let provision generate it" % lp.configfile)
524
525     if lp.get("realm").upper() != realm:
526         raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
527
528     if lp.get("server role").lower() != serverrole:
529         raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), serverrole, lp.configfile))
530
531     if serverrole == "domain controller":
532         if domain is None:
533             # This will, for better or worse, default to 'WORKGROUP'
534             domain = lp.get("workgroup")
535         domain = domain.upper()
536
537         if lp.get("workgroup").upper() != domain:
538             raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!  Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
539
540         if domaindn is None:
541             domaindn = samba.dn_from_dns_name(dnsdomain)
542
543         if domain == netbiosname:
544             raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
545     else:
546         domain = netbiosname
547         if domaindn is None:
548             domaindn = "DC=" + netbiosname
549
550     if not valid_netbios_name(domain):
551         raise InvalidNetbiosName(domain)
552
553     if hostname.upper() == realm:
554         raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
555     if netbiosname.upper() == realm:
556         raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
557     if domain == realm:
558         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
559
560     if rootdn is None:
561        rootdn = domaindn
562
563     if configdn is None:
564         configdn = "CN=Configuration," + rootdn
565     if schemadn is None:
566         schemadn = "CN=Schema," + configdn
567
568     if sitename is None:
569         sitename=DEFAULTSITE
570
571     names = ProvisionNames()
572     names.rootdn = rootdn
573     names.domaindn = domaindn
574     names.configdn = configdn
575     names.schemadn = schemadn
576     names.ldapmanagerdn = "CN=Manager," + rootdn
577     names.dnsdomain = dnsdomain
578     names.domain = domain
579     names.realm = realm
580     names.netbiosname = netbiosname
581     names.hostname = hostname
582     names.sitename = sitename
583     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
584         netbiosname, sitename, configdn)
585
586     return names
587
588
589 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
590                  targetdir, sid_generator="internal", eadb=False, lp=None,
591                  server_services=None):
592     """Create a new smb.conf file based on a couple of basic settings.
593     """
594     assert smbconf is not None
595     if hostname is None:
596         hostname = socket.gethostname().split(".")[0]
597         netbiosname = hostname.upper()
598         # remove forbidden chars
599         newnbname = ""
600         for x in netbiosname:
601             if x.isalnum() or x in VALID_NETBIOS_CHARS:
602                 newnbname = "%s%c" % (newnbname, x)
603         #force the length to be <16
604         netbiosname = newnbname[0:15]
605     else:
606         netbiosname = hostname.upper()
607
608     if serverrole is None:
609         serverrole = "standalone"
610
611     assert serverrole in ("domain controller", "member server", "standalone")
612     if serverrole == "domain controller":
613         smbconfsuffix = "dc"
614     elif serverrole == "member server":
615         smbconfsuffix = "member"
616     elif serverrole == "standalone":
617         smbconfsuffix = "standalone"
618
619     if sid_generator is None:
620         sid_generator = "internal"
621
622     assert domain is not None
623     domain = domain.upper()
624
625     assert realm is not None
626     realm = realm.upper()
627
628     if lp is None:
629         lp = samba.param.LoadParm()
630     #Load non-existant file
631     if os.path.exists(smbconf):
632         lp.load(smbconf)
633     if eadb and not lp.get("posix:eadb"):
634         if targetdir is not None:
635             privdir = os.path.join(targetdir, "private")
636         else:
637             privdir = lp.get("private dir")
638         lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
639
640     if server_services is not None:
641         server_services_line = "server services = " + " ".join(server_services)
642     else:
643         server_services_line = ""
644
645     if targetdir is not None:
646         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
647         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
648         statedir_line = "state directory = " + os.path.abspath(targetdir)
649         cachedir_line = "cache directory = " + os.path.abspath(targetdir)
650
651         lp.set("lock dir", os.path.abspath(targetdir))
652         lp.set("state directory", os.path.abspath(targetdir))
653         lp.set("cache directory", os.path.abspath(targetdir))
654     else:
655         privatedir_line = ""
656         lockdir_line = ""
657         statedir_line = ""
658         cachedir_line = ""
659
660     sysvol = os.path.join(lp.get("state directory"), "sysvol")
661     netlogon = os.path.join(sysvol, realm.lower(), "scripts")
662
663     setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
664                smbconf, {
665             "NETBIOS_NAME": netbiosname,
666             "DOMAIN": domain,
667             "REALM": realm,
668             "SERVERROLE": serverrole,
669             "NETLOGONPATH": netlogon,
670             "SYSVOLPATH": sysvol,
671             "PRIVATEDIR_LINE": privatedir_line,
672             "LOCKDIR_LINE": lockdir_line,
673             "STATEDIR_LINE": statedir_line,
674             "CACHEDIR_LINE": cachedir_line,
675             "SERVER_SERVICES_LINE": server_services_line
676             })
677
678     # reload the smb.conf
679     lp.load(smbconf)
680
681     # and dump it without any values that are the default
682     # this ensures that any smb.conf parameters that were set
683     # on the provision/join command line are set in the resulting smb.conf
684     f = open(smbconf, mode='w')
685     lp.dump(f, False)
686     f.close()
687
688
689 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
690                         users_gid, wheel_gid):
691     """setup reasonable name mappings for sam names to unix names.
692
693     :param samdb: SamDB object.
694     :param idmap: IDmap db object.
695     :param sid: The domain sid.
696     :param domaindn: The domain DN.
697     :param root_uid: uid of the UNIX root user.
698     :param nobody_uid: uid of the UNIX nobody user.
699     :param users_gid: gid of the UNIX users group.
700     :param wheel_gid: gid of the UNIX wheel group.
701     """
702     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
703     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
704
705     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
706     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
707
708
709 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
710                            provision_backend, names, schema, serverrole,
711                            erase=False):
712     """Setup the partitions for the SAM database.
713
714     Alternatively, provision() may call this, and then populate the database.
715
716     :note: This will wipe the Sam Database!
717
718     :note: This function always removes the local SAM LDB file. The erase
719         parameter controls whether to erase the existing data, which
720         may not be stored locally but in LDAP.
721
722     """
723     assert session_info is not None
724
725     # We use options=["modules:"] to stop the modules loading - we
726     # just want to wipe and re-initialise the database, not start it up
727
728     try:
729         os.unlink(samdb_path)
730     except OSError:
731         pass
732
733     samdb = Ldb(url=samdb_path, session_info=session_info,
734                 lp=lp, options=["modules:"])
735
736     ldap_backend_line = "# No LDAP backend"
737     if provision_backend.type != "ldb":
738         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
739
740     samdb.transaction_start()
741     try:
742         logger.info("Setting up sam.ldb partitions and settings")
743         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
744                 "LDAP_BACKEND_LINE": ldap_backend_line
745         })
746
747
748         setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
749                 "BACKEND_TYPE": provision_backend.type,
750                 "SERVER_ROLE": serverrole
751                 })
752
753         logger.info("Setting up sam.ldb rootDSE")
754         setup_samdb_rootdse(samdb, names)
755     except:
756         samdb.transaction_cancel()
757         raise
758     else:
759         samdb.transaction_commit()
760
761
762 def secretsdb_self_join(secretsdb, domain,
763                         netbiosname, machinepass, domainsid=None,
764                         realm=None, dnsdomain=None,
765                         keytab_path=None,
766                         key_version_number=1,
767                         secure_channel_type=SEC_CHAN_WKSTA):
768     """Add domain join-specific bits to a secrets database.
769
770     :param secretsdb: Ldb Handle to the secrets database
771     :param machinepass: Machine password
772     """
773     attrs = ["whenChanged",
774            "secret",
775            "priorSecret",
776            "priorChanged",
777            "krb5Keytab",
778            "privateKeytab"]
779
780     if realm is not None:
781         if dnsdomain is None:
782             dnsdomain = realm.lower()
783         dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
784     else:
785         dnsname = None
786     shortname = netbiosname.lower()
787
788     # We don't need to set msg["flatname"] here, because rdn_name will handle
789     # it, and it causes problems for modifies anyway
790     msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
791     msg["secureChannelType"] = [str(secure_channel_type)]
792     msg["objectClass"] = ["top", "primaryDomain"]
793     if dnsname is not None:
794         msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
795         msg["realm"] = [realm]
796         msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
797         msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
798         msg["privateKeytab"] = ["secrets.keytab"]
799
800     msg["secret"] = [machinepass]
801     msg["samAccountName"] = ["%s$" % netbiosname]
802     msg["secureChannelType"] = [str(secure_channel_type)]
803     if domainsid is not None:
804         msg["objectSid"] = [ndr_pack(domainsid)]
805
806     # This complex expression tries to ensure that we don't have more
807     # than one record for this SID, realm or netbios domain at a time,
808     # but we don't delete the old record that we are about to modify,
809     # because that would delete the keytab and previous password.
810     res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
811         expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
812         scope=ldb.SCOPE_ONELEVEL)
813
814     for del_msg in res:
815         secretsdb.delete(del_msg.dn)
816
817     res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
818
819     if len(res) == 1:
820         msg["priorSecret"] = [res[0]["secret"][0]]
821         msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
822
823         try:
824             msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
825         except KeyError:
826             pass
827
828         try:
829             msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
830         except KeyError:
831             pass
832
833         for el in msg:
834             if el != 'dn':
835                 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
836         secretsdb.modify(msg)
837         secretsdb.rename(res[0].dn, msg.dn)
838     else:
839         spn = [ 'HOST/%s' % shortname ]
840         if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
841             # we are a domain controller then we add servicePrincipalName
842             # entries for the keytab code to update.
843             spn.extend([ 'HOST/%s' % dnsname ])
844         msg["servicePrincipalName"] = spn
845
846         secretsdb.add(msg)
847
848
849 def setup_secretsdb(paths, session_info, backend_credentials, lp):
850     """Setup the secrets database.
851
852    :note: This function does not handle exceptions and transaction on purpose,
853        it's up to the caller to do this job.
854
855     :param path: Path to the secrets database.
856     :param session_info: Session info.
857     :param credentials: Credentials
858     :param lp: Loadparm context
859     :return: LDB handle for the created secrets database
860     """
861     if os.path.exists(paths.secrets):
862         os.unlink(paths.secrets)
863
864     keytab_path = os.path.join(paths.private_dir, paths.keytab)
865     if os.path.exists(keytab_path):
866         os.unlink(keytab_path)
867
868     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
869     if os.path.exists(dns_keytab_path):
870         os.unlink(dns_keytab_path)
871
872     path = paths.secrets
873
874     secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
875     secrets_ldb.erase()
876     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
877     secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
878     secrets_ldb.transaction_start()
879     try:
880         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
881
882         if (backend_credentials is not None and
883             backend_credentials.authentication_requested()):
884             if backend_credentials.get_bind_dn() is not None:
885                 setup_add_ldif(secrets_ldb,
886                     setup_path("secrets_simple_ldap.ldif"), {
887                         "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
888                         "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
889                         })
890             else:
891                 setup_add_ldif(secrets_ldb,
892                     setup_path("secrets_sasl_ldap.ldif"), {
893                         "LDAPADMINUSER": backend_credentials.get_username(),
894                         "LDAPADMINREALM": backend_credentials.get_realm(),
895                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
896                         })
897     except:
898         secrets_ldb.transaction_cancel()
899         raise
900     return secrets_ldb
901
902
903 def setup_privileges(path, session_info, lp):
904     """Setup the privileges database.
905
906     :param path: Path to the privileges database.
907     :param session_info: Session info.
908     :param credentials: Credentials
909     :param lp: Loadparm context
910     :return: LDB handle for the created secrets database
911     """
912     if os.path.exists(path):
913         os.unlink(path)
914     privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
915     privilege_ldb.erase()
916     privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
917
918
919 def setup_registry(path, session_info, lp):
920     """Setup the registry.
921
922     :param path: Path to the registry database
923     :param session_info: Session information
924     :param credentials: Credentials
925     :param lp: Loadparm context
926     """
927     reg = samba.registry.Registry()
928     hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
929     reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
930     provision_reg = setup_path("provision.reg")
931     assert os.path.exists(provision_reg)
932     reg.diff_apply(provision_reg)
933
934
935 def setup_idmapdb(path, session_info, lp):
936     """Setup the idmap database.
937
938     :param path: path to the idmap database
939     :param session_info: Session information
940     :param credentials: Credentials
941     :param lp: Loadparm context
942     """
943     if os.path.exists(path):
944         os.unlink(path)
945
946     idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
947     idmap_ldb.erase()
948     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
949     return idmap_ldb
950
951
952 def setup_samdb_rootdse(samdb, names):
953     """Setup the SamDB rootdse.
954
955     :param samdb: Sam Database handle
956     """
957     setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
958         "SCHEMADN": names.schemadn,
959         "DOMAINDN": names.domaindn,
960         "ROOTDN"  : names.rootdn,
961         "CONFIGDN": names.configdn,
962         "SERVERDN": names.serverdn,
963         })
964
965
966 def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
967         dnspass, domainsid, next_rid, invocationid, policyguid, policyguid_dc,
968         domainControllerFunctionality, ntdsguid=None, dc_rid=None):
969     """Join a host to its own domain."""
970     assert isinstance(invocationid, str)
971     if ntdsguid is not None:
972         ntdsguid_line = "objectGUID: %s\n"%ntdsguid
973     else:
974         ntdsguid_line = ""
975
976     if dc_rid is None:
977         dc_rid = next_rid
978
979     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
980               "CONFIGDN": names.configdn,
981               "SCHEMADN": names.schemadn,
982               "DOMAINDN": names.domaindn,
983               "SERVERDN": names.serverdn,
984               "INVOCATIONID": invocationid,
985               "NETBIOSNAME": names.netbiosname,
986               "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
987               "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
988               "DOMAINSID": str(domainsid),
989               "DCRID": str(dc_rid),
990               "SAMBA_VERSION_STRING": version,
991               "NTDSGUID": ntdsguid_line,
992               "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
993                   domainControllerFunctionality),
994               "RIDALLOCATIONSTART": str(next_rid + 100),
995               "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
996
997     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
998               "POLICYGUID": policyguid,
999               "POLICYGUID_DC": policyguid_dc,
1000               "DNSDOMAIN": names.dnsdomain,
1001               "DOMAINDN": names.domaindn})
1002
1003     # If we are setting up a subdomain, then this has been replicated in, so we
1004     # don't need to add it
1005     if fill == FILL_FULL:
1006         setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1007                 "CONFIGDN": names.configdn,
1008                 "SCHEMADN": names.schemadn,
1009                 "DOMAINDN": names.domaindn,
1010                 "SERVERDN": names.serverdn,
1011                 "INVOCATIONID": invocationid,
1012                 "NETBIOSNAME": names.netbiosname,
1013                 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1014                 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1015                 "DOMAINSID": str(domainsid),
1016                 "DCRID": str(dc_rid),
1017                 "SAMBA_VERSION_STRING": version,
1018                 "NTDSGUID": ntdsguid_line,
1019                 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1020                     domainControllerFunctionality)})
1021
1022     # Setup fSMORoleOwner entries to point at the newly created DC entry
1023         setup_modify_ldif(samdb,
1024             setup_path("provision_self_join_modify_config.ldif"), {
1025                 "CONFIGDN": names.configdn,
1026                 "SCHEMADN": names.schemadn,
1027                 "DEFAULTSITE": names.sitename,
1028                 "NETBIOSNAME": names.netbiosname,
1029                 "SERVERDN": names.serverdn,
1030                 })
1031
1032     system_session_info = system_session()
1033     samdb.set_session_info(system_session_info)
1034     # Setup fSMORoleOwner entries to point at the newly created DC entry to
1035     # modify a serverReference under cn=config when we are a subdomain, we must
1036     # be system due to ACLs
1037     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1038               "DOMAINDN": names.domaindn,
1039               "SERVERDN": names.serverdn,
1040               "NETBIOSNAME": names.netbiosname,
1041               })
1042
1043     samdb.set_session_info(admin_session_info)
1044
1045     # This is Samba4 specific and should be replaced by the correct
1046     # DNS AD-style setup
1047     setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1048               "DNSDOMAIN": names.dnsdomain,
1049               "DOMAINDN": names.domaindn,
1050               "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1051               "HOSTNAME" : names.hostname,
1052               "DNSNAME" : '%s.%s' % (
1053                   names.netbiosname.lower(), names.dnsdomain.lower())
1054               })
1055
1056
1057 def getpolicypath(sysvolpath, dnsdomain, guid):
1058     """Return the physical path of policy given its guid.
1059
1060     :param sysvolpath: Path to the sysvol folder
1061     :param dnsdomain: DNS name of the AD domain
1062     :param guid: The GUID of the policy
1063     :return: A string with the complete path to the policy folder
1064     """
1065     if guid[0] != "{":
1066         guid = "{%s}" % guid
1067     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1068     return policy_path
1069
1070
1071 def create_gpo_struct(policy_path):
1072     if not os.path.exists(policy_path):
1073         os.makedirs(policy_path, 0775)
1074     open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1075                       "[General]\r\nVersion=0")
1076     p = os.path.join(policy_path, "MACHINE")
1077     if not os.path.exists(p):
1078         os.makedirs(p, 0775)
1079     p = os.path.join(policy_path, "USER")
1080     if not os.path.exists(p):
1081         os.makedirs(p, 0775)
1082
1083
1084 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1085     """Create the default GPO for a domain
1086
1087     :param sysvolpath: Physical path for the sysvol folder
1088     :param dnsdomain: DNS domain name of the AD domain
1089     :param policyguid: GUID of the default domain policy
1090     :param policyguid_dc: GUID of the default domain controler policy
1091     """
1092     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1093     create_gpo_struct(policy_path)
1094
1095     policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1096     create_gpo_struct(policy_path)
1097
1098
1099 def setup_samdb(path, session_info, provision_backend, lp, names,
1100         logger, fill, serverrole, schema, am_rodc=False):
1101     """Setup a complete SAM Database.
1102
1103     :note: This will wipe the main SAM database file!
1104     """
1105
1106     # Also wipes the database
1107     setup_samdb_partitions(path, logger=logger, lp=lp,
1108         provision_backend=provision_backend, session_info=session_info,
1109         names=names, serverrole=serverrole, schema=schema)
1110
1111     # Load the database, but don's load the global schema and don't connect
1112     # quite yet
1113     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1114                   credentials=provision_backend.credentials, lp=lp,
1115                   global_schema=False, am_rodc=am_rodc)
1116
1117     logger.info("Pre-loading the Samba 4 and AD schema")
1118
1119     # Load the schema from the one we computed earlier
1120     samdb.set_schema(schema)
1121
1122     # Set the NTDS settings DN manually - in order to have it already around
1123     # before the provisioned tree exists and we connect
1124     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1125
1126     # And now we can connect to the DB - the schema won't be loaded from the
1127     # DB
1128     samdb.connect(path)
1129
1130     return samdb
1131
1132
1133 def fill_samdb(samdb, lp, names,
1134         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1135         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1136         serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1137         next_rid=None, dc_rid=None):
1138
1139     if next_rid is None:
1140         next_rid = 1000
1141
1142     # Provision does not make much sense values larger than 1000000000
1143     # as the upper range of the rIDAvailablePool is 1073741823 and
1144     # we don't want to create a domain that cannot allocate rids.
1145     if next_rid < 1000 or next_rid > 1000000000:
1146         error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1147         error += "the valid range is %u-%u. The default is %u." % (
1148             1000, 1000000000, 1000)
1149         raise ProvisioningError(error)
1150
1151     # ATTENTION: Do NOT change these default values without discussion with the
1152     # team and/or release manager. They have a big impact on the whole program!
1153     domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1154
1155     if dom_for_fun_level is None:
1156         dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1157
1158     if dom_for_fun_level > domainControllerFunctionality:
1159         raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
1160
1161     domainFunctionality = dom_for_fun_level
1162     forestFunctionality = dom_for_fun_level
1163
1164     # Set the NTDS settings DN manually - in order to have it already around
1165     # before the provisioned tree exists and we connect
1166     samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1167
1168     samdb.transaction_start()
1169     try:
1170         # Set the domain functionality levels onto the database.
1171         # Various module (the password_hash module in particular) need
1172         # to know what level of AD we are emulating.
1173
1174         # These will be fixed into the database via the database
1175         # modifictions below, but we need them set from the start.
1176         samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1177         samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1178         samdb.set_opaque_integer("domainControllerFunctionality",
1179             domainControllerFunctionality)
1180
1181         samdb.set_domain_sid(str(domainsid))
1182         samdb.set_invocation_id(invocationid)
1183
1184         logger.info("Adding DomainDN: %s" % names.domaindn)
1185
1186         # impersonate domain admin
1187         admin_session_info = admin_session(lp, str(domainsid))
1188         samdb.set_session_info(admin_session_info)
1189         if domainguid is not None:
1190             domainguid_line = "objectGUID: %s\n-" % domainguid
1191         else:
1192             domainguid_line = ""
1193
1194         descr = b64encode(get_domain_descriptor(domainsid))
1195         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1196                 "DOMAINDN": names.domaindn,
1197                 "DOMAINSID": str(domainsid),
1198                 "DESCRIPTOR": descr,
1199                 "DOMAINGUID": domainguid_line
1200                 })
1201
1202         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1203             "DOMAINDN": names.domaindn,
1204             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1205             "NEXTRID": str(next_rid),
1206             "DEFAULTSITE": names.sitename,
1207             "CONFIGDN": names.configdn,
1208             "POLICYGUID": policyguid,
1209             "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1210             "SAMBA_VERSION_STRING": version
1211             })
1212
1213         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1214         if fill == FILL_FULL:
1215             logger.info("Adding configuration container")
1216             descr = b64encode(get_config_descriptor(domainsid))
1217             setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1218                     "CONFIGDN": names.configdn,
1219                     "DESCRIPTOR": descr,
1220                     })
1221
1222             # The LDIF here was created when the Schema object was constructed
1223             logger.info("Setting up sam.ldb schema")
1224             samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1225             samdb.modify_ldif(schema.schema_dn_modify)
1226             samdb.write_prefixes_from_schema()
1227             samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1228             setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1229                            {"SCHEMADN": names.schemadn})
1230
1231         # Now register this container in the root of the forest
1232         msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1233         msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1234                     "subRefs")
1235
1236     except:
1237         samdb.transaction_cancel()
1238         raise
1239     else:
1240         samdb.transaction_commit()
1241
1242     samdb.transaction_start()
1243     try:
1244         samdb.invocation_id = invocationid
1245
1246         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1247         if fill == FILL_FULL:
1248             logger.info("Setting up sam.ldb configuration data")
1249             setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1250                     "CONFIGDN": names.configdn,
1251                     "NETBIOSNAME": names.netbiosname,
1252                     "DEFAULTSITE": names.sitename,
1253                     "DNSDOMAIN": names.dnsdomain,
1254                     "DOMAIN": names.domain,
1255                     "SCHEMADN": names.schemadn,
1256                     "DOMAINDN": names.domaindn,
1257                     "SERVERDN": names.serverdn,
1258                     "FOREST_FUNCTIONALITY": str(forestFunctionality),
1259                     "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1260                     })
1261
1262             logger.info("Setting up display specifiers")
1263             display_specifiers_ldif = read_ms_ldif(
1264                 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1265             display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1266                                                      {"CONFIGDN": names.configdn})
1267             check_all_substituted(display_specifiers_ldif)
1268             samdb.add_ldif(display_specifiers_ldif)
1269
1270         logger.info("Adding users container")
1271         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1272                 "DOMAINDN": names.domaindn})
1273         logger.info("Modifying users container")
1274         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1275                 "DOMAINDN": names.domaindn})
1276         logger.info("Adding computers container")
1277         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1278                 "DOMAINDN": names.domaindn})
1279         logger.info("Modifying computers container")
1280         setup_modify_ldif(samdb,
1281             setup_path("provision_computers_modify.ldif"), {
1282                 "DOMAINDN": names.domaindn})
1283         logger.info("Setting up sam.ldb data")
1284         setup_add_ldif(samdb, setup_path("provision.ldif"), {
1285             "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1286             "DOMAINDN": names.domaindn,
1287             "NETBIOSNAME": names.netbiosname,
1288             "DEFAULTSITE": names.sitename,
1289             "CONFIGDN": names.configdn,
1290             "SERVERDN": names.serverdn,
1291             "RIDAVAILABLESTART": str(next_rid + 600),
1292             "POLICYGUID_DC": policyguid_dc
1293             })
1294
1295         # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1296         if fill == FILL_FULL:
1297             setup_modify_ldif(samdb,
1298                               setup_path("provision_configuration_references.ldif"), {
1299                     "CONFIGDN": names.configdn,
1300                     "SCHEMADN": names.schemadn})
1301
1302             logger.info("Setting up well known security principals")
1303             setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1304                 "CONFIGDN": names.configdn,
1305                 })
1306
1307         if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1308             setup_modify_ldif(samdb,
1309                               setup_path("provision_basedn_references.ldif"),
1310                               {"DOMAINDN": names.domaindn})
1311
1312             logger.info("Setting up sam.ldb users and groups")
1313             setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1314                 "DOMAINDN": names.domaindn,
1315                 "DOMAINSID": str(domainsid),
1316                 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1317                 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1318                 })
1319
1320             logger.info("Setting up self join")
1321             setup_self_join(samdb, admin_session_info, names=names, fill=fill,
1322                 invocationid=invocationid,
1323                 dnspass=dnspass,
1324                 machinepass=machinepass,
1325                 domainsid=domainsid,
1326                 next_rid=next_rid,
1327                 dc_rid=dc_rid,
1328                 policyguid=policyguid,
1329                 policyguid_dc=policyguid_dc,
1330                 domainControllerFunctionality=domainControllerFunctionality,
1331                 ntdsguid=ntdsguid)
1332
1333             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1334             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1335                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1336             assert isinstance(names.ntdsguid, str)
1337     except:
1338         samdb.transaction_cancel()
1339         raise
1340     else:
1341         samdb.transaction_commit()
1342         return samdb
1343
1344
1345 FILL_FULL = "FULL"
1346 FILL_SUBDOMAIN = "SUBDOMAIN"
1347 FILL_NT4SYNC = "NT4SYNC"
1348 FILL_DRS = "DRS"
1349 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1350 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1351
1352
1353 def set_dir_acl(path, acl, lp, domsid):
1354     setntacl(lp, path, acl, domsid)
1355     for root, dirs, files in os.walk(path, topdown=False):
1356         for name in files:
1357             setntacl(lp, os.path.join(root, name), acl, domsid)
1358         for name in dirs:
1359             setntacl(lp, os.path.join(root, name), acl, domsid)
1360
1361
1362 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1363     """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1364     folders beneath.
1365
1366     :param sysvol: Physical path for the sysvol folder
1367     :param dnsdomain: The DNS name of the domain
1368     :param domainsid: The SID of the domain
1369     :param domaindn: The DN of the domain (ie. DC=...)
1370     :param samdb: An LDB object on the SAM db
1371     :param lp: an LP object
1372     """
1373
1374     # Set ACL for GPO root folder
1375     root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1376     setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1377
1378     res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1379                         attrs=["cn", "nTSecurityDescriptor"],
1380                         expression="", scope=ldb.SCOPE_ONELEVEL)
1381
1382     for policy in res:
1383         acl = ndr_unpack(security.descriptor,
1384                          str(policy["nTSecurityDescriptor"])).as_sddl()
1385         policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1386         set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1387                     str(domainsid))
1388
1389
1390 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1391     lp):
1392     """Set the ACL for the sysvol share and the subfolders
1393
1394     :param samdb: An LDB object on the SAM db
1395     :param netlogon: Physical path for the netlogon folder
1396     :param sysvol: Physical path for the sysvol folder
1397     :param gid: The GID of the "Domain adminstrators" group
1398     :param domainsid: The SID of the domain
1399     :param dnsdomain: The DNS name of the domain
1400     :param domaindn: The DN of the domain (ie. DC=...)
1401     """
1402
1403     try:
1404         os.chown(sysvol, -1, gid)
1405     except OSError:
1406         canchown = False
1407     else:
1408         canchown = True
1409
1410     # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1411     setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1412     for root, dirs, files in os.walk(sysvol, topdown=False):
1413         for name in files:
1414             if canchown:
1415                 os.chown(os.path.join(root, name), -1, gid)
1416             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1417         for name in dirs:
1418             if canchown:
1419                 os.chown(os.path.join(root, name), -1, gid)
1420             setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1421
1422     # Set acls on Policy folder and policies folders
1423     set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1424
1425
1426 def interface_ips_v4(lp):
1427     '''return only IPv4 IPs'''
1428     ips = samba.interface_ips(lp, False)
1429     ret = []
1430     for i in ips:
1431         if i.find(':') == -1:
1432             ret.append(i)
1433     return ret
1434
1435 def interface_ips_v6(lp, linklocal=False):
1436     '''return only IPv6 IPs'''
1437     ips = samba.interface_ips(lp, False)
1438     ret = []
1439     for i in ips:
1440         if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1441             ret.append(i)
1442     return ret
1443
1444
1445 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1446                    domainsid, schema=None,
1447                    targetdir=None, samdb_fill=FILL_FULL,
1448                    hostip=None, hostip6=None,
1449                    next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1450                    domainguid=None, policyguid=None, policyguid_dc=None,
1451                    invocationid=None, machinepass=None, ntdsguid=None,
1452                    dns_backend=None, dnspass=None,
1453                    serverrole=None, dom_for_fun_level=None,
1454                    am_rodc=False, lp=None):
1455     # create/adapt the group policy GUIDs
1456     # Default GUID for default policy are described at
1457     # "How Core Group Policy Works"
1458     # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1459     if policyguid is None:
1460         policyguid = DEFAULT_POLICY_GUID
1461     policyguid = policyguid.upper()
1462     if policyguid_dc is None:
1463         policyguid_dc = DEFAULT_DC_POLICY_GUID
1464     policyguid_dc = policyguid_dc.upper()
1465
1466     if invocationid is None:
1467         invocationid = str(uuid.uuid4())
1468
1469     if krbtgtpass is None:
1470         krbtgtpass = samba.generate_random_password(128, 255)
1471     if machinepass is None:
1472         machinepass  = samba.generate_random_password(128, 255)
1473     if dnspass is None:
1474         dnspass = samba.generate_random_password(128, 255)
1475
1476     samdb = fill_samdb(samdb, lp, names, logger=logger,
1477                        domainsid=domainsid, schema=schema, domainguid=domainguid,
1478                        policyguid=policyguid, policyguid_dc=policyguid_dc,
1479                        fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1480                        invocationid=invocationid, machinepass=machinepass,
1481                        dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1482                        dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1483                        next_rid=next_rid, dc_rid=dc_rid)
1484
1485     if serverrole == "domain controller":
1486         # Set up group policies (domain policy and domain controller
1487         # policy)
1488         create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1489                            policyguid_dc)
1490         setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1491                      domainsid, names.dnsdomain, names.domaindn, lp)
1492
1493         secretsdb_self_join(secrets_ldb, domain=names.domain,
1494                             realm=names.realm, dnsdomain=names.dnsdomain,
1495                             netbiosname=names.netbiosname, domainsid=domainsid,
1496                             machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1497
1498         # Now set up the right msDS-SupportedEncryptionTypes into the DB
1499         # In future, this might be determined from some configuration
1500         kerberos_enctypes = str(ENC_ALL_TYPES)
1501
1502         try:
1503             msg = ldb.Message(ldb.Dn(samdb,
1504                                      samdb.searchone("distinguishedName",
1505                                                      expression="samAccountName=%s$" % names.netbiosname,
1506                                                      scope=ldb.SCOPE_SUBTREE)))
1507             msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1508                 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1509                 name="msDS-SupportedEncryptionTypes")
1510             samdb.modify(msg)
1511         except ldb.LdbError, (enum, estr):
1512             if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1513                 # It might be that this attribute does not exist in this schema
1514                 raise
1515
1516         setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
1517                      hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1518                      dnspass=dnspass, os_level=dom_for_fun_level,
1519                      targetdir=targetdir, site=DEFAULTSITE)
1520
1521         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1522                                      attribute="objectGUID")
1523         assert isinstance(domainguid, str)
1524
1525     lastProvisionUSNs = get_last_provision_usn(samdb)
1526     maxUSN = get_max_usn(samdb, str(names.rootdn))
1527     if lastProvisionUSNs is not None:
1528         update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1529     else:
1530         set_provision_usn(samdb, 0, maxUSN, invocationid)
1531
1532     logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1533     setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1534                       { 'NTDSGUID' : names.ntdsguid })
1535
1536     # fix any dangling GUIDs from the provision
1537     logger.info("Fixing provision GUIDs")
1538     chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
1539             quiet=True)
1540     samdb.transaction_start()
1541     try:
1542         # a small number of GUIDs are missing because of ordering issues in the
1543         # provision code
1544         for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1545             chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1546                                scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1547         chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1548                            scope=ldb.SCOPE_ONELEVEL,
1549                            attrs=['ipsecOwnersReference',
1550                                   'ipsecFilterReference',
1551                                   'ipsecISAKMPReference',
1552                                   'ipsecNegotiationPolicyReference',
1553                                   'ipsecNFAReference'])
1554     except:
1555         samdb.transaction_cancel()
1556         raise
1557     else:
1558         samdb.transaction_commit()
1559
1560
1561 _ROLES_MAP = {
1562     "ROLE_STANDALONE": "standalone",
1563     "ROLE_DOMAIN_MEMBER": "member server",
1564     "ROLE_DOMAIN_BDC": "domain controller",
1565     "ROLE_DOMAIN_PDC": "domain controller",
1566     "dc": "domain controller",
1567     "member": "member server",
1568     "domain controller": "domain controller",
1569     "member server": "member server",
1570     "standalone": "standalone",
1571     }
1572
1573
1574 def sanitize_server_role(role):
1575     """Sanitize a server role name.
1576
1577     :param role: Server role
1578     :raise ValueError: If the role can not be interpreted
1579     :return: Sanitized server role (one of "member server",
1580         "domain controller", "standalone")
1581     """
1582     try:
1583         return  _ROLES_MAP[role]
1584     except KeyError:
1585         raise ValueError(role)
1586
1587
1588 def provision(logger, session_info, credentials, smbconf=None,
1589         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1590         domaindn=None, schemadn=None, configdn=None, serverdn=None,
1591         domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1592         next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1593         domainguid=None, policyguid=None, policyguid_dc=None,
1594         dns_backend=None, dnspass=None,
1595         invocationid=None, machinepass=None, ntdsguid=None,
1596         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1597         serverrole=None, dom_for_fun_level=None, 
1598         backend_type=None, sitename=None,
1599         ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1600         useeadb=False, am_rodc=False,
1601         lp=None):
1602     """Provision samba4
1603
1604     :note: caution, this wipes all existing data!
1605     """
1606
1607     try:
1608         serverrole = sanitize_server_role(serverrole)
1609     except ValueError:
1610         raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
1611
1612     if ldapadminpass is None:
1613         # Make a new, random password between Samba and it's LDAP server
1614         ldapadminpass = samba.generate_random_password(128, 255)
1615
1616     if backend_type is None:
1617         backend_type = "ldb"
1618
1619     if domainsid is None:
1620         domainsid = security.random_sid()
1621     else:
1622         domainsid = security.dom_sid(domainsid)
1623
1624     sid_generator = "internal"
1625     if backend_type == "fedora-ds":
1626         sid_generator = "backend"
1627
1628     root_uid = findnss_uid([root or "root"])
1629     nobody_uid = findnss_uid([nobody or "nobody"])
1630     users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1631     if wheel is None:
1632         wheel_gid = findnss_gid(["wheel", "adm"])
1633     else:
1634         wheel_gid = findnss_gid([wheel])
1635     try:
1636         bind_gid = findnss_gid(["bind", "named"])
1637     except KeyError:
1638         bind_gid = None
1639
1640     if targetdir is not None:
1641         smbconf = os.path.join(targetdir, "etc", "smb.conf")
1642     elif smbconf is None:
1643         smbconf = samba.param.default_path()
1644     if not os.path.exists(os.path.dirname(smbconf)):
1645         os.makedirs(os.path.dirname(smbconf))
1646
1647     server_services = None
1648     if dns_backend == "SAMBA_INTERNAL":
1649         server_services = [ "+dns" ]
1650
1651     # only install a new smb.conf if there isn't one there already
1652     if os.path.exists(smbconf):
1653         # if Samba Team members can't figure out the weird errors
1654         # loading an empty smb.conf gives, then we need to be smarter.
1655         # Pretend it just didn't exist --abartlet
1656         data = open(smbconf, 'r').read()
1657         data = data.lstrip()
1658         if data is None or data == "":
1659             make_smbconf(smbconf, hostname, domain, realm,
1660                          serverrole, targetdir, sid_generator, useeadb,
1661                          lp=lp, server_services=server_services)
1662     else:
1663         make_smbconf(smbconf, hostname, domain, realm, serverrole,
1664                      targetdir, sid_generator, useeadb, lp=lp,
1665                      server_services=server_services)
1666
1667     if lp is None:
1668         lp = samba.param.LoadParm()
1669     lp.load(smbconf)
1670     names = guess_names(lp=lp, hostname=hostname, domain=domain,
1671         dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1672         configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1673         sitename=sitename, rootdn=rootdn)
1674     paths = provision_paths_from_lp(lp, names.dnsdomain)
1675
1676     paths.bind_gid = bind_gid
1677     paths.wheel_gid = wheel_gid
1678
1679     if hostip is None:
1680         logger.info("Looking up IPv4 addresses")
1681         hostips = interface_ips_v4(lp)
1682         if len(hostips) > 0:
1683             hostip = hostips[0]
1684             if len(hostips) > 1:
1685                 logger.warning("More than one IPv4 address found. Using %s",
1686                     hostip)
1687     if hostip == "127.0.0.1":
1688         hostip = None
1689     if hostip is None:
1690         logger.warning("No IPv4 address will be assigned")
1691
1692     if hostip6 is None:
1693         logger.info("Looking up IPv6 addresses")
1694         hostips = interface_ips_v6(lp, linklocal=False)
1695         if hostips:
1696             hostip6 = hostips[0]
1697         if len(hostips) > 1:
1698             logger.warning("More than one IPv6 address found. Using %s", hostip6)
1699     if hostip6 is None:
1700         logger.warning("No IPv6 address will be assigned")
1701
1702     names.hostip = hostip
1703     names.hostip6 = hostip6
1704
1705     if serverrole is None:
1706         serverrole = lp.get("server role")
1707
1708     if not os.path.exists(paths.private_dir):
1709         os.mkdir(paths.private_dir)
1710     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1711         os.mkdir(os.path.join(paths.private_dir, "tls"))
1712
1713     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1714
1715     schema = Schema(domainsid, invocationid=invocationid,
1716         schemadn=names.schemadn)
1717
1718     if backend_type == "ldb":
1719         provision_backend = LDBBackend(backend_type, paths=paths,
1720             lp=lp, credentials=credentials,
1721             names=names, logger=logger)
1722     elif backend_type == "existing":
1723         # If support for this is ever added back, then the URI will need to be specified again
1724         provision_backend = ExistingBackend(backend_type, paths=paths,
1725             lp=lp, credentials=credentials,
1726             names=names, logger=logger,
1727             ldap_backend_forced_uri=None)
1728     elif backend_type == "fedora-ds":
1729         provision_backend = FDSBackend(backend_type, paths=paths,
1730             lp=lp, credentials=credentials,
1731             names=names, logger=logger, domainsid=domainsid,
1732             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1733             slapd_path=slapd_path,
1734             root=root)
1735     elif backend_type == "openldap":
1736         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1737             lp=lp, credentials=credentials,
1738             names=names, logger=logger, domainsid=domainsid,
1739             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1740             slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1741     else:
1742         raise ValueError("Unknown LDAP backend type selected")
1743
1744     provision_backend.init()
1745     provision_backend.start()
1746
1747     # only install a new shares config db if there is none
1748     if not os.path.exists(paths.shareconf):
1749         logger.info("Setting up share.ldb")
1750         share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
1751         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1752
1753     logger.info("Setting up secrets.ldb")
1754     secrets_ldb = setup_secretsdb(paths,
1755         session_info=session_info,
1756         backend_credentials=provision_backend.secrets_credentials, lp=lp)
1757
1758     try:
1759         logger.info("Setting up the registry")
1760         setup_registry(paths.hklm, session_info, lp=lp)
1761
1762         logger.info("Setting up the privileges database")
1763         setup_privileges(paths.privilege, session_info, lp=lp)
1764
1765         logger.info("Setting up idmap db")
1766         idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
1767
1768         setup_name_mappings(idmap, sid=str(domainsid),
1769                             root_uid=root_uid, nobody_uid=nobody_uid,
1770                             users_gid=users_gid, wheel_gid=wheel_gid)
1771
1772         logger.info("Setting up SAM db")
1773         samdb = setup_samdb(paths.samdb, session_info,
1774                             provision_backend, lp, names, logger=logger,
1775                             serverrole=serverrole,
1776                             schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1777
1778         if serverrole == "domain controller":
1779             if paths.netlogon is None:
1780                 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1781                 logger.info("Please either remove %s or see the template at %s" %
1782                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1783                 assert paths.netlogon is not None
1784
1785             if paths.sysvol is None:
1786                 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1787                         " are configuring a DC.")
1788                 logger.info("Please either remove %s or see the template at %s" %
1789                         (paths.smbconf, setup_path("provision.smb.conf.dc")))
1790                 assert paths.sysvol is not None
1791
1792             if not os.path.isdir(paths.netlogon):
1793                 os.makedirs(paths.netlogon, 0755)
1794
1795         if adminpass is None:
1796             adminpass = samba.generate_random_password(12, 32)
1797             adminpass_generated = True
1798         else:
1799             adminpass_generated = False
1800
1801         if samdb_fill == FILL_FULL:
1802             provision_fill(samdb, secrets_ldb, logger, names, paths,
1803                     schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
1804                     hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1805                     next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1806                     krbtgtpass=krbtgtpass, domainguid=domainguid,
1807                     policyguid=policyguid, policyguid_dc=policyguid_dc,
1808                     invocationid=invocationid, machinepass=machinepass,
1809                     ntdsguid=ntdsguid, dns_backend=dns_backend,
1810                     dnspass=dnspass, serverrole=serverrole,
1811                     dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1812                     lp=lp)
1813
1814         create_krb5_conf(paths.krb5conf,
1815                          dnsdomain=names.dnsdomain, hostname=names.hostname,
1816                          realm=names.realm)
1817         logger.info("A Kerberos configuration suitable for Samba 4 has been "
1818                     "generated at %s", paths.krb5conf)
1819
1820         if serverrole == "domain controller":
1821             create_dns_update_list(lp, logger, paths)
1822
1823         backend_result = provision_backend.post_setup()
1824         provision_backend.shutdown()
1825
1826         create_phpldapadmin_config(paths.phpldapadminconfig,
1827                                    ldapi_url)
1828     except:
1829         secrets_ldb.transaction_cancel()
1830         raise
1831
1832     # Now commit the secrets.ldb to disk
1833     secrets_ldb.transaction_commit()
1834
1835     # the commit creates the dns.keytab, now chown it
1836     dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1837     if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1838         try:
1839             os.chmod(dns_keytab_path, 0640)
1840             os.chown(dns_keytab_path, -1, paths.bind_gid)
1841         except OSError:
1842             if not os.environ.has_key('SAMBA_SELFTEST'):
1843                 logger.info("Failed to chown %s to bind gid %u",
1844                             dns_keytab_path, paths.bind_gid)
1845
1846     result = ProvisionResult()
1847     result.server_role = serverrole
1848     result.domaindn = domaindn
1849     result.paths = paths
1850     result.names = names
1851     result.lp = lp
1852     result.samdb = samdb
1853     result.idmap = idmap
1854     result.domainsid = str(domainsid)
1855
1856     if samdb_fill == FILL_FULL:
1857         result.adminpass_generated = adminpass_generated
1858         result.adminpass = adminpass
1859     else:
1860         result.adminpass_generated = False
1861         result.adminpass = None
1862
1863     result.backend_result = backend_result
1864
1865     return result
1866
1867
1868 def provision_become_dc(smbconf=None, targetdir=None,
1869         realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1870         serverdn=None, domain=None, hostname=None, domainsid=None,
1871         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1872         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1873         dns_backend=None, root=None, nobody=None, users=None, wheel=None,
1874         backup=None, serverrole=None, ldap_backend=None,
1875         ldap_backend_type=None, sitename=None, debuglevel=1):
1876
1877     logger = logging.getLogger("provision")
1878     samba.set_debug_level(debuglevel)
1879
1880     res = provision(logger, system_session(), None,
1881         smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1882         realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1883         configdn=configdn, serverdn=serverdn, domain=domain,
1884         hostname=hostname, hostip=None, domainsid=domainsid,
1885         machinepass=machinepass, serverrole="domain controller",
1886         sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1887     res.lp.set("debuglevel", str(debuglevel))
1888     return res
1889
1890
1891 def create_phpldapadmin_config(path, ldapi_uri):
1892     """Create a PHP LDAP admin configuration file.
1893
1894     :param path: Path to write the configuration to.
1895     """
1896     setup_file(setup_path("phpldapadmin-config.php"), path,
1897             {"S4_LDAPI_URI": ldapi_uri})
1898
1899
1900 def create_krb5_conf(path, dnsdomain, hostname, realm):
1901     """Write out a file containing zone statements suitable for inclusion in a
1902     named.conf file (including GSS-TSIG configuration).
1903
1904     :param path: Path of the new named.conf file.
1905     :param dnsdomain: DNS Domain name
1906     :param hostname: Local hostname
1907     :param realm: Realm name
1908     """
1909     setup_file(setup_path("krb5.conf"), path, {
1910             "DNSDOMAIN": dnsdomain,
1911             "HOSTNAME": hostname,
1912             "REALM": realm,
1913         })
1914
1915
1916 class ProvisioningError(Exception):
1917     """A generic provision error."""
1918
1919     def __init__(self, value):
1920         self.value = value
1921
1922     def __str__(self):
1923         return "ProvisioningError: " + self.value
1924
1925
1926 class InvalidNetbiosName(Exception):
1927     """A specified name was not a valid NetBIOS name."""
1928
1929     def __init__(self, name):
1930         super(InvalidNetbiosName, self).__init__(
1931             "The name '%r' is not a valid NetBIOS name" % name)