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