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