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