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 "DOMAINSID": str(domainsid),
1131 "DESCRIPTOR": descr,
1132 "DOMAINGUID": domainguid_line
1135 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1136 "DOMAINDN": names.domaindn,
1137 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1138 "NEXTRID": str(next_rid),
1139 "DEFAULTSITE": names.sitename,
1140 "CONFIGDN": names.configdn,
1141 "POLICYGUID": policyguid,
1142 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1143 "SAMBA_VERSION_STRING": version
1146 logger.info("Adding configuration container")
1147 descr = b64encode(get_config_descriptor(domainsid))
1148 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1149 "CONFIGDN": names.configdn,
1150 "DESCRIPTOR": descr,
1153 # The LDIF here was created when the Schema object was constructed
1154 logger.info("Setting up sam.ldb schema")
1155 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1156 samdb.modify_ldif(schema.schema_dn_modify)
1157 samdb.write_prefixes_from_schema()
1158 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1159 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1160 {"SCHEMADN": names.schemadn})
1162 logger.info("Reopening sam.ldb with new schema")
1164 samdb.transaction_cancel()
1167 samdb.transaction_commit()
1169 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1170 credentials=provision_backend.credentials, lp=lp,
1171 global_schema=False, am_rodc=am_rodc)
1173 # Set the NTDS settings DN manually - in order to have it already around
1174 # before the provisioned tree exists and we connect
1175 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1179 samdb.transaction_start()
1181 samdb.invocation_id = invocationid
1183 logger.info("Setting up sam.ldb configuration data")
1184 descr = b64encode(get_sites_descriptor(domainsid))
1185 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1186 "CONFIGDN": names.configdn,
1187 "NETBIOSNAME": names.netbiosname,
1188 "DEFAULTSITE": names.sitename,
1189 "DNSDOMAIN": names.dnsdomain,
1190 "DOMAIN": names.domain,
1191 "SCHEMADN": names.schemadn,
1192 "DOMAINDN": names.domaindn,
1193 "SERVERDN": names.serverdn,
1194 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1195 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1196 "SITES_DESCRIPTOR": descr
1199 logger.info("Setting up display specifiers")
1200 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1201 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1202 check_all_substituted(display_specifiers_ldif)
1203 samdb.add_ldif(display_specifiers_ldif)
1205 logger.info("Adding users container")
1206 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1207 "DOMAINDN": names.domaindn})
1208 logger.info("Modifying users container")
1209 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1210 "DOMAINDN": names.domaindn})
1211 logger.info("Adding computers container")
1212 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1213 "DOMAINDN": names.domaindn})
1214 logger.info("Modifying computers container")
1215 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1216 "DOMAINDN": names.domaindn})
1217 logger.info("Setting up sam.ldb data")
1218 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1219 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1220 "DOMAINDN": names.domaindn,
1221 "NETBIOSNAME": names.netbiosname,
1222 "DEFAULTSITE": names.sitename,
1223 "CONFIGDN": names.configdn,
1224 "SERVERDN": names.serverdn,
1225 "RIDAVAILABLESTART": str(next_rid + 600),
1226 "POLICYGUID_DC": policyguid_dc
1229 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1230 "DOMAINDN": names.domaindn})
1232 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1233 "CONFIGDN": names.configdn,
1234 "SCHEMADN": names.schemadn})
1235 if fill == FILL_FULL:
1236 logger.info("Setting up sam.ldb users and groups")
1237 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1238 "DOMAINDN": names.domaindn,
1239 "DOMAINSID": str(domainsid),
1240 "CONFIGDN": names.configdn,
1241 "ADMINPASS_B64": b64encode(adminpass),
1242 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1245 logger.info("Setting up self join")
1246 setup_self_join(samdb, names=names, invocationid=invocationid,
1248 machinepass=machinepass,
1249 domainsid=domainsid,
1251 policyguid=policyguid,
1252 policyguid_dc=policyguid_dc,
1253 setup_path=setup_path,
1254 domainControllerFunctionality=domainControllerFunctionality,
1257 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1258 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1259 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1260 assert isinstance(names.ntdsguid, str)
1262 samdb.transaction_cancel()
1265 samdb.transaction_commit()
1270 FILL_NT4SYNC = "NT4SYNC"
1272 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1273 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)"
1275 def set_dir_acl(path, acl, lp, domsid):
1276 setntacl(lp, path, acl, domsid)
1277 for root, dirs, files in os.walk(path, topdown=False):
1279 setntacl(lp, os.path.join(root, name), acl, domsid)
1281 setntacl(lp, os.path.join(root, name), acl, domsid)
1284 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1285 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1288 :param sysvol: Physical path for the sysvol folder
1289 :param dnsdomain: The DNS name of the domain
1290 :param domainsid: The SID of the domain
1291 :param domaindn: The DN of the domain (ie. DC=...)
1292 :param samdb: An LDB object on the SAM db
1293 :param lp: an LP object
1296 # Set ACL for GPO root folder
1297 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1298 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1300 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1301 attrs=["cn", "nTSecurityDescriptor"],
1302 expression="", scope=ldb.SCOPE_ONELEVEL)
1305 acl = ndr_unpack(security.descriptor,
1306 str(policy["nTSecurityDescriptor"])).as_sddl()
1307 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1308 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1311 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1313 """Set the ACL for the sysvol share and the subfolders
1315 :param samdb: An LDB object on the SAM db
1316 :param netlogon: Physical path for the netlogon folder
1317 :param sysvol: Physical path for the sysvol folder
1318 :param gid: The GID of the "Domain adminstrators" group
1319 :param domainsid: The SID of the domain
1320 :param dnsdomain: The DNS name of the domain
1321 :param domaindn: The DN of the domain (ie. DC=...)
1325 os.chown(sysvol,-1,gid)
1331 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1332 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1333 for root, dirs, files in os.walk(sysvol, topdown=False):
1336 os.chown(os.path.join(root, name), -1, gid)
1337 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1340 os.chown(os.path.join(root, name), -1, gid)
1341 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1343 # Set acls on Policy folder and policies folders
1344 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1347 def provision(setup_dir, logger, session_info,
1348 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1350 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1352 domain=None, hostname=None, hostip=None, hostip6=None,
1353 domainsid=None, next_rid=1000,
1354 adminpass=None, ldapadminpass=None,
1355 krbtgtpass=None, domainguid=None,
1356 policyguid=None, policyguid_dc=None, invocationid=None,
1357 machinepass=None, ntdsguid=None,
1358 dnspass=None, root=None, nobody=None, users=None,
1359 wheel=None, backup=None, aci=None, serverrole=None,
1360 dom_for_fun_level=None,
1361 ldap_backend_extra_port=None, ldap_backend_forced_uri=None, backend_type=None,
1363 ol_mmr_urls=None, ol_olc=None,
1364 setup_ds_path=None, slapd_path=None, nosync=False,
1365 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1368 :note: caution, this wipes all existing data!
1371 def setup_path(file):
1372 return os.path.join(setup_dir, file)
1374 if domainsid is None:
1375 domainsid = security.random_sid()
1377 domainsid = security.dom_sid(domainsid)
1379 # create/adapt the group policy GUIDs
1380 # Default GUID for default policy are described at
1381 # "How Core Group Policy Works"
1382 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1383 if policyguid is None:
1384 policyguid = DEFAULT_POLICY_GUID
1385 policyguid = policyguid.upper()
1386 if policyguid_dc is None:
1387 policyguid_dc = DEFAULT_DC_POLICY_GUID
1388 policyguid_dc = policyguid_dc.upper()
1390 if adminpass is None:
1391 adminpass = samba.generate_random_password(12, 32)
1392 if krbtgtpass is None:
1393 krbtgtpass = samba.generate_random_password(128, 255)
1394 if machinepass is None:
1395 machinepass = samba.generate_random_password(128, 255)
1397 dnspass = samba.generate_random_password(128, 255)
1398 if ldapadminpass is None:
1399 #Make a new, random password between Samba and it's LDAP server
1400 ldapadminpass=samba.generate_random_password(128, 255)
1402 if backend_type is None:
1403 backend_type = "ldb"
1405 sid_generator = "internal"
1406 if backend_type == "fedora-ds":
1407 sid_generator = "backend"
1409 root_uid = findnss_uid([root or "root"])
1410 nobody_uid = findnss_uid([nobody or "nobody"])
1411 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1413 wheel_gid = findnss_gid(["wheel", "adm"])
1415 wheel_gid = findnss_gid([wheel])
1417 bind_gid = findnss_gid(["bind", "named"])
1421 if targetdir is not None:
1422 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1423 elif smbconf is None:
1424 smbconf = samba.param.default_path()
1425 if not os.path.exists(os.path.dirname(smbconf)):
1426 os.makedirs(os.path.dirname(smbconf))
1428 # only install a new smb.conf if there isn't one there already
1429 if os.path.exists(smbconf):
1430 # if Samba Team members can't figure out the weird errors
1431 # loading an empty smb.conf gives, then we need to be smarter.
1432 # Pretend it just didn't exist --abartlet
1433 data = open(smbconf, 'r').read()
1434 data = data.lstrip()
1435 if data is None or data == "":
1436 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1437 serverrole, targetdir, sid_generator, useeadb)
1439 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1440 targetdir, sid_generator, useeadb)
1442 lp = samba.param.LoadParm()
1444 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1445 dnsdomain=realm, serverrole=serverrole,
1446 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1447 serverdn=serverdn, sitename=sitename)
1448 paths = provision_paths_from_lp(lp, names.dnsdomain)
1450 paths.bind_gid = bind_gid
1453 hostips = samba.interface_ips(lp, False)
1454 if len(hostips) == 0:
1455 logger.warning("No external IPv4 address has been found. Using loopback.")
1456 hostip = '127.0.0.1'
1459 if len(hostips) > 1:
1460 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1464 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1467 if hostip6 == '::1' and ip[-1][0] != '::1':
1469 except socket.gaierror, (socket.EAI_NODATA, msg):
1472 if serverrole is None:
1473 serverrole = lp.get("server role")
1475 assert serverrole in ("domain controller", "member server", "standalone")
1476 if invocationid is None:
1477 invocationid = str(uuid.uuid4())
1479 if not os.path.exists(paths.private_dir):
1480 os.mkdir(paths.private_dir)
1481 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1482 os.mkdir(os.path.join(paths.private_dir, "tls"))
1484 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1486 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn)
1488 if backend_type == "ldb":
1489 provision_backend = LDBBackend(backend_type,
1490 paths=paths, setup_path=setup_path,
1491 lp=lp, credentials=credentials,
1494 elif backend_type == "existing":
1495 provision_backend = ExistingBackend(backend_type,
1496 paths=paths, setup_path=setup_path,
1497 lp=lp, credentials=credentials,
1500 ldap_backend_forced_uri=ldap_backend_forced_uri)
1501 elif backend_type == "fedora-ds":
1502 provision_backend = FDSBackend(backend_type,
1503 paths=paths, setup_path=setup_path,
1504 lp=lp, credentials=credentials,
1507 domainsid=domainsid,
1510 ldapadminpass=ldapadminpass,
1511 slapd_path=slapd_path,
1512 ldap_backend_extra_port=ldap_backend_extra_port,
1513 ldap_dryrun_mode=ldap_dryrun_mode,
1515 setup_ds_path=setup_ds_path,
1516 ldap_backend_forced_uri=ldap_backend_forced_uri)
1517 elif backend_type == "openldap":
1518 provision_backend = OpenLDAPBackend(backend_type,
1519 paths=paths, setup_path=setup_path,
1520 lp=lp, credentials=credentials,
1523 domainsid=domainsid,
1526 ldapadminpass=ldapadminpass,
1527 slapd_path=slapd_path,
1528 ldap_backend_extra_port=ldap_backend_extra_port,
1529 ldap_dryrun_mode=ldap_dryrun_mode,
1530 ol_mmr_urls=ol_mmr_urls,
1532 ldap_backend_forced_uri=ldap_backend_forced_uri)
1534 raise ValueError("Unknown LDAP backend type selected")
1536 provision_backend.init()
1537 provision_backend.start()
1539 # only install a new shares config db if there is none
1540 if not os.path.exists(paths.shareconf):
1541 logger.info("Setting up share.ldb")
1542 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1544 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1546 logger.info("Setting up secrets.ldb")
1547 secrets_ldb = setup_secretsdb(paths, setup_path,
1548 session_info=session_info,
1549 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1552 logger.info("Setting up the registry")
1553 setup_registry(paths.hklm, setup_path, session_info,
1556 logger.info("Setting up the privileges database")
1557 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1559 logger.info("Setting up idmap db")
1560 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1563 logger.info("Setting up SAM db")
1564 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1565 provision_backend, lp, names,
1567 domainsid=domainsid,
1568 schema=schema, domainguid=domainguid,
1569 policyguid=policyguid, policyguid_dc=policyguid_dc,
1571 adminpass=adminpass, krbtgtpass=krbtgtpass,
1572 invocationid=invocationid,
1573 machinepass=machinepass, dnspass=dnspass,
1574 ntdsguid=ntdsguid, serverrole=serverrole,
1575 dom_for_fun_level=dom_for_fun_level,
1576 am_rodc=am_rodc, next_rid=next_rid)
1578 if serverrole == "domain controller":
1579 if paths.netlogon is None:
1580 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1581 logger.info("Please either remove %s or see the template at %s" %
1582 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1583 assert paths.netlogon is not None
1585 if paths.sysvol is None:
1586 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1587 " are configuring a DC.")
1588 logger.info("Please either remove %s or see the template at %s" %
1589 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1590 assert paths.sysvol is not None
1592 if not os.path.isdir(paths.netlogon):
1593 os.makedirs(paths.netlogon, 0755)
1595 if samdb_fill == FILL_FULL:
1596 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1597 root_uid=root_uid, nobody_uid=nobody_uid,
1598 users_gid=users_gid, wheel_gid=wheel_gid)
1600 if serverrole == "domain controller":
1601 # Set up group policies (domain policy and domain controller policy)
1602 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1603 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1604 domainsid, names.dnsdomain, names.domaindn, lp)
1606 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1607 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1609 secretsdb_self_join(secrets_ldb, domain=names.domain,
1611 dnsdomain=names.dnsdomain,
1612 netbiosname=names.netbiosname,
1613 domainsid=domainsid,
1614 machinepass=machinepass,
1615 secure_channel_type=SEC_CHAN_BDC)
1617 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1618 # In future, this might be determined from some configuration
1619 kerberos_enctypes = str(ENC_ALL_TYPES)
1622 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1623 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1624 flags=ldb.FLAG_MOD_REPLACE,
1625 name="msDS-SupportedEncryptionTypes")
1627 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1628 # It might be that this attribute does not exist in this schema
1632 if serverrole == "domain controller":
1633 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1635 realm=names.realm, dnsdomain=names.dnsdomain,
1636 dns_keytab_path=paths.dns_keytab,
1639 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1640 assert isinstance(domainguid, str)
1642 # Only make a zone file on the first DC, it should be replicated
1643 # with DNS replication
1644 create_zone_file(lp, logger, paths, targetdir, setup_path,
1645 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1646 hostname=names.hostname, realm=names.realm,
1647 domainguid=domainguid, ntdsguid=names.ntdsguid)
1649 create_named_conf(paths, setup_path, realm=names.realm,
1650 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1652 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1653 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1654 keytab_name=paths.dns_keytab)
1655 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1656 logger.info("and %s for further documentation required for secure DNS "
1657 "updates", paths.namedtxt)
1659 lastProvisionUSNs = get_last_provision_usn(samdb)
1660 maxUSN = get_max_usn(samdb, str(names.rootdn))
1661 if lastProvisionUSNs is not None:
1662 update_provision_usn(samdb, 0, maxUSN, 1)
1664 set_provision_usn(samdb, 0, maxUSN)
1666 create_krb5_conf(paths.krb5conf, setup_path,
1667 dnsdomain=names.dnsdomain, hostname=names.hostname,
1669 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1670 "generated at %s", paths.krb5conf)
1672 if serverrole == "domain controller":
1673 create_dns_update_list(lp, logger, paths, setup_path)
1675 provision_backend.post_setup()
1676 provision_backend.shutdown()
1678 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1681 secrets_ldb.transaction_cancel()
1684 #Now commit the secrets.ldb to disk
1685 secrets_ldb.transaction_commit()
1687 # the commit creates the dns.keytab, now chown it
1688 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1689 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1691 os.chmod(dns_keytab_path, 0640)
1692 os.chown(dns_keytab_path, -1, paths.bind_gid)
1694 if not os.environ.has_key('SAMBA_SELFTEST'):
1695 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1699 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1700 paths.phpldapadminconfig)
1702 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1703 logger.info("Server Role: %s" % serverrole)
1704 logger.info("Hostname: %s" % names.hostname)
1705 logger.info("NetBIOS Domain: %s" % names.domain)
1706 logger.info("DNS Domain: %s" % names.dnsdomain)
1707 logger.info("DOMAIN SID: %s" % str(domainsid))
1708 if samdb_fill == FILL_FULL:
1709 logger.info("Admin password: %s" % adminpass)
1710 if provision_backend.type is not "ldb":
1711 if provision_backend.credentials.get_bind_dn() is not None:
1712 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1714 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1716 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1718 if provision_backend.slapd_command_escaped is not None:
1719 # now display slapd_command_file.txt to show how slapd must be started next time
1720 logger.info("Use later the following commandline to start slapd, then Samba:")
1721 logger.info(provision_backend.slapd_command_escaped)
1722 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1723 provision_backend.ldapdir)
1725 result = ProvisionResult()
1726 result.domaindn = domaindn
1727 result.paths = paths
1729 result.samdb = samdb
1733 def provision_become_dc(setup_dir=None,
1734 smbconf=None, targetdir=None, realm=None,
1735 rootdn=None, domaindn=None, schemadn=None,
1736 configdn=None, serverdn=None,
1737 domain=None, hostname=None, domainsid=None,
1738 adminpass=None, krbtgtpass=None, domainguid=None,
1739 policyguid=None, policyguid_dc=None, invocationid=None,
1741 dnspass=None, root=None, nobody=None, users=None,
1742 wheel=None, backup=None, serverrole=None,
1743 ldap_backend=None, ldap_backend_type=None,
1744 sitename=None, debuglevel=1):
1746 logger = logging.getLogger("provision")
1747 samba.set_debug_level(debuglevel)
1749 res = provision(setup_dir, logger, system_session(), None,
1750 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1751 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1752 configdn=configdn, serverdn=serverdn, domain=domain,
1753 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1754 machinepass=machinepass, serverrole="domain controller",
1756 res.lp.set("debuglevel", str(debuglevel))
1760 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1761 """Create a PHP LDAP admin configuration file.
1763 :param path: Path to write the configuration to.
1764 :param setup_path: Function to generate setup paths.
1766 setup_file(setup_path("phpldapadmin-config.php"), path,
1767 {"S4_LDAPI_URI": ldapi_uri})
1770 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1771 hostip, hostip6, hostname, realm, domainguid,
1773 """Write out a DNS zone file, from the info in the current database.
1775 :param paths: paths object
1776 :param setup_path: Setup path function.
1777 :param dnsdomain: DNS Domain name
1778 :param domaindn: DN of the Domain
1779 :param hostip: Local IPv4 IP
1780 :param hostip6: Local IPv6 IP
1781 :param hostname: Local hostname
1782 :param realm: Realm name
1783 :param domainguid: GUID of the domain.
1784 :param ntdsguid: GUID of the hosts nTDSDSA record.
1786 assert isinstance(domainguid, str)
1788 if hostip6 is not None:
1789 hostip6_base_line = " IN AAAA " + hostip6
1790 hostip6_host_line = hostname + " IN AAAA " + hostip6
1791 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1793 hostip6_base_line = ""
1794 hostip6_host_line = ""
1795 gc_msdcs_ip6_line = ""
1797 if hostip is not None:
1798 hostip_base_line = " IN A " + hostip
1799 hostip_host_line = hostname + " IN A " + hostip
1800 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1802 hostip_base_line = ""
1803 hostip_host_line = ""
1804 gc_msdcs_ip_line = ""
1806 dns_dir = os.path.dirname(paths.dns)
1809 shutil.rmtree(dns_dir, True)
1813 os.mkdir(dns_dir, 0775)
1815 # we need to freeze the zone while we update the contents
1816 if targetdir is None:
1817 rndc = ' '.join(lp.get("rndc command"))
1818 os.system(rndc + " freeze " + lp.get("realm"))
1820 setup_file(setup_path("provision.zone"), paths.dns, {
1821 "HOSTNAME": hostname,
1822 "DNSDOMAIN": dnsdomain,
1824 "HOSTIP_BASE_LINE": hostip_base_line,
1825 "HOSTIP_HOST_LINE": hostip_host_line,
1826 "DOMAINGUID": domainguid,
1827 "DATESTRING": time.strftime("%Y%m%d%H"),
1828 "DEFAULTSITE": DEFAULTSITE,
1829 "NTDSGUID": ntdsguid,
1830 "HOSTIP6_BASE_LINE": hostip6_base_line,
1831 "HOSTIP6_HOST_LINE": hostip6_host_line,
1832 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1833 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1836 # note that we use no variable substitution on this file
1837 # the substitution is done at runtime by samba_dnsupdate
1838 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1840 # and the SPN update list
1841 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1843 if paths.bind_gid is not None:
1845 os.chown(dns_dir, -1, paths.bind_gid)
1846 os.chown(paths.dns, -1, paths.bind_gid)
1847 # chmod needed to cope with umask
1848 os.chmod(dns_dir, 0775)
1849 os.chmod(paths.dns, 0664)
1851 if not os.environ.has_key('SAMBA_SELFTEST'):
1852 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1854 if targetdir is None:
1855 os.system(rndc + " unfreeze " + lp.get("realm"))
1858 def create_dns_update_list(lp, logger, paths, setup_path):
1859 """Write out a dns_update_list file"""
1860 # note that we use no variable substitution on this file
1861 # the substitution is done at runtime by samba_dnsupdate
1862 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1863 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1866 def create_named_conf(paths, setup_path, realm, dnsdomain,
1868 """Write out a file containing zone statements suitable for inclusion in a
1869 named.conf file (including GSS-TSIG configuration).
1871 :param paths: all paths
1872 :param setup_path: Setup path function.
1873 :param realm: Realm name
1874 :param dnsdomain: DNS Domain name
1875 :param private_dir: Path to private directory
1876 :param keytab_name: File name of DNS keytab file
1879 setup_file(setup_path("named.conf"), paths.namedconf, {
1880 "DNSDOMAIN": dnsdomain,
1882 "ZONE_FILE": paths.dns,
1883 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1884 "NAMED_CONF": paths.namedconf,
1885 "NAMED_CONF_UPDATE": paths.namedconf_update
1888 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1891 def create_named_txt(path, setup_path, realm, dnsdomain,
1892 private_dir, keytab_name):
1893 """Write out a file containing zone statements suitable for inclusion in a
1894 named.conf file (including GSS-TSIG configuration).
1896 :param path: Path of the new named.conf file.
1897 :param setup_path: Setup path function.
1898 :param realm: Realm name
1899 :param dnsdomain: DNS Domain name
1900 :param private_dir: Path to private directory
1901 :param keytab_name: File name of DNS keytab file
1904 setup_file(setup_path("named.txt"), path, {
1905 "DNSDOMAIN": dnsdomain,
1907 "DNS_KEYTAB": keytab_name,
1908 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1909 "PRIVATE_DIR": private_dir
1913 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1914 """Write out a file containing zone statements suitable for inclusion in a
1915 named.conf file (including GSS-TSIG configuration).
1917 :param path: Path of the new named.conf file.
1918 :param setup_path: Setup path function.
1919 :param dnsdomain: DNS Domain name
1920 :param hostname: Local hostname
1921 :param realm: Realm name
1923 setup_file(setup_path("krb5.conf"), path, {
1924 "DNSDOMAIN": dnsdomain,
1925 "HOSTNAME": hostname,
1930 class ProvisioningError(Exception):
1931 """A generic provision error."""
1933 def __init__(self, value):
1937 return "ProvisioningError: " + self.value
1940 class InvalidNetbiosName(Exception):
1941 """A specified name was not a valid NetBIOS name."""
1942 def __init__(self, name):
1943 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)