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 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
958 "MACHINEPASS_B64": b64encode(machinepass),
959 "DOMAINSID": str(domainsid),
960 "DCRID": str(next_rid),
961 "SAMBA_VERSION_STRING": version,
962 "NTDSGUID": ntdsguid_line,
963 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
965 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
966 "POLICYGUID": policyguid,
967 "POLICYGUID_DC": policyguid_dc,
968 "DNSDOMAIN": names.dnsdomain,
969 "DOMAINDN": names.domaindn})
971 # add the NTDSGUID based SPNs
972 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
973 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
974 expression="", scope=ldb.SCOPE_BASE)
975 assert isinstance(names.ntdsguid, str)
977 # Setup fSMORoleOwner entries to point at the newly created DC entry
978 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
979 "DOMAINDN": names.domaindn,
980 "CONFIGDN": names.configdn,
981 "SCHEMADN": names.schemadn,
982 "DEFAULTSITE": names.sitename,
983 "SERVERDN": names.serverdn,
984 "NETBIOSNAME": names.netbiosname,
985 "RIDALLOCATIONSTART": str(next_rid + 100),
986 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
989 # This is partially Samba4 specific and should be replaced by the correct
991 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
992 "DNSDOMAIN": names.dnsdomain,
993 "DOMAINDN": names.domaindn,
994 "DNSPASS_B64": b64encode(dnspass),
995 "HOSTNAME" : names.hostname,
996 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
999 def getpolicypath(sysvolpath, dnsdomain, guid):
1000 """Return the physical path of policy given its guid.
1002 :param sysvolpath: Path to the sysvol folder
1003 :param dnsdomain: DNS name of the AD domain
1004 :param guid: The GUID of the policy
1005 :return: A string with the complete path to the policy folder
1009 guid = "{%s}" % guid
1010 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1013 def create_gpo_struct(policy_path):
1014 if not os.path.exists(policy_path):
1015 os.makedirs(policy_path, 0775)
1016 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1017 "[General]\r\nVersion=0")
1018 p = os.path.join(policy_path, "MACHINE")
1019 if not os.path.exists(p):
1020 os.makedirs(p, 0775)
1021 p = os.path.join(policy_path, "USER")
1022 if not os.path.exists(p):
1023 os.makedirs(p, 0775)
1026 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1027 """Create the default GPO for a domain
1029 :param sysvolpath: Physical path for the sysvol folder
1030 :param dnsdomain: DNS domain name of the AD domain
1031 :param policyguid: GUID of the default domain policy
1032 :param policyguid_dc: GUID of the default domain controler policy
1035 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1036 create_gpo_struct(policy_path)
1038 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1039 create_gpo_struct(policy_path)
1042 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1043 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1044 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1045 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1047 """Setup a complete SAM Database.
1049 :note: This will wipe the main SAM database file!
1053 # Provision does not make much sense values larger than 1000000000
1054 # as the upper range of the rIDAvailablePool is 1073741823 and
1055 # we don't want to create a domain that cannot allocate rids.
1056 if next_rid < 1000 or next_rid > 1000000000:
1057 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1058 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1059 raise ProvisioningError(error)
1061 # ATTENTION: Do NOT change these default values without discussion with the
1062 # team and/or release manager. They have a big impact on the whole program!
1063 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1065 if dom_for_fun_level is None:
1066 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1068 if dom_for_fun_level > domainControllerFunctionality:
1069 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!")
1071 domainFunctionality = dom_for_fun_level
1072 forestFunctionality = dom_for_fun_level
1074 # Also wipes the database
1075 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1076 provision_backend=provision_backend, session_info=session_info,
1077 names=names, serverrole=serverrole, schema=schema)
1080 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1082 # Load the database, but don's load the global schema and don't connect quite yet
1083 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1084 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1087 logger.info("Pre-loading the Samba 4 and AD schema")
1089 # Load the schema from the one we computed earlier
1090 samdb.set_schema(schema)
1092 # Set the NTDS settings DN manually - in order to have it already around
1093 # before the provisioned tree exists and we connect
1094 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1096 # And now we can connect to the DB - the schema won't be loaded from the DB
1099 if fill == FILL_DRS:
1102 samdb.transaction_start()
1104 # Set the domain functionality levels onto the database.
1105 # Various module (the password_hash module in particular) need
1106 # to know what level of AD we are emulating.
1108 # These will be fixed into the database via the database
1109 # modifictions below, but we need them set from the start.
1110 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1111 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1112 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1114 samdb.set_domain_sid(str(domainsid))
1115 samdb.set_invocation_id(invocationid)
1117 logger.info("Adding DomainDN: %s" % names.domaindn)
1119 #impersonate domain admin
1120 admin_session_info = admin_session(lp, str(domainsid))
1121 samdb.set_session_info(admin_session_info)
1122 if domainguid is not None:
1123 domainguid_line = "objectGUID: %s\n-" % domainguid
1125 domainguid_line = ""
1127 descr = b64encode(get_domain_descriptor(domainsid))
1128 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1129 "DOMAINDN": names.domaindn,
1130 "DOMAINGUID": domainguid_line,
1135 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1136 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1137 "DOMAINSID": str(domainsid),
1138 "NEXTRID": str(next_rid),
1139 "SCHEMADN": names.schemadn,
1140 "NETBIOSNAME": names.netbiosname,
1141 "DEFAULTSITE": names.sitename,
1142 "CONFIGDN": names.configdn,
1143 "SERVERDN": names.serverdn,
1144 "POLICYGUID": policyguid,
1145 "DOMAINDN": names.domaindn,
1146 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1147 "SAMBA_VERSION_STRING": version
1150 logger.info("Adding configuration container")
1151 descr = b64encode(get_config_descriptor(domainsid))
1152 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1153 "CONFIGDN": names.configdn,
1154 "DESCRIPTOR": descr,
1157 # The LDIF here was created when the Schema object was constructed
1158 logger.info("Setting up sam.ldb schema")
1159 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1160 samdb.modify_ldif(schema.schema_dn_modify)
1161 samdb.write_prefixes_from_schema()
1162 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1163 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1164 {"SCHEMADN": names.schemadn})
1166 logger.info("Reopening sam.ldb with new schema")
1168 samdb.transaction_cancel()
1171 samdb.transaction_commit()
1173 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1174 credentials=provision_backend.credentials, lp=lp,
1175 global_schema=False, am_rodc=am_rodc)
1177 # Set the NTDS settings DN manually - in order to have it already around
1178 # before the provisioned tree exists and we connect
1179 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1183 samdb.transaction_start()
1185 samdb.invocation_id = invocationid
1187 logger.info("Setting up sam.ldb configuration data")
1188 descr = b64encode(get_sites_descriptor(domainsid))
1189 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1190 "CONFIGDN": names.configdn,
1191 "NETBIOSNAME": names.netbiosname,
1192 "DEFAULTSITE": names.sitename,
1193 "DNSDOMAIN": names.dnsdomain,
1194 "DOMAIN": names.domain,
1195 "SCHEMADN": names.schemadn,
1196 "DOMAINDN": names.domaindn,
1197 "SERVERDN": names.serverdn,
1198 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1199 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1200 "SITES_DESCRIPTOR": descr
1203 logger.info("Setting up display specifiers")
1204 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1205 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1206 check_all_substituted(display_specifiers_ldif)
1207 samdb.add_ldif(display_specifiers_ldif)
1209 logger.info("Adding users container")
1210 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1211 "DOMAINDN": names.domaindn})
1212 logger.info("Modifying users container")
1213 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1214 "DOMAINDN": names.domaindn})
1215 logger.info("Adding computers container")
1216 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1217 "DOMAINDN": names.domaindn})
1218 logger.info("Modifying computers container")
1219 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1220 "DOMAINDN": names.domaindn})
1221 logger.info("Setting up sam.ldb data")
1222 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1223 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1224 "DOMAINDN": names.domaindn,
1225 "NETBIOSNAME": names.netbiosname,
1226 "DEFAULTSITE": names.sitename,
1227 "CONFIGDN": names.configdn,
1228 "SERVERDN": names.serverdn,
1229 "RIDAVAILABLESTART": str(next_rid + 600),
1230 "POLICYGUID_DC": policyguid_dc
1233 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1234 "DOMAINDN": names.domaindn})
1236 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1237 "CONFIGDN": names.configdn,
1238 "SCHEMADN": names.schemadn})
1239 if fill == FILL_FULL:
1240 logger.info("Setting up sam.ldb users and groups")
1241 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1242 "DOMAINDN": names.domaindn,
1243 "DOMAINSID": str(domainsid),
1244 "CONFIGDN": names.configdn,
1245 "ADMINPASS_B64": b64encode(adminpass),
1246 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1249 logger.info("Setting up self join")
1250 setup_self_join(samdb, names=names, invocationid=invocationid,
1252 machinepass=machinepass,
1253 domainsid=domainsid,
1255 policyguid=policyguid,
1256 policyguid_dc=policyguid_dc,
1257 setup_path=setup_path,
1258 domainControllerFunctionality=domainControllerFunctionality,
1261 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1262 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1263 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1264 assert isinstance(names.ntdsguid, str)
1266 samdb.transaction_cancel()
1269 samdb.transaction_commit()
1274 FILL_NT4SYNC = "NT4SYNC"
1276 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1277 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)"
1279 def set_dir_acl(path, acl, lp, domsid):
1280 setntacl(lp, path, acl, domsid)
1281 for root, dirs, files in os.walk(path, topdown=False):
1283 setntacl(lp, os.path.join(root, name), acl, domsid)
1285 setntacl(lp, os.path.join(root, name), acl, domsid)
1288 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1289 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1292 :param sysvol: Physical path for the sysvol folder
1293 :param dnsdomain: The DNS name of the domain
1294 :param domainsid: The SID of the domain
1295 :param domaindn: The DN of the domain (ie. DC=...)
1296 :param samdb: An LDB object on the SAM db
1297 :param lp: an LP object
1300 # Set ACL for GPO root folder
1301 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1302 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1304 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1305 attrs=["cn", "nTSecurityDescriptor"],
1306 expression="", scope=ldb.SCOPE_ONELEVEL)
1309 acl = ndr_unpack(security.descriptor,
1310 str(policy["nTSecurityDescriptor"])).as_sddl()
1311 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1312 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1315 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1317 """Set the ACL for the sysvol share and the subfolders
1319 :param samdb: An LDB object on the SAM db
1320 :param netlogon: Physical path for the netlogon folder
1321 :param sysvol: Physical path for the sysvol folder
1322 :param gid: The GID of the "Domain adminstrators" group
1323 :param domainsid: The SID of the domain
1324 :param dnsdomain: The DNS name of the domain
1325 :param domaindn: The DN of the domain (ie. DC=...)
1329 os.chown(sysvol,-1,gid)
1335 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1336 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1337 for root, dirs, files in os.walk(sysvol, topdown=False):
1340 os.chown(os.path.join(root, name), -1, gid)
1341 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1344 os.chown(os.path.join(root, name), -1, gid)
1345 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1347 # Set acls on Policy folder and policies folders
1348 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1351 def provision(setup_dir, logger, session_info,
1352 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1354 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1356 domain=None, hostname=None, hostip=None, hostip6=None,
1357 domainsid=None, next_rid=1000,
1358 adminpass=None, ldapadminpass=None,
1359 krbtgtpass=None, domainguid=None,
1360 policyguid=None, policyguid_dc=None, invocationid=None,
1361 machinepass=None, ntdsguid=None,
1362 dnspass=None, root=None, nobody=None, users=None,
1363 wheel=None, backup=None, aci=None, serverrole=None,
1364 dom_for_fun_level=None,
1365 ldap_backend_extra_port=None, ldap_backend_forced_uri=None, backend_type=None,
1367 ol_mmr_urls=None, ol_olc=None,
1368 setup_ds_path=None, slapd_path=None, nosync=False,
1369 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1372 :note: caution, this wipes all existing data!
1375 def setup_path(file):
1376 return os.path.join(setup_dir, file)
1378 if domainsid is None:
1379 domainsid = security.random_sid()
1381 domainsid = security.dom_sid(domainsid)
1383 # create/adapt the group policy GUIDs
1384 # Default GUID for default policy are described at
1385 # "How Core Group Policy Works"
1386 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1387 if policyguid is None:
1388 policyguid = DEFAULT_POLICY_GUID
1389 policyguid = policyguid.upper()
1390 if policyguid_dc is None:
1391 policyguid_dc = DEFAULT_DC_POLICY_GUID
1392 policyguid_dc = policyguid_dc.upper()
1394 if adminpass is None:
1395 adminpass = samba.generate_random_password(12, 32)
1396 if krbtgtpass is None:
1397 krbtgtpass = samba.generate_random_password(128, 255)
1398 if machinepass is None:
1399 machinepass = samba.generate_random_password(128, 255)
1401 dnspass = samba.generate_random_password(128, 255)
1402 if ldapadminpass is None:
1403 #Make a new, random password between Samba and it's LDAP server
1404 ldapadminpass=samba.generate_random_password(128, 255)
1406 if backend_type is None:
1407 backend_type = "ldb"
1409 sid_generator = "internal"
1410 if backend_type == "fedora-ds":
1411 sid_generator = "backend"
1413 root_uid = findnss_uid([root or "root"])
1414 nobody_uid = findnss_uid([nobody or "nobody"])
1415 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1417 wheel_gid = findnss_gid(["wheel", "adm"])
1419 wheel_gid = findnss_gid([wheel])
1421 bind_gid = findnss_gid(["bind", "named"])
1425 if targetdir is not None:
1426 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1427 elif smbconf is None:
1428 smbconf = samba.param.default_path()
1429 if not os.path.exists(os.path.dirname(smbconf)):
1430 os.makedirs(os.path.dirname(smbconf))
1432 # only install a new smb.conf if there isn't one there already
1433 if os.path.exists(smbconf):
1434 # if Samba Team members can't figure out the weird errors
1435 # loading an empty smb.conf gives, then we need to be smarter.
1436 # Pretend it just didn't exist --abartlet
1437 data = open(smbconf, 'r').read()
1438 data = data.lstrip()
1439 if data is None or data == "":
1440 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1441 serverrole, targetdir, sid_generator, useeadb)
1443 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1444 targetdir, sid_generator, useeadb)
1446 lp = samba.param.LoadParm()
1448 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1449 dnsdomain=realm, serverrole=serverrole,
1450 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1451 serverdn=serverdn, sitename=sitename)
1452 paths = provision_paths_from_lp(lp, names.dnsdomain)
1454 paths.bind_gid = bind_gid
1457 hostips = samba.interface_ips(lp, False)
1458 if len(hostips) == 0:
1459 logger.warning("No external IPv4 address has been found. Using loopback.")
1460 hostip = '127.0.0.1'
1463 if len(hostips) > 1:
1464 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1468 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1471 if hostip6 == '::1' and ip[-1][0] != '::1':
1473 except socket.gaierror, (socket.EAI_NODATA, msg):
1476 if serverrole is None:
1477 serverrole = lp.get("server role")
1479 assert serverrole in ("domain controller", "member server", "standalone")
1480 if invocationid is None:
1481 invocationid = str(uuid.uuid4())
1483 if not os.path.exists(paths.private_dir):
1484 os.mkdir(paths.private_dir)
1485 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1486 os.mkdir(os.path.join(paths.private_dir, "tls"))
1488 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1490 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn)
1492 if backend_type == "ldb":
1493 provision_backend = LDBBackend(backend_type,
1494 paths=paths, setup_path=setup_path,
1495 lp=lp, credentials=credentials,
1498 elif backend_type == "existing":
1499 provision_backend = ExistingBackend(backend_type,
1500 paths=paths, setup_path=setup_path,
1501 lp=lp, credentials=credentials,
1504 ldap_backend_forced_uri=ldap_backend_forced_uri)
1505 elif backend_type == "fedora-ds":
1506 provision_backend = FDSBackend(backend_type,
1507 paths=paths, setup_path=setup_path,
1508 lp=lp, credentials=credentials,
1511 domainsid=domainsid,
1514 ldapadminpass=ldapadminpass,
1515 slapd_path=slapd_path,
1516 ldap_backend_extra_port=ldap_backend_extra_port,
1517 ldap_dryrun_mode=ldap_dryrun_mode,
1519 setup_ds_path=setup_ds_path,
1520 ldap_backend_forced_uri=ldap_backend_forced_uri)
1521 elif backend_type == "openldap":
1522 provision_backend = OpenLDAPBackend(backend_type,
1523 paths=paths, setup_path=setup_path,
1524 lp=lp, credentials=credentials,
1527 domainsid=domainsid,
1530 ldapadminpass=ldapadminpass,
1531 slapd_path=slapd_path,
1532 ldap_backend_extra_port=ldap_backend_extra_port,
1533 ldap_dryrun_mode=ldap_dryrun_mode,
1534 ol_mmr_urls=ol_mmr_urls,
1536 ldap_backend_forced_uri=ldap_backend_forced_uri)
1538 raise ValueError("Unknown LDAP backend type selected")
1540 provision_backend.init()
1541 provision_backend.start()
1543 # only install a new shares config db if there is none
1544 if not os.path.exists(paths.shareconf):
1545 logger.info("Setting up share.ldb")
1546 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1548 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1550 logger.info("Setting up secrets.ldb")
1551 secrets_ldb = setup_secretsdb(paths, setup_path,
1552 session_info=session_info,
1553 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1556 logger.info("Setting up the registry")
1557 setup_registry(paths.hklm, setup_path, session_info,
1560 logger.info("Setting up the privileges database")
1561 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1563 logger.info("Setting up idmap db")
1564 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1567 logger.info("Setting up SAM db")
1568 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1569 provision_backend, lp, names,
1571 domainsid=domainsid,
1572 schema=schema, domainguid=domainguid,
1573 policyguid=policyguid, policyguid_dc=policyguid_dc,
1575 adminpass=adminpass, krbtgtpass=krbtgtpass,
1576 invocationid=invocationid,
1577 machinepass=machinepass, dnspass=dnspass,
1578 ntdsguid=ntdsguid, serverrole=serverrole,
1579 dom_for_fun_level=dom_for_fun_level,
1580 am_rodc=am_rodc, next_rid=next_rid)
1582 if serverrole == "domain controller":
1583 if paths.netlogon is None:
1584 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1585 logger.info("Please either remove %s or see the template at %s" %
1586 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1587 assert paths.netlogon is not None
1589 if paths.sysvol is None:
1590 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1591 " are configuring a DC.")
1592 logger.info("Please either remove %s or see the template at %s" %
1593 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1594 assert paths.sysvol is not None
1596 if not os.path.isdir(paths.netlogon):
1597 os.makedirs(paths.netlogon, 0755)
1599 if samdb_fill == FILL_FULL:
1600 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1601 root_uid=root_uid, nobody_uid=nobody_uid,
1602 users_gid=users_gid, wheel_gid=wheel_gid)
1604 if serverrole == "domain controller":
1605 # Set up group policies (domain policy and domain controller policy)
1606 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1607 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1608 domainsid, names.dnsdomain, names.domaindn, lp)
1610 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1611 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1613 secretsdb_self_join(secrets_ldb, domain=names.domain,
1615 dnsdomain=names.dnsdomain,
1616 netbiosname=names.netbiosname,
1617 domainsid=domainsid,
1618 machinepass=machinepass,
1619 secure_channel_type=SEC_CHAN_BDC)
1621 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1622 # In future, this might be determined from some configuration
1623 kerberos_enctypes = str(ENC_ALL_TYPES)
1626 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1627 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1628 flags=ldb.FLAG_MOD_REPLACE,
1629 name="msDS-SupportedEncryptionTypes")
1631 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1632 # It might be that this attribute does not exist in this schema
1636 if serverrole == "domain controller":
1637 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1639 realm=names.realm, dnsdomain=names.dnsdomain,
1640 dns_keytab_path=paths.dns_keytab,
1643 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1644 assert isinstance(domainguid, str)
1646 # Only make a zone file on the first DC, it should be replicated
1647 # with DNS replication
1648 create_zone_file(lp, logger, paths, targetdir, setup_path,
1649 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1650 hostname=names.hostname, realm=names.realm,
1651 domainguid=domainguid, ntdsguid=names.ntdsguid)
1653 create_named_conf(paths, setup_path, realm=names.realm,
1654 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1656 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1657 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1658 keytab_name=paths.dns_keytab)
1659 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1660 logger.info("and %s for further documentation required for secure DNS "
1661 "updates", paths.namedtxt)
1663 lastProvisionUSNs = get_last_provision_usn(samdb)
1664 maxUSN = get_max_usn(samdb, str(names.rootdn))
1665 if lastProvisionUSNs is not None:
1666 update_provision_usn(samdb, 0, maxUSN, 1)
1668 set_provision_usn(samdb, 0, maxUSN)
1670 create_krb5_conf(paths.krb5conf, setup_path,
1671 dnsdomain=names.dnsdomain, hostname=names.hostname,
1673 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1674 "generated at %s", paths.krb5conf)
1676 if serverrole == "domain controller":
1677 create_dns_update_list(lp, logger, paths, setup_path)
1679 provision_backend.post_setup()
1680 provision_backend.shutdown()
1682 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1685 secrets_ldb.transaction_cancel()
1688 #Now commit the secrets.ldb to disk
1689 secrets_ldb.transaction_commit()
1691 # the commit creates the dns.keytab, now chown it
1692 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1693 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1695 os.chmod(dns_keytab_path, 0640)
1696 os.chown(dns_keytab_path, -1, paths.bind_gid)
1698 if not os.environ.has_key('SAMBA_SELFTEST'):
1699 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1703 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1704 paths.phpldapadminconfig)
1706 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1707 logger.info("Server Role: %s" % serverrole)
1708 logger.info("Hostname: %s" % names.hostname)
1709 logger.info("NetBIOS Domain: %s" % names.domain)
1710 logger.info("DNS Domain: %s" % names.dnsdomain)
1711 logger.info("DOMAIN SID: %s" % str(domainsid))
1712 if samdb_fill == FILL_FULL:
1713 logger.info("Admin password: %s" % adminpass)
1714 if provision_backend.type is not "ldb":
1715 if provision_backend.credentials.get_bind_dn() is not None:
1716 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1718 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1720 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1722 if provision_backend.slapd_command_escaped is not None:
1723 # now display slapd_command_file.txt to show how slapd must be started next time
1724 logger.info("Use later the following commandline to start slapd, then Samba:")
1725 logger.info(provision_backend.slapd_command_escaped)
1726 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1727 provision_backend.ldapdir)
1729 result = ProvisionResult()
1730 result.domaindn = domaindn
1731 result.paths = paths
1733 result.samdb = samdb
1737 def provision_become_dc(setup_dir=None,
1738 smbconf=None, targetdir=None, realm=None,
1739 rootdn=None, domaindn=None, schemadn=None,
1740 configdn=None, serverdn=None,
1741 domain=None, hostname=None, domainsid=None,
1742 adminpass=None, krbtgtpass=None, domainguid=None,
1743 policyguid=None, policyguid_dc=None, invocationid=None,
1745 dnspass=None, root=None, nobody=None, users=None,
1746 wheel=None, backup=None, serverrole=None,
1747 ldap_backend=None, ldap_backend_type=None,
1748 sitename=None, debuglevel=1):
1750 logger = logging.getLogger("provision")
1751 samba.set_debug_level(debuglevel)
1753 res = provision(setup_dir, logger, system_session(), None,
1754 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1755 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1756 configdn=configdn, serverdn=serverdn, domain=domain,
1757 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1758 machinepass=machinepass, serverrole="domain controller",
1760 res.lp.set("debuglevel", str(debuglevel))
1764 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1765 """Create a PHP LDAP admin configuration file.
1767 :param path: Path to write the configuration to.
1768 :param setup_path: Function to generate setup paths.
1770 setup_file(setup_path("phpldapadmin-config.php"), path,
1771 {"S4_LDAPI_URI": ldapi_uri})
1774 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1775 hostip, hostip6, hostname, realm, domainguid,
1777 """Write out a DNS zone file, from the info in the current database.
1779 :param paths: paths object
1780 :param setup_path: Setup path function.
1781 :param dnsdomain: DNS Domain name
1782 :param domaindn: DN of the Domain
1783 :param hostip: Local IPv4 IP
1784 :param hostip6: Local IPv6 IP
1785 :param hostname: Local hostname
1786 :param realm: Realm name
1787 :param domainguid: GUID of the domain.
1788 :param ntdsguid: GUID of the hosts nTDSDSA record.
1790 assert isinstance(domainguid, str)
1792 if hostip6 is not None:
1793 hostip6_base_line = " IN AAAA " + hostip6
1794 hostip6_host_line = hostname + " IN AAAA " + hostip6
1795 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1797 hostip6_base_line = ""
1798 hostip6_host_line = ""
1799 gc_msdcs_ip6_line = ""
1801 if hostip is not None:
1802 hostip_base_line = " IN A " + hostip
1803 hostip_host_line = hostname + " IN A " + hostip
1804 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1806 hostip_base_line = ""
1807 hostip_host_line = ""
1808 gc_msdcs_ip_line = ""
1810 dns_dir = os.path.dirname(paths.dns)
1813 shutil.rmtree(dns_dir, True)
1817 os.mkdir(dns_dir, 0775)
1819 # we need to freeze the zone while we update the contents
1820 if targetdir is None:
1821 rndc = ' '.join(lp.get("rndc command"))
1822 os.system(rndc + " freeze " + lp.get("realm"))
1824 setup_file(setup_path("provision.zone"), paths.dns, {
1825 "HOSTNAME": hostname,
1826 "DNSDOMAIN": dnsdomain,
1828 "HOSTIP_BASE_LINE": hostip_base_line,
1829 "HOSTIP_HOST_LINE": hostip_host_line,
1830 "DOMAINGUID": domainguid,
1831 "DATESTRING": time.strftime("%Y%m%d%H"),
1832 "DEFAULTSITE": DEFAULTSITE,
1833 "NTDSGUID": ntdsguid,
1834 "HOSTIP6_BASE_LINE": hostip6_base_line,
1835 "HOSTIP6_HOST_LINE": hostip6_host_line,
1836 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1837 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1840 # note that we use no variable substitution on this file
1841 # the substitution is done at runtime by samba_dnsupdate
1842 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1844 # and the SPN update list
1845 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1847 if paths.bind_gid is not None:
1849 os.chown(dns_dir, -1, paths.bind_gid)
1850 os.chown(paths.dns, -1, paths.bind_gid)
1851 # chmod needed to cope with umask
1852 os.chmod(dns_dir, 0775)
1853 os.chmod(paths.dns, 0664)
1855 if not os.environ.has_key('SAMBA_SELFTEST'):
1856 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1858 if targetdir is None:
1859 os.system(rndc + " unfreeze " + lp.get("realm"))
1862 def create_dns_update_list(lp, logger, paths, setup_path):
1863 """Write out a dns_update_list file"""
1864 # note that we use no variable substitution on this file
1865 # the substitution is done at runtime by samba_dnsupdate
1866 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1867 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1870 def create_named_conf(paths, setup_path, realm, dnsdomain,
1872 """Write out a file containing zone statements suitable for inclusion in a
1873 named.conf file (including GSS-TSIG configuration).
1875 :param paths: all paths
1876 :param setup_path: Setup path function.
1877 :param realm: Realm name
1878 :param dnsdomain: DNS Domain name
1879 :param private_dir: Path to private directory
1880 :param keytab_name: File name of DNS keytab file
1883 setup_file(setup_path("named.conf"), paths.namedconf, {
1884 "DNSDOMAIN": dnsdomain,
1886 "ZONE_FILE": paths.dns,
1887 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1888 "NAMED_CONF": paths.namedconf,
1889 "NAMED_CONF_UPDATE": paths.namedconf_update
1892 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1895 def create_named_txt(path, setup_path, realm, dnsdomain,
1896 private_dir, keytab_name):
1897 """Write out a file containing zone statements suitable for inclusion in a
1898 named.conf file (including GSS-TSIG configuration).
1900 :param path: Path of the new named.conf file.
1901 :param setup_path: Setup path function.
1902 :param realm: Realm name
1903 :param dnsdomain: DNS Domain name
1904 :param private_dir: Path to private directory
1905 :param keytab_name: File name of DNS keytab file
1908 setup_file(setup_path("named.txt"), path, {
1909 "DNSDOMAIN": dnsdomain,
1911 "DNS_KEYTAB": keytab_name,
1912 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1913 "PRIVATE_DIR": private_dir
1917 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1918 """Write out a file containing zone statements suitable for inclusion in a
1919 named.conf file (including GSS-TSIG configuration).
1921 :param path: Path of the new named.conf file.
1922 :param setup_path: Setup path function.
1923 :param dnsdomain: DNS Domain name
1924 :param hostname: Local hostname
1925 :param realm: Realm name
1927 setup_file(setup_path("krb5.conf"), path, {
1928 "DNSDOMAIN": dnsdomain,
1929 "HOSTNAME": hostname,
1934 class ProvisioningError(Exception):
1935 """A generic provision error."""
1937 def __init__(self, value):
1941 return "ProvisioningError: " + self.value
1944 class InvalidNetbiosName(Exception):
1945 """A specified name was not a valid NetBIOS name."""
1946 def __init__(self, name):
1947 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)