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 __docformat__ = "restructuredText"
30 from base64 import b64encode
44 from samba.auth import system_session, admin_session
48 check_all_substituted,
57 from samba.dcerpc import security
58 from samba.dcerpc.misc import (
62 from samba.dsdb import (
63 DS_DOMAIN_FUNCTION_2003,
64 DS_DOMAIN_FUNCTION_2008_R2,
67 from samba.idmap import IDmapDB
68 from samba.ms_display_specifiers import read_ms_ldif
69 from samba.ntacls import setntacl, dsacl2fsacl
70 from samba.ndr import ndr_pack, ndr_unpack
71 from samba.provision.backend import (
79 from samba.schema import Schema
80 from samba.samdb import SamDB
82 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
83 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
84 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
85 DEFAULTSITE = "Default-First-Site-Name"
86 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
90 """Find the setup directory used by provision."""
93 return os.path.join(source_tree_topdir(), "source4/setup")
96 for prefix in [sys.prefix,
97 os.path.join(os.path.dirname(__file__), "../../../../..")]:
98 for suffix in ["share/setup", "share/samba/setup", "setup"]:
99 ret = os.path.normpath(os.path.join(prefix, suffix))
100 if os.path.isdir(ret):
102 raise Exception("Unable to find setup directory.")
104 # Descriptors of naming contexts and other important objects
106 # "get_schema_descriptor" is located in "schema.py"
108 def get_sites_descriptor(domain_sid):
109 sddl = "D:(A;;RPLCLORC;;;AU)" \
110 "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
111 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
112 "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
113 "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
114 "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
115 "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
116 "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
117 sec = security.descriptor.from_sddl(sddl, domain_sid)
121 def get_config_descriptor(domain_sid):
122 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
123 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
124 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
125 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
126 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
127 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
128 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
129 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
130 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
132 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
133 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
134 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
135 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
136 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
137 sec = security.descriptor.from_sddl(sddl, domain_sid)
141 def get_domain_descriptor(domain_sid):
142 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
143 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
144 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
145 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
146 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
148 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
149 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
150 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
151 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
152 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
153 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
154 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
155 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
156 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
157 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
158 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
159 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
160 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
161 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
162 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
163 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
164 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
165 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
166 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
167 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
168 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
169 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
170 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
171 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
172 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
173 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
174 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
175 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
176 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
177 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
178 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
179 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
180 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
183 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
185 "(A;;RPLCLORC;;;ED)" \
186 "(A;;RPLCLORC;;;AU)" \
187 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
188 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
189 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
190 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
191 sec = security.descriptor.from_sddl(sddl, domain_sid)
195 class ProvisionPaths(object):
198 self.shareconf = None
209 self.dns_keytab = None
212 self.private_dir = None
215 class ProvisionNames(object):
222 self.ldapmanagerdn = None
223 self.dnsdomain = None
225 self.netbiosname = None
232 def update_provision_usn(samdb, low, high, replace=False):
233 """Update the field provisionUSN in sam.ldb
235 This field is used to track range of USN modified by provision and
237 This value is used afterward by next provision to figure out if
238 the field have been modified since last provision.
240 :param samdb: An LDB object connect to sam.ldb
241 :param low: The lowest USN modified by this upgrade
242 :param high: The highest USN modified by this upgrade
243 :param replace: A boolean indicating if the range should replace any
244 existing one or appended (default)
249 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
250 LAST_PROVISION_USN_ATTRIBUTE, base="",
251 scope=ldb.SCOPE_SUBTREE,
252 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
253 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
256 tab.append("%s-%s" % (low, high))
257 delta = ldb.Message()
258 delta.dn = ldb.Dn(samdb, "@PROVISION")
259 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
260 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
264 def set_provision_usn(samdb, low, high):
265 """Set the field provisionUSN in sam.ldb
266 This field is used to track range of USN modified by provision and
268 This value is used afterward by next provision to figure out if
269 the field have been modified since last provision.
271 :param samdb: An LDB object connect to sam.ldb
272 :param low: The lowest USN modified by this upgrade
273 :param high: The highest USN modified by this upgrade"""
275 tab.append("%s-%s" % (low, high))
276 delta = ldb.Message()
277 delta.dn = ldb.Dn(samdb, "@PROVISION")
278 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
279 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
283 def get_max_usn(samdb,basedn):
284 """ This function return the biggest USN present in the provision
286 :param samdb: A LDB object pointing to the sam.ldb
287 :param basedn: A string containing the base DN of the provision
289 :return: The biggest USN in the provision"""
291 res = samdb.search(expression="objectClass=*",base=basedn,
292 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
293 controls=["search_options:1:2",
294 "server_sort:1:1:uSNChanged",
295 "paged_results:1:1"])
296 return res[0]["uSNChanged"]
299 def get_last_provision_usn(sam):
300 """Get the lastest USN modified by a provision or an upgradeprovision
302 :param sam: An LDB object pointing to the sam.ldb
303 :return: an integer corresponding to the highest USN modified by
304 (upgrade)provision, 0 is this value is unknown
306 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
307 LAST_PROVISION_USN_ATTRIBUTE,
308 base="", scope=ldb.SCOPE_SUBTREE,
309 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
314 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
315 tab = p.split(str(r))
324 class ProvisionResult(object):
333 def check_install(lp, session_info, credentials):
334 """Check whether the current install seems ok.
336 :param lp: Loadparm context
337 :param session_info: Session information
338 :param credentials: Credentials
340 if lp.get("realm") == "":
341 raise Exception("Realm empty")
342 samdb = Ldb(lp.get("sam database"), session_info=session_info,
343 credentials=credentials, lp=lp)
344 if len(samdb.search("(cn=Administrator)")) != 1:
345 raise ProvisioningError("No administrator account found")
348 def findnss(nssfn, names):
349 """Find a user or group from a list of possibilities.
351 :param nssfn: NSS Function to try (should raise KeyError if not found)
352 :param names: Names to check.
353 :return: Value return by first names list.
360 raise KeyError("Unable to find user/group in %r" % names)
363 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
364 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
367 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
368 """Setup a ldb in the private dir.
370 :param ldb: LDB file to import data into
371 :param ldif_path: Path of the LDIF file to load
372 :param subst_vars: Optional variables to subsitute in LDIF.
373 :param nocontrols: Optional list of controls, can be None for no controls
375 assert isinstance(ldif_path, str)
376 data = read_and_sub_file(ldif_path, subst_vars)
377 ldb.add_ldif(data, controls)
380 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
381 """Modify a ldb in the private dir.
383 :param ldb: LDB object.
384 :param ldif_path: LDIF file path.
385 :param subst_vars: Optional dictionary with substitution variables.
387 data = read_and_sub_file(ldif_path, subst_vars)
388 ldb.modify_ldif(data, controls)
391 def setup_ldb(ldb, ldif_path, subst_vars):
392 """Import a LDIF a file into a LDB handle, optionally substituting
395 :note: Either all LDIF data will be added or none (using transactions).
397 :param ldb: LDB file to import into.
398 :param ldif_path: Path to the LDIF file.
399 :param subst_vars: Dictionary with substitution variables.
401 assert ldb is not None
402 ldb.transaction_start()
404 setup_add_ldif(ldb, ldif_path, subst_vars)
406 ldb.transaction_cancel()
409 ldb.transaction_commit()
412 def provision_paths_from_lp(lp, dnsdomain):
413 """Set the default paths for provisioning.
415 :param lp: Loadparm context.
416 :param dnsdomain: DNS Domain name
418 paths = ProvisionPaths()
419 paths.private_dir = lp.get("private dir")
421 # This is stored without path prefix for the "privateKeytab" attribute in
422 # "secrets_dns.ldif".
423 paths.dns_keytab = "dns.keytab"
424 paths.keytab = "secrets.keytab"
426 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
427 paths.samdb = os.path.join(paths.private_dir,
428 lp.get("sam database") or "samdb.ldb")
429 paths.idmapdb = os.path.join(paths.private_dir,
430 lp.get("idmap database") or "idmap.ldb")
431 paths.secrets = os.path.join(paths.private_dir,
432 lp.get("secrets database") or "secrets.ldb")
433 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
434 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
435 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
436 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
437 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
438 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
439 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
440 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
441 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
442 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
443 paths.phpldapadminconfig = os.path.join(paths.private_dir,
444 "phpldapadmin-config.php")
445 paths.hklm = "hklm.ldb"
446 paths.hkcr = "hkcr.ldb"
447 paths.hkcu = "hkcu.ldb"
448 paths.hku = "hku.ldb"
449 paths.hkpd = "hkpd.ldb"
450 paths.hkpt = "hkpt.ldb"
451 paths.sysvol = lp.get("path", "sysvol")
452 paths.netlogon = lp.get("path", "netlogon")
453 paths.smbconf = lp.configfile
457 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
458 serverrole=None, rootdn=None, domaindn=None, configdn=None,
459 schemadn=None, serverdn=None, sitename=None):
460 """Guess configuration settings to use."""
463 hostname = socket.gethostname().split(".")[0]
465 netbiosname = lp.get("netbios name")
466 if netbiosname is None:
467 netbiosname = hostname
468 # remove forbidden chars
470 for x in netbiosname:
471 if x.isalnum() or x in VALID_NETBIOS_CHARS:
472 newnbname = "%s%c" % (newnbname, x)
473 # force the length to be <16
474 netbiosname = newnbname[0:15]
475 assert netbiosname is not None
476 netbiosname = netbiosname.upper()
477 if not valid_netbios_name(netbiosname):
478 raise InvalidNetbiosName(netbiosname)
480 if dnsdomain is None:
481 dnsdomain = lp.get("realm")
482 if dnsdomain is None or dnsdomain == "":
483 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
485 dnsdomain = dnsdomain.lower()
487 if serverrole is None:
488 serverrole = lp.get("server role")
489 if serverrole is None:
490 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
492 serverrole = serverrole.lower()
494 realm = dnsdomain.upper()
496 if lp.get("realm") == "":
497 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
499 if lp.get("realm").upper() != realm:
500 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))
502 if lp.get("server role").lower() != serverrole:
503 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))
505 if serverrole == "domain controller":
507 # This will, for better or worse, default to 'WORKGROUP'
508 domain = lp.get("workgroup")
509 domain = domain.upper()
511 if lp.get("workgroup").upper() != domain:
512 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))
515 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
517 if domain == netbiosname:
518 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
522 domaindn = "DC=" + netbiosname
524 if not valid_netbios_name(domain):
525 raise InvalidNetbiosName(domain)
527 if hostname.upper() == realm:
528 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
529 if netbiosname.upper() == realm:
530 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
532 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
538 configdn = "CN=Configuration," + rootdn
540 schemadn = "CN=Schema," + configdn
545 names = ProvisionNames()
546 names.rootdn = rootdn
547 names.domaindn = domaindn
548 names.configdn = configdn
549 names.schemadn = schemadn
550 names.ldapmanagerdn = "CN=Manager," + rootdn
551 names.dnsdomain = dnsdomain
552 names.domain = domain
554 names.netbiosname = netbiosname
555 names.hostname = hostname
556 names.sitename = sitename
557 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
558 netbiosname, sitename, configdn)
563 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
564 targetdir, sid_generator="internal", eadb=False, lp=None):
565 """Create a new smb.conf file based on a couple of basic settings.
567 assert smbconf is not None
569 hostname = socket.gethostname().split(".")[0]
570 netbiosname = hostname.upper()
571 # remove forbidden chars
573 for x in netbiosname:
574 if x.isalnum() or x in VALID_NETBIOS_CHARS:
575 newnbname = "%s%c" % (newnbname, x)
576 #force the length to be <16
577 netbiosname = newnbname[0:15]
579 netbiosname = hostname.upper()
581 if serverrole is None:
582 serverrole = "standalone"
584 assert serverrole in ("domain controller", "member server", "standalone")
585 if serverrole == "domain controller":
587 elif serverrole == "member server":
588 smbconfsuffix = "member"
589 elif serverrole == "standalone":
590 smbconfsuffix = "standalone"
592 if sid_generator is None:
593 sid_generator = "internal"
595 assert domain is not None
596 domain = domain.upper()
598 assert realm is not None
599 realm = realm.upper()
602 lp = samba.param.LoadParm()
603 #Load non-existant file
604 if os.path.exists(smbconf):
606 if eadb and not lp.get("posix:eadb"):
607 if targetdir is not None:
608 privdir = os.path.join(targetdir, "private")
610 privdir = lp.get("private dir")
611 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
613 if targetdir is not None:
614 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
615 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
617 lp.set("lock dir", os.path.abspath(targetdir))
622 if sid_generator == "internal":
623 sid_generator_line = ""
625 sid_generator_line = "sid generator = " + sid_generator
627 used_setup_dir = setup_path("")
628 default_setup_dir = lp.get("setup directory")
630 if used_setup_dir != default_setup_dir:
631 setupdir_line = "setup directory = %s" % used_setup_dir
632 lp.set("setup directory", used_setup_dir)
634 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
635 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
637 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
639 "NETBIOS_NAME": netbiosname,
642 "SERVERROLE": serverrole,
643 "NETLOGONPATH": netlogon,
644 "SYSVOLPATH": sysvol,
645 "SETUPDIRECTORY_LINE": setupdir_line,
646 "SIDGENERATOR_LINE": sid_generator_line,
647 "PRIVATEDIR_LINE": privatedir_line,
648 "LOCKDIR_LINE": lockdir_line
651 # reload the smb.conf
654 # and dump it without any values that are the default
655 # this ensures that any smb.conf parameters that were set
656 # on the provision/join command line are set in the resulting smb.conf
657 f = open(smbconf, mode='w')
663 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
664 users_gid, wheel_gid):
665 """setup reasonable name mappings for sam names to unix names.
667 :param samdb: SamDB object.
668 :param idmap: IDmap db object.
669 :param sid: The domain sid.
670 :param domaindn: The domain DN.
671 :param root_uid: uid of the UNIX root user.
672 :param nobody_uid: uid of the UNIX nobody user.
673 :param users_gid: gid of the UNIX users group.
674 :param wheel_gid: gid of the UNIX wheel group.
676 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
677 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
679 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
680 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
683 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
684 provision_backend, names, schema, serverrole,
686 """Setup the partitions for the SAM database.
688 Alternatively, provision() may call this, and then populate the database.
690 :note: This will wipe the Sam Database!
692 :note: This function always removes the local SAM LDB file. The erase
693 parameter controls whether to erase the existing data, which
694 may not be stored locally but in LDAP.
697 assert session_info is not None
699 # We use options=["modules:"] to stop the modules loading - we
700 # just want to wipe and re-initialise the database, not start it up
703 os.unlink(samdb_path)
707 samdb = Ldb(url=samdb_path, session_info=session_info,
708 lp=lp, options=["modules:"])
710 ldap_backend_line = "# No LDAP backend"
711 if provision_backend.type is not "ldb":
712 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
714 samdb.transaction_start()
716 logger.info("Setting up sam.ldb partitions and settings")
717 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
718 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
719 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
720 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
721 "LDAP_BACKEND_LINE": ldap_backend_line,
725 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
726 "BACKEND_TYPE": provision_backend.type,
727 "SERVER_ROLE": serverrole
730 logger.info("Setting up sam.ldb rootDSE")
731 setup_samdb_rootdse(samdb, setup_path, names)
733 samdb.transaction_cancel()
736 samdb.transaction_commit()
739 def secretsdb_self_join(secretsdb, domain,
740 netbiosname, machinepass, domainsid=None,
741 realm=None, dnsdomain=None,
743 key_version_number=1,
744 secure_channel_type=SEC_CHAN_WKSTA):
745 """Add domain join-specific bits to a secrets database.
747 :param secretsdb: Ldb Handle to the secrets database
748 :param machinepass: Machine password
750 attrs = ["whenChanged",
757 if realm is not None:
758 if dnsdomain is None:
759 dnsdomain = realm.lower()
760 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
763 shortname = netbiosname.lower()
765 # We don't need to set msg["flatname"] here, because rdn_name will handle
766 # it, and it causes problems for modifies anyway
767 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
768 msg["secureChannelType"] = [str(secure_channel_type)]
769 msg["objectClass"] = ["top", "primaryDomain"]
770 if dnsname is not None:
771 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
772 msg["realm"] = [realm]
773 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
774 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
775 msg["privateKeytab"] = ["secrets.keytab"]
777 msg["secret"] = [machinepass]
778 msg["samAccountName"] = ["%s$" % netbiosname]
779 msg["secureChannelType"] = [str(secure_channel_type)]
780 if domainsid is not None:
781 msg["objectSid"] = [ndr_pack(domainsid)]
783 # This complex expression tries to ensure that we don't have more
784 # than one record for this SID, realm or netbios domain at a time,
785 # but we don't delete the old record that we are about to modify,
786 # because that would delete the keytab and previous password.
787 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
788 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
789 scope=ldb.SCOPE_ONELEVEL)
792 secretsdb.delete(del_msg.dn)
794 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
797 msg["priorSecret"] = [res[0]["secret"][0]]
798 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
801 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
806 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
812 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
813 secretsdb.modify(msg)
814 secretsdb.rename(res[0].dn, msg.dn)
816 spn = [ 'HOST/%s' % shortname ]
817 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
818 # we are a domain controller then we add servicePrincipalName
819 # entries for the keytab code to update.
820 spn.extend([ 'HOST/%s' % dnsname ])
821 msg["servicePrincipalName"] = spn
826 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir, realm,
827 dnsdomain, dns_keytab_path, dnspass):
828 """Add DNS specific bits to a secrets database.
830 :param secretsdb: Ldb Handle to the secrets database
831 :param setup_path: Setup path function
832 :param machinepass: Machine password
835 os.unlink(os.path.join(private_dir, dns_keytab_path))
839 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
841 "DNSDOMAIN": dnsdomain,
842 "DNS_KEYTAB": dns_keytab_path,
843 "DNSPASS_B64": b64encode(dnspass),
844 "HOSTNAME": names.hostname,
845 "DNSNAME" : '%s.%s' % (
846 names.netbiosname.lower(), names.dnsdomain.lower())
850 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
851 """Setup the secrets database.
853 :note: This function does not handle exceptions and transaction on purpose,
854 it's up to the caller to do this job.
856 :param path: Path to the secrets database.
857 :param setup_path: Get the path to a setup file.
858 :param session_info: Session info.
859 :param credentials: Credentials
860 :param lp: Loadparm context
861 :return: LDB handle for the created secrets database
863 if os.path.exists(paths.secrets):
864 os.unlink(paths.secrets)
866 keytab_path = os.path.join(paths.private_dir, paths.keytab)
867 if os.path.exists(keytab_path):
868 os.unlink(keytab_path)
870 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
871 if os.path.exists(dns_keytab_path):
872 os.unlink(dns_keytab_path)
876 secrets_ldb = Ldb(path, session_info=session_info,
879 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
880 secrets_ldb = Ldb(path, session_info=session_info,
882 secrets_ldb.transaction_start()
884 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
886 if (backend_credentials is not None and
887 backend_credentials.authentication_requested()):
888 if backend_credentials.get_bind_dn() is not None:
889 setup_add_ldif(secrets_ldb,
890 setup_path("secrets_simple_ldap.ldif"), {
891 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
892 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
895 setup_add_ldif(secrets_ldb,
896 setup_path("secrets_sasl_ldap.ldif"), {
897 "LDAPADMINUSER": backend_credentials.get_username(),
898 "LDAPADMINREALM": backend_credentials.get_realm(),
899 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
904 secrets_ldb.transaction_cancel()
908 def setup_privileges(path, setup_path, session_info, lp):
909 """Setup the privileges database.
911 :param path: Path to the privileges database.
912 :param setup_path: Get the path to a setup file.
913 :param session_info: Session info.
914 :param credentials: Credentials
915 :param lp: Loadparm context
916 :return: LDB handle for the created secrets database
918 if os.path.exists(path):
920 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
921 privilege_ldb.erase()
922 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
925 def setup_registry(path, setup_path, session_info, lp):
926 """Setup the registry.
928 :param path: Path to the registry database
929 :param setup_path: Function that returns the path to a setup.
930 :param session_info: Session information
931 :param credentials: Credentials
932 :param lp: Loadparm context
934 reg = samba.registry.Registry()
935 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
936 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
937 provision_reg = setup_path("provision.reg")
938 assert os.path.exists(provision_reg)
939 reg.diff_apply(provision_reg)
942 def setup_idmapdb(path, setup_path, session_info, lp):
943 """Setup the idmap database.
945 :param path: path to the idmap database
946 :param setup_path: Function that returns a path to a setup file
947 :param session_info: Session information
948 :param credentials: Credentials
949 :param lp: Loadparm context
951 if os.path.exists(path):
954 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
956 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
960 def setup_samdb_rootdse(samdb, setup_path, names):
961 """Setup the SamDB rootdse.
963 :param samdb: Sam Database handle
964 :param setup_path: Obtain setup path
966 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
967 "SCHEMADN": names.schemadn,
968 "DOMAINDN": names.domaindn,
969 "ROOTDN": names.rootdn,
970 "CONFIGDN": names.configdn,
971 "SERVERDN": names.serverdn,
975 def setup_self_join(samdb, names, machinepass, dnspass,
976 domainsid, next_rid, invocationid, setup_path,
977 policyguid, policyguid_dc, domainControllerFunctionality,
979 """Join a host to its own domain."""
980 assert isinstance(invocationid, str)
981 if ntdsguid is not None:
982 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
985 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
986 "CONFIGDN": names.configdn,
987 "SCHEMADN": names.schemadn,
988 "DOMAINDN": names.domaindn,
989 "SERVERDN": names.serverdn,
990 "INVOCATIONID": invocationid,
991 "NETBIOSNAME": names.netbiosname,
992 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
993 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
994 "DOMAINSID": str(domainsid),
995 "DCRID": str(next_rid),
996 "SAMBA_VERSION_STRING": version,
997 "NTDSGUID": ntdsguid_line,
998 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
999 domainControllerFunctionality)})
1001 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1002 "POLICYGUID": policyguid,
1003 "POLICYGUID_DC": policyguid_dc,
1004 "DNSDOMAIN": names.dnsdomain,
1005 "DOMAINDN": names.domaindn})
1007 # add the NTDSGUID based SPNs
1008 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1009 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1010 expression="", scope=ldb.SCOPE_BASE)
1011 assert isinstance(names.ntdsguid, str)
1013 # Setup fSMORoleOwner entries to point at the newly created DC entry
1014 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1015 "DOMAINDN": names.domaindn,
1016 "CONFIGDN": names.configdn,
1017 "SCHEMADN": names.schemadn,
1018 "DEFAULTSITE": names.sitename,
1019 "SERVERDN": names.serverdn,
1020 "NETBIOSNAME": names.netbiosname,
1021 "RIDALLOCATIONSTART": str(next_rid + 100),
1022 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1025 # This is partially Samba4 specific and should be replaced by the correct
1026 # DNS AD-style setup
1027 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1028 "DNSDOMAIN": names.dnsdomain,
1029 "DOMAINDN": names.domaindn,
1030 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1031 "HOSTNAME" : names.hostname,
1032 "DNSNAME" : '%s.%s' % (
1033 names.netbiosname.lower(), names.dnsdomain.lower())
1037 def getpolicypath(sysvolpath, dnsdomain, guid):
1038 """Return the physical path of policy given its guid.
1040 :param sysvolpath: Path to the sysvol folder
1041 :param dnsdomain: DNS name of the AD domain
1042 :param guid: The GUID of the policy
1043 :return: A string with the complete path to the policy folder
1047 guid = "{%s}" % guid
1048 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1052 def create_gpo_struct(policy_path):
1053 if not os.path.exists(policy_path):
1054 os.makedirs(policy_path, 0775)
1055 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1056 "[General]\r\nVersion=0")
1057 p = os.path.join(policy_path, "MACHINE")
1058 if not os.path.exists(p):
1059 os.makedirs(p, 0775)
1060 p = os.path.join(policy_path, "USER")
1061 if not os.path.exists(p):
1062 os.makedirs(p, 0775)
1065 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1066 """Create the default GPO for a domain
1068 :param sysvolpath: Physical path for the sysvol folder
1069 :param dnsdomain: DNS domain name of the AD domain
1070 :param policyguid: GUID of the default domain policy
1071 :param policyguid_dc: GUID of the default domain controler policy
1073 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1074 create_gpo_struct(policy_path)
1076 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1077 create_gpo_struct(policy_path)
1080 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1081 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1082 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1083 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1085 """Setup a complete SAM Database.
1087 :note: This will wipe the main SAM database file!
1090 # Provision does not make much sense values larger than 1000000000
1091 # as the upper range of the rIDAvailablePool is 1073741823 and
1092 # we don't want to create a domain that cannot allocate rids.
1093 if next_rid < 1000 or next_rid > 1000000000:
1094 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1095 error += "the valid range is %u-%u. The default is %u." % (
1096 1000, 1000000000, 1000)
1097 raise ProvisioningError(error)
1099 # ATTENTION: Do NOT change these default values without discussion with the
1100 # team and/or release manager. They have a big impact on the whole program!
1101 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1103 if dom_for_fun_level is None:
1104 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1106 if dom_for_fun_level > domainControllerFunctionality:
1107 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!")
1109 domainFunctionality = dom_for_fun_level
1110 forestFunctionality = dom_for_fun_level
1112 # Also wipes the database
1113 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1114 provision_backend=provision_backend, session_info=session_info,
1115 names=names, serverrole=serverrole, schema=schema)
1118 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1120 # Load the database, but don's load the global schema and don't connect
1122 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1123 credentials=provision_backend.credentials, lp=lp,
1124 global_schema=False, am_rodc=am_rodc)
1126 logger.info("Pre-loading the Samba 4 and AD schema")
1128 # Load the schema from the one we computed earlier
1129 samdb.set_schema(schema)
1131 # Set the NTDS settings DN manually - in order to have it already around
1132 # before the provisioned tree exists and we connect
1133 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1135 # And now we can connect to the DB - the schema won't be loaded from the
1139 if fill == FILL_DRS:
1142 samdb.transaction_start()
1144 # Set the domain functionality levels onto the database.
1145 # Various module (the password_hash module in particular) need
1146 # to know what level of AD we are emulating.
1148 # These will be fixed into the database via the database
1149 # modifictions below, but we need them set from the start.
1150 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1151 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1152 samdb.set_opaque_integer("domainControllerFunctionality",
1153 domainControllerFunctionality)
1155 samdb.set_domain_sid(str(domainsid))
1156 samdb.set_invocation_id(invocationid)
1158 logger.info("Adding DomainDN: %s" % names.domaindn)
1160 # impersonate domain admin
1161 admin_session_info = admin_session(lp, str(domainsid))
1162 samdb.set_session_info(admin_session_info)
1163 if domainguid is not None:
1164 domainguid_line = "objectGUID: %s\n-" % domainguid
1166 domainguid_line = ""
1168 descr = b64encode(get_domain_descriptor(domainsid))
1169 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1170 "DOMAINDN": names.domaindn,
1171 "DOMAINSID": str(domainsid),
1172 "DESCRIPTOR": descr,
1173 "DOMAINGUID": domainguid_line
1176 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1177 "DOMAINDN": names.domaindn,
1178 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1179 "NEXTRID": str(next_rid),
1180 "DEFAULTSITE": names.sitename,
1181 "CONFIGDN": names.configdn,
1182 "POLICYGUID": policyguid,
1183 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1184 "SAMBA_VERSION_STRING": version
1187 logger.info("Adding configuration container")
1188 descr = b64encode(get_config_descriptor(domainsid))
1189 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1190 "CONFIGDN": names.configdn,
1191 "DESCRIPTOR": descr,
1194 # The LDIF here was created when the Schema object was constructed
1195 logger.info("Setting up sam.ldb schema")
1196 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1197 samdb.modify_ldif(schema.schema_dn_modify)
1198 samdb.write_prefixes_from_schema()
1199 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1200 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1201 {"SCHEMADN": names.schemadn})
1203 logger.info("Reopening sam.ldb with new schema")
1205 samdb.transaction_cancel()
1208 samdb.transaction_commit()
1210 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1211 credentials=provision_backend.credentials, lp=lp,
1212 global_schema=False, am_rodc=am_rodc)
1214 # Set the NTDS settings DN manually - in order to have it already around
1215 # before the provisioned tree exists and we connect
1216 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1219 samdb.transaction_start()
1221 samdb.invocation_id = invocationid
1223 logger.info("Setting up sam.ldb configuration data")
1224 descr = b64encode(get_sites_descriptor(domainsid))
1225 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1226 "CONFIGDN": names.configdn,
1227 "NETBIOSNAME": names.netbiosname,
1228 "DEFAULTSITE": names.sitename,
1229 "DNSDOMAIN": names.dnsdomain,
1230 "DOMAIN": names.domain,
1231 "SCHEMADN": names.schemadn,
1232 "DOMAINDN": names.domaindn,
1233 "SERVERDN": names.serverdn,
1234 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1235 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1236 "SITES_DESCRIPTOR": descr
1239 logger.info("Setting up display specifiers")
1240 display_specifiers_ldif = read_ms_ldif(
1241 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1242 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1243 {"CONFIGDN": names.configdn})
1244 check_all_substituted(display_specifiers_ldif)
1245 samdb.add_ldif(display_specifiers_ldif)
1247 logger.info("Adding users container")
1248 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1249 "DOMAINDN": names.domaindn})
1250 logger.info("Modifying users container")
1251 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1252 "DOMAINDN": names.domaindn})
1253 logger.info("Adding computers container")
1254 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1255 "DOMAINDN": names.domaindn})
1256 logger.info("Modifying computers container")
1257 setup_modify_ldif(samdb,
1258 setup_path("provision_computers_modify.ldif"), {
1259 "DOMAINDN": names.domaindn})
1260 logger.info("Setting up sam.ldb data")
1261 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1262 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1263 "DOMAINDN": names.domaindn,
1264 "NETBIOSNAME": names.netbiosname,
1265 "DEFAULTSITE": names.sitename,
1266 "CONFIGDN": names.configdn,
1267 "SERVERDN": names.serverdn,
1268 "RIDAVAILABLESTART": str(next_rid + 600),
1269 "POLICYGUID_DC": policyguid_dc
1272 setup_modify_ldif(samdb,
1273 setup_path("provision_basedn_references.ldif"), {
1274 "DOMAINDN": names.domaindn})
1276 setup_modify_ldif(samdb,
1277 setup_path("provision_configuration_references.ldif"), {
1278 "CONFIGDN": names.configdn,
1279 "SCHEMADN": names.schemadn})
1280 if fill == FILL_FULL:
1281 logger.info("Setting up sam.ldb users and groups")
1282 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1283 "DOMAINDN": names.domaindn,
1284 "DOMAINSID": str(domainsid),
1285 "CONFIGDN": names.configdn,
1286 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1287 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1290 logger.info("Setting up self join")
1291 setup_self_join(samdb, names=names, invocationid=invocationid,
1293 machinepass=machinepass,
1294 domainsid=domainsid,
1296 policyguid=policyguid,
1297 policyguid_dc=policyguid_dc,
1298 setup_path=setup_path,
1299 domainControllerFunctionality=domainControllerFunctionality,
1302 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1303 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1304 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1305 assert isinstance(names.ntdsguid, str)
1307 samdb.transaction_cancel()
1310 samdb.transaction_commit()
1315 FILL_NT4SYNC = "NT4SYNC"
1317 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1318 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)"
1321 def set_dir_acl(path, acl, lp, domsid):
1322 setntacl(lp, path, acl, domsid)
1323 for root, dirs, files in os.walk(path, topdown=False):
1325 setntacl(lp, os.path.join(root, name), acl, domsid)
1327 setntacl(lp, os.path.join(root, name), acl, domsid)
1330 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1331 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1334 :param sysvol: Physical path for the sysvol folder
1335 :param dnsdomain: The DNS name of the domain
1336 :param domainsid: The SID of the domain
1337 :param domaindn: The DN of the domain (ie. DC=...)
1338 :param samdb: An LDB object on the SAM db
1339 :param lp: an LP object
1342 # Set ACL for GPO root folder
1343 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1344 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1346 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1347 attrs=["cn", "nTSecurityDescriptor"],
1348 expression="", scope=ldb.SCOPE_ONELEVEL)
1351 acl = ndr_unpack(security.descriptor,
1352 str(policy["nTSecurityDescriptor"])).as_sddl()
1353 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1354 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1358 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1360 """Set the ACL for the sysvol share and the subfolders
1362 :param samdb: An LDB object on the SAM db
1363 :param netlogon: Physical path for the netlogon folder
1364 :param sysvol: Physical path for the sysvol folder
1365 :param gid: The GID of the "Domain adminstrators" group
1366 :param domainsid: The SID of the domain
1367 :param dnsdomain: The DNS name of the domain
1368 :param domaindn: The DN of the domain (ie. DC=...)
1372 os.chown(sysvol, -1, gid)
1378 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1379 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1380 for root, dirs, files in os.walk(sysvol, topdown=False):
1383 os.chown(os.path.join(root, name), -1, gid)
1384 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1387 os.chown(os.path.join(root, name), -1, gid)
1388 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1390 # Set acls on Policy folder and policies folders
1391 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1394 def provision(setup_dir, logger, session_info, credentials, smbconf=None,
1395 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1396 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1397 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1398 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1399 domainguid=None, policyguid=None, policyguid_dc=None,
1400 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1401 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1402 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1403 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1404 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1405 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1409 :note: caution, this wipes all existing data!
1412 def setup_path(file):
1413 return os.path.join(setup_dir, file)
1415 if domainsid is None:
1416 domainsid = security.random_sid()
1418 domainsid = security.dom_sid(domainsid)
1420 # create/adapt the group policy GUIDs
1421 # Default GUID for default policy are described at
1422 # "How Core Group Policy Works"
1423 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1424 if policyguid is None:
1425 policyguid = DEFAULT_POLICY_GUID
1426 policyguid = policyguid.upper()
1427 if policyguid_dc is None:
1428 policyguid_dc = DEFAULT_DC_POLICY_GUID
1429 policyguid_dc = policyguid_dc.upper()
1431 if adminpass is None:
1432 adminpass = samba.generate_random_password(12, 32)
1433 if krbtgtpass is None:
1434 krbtgtpass = samba.generate_random_password(128, 255)
1435 if machinepass is None:
1436 machinepass = samba.generate_random_password(128, 255)
1438 dnspass = samba.generate_random_password(128, 255)
1439 if ldapadminpass is None:
1440 # Make a new, random password between Samba and it's LDAP server
1441 ldapadminpass=samba.generate_random_password(128, 255)
1443 if backend_type is None:
1444 backend_type = "ldb"
1446 sid_generator = "internal"
1447 if backend_type == "fedora-ds":
1448 sid_generator = "backend"
1450 root_uid = findnss_uid([root or "root"])
1451 nobody_uid = findnss_uid([nobody or "nobody"])
1452 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1454 wheel_gid = findnss_gid(["wheel", "adm"])
1456 wheel_gid = findnss_gid([wheel])
1458 bind_gid = findnss_gid(["bind", "named"])
1462 if targetdir is not None:
1463 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1464 elif smbconf is None:
1465 smbconf = samba.param.default_path()
1466 if not os.path.exists(os.path.dirname(smbconf)):
1467 os.makedirs(os.path.dirname(smbconf))
1469 # only install a new smb.conf if there isn't one there already
1470 if os.path.exists(smbconf):
1471 # if Samba Team members can't figure out the weird errors
1472 # loading an empty smb.conf gives, then we need to be smarter.
1473 # Pretend it just didn't exist --abartlet
1474 data = open(smbconf, 'r').read()
1475 data = data.lstrip()
1476 if data is None or data == "":
1477 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1478 serverrole, targetdir, sid_generator, useeadb,
1481 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1482 targetdir, sid_generator, useeadb, lp=lp)
1485 lp = samba.param.LoadParm()
1487 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1488 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1489 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1491 paths = provision_paths_from_lp(lp, names.dnsdomain)
1493 paths.bind_gid = bind_gid
1496 logger.info("Looking up IPv4 addresses")
1497 hostips = samba.interface_ips(lp, False)
1498 if len(hostips) == 0:
1499 logger.warning("No external IPv4 address has been found. Using loopback.")
1500 hostip = '127.0.0.1'
1503 if len(hostips) > 1:
1504 logger.warning("More than one IPv4 address found. Using %s.",
1507 if serverrole is None:
1508 serverrole = lp.get("server role")
1510 assert serverrole in ("domain controller", "member server", "standalone")
1511 if invocationid is None:
1512 invocationid = str(uuid.uuid4())
1514 if not os.path.exists(paths.private_dir):
1515 os.mkdir(paths.private_dir)
1516 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1517 os.mkdir(os.path.join(paths.private_dir, "tls"))
1519 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1521 schema = Schema(setup_path, domainsid, invocationid=invocationid,
1522 schemadn=names.schemadn)
1524 if backend_type == "ldb":
1525 provision_backend = LDBBackend(backend_type, paths=paths,
1526 setup_path=setup_path, lp=lp, credentials=credentials,
1527 names=names, logger=logger)
1528 elif backend_type == "existing":
1529 provision_backend = ExistingBackend(backend_type, paths=paths,
1530 setup_path=setup_path, lp=lp, credentials=credentials,
1531 names=names, logger=logger,
1532 ldap_backend_forced_uri=ldap_backend_forced_uri)
1533 elif backend_type == "fedora-ds":
1534 provision_backend = FDSBackend(backend_type, paths=paths,
1535 setup_path=setup_path, lp=lp, credentials=credentials,
1536 names=names, logger=logger, domainsid=domainsid,
1537 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1538 slapd_path=slapd_path,
1539 ldap_backend_extra_port=ldap_backend_extra_port,
1540 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1541 setup_ds_path=setup_ds_path,
1542 ldap_backend_forced_uri=ldap_backend_forced_uri)
1543 elif backend_type == "openldap":
1544 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1545 setup_path=setup_path, lp=lp, credentials=credentials,
1546 names=names, logger=logger, domainsid=domainsid,
1547 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1548 slapd_path=slapd_path,
1549 ldap_backend_extra_port=ldap_backend_extra_port,
1550 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1552 ldap_backend_forced_uri=ldap_backend_forced_uri)
1554 raise ValueError("Unknown LDAP backend type selected")
1556 provision_backend.init()
1557 provision_backend.start()
1559 # only install a new shares config db if there is none
1560 if not os.path.exists(paths.shareconf):
1561 logger.info("Setting up share.ldb")
1562 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1564 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1566 logger.info("Setting up secrets.ldb")
1567 secrets_ldb = setup_secretsdb(paths, setup_path,
1568 session_info=session_info,
1569 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1572 logger.info("Setting up the registry")
1573 setup_registry(paths.hklm, setup_path, session_info,
1576 logger.info("Setting up the privileges database")
1577 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1579 logger.info("Setting up idmap db")
1580 idmap = setup_idmapdb(paths.idmapdb, setup_path,
1581 session_info=session_info, lp=lp)
1583 logger.info("Setting up SAM db")
1584 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1585 provision_backend, lp, names, logger=logger,
1586 domainsid=domainsid, schema=schema, domainguid=domainguid,
1587 policyguid=policyguid, policyguid_dc=policyguid_dc,
1588 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1589 invocationid=invocationid, machinepass=machinepass,
1590 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1591 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1594 if serverrole == "domain controller":
1595 if paths.netlogon is None:
1596 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1597 logger.info("Please either remove %s or see the template at %s" %
1598 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1599 assert paths.netlogon is not None
1601 if paths.sysvol is None:
1602 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1603 " are configuring a DC.")
1604 logger.info("Please either remove %s or see the template at %s" %
1605 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1606 assert paths.sysvol is not None
1608 if not os.path.isdir(paths.netlogon):
1609 os.makedirs(paths.netlogon, 0755)
1611 if samdb_fill == FILL_FULL:
1612 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1613 root_uid=root_uid, nobody_uid=nobody_uid,
1614 users_gid=users_gid, wheel_gid=wheel_gid)
1616 if serverrole == "domain controller":
1617 # Set up group policies (domain policy and domain controller
1619 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1621 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1622 domainsid, names.dnsdomain, names.domaindn, lp)
1624 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1625 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1627 secretsdb_self_join(secrets_ldb, domain=names.domain,
1628 realm=names.realm, dnsdomain=names.dnsdomain,
1629 netbiosname=names.netbiosname, domainsid=domainsid,
1630 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1632 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1633 # In future, this might be determined from some configuration
1634 kerberos_enctypes = str(ENC_ALL_TYPES)
1637 msg = ldb.Message(ldb.Dn(samdb,
1638 samdb.searchone("distinguishedName",
1639 expression="samAccountName=%s$" % names.netbiosname,
1640 scope=ldb.SCOPE_SUBTREE)))
1641 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1642 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1643 name="msDS-SupportedEncryptionTypes")
1645 except ldb.LdbError, (enum, estr):
1646 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1647 # It might be that this attribute does not exist in this schema
1650 if serverrole == "domain controller":
1651 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1652 paths.private_dir, realm=names.realm,
1653 dnsdomain=names.dnsdomain,
1654 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1656 domainguid = samdb.searchone(basedn=domaindn,
1657 attribute="objectGUID")
1658 assert isinstance(domainguid, str)
1660 # Only make a zone file on the first DC, it should be
1661 # replicated with DNS replication
1662 create_zone_file(lp, logger, paths, targetdir, setup_path,
1663 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1664 hostname=names.hostname, realm=names.realm,
1665 domainguid=domainguid, ntdsguid=names.ntdsguid)
1667 create_named_conf(paths, setup_path, realm=names.realm,
1668 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1670 create_named_txt(paths.namedtxt, setup_path,
1671 realm=names.realm, dnsdomain=names.dnsdomain,
1672 private_dir=paths.private_dir,
1673 keytab_name=paths.dns_keytab)
1674 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1675 logger.info("and %s for further documentation required for secure DNS "
1676 "updates", paths.namedtxt)
1678 lastProvisionUSNs = get_last_provision_usn(samdb)
1679 maxUSN = get_max_usn(samdb, str(names.rootdn))
1680 if lastProvisionUSNs is not None:
1681 update_provision_usn(samdb, 0, maxUSN, 1)
1683 set_provision_usn(samdb, 0, maxUSN)
1685 create_krb5_conf(paths.krb5conf, setup_path,
1686 dnsdomain=names.dnsdomain, hostname=names.hostname,
1688 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1689 "generated at %s", paths.krb5conf)
1691 if serverrole == "domain controller":
1692 create_dns_update_list(lp, logger, paths, setup_path)
1694 provision_backend.post_setup()
1695 provision_backend.shutdown()
1697 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1700 secrets_ldb.transaction_cancel()
1703 # Now commit the secrets.ldb to disk
1704 secrets_ldb.transaction_commit()
1706 # the commit creates the dns.keytab, now chown it
1707 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1708 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1710 os.chmod(dns_keytab_path, 0640)
1711 os.chown(dns_keytab_path, -1, paths.bind_gid)
1713 if not os.environ.has_key('SAMBA_SELFTEST'):
1714 logger.info("Failed to chown %s to bind gid %u",
1715 dns_keytab_path, paths.bind_gid)
1718 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1719 paths.phpldapadminconfig)
1721 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1722 logger.info("Server Role: %s" % serverrole)
1723 logger.info("Hostname: %s" % names.hostname)
1724 logger.info("NetBIOS Domain: %s" % names.domain)
1725 logger.info("DNS Domain: %s" % names.dnsdomain)
1726 logger.info("DOMAIN SID: %s" % str(domainsid))
1727 if samdb_fill == FILL_FULL:
1728 logger.info("Admin password: %s" % adminpass)
1729 if provision_backend.type is not "ldb":
1730 if provision_backend.credentials.get_bind_dn() is not None:
1731 logger.info("LDAP Backend Admin DN: %s" %
1732 provision_backend.credentials.get_bind_dn())
1734 logger.info("LDAP Admin User: %s" %
1735 provision_backend.credentials.get_username())
1737 logger.info("LDAP Admin Password: %s" %
1738 provision_backend.credentials.get_password())
1740 if provision_backend.slapd_command_escaped is not None:
1741 # now display slapd_command_file.txt to show how slapd must be
1743 logger.info("Use later the following commandline to start slapd, then Samba:")
1744 logger.info(provision_backend.slapd_command_escaped)
1745 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1746 provision_backend.ldapdir)
1748 result = ProvisionResult()
1749 result.domaindn = domaindn
1750 result.paths = paths
1752 result.samdb = samdb
1756 def provision_become_dc(setup_dir=None, smbconf=None, targetdir=None,
1757 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1758 serverdn=None, domain=None, hostname=None, domainsid=None,
1759 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1760 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1761 root=None, nobody=None, users=None, wheel=None, backup=None,
1762 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1763 sitename=None, debuglevel=1):
1765 logger = logging.getLogger("provision")
1766 samba.set_debug_level(debuglevel)
1768 res = provision(setup_dir, logger, system_session(), None,
1769 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1770 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1771 configdn=configdn, serverdn=serverdn, domain=domain,
1772 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1773 machinepass=machinepass, serverrole="domain controller",
1775 res.lp.set("debuglevel", str(debuglevel))
1779 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1780 """Create a PHP LDAP admin configuration file.
1782 :param path: Path to write the configuration to.
1783 :param setup_path: Function to generate setup paths.
1785 setup_file(setup_path("phpldapadmin-config.php"), path,
1786 {"S4_LDAPI_URI": ldapi_uri})
1789 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1790 hostip, hostip6, hostname, realm, domainguid,
1792 """Write out a DNS zone file, from the info in the current database.
1794 :param paths: paths object
1795 :param setup_path: Setup path function.
1796 :param dnsdomain: DNS Domain name
1797 :param domaindn: DN of the Domain
1798 :param hostip: Local IPv4 IP
1799 :param hostip6: Local IPv6 IP
1800 :param hostname: Local hostname
1801 :param realm: Realm name
1802 :param domainguid: GUID of the domain.
1803 :param ntdsguid: GUID of the hosts nTDSDSA record.
1805 assert isinstance(domainguid, str)
1807 if hostip6 is not None:
1808 hostip6_base_line = " IN AAAA " + hostip6
1809 hostip6_host_line = hostname + " IN AAAA " + hostip6
1810 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1812 hostip6_base_line = ""
1813 hostip6_host_line = ""
1814 gc_msdcs_ip6_line = ""
1816 if hostip is not None:
1817 hostip_base_line = " IN A " + hostip
1818 hostip_host_line = hostname + " IN A " + hostip
1819 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1821 hostip_base_line = ""
1822 hostip_host_line = ""
1823 gc_msdcs_ip_line = ""
1825 dns_dir = os.path.dirname(paths.dns)
1828 shutil.rmtree(dns_dir, True)
1832 os.mkdir(dns_dir, 0775)
1834 # we need to freeze the zone while we update the contents
1835 if targetdir is None:
1836 rndc = ' '.join(lp.get("rndc command"))
1837 os.system(rndc + " freeze " + lp.get("realm"))
1839 setup_file(setup_path("provision.zone"), paths.dns, {
1840 "HOSTNAME": hostname,
1841 "DNSDOMAIN": dnsdomain,
1843 "HOSTIP_BASE_LINE": hostip_base_line,
1844 "HOSTIP_HOST_LINE": hostip_host_line,
1845 "DOMAINGUID": domainguid,
1846 "DATESTRING": time.strftime("%Y%m%d%H"),
1847 "DEFAULTSITE": DEFAULTSITE,
1848 "NTDSGUID": ntdsguid,
1849 "HOSTIP6_BASE_LINE": hostip6_base_line,
1850 "HOSTIP6_HOST_LINE": hostip6_host_line,
1851 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1852 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1855 # note that we use no variable substitution on this file
1856 # the substitution is done at runtime by samba_dnsupdate
1857 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1859 # and the SPN update list
1860 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1862 if paths.bind_gid is not None:
1864 os.chown(dns_dir, -1, paths.bind_gid)
1865 os.chown(paths.dns, -1, paths.bind_gid)
1866 # chmod needed to cope with umask
1867 os.chmod(dns_dir, 0775)
1868 os.chmod(paths.dns, 0664)
1870 if not os.environ.has_key('SAMBA_SELFTEST'):
1871 logger.error("Failed to chown %s to bind gid %u" % (
1872 dns_dir, paths.bind_gid))
1874 if targetdir is None:
1875 os.system(rndc + " unfreeze " + lp.get("realm"))
1878 def create_dns_update_list(lp, logger, paths, setup_path):
1879 """Write out a dns_update_list file"""
1880 # note that we use no variable substitution on this file
1881 # the substitution is done at runtime by samba_dnsupdate
1882 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1883 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1886 def create_named_conf(paths, setup_path, realm, dnsdomain,
1888 """Write out a file containing zone statements suitable for inclusion in a
1889 named.conf file (including GSS-TSIG configuration).
1891 :param paths: all paths
1892 :param setup_path: Setup path function.
1893 :param realm: Realm name
1894 :param dnsdomain: DNS Domain name
1895 :param private_dir: Path to private directory
1896 :param keytab_name: File name of DNS keytab file
1899 setup_file(setup_path("named.conf"), paths.namedconf, {
1900 "DNSDOMAIN": dnsdomain,
1902 "ZONE_FILE": paths.dns,
1903 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1904 "NAMED_CONF": paths.namedconf,
1905 "NAMED_CONF_UPDATE": paths.namedconf_update
1908 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1911 def create_named_txt(path, setup_path, realm, dnsdomain, private_dir,
1913 """Write out a file containing zone statements suitable for inclusion in a
1914 named.conf file (including GSS-TSIG configuration).
1916 :param path: Path of the new named.conf file.
1917 :param setup_path: Setup path function.
1918 :param realm: Realm name
1919 :param dnsdomain: DNS Domain name
1920 :param private_dir: Path to private directory
1921 :param keytab_name: File name of DNS keytab file
1923 setup_file(setup_path("named.txt"), path, {
1924 "DNSDOMAIN": dnsdomain,
1926 "DNS_KEYTAB": keytab_name,
1927 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1928 "PRIVATE_DIR": private_dir
1932 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1933 """Write out a file containing zone statements suitable for inclusion in a
1934 named.conf file (including GSS-TSIG configuration).
1936 :param path: Path of the new named.conf file.
1937 :param setup_path: Setup path function.
1938 :param dnsdomain: DNS Domain name
1939 :param hostname: Local hostname
1940 :param realm: Realm name
1942 setup_file(setup_path("krb5.conf"), path, {
1943 "DNSDOMAIN": dnsdomain,
1944 "HOSTNAME": hostname,
1949 class ProvisioningError(Exception):
1950 """A generic provision error."""
1952 def __init__(self, value):
1956 return "ProvisioningError: " + self.value
1959 class InvalidNetbiosName(Exception):
1960 """A specified name was not a valid NetBIOS name."""
1961 def __init__(self, name):
1962 super(InvalidNetbiosName, self).__init__(
1963 "The name '%r' is not a valid NetBIOS name" % name)