2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
42 from samba.auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name
45 from samba import check_all_substituted, read_and_sub_file, setup_file
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008_R2, ENC_ALL_TYPES
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ms_display_specifiers import read_ms_ldif
51 from samba.ntacls import setntacl, dsacl2fsacl
52 from samba.ndr import ndr_pack,ndr_unpack
53 from samba.provisionbackend import (
61 from samba.schema import Schema
62 from samba.samdb import SamDB
64 __docformat__ = "restructuredText"
65 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
66 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
69 """Find the setup directory used by provision."""
71 for prefix in [sys.prefix,
72 os.path.join(os.path.dirname(__file__), "../../../..")]:
73 for suffix in ["share/setup", "share/samba/setup", "setup"]:
74 ret = os.path.join(prefix, suffix)
75 if os.path.isdir(ret):
78 dirname = os.path.dirname(__file__)
79 ret = os.path.join(dirname, "../../../setup")
80 if os.path.isdir(ret):
82 raise Exception("Unable to find setup directory.")
84 # Descriptors of naming contexts and other important objects
86 # "get_schema_descriptor" is located in "schema.py"
88 def get_sites_descriptor(domain_sid):
89 sddl = "O:EAG:EAD:AI(A;;RPLCLORC;;;AU)" \
90 "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
91 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
92 "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
93 "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
94 "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
95 "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
96 "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
97 "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
98 "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
99 sec = security.descriptor.from_sddl(sddl, domain_sid)
102 def get_config_descriptor(domain_sid):
103 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
104 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
105 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
106 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
107 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
108 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
109 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
110 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
111 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
112 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
113 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
115 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
116 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
117 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
118 sec = security.descriptor.from_sddl(sddl, domain_sid)
121 def get_domain_descriptor(domain_sid):
122 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
131 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
132 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
133 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
134 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
135 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
136 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
137 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
138 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
142 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
143 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
144 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
145 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
146 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
148 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
149 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
150 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
151 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
152 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
153 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
156 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
157 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
158 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
159 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
160 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
163 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
165 "(A;;RPLCLORC;;;ED)" \
166 "(A;;RPLCLORC;;;AU)" \
167 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
168 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
169 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
170 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
171 sec = security.descriptor.from_sddl(sddl, domain_sid)
174 DEFAULTSITE = "Default-First-Site-Name"
175 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
177 class ProvisionPaths(object):
180 self.shareconf = None
191 self.dns_keytab = None
194 self.private_dir = None
197 class ProvisionNames(object):
204 self.ldapmanagerdn = None
205 self.dnsdomain = None
207 self.netbiosname = None
214 def update_provision_usn(samdb, low, high, replace=False):
215 """Update the field provisionUSN in sam.ldb
217 This field is used to track range of USN modified by provision and
219 This value is used afterward by next provision to figure out if
220 the field have been modified since last provision.
222 :param samdb: An LDB object connect to sam.ldb
223 :param low: The lowest USN modified by this upgrade
224 :param high: The highest USN modified by this upgrade
225 :param replace: A boolean indicating if the range should replace any
226 existing one or appended (default)
231 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % \
232 LAST_PROVISION_USN_ATTRIBUTE, base="",
233 scope=ldb.SCOPE_SUBTREE,
234 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
235 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
238 tab.append("%s-%s" % (low, high))
239 delta = ldb.Message()
240 delta.dn = ldb.Dn(samdb, "@PROVISION")
241 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
242 ldb.FLAG_MOD_REPLACE,
243 LAST_PROVISION_USN_ATTRIBUTE)
247 def set_provision_usn(samdb, low, high):
248 """Set the field provisionUSN in sam.ldb
249 This field is used to track range of USN modified by provision and
251 This value is used afterward by next provision to figure out if
252 the field have been modified since last provision.
254 :param samdb: An LDB object connect to sam.ldb
255 :param low: The lowest USN modified by this upgrade
256 :param high: The highest USN modified by this upgrade"""
258 tab.append("%s-%s" % (low, high))
259 delta = ldb.Message()
260 delta.dn = ldb.Dn(samdb, "@PROVISION")
261 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
263 LAST_PROVISION_USN_ATTRIBUTE)
267 def get_max_usn(samdb,basedn):
268 """ This function return the biggest USN present in the provision
270 :param samdb: A LDB object pointing to the sam.ldb
271 :param basedn: A string containing the base DN of the provision
273 :return: The biggest USN in the provision"""
275 res = samdb.search(expression="objectClass=*",base=basedn,
276 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
277 controls=["search_options:1:2",
278 "server_sort:1:1:uSNChanged",
279 "paged_results:1:1"])
280 return res[0]["uSNChanged"]
282 def get_last_provision_usn(sam):
283 """Get the lastest USN modified by a provision or an upgradeprovision
285 :param sam: An LDB object pointing to the sam.ldb
286 :return an integer corresponding to the highest USN modified by
287 (upgrade)provision, 0 is this value is unknown"""
289 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % \
290 LAST_PROVISION_USN_ATTRIBUTE,
291 base="", scope=ldb.SCOPE_SUBTREE,
292 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
297 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
298 tab = p.split(str(r))
306 class ProvisionResult(object):
315 def check_install(lp, session_info, credentials):
316 """Check whether the current install seems ok.
318 :param lp: Loadparm context
319 :param session_info: Session information
320 :param credentials: Credentials
322 if lp.get("realm") == "":
323 raise Exception("Realm empty")
324 samdb = Ldb(lp.get("sam database"), session_info=session_info,
325 credentials=credentials, lp=lp)
326 if len(samdb.search("(cn=Administrator)")) != 1:
327 raise ProvisioningError("No administrator account found")
330 def findnss(nssfn, names):
331 """Find a user or group from a list of possibilities.
333 :param nssfn: NSS Function to try (should raise KeyError if not found)
334 :param names: Names to check.
335 :return: Value return by first names list.
342 raise KeyError("Unable to find user/group in %r" % names)
345 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
346 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
349 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
350 """Setup a ldb in the private dir.
352 :param ldb: LDB file to import data into
353 :param ldif_path: Path of the LDIF file to load
354 :param subst_vars: Optional variables to subsitute in LDIF.
355 :param nocontrols: Optional list of controls, can be None for no controls
357 assert isinstance(ldif_path, str)
358 data = read_and_sub_file(ldif_path, subst_vars)
359 ldb.add_ldif(data, controls)
362 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
363 """Modify a ldb in the private dir.
365 :param ldb: LDB object.
366 :param ldif_path: LDIF file path.
367 :param subst_vars: Optional dictionary with substitution variables.
369 data = read_and_sub_file(ldif_path, subst_vars)
370 ldb.modify_ldif(data, controls)
373 def setup_ldb(ldb, ldif_path, subst_vars):
374 """Import a LDIF a file into a LDB handle, optionally substituting variables.
376 :note: Either all LDIF data will be added or none (using transactions).
378 :param ldb: LDB file to import into.
379 :param ldif_path: Path to the LDIF file.
380 :param subst_vars: Dictionary with substitution variables.
382 assert ldb is not None
383 ldb.transaction_start()
385 setup_add_ldif(ldb, ldif_path, subst_vars)
387 ldb.transaction_cancel()
390 ldb.transaction_commit()
393 def provision_paths_from_lp(lp, dnsdomain):
394 """Set the default paths for provisioning.
396 :param lp: Loadparm context.
397 :param dnsdomain: DNS Domain name
399 paths = ProvisionPaths()
400 paths.private_dir = lp.get("private dir")
402 # This is stored without path prefix for the "privateKeytab" attribute in
403 # "secrets_dns.ldif".
404 paths.dns_keytab = "dns.keytab"
405 paths.keytab = "secrets.keytab"
407 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
408 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
409 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
410 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
411 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
412 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
413 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
414 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
415 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
416 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
417 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
418 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
419 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
420 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
421 paths.phpldapadminconfig = os.path.join(paths.private_dir,
422 "phpldapadmin-config.php")
423 paths.hklm = "hklm.ldb"
424 paths.hkcr = "hkcr.ldb"
425 paths.hkcu = "hkcu.ldb"
426 paths.hku = "hku.ldb"
427 paths.hkpd = "hkpd.ldb"
428 paths.hkpt = "hkpt.ldb"
429 paths.sysvol = lp.get("path", "sysvol")
430 paths.netlogon = lp.get("path", "netlogon")
431 paths.smbconf = lp.configfile
435 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
436 serverrole=None, rootdn=None, domaindn=None, configdn=None,
437 schemadn=None, serverdn=None, sitename=None):
438 """Guess configuration settings to use."""
441 hostname = socket.gethostname().split(".")[0]
443 netbiosname = lp.get("netbios name")
444 if netbiosname is None:
445 netbiosname = hostname
446 assert netbiosname is not None
447 netbiosname = netbiosname.upper()
448 if not valid_netbios_name(netbiosname):
449 raise InvalidNetbiosName(netbiosname)
451 if dnsdomain is None:
452 dnsdomain = lp.get("realm")
453 if dnsdomain is None or dnsdomain == "":
454 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
456 dnsdomain = dnsdomain.lower()
458 if serverrole is None:
459 serverrole = lp.get("server role")
460 if serverrole is None:
461 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
463 serverrole = serverrole.lower()
465 realm = dnsdomain.upper()
467 if lp.get("realm") == "":
468 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
470 if lp.get("realm").upper() != realm:
471 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
473 if lp.get("server role").lower() != serverrole:
474 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").upper(), serverrole, lp.configfile))
476 if serverrole == "domain controller":
478 # This will, for better or worse, default to 'WORKGROUP'
479 domain = lp.get("workgroup")
480 domain = domain.upper()
482 if lp.get("workgroup").upper() != domain:
483 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))
486 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
490 domaindn = "DC=" + netbiosname
492 if not valid_netbios_name(domain):
493 raise InvalidNetbiosName(domain)
495 if hostname.upper() == realm:
496 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
497 if netbiosname == realm:
498 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
500 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
506 configdn = "CN=Configuration," + rootdn
508 schemadn = "CN=Schema," + configdn
513 names = ProvisionNames()
514 names.rootdn = rootdn
515 names.domaindn = domaindn
516 names.configdn = configdn
517 names.schemadn = schemadn
518 names.ldapmanagerdn = "CN=Manager," + rootdn
519 names.dnsdomain = dnsdomain
520 names.domain = domain
522 names.netbiosname = netbiosname
523 names.hostname = hostname
524 names.sitename = sitename
525 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
530 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
531 targetdir, sid_generator="internal", eadb=False):
532 """Create a new smb.conf file based on a couple of basic settings.
534 assert smbconf is not None
536 hostname = socket.gethostname().split(".")[0]
537 netbiosname = hostname.upper()
539 if serverrole is None:
540 serverrole = "standalone"
542 assert serverrole in ("domain controller", "member server", "standalone")
543 if serverrole == "domain controller":
545 elif serverrole == "member server":
546 smbconfsuffix = "member"
547 elif serverrole == "standalone":
548 smbconfsuffix = "standalone"
550 if sid_generator is None:
551 sid_generator = "internal"
553 assert domain is not None
554 domain = domain.upper()
556 assert realm is not None
557 realm = realm.upper()
559 default_lp = samba.param.LoadParm()
560 #Load non-existant file
561 if os.path.exists(smbconf):
562 default_lp.load(smbconf)
564 if targetdir is not None:
565 privdir = os.path.join(targetdir, "private")
567 privdir = default_lp.get("private dir")
568 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
572 if targetdir is not None:
573 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
574 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
576 default_lp.set("lock dir", os.path.abspath(targetdir))
581 if sid_generator == "internal":
582 sid_generator_line = ""
584 sid_generator_line = "sid generator = " + sid_generator
586 used_setup_dir = setup_path("")
587 default_setup_dir = default_lp.get("setup directory")
589 if used_setup_dir != default_setup_dir:
590 setupdir_line = "setup directory = %s" % used_setup_dir
591 default_lp.set("setup directory", used_setup_dir)
593 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
594 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
596 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
598 "NETBIOS_NAME": netbiosname,
601 "SERVERROLE": serverrole,
602 "NETLOGONPATH": netlogon,
603 "SYSVOLPATH": sysvol,
604 "SETUPDIRECTORY_LINE": setupdir_line,
605 "SIDGENERATOR_LINE": sid_generator_line,
606 "PRIVATEDIR_LINE": privatedir_line,
607 "LOCKDIR_LINE": lockdir_line,
608 "POSIXEADB_LINE": posixeadb_line
612 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
613 users_gid, wheel_gid):
614 """setup reasonable name mappings for sam names to unix names.
616 :param samdb: SamDB object.
617 :param idmap: IDmap db object.
618 :param sid: The domain sid.
619 :param domaindn: The domain DN.
620 :param root_uid: uid of the UNIX root user.
621 :param nobody_uid: uid of the UNIX nobody user.
622 :param users_gid: gid of the UNIX users group.
623 :param wheel_gid: gid of the UNIX wheel group."""
624 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
625 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
627 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
628 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
631 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
632 provision_backend, names, schema, serverrole,
634 """Setup the partitions for the SAM database.
636 Alternatively, provision() may call this, and then populate the database.
638 :note: This will wipe the Sam Database!
640 :note: This function always removes the local SAM LDB file. The erase
641 parameter controls whether to erase the existing data, which
642 may not be stored locally but in LDAP.
645 assert session_info is not None
647 # We use options=["modules:"] to stop the modules loading - we
648 # just want to wipe and re-initialise the database, not start it up
651 os.unlink(samdb_path)
655 samdb = Ldb(url=samdb_path, session_info=session_info,
656 lp=lp, options=["modules:"])
658 ldap_backend_line = "# No LDAP backend"
659 if provision_backend.type is not "ldb":
660 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
662 samdb.transaction_start()
664 logger.info("Setting up sam.ldb partitions and settings")
665 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
666 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
667 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
668 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
669 "LDAP_BACKEND_LINE": ldap_backend_line,
673 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
674 "BACKEND_TYPE": provision_backend.type,
675 "SERVER_ROLE": serverrole
678 logger.info("Setting up sam.ldb rootDSE")
679 setup_samdb_rootdse(samdb, setup_path, names)
681 samdb.transaction_cancel()
684 samdb.transaction_commit()
687 def secretsdb_self_join(secretsdb, domain,
688 netbiosname, machinepass, domainsid=None,
689 realm=None, dnsdomain=None,
691 key_version_number=1,
692 secure_channel_type=SEC_CHAN_WKSTA):
693 """Add domain join-specific bits to a secrets database.
695 :param secretsdb: Ldb Handle to the secrets database
696 :param machinepass: Machine password
698 attrs=["whenChanged",
705 if realm is not None:
706 if dnsdomain is None:
707 dnsdomain = realm.lower()
708 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
711 shortname = netbiosname.lower()
713 #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
714 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
715 msg["secureChannelType"] = [str(secure_channel_type)]
716 msg["objectClass"] = ["top", "primaryDomain"]
717 if dnsname is not None:
718 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
719 msg["realm"] = [realm]
720 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
721 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
722 msg["privateKeytab"] = ["secrets.keytab"]
724 msg["secret"] = [machinepass]
725 msg["samAccountName"] = ["%s$" % netbiosname]
726 msg["secureChannelType"] = [str(secure_channel_type)]
727 if domainsid is not None:
728 msg["objectSid"] = [ndr_pack(domainsid)]
730 # This complex expression tries to ensure that we don't have more
731 # than one record for this SID, realm or netbios domain at a time,
732 # but we don't delete the old record that we are about to modify,
733 # because that would delete the keytab and previous password.
734 res = secretsdb.search(base="cn=Primary Domains",
736 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
737 scope=ldb.SCOPE_ONELEVEL)
740 secretsdb.delete(del_msg.dn)
742 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
745 msg["priorSecret"] = [res[0]["secret"][0]]
746 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
749 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
754 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
760 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
761 secretsdb.modify(msg)
762 secretsdb.rename(res[0].dn, msg.dn)
764 spn = [ 'HOST/%s' % shortname ]
765 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
766 # we are a domain controller then we add servicePrincipalName entries
767 # for the keytab code to update
768 spn.extend([ 'HOST/%s' % dnsname ])
769 msg["servicePrincipalName"] = spn
774 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir,
776 dns_keytab_path, dnspass):
777 """Add DNS specific bits to a secrets database.
779 :param secretsdb: Ldb Handle to the secrets database
780 :param setup_path: Setup path function
781 :param machinepass: Machine password
784 os.unlink(os.path.join(private_dir, dns_keytab_path))
788 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
790 "DNSDOMAIN": dnsdomain,
791 "DNS_KEYTAB": dns_keytab_path,
792 "DNSPASS_B64": b64encode(dnspass),
793 "HOSTNAME": names.hostname,
794 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
798 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
799 """Setup the secrets database.
801 :note: This function does not handle exceptions and transaction on purpose,
802 it's up to the caller to do this job.
804 :param path: Path to the secrets database.
805 :param setup_path: Get the path to a setup file.
806 :param session_info: Session info.
807 :param credentials: Credentials
808 :param lp: Loadparm context
809 :return: LDB handle for the created secrets database
811 if os.path.exists(paths.secrets):
812 os.unlink(paths.secrets)
814 keytab_path = os.path.join(paths.private_dir, paths.keytab)
815 if os.path.exists(keytab_path):
816 os.unlink(keytab_path)
818 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
819 if os.path.exists(dns_keytab_path):
820 os.unlink(dns_keytab_path)
824 secrets_ldb = Ldb(path, session_info=session_info,
827 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
828 secrets_ldb = Ldb(path, session_info=session_info,
830 secrets_ldb.transaction_start()
832 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
834 if backend_credentials is not None and backend_credentials.authentication_requested():
835 if backend_credentials.get_bind_dn() is not None:
836 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
837 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
838 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
841 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
842 "LDAPADMINUSER": backend_credentials.get_username(),
843 "LDAPADMINREALM": backend_credentials.get_realm(),
844 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
849 secrets_ldb.transaction_cancel()
852 def setup_privileges(path, setup_path, session_info, lp):
853 """Setup the privileges database.
855 :param path: Path to the privileges database.
856 :param setup_path: Get the path to a setup file.
857 :param session_info: Session info.
858 :param credentials: Credentials
859 :param lp: Loadparm context
860 :return: LDB handle for the created secrets database
862 if os.path.exists(path):
864 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
865 privilege_ldb.erase()
866 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
869 def setup_registry(path, setup_path, session_info, lp):
870 """Setup the registry.
872 :param path: Path to the registry database
873 :param setup_path: Function that returns the path to a setup.
874 :param session_info: Session information
875 :param credentials: Credentials
876 :param lp: Loadparm context
878 reg = samba.registry.Registry()
879 hive = samba.registry.open_ldb(path, session_info=session_info,
881 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
882 provision_reg = setup_path("provision.reg")
883 assert os.path.exists(provision_reg)
884 reg.diff_apply(provision_reg)
887 def setup_idmapdb(path, setup_path, session_info, lp):
888 """Setup the idmap database.
890 :param path: path to the idmap database
891 :param setup_path: Function that returns a path to a setup file
892 :param session_info: Session information
893 :param credentials: Credentials
894 :param lp: Loadparm context
896 if os.path.exists(path):
899 idmap_ldb = IDmapDB(path, session_info=session_info,
903 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
907 def setup_samdb_rootdse(samdb, setup_path, names):
908 """Setup the SamDB rootdse.
910 :param samdb: Sam Database handle
911 :param setup_path: Obtain setup path
913 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
914 "SCHEMADN": names.schemadn,
915 "DOMAINDN": names.domaindn,
916 "ROOTDN": names.rootdn,
917 "CONFIGDN": names.configdn,
918 "SERVERDN": names.serverdn,
922 def setup_self_join(samdb, names,
923 machinepass, dnspass,
924 domainsid, next_rid, invocationid, setup_path,
925 policyguid, policyguid_dc, domainControllerFunctionality,
927 """Join a host to its own domain."""
928 assert isinstance(invocationid, str)
929 if ntdsguid is not None:
930 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
933 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
934 "CONFIGDN": names.configdn,
935 "SCHEMADN": names.schemadn,
936 "DOMAINDN": names.domaindn,
937 "SERVERDN": names.serverdn,
938 "INVOCATIONID": invocationid,
939 "NETBIOSNAME": names.netbiosname,
940 "DEFAULTSITE": names.sitename,
941 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
942 "MACHINEPASS_B64": b64encode(machinepass),
943 "REALM": names.realm,
944 "DOMAIN": names.domain,
945 "DOMAINSID": str(domainsid),
946 "DCRID": str(next_rid),
947 "DNSDOMAIN": names.dnsdomain,
948 "SAMBA_VERSION_STRING": version,
949 "NTDSGUID": ntdsguid_line,
950 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
952 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
953 "POLICYGUID": policyguid,
954 "POLICYGUID_DC": policyguid_dc,
955 "DNSDOMAIN": names.dnsdomain,
956 "DOMAINSID": str(domainsid),
957 "DOMAINDN": names.domaindn})
959 # add the NTDSGUID based SPNs
960 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
961 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
962 expression="", scope=ldb.SCOPE_BASE)
963 assert isinstance(names.ntdsguid, str)
965 # Setup fSMORoleOwner entries to point at the newly created DC entry
966 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
967 "DOMAIN": names.domain,
968 "DNSDOMAIN": names.dnsdomain,
969 "DOMAINDN": names.domaindn,
970 "CONFIGDN": names.configdn,
971 "SCHEMADN": names.schemadn,
972 "DEFAULTSITE": names.sitename,
973 "SERVERDN": names.serverdn,
974 "NETBIOSNAME": names.netbiosname,
975 "NTDSGUID": names.ntdsguid,
976 "RIDALLOCATIONSTART": str(next_rid + 100),
977 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
980 # This is partially Samba4 specific and should be replaced by the correct
982 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
983 "DNSDOMAIN": names.dnsdomain,
984 "DOMAINDN": names.domaindn,
985 "DNSPASS_B64": b64encode(dnspass),
986 "HOSTNAME" : names.hostname,
987 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
990 def getpolicypath(sysvolpath, dnsdomain, guid):
991 """Return the physical path of policy given its guid.
993 :param sysvolpath: Path to the sysvol folder
994 :param dnsdomain: DNS name of the AD domain
995 :param guid: The GUID of the policy
996 :return: A string with the complete path to the policy folder
1000 guid = "{%s}" % guid
1001 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1004 def create_gpo_struct(policy_path):
1005 if not os.path.exists(policy_path):
1006 os.makedirs(policy_path, 0775)
1007 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1008 "[General]\r\nVersion=0")
1009 p = os.path.join(policy_path, "MACHINE")
1010 if not os.path.exists(p):
1011 os.makedirs(p, 0775)
1012 p = os.path.join(policy_path, "USER")
1013 if not os.path.exists(p):
1014 os.makedirs(p, 0775)
1017 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1018 """Create the default GPO for a domain
1020 :param sysvolpath: Physical path for the sysvol folder
1021 :param dnsdomain: DNS domain name of the AD domain
1022 :param policyguid: GUID of the default domain policy
1023 :param policyguid_dc: GUID of the default domain controler policy
1026 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1027 create_gpo_struct(policy_path)
1029 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1030 create_gpo_struct(policy_path)
1033 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1034 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1035 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1036 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1038 """Setup a complete SAM Database.
1040 :note: This will wipe the main SAM database file!
1044 # Provision does not make much sense values larger than 1000000000
1045 # as the upper range of the rIDAvailablePool is 1073741823 and
1046 # we don't want to create a domain that cannot allocate rids.
1047 if next_rid < 1000 or next_rid > 1000000000:
1048 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1049 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1050 raise ProvisioningError(error)
1052 # ATTENTION: Do NOT change these default values without discussion with the
1053 # team and/or release manager. They have a big impact on the whole program!
1054 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1056 if dom_for_fun_level is None:
1057 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1059 if dom_for_fun_level > domainControllerFunctionality:
1060 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!")
1062 domainFunctionality = dom_for_fun_level
1063 forestFunctionality = dom_for_fun_level
1065 # Also wipes the database
1066 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1067 provision_backend=provision_backend, session_info=session_info,
1068 names=names, serverrole=serverrole, schema=schema)
1071 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1073 # Load the database, but don's load the global schema and don't connect quite yet
1074 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1075 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1078 logger.info("Pre-loading the Samba 4 and AD schema")
1080 # Load the schema from the one we computed earlier
1081 samdb.set_schema(schema)
1083 # Set the NTDS settings DN manually - in order to have it already around
1084 # before the provisioned tree exists and we connect
1085 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1087 # And now we can connect to the DB - the schema won't be loaded from the DB
1090 if fill == FILL_DRS:
1093 samdb.transaction_start()
1095 # Set the domain functionality levels onto the database.
1096 # Various module (the password_hash module in particular) need
1097 # to know what level of AD we are emulating.
1099 # These will be fixed into the database via the database
1100 # modifictions below, but we need them set from the start.
1101 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1102 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1103 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1105 samdb.set_domain_sid(str(domainsid))
1106 samdb.set_invocation_id(invocationid)
1108 logger.info("Adding DomainDN: %s" % names.domaindn)
1110 #impersonate domain admin
1111 admin_session_info = admin_session(lp, str(domainsid))
1112 samdb.set_session_info(admin_session_info)
1113 if domainguid is not None:
1114 domainguid_line = "objectGUID: %s\n-" % domainguid
1116 domainguid_line = ""
1118 descr = b64encode(get_domain_descriptor(domainsid))
1119 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1120 "DOMAINDN": names.domaindn,
1121 "DOMAINGUID": domainguid_line,
1126 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1127 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1128 "DOMAINSID": str(domainsid),
1129 "NEXTRID": str(next_rid),
1130 "SCHEMADN": names.schemadn,
1131 "NETBIOSNAME": names.netbiosname,
1132 "DEFAULTSITE": names.sitename,
1133 "CONFIGDN": names.configdn,
1134 "SERVERDN": names.serverdn,
1135 "POLICYGUID": policyguid,
1136 "DOMAINDN": names.domaindn,
1137 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1138 "SAMBA_VERSION_STRING": version
1141 logger.info("Adding configuration container")
1142 descr = b64encode(get_config_descriptor(domainsid))
1143 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1144 "CONFIGDN": names.configdn,
1145 "DESCRIPTOR": descr,
1148 # The LDIF here was created when the Schema object was constructed
1149 logger.info("Setting up sam.ldb schema")
1150 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1151 samdb.modify_ldif(schema.schema_dn_modify)
1152 samdb.write_prefixes_from_schema()
1153 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1154 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1155 {"SCHEMADN": names.schemadn})
1157 logger.info("Reopening sam.ldb with new schema")
1159 samdb.transaction_cancel()
1162 samdb.transaction_commit()
1164 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1165 credentials=provision_backend.credentials, lp=lp,
1166 global_schema=False, am_rodc=am_rodc)
1168 # Set the NTDS settings DN manually - in order to have it already around
1169 # before the provisioned tree exists and we connect
1170 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1174 samdb.transaction_start()
1176 samdb.invocation_id = invocationid
1178 logger.info("Setting up sam.ldb configuration data")
1179 descr = b64encode(get_sites_descriptor(domainsid))
1180 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1181 "CONFIGDN": names.configdn,
1182 "NETBIOSNAME": names.netbiosname,
1183 "DEFAULTSITE": names.sitename,
1184 "DNSDOMAIN": names.dnsdomain,
1185 "DOMAIN": names.domain,
1186 "SCHEMADN": names.schemadn,
1187 "DOMAINDN": names.domaindn,
1188 "SERVERDN": names.serverdn,
1189 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1190 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1191 "SITES_DESCRIPTOR": descr
1194 logger.info("Setting up display specifiers")
1195 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1196 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1197 check_all_substituted(display_specifiers_ldif)
1198 samdb.add_ldif(display_specifiers_ldif)
1200 logger.info("Adding users container")
1201 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1202 "DOMAINDN": names.domaindn})
1203 logger.info("Modifying users container")
1204 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1205 "DOMAINDN": names.domaindn})
1206 logger.info("Adding computers container")
1207 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1208 "DOMAINDN": names.domaindn})
1209 logger.info("Modifying computers container")
1210 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1211 "DOMAINDN": names.domaindn})
1212 logger.info("Setting up sam.ldb data")
1213 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1214 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1215 "DOMAINDN": names.domaindn,
1216 "NETBIOSNAME": names.netbiosname,
1217 "DEFAULTSITE": names.sitename,
1218 "CONFIGDN": names.configdn,
1219 "SERVERDN": names.serverdn,
1220 "RIDAVAILABLESTART": str(next_rid + 600),
1221 "POLICYGUID_DC": policyguid_dc
1224 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1225 "DOMAINDN": names.domaindn})
1227 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1228 "CONFIGDN": names.configdn,
1229 "SCHEMADN": names.schemadn})
1230 if fill == FILL_FULL:
1231 logger.info("Setting up sam.ldb users and groups")
1232 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1233 "DOMAINDN": names.domaindn,
1234 "DOMAINSID": str(domainsid),
1235 "CONFIGDN": names.configdn,
1236 "ADMINPASS_B64": b64encode(adminpass),
1237 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1240 logger.info("Setting up self join")
1241 setup_self_join(samdb, names=names, invocationid=invocationid,
1243 machinepass=machinepass,
1244 domainsid=domainsid,
1246 policyguid=policyguid,
1247 policyguid_dc=policyguid_dc,
1248 setup_path=setup_path,
1249 domainControllerFunctionality=domainControllerFunctionality,
1252 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1253 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1254 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1255 assert isinstance(names.ntdsguid, str)
1257 samdb.transaction_cancel()
1260 samdb.transaction_commit()
1265 FILL_NT4SYNC = "NT4SYNC"
1267 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1268 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)"
1270 def set_dir_acl(path, acl, lp, domsid):
1271 setntacl(lp, path, acl, domsid)
1272 for root, dirs, files in os.walk(path, topdown=False):
1274 setntacl(lp, os.path.join(root, name), acl, domsid)
1276 setntacl(lp, os.path.join(root, name), acl, domsid)
1279 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1280 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1283 :param sysvol: Physical path for the sysvol folder
1284 :param dnsdomain: The DNS name of the domain
1285 :param domainsid: The SID of the domain
1286 :param domaindn: The DN of the domain (ie. DC=...)
1287 :param samdb: An LDB object on the SAM db
1288 :param lp: an LP object
1291 # Set ACL for GPO root folder
1292 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1293 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1295 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1296 attrs=["cn", "nTSecurityDescriptor"],
1297 expression="", scope=ldb.SCOPE_ONELEVEL)
1300 acl = ndr_unpack(security.descriptor,
1301 str(policy["nTSecurityDescriptor"])).as_sddl()
1302 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1303 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1306 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1308 """Set the ACL for the sysvol share and the subfolders
1310 :param samdb: An LDB object on the SAM db
1311 :param netlogon: Physical path for the netlogon folder
1312 :param sysvol: Physical path for the sysvol folder
1313 :param gid: The GID of the "Domain adminstrators" group
1314 :param domainsid: The SID of the domain
1315 :param dnsdomain: The DNS name of the domain
1316 :param domaindn: The DN of the domain (ie. DC=...)
1320 os.chown(sysvol,-1,gid)
1326 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1327 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1328 for root, dirs, files in os.walk(sysvol, topdown=False):
1331 os.chown(os.path.join(root, name), -1, gid)
1332 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1335 os.chown(os.path.join(root, name), -1, gid)
1336 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1338 # Set acls on Policy folder and policies folders
1339 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1342 def provision(setup_dir, logger, session_info,
1343 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1345 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1347 domain=None, hostname=None, hostip=None, hostip6=None,
1348 domainsid=None, next_rid=1000,
1349 adminpass=None, ldapadminpass=None,
1350 krbtgtpass=None, domainguid=None,
1351 policyguid=None, policyguid_dc=None, invocationid=None,
1352 machinepass=None, ntdsguid=None,
1353 dnspass=None, root=None, nobody=None, users=None,
1354 wheel=None, backup=None, aci=None, serverrole=None,
1355 dom_for_fun_level=None,
1356 ldap_backend_extra_port=None, ldap_backend_forced_uri=None, backend_type=None,
1358 ol_mmr_urls=None, ol_olc=None,
1359 setup_ds_path=None, slapd_path=None, nosync=False,
1360 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1363 :note: caution, this wipes all existing data!
1366 def setup_path(file):
1367 return os.path.join(setup_dir, file)
1369 if domainsid is None:
1370 domainsid = security.random_sid()
1372 domainsid = security.dom_sid(domainsid)
1374 # create/adapt the group policy GUIDs
1375 # Default GUID for default policy are described at
1376 # "How Core Group Policy Works"
1377 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1378 if policyguid is None:
1379 policyguid = DEFAULT_POLICY_GUID
1380 policyguid = policyguid.upper()
1381 if policyguid_dc is None:
1382 policyguid_dc = DEFAULT_DC_POLICY_GUID
1383 policyguid_dc = policyguid_dc.upper()
1385 if adminpass is None:
1386 adminpass = samba.generate_random_password(12, 32)
1387 if krbtgtpass is None:
1388 krbtgtpass = samba.generate_random_password(128, 255)
1389 if machinepass is None:
1390 machinepass = samba.generate_random_password(128, 255)
1392 dnspass = samba.generate_random_password(128, 255)
1393 if ldapadminpass is None:
1394 #Make a new, random password between Samba and it's LDAP server
1395 ldapadminpass=samba.generate_random_password(128, 255)
1397 if backend_type is None:
1398 backend_type = "ldb"
1400 sid_generator = "internal"
1401 if backend_type == "fedora-ds":
1402 sid_generator = "backend"
1404 root_uid = findnss_uid([root or "root"])
1405 nobody_uid = findnss_uid([nobody or "nobody"])
1406 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1408 wheel_gid = findnss_gid(["wheel", "adm"])
1410 wheel_gid = findnss_gid([wheel])
1412 bind_gid = findnss_gid(["bind", "named"])
1416 if targetdir is not None:
1417 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1418 elif smbconf is None:
1419 smbconf = samba.param.default_path()
1420 if not os.path.exists(os.path.dirname(smbconf)):
1421 os.makedirs(os.path.dirname(smbconf))
1423 # only install a new smb.conf if there isn't one there already
1424 if os.path.exists(smbconf):
1425 # if Samba Team members can't figure out the weird errors
1426 # loading an empty smb.conf gives, then we need to be smarter.
1427 # Pretend it just didn't exist --abartlet
1428 data = open(smbconf, 'r').read()
1429 data = data.lstrip()
1430 if data is None or data == "":
1431 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1432 serverrole, targetdir, sid_generator, useeadb)
1434 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1435 targetdir, sid_generator, useeadb)
1437 lp = samba.param.LoadParm()
1440 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1441 dnsdomain=realm, serverrole=serverrole,
1442 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1443 serverdn=serverdn, sitename=sitename)
1445 paths = provision_paths_from_lp(lp, names.dnsdomain)
1447 paths.bind_gid = bind_gid
1450 hostips = samba.interface_ips(lp, False)
1451 if len(hostips) == 0:
1452 logger.warning("No external IPv4 address has been found. Using loopback.")
1453 hostip = '127.0.0.1'
1456 if len(hostips) > 1:
1457 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1461 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1464 if hostip6 == '::1' and ip[-1][0] != '::1':
1466 except socket.gaierror, (socket.EAI_NODATA, msg):
1469 if serverrole is None:
1470 serverrole = lp.get("server role")
1472 assert serverrole in ("domain controller", "member server", "standalone")
1473 if invocationid is None:
1474 invocationid = str(uuid.uuid4())
1476 if not os.path.exists(paths.private_dir):
1477 os.mkdir(paths.private_dir)
1478 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1479 os.mkdir(os.path.join(paths.private_dir, "tls"))
1481 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1483 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn)
1485 if backend_type == "ldb":
1486 provision_backend = LDBBackend(backend_type,
1487 paths=paths, setup_path=setup_path,
1488 lp=lp, credentials=credentials,
1491 elif backend_type == "existing":
1492 provision_backend = ExistingBackend(backend_type,
1493 paths=paths, setup_path=setup_path,
1494 lp=lp, credentials=credentials,
1497 ldap_backend_forced_uri=ldap_backend_forced_uri)
1498 elif backend_type == "fedora-ds":
1499 provision_backend = FDSBackend(backend_type,
1500 paths=paths, setup_path=setup_path,
1501 lp=lp, credentials=credentials,
1504 domainsid=domainsid,
1507 ldapadminpass=ldapadminpass,
1508 slapd_path=slapd_path,
1509 ldap_backend_extra_port=ldap_backend_extra_port,
1510 ldap_dryrun_mode=ldap_dryrun_mode,
1512 setup_ds_path=setup_ds_path,
1513 ldap_backend_forced_uri=ldap_backend_forced_uri)
1514 elif backend_type == "openldap":
1515 provision_backend = OpenLDAPBackend(backend_type,
1516 paths=paths, setup_path=setup_path,
1517 lp=lp, credentials=credentials,
1520 domainsid=domainsid,
1523 ldapadminpass=ldapadminpass,
1524 slapd_path=slapd_path,
1525 ldap_backend_extra_port=ldap_backend_extra_port,
1526 ldap_dryrun_mode=ldap_dryrun_mode,
1527 ol_mmr_urls=ol_mmr_urls,
1529 ldap_backend_forced_uri=ldap_backend_forced_uri)
1531 raise ValueError("Unknown LDAP backend type selected")
1533 provision_backend.init()
1534 provision_backend.start()
1536 # only install a new shares config db if there is none
1537 if not os.path.exists(paths.shareconf):
1538 logger.info("Setting up share.ldb")
1539 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1541 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1543 logger.info("Setting up secrets.ldb")
1544 secrets_ldb = setup_secretsdb(paths, setup_path,
1545 session_info=session_info,
1546 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1549 logger.info("Setting up the registry")
1550 setup_registry(paths.hklm, setup_path, session_info,
1553 logger.info("Setting up the privileges database")
1554 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1556 logger.info("Setting up idmap db")
1557 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1560 logger.info("Setting up SAM db")
1561 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1562 provision_backend, lp, names,
1564 domainsid=domainsid,
1565 schema=schema, domainguid=domainguid,
1566 policyguid=policyguid, policyguid_dc=policyguid_dc,
1568 adminpass=adminpass, krbtgtpass=krbtgtpass,
1569 invocationid=invocationid,
1570 machinepass=machinepass, dnspass=dnspass,
1571 ntdsguid=ntdsguid, serverrole=serverrole,
1572 dom_for_fun_level=dom_for_fun_level,
1573 am_rodc=am_rodc, next_rid=next_rid)
1575 if serverrole == "domain controller":
1576 if paths.netlogon is None:
1577 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1578 logger.info("Please either remove %s or see the template at %s" %
1579 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1580 assert paths.netlogon is not None
1582 if paths.sysvol is None:
1583 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1584 " are configuring a DC.")
1585 logger.info("Please either remove %s or see the template at %s" %
1586 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1587 assert paths.sysvol is not None
1589 if not os.path.isdir(paths.netlogon):
1590 os.makedirs(paths.netlogon, 0755)
1592 if samdb_fill == FILL_FULL:
1593 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1594 root_uid=root_uid, nobody_uid=nobody_uid,
1595 users_gid=users_gid, wheel_gid=wheel_gid)
1597 if serverrole == "domain controller":
1598 # Set up group policies (domain policy and domain controller policy)
1599 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1600 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1601 domainsid, names.dnsdomain, names.domaindn, lp)
1603 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1604 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1606 secretsdb_self_join(secrets_ldb, domain=names.domain,
1608 dnsdomain=names.dnsdomain,
1609 netbiosname=names.netbiosname,
1610 domainsid=domainsid,
1611 machinepass=machinepass,
1612 secure_channel_type=SEC_CHAN_BDC)
1614 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1615 # In future, this might be determined from some configuration
1616 kerberos_enctypes = str(ENC_ALL_TYPES)
1619 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1620 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1621 flags=ldb.FLAG_MOD_REPLACE,
1622 name="msDS-SupportedEncryptionTypes")
1624 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1625 # It might be that this attribute does not exist in this schema
1629 if serverrole == "domain controller":
1630 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1632 realm=names.realm, dnsdomain=names.dnsdomain,
1633 dns_keytab_path=paths.dns_keytab,
1636 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1637 assert isinstance(domainguid, str)
1639 # Only make a zone file on the first DC, it should be replicated
1640 # with DNS replication
1641 create_zone_file(lp, logger, paths, targetdir, setup_path,
1642 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1643 hostname=names.hostname, realm=names.realm,
1644 domainguid=domainguid, ntdsguid=names.ntdsguid)
1646 create_named_conf(paths, setup_path, realm=names.realm,
1647 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1649 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1650 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1651 keytab_name=paths.dns_keytab)
1652 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1653 logger.info("and %s for further documentation required for secure DNS "
1654 "updates", paths.namedtxt)
1656 lastProvisionUSNs = get_last_provision_usn(samdb)
1657 maxUSN = get_max_usn(samdb, str(names.rootdn))
1658 if lastProvisionUSNs is not None:
1659 update_provision_usn(samdb, 0, maxUSN, 1)
1661 set_provision_usn(samdb, 0, maxUSN)
1663 create_krb5_conf(paths.krb5conf, setup_path,
1664 dnsdomain=names.dnsdomain, hostname=names.hostname,
1666 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1667 "generated at %s", paths.krb5conf)
1669 if serverrole == "domain controller":
1670 create_dns_update_list(lp, logger, paths, setup_path)
1672 provision_backend.post_setup()
1673 provision_backend.shutdown()
1675 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1678 secrets_ldb.transaction_cancel()
1681 #Now commit the secrets.ldb to disk
1682 secrets_ldb.transaction_commit()
1684 # the commit creates the dns.keytab, now chown it
1685 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1686 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1688 os.chmod(dns_keytab_path, 0640)
1689 os.chown(dns_keytab_path, -1, paths.bind_gid)
1691 if not os.environ.has_key('SAMBA_SELFTEST'):
1692 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1696 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1697 paths.phpldapadminconfig)
1699 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1700 logger.info("Server Role: %s" % serverrole)
1701 logger.info("Hostname: %s" % names.hostname)
1702 logger.info("NetBIOS Domain: %s" % names.domain)
1703 logger.info("DNS Domain: %s" % names.dnsdomain)
1704 logger.info("DOMAIN SID: %s" % str(domainsid))
1705 if samdb_fill == FILL_FULL:
1706 logger.info("Admin password: %s" % adminpass)
1707 if provision_backend.type is not "ldb":
1708 if provision_backend.credentials.get_bind_dn() is not None:
1709 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1711 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1713 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1715 if provision_backend.slapd_command_escaped is not None:
1716 # now display slapd_command_file.txt to show how slapd must be started next time
1717 logger.info("Use later the following commandline to start slapd, then Samba:")
1718 logger.info(provision_backend.slapd_command_escaped)
1719 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1720 provision_backend.ldapdir)
1722 result = ProvisionResult()
1723 result.domaindn = domaindn
1724 result.paths = paths
1726 result.samdb = samdb
1730 def provision_become_dc(setup_dir=None,
1731 smbconf=None, targetdir=None, realm=None,
1732 rootdn=None, domaindn=None, schemadn=None,
1733 configdn=None, serverdn=None,
1734 domain=None, hostname=None, domainsid=None,
1735 adminpass=None, krbtgtpass=None, domainguid=None,
1736 policyguid=None, policyguid_dc=None, invocationid=None,
1738 dnspass=None, root=None, nobody=None, users=None,
1739 wheel=None, backup=None, serverrole=None,
1740 ldap_backend=None, ldap_backend_type=None,
1741 sitename=None, debuglevel=1):
1743 logger = logging.getLogger("provision")
1744 samba.set_debug_level(debuglevel)
1746 res = provision(setup_dir, logger, system_session(), None,
1747 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1748 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1749 configdn=configdn, serverdn=serverdn, domain=domain,
1750 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1751 machinepass=machinepass, serverrole="domain controller",
1753 res.lp.set("debuglevel", str(debuglevel))
1757 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1758 """Create a PHP LDAP admin configuration file.
1760 :param path: Path to write the configuration to.
1761 :param setup_path: Function to generate setup paths.
1763 setup_file(setup_path("phpldapadmin-config.php"), path,
1764 {"S4_LDAPI_URI": ldapi_uri})
1767 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1768 hostip, hostip6, hostname, realm, domainguid,
1770 """Write out a DNS zone file, from the info in the current database.
1772 :param paths: paths object
1773 :param setup_path: Setup path function.
1774 :param dnsdomain: DNS Domain name
1775 :param domaindn: DN of the Domain
1776 :param hostip: Local IPv4 IP
1777 :param hostip6: Local IPv6 IP
1778 :param hostname: Local hostname
1779 :param realm: Realm name
1780 :param domainguid: GUID of the domain.
1781 :param ntdsguid: GUID of the hosts nTDSDSA record.
1783 assert isinstance(domainguid, str)
1785 if hostip6 is not None:
1786 hostip6_base_line = " IN AAAA " + hostip6
1787 hostip6_host_line = hostname + " IN AAAA " + hostip6
1788 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1790 hostip6_base_line = ""
1791 hostip6_host_line = ""
1792 gc_msdcs_ip6_line = ""
1794 if hostip is not None:
1795 hostip_base_line = " IN A " + hostip
1796 hostip_host_line = hostname + " IN A " + hostip
1797 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1799 hostip_base_line = ""
1800 hostip_host_line = ""
1801 gc_msdcs_ip_line = ""
1803 dns_dir = os.path.dirname(paths.dns)
1806 shutil.rmtree(dns_dir, True)
1810 os.mkdir(dns_dir, 0775)
1812 # we need to freeze the zone while we update the contents
1813 if targetdir is None:
1814 rndc = ' '.join(lp.get("rndc command"))
1815 os.system(rndc + " freeze " + lp.get("realm"))
1817 setup_file(setup_path("provision.zone"), paths.dns, {
1818 "HOSTNAME": hostname,
1819 "DNSDOMAIN": dnsdomain,
1821 "HOSTIP_BASE_LINE": hostip_base_line,
1822 "HOSTIP_HOST_LINE": hostip_host_line,
1823 "DOMAINGUID": domainguid,
1824 "DATESTRING": time.strftime("%Y%m%d%H"),
1825 "DEFAULTSITE": DEFAULTSITE,
1826 "NTDSGUID": ntdsguid,
1827 "HOSTIP6_BASE_LINE": hostip6_base_line,
1828 "HOSTIP6_HOST_LINE": hostip6_host_line,
1829 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1830 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1833 # note that we use no variable substitution on this file
1834 # the substitution is done at runtime by samba_dnsupdate
1835 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1837 # and the SPN update list
1838 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1840 if paths.bind_gid is not None:
1842 os.chown(dns_dir, -1, paths.bind_gid)
1843 os.chown(paths.dns, -1, paths.bind_gid)
1844 # chmod needed to cope with umask
1845 os.chmod(dns_dir, 0775)
1846 os.chmod(paths.dns, 0664)
1848 if not os.environ.has_key('SAMBA_SELFTEST'):
1849 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1851 if targetdir is None:
1852 os.system(rndc + " unfreeze " + lp.get("realm"))
1855 def create_dns_update_list(lp, logger, paths, setup_path):
1856 """Write out a dns_update_list file"""
1857 # note that we use no variable substitution on this file
1858 # the substitution is done at runtime by samba_dnsupdate
1859 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1860 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1863 def create_named_conf(paths, setup_path, realm, dnsdomain,
1865 """Write out a file containing zone statements suitable for inclusion in a
1866 named.conf file (including GSS-TSIG configuration).
1868 :param paths: all paths
1869 :param setup_path: Setup path function.
1870 :param realm: Realm name
1871 :param dnsdomain: DNS Domain name
1872 :param private_dir: Path to private directory
1873 :param keytab_name: File name of DNS keytab file
1876 setup_file(setup_path("named.conf"), paths.namedconf, {
1877 "DNSDOMAIN": dnsdomain,
1879 "ZONE_FILE": paths.dns,
1880 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1881 "NAMED_CONF": paths.namedconf,
1882 "NAMED_CONF_UPDATE": paths.namedconf_update
1885 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1888 def create_named_txt(path, setup_path, realm, dnsdomain,
1889 private_dir, keytab_name):
1890 """Write out a file containing zone statements suitable for inclusion in a
1891 named.conf file (including GSS-TSIG configuration).
1893 :param path: Path of the new named.conf file.
1894 :param setup_path: Setup path function.
1895 :param realm: Realm name
1896 :param dnsdomain: DNS Domain name
1897 :param private_dir: Path to private directory
1898 :param keytab_name: File name of DNS keytab file
1901 setup_file(setup_path("named.txt"), path, {
1902 "DNSDOMAIN": dnsdomain,
1904 "DNS_KEYTAB": keytab_name,
1905 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1906 "PRIVATE_DIR": private_dir
1910 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1911 """Write out a file containing zone statements suitable for inclusion in a
1912 named.conf file (including GSS-TSIG configuration).
1914 :param path: Path of the new named.conf file.
1915 :param setup_path: Setup path function.
1916 :param dnsdomain: DNS Domain name
1917 :param hostname: Local hostname
1918 :param realm: Realm name
1920 setup_file(setup_path("krb5.conf"), path, {
1921 "DNSDOMAIN": dnsdomain,
1922 "HOSTNAME": hostname,
1927 class ProvisioningError(Exception):
1928 """A generic provision error."""
1930 def __init__(self, value):
1934 return "ProvisioningError: " + self.value
1937 class InvalidNetbiosName(Exception):
1938 """A specified name was not a valid NetBIOS name."""
1939 def __init__(self, name):
1940 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)