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,
56 from samba.dsdb import (
57 DS_DOMAIN_FUNCTION_2003,
58 DS_DOMAIN_FUNCTION_2008_R2,
61 from samba.dcerpc import security
62 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
63 from samba.idmap import IDmapDB
64 from samba.ms_display_specifiers import read_ms_ldif
65 from samba.ntacls import setntacl, dsacl2fsacl
66 from samba.ndr import ndr_pack,ndr_unpack
67 from samba.provision.backend import (
75 from samba.schema import Schema
76 from samba.samdb import SamDB
78 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
79 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
80 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
81 DEFAULTSITE = "Default-First-Site-Name"
82 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
86 """Find the setup directory used by provision."""
89 dirname = os.path.dirname(__file__)
90 return os.path.normpath(os.path.join(dirname, "../../../setup"))
93 for prefix in [sys.prefix,
94 os.path.join(os.path.dirname(__file__), "../../../..")]:
95 for suffix in ["share/setup", "share/samba/setup", "setup"]:
96 ret = os.path.normpath(os.path.join(prefix, suffix))
97 if os.path.isdir(ret):
99 raise Exception("Unable to find setup directory.")
101 # Descriptors of naming contexts and other important objects
103 # "get_schema_descriptor" is located in "schema.py"
105 def get_sites_descriptor(domain_sid):
106 sddl = "O:EAG:EAD:AI(A;;RPLCLORC;;;AU)" \
107 "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
108 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
109 "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
110 "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
111 "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
112 "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
113 "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
114 "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
115 "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
116 sec = security.descriptor.from_sddl(sddl, domain_sid)
120 def get_config_descriptor(domain_sid):
121 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
122 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
123 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
124 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
125 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
126 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
127 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
128 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
129 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
131 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
132 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
133 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
134 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
135 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
136 sec = security.descriptor.from_sddl(sddl, domain_sid)
140 def get_domain_descriptor(domain_sid):
141 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
142 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
143 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
144 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
145 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
146 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
147 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
148 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
149 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
150 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
151 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
152 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
153 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
154 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
155 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
156 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
157 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
158 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
159 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
160 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
161 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
162 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
163 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
164 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
165 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
166 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
167 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
168 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
169 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
170 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
171 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
172 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
173 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
174 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
175 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
176 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
177 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
178 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
179 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
182 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
184 "(A;;RPLCLORC;;;ED)" \
185 "(A;;RPLCLORC;;;AU)" \
186 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
187 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
188 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
189 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
190 sec = security.descriptor.from_sddl(sddl, domain_sid)
194 class ProvisionPaths(object):
197 self.shareconf = None
208 self.dns_keytab = None
211 self.private_dir = None
214 class ProvisionNames(object):
221 self.ldapmanagerdn = None
222 self.dnsdomain = None
224 self.netbiosname = None
231 def update_provision_usn(samdb, low, high, replace=False):
232 """Update the field provisionUSN in sam.ldb
234 This field is used to track range of USN modified by provision and
236 This value is used afterward by next provision to figure out if
237 the field have been modified since last provision.
239 :param samdb: An LDB object connect to sam.ldb
240 :param low: The lowest USN modified by this upgrade
241 :param high: The highest USN modified by this upgrade
242 :param replace: A boolean indicating if the range should replace any
243 existing one or appended (default)
248 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
249 LAST_PROVISION_USN_ATTRIBUTE, base="",
250 scope=ldb.SCOPE_SUBTREE,
251 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
252 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
255 tab.append("%s-%s" % (low, high))
256 delta = ldb.Message()
257 delta.dn = ldb.Dn(samdb, "@PROVISION")
258 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
259 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
263 def set_provision_usn(samdb, low, high):
264 """Set the field provisionUSN in sam.ldb
265 This field is used to track range of USN modified by provision and
267 This value is used afterward by next provision to figure out if
268 the field have been modified since last provision.
270 :param samdb: An LDB object connect to sam.ldb
271 :param low: The lowest USN modified by this upgrade
272 :param high: The highest USN modified by this upgrade"""
274 tab.append("%s-%s" % (low, high))
275 delta = ldb.Message()
276 delta.dn = ldb.Dn(samdb, "@PROVISION")
277 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
278 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
282 def get_max_usn(samdb,basedn):
283 """ This function return the biggest USN present in the provision
285 :param samdb: A LDB object pointing to the sam.ldb
286 :param basedn: A string containing the base DN of the provision
288 :return: The biggest USN in the provision"""
290 res = samdb.search(expression="objectClass=*",base=basedn,
291 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
292 controls=["search_options:1:2",
293 "server_sort:1:1:uSNChanged",
294 "paged_results:1:1"])
295 return res[0]["uSNChanged"]
298 def get_last_provision_usn(sam):
299 """Get the lastest USN modified by a provision or an upgradeprovision
301 :param sam: An LDB object pointing to the sam.ldb
302 :return: an integer corresponding to the highest USN modified by
303 (upgrade)provision, 0 is this value is unknown
305 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
306 LAST_PROVISION_USN_ATTRIBUTE,
307 base="", scope=ldb.SCOPE_SUBTREE,
308 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
313 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
314 tab = p.split(str(r))
323 class ProvisionResult(object):
332 def check_install(lp, session_info, credentials):
333 """Check whether the current install seems ok.
335 :param lp: Loadparm context
336 :param session_info: Session information
337 :param credentials: Credentials
339 if lp.get("realm") == "":
340 raise Exception("Realm empty")
341 samdb = Ldb(lp.get("sam database"), session_info=session_info,
342 credentials=credentials, lp=lp)
343 if len(samdb.search("(cn=Administrator)")) != 1:
344 raise ProvisioningError("No administrator account found")
347 def findnss(nssfn, names):
348 """Find a user or group from a list of possibilities.
350 :param nssfn: NSS Function to try (should raise KeyError if not found)
351 :param names: Names to check.
352 :return: Value return by first names list.
359 raise KeyError("Unable to find user/group in %r" % names)
362 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
363 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
366 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
367 """Setup a ldb in the private dir.
369 :param ldb: LDB file to import data into
370 :param ldif_path: Path of the LDIF file to load
371 :param subst_vars: Optional variables to subsitute in LDIF.
372 :param nocontrols: Optional list of controls, can be None for no controls
374 assert isinstance(ldif_path, str)
375 data = read_and_sub_file(ldif_path, subst_vars)
376 ldb.add_ldif(data, controls)
379 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
380 """Modify a ldb in the private dir.
382 :param ldb: LDB object.
383 :param ldif_path: LDIF file path.
384 :param subst_vars: Optional dictionary with substitution variables.
386 data = read_and_sub_file(ldif_path, subst_vars)
387 ldb.modify_ldif(data, controls)
390 def setup_ldb(ldb, ldif_path, subst_vars):
391 """Import a LDIF a file into a LDB handle, optionally substituting
394 :note: Either all LDIF data will be added or none (using transactions).
396 :param ldb: LDB file to import into.
397 :param ldif_path: Path to the LDIF file.
398 :param subst_vars: Dictionary with substitution variables.
400 assert ldb is not None
401 ldb.transaction_start()
403 setup_add_ldif(ldb, ldif_path, subst_vars)
405 ldb.transaction_cancel()
408 ldb.transaction_commit()
411 def provision_paths_from_lp(lp, dnsdomain):
412 """Set the default paths for provisioning.
414 :param lp: Loadparm context.
415 :param dnsdomain: DNS Domain name
417 paths = ProvisionPaths()
418 paths.private_dir = lp.get("private dir")
420 # This is stored without path prefix for the "privateKeytab" attribute in
421 # "secrets_dns.ldif".
422 paths.dns_keytab = "dns.keytab"
423 paths.keytab = "secrets.keytab"
425 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
426 paths.samdb = os.path.join(paths.private_dir,
427 lp.get("sam database") or "samdb.ldb")
428 paths.idmapdb = os.path.join(paths.private_dir,
429 lp.get("idmap database") or "idmap.ldb")
430 paths.secrets = os.path.join(paths.private_dir,
431 lp.get("secrets database") or "secrets.ldb")
432 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
433 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
434 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
435 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
436 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
437 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
438 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
439 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
440 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
441 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
442 paths.phpldapadminconfig = os.path.join(paths.private_dir,
443 "phpldapadmin-config.php")
444 paths.hklm = "hklm.ldb"
445 paths.hkcr = "hkcr.ldb"
446 paths.hkcu = "hkcu.ldb"
447 paths.hku = "hku.ldb"
448 paths.hkpd = "hkpd.ldb"
449 paths.hkpt = "hkpt.ldb"
450 paths.sysvol = lp.get("path", "sysvol")
451 paths.netlogon = lp.get("path", "netlogon")
452 paths.smbconf = lp.configfile
456 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
457 serverrole=None, rootdn=None, domaindn=None, configdn=None,
458 schemadn=None, serverdn=None, sitename=None):
459 """Guess configuration settings to use."""
462 hostname = socket.gethostname().split(".")[0]
464 netbiosname = lp.get("netbios name")
465 if netbiosname is None:
466 netbiosname = hostname
467 # remove forbidden chars
469 for x in netbiosname:
470 if x.isalnum() or x in VALID_NETBIOS_CHARS:
471 newnbname = "%s%c" % (newnbname, x)
472 #force the length to be <16
473 netbiosname = newnbname[0:15]
474 assert netbiosname is not None
475 netbiosname = netbiosname.upper()
476 if not valid_netbios_name(netbiosname):
477 raise InvalidNetbiosName(netbiosname)
479 if dnsdomain is None:
480 dnsdomain = lp.get("realm")
481 if dnsdomain is None or dnsdomain == "":
482 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
484 dnsdomain = dnsdomain.lower()
486 if serverrole is None:
487 serverrole = lp.get("server role")
488 if serverrole is None:
489 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
491 serverrole = serverrole.lower()
493 realm = dnsdomain.upper()
495 if lp.get("realm") == "":
496 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
498 if lp.get("realm").upper() != realm:
499 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))
501 if lp.get("server role").lower() != serverrole:
502 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))
504 if serverrole == "domain controller":
506 # This will, for better or worse, default to 'WORKGROUP'
507 domain = lp.get("workgroup")
508 domain = domain.upper()
510 if lp.get("workgroup").upper() != domain:
511 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))
514 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
518 domaindn = "DC=" + netbiosname
520 if not valid_netbios_name(domain):
521 raise InvalidNetbiosName(domain)
523 if hostname.upper() == realm:
524 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
525 if netbiosname == realm:
526 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
528 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
534 configdn = "CN=Configuration," + rootdn
536 schemadn = "CN=Schema," + configdn
541 names = ProvisionNames()
542 names.rootdn = rootdn
543 names.domaindn = domaindn
544 names.configdn = configdn
545 names.schemadn = schemadn
546 names.ldapmanagerdn = "CN=Manager," + rootdn
547 names.dnsdomain = dnsdomain
548 names.domain = domain
550 names.netbiosname = netbiosname
551 names.hostname = hostname
552 names.sitename = sitename
553 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
554 netbiosname, sitename, configdn)
559 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
560 targetdir, sid_generator="internal", eadb=False, lp=None):
561 """Create a new smb.conf file based on a couple of basic settings.
563 assert smbconf is not None
565 hostname = socket.gethostname().split(".")[0]
566 netbiosname = hostname.upper()
567 # remove forbidden chars
569 for x in netbiosname:
570 if x.isalnum() or x in VALID_NETBIOS_CHARS:
571 newnbname = "%s%c" % (newnbname, x)
572 #force the length to be <16
573 netbiosname = newnbname[0:15]
575 netbiosname = hostname.upper()
577 if serverrole is None:
578 serverrole = "standalone"
580 assert serverrole in ("domain controller", "member server", "standalone")
581 if serverrole == "domain controller":
583 elif serverrole == "member server":
584 smbconfsuffix = "member"
585 elif serverrole == "standalone":
586 smbconfsuffix = "standalone"
588 if sid_generator is None:
589 sid_generator = "internal"
591 assert domain is not None
592 domain = domain.upper()
594 assert realm is not None
595 realm = realm.upper()
598 lp = samba.param.LoadParm()
599 #Load non-existant file
600 if os.path.exists(smbconf):
602 if eadb and not lp.get("posix:eadb"):
603 if targetdir is not None:
604 privdir = os.path.join(targetdir, "private")
606 privdir = lp.get("private dir")
607 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
609 if targetdir is not None:
610 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
611 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
613 lp.set("lock dir", os.path.abspath(targetdir))
618 if sid_generator == "internal":
619 sid_generator_line = ""
621 sid_generator_line = "sid generator = " + sid_generator
623 used_setup_dir = setup_path("")
624 default_setup_dir = lp.get("setup directory")
626 if used_setup_dir != default_setup_dir:
627 setupdir_line = "setup directory = %s" % used_setup_dir
628 lp.set("setup directory", used_setup_dir)
630 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
631 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
633 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
635 "NETBIOS_NAME": netbiosname,
638 "SERVERROLE": serverrole,
639 "NETLOGONPATH": netlogon,
640 "SYSVOLPATH": sysvol,
641 "SETUPDIRECTORY_LINE": setupdir_line,
642 "SIDGENERATOR_LINE": sid_generator_line,
643 "PRIVATEDIR_LINE": privatedir_line,
644 "LOCKDIR_LINE": lockdir_line
647 # reload the smb.conf
650 # and dump it without any values that are the default
651 # this ensures that any smb.conf parameters that were set
652 # on the provision/join command line are set in the resulting smb.conf
653 f = open(smbconf, mode='w')
659 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
660 users_gid, wheel_gid):
661 """setup reasonable name mappings for sam names to unix names.
663 :param samdb: SamDB object.
664 :param idmap: IDmap db object.
665 :param sid: The domain sid.
666 :param domaindn: The domain DN.
667 :param root_uid: uid of the UNIX root user.
668 :param nobody_uid: uid of the UNIX nobody user.
669 :param users_gid: gid of the UNIX users group.
670 :param wheel_gid: gid of the UNIX wheel group.
672 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
673 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
675 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
676 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
679 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
680 provision_backend, names, schema, serverrole,
682 """Setup the partitions for the SAM database.
684 Alternatively, provision() may call this, and then populate the database.
686 :note: This will wipe the Sam Database!
688 :note: This function always removes the local SAM LDB file. The erase
689 parameter controls whether to erase the existing data, which
690 may not be stored locally but in LDAP.
693 assert session_info is not None
695 # We use options=["modules:"] to stop the modules loading - we
696 # just want to wipe and re-initialise the database, not start it up
699 os.unlink(samdb_path)
703 samdb = Ldb(url=samdb_path, session_info=session_info,
704 lp=lp, options=["modules:"])
706 ldap_backend_line = "# No LDAP backend"
707 if provision_backend.type is not "ldb":
708 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
710 samdb.transaction_start()
712 logger.info("Setting up sam.ldb partitions and settings")
713 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
714 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
715 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
716 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
717 "LDAP_BACKEND_LINE": ldap_backend_line,
721 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
722 "BACKEND_TYPE": provision_backend.type,
723 "SERVER_ROLE": serverrole
726 logger.info("Setting up sam.ldb rootDSE")
727 setup_samdb_rootdse(samdb, setup_path, names)
729 samdb.transaction_cancel()
732 samdb.transaction_commit()
735 def secretsdb_self_join(secretsdb, domain,
736 netbiosname, machinepass, domainsid=None,
737 realm=None, dnsdomain=None,
739 key_version_number=1,
740 secure_channel_type=SEC_CHAN_WKSTA):
741 """Add domain join-specific bits to a secrets database.
743 :param secretsdb: Ldb Handle to the secrets database
744 :param machinepass: Machine password
746 attrs = ["whenChanged",
753 if realm is not None:
754 if dnsdomain is None:
755 dnsdomain = realm.lower()
756 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
759 shortname = netbiosname.lower()
761 # We don't need to set msg["flatname"] here, because rdn_name will handle
762 # it, and it causes problems for modifies anyway
763 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
764 msg["secureChannelType"] = [str(secure_channel_type)]
765 msg["objectClass"] = ["top", "primaryDomain"]
766 if dnsname is not None:
767 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
768 msg["realm"] = [realm]
769 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
770 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
771 msg["privateKeytab"] = ["secrets.keytab"]
773 msg["secret"] = [machinepass]
774 msg["samAccountName"] = ["%s$" % netbiosname]
775 msg["secureChannelType"] = [str(secure_channel_type)]
776 if domainsid is not None:
777 msg["objectSid"] = [ndr_pack(domainsid)]
779 # This complex expression tries to ensure that we don't have more
780 # than one record for this SID, realm or netbios domain at a time,
781 # but we don't delete the old record that we are about to modify,
782 # because that would delete the keytab and previous password.
783 res = secretsdb.search(base="cn=Primary Domains",
785 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
786 scope=ldb.SCOPE_ONELEVEL)
789 secretsdb.delete(del_msg.dn)
791 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
794 msg["priorSecret"] = [res[0]["secret"][0]]
795 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
798 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
803 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
809 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
810 secretsdb.modify(msg)
811 secretsdb.rename(res[0].dn, msg.dn)
813 spn = [ 'HOST/%s' % shortname ]
814 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
815 # we are a domain controller then we add servicePrincipalName
816 # entries for the keytab code to update.
817 spn.extend([ 'HOST/%s' % dnsname ])
818 msg["servicePrincipalName"] = spn
823 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir, realm,
824 dnsdomain, dns_keytab_path, dnspass):
825 """Add DNS specific bits to a secrets database.
827 :param secretsdb: Ldb Handle to the secrets database
828 :param setup_path: Setup path function
829 :param machinepass: Machine password
832 os.unlink(os.path.join(private_dir, dns_keytab_path))
836 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
838 "DNSDOMAIN": dnsdomain,
839 "DNS_KEYTAB": dns_keytab_path,
840 "DNSPASS_B64": b64encode(dnspass),
841 "HOSTNAME": names.hostname,
842 "DNSNAME" : '%s.%s' % (
843 names.netbiosname.lower(), names.dnsdomain.lower())
847 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
848 """Setup the secrets database.
850 :note: This function does not handle exceptions and transaction on purpose,
851 it's up to the caller to do this job.
853 :param path: Path to the secrets database.
854 :param setup_path: Get the path to a setup file.
855 :param session_info: Session info.
856 :param credentials: Credentials
857 :param lp: Loadparm context
858 :return: LDB handle for the created secrets database
860 if os.path.exists(paths.secrets):
861 os.unlink(paths.secrets)
863 keytab_path = os.path.join(paths.private_dir, paths.keytab)
864 if os.path.exists(keytab_path):
865 os.unlink(keytab_path)
867 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
868 if os.path.exists(dns_keytab_path):
869 os.unlink(dns_keytab_path)
873 secrets_ldb = Ldb(path, session_info=session_info,
876 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
877 secrets_ldb = Ldb(path, session_info=session_info,
879 secrets_ldb.transaction_start()
881 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
883 if (backend_credentials is not None and
884 backend_credentials.authentication_requested()):
885 if backend_credentials.get_bind_dn() is not None:
886 setup_add_ldif(secrets_ldb,
887 setup_path("secrets_simple_ldap.ldif"), {
888 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
889 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
892 setup_add_ldif(secrets_ldb,
893 setup_path("secrets_sasl_ldap.ldif"), {
894 "LDAPADMINUSER": backend_credentials.get_username(),
895 "LDAPADMINREALM": backend_credentials.get_realm(),
896 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
901 secrets_ldb.transaction_cancel()
905 def setup_privileges(path, setup_path, session_info, lp):
906 """Setup the privileges database.
908 :param path: Path to the privileges database.
909 :param setup_path: Get the path to a setup file.
910 :param session_info: Session info.
911 :param credentials: Credentials
912 :param lp: Loadparm context
913 :return: LDB handle for the created secrets database
915 if os.path.exists(path):
917 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
918 privilege_ldb.erase()
919 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
922 def setup_registry(path, setup_path, session_info, lp):
923 """Setup the registry.
925 :param path: Path to the registry database
926 :param setup_path: Function that returns the path to a setup.
927 :param session_info: Session information
928 :param credentials: Credentials
929 :param lp: Loadparm context
931 reg = samba.registry.Registry()
932 hive = samba.registry.open_ldb(path, session_info=session_info,
934 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
935 provision_reg = setup_path("provision.reg")
936 assert os.path.exists(provision_reg)
937 reg.diff_apply(provision_reg)
940 def setup_idmapdb(path, setup_path, session_info, lp):
941 """Setup the idmap database.
943 :param path: path to the idmap database
944 :param setup_path: Function that returns a path to a setup file
945 :param session_info: Session information
946 :param credentials: Credentials
947 :param lp: Loadparm context
949 if os.path.exists(path):
952 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
954 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
958 def setup_samdb_rootdse(samdb, setup_path, names):
959 """Setup the SamDB rootdse.
961 :param samdb: Sam Database handle
962 :param setup_path: Obtain setup path
964 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
965 "SCHEMADN": names.schemadn,
966 "DOMAINDN": names.domaindn,
967 "ROOTDN": names.rootdn,
968 "CONFIGDN": names.configdn,
969 "SERVERDN": names.serverdn,
973 def setup_self_join(samdb, names,
974 machinepass, dnspass,
975 domainsid, next_rid, invocationid, setup_path,
976 policyguid, policyguid_dc, domainControllerFunctionality,
978 """Join a host to its own domain."""
979 assert isinstance(invocationid, str)
980 if ntdsguid is not None:
981 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
984 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
985 "CONFIGDN": names.configdn,
986 "SCHEMADN": names.schemadn,
987 "DOMAINDN": names.domaindn,
988 "SERVERDN": names.serverdn,
989 "INVOCATIONID": invocationid,
990 "NETBIOSNAME": names.netbiosname,
991 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
992 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
993 "DOMAINSID": str(domainsid),
994 "DCRID": str(next_rid),
995 "SAMBA_VERSION_STRING": version,
996 "NTDSGUID": ntdsguid_line,
997 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
998 domainControllerFunctionality)})
1000 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1001 "POLICYGUID": policyguid,
1002 "POLICYGUID_DC": policyguid_dc,
1003 "DNSDOMAIN": names.dnsdomain,
1004 "DOMAINDN": names.domaindn})
1006 # add the NTDSGUID based SPNs
1007 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1008 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1009 expression="", scope=ldb.SCOPE_BASE)
1010 assert isinstance(names.ntdsguid, str)
1012 # Setup fSMORoleOwner entries to point at the newly created DC entry
1013 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1014 "DOMAINDN": names.domaindn,
1015 "CONFIGDN": names.configdn,
1016 "SCHEMADN": names.schemadn,
1017 "DEFAULTSITE": names.sitename,
1018 "SERVERDN": names.serverdn,
1019 "NETBIOSNAME": names.netbiosname,
1020 "RIDALLOCATIONSTART": str(next_rid + 100),
1021 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1024 # This is partially Samba4 specific and should be replaced by the correct
1025 # DNS AD-style setup
1026 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1027 "DNSDOMAIN": names.dnsdomain,
1028 "DOMAINDN": names.domaindn,
1029 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1030 "HOSTNAME" : names.hostname,
1031 "DNSNAME" : '%s.%s' % (
1032 names.netbiosname.lower(), names.dnsdomain.lower())
1036 def getpolicypath(sysvolpath, dnsdomain, guid):
1037 """Return the physical path of policy given its guid.
1039 :param sysvolpath: Path to the sysvol folder
1040 :param dnsdomain: DNS name of the AD domain
1041 :param guid: The GUID of the policy
1042 :return: A string with the complete path to the policy folder
1046 guid = "{%s}" % guid
1047 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1051 def create_gpo_struct(policy_path):
1052 if not os.path.exists(policy_path):
1053 os.makedirs(policy_path, 0775)
1054 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1055 "[General]\r\nVersion=0")
1056 p = os.path.join(policy_path, "MACHINE")
1057 if not os.path.exists(p):
1058 os.makedirs(p, 0775)
1059 p = os.path.join(policy_path, "USER")
1060 if not os.path.exists(p):
1061 os.makedirs(p, 0775)
1064 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1065 """Create the default GPO for a domain
1067 :param sysvolpath: Physical path for the sysvol folder
1068 :param dnsdomain: DNS domain name of the AD domain
1069 :param policyguid: GUID of the default domain policy
1070 :param policyguid_dc: GUID of the default domain controler policy
1072 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1073 create_gpo_struct(policy_path)
1075 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1076 create_gpo_struct(policy_path)
1079 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1080 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1081 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1082 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1084 """Setup a complete SAM Database.
1086 :note: This will wipe the main SAM database file!
1089 # Provision does not make much sense values larger than 1000000000
1090 # as the upper range of the rIDAvailablePool is 1073741823 and
1091 # we don't want to create a domain that cannot allocate rids.
1092 if next_rid < 1000 or next_rid > 1000000000:
1093 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1094 error += "the valid range is %u-%u. The default is %u." % (
1095 1000, 1000000000, 1000)
1096 raise ProvisioningError(error)
1098 # ATTENTION: Do NOT change these default values without discussion with the
1099 # team and/or release manager. They have a big impact on the whole program!
1100 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1102 if dom_for_fun_level is None:
1103 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1105 if dom_for_fun_level > domainControllerFunctionality:
1106 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!")
1108 domainFunctionality = dom_for_fun_level
1109 forestFunctionality = dom_for_fun_level
1111 # Also wipes the database
1112 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1113 provision_backend=provision_backend, session_info=session_info,
1114 names=names, serverrole=serverrole, schema=schema)
1117 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1119 # Load the database, but don's load the global schema and don't connect
1121 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1122 credentials=provision_backend.credentials, lp=lp,
1123 global_schema=False, am_rodc=am_rodc)
1125 logger.info("Pre-loading the Samba 4 and AD schema")
1127 # Load the schema from the one we computed earlier
1128 samdb.set_schema(schema)
1130 # Set the NTDS settings DN manually - in order to have it already around
1131 # before the provisioned tree exists and we connect
1132 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1134 # And now we can connect to the DB - the schema won't be loaded from the
1138 if fill == FILL_DRS:
1141 samdb.transaction_start()
1143 # Set the domain functionality levels onto the database.
1144 # Various module (the password_hash module in particular) need
1145 # to know what level of AD we are emulating.
1147 # These will be fixed into the database via the database
1148 # modifictions below, but we need them set from the start.
1149 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1150 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1151 samdb.set_opaque_integer("domainControllerFunctionality",
1152 domainControllerFunctionality)
1154 samdb.set_domain_sid(str(domainsid))
1155 samdb.set_invocation_id(invocationid)
1157 logger.info("Adding DomainDN: %s" % names.domaindn)
1159 # impersonate domain admin
1160 admin_session_info = admin_session(lp, str(domainsid))
1161 samdb.set_session_info(admin_session_info)
1162 if domainguid is not None:
1163 domainguid_line = "objectGUID: %s\n-" % domainguid
1165 domainguid_line = ""
1167 descr = b64encode(get_domain_descriptor(domainsid))
1168 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1169 "DOMAINDN": names.domaindn,
1170 "DOMAINSID": str(domainsid),
1171 "DESCRIPTOR": descr,
1172 "DOMAINGUID": domainguid_line
1175 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1176 "DOMAINDN": names.domaindn,
1177 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1178 "NEXTRID": str(next_rid),
1179 "DEFAULTSITE": names.sitename,
1180 "CONFIGDN": names.configdn,
1181 "POLICYGUID": policyguid,
1182 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1183 "SAMBA_VERSION_STRING": version
1186 logger.info("Adding configuration container")
1187 descr = b64encode(get_config_descriptor(domainsid))
1188 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1189 "CONFIGDN": names.configdn,
1190 "DESCRIPTOR": descr,
1193 # The LDIF here was created when the Schema object was constructed
1194 logger.info("Setting up sam.ldb schema")
1195 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1196 samdb.modify_ldif(schema.schema_dn_modify)
1197 samdb.write_prefixes_from_schema()
1198 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1199 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1200 {"SCHEMADN": names.schemadn})
1202 logger.info("Reopening sam.ldb with new schema")
1204 samdb.transaction_cancel()
1207 samdb.transaction_commit()
1209 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1210 credentials=provision_backend.credentials, lp=lp,
1211 global_schema=False, am_rodc=am_rodc)
1213 # Set the NTDS settings DN manually - in order to have it already around
1214 # before the provisioned tree exists and we connect
1215 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1218 samdb.transaction_start()
1220 samdb.invocation_id = invocationid
1222 logger.info("Setting up sam.ldb configuration data")
1223 descr = b64encode(get_sites_descriptor(domainsid))
1224 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1225 "CONFIGDN": names.configdn,
1226 "NETBIOSNAME": names.netbiosname,
1227 "DEFAULTSITE": names.sitename,
1228 "DNSDOMAIN": names.dnsdomain,
1229 "DOMAIN": names.domain,
1230 "SCHEMADN": names.schemadn,
1231 "DOMAINDN": names.domaindn,
1232 "SERVERDN": names.serverdn,
1233 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1234 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1235 "SITES_DESCRIPTOR": descr
1238 logger.info("Setting up display specifiers")
1239 display_specifiers_ldif = read_ms_ldif(
1240 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1241 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1242 {"CONFIGDN": names.configdn})
1243 check_all_substituted(display_specifiers_ldif)
1244 samdb.add_ldif(display_specifiers_ldif)
1246 logger.info("Adding users container")
1247 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1248 "DOMAINDN": names.domaindn})
1249 logger.info("Modifying users container")
1250 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1251 "DOMAINDN": names.domaindn})
1252 logger.info("Adding computers container")
1253 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1254 "DOMAINDN": names.domaindn})
1255 logger.info("Modifying computers container")
1256 setup_modify_ldif(samdb,
1257 setup_path("provision_computers_modify.ldif"), {
1258 "DOMAINDN": names.domaindn})
1259 logger.info("Setting up sam.ldb data")
1260 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1261 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1262 "DOMAINDN": names.domaindn,
1263 "NETBIOSNAME": names.netbiosname,
1264 "DEFAULTSITE": names.sitename,
1265 "CONFIGDN": names.configdn,
1266 "SERVERDN": names.serverdn,
1267 "RIDAVAILABLESTART": str(next_rid + 600),
1268 "POLICYGUID_DC": policyguid_dc
1271 setup_modify_ldif(samdb,
1272 setup_path("provision_basedn_references.ldif"), {
1273 "DOMAINDN": names.domaindn})
1275 setup_modify_ldif(samdb,
1276 setup_path("provision_configuration_references.ldif"), {
1277 "CONFIGDN": names.configdn,
1278 "SCHEMADN": names.schemadn})
1279 if fill == FILL_FULL:
1280 logger.info("Setting up sam.ldb users and groups")
1281 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1282 "DOMAINDN": names.domaindn,
1283 "DOMAINSID": str(domainsid),
1284 "CONFIGDN": names.configdn,
1285 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1286 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1289 logger.info("Setting up self join")
1290 setup_self_join(samdb, names=names, invocationid=invocationid,
1292 machinepass=machinepass,
1293 domainsid=domainsid,
1295 policyguid=policyguid,
1296 policyguid_dc=policyguid_dc,
1297 setup_path=setup_path,
1298 domainControllerFunctionality=domainControllerFunctionality,
1301 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1302 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1303 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1304 assert isinstance(names.ntdsguid, str)
1306 samdb.transaction_cancel()
1309 samdb.transaction_commit()
1314 FILL_NT4SYNC = "NT4SYNC"
1316 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1317 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)"
1319 def set_dir_acl(path, acl, lp, domsid):
1320 setntacl(lp, path, acl, domsid)
1321 for root, dirs, files in os.walk(path, topdown=False):
1323 setntacl(lp, os.path.join(root, name), acl, domsid)
1325 setntacl(lp, os.path.join(root, name), acl, domsid)
1328 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1329 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1332 :param sysvol: Physical path for the sysvol folder
1333 :param dnsdomain: The DNS name of the domain
1334 :param domainsid: The SID of the domain
1335 :param domaindn: The DN of the domain (ie. DC=...)
1336 :param samdb: An LDB object on the SAM db
1337 :param lp: an LP object
1340 # Set ACL for GPO root folder
1341 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1342 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1344 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1345 attrs=["cn", "nTSecurityDescriptor"],
1346 expression="", scope=ldb.SCOPE_ONELEVEL)
1349 acl = ndr_unpack(security.descriptor,
1350 str(policy["nTSecurityDescriptor"])).as_sddl()
1351 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1352 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1356 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1358 """Set the ACL for the sysvol share and the subfolders
1360 :param samdb: An LDB object on the SAM db
1361 :param netlogon: Physical path for the netlogon folder
1362 :param sysvol: Physical path for the sysvol folder
1363 :param gid: The GID of the "Domain adminstrators" group
1364 :param domainsid: The SID of the domain
1365 :param dnsdomain: The DNS name of the domain
1366 :param domaindn: The DN of the domain (ie. DC=...)
1370 os.chown(sysvol, -1, gid)
1376 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1377 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1378 for root, dirs, files in os.walk(sysvol, topdown=False):
1381 os.chown(os.path.join(root, name), -1, gid)
1382 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1385 os.chown(os.path.join(root, name), -1, gid)
1386 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1388 # Set acls on Policy folder and policies folders
1389 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1392 def provision(setup_dir, logger, session_info, credentials, smbconf=None,
1393 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1394 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1395 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1396 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1397 domainguid=None, policyguid=None, policyguid_dc=None,
1398 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1399 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1400 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1401 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1402 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1403 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1407 :note: caution, this wipes all existing data!
1410 def setup_path(file):
1411 return os.path.join(setup_dir, file)
1413 if domainsid is None:
1414 domainsid = security.random_sid()
1416 domainsid = security.dom_sid(domainsid)
1418 # create/adapt the group policy GUIDs
1419 # Default GUID for default policy are described at
1420 # "How Core Group Policy Works"
1421 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1422 if policyguid is None:
1423 policyguid = DEFAULT_POLICY_GUID
1424 policyguid = policyguid.upper()
1425 if policyguid_dc is None:
1426 policyguid_dc = DEFAULT_DC_POLICY_GUID
1427 policyguid_dc = policyguid_dc.upper()
1429 if adminpass is None:
1430 adminpass = samba.generate_random_password(12, 32)
1431 if krbtgtpass is None:
1432 krbtgtpass = samba.generate_random_password(128, 255)
1433 if machinepass is None:
1434 machinepass = samba.generate_random_password(128, 255)
1436 dnspass = samba.generate_random_password(128, 255)
1437 if ldapadminpass is None:
1438 # Make a new, random password between Samba and it's LDAP server
1439 ldapadminpass=samba.generate_random_password(128, 255)
1441 if backend_type is None:
1442 backend_type = "ldb"
1444 sid_generator = "internal"
1445 if backend_type == "fedora-ds":
1446 sid_generator = "backend"
1448 root_uid = findnss_uid([root or "root"])
1449 nobody_uid = findnss_uid([nobody or "nobody"])
1450 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1452 wheel_gid = findnss_gid(["wheel", "adm"])
1454 wheel_gid = findnss_gid([wheel])
1456 bind_gid = findnss_gid(["bind", "named"])
1460 if targetdir is not None:
1461 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1462 elif smbconf is None:
1463 smbconf = samba.param.default_path()
1464 if not os.path.exists(os.path.dirname(smbconf)):
1465 os.makedirs(os.path.dirname(smbconf))
1467 # only install a new smb.conf if there isn't one there already
1468 if os.path.exists(smbconf):
1469 # if Samba Team members can't figure out the weird errors
1470 # loading an empty smb.conf gives, then we need to be smarter.
1471 # Pretend it just didn't exist --abartlet
1472 data = open(smbconf, 'r').read()
1473 data = data.lstrip()
1474 if data is None or data == "":
1475 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1476 serverrole, targetdir, sid_generator, useeadb,
1479 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1480 targetdir, sid_generator, useeadb, lp=lp)
1483 lp = samba.param.LoadParm()
1485 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1486 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1487 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1489 paths = provision_paths_from_lp(lp, names.dnsdomain)
1491 paths.bind_gid = bind_gid
1494 logger.info("Looking up IPv4 addresses")
1495 hostips = samba.interface_ips(lp, False)
1496 if len(hostips) == 0:
1497 logger.warning("No external IPv4 address has been found. Using loopback.")
1498 hostip = '127.0.0.1'
1501 if len(hostips) > 1:
1502 logger.warning("More than one IPv4 address found. Using %s.",
1505 if serverrole is None:
1506 serverrole = lp.get("server role")
1508 assert serverrole in ("domain controller", "member server", "standalone")
1509 if invocationid is None:
1510 invocationid = str(uuid.uuid4())
1512 if not os.path.exists(paths.private_dir):
1513 os.mkdir(paths.private_dir)
1514 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1515 os.mkdir(os.path.join(paths.private_dir, "tls"))
1517 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1519 schema = Schema(setup_path, domainsid, invocationid=invocationid,
1520 schemadn=names.schemadn)
1522 if backend_type == "ldb":
1523 provision_backend = LDBBackend(backend_type, paths=paths,
1524 setup_path=setup_path, lp=lp, credentials=credentials,
1525 names=names, logger=logger)
1526 elif backend_type == "existing":
1527 provision_backend = ExistingBackend(backend_type, paths=paths,
1528 setup_path=setup_path, lp=lp, credentials=credentials,
1529 names=names, logger=logger,
1530 ldap_backend_forced_uri=ldap_backend_forced_uri)
1531 elif backend_type == "fedora-ds":
1532 provision_backend = FDSBackend(backend_type, paths=paths,
1533 setup_path=setup_path, lp=lp, credentials=credentials,
1534 names=names, logger=logger, domainsid=domainsid,
1535 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1536 slapd_path=slapd_path,
1537 ldap_backend_extra_port=ldap_backend_extra_port,
1538 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1539 setup_ds_path=setup_ds_path,
1540 ldap_backend_forced_uri=ldap_backend_forced_uri)
1541 elif backend_type == "openldap":
1542 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1543 setup_path=setup_path, lp=lp, credentials=credentials,
1544 names=names, logger=logger, domainsid=domainsid,
1545 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1546 slapd_path=slapd_path,
1547 ldap_backend_extra_port=ldap_backend_extra_port,
1548 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1550 ldap_backend_forced_uri=ldap_backend_forced_uri)
1552 raise ValueError("Unknown LDAP backend type selected")
1554 provision_backend.init()
1555 provision_backend.start()
1557 # only install a new shares config db if there is none
1558 if not os.path.exists(paths.shareconf):
1559 logger.info("Setting up share.ldb")
1560 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1562 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1564 logger.info("Setting up secrets.ldb")
1565 secrets_ldb = setup_secretsdb(paths, setup_path,
1566 session_info=session_info,
1567 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1570 logger.info("Setting up the registry")
1571 setup_registry(paths.hklm, setup_path, session_info,
1574 logger.info("Setting up the privileges database")
1575 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1577 logger.info("Setting up idmap db")
1578 idmap = setup_idmapdb(paths.idmapdb, setup_path,
1579 session_info=session_info, lp=lp)
1581 logger.info("Setting up SAM db")
1582 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1583 provision_backend, lp, names, logger=logger,
1584 domainsid=domainsid, schema=schema, domainguid=domainguid,
1585 policyguid=policyguid, policyguid_dc=policyguid_dc,
1586 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1587 invocationid=invocationid, machinepass=machinepass,
1588 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1589 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1592 if serverrole == "domain controller":
1593 if paths.netlogon is None:
1594 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1595 logger.info("Please either remove %s or see the template at %s" %
1596 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1597 assert paths.netlogon is not None
1599 if paths.sysvol is None:
1600 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1601 " are configuring a DC.")
1602 logger.info("Please either remove %s or see the template at %s" %
1603 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1604 assert paths.sysvol is not None
1606 if not os.path.isdir(paths.netlogon):
1607 os.makedirs(paths.netlogon, 0755)
1609 if samdb_fill == FILL_FULL:
1610 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1611 root_uid=root_uid, nobody_uid=nobody_uid,
1612 users_gid=users_gid, wheel_gid=wheel_gid)
1614 if serverrole == "domain controller":
1615 # Set up group policies (domain policy and domain controller
1617 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1619 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1620 domainsid, names.dnsdomain, names.domaindn, lp)
1622 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1623 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1625 secretsdb_self_join(secrets_ldb, domain=names.domain,
1626 realm=names.realm, dnsdomain=names.dnsdomain,
1627 netbiosname=names.netbiosname, domainsid=domainsid,
1628 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1630 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1631 # In future, this might be determined from some configuration
1632 kerberos_enctypes = str(ENC_ALL_TYPES)
1635 msg = ldb.Message(ldb.Dn(samdb,
1636 samdb.searchone("distinguishedName",
1637 expression="samAccountName=%s$" % names.netbiosname,
1638 scope=ldb.SCOPE_SUBTREE)))
1639 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1640 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1641 name="msDS-SupportedEncryptionTypes")
1643 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1644 # It might be that this attribute does not exist in this schema
1647 if serverrole == "domain controller":
1648 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1649 paths.private_dir, realm=names.realm,
1650 dnsdomain=names.dnsdomain,
1651 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1653 domainguid = samdb.searchone(basedn=domaindn,
1654 attribute="objectGUID")
1655 assert isinstance(domainguid, str)
1657 # Only make a zone file on the first DC, it should be
1658 # replicated with DNS replication
1659 create_zone_file(lp, logger, paths, targetdir, setup_path,
1660 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1661 hostname=names.hostname, realm=names.realm,
1662 domainguid=domainguid, ntdsguid=names.ntdsguid)
1664 create_named_conf(paths, setup_path, realm=names.realm,
1665 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1667 create_named_txt(paths.namedtxt, setup_path,
1668 realm=names.realm, dnsdomain=names.dnsdomain,
1669 private_dir=paths.private_dir,
1670 keytab_name=paths.dns_keytab)
1671 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1672 logger.info("and %s for further documentation required for secure DNS "
1673 "updates", paths.namedtxt)
1675 lastProvisionUSNs = get_last_provision_usn(samdb)
1676 maxUSN = get_max_usn(samdb, str(names.rootdn))
1677 if lastProvisionUSNs is not None:
1678 update_provision_usn(samdb, 0, maxUSN, 1)
1680 set_provision_usn(samdb, 0, maxUSN)
1682 create_krb5_conf(paths.krb5conf, setup_path,
1683 dnsdomain=names.dnsdomain, hostname=names.hostname,
1685 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1686 "generated at %s", paths.krb5conf)
1688 if serverrole == "domain controller":
1689 create_dns_update_list(lp, logger, paths, setup_path)
1691 provision_backend.post_setup()
1692 provision_backend.shutdown()
1694 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1697 secrets_ldb.transaction_cancel()
1700 # Now commit the secrets.ldb to disk
1701 secrets_ldb.transaction_commit()
1703 # the commit creates the dns.keytab, now chown it
1704 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1705 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1707 os.chmod(dns_keytab_path, 0640)
1708 os.chown(dns_keytab_path, -1, paths.bind_gid)
1710 if not os.environ.has_key('SAMBA_SELFTEST'):
1711 logger.info("Failed to chown %s to bind gid %u",
1712 dns_keytab_path, paths.bind_gid)
1715 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1716 paths.phpldapadminconfig)
1718 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1719 logger.info("Server Role: %s" % serverrole)
1720 logger.info("Hostname: %s" % names.hostname)
1721 logger.info("NetBIOS Domain: %s" % names.domain)
1722 logger.info("DNS Domain: %s" % names.dnsdomain)
1723 logger.info("DOMAIN SID: %s" % str(domainsid))
1724 if samdb_fill == FILL_FULL:
1725 logger.info("Admin password: %s" % adminpass)
1726 if provision_backend.type is not "ldb":
1727 if provision_backend.credentials.get_bind_dn() is not None:
1728 logger.info("LDAP Backend Admin DN: %s" %
1729 provision_backend.credentials.get_bind_dn())
1731 logger.info("LDAP Admin User: %s" %
1732 provision_backend.credentials.get_username())
1734 logger.info("LDAP Admin Password: %s" %
1735 provision_backend.credentials.get_password())
1737 if provision_backend.slapd_command_escaped is not None:
1738 # now display slapd_command_file.txt to show how slapd must be
1740 logger.info("Use later the following commandline to start slapd, then Samba:")
1741 logger.info(provision_backend.slapd_command_escaped)
1742 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1743 provision_backend.ldapdir)
1745 result = ProvisionResult()
1746 result.domaindn = domaindn
1747 result.paths = paths
1749 result.samdb = samdb
1753 def provision_become_dc(setup_dir=None, smbconf=None, targetdir=None,
1754 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1755 serverdn=None, domain=None, hostname=None, domainsid=None,
1756 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1757 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1758 root=None, nobody=None, users=None, wheel=None, backup=None,
1759 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1760 sitename=None, debuglevel=1):
1762 logger = logging.getLogger("provision")
1763 samba.set_debug_level(debuglevel)
1765 res = provision(setup_dir, logger, system_session(), None,
1766 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1767 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1768 configdn=configdn, serverdn=serverdn, domain=domain,
1769 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1770 machinepass=machinepass, serverrole="domain controller",
1772 res.lp.set("debuglevel", str(debuglevel))
1776 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1777 """Create a PHP LDAP admin configuration file.
1779 :param path: Path to write the configuration to.
1780 :param setup_path: Function to generate setup paths.
1782 setup_file(setup_path("phpldapadmin-config.php"), path,
1783 {"S4_LDAPI_URI": ldapi_uri})
1786 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1787 hostip, hostip6, hostname, realm, domainguid,
1789 """Write out a DNS zone file, from the info in the current database.
1791 :param paths: paths object
1792 :param setup_path: Setup path function.
1793 :param dnsdomain: DNS Domain name
1794 :param domaindn: DN of the Domain
1795 :param hostip: Local IPv4 IP
1796 :param hostip6: Local IPv6 IP
1797 :param hostname: Local hostname
1798 :param realm: Realm name
1799 :param domainguid: GUID of the domain.
1800 :param ntdsguid: GUID of the hosts nTDSDSA record.
1802 assert isinstance(domainguid, str)
1804 if hostip6 is not None:
1805 hostip6_base_line = " IN AAAA " + hostip6
1806 hostip6_host_line = hostname + " IN AAAA " + hostip6
1807 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1809 hostip6_base_line = ""
1810 hostip6_host_line = ""
1811 gc_msdcs_ip6_line = ""
1813 if hostip is not None:
1814 hostip_base_line = " IN A " + hostip
1815 hostip_host_line = hostname + " IN A " + hostip
1816 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1818 hostip_base_line = ""
1819 hostip_host_line = ""
1820 gc_msdcs_ip_line = ""
1822 dns_dir = os.path.dirname(paths.dns)
1825 shutil.rmtree(dns_dir, True)
1829 os.mkdir(dns_dir, 0775)
1831 # we need to freeze the zone while we update the contents
1832 if targetdir is None:
1833 rndc = ' '.join(lp.get("rndc command"))
1834 os.system(rndc + " freeze " + lp.get("realm"))
1836 setup_file(setup_path("provision.zone"), paths.dns, {
1837 "HOSTNAME": hostname,
1838 "DNSDOMAIN": dnsdomain,
1840 "HOSTIP_BASE_LINE": hostip_base_line,
1841 "HOSTIP_HOST_LINE": hostip_host_line,
1842 "DOMAINGUID": domainguid,
1843 "DATESTRING": time.strftime("%Y%m%d%H"),
1844 "DEFAULTSITE": DEFAULTSITE,
1845 "NTDSGUID": ntdsguid,
1846 "HOSTIP6_BASE_LINE": hostip6_base_line,
1847 "HOSTIP6_HOST_LINE": hostip6_host_line,
1848 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1849 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1852 # note that we use no variable substitution on this file
1853 # the substitution is done at runtime by samba_dnsupdate
1854 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1856 # and the SPN update list
1857 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1859 if paths.bind_gid is not None:
1861 os.chown(dns_dir, -1, paths.bind_gid)
1862 os.chown(paths.dns, -1, paths.bind_gid)
1863 # chmod needed to cope with umask
1864 os.chmod(dns_dir, 0775)
1865 os.chmod(paths.dns, 0664)
1867 if not os.environ.has_key('SAMBA_SELFTEST'):
1868 logger.error("Failed to chown %s to bind gid %u" % (
1869 dns_dir, paths.bind_gid))
1871 if targetdir is None:
1872 os.system(rndc + " unfreeze " + lp.get("realm"))
1875 def create_dns_update_list(lp, logger, paths, setup_path):
1876 """Write out a dns_update_list file"""
1877 # note that we use no variable substitution on this file
1878 # the substitution is done at runtime by samba_dnsupdate
1879 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1880 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1883 def create_named_conf(paths, setup_path, realm, dnsdomain,
1885 """Write out a file containing zone statements suitable for inclusion in a
1886 named.conf file (including GSS-TSIG configuration).
1888 :param paths: all paths
1889 :param setup_path: Setup path function.
1890 :param realm: Realm name
1891 :param dnsdomain: DNS Domain name
1892 :param private_dir: Path to private directory
1893 :param keytab_name: File name of DNS keytab file
1896 setup_file(setup_path("named.conf"), paths.namedconf, {
1897 "DNSDOMAIN": dnsdomain,
1899 "ZONE_FILE": paths.dns,
1900 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1901 "NAMED_CONF": paths.namedconf,
1902 "NAMED_CONF_UPDATE": paths.namedconf_update
1905 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1908 def create_named_txt(path, setup_path, realm, dnsdomain, private_dir,
1910 """Write out a file containing zone statements suitable for inclusion in a
1911 named.conf file (including GSS-TSIG configuration).
1913 :param path: Path of the new named.conf file.
1914 :param setup_path: Setup path function.
1915 :param realm: Realm name
1916 :param dnsdomain: DNS Domain name
1917 :param private_dir: Path to private directory
1918 :param keytab_name: File name of DNS keytab file
1920 setup_file(setup_path("named.txt"), path, {
1921 "DNSDOMAIN": dnsdomain,
1923 "DNS_KEYTAB": keytab_name,
1924 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1925 "PRIVATE_DIR": private_dir
1929 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1930 """Write out a file containing zone statements suitable for inclusion in a
1931 named.conf file (including GSS-TSIG configuration).
1933 :param path: Path of the new named.conf file.
1934 :param setup_path: Setup path function.
1935 :param dnsdomain: DNS Domain name
1936 :param hostname: Local hostname
1937 :param realm: Realm name
1939 setup_file(setup_path("krb5.conf"), path, {
1940 "DNSDOMAIN": dnsdomain,
1941 "HOSTNAME": hostname,
1946 class ProvisioningError(Exception):
1947 """A generic provision error."""
1949 def __init__(self, value):
1953 return "ProvisioningError: " + self.value
1956 class InvalidNetbiosName(Exception):
1957 """A specified name was not a valid NetBIOS name."""
1958 def __init__(self, name):
1959 super(InvalidNetbiosName, self).__init__(
1960 "The name '%r' is not a valid NetBIOS name" % name)