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