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