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