2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
42 from samba.auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name
45 from samba import check_all_substituted, read_and_sub_file, setup_file
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008_R2, ENC_ALL_TYPES
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ms_display_specifiers import read_ms_ldif
51 from samba.ntacls import setntacl, dsacl2fsacl
52 from samba.ndr import ndr_pack,ndr_unpack
53 from samba.provisionbackend import (
61 from samba.schema import Schema
62 from samba.samdb import SamDB
64 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
65 __docformat__ = "restructuredText"
66 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
67 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
70 """Find the setup directory used by provision."""
72 for prefix in [sys.prefix,
73 os.path.join(os.path.dirname(__file__), "../../../..")]:
74 for suffix in ["share/setup", "share/samba/setup", "setup"]:
75 ret = os.path.join(prefix, suffix)
76 if os.path.isdir(ret):
79 dirname = os.path.dirname(__file__)
80 ret = os.path.join(dirname, "../../../setup")
81 if os.path.isdir(ret):
83 raise Exception("Unable to find setup directory.")
85 # Descriptors of naming contexts and other important objects
87 # "get_schema_descriptor" is located in "schema.py"
89 def get_sites_descriptor(domain_sid):
90 sddl = "O:EAG:EAD:AI(A;;RPLCLORC;;;AU)" \
91 "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
92 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
93 "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
94 "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
95 "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
96 "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
97 "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
98 "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
99 "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
100 sec = security.descriptor.from_sddl(sddl, domain_sid)
103 def get_config_descriptor(domain_sid):
104 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
105 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
106 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
107 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
108 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
109 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
110 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
111 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
112 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
114 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
116 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
117 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
118 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
119 sec = security.descriptor.from_sddl(sddl, domain_sid)
122 def get_domain_descriptor(domain_sid):
123 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
124 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
125 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
126 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
127 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
128 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
129 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
130 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
131 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
132 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
133 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
134 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
135 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
136 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
137 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
138 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
139 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
142 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
143 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
144 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
145 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
146 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
147 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
148 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
149 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
150 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
151 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
152 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
153 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
154 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
156 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
157 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
158 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
159 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
160 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
161 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
164 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
166 "(A;;RPLCLORC;;;ED)" \
167 "(A;;RPLCLORC;;;AU)" \
168 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
169 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
170 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
171 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
172 sec = security.descriptor.from_sddl(sddl, domain_sid)
175 DEFAULTSITE = "Default-First-Site-Name"
176 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
178 class ProvisionPaths(object):
181 self.shareconf = None
192 self.dns_keytab = None
195 self.private_dir = None
198 class ProvisionNames(object):
205 self.ldapmanagerdn = None
206 self.dnsdomain = None
208 self.netbiosname = None
215 def update_provision_usn(samdb, low, high, replace=False):
216 """Update the field provisionUSN in sam.ldb
218 This field is used to track range of USN modified by provision and
220 This value is used afterward by next provision to figure out if
221 the field have been modified since last provision.
223 :param samdb: An LDB object connect to sam.ldb
224 :param low: The lowest USN modified by this upgrade
225 :param high: The highest USN modified by this upgrade
226 :param replace: A boolean indicating if the range should replace any
227 existing one or appended (default)
232 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % \
233 LAST_PROVISION_USN_ATTRIBUTE, base="",
234 scope=ldb.SCOPE_SUBTREE,
235 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
236 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
239 tab.append("%s-%s" % (low, high))
240 delta = ldb.Message()
241 delta.dn = ldb.Dn(samdb, "@PROVISION")
242 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
243 ldb.FLAG_MOD_REPLACE,
244 LAST_PROVISION_USN_ATTRIBUTE)
248 def set_provision_usn(samdb, low, high):
249 """Set the field provisionUSN in sam.ldb
250 This field is used to track range of USN modified by provision and
252 This value is used afterward by next provision to figure out if
253 the field have been modified since last provision.
255 :param samdb: An LDB object connect to sam.ldb
256 :param low: The lowest USN modified by this upgrade
257 :param high: The highest USN modified by this upgrade"""
259 tab.append("%s-%s" % (low, high))
260 delta = ldb.Message()
261 delta.dn = ldb.Dn(samdb, "@PROVISION")
262 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
264 LAST_PROVISION_USN_ATTRIBUTE)
268 def get_max_usn(samdb,basedn):
269 """ This function return the biggest USN present in the provision
271 :param samdb: A LDB object pointing to the sam.ldb
272 :param basedn: A string containing the base DN of the provision
274 :return: The biggest USN in the provision"""
276 res = samdb.search(expression="objectClass=*",base=basedn,
277 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
278 controls=["search_options:1:2",
279 "server_sort:1:1:uSNChanged",
280 "paged_results:1:1"])
281 return res[0]["uSNChanged"]
283 def get_last_provision_usn(sam):
284 """Get the lastest USN modified by a provision or an upgradeprovision
286 :param sam: An LDB object pointing to the sam.ldb
287 :return an integer corresponding to the highest USN modified by
288 (upgrade)provision, 0 is this value is unknown"""
290 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % \
291 LAST_PROVISION_USN_ATTRIBUTE,
292 base="", scope=ldb.SCOPE_SUBTREE,
293 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
298 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
299 tab = p.split(str(r))
307 class ProvisionResult(object):
316 def check_install(lp, session_info, credentials):
317 """Check whether the current install seems ok.
319 :param lp: Loadparm context
320 :param session_info: Session information
321 :param credentials: Credentials
323 if lp.get("realm") == "":
324 raise Exception("Realm empty")
325 samdb = Ldb(lp.get("sam database"), session_info=session_info,
326 credentials=credentials, lp=lp)
327 if len(samdb.search("(cn=Administrator)")) != 1:
328 raise ProvisioningError("No administrator account found")
331 def findnss(nssfn, names):
332 """Find a user or group from a list of possibilities.
334 :param nssfn: NSS Function to try (should raise KeyError if not found)
335 :param names: Names to check.
336 :return: Value return by first names list.
343 raise KeyError("Unable to find user/group in %r" % names)
346 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
347 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
350 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
351 """Setup a ldb in the private dir.
353 :param ldb: LDB file to import data into
354 :param ldif_path: Path of the LDIF file to load
355 :param subst_vars: Optional variables to subsitute in LDIF.
356 :param nocontrols: Optional list of controls, can be None for no controls
358 assert isinstance(ldif_path, str)
359 data = read_and_sub_file(ldif_path, subst_vars)
360 ldb.add_ldif(data, controls)
363 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
364 """Modify a ldb in the private dir.
366 :param ldb: LDB object.
367 :param ldif_path: LDIF file path.
368 :param subst_vars: Optional dictionary with substitution variables.
370 data = read_and_sub_file(ldif_path, subst_vars)
371 ldb.modify_ldif(data, controls)
374 def setup_ldb(ldb, ldif_path, subst_vars):
375 """Import a LDIF a file into a LDB handle, optionally substituting variables.
377 :note: Either all LDIF data will be added or none (using transactions).
379 :param ldb: LDB file to import into.
380 :param ldif_path: Path to the LDIF file.
381 :param subst_vars: Dictionary with substitution variables.
383 assert ldb is not None
384 ldb.transaction_start()
386 setup_add_ldif(ldb, ldif_path, subst_vars)
388 ldb.transaction_cancel()
391 ldb.transaction_commit()
394 def provision_paths_from_lp(lp, dnsdomain):
395 """Set the default paths for provisioning.
397 :param lp: Loadparm context.
398 :param dnsdomain: DNS Domain name
400 paths = ProvisionPaths()
401 paths.private_dir = lp.get("private dir")
403 # This is stored without path prefix for the "privateKeytab" attribute in
404 # "secrets_dns.ldif".
405 paths.dns_keytab = "dns.keytab"
406 paths.keytab = "secrets.keytab"
408 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
409 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
410 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
411 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
412 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
413 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
414 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
415 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
416 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
417 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
418 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
419 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
420 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
421 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
422 paths.phpldapadminconfig = os.path.join(paths.private_dir,
423 "phpldapadmin-config.php")
424 paths.hklm = "hklm.ldb"
425 paths.hkcr = "hkcr.ldb"
426 paths.hkcu = "hkcu.ldb"
427 paths.hku = "hku.ldb"
428 paths.hkpd = "hkpd.ldb"
429 paths.hkpt = "hkpt.ldb"
430 paths.sysvol = lp.get("path", "sysvol")
431 paths.netlogon = lp.get("path", "netlogon")
432 paths.smbconf = lp.configfile
436 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
437 serverrole=None, rootdn=None, domaindn=None, configdn=None,
438 schemadn=None, serverdn=None, sitename=None):
439 """Guess configuration settings to use."""
442 hostname = socket.gethostname().split(".")[0]
444 netbiosname = lp.get("netbios name")
445 if netbiosname is None:
446 netbiosname = hostname
447 # remove forbidden chars
449 for x in netbiosname:
450 if x.isalnum() or x in VALID_NETBIOS_CHARS:
451 newnbname = "%s%c" % (newnbname, x)
452 #force the length to be <16
453 netbiosname = newnbname[0:15]
454 assert netbiosname is not None
455 netbiosname = netbiosname.upper()
456 if not valid_netbios_name(netbiosname):
457 raise InvalidNetbiosName(netbiosname)
459 if dnsdomain is None:
460 dnsdomain = lp.get("realm")
461 if dnsdomain is None or dnsdomain == "":
462 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
464 dnsdomain = dnsdomain.lower()
466 if serverrole is None:
467 serverrole = lp.get("server role")
468 if serverrole is None:
469 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
471 serverrole = serverrole.lower()
473 realm = dnsdomain.upper()
475 if lp.get("realm") == "":
476 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
478 if lp.get("realm").upper() != realm:
479 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))
481 if lp.get("server role").lower() != serverrole:
482 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))
484 if serverrole == "domain controller":
486 # This will, for better or worse, default to 'WORKGROUP'
487 domain = lp.get("workgroup")
488 domain = domain.upper()
490 if lp.get("workgroup").upper() != domain:
491 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))
494 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
498 domaindn = "DC=" + netbiosname
500 if not valid_netbios_name(domain):
501 raise InvalidNetbiosName(domain)
503 if hostname.upper() == realm:
504 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
505 if netbiosname == realm:
506 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
508 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
514 configdn = "CN=Configuration," + rootdn
516 schemadn = "CN=Schema," + configdn
521 names = ProvisionNames()
522 names.rootdn = rootdn
523 names.domaindn = domaindn
524 names.configdn = configdn
525 names.schemadn = schemadn
526 names.ldapmanagerdn = "CN=Manager," + rootdn
527 names.dnsdomain = dnsdomain
528 names.domain = domain
530 names.netbiosname = netbiosname
531 names.hostname = hostname
532 names.sitename = sitename
533 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
538 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
539 targetdir, sid_generator="internal", eadb=False):
540 """Create a new smb.conf file based on a couple of basic settings.
542 assert smbconf is not None
544 hostname = socket.gethostname().split(".")[0]
545 netbiosname = hostname.upper()
546 # remove forbidden chars
548 for x in netbiosname:
549 if x.isalnum() or x in VALID_NETBIOS_CHARS:
550 newnbname = "%s%c" % (newnbname, x)
551 #force the length to be <16
552 netbiosname = newnbname[0:15]
554 netbiosname = hostname.upper()
556 if serverrole is None:
557 serverrole = "standalone"
559 assert serverrole in ("domain controller", "member server", "standalone")
560 if serverrole == "domain controller":
562 elif serverrole == "member server":
563 smbconfsuffix = "member"
564 elif serverrole == "standalone":
565 smbconfsuffix = "standalone"
567 if sid_generator is None:
568 sid_generator = "internal"
570 assert domain is not None
571 domain = domain.upper()
573 assert realm is not None
574 realm = realm.upper()
576 default_lp = samba.param.LoadParm()
577 #Load non-existant file
578 if os.path.exists(smbconf):
579 default_lp.load(smbconf)
581 if targetdir is not None:
582 privdir = os.path.join(targetdir, "private")
584 privdir = default_lp.get("private dir")
585 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
589 if targetdir is not None:
590 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
591 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
593 default_lp.set("lock dir", os.path.abspath(targetdir))
598 if sid_generator == "internal":
599 sid_generator_line = ""
601 sid_generator_line = "sid generator = " + sid_generator
603 used_setup_dir = setup_path("")
604 default_setup_dir = default_lp.get("setup directory")
606 if used_setup_dir != default_setup_dir:
607 setupdir_line = "setup directory = %s" % used_setup_dir
608 default_lp.set("setup directory", used_setup_dir)
610 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
611 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
613 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
615 "NETBIOS_NAME": netbiosname,
618 "SERVERROLE": serverrole,
619 "NETLOGONPATH": netlogon,
620 "SYSVOLPATH": sysvol,
621 "SETUPDIRECTORY_LINE": setupdir_line,
622 "SIDGENERATOR_LINE": sid_generator_line,
623 "PRIVATEDIR_LINE": privatedir_line,
624 "LOCKDIR_LINE": lockdir_line,
625 "POSIXEADB_LINE": posixeadb_line
629 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
630 users_gid, wheel_gid):
631 """setup reasonable name mappings for sam names to unix names.
633 :param samdb: SamDB object.
634 :param idmap: IDmap db object.
635 :param sid: The domain sid.
636 :param domaindn: The domain DN.
637 :param root_uid: uid of the UNIX root user.
638 :param nobody_uid: uid of the UNIX nobody user.
639 :param users_gid: gid of the UNIX users group.
640 :param wheel_gid: gid of the UNIX wheel group."""
641 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
642 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
644 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
645 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
648 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
649 provision_backend, names, schema, serverrole,
651 """Setup the partitions for the SAM database.
653 Alternatively, provision() may call this, and then populate the database.
655 :note: This will wipe the Sam Database!
657 :note: This function always removes the local SAM LDB file. The erase
658 parameter controls whether to erase the existing data, which
659 may not be stored locally but in LDAP.
662 assert session_info is not None
664 # We use options=["modules:"] to stop the modules loading - we
665 # just want to wipe and re-initialise the database, not start it up
668 os.unlink(samdb_path)
672 samdb = Ldb(url=samdb_path, session_info=session_info,
673 lp=lp, options=["modules:"])
675 ldap_backend_line = "# No LDAP backend"
676 if provision_backend.type is not "ldb":
677 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
679 samdb.transaction_start()
681 logger.info("Setting up sam.ldb partitions and settings")
682 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
683 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
684 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
685 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
686 "LDAP_BACKEND_LINE": ldap_backend_line,
690 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
691 "BACKEND_TYPE": provision_backend.type,
692 "SERVER_ROLE": serverrole
695 logger.info("Setting up sam.ldb rootDSE")
696 setup_samdb_rootdse(samdb, setup_path, names)
698 samdb.transaction_cancel()
701 samdb.transaction_commit()
704 def secretsdb_self_join(secretsdb, domain,
705 netbiosname, machinepass, domainsid=None,
706 realm=None, dnsdomain=None,
708 key_version_number=1,
709 secure_channel_type=SEC_CHAN_WKSTA):
710 """Add domain join-specific bits to a secrets database.
712 :param secretsdb: Ldb Handle to the secrets database
713 :param machinepass: Machine password
715 attrs=["whenChanged",
722 if realm is not None:
723 if dnsdomain is None:
724 dnsdomain = realm.lower()
725 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
728 shortname = netbiosname.lower()
730 #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
731 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
732 msg["secureChannelType"] = [str(secure_channel_type)]
733 msg["objectClass"] = ["top", "primaryDomain"]
734 if dnsname is not None:
735 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
736 msg["realm"] = [realm]
737 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
738 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
739 msg["privateKeytab"] = ["secrets.keytab"]
741 msg["secret"] = [machinepass]
742 msg["samAccountName"] = ["%s$" % netbiosname]
743 msg["secureChannelType"] = [str(secure_channel_type)]
744 if domainsid is not None:
745 msg["objectSid"] = [ndr_pack(domainsid)]
747 # This complex expression tries to ensure that we don't have more
748 # than one record for this SID, realm or netbios domain at a time,
749 # but we don't delete the old record that we are about to modify,
750 # because that would delete the keytab and previous password.
751 res = secretsdb.search(base="cn=Primary Domains",
753 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
754 scope=ldb.SCOPE_ONELEVEL)
757 secretsdb.delete(del_msg.dn)
759 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
762 msg["priorSecret"] = [res[0]["secret"][0]]
763 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
766 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
771 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
777 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
778 secretsdb.modify(msg)
779 secretsdb.rename(res[0].dn, msg.dn)
781 spn = [ 'HOST/%s' % shortname ]
782 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
783 # we are a domain controller then we add servicePrincipalName entries
784 # for the keytab code to update
785 spn.extend([ 'HOST/%s' % dnsname ])
786 msg["servicePrincipalName"] = spn
791 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir,
793 dns_keytab_path, dnspass):
794 """Add DNS specific bits to a secrets database.
796 :param secretsdb: Ldb Handle to the secrets database
797 :param setup_path: Setup path function
798 :param machinepass: Machine password
801 os.unlink(os.path.join(private_dir, dns_keytab_path))
805 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
807 "DNSDOMAIN": dnsdomain,
808 "DNS_KEYTAB": dns_keytab_path,
809 "DNSPASS_B64": b64encode(dnspass),
810 "HOSTNAME": names.hostname,
811 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
815 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
816 """Setup the secrets database.
818 :note: This function does not handle exceptions and transaction on purpose,
819 it's up to the caller to do this job.
821 :param path: Path to the secrets database.
822 :param setup_path: Get the path to a setup file.
823 :param session_info: Session info.
824 :param credentials: Credentials
825 :param lp: Loadparm context
826 :return: LDB handle for the created secrets database
828 if os.path.exists(paths.secrets):
829 os.unlink(paths.secrets)
831 keytab_path = os.path.join(paths.private_dir, paths.keytab)
832 if os.path.exists(keytab_path):
833 os.unlink(keytab_path)
835 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
836 if os.path.exists(dns_keytab_path):
837 os.unlink(dns_keytab_path)
841 secrets_ldb = Ldb(path, session_info=session_info,
844 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
845 secrets_ldb = Ldb(path, session_info=session_info,
847 secrets_ldb.transaction_start()
849 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
851 if backend_credentials is not None and backend_credentials.authentication_requested():
852 if backend_credentials.get_bind_dn() is not None:
853 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
854 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
855 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
858 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
859 "LDAPADMINUSER": backend_credentials.get_username(),
860 "LDAPADMINREALM": backend_credentials.get_realm(),
861 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
866 secrets_ldb.transaction_cancel()
869 def setup_privileges(path, setup_path, session_info, lp):
870 """Setup the privileges database.
872 :param path: Path to the privileges database.
873 :param setup_path: Get the path to a setup file.
874 :param session_info: Session info.
875 :param credentials: Credentials
876 :param lp: Loadparm context
877 :return: LDB handle for the created secrets database
879 if os.path.exists(path):
881 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
882 privilege_ldb.erase()
883 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
886 def setup_registry(path, setup_path, session_info, lp):
887 """Setup the registry.
889 :param path: Path to the registry database
890 :param setup_path: Function that returns the path to a setup.
891 :param session_info: Session information
892 :param credentials: Credentials
893 :param lp: Loadparm context
895 reg = samba.registry.Registry()
896 hive = samba.registry.open_ldb(path, session_info=session_info,
898 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
899 provision_reg = setup_path("provision.reg")
900 assert os.path.exists(provision_reg)
901 reg.diff_apply(provision_reg)
904 def setup_idmapdb(path, setup_path, session_info, lp):
905 """Setup the idmap database.
907 :param path: path to the idmap database
908 :param setup_path: Function that returns a path to a setup file
909 :param session_info: Session information
910 :param credentials: Credentials
911 :param lp: Loadparm context
913 if os.path.exists(path):
916 idmap_ldb = IDmapDB(path, session_info=session_info,
920 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
924 def setup_samdb_rootdse(samdb, setup_path, names):
925 """Setup the SamDB rootdse.
927 :param samdb: Sam Database handle
928 :param setup_path: Obtain setup path
930 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
931 "SCHEMADN": names.schemadn,
932 "DOMAINDN": names.domaindn,
933 "ROOTDN": names.rootdn,
934 "CONFIGDN": names.configdn,
935 "SERVERDN": names.serverdn,
939 def setup_self_join(samdb, names,
940 machinepass, dnspass,
941 domainsid, next_rid, invocationid, setup_path,
942 policyguid, policyguid_dc, domainControllerFunctionality,
944 """Join a host to its own domain."""
945 assert isinstance(invocationid, str)
946 if ntdsguid is not None:
947 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
950 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
951 "CONFIGDN": names.configdn,
952 "SCHEMADN": names.schemadn,
953 "DOMAINDN": names.domaindn,
954 "SERVERDN": names.serverdn,
955 "INVOCATIONID": invocationid,
956 "NETBIOSNAME": names.netbiosname,
957 "DEFAULTSITE": names.sitename,
958 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
959 "MACHINEPASS_B64": b64encode(machinepass),
960 "REALM": names.realm,
961 "DOMAIN": names.domain,
962 "DOMAINSID": str(domainsid),
963 "DCRID": str(next_rid),
964 "DNSDOMAIN": names.dnsdomain,
965 "SAMBA_VERSION_STRING": version,
966 "NTDSGUID": ntdsguid_line,
967 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
969 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
970 "POLICYGUID": policyguid,
971 "POLICYGUID_DC": policyguid_dc,
972 "DNSDOMAIN": names.dnsdomain,
973 "DOMAINSID": str(domainsid),
974 "DOMAINDN": names.domaindn})
976 # add the NTDSGUID based SPNs
977 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
978 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
979 expression="", scope=ldb.SCOPE_BASE)
980 assert isinstance(names.ntdsguid, str)
982 # Setup fSMORoleOwner entries to point at the newly created DC entry
983 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
984 "DOMAIN": names.domain,
985 "DNSDOMAIN": names.dnsdomain,
986 "DOMAINDN": names.domaindn,
987 "CONFIGDN": names.configdn,
988 "SCHEMADN": names.schemadn,
989 "DEFAULTSITE": names.sitename,
990 "SERVERDN": names.serverdn,
991 "NETBIOSNAME": names.netbiosname,
992 "NTDSGUID": names.ntdsguid,
993 "RIDALLOCATIONSTART": str(next_rid + 100),
994 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
997 # This is partially Samba4 specific and should be replaced by the correct
999 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1000 "DNSDOMAIN": names.dnsdomain,
1001 "DOMAINDN": names.domaindn,
1002 "DNSPASS_B64": b64encode(dnspass),
1003 "HOSTNAME" : names.hostname,
1004 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
1007 def getpolicypath(sysvolpath, dnsdomain, guid):
1008 """Return the physical path of policy given its guid.
1010 :param sysvolpath: Path to the sysvol folder
1011 :param dnsdomain: DNS name of the AD domain
1012 :param guid: The GUID of the policy
1013 :return: A string with the complete path to the policy folder
1017 guid = "{%s}" % guid
1018 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1021 def create_gpo_struct(policy_path):
1022 if not os.path.exists(policy_path):
1023 os.makedirs(policy_path, 0775)
1024 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1025 "[General]\r\nVersion=0")
1026 p = os.path.join(policy_path, "MACHINE")
1027 if not os.path.exists(p):
1028 os.makedirs(p, 0775)
1029 p = os.path.join(policy_path, "USER")
1030 if not os.path.exists(p):
1031 os.makedirs(p, 0775)
1034 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1035 """Create the default GPO for a domain
1037 :param sysvolpath: Physical path for the sysvol folder
1038 :param dnsdomain: DNS domain name of the AD domain
1039 :param policyguid: GUID of the default domain policy
1040 :param policyguid_dc: GUID of the default domain controler policy
1043 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1044 create_gpo_struct(policy_path)
1046 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1047 create_gpo_struct(policy_path)
1050 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1051 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1052 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1053 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1055 """Setup a complete SAM Database.
1057 :note: This will wipe the main SAM database file!
1061 # Provision does not make much sense values larger than 1000000000
1062 # as the upper range of the rIDAvailablePool is 1073741823 and
1063 # we don't want to create a domain that cannot allocate rids.
1064 if next_rid < 1000 or next_rid > 1000000000:
1065 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1066 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1067 raise ProvisioningError(error)
1069 # ATTENTION: Do NOT change these default values without discussion with the
1070 # team and/or release manager. They have a big impact on the whole program!
1071 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1073 if dom_for_fun_level is None:
1074 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1076 if dom_for_fun_level > domainControllerFunctionality:
1077 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!")
1079 domainFunctionality = dom_for_fun_level
1080 forestFunctionality = dom_for_fun_level
1082 # Also wipes the database
1083 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1084 provision_backend=provision_backend, session_info=session_info,
1085 names=names, serverrole=serverrole, schema=schema)
1088 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1090 # Load the database, but don's load the global schema and don't connect quite yet
1091 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1092 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1095 logger.info("Pre-loading the Samba 4 and AD schema")
1097 # Load the schema from the one we computed earlier
1098 samdb.set_schema(schema)
1100 # Set the NTDS settings DN manually - in order to have it already around
1101 # before the provisioned tree exists and we connect
1102 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1104 # And now we can connect to the DB - the schema won't be loaded from the DB
1107 if fill == FILL_DRS:
1110 samdb.transaction_start()
1112 # Set the domain functionality levels onto the database.
1113 # Various module (the password_hash module in particular) need
1114 # to know what level of AD we are emulating.
1116 # These will be fixed into the database via the database
1117 # modifictions below, but we need them set from the start.
1118 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1119 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1120 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1122 samdb.set_domain_sid(str(domainsid))
1123 samdb.set_invocation_id(invocationid)
1125 logger.info("Adding DomainDN: %s" % names.domaindn)
1127 #impersonate domain admin
1128 admin_session_info = admin_session(lp, str(domainsid))
1129 samdb.set_session_info(admin_session_info)
1130 if domainguid is not None:
1131 domainguid_line = "objectGUID: %s\n-" % domainguid
1133 domainguid_line = ""
1135 descr = b64encode(get_domain_descriptor(domainsid))
1136 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1137 "DOMAINDN": names.domaindn,
1138 "DOMAINGUID": domainguid_line,
1143 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1144 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1145 "DOMAINSID": str(domainsid),
1146 "NEXTRID": str(next_rid),
1147 "SCHEMADN": names.schemadn,
1148 "NETBIOSNAME": names.netbiosname,
1149 "DEFAULTSITE": names.sitename,
1150 "CONFIGDN": names.configdn,
1151 "SERVERDN": names.serverdn,
1152 "POLICYGUID": policyguid,
1153 "DOMAINDN": names.domaindn,
1154 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1155 "SAMBA_VERSION_STRING": version
1158 logger.info("Adding configuration container")
1159 descr = b64encode(get_config_descriptor(domainsid))
1160 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1161 "CONFIGDN": names.configdn,
1162 "DESCRIPTOR": descr,
1165 # The LDIF here was created when the Schema object was constructed
1166 logger.info("Setting up sam.ldb schema")
1167 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1168 samdb.modify_ldif(schema.schema_dn_modify)
1169 samdb.write_prefixes_from_schema()
1170 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1171 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1172 {"SCHEMADN": names.schemadn})
1174 logger.info("Reopening sam.ldb with new schema")
1176 samdb.transaction_cancel()
1179 samdb.transaction_commit()
1181 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1182 credentials=provision_backend.credentials, lp=lp,
1183 global_schema=False, am_rodc=am_rodc)
1185 # Set the NTDS settings DN manually - in order to have it already around
1186 # before the provisioned tree exists and we connect
1187 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1191 samdb.transaction_start()
1193 samdb.invocation_id = invocationid
1195 logger.info("Setting up sam.ldb configuration data")
1196 descr = b64encode(get_sites_descriptor(domainsid))
1197 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1198 "CONFIGDN": names.configdn,
1199 "NETBIOSNAME": names.netbiosname,
1200 "DEFAULTSITE": names.sitename,
1201 "DNSDOMAIN": names.dnsdomain,
1202 "DOMAIN": names.domain,
1203 "SCHEMADN": names.schemadn,
1204 "DOMAINDN": names.domaindn,
1205 "SERVERDN": names.serverdn,
1206 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1207 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1208 "SITES_DESCRIPTOR": descr
1211 logger.info("Setting up display specifiers")
1212 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1213 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1214 check_all_substituted(display_specifiers_ldif)
1215 samdb.add_ldif(display_specifiers_ldif)
1217 logger.info("Adding users container")
1218 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1219 "DOMAINDN": names.domaindn})
1220 logger.info("Modifying users container")
1221 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1222 "DOMAINDN": names.domaindn})
1223 logger.info("Adding computers container")
1224 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1225 "DOMAINDN": names.domaindn})
1226 logger.info("Modifying computers container")
1227 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1228 "DOMAINDN": names.domaindn})
1229 logger.info("Setting up sam.ldb data")
1230 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1231 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1232 "DOMAINDN": names.domaindn,
1233 "NETBIOSNAME": names.netbiosname,
1234 "DEFAULTSITE": names.sitename,
1235 "CONFIGDN": names.configdn,
1236 "SERVERDN": names.serverdn,
1237 "RIDAVAILABLESTART": str(next_rid + 600),
1238 "POLICYGUID_DC": policyguid_dc
1241 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1242 "DOMAINDN": names.domaindn})
1244 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1245 "CONFIGDN": names.configdn,
1246 "SCHEMADN": names.schemadn})
1247 if fill == FILL_FULL:
1248 logger.info("Setting up sam.ldb users and groups")
1249 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1250 "DOMAINDN": names.domaindn,
1251 "DOMAINSID": str(domainsid),
1252 "CONFIGDN": names.configdn,
1253 "ADMINPASS_B64": b64encode(adminpass),
1254 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1257 logger.info("Setting up self join")
1258 setup_self_join(samdb, names=names, invocationid=invocationid,
1260 machinepass=machinepass,
1261 domainsid=domainsid,
1263 policyguid=policyguid,
1264 policyguid_dc=policyguid_dc,
1265 setup_path=setup_path,
1266 domainControllerFunctionality=domainControllerFunctionality,
1269 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1270 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1271 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1272 assert isinstance(names.ntdsguid, str)
1274 samdb.transaction_cancel()
1277 samdb.transaction_commit()
1282 FILL_NT4SYNC = "NT4SYNC"
1284 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1285 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)"
1287 def set_dir_acl(path, acl, lp, domsid):
1288 setntacl(lp, path, acl, domsid)
1289 for root, dirs, files in os.walk(path, topdown=False):
1291 setntacl(lp, os.path.join(root, name), acl, domsid)
1293 setntacl(lp, os.path.join(root, name), acl, domsid)
1296 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1297 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1300 :param sysvol: Physical path for the sysvol folder
1301 :param dnsdomain: The DNS name of the domain
1302 :param domainsid: The SID of the domain
1303 :param domaindn: The DN of the domain (ie. DC=...)
1304 :param samdb: An LDB object on the SAM db
1305 :param lp: an LP object
1308 # Set ACL for GPO root folder
1309 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1310 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1312 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1313 attrs=["cn", "nTSecurityDescriptor"],
1314 expression="", scope=ldb.SCOPE_ONELEVEL)
1317 acl = ndr_unpack(security.descriptor,
1318 str(policy["nTSecurityDescriptor"])).as_sddl()
1319 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1320 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1323 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1325 """Set the ACL for the sysvol share and the subfolders
1327 :param samdb: An LDB object on the SAM db
1328 :param netlogon: Physical path for the netlogon folder
1329 :param sysvol: Physical path for the sysvol folder
1330 :param gid: The GID of the "Domain adminstrators" group
1331 :param domainsid: The SID of the domain
1332 :param dnsdomain: The DNS name of the domain
1333 :param domaindn: The DN of the domain (ie. DC=...)
1337 os.chown(sysvol,-1,gid)
1343 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1344 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1345 for root, dirs, files in os.walk(sysvol, topdown=False):
1348 os.chown(os.path.join(root, name), -1, gid)
1349 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1352 os.chown(os.path.join(root, name), -1, gid)
1353 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1355 # Set acls on Policy folder and policies folders
1356 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1359 def provision(setup_dir, logger, session_info,
1360 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1362 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1364 domain=None, hostname=None, hostip=None, hostip6=None,
1365 domainsid=None, next_rid=1000,
1366 adminpass=None, ldapadminpass=None,
1367 krbtgtpass=None, domainguid=None,
1368 policyguid=None, policyguid_dc=None, invocationid=None,
1369 machinepass=None, ntdsguid=None,
1370 dnspass=None, root=None, nobody=None, users=None,
1371 wheel=None, backup=None, aci=None, serverrole=None,
1372 dom_for_fun_level=None,
1373 ldap_backend_extra_port=None, ldap_backend_forced_uri=None, backend_type=None,
1375 ol_mmr_urls=None, ol_olc=None,
1376 setup_ds_path=None, slapd_path=None, nosync=False,
1377 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1380 :note: caution, this wipes all existing data!
1383 def setup_path(file):
1384 return os.path.join(setup_dir, file)
1386 if domainsid is None:
1387 domainsid = security.random_sid()
1389 domainsid = security.dom_sid(domainsid)
1391 # create/adapt the group policy GUIDs
1392 # Default GUID for default policy are described at
1393 # "How Core Group Policy Works"
1394 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1395 if policyguid is None:
1396 policyguid = DEFAULT_POLICY_GUID
1397 policyguid = policyguid.upper()
1398 if policyguid_dc is None:
1399 policyguid_dc = DEFAULT_DC_POLICY_GUID
1400 policyguid_dc = policyguid_dc.upper()
1402 if adminpass is None:
1403 adminpass = samba.generate_random_password(12, 32)
1404 if krbtgtpass is None:
1405 krbtgtpass = samba.generate_random_password(128, 255)
1406 if machinepass is None:
1407 machinepass = samba.generate_random_password(128, 255)
1409 dnspass = samba.generate_random_password(128, 255)
1410 if ldapadminpass is None:
1411 #Make a new, random password between Samba and it's LDAP server
1412 ldapadminpass=samba.generate_random_password(128, 255)
1414 if backend_type is None:
1415 backend_type = "ldb"
1417 sid_generator = "internal"
1418 if backend_type == "fedora-ds":
1419 sid_generator = "backend"
1421 root_uid = findnss_uid([root or "root"])
1422 nobody_uid = findnss_uid([nobody or "nobody"])
1423 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1425 wheel_gid = findnss_gid(["wheel", "adm"])
1427 wheel_gid = findnss_gid([wheel])
1429 bind_gid = findnss_gid(["bind", "named"])
1433 if targetdir is not None:
1434 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1435 elif smbconf is None:
1436 smbconf = samba.param.default_path()
1437 if not os.path.exists(os.path.dirname(smbconf)):
1438 os.makedirs(os.path.dirname(smbconf))
1440 # only install a new smb.conf if there isn't one there already
1441 if os.path.exists(smbconf):
1442 # if Samba Team members can't figure out the weird errors
1443 # loading an empty smb.conf gives, then we need to be smarter.
1444 # Pretend it just didn't exist --abartlet
1445 data = open(smbconf, 'r').read()
1446 data = data.lstrip()
1447 if data is None or data == "":
1448 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1449 serverrole, targetdir, sid_generator, useeadb)
1451 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1452 targetdir, sid_generator, useeadb)
1454 lp = samba.param.LoadParm()
1456 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1457 dnsdomain=realm, serverrole=serverrole,
1458 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1459 serverdn=serverdn, sitename=sitename)
1460 paths = provision_paths_from_lp(lp, names.dnsdomain)
1462 paths.bind_gid = bind_gid
1465 hostips = samba.interface_ips(lp, False)
1466 if len(hostips) == 0:
1467 logger.warning("No external IPv4 address has been found. Using loopback.")
1468 hostip = '127.0.0.1'
1471 if len(hostips) > 1:
1472 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1476 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1479 if hostip6 == '::1' and ip[-1][0] != '::1':
1481 except socket.gaierror, (socket.EAI_NODATA, msg):
1484 if serverrole is None:
1485 serverrole = lp.get("server role")
1487 assert serverrole in ("domain controller", "member server", "standalone")
1488 if invocationid is None:
1489 invocationid = str(uuid.uuid4())
1491 if not os.path.exists(paths.private_dir):
1492 os.mkdir(paths.private_dir)
1493 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1494 os.mkdir(os.path.join(paths.private_dir, "tls"))
1496 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1498 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn)
1500 if backend_type == "ldb":
1501 provision_backend = LDBBackend(backend_type,
1502 paths=paths, setup_path=setup_path,
1503 lp=lp, credentials=credentials,
1506 elif backend_type == "existing":
1507 provision_backend = ExistingBackend(backend_type,
1508 paths=paths, setup_path=setup_path,
1509 lp=lp, credentials=credentials,
1512 ldap_backend_forced_uri=ldap_backend_forced_uri)
1513 elif backend_type == "fedora-ds":
1514 provision_backend = FDSBackend(backend_type,
1515 paths=paths, setup_path=setup_path,
1516 lp=lp, credentials=credentials,
1519 domainsid=domainsid,
1522 ldapadminpass=ldapadminpass,
1523 slapd_path=slapd_path,
1524 ldap_backend_extra_port=ldap_backend_extra_port,
1525 ldap_dryrun_mode=ldap_dryrun_mode,
1527 setup_ds_path=setup_ds_path,
1528 ldap_backend_forced_uri=ldap_backend_forced_uri)
1529 elif backend_type == "openldap":
1530 provision_backend = OpenLDAPBackend(backend_type,
1531 paths=paths, setup_path=setup_path,
1532 lp=lp, credentials=credentials,
1535 domainsid=domainsid,
1538 ldapadminpass=ldapadminpass,
1539 slapd_path=slapd_path,
1540 ldap_backend_extra_port=ldap_backend_extra_port,
1541 ldap_dryrun_mode=ldap_dryrun_mode,
1542 ol_mmr_urls=ol_mmr_urls,
1544 ldap_backend_forced_uri=ldap_backend_forced_uri)
1546 raise ValueError("Unknown LDAP backend type selected")
1548 provision_backend.init()
1549 provision_backend.start()
1551 # only install a new shares config db if there is none
1552 if not os.path.exists(paths.shareconf):
1553 logger.info("Setting up share.ldb")
1554 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1556 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1558 logger.info("Setting up secrets.ldb")
1559 secrets_ldb = setup_secretsdb(paths, setup_path,
1560 session_info=session_info,
1561 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1564 logger.info("Setting up the registry")
1565 setup_registry(paths.hklm, setup_path, session_info,
1568 logger.info("Setting up the privileges database")
1569 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1571 logger.info("Setting up idmap db")
1572 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1575 logger.info("Setting up SAM db")
1576 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1577 provision_backend, lp, names,
1579 domainsid=domainsid,
1580 schema=schema, domainguid=domainguid,
1581 policyguid=policyguid, policyguid_dc=policyguid_dc,
1583 adminpass=adminpass, krbtgtpass=krbtgtpass,
1584 invocationid=invocationid,
1585 machinepass=machinepass, dnspass=dnspass,
1586 ntdsguid=ntdsguid, serverrole=serverrole,
1587 dom_for_fun_level=dom_for_fun_level,
1588 am_rodc=am_rodc, next_rid=next_rid)
1590 if serverrole == "domain controller":
1591 if paths.netlogon is None:
1592 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1593 logger.info("Please either remove %s or see the template at %s" %
1594 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1595 assert paths.netlogon is not None
1597 if paths.sysvol is None:
1598 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1599 " are configuring a DC.")
1600 logger.info("Please either remove %s or see the template at %s" %
1601 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1602 assert paths.sysvol is not None
1604 if not os.path.isdir(paths.netlogon):
1605 os.makedirs(paths.netlogon, 0755)
1607 if samdb_fill == FILL_FULL:
1608 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1609 root_uid=root_uid, nobody_uid=nobody_uid,
1610 users_gid=users_gid, wheel_gid=wheel_gid)
1612 if serverrole == "domain controller":
1613 # Set up group policies (domain policy and domain controller policy)
1614 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1615 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1616 domainsid, names.dnsdomain, names.domaindn, lp)
1618 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1619 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1621 secretsdb_self_join(secrets_ldb, domain=names.domain,
1623 dnsdomain=names.dnsdomain,
1624 netbiosname=names.netbiosname,
1625 domainsid=domainsid,
1626 machinepass=machinepass,
1627 secure_channel_type=SEC_CHAN_BDC)
1629 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1630 # In future, this might be determined from some configuration
1631 kerberos_enctypes = str(ENC_ALL_TYPES)
1634 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1635 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1636 flags=ldb.FLAG_MOD_REPLACE,
1637 name="msDS-SupportedEncryptionTypes")
1639 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1640 # It might be that this attribute does not exist in this schema
1644 if serverrole == "domain controller":
1645 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1647 realm=names.realm, dnsdomain=names.dnsdomain,
1648 dns_keytab_path=paths.dns_keytab,
1651 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1652 assert isinstance(domainguid, str)
1654 # Only make a zone file on the first DC, it should be replicated
1655 # with DNS replication
1656 create_zone_file(lp, logger, paths, targetdir, setup_path,
1657 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1658 hostname=names.hostname, realm=names.realm,
1659 domainguid=domainguid, ntdsguid=names.ntdsguid)
1661 create_named_conf(paths, setup_path, realm=names.realm,
1662 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1664 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1665 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1666 keytab_name=paths.dns_keytab)
1667 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1668 logger.info("and %s for further documentation required for secure DNS "
1669 "updates", paths.namedtxt)
1671 lastProvisionUSNs = get_last_provision_usn(samdb)
1672 maxUSN = get_max_usn(samdb, str(names.rootdn))
1673 if lastProvisionUSNs is not None:
1674 update_provision_usn(samdb, 0, maxUSN, 1)
1676 set_provision_usn(samdb, 0, maxUSN)
1678 create_krb5_conf(paths.krb5conf, setup_path,
1679 dnsdomain=names.dnsdomain, hostname=names.hostname,
1681 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1682 "generated at %s", paths.krb5conf)
1684 if serverrole == "domain controller":
1685 create_dns_update_list(lp, logger, paths, setup_path)
1687 provision_backend.post_setup()
1688 provision_backend.shutdown()
1690 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1693 secrets_ldb.transaction_cancel()
1696 #Now commit the secrets.ldb to disk
1697 secrets_ldb.transaction_commit()
1699 # the commit creates the dns.keytab, now chown it
1700 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1701 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1703 os.chmod(dns_keytab_path, 0640)
1704 os.chown(dns_keytab_path, -1, paths.bind_gid)
1706 if not os.environ.has_key('SAMBA_SELFTEST'):
1707 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1711 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1712 paths.phpldapadminconfig)
1714 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1715 logger.info("Server Role: %s" % serverrole)
1716 logger.info("Hostname: %s" % names.hostname)
1717 logger.info("NetBIOS Domain: %s" % names.domain)
1718 logger.info("DNS Domain: %s" % names.dnsdomain)
1719 logger.info("DOMAIN SID: %s" % str(domainsid))
1720 if samdb_fill == FILL_FULL:
1721 logger.info("Admin password: %s" % adminpass)
1722 if provision_backend.type is not "ldb":
1723 if provision_backend.credentials.get_bind_dn() is not None:
1724 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1726 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1728 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1730 if provision_backend.slapd_command_escaped is not None:
1731 # now display slapd_command_file.txt to show how slapd must be started next time
1732 logger.info("Use later the following commandline to start slapd, then Samba:")
1733 logger.info(provision_backend.slapd_command_escaped)
1734 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1735 provision_backend.ldapdir)
1737 result = ProvisionResult()
1738 result.domaindn = domaindn
1739 result.paths = paths
1741 result.samdb = samdb
1745 def provision_become_dc(setup_dir=None,
1746 smbconf=None, targetdir=None, realm=None,
1747 rootdn=None, domaindn=None, schemadn=None,
1748 configdn=None, serverdn=None,
1749 domain=None, hostname=None, domainsid=None,
1750 adminpass=None, krbtgtpass=None, domainguid=None,
1751 policyguid=None, policyguid_dc=None, invocationid=None,
1753 dnspass=None, root=None, nobody=None, users=None,
1754 wheel=None, backup=None, serverrole=None,
1755 ldap_backend=None, ldap_backend_type=None,
1756 sitename=None, debuglevel=1):
1758 logger = logging.getLogger("provision")
1759 samba.set_debug_level(debuglevel)
1761 res = provision(setup_dir, logger, system_session(), None,
1762 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1763 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1764 configdn=configdn, serverdn=serverdn, domain=domain,
1765 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1766 machinepass=machinepass, serverrole="domain controller",
1768 res.lp.set("debuglevel", str(debuglevel))
1772 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1773 """Create a PHP LDAP admin configuration file.
1775 :param path: Path to write the configuration to.
1776 :param setup_path: Function to generate setup paths.
1778 setup_file(setup_path("phpldapadmin-config.php"), path,
1779 {"S4_LDAPI_URI": ldapi_uri})
1782 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1783 hostip, hostip6, hostname, realm, domainguid,
1785 """Write out a DNS zone file, from the info in the current database.
1787 :param paths: paths object
1788 :param setup_path: Setup path function.
1789 :param dnsdomain: DNS Domain name
1790 :param domaindn: DN of the Domain
1791 :param hostip: Local IPv4 IP
1792 :param hostip6: Local IPv6 IP
1793 :param hostname: Local hostname
1794 :param realm: Realm name
1795 :param domainguid: GUID of the domain.
1796 :param ntdsguid: GUID of the hosts nTDSDSA record.
1798 assert isinstance(domainguid, str)
1800 if hostip6 is not None:
1801 hostip6_base_line = " IN AAAA " + hostip6
1802 hostip6_host_line = hostname + " IN AAAA " + hostip6
1803 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1805 hostip6_base_line = ""
1806 hostip6_host_line = ""
1807 gc_msdcs_ip6_line = ""
1809 if hostip is not None:
1810 hostip_base_line = " IN A " + hostip
1811 hostip_host_line = hostname + " IN A " + hostip
1812 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1814 hostip_base_line = ""
1815 hostip_host_line = ""
1816 gc_msdcs_ip_line = ""
1818 dns_dir = os.path.dirname(paths.dns)
1821 shutil.rmtree(dns_dir, True)
1825 os.mkdir(dns_dir, 0775)
1827 # we need to freeze the zone while we update the contents
1828 if targetdir is None:
1829 rndc = ' '.join(lp.get("rndc command"))
1830 os.system(rndc + " freeze " + lp.get("realm"))
1832 setup_file(setup_path("provision.zone"), paths.dns, {
1833 "HOSTNAME": hostname,
1834 "DNSDOMAIN": dnsdomain,
1836 "HOSTIP_BASE_LINE": hostip_base_line,
1837 "HOSTIP_HOST_LINE": hostip_host_line,
1838 "DOMAINGUID": domainguid,
1839 "DATESTRING": time.strftime("%Y%m%d%H"),
1840 "DEFAULTSITE": DEFAULTSITE,
1841 "NTDSGUID": ntdsguid,
1842 "HOSTIP6_BASE_LINE": hostip6_base_line,
1843 "HOSTIP6_HOST_LINE": hostip6_host_line,
1844 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1845 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1848 # note that we use no variable substitution on this file
1849 # the substitution is done at runtime by samba_dnsupdate
1850 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1852 # and the SPN update list
1853 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1855 if paths.bind_gid is not None:
1857 os.chown(dns_dir, -1, paths.bind_gid)
1858 os.chown(paths.dns, -1, paths.bind_gid)
1859 # chmod needed to cope with umask
1860 os.chmod(dns_dir, 0775)
1861 os.chmod(paths.dns, 0664)
1863 if not os.environ.has_key('SAMBA_SELFTEST'):
1864 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1866 if targetdir is None:
1867 os.system(rndc + " unfreeze " + lp.get("realm"))
1870 def create_dns_update_list(lp, logger, paths, setup_path):
1871 """Write out a dns_update_list file"""
1872 # note that we use no variable substitution on this file
1873 # the substitution is done at runtime by samba_dnsupdate
1874 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1875 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1878 def create_named_conf(paths, setup_path, realm, dnsdomain,
1880 """Write out a file containing zone statements suitable for inclusion in a
1881 named.conf file (including GSS-TSIG configuration).
1883 :param paths: all paths
1884 :param setup_path: Setup path function.
1885 :param realm: Realm name
1886 :param dnsdomain: DNS Domain name
1887 :param private_dir: Path to private directory
1888 :param keytab_name: File name of DNS keytab file
1891 setup_file(setup_path("named.conf"), paths.namedconf, {
1892 "DNSDOMAIN": dnsdomain,
1894 "ZONE_FILE": paths.dns,
1895 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1896 "NAMED_CONF": paths.namedconf,
1897 "NAMED_CONF_UPDATE": paths.namedconf_update
1900 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1903 def create_named_txt(path, setup_path, realm, dnsdomain,
1904 private_dir, keytab_name):
1905 """Write out a file containing zone statements suitable for inclusion in a
1906 named.conf file (including GSS-TSIG configuration).
1908 :param path: Path of the new named.conf file.
1909 :param setup_path: Setup path function.
1910 :param realm: Realm name
1911 :param dnsdomain: DNS Domain name
1912 :param private_dir: Path to private directory
1913 :param keytab_name: File name of DNS keytab file
1916 setup_file(setup_path("named.txt"), path, {
1917 "DNSDOMAIN": dnsdomain,
1919 "DNS_KEYTAB": keytab_name,
1920 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1921 "PRIVATE_DIR": private_dir
1925 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1926 """Write out a file containing zone statements suitable for inclusion in a
1927 named.conf file (including GSS-TSIG configuration).
1929 :param path: Path of the new named.conf file.
1930 :param setup_path: Setup path function.
1931 :param dnsdomain: DNS Domain name
1932 :param hostname: Local hostname
1933 :param realm: Realm name
1935 setup_file(setup_path("krb5.conf"), path, {
1936 "DNSDOMAIN": dnsdomain,
1937 "HOSTNAME": hostname,
1942 class ProvisioningError(Exception):
1943 """A generic provision error."""
1945 def __init__(self, value):
1949 return "ProvisioningError: " + self.value
1952 class InvalidNetbiosName(Exception):
1953 """A specified name was not a valid NetBIOS name."""
1954 def __init__(self, name):
1955 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)