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