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