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