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