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