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