2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 __docformat__ = "restructuredText"
30 from base64 import b64encode
44 from samba.auth import system_session, admin_session
48 check_all_substituted,
57 from samba.dcerpc import security
58 from samba.dcerpc.misc import (
62 from samba.dsdb import (
63 DS_DOMAIN_FUNCTION_2003,
64 DS_DOMAIN_FUNCTION_2008_R2,
67 from samba.idmap import IDmapDB
68 from samba.ms_display_specifiers import read_ms_ldif
69 from samba.ntacls import setntacl, dsacl2fsacl
70 from samba.ndr import ndr_pack, ndr_unpack
71 from samba.provision.backend import (
79 from samba.schema import Schema
80 from samba.samdb import SamDB
82 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
83 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
84 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
85 DEFAULTSITE = "Default-First-Site-Name"
86 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
90 """Find the setup directory used by provision."""
93 return os.path.join(source_tree_topdir(), "source4/setup")
96 for prefix in [sys.prefix,
97 os.path.join(os.path.dirname(__file__), "../../../../..")]:
98 for suffix in ["share/setup", "share/samba/setup", "setup"]:
99 ret = os.path.normpath(os.path.join(prefix, suffix))
100 if os.path.isdir(ret):
102 raise Exception("Unable to find setup directory.")
104 # Descriptors of naming contexts and other important objects
106 # "get_schema_descriptor" is located in "schema.py"
108 def get_sites_descriptor(domain_sid):
109 sddl = "D:(A;;RPLCLORC;;;AU)" \
110 "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
111 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
112 "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
113 "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
114 "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
115 "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
116 "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
117 sec = security.descriptor.from_sddl(sddl, domain_sid)
121 def get_config_descriptor(domain_sid):
122 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
123 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
124 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
125 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
126 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
127 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
128 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
129 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
130 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
132 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
133 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
134 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
135 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
136 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
137 sec = security.descriptor.from_sddl(sddl, domain_sid)
141 def get_domain_descriptor(domain_sid):
142 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
143 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
144 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
145 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
146 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
148 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
149 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
150 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
151 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
152 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
153 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
154 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
155 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
156 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
157 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
158 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
159 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
160 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
161 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
162 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
163 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
164 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
165 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
166 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
167 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
168 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
169 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
170 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
171 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
172 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
173 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
174 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
175 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
176 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
177 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
178 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
179 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
180 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
183 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
185 "(A;;RPLCLORC;;;ED)" \
186 "(A;;RPLCLORC;;;AU)" \
187 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
188 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
189 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
190 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
191 sec = security.descriptor.from_sddl(sddl, domain_sid)
195 class ProvisionPaths(object):
198 self.shareconf = None
209 self.dns_keytab = None
212 self.private_dir = None
215 class ProvisionNames(object):
222 self.ldapmanagerdn = None
223 self.dnsdomain = None
225 self.netbiosname = None
232 def update_provision_usn(samdb, low, high, replace=False):
233 """Update the field provisionUSN in sam.ldb
235 This field is used to track range of USN modified by provision and
237 This value is used afterward by next provision to figure out if
238 the field have been modified since last provision.
240 :param samdb: An LDB object connect to sam.ldb
241 :param low: The lowest USN modified by this upgrade
242 :param high: The highest USN modified by this upgrade
243 :param replace: A boolean indicating if the range should replace any
244 existing one or appended (default)
249 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
250 LAST_PROVISION_USN_ATTRIBUTE, base="",
251 scope=ldb.SCOPE_SUBTREE,
252 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
253 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
256 tab.append("%s-%s" % (low, high))
257 delta = ldb.Message()
258 delta.dn = ldb.Dn(samdb, "@PROVISION")
259 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
260 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
264 def set_provision_usn(samdb, low, high):
265 """Set the field provisionUSN in sam.ldb
266 This field is used to track range of USN modified by provision and
268 This value is used afterward by next provision to figure out if
269 the field have been modified since last provision.
271 :param samdb: An LDB object connect to sam.ldb
272 :param low: The lowest USN modified by this upgrade
273 :param high: The highest USN modified by this upgrade"""
275 tab.append("%s-%s" % (low, high))
276 delta = ldb.Message()
277 delta.dn = ldb.Dn(samdb, "@PROVISION")
278 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
279 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
283 def get_max_usn(samdb,basedn):
284 """ This function return the biggest USN present in the provision
286 :param samdb: A LDB object pointing to the sam.ldb
287 :param basedn: A string containing the base DN of the provision
289 :return: The biggest USN in the provision"""
291 res = samdb.search(expression="objectClass=*",base=basedn,
292 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
293 controls=["search_options:1:2",
294 "server_sort:1:1:uSNChanged",
295 "paged_results:1:1"])
296 return res[0]["uSNChanged"]
299 def get_last_provision_usn(sam):
300 """Get the lastest USN modified by a provision or an upgradeprovision
302 :param sam: An LDB object pointing to the sam.ldb
303 :return: an integer corresponding to the highest USN modified by
304 (upgrade)provision, 0 is this value is unknown
306 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
307 LAST_PROVISION_USN_ATTRIBUTE,
308 base="", scope=ldb.SCOPE_SUBTREE,
309 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
314 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
315 tab = p.split(str(r))
324 class ProvisionResult(object):
333 def check_install(lp, session_info, credentials):
334 """Check whether the current install seems ok.
336 :param lp: Loadparm context
337 :param session_info: Session information
338 :param credentials: Credentials
340 if lp.get("realm") == "":
341 raise Exception("Realm empty")
342 samdb = Ldb(lp.get("sam database"), session_info=session_info,
343 credentials=credentials, lp=lp)
344 if len(samdb.search("(cn=Administrator)")) != 1:
345 raise ProvisioningError("No administrator account found")
348 def findnss(nssfn, names):
349 """Find a user or group from a list of possibilities.
351 :param nssfn: NSS Function to try (should raise KeyError if not found)
352 :param names: Names to check.
353 :return: Value return by first names list.
360 raise KeyError("Unable to find user/group in %r" % names)
363 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
364 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
367 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
368 """Setup a ldb in the private dir.
370 :param ldb: LDB file to import data into
371 :param ldif_path: Path of the LDIF file to load
372 :param subst_vars: Optional variables to subsitute in LDIF.
373 :param nocontrols: Optional list of controls, can be None for no controls
375 assert isinstance(ldif_path, str)
376 data = read_and_sub_file(ldif_path, subst_vars)
377 ldb.add_ldif(data, controls)
380 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
381 """Modify a ldb in the private dir.
383 :param ldb: LDB object.
384 :param ldif_path: LDIF file path.
385 :param subst_vars: Optional dictionary with substitution variables.
387 data = read_and_sub_file(ldif_path, subst_vars)
388 ldb.modify_ldif(data, controls)
391 def setup_ldb(ldb, ldif_path, subst_vars):
392 """Import a LDIF a file into a LDB handle, optionally substituting
395 :note: Either all LDIF data will be added or none (using transactions).
397 :param ldb: LDB file to import into.
398 :param ldif_path: Path to the LDIF file.
399 :param subst_vars: Dictionary with substitution variables.
401 assert ldb is not None
402 ldb.transaction_start()
404 setup_add_ldif(ldb, ldif_path, subst_vars)
406 ldb.transaction_cancel()
409 ldb.transaction_commit()
412 def provision_paths_from_lp(lp, dnsdomain):
413 """Set the default paths for provisioning.
415 :param lp: Loadparm context.
416 :param dnsdomain: DNS Domain name
418 paths = ProvisionPaths()
419 paths.private_dir = lp.get("private dir")
421 # This is stored without path prefix for the "privateKeytab" attribute in
422 # "secrets_dns.ldif".
423 paths.dns_keytab = "dns.keytab"
424 paths.keytab = "secrets.keytab"
426 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
427 paths.samdb = os.path.join(paths.private_dir,
428 lp.get("sam database") or "samdb.ldb")
429 paths.idmapdb = os.path.join(paths.private_dir,
430 lp.get("idmap database") or "idmap.ldb")
431 paths.secrets = os.path.join(paths.private_dir,
432 lp.get("secrets database") or "secrets.ldb")
433 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
434 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
435 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
436 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
437 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
438 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
439 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
440 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
441 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
442 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
443 paths.phpldapadminconfig = os.path.join(paths.private_dir,
444 "phpldapadmin-config.php")
445 paths.hklm = "hklm.ldb"
446 paths.hkcr = "hkcr.ldb"
447 paths.hkcu = "hkcu.ldb"
448 paths.hku = "hku.ldb"
449 paths.hkpd = "hkpd.ldb"
450 paths.hkpt = "hkpt.ldb"
451 paths.sysvol = lp.get("path", "sysvol")
452 paths.netlogon = lp.get("path", "netlogon")
453 paths.smbconf = lp.configfile
457 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
458 serverrole=None, rootdn=None, domaindn=None, configdn=None,
459 schemadn=None, serverdn=None, sitename=None):
460 """Guess configuration settings to use."""
463 hostname = socket.gethostname().split(".")[0]
465 netbiosname = lp.get("netbios name")
466 if netbiosname is None:
467 netbiosname = hostname
468 # remove forbidden chars
470 for x in netbiosname:
471 if x.isalnum() or x in VALID_NETBIOS_CHARS:
472 newnbname = "%s%c" % (newnbname, x)
473 # force the length to be <16
474 netbiosname = newnbname[0:15]
475 assert netbiosname is not None
476 netbiosname = netbiosname.upper()
477 if not valid_netbios_name(netbiosname):
478 raise InvalidNetbiosName(netbiosname)
480 if dnsdomain is None:
481 dnsdomain = lp.get("realm")
482 if dnsdomain is None or dnsdomain == "":
483 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
485 dnsdomain = dnsdomain.lower()
487 if serverrole is None:
488 serverrole = lp.get("server role")
489 if serverrole is None:
490 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
492 serverrole = serverrole.lower()
494 realm = dnsdomain.upper()
496 if lp.get("realm") == "":
497 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
499 if lp.get("realm").upper() != realm:
500 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
502 if lp.get("server role").lower() != serverrole:
503 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role").upper(), serverrole, lp.configfile))
505 if serverrole == "domain controller":
507 # This will, for better or worse, default to 'WORKGROUP'
508 domain = lp.get("workgroup")
509 domain = domain.upper()
511 if lp.get("workgroup").upper() != domain:
512 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
515 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
517 if domain == netbiosname:
518 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
522 domaindn = "DC=" + netbiosname
524 if not valid_netbios_name(domain):
525 raise InvalidNetbiosName(domain)
527 if hostname.upper() == realm:
528 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
529 if netbiosname.upper() == realm:
530 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
532 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
538 configdn = "CN=Configuration," + rootdn
540 schemadn = "CN=Schema," + configdn
545 names = ProvisionNames()
546 names.rootdn = rootdn
547 names.domaindn = domaindn
548 names.configdn = configdn
549 names.schemadn = schemadn
550 names.ldapmanagerdn = "CN=Manager," + rootdn
551 names.dnsdomain = dnsdomain
552 names.domain = domain
554 names.netbiosname = netbiosname
555 names.hostname = hostname
556 names.sitename = sitename
557 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
558 netbiosname, sitename, configdn)
563 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
564 targetdir, sid_generator="internal", eadb=False, lp=None):
565 """Create a new smb.conf file based on a couple of basic settings.
567 assert smbconf is not None
569 hostname = socket.gethostname().split(".")[0]
570 netbiosname = hostname.upper()
571 # remove forbidden chars
573 for x in netbiosname:
574 if x.isalnum() or x in VALID_NETBIOS_CHARS:
575 newnbname = "%s%c" % (newnbname, x)
576 #force the length to be <16
577 netbiosname = newnbname[0:15]
579 netbiosname = hostname.upper()
581 if serverrole is None:
582 serverrole = "standalone"
584 assert serverrole in ("domain controller", "member server", "standalone")
585 if serverrole == "domain controller":
587 elif serverrole == "member server":
588 smbconfsuffix = "member"
589 elif serverrole == "standalone":
590 smbconfsuffix = "standalone"
592 if sid_generator is None:
593 sid_generator = "internal"
595 assert domain is not None
596 domain = domain.upper()
598 assert realm is not None
599 realm = realm.upper()
602 lp = samba.param.LoadParm()
603 #Load non-existant file
604 if os.path.exists(smbconf):
606 if eadb and not lp.get("posix:eadb"):
607 if targetdir is not None:
608 privdir = os.path.join(targetdir, "private")
610 privdir = lp.get("private dir")
611 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
613 if targetdir is not None:
614 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
615 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
617 lp.set("lock dir", os.path.abspath(targetdir))
622 if sid_generator == "internal":
623 sid_generator_line = ""
625 sid_generator_line = "sid generator = " + sid_generator
627 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
628 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
630 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
632 "NETBIOS_NAME": netbiosname,
635 "SERVERROLE": serverrole,
636 "NETLOGONPATH": netlogon,
637 "SYSVOLPATH": sysvol,
638 "SIDGENERATOR_LINE": sid_generator_line,
639 "PRIVATEDIR_LINE": privatedir_line,
640 "LOCKDIR_LINE": lockdir_line
643 # reload the smb.conf
646 # and dump it without any values that are the default
647 # this ensures that any smb.conf parameters that were set
648 # on the provision/join command line are set in the resulting smb.conf
649 f = open(smbconf, mode='w')
655 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
656 users_gid, wheel_gid):
657 """setup reasonable name mappings for sam names to unix names.
659 :param samdb: SamDB object.
660 :param idmap: IDmap db object.
661 :param sid: The domain sid.
662 :param domaindn: The domain DN.
663 :param root_uid: uid of the UNIX root user.
664 :param nobody_uid: uid of the UNIX nobody user.
665 :param users_gid: gid of the UNIX users group.
666 :param wheel_gid: gid of the UNIX wheel group.
668 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
669 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
671 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
672 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
675 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
676 provision_backend, names, schema, serverrole,
678 """Setup the partitions for the SAM database.
680 Alternatively, provision() may call this, and then populate the database.
682 :note: This will wipe the Sam Database!
684 :note: This function always removes the local SAM LDB file. The erase
685 parameter controls whether to erase the existing data, which
686 may not be stored locally but in LDAP.
689 assert session_info is not None
691 # We use options=["modules:"] to stop the modules loading - we
692 # just want to wipe and re-initialise the database, not start it up
695 os.unlink(samdb_path)
699 samdb = Ldb(url=samdb_path, session_info=session_info,
700 lp=lp, options=["modules:"])
702 ldap_backend_line = "# No LDAP backend"
703 if provision_backend.type is not "ldb":
704 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
706 samdb.transaction_start()
708 logger.info("Setting up sam.ldb partitions and settings")
709 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
710 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
711 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
712 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
713 "LDAP_BACKEND_LINE": ldap_backend_line,
717 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
718 "BACKEND_TYPE": provision_backend.type,
719 "SERVER_ROLE": serverrole
722 logger.info("Setting up sam.ldb rootDSE")
723 setup_samdb_rootdse(samdb, setup_path, names)
725 samdb.transaction_cancel()
728 samdb.transaction_commit()
731 def secretsdb_self_join(secretsdb, domain,
732 netbiosname, machinepass, domainsid=None,
733 realm=None, dnsdomain=None,
735 key_version_number=1,
736 secure_channel_type=SEC_CHAN_WKSTA):
737 """Add domain join-specific bits to a secrets database.
739 :param secretsdb: Ldb Handle to the secrets database
740 :param machinepass: Machine password
742 attrs = ["whenChanged",
749 if realm is not None:
750 if dnsdomain is None:
751 dnsdomain = realm.lower()
752 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
755 shortname = netbiosname.lower()
757 # We don't need to set msg["flatname"] here, because rdn_name will handle
758 # it, and it causes problems for modifies anyway
759 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
760 msg["secureChannelType"] = [str(secure_channel_type)]
761 msg["objectClass"] = ["top", "primaryDomain"]
762 if dnsname is not None:
763 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
764 msg["realm"] = [realm]
765 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
766 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
767 msg["privateKeytab"] = ["secrets.keytab"]
769 msg["secret"] = [machinepass]
770 msg["samAccountName"] = ["%s$" % netbiosname]
771 msg["secureChannelType"] = [str(secure_channel_type)]
772 if domainsid is not None:
773 msg["objectSid"] = [ndr_pack(domainsid)]
775 # This complex expression tries to ensure that we don't have more
776 # than one record for this SID, realm or netbios domain at a time,
777 # but we don't delete the old record that we are about to modify,
778 # because that would delete the keytab and previous password.
779 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
780 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
781 scope=ldb.SCOPE_ONELEVEL)
784 secretsdb.delete(del_msg.dn)
786 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
789 msg["priorSecret"] = [res[0]["secret"][0]]
790 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
793 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
798 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
804 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
805 secretsdb.modify(msg)
806 secretsdb.rename(res[0].dn, msg.dn)
808 spn = [ 'HOST/%s' % shortname ]
809 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
810 # we are a domain controller then we add servicePrincipalName
811 # entries for the keytab code to update.
812 spn.extend([ 'HOST/%s' % dnsname ])
813 msg["servicePrincipalName"] = spn
818 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir, realm,
819 dnsdomain, dns_keytab_path, dnspass):
820 """Add DNS specific bits to a secrets database.
822 :param secretsdb: Ldb Handle to the secrets database
823 :param setup_path: Setup path function
824 :param machinepass: Machine password
827 os.unlink(os.path.join(private_dir, dns_keytab_path))
831 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
833 "DNSDOMAIN": dnsdomain,
834 "DNS_KEYTAB": dns_keytab_path,
835 "DNSPASS_B64": b64encode(dnspass),
836 "HOSTNAME": names.hostname,
837 "DNSNAME" : '%s.%s' % (
838 names.netbiosname.lower(), names.dnsdomain.lower())
842 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
843 """Setup the secrets database.
845 :note: This function does not handle exceptions and transaction on purpose,
846 it's up to the caller to do this job.
848 :param path: Path to the secrets database.
849 :param setup_path: Get the path to a setup file.
850 :param session_info: Session info.
851 :param credentials: Credentials
852 :param lp: Loadparm context
853 :return: LDB handle for the created secrets database
855 if os.path.exists(paths.secrets):
856 os.unlink(paths.secrets)
858 keytab_path = os.path.join(paths.private_dir, paths.keytab)
859 if os.path.exists(keytab_path):
860 os.unlink(keytab_path)
862 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
863 if os.path.exists(dns_keytab_path):
864 os.unlink(dns_keytab_path)
868 secrets_ldb = Ldb(path, session_info=session_info,
871 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
872 secrets_ldb = Ldb(path, session_info=session_info,
874 secrets_ldb.transaction_start()
876 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
878 if (backend_credentials is not None and
879 backend_credentials.authentication_requested()):
880 if backend_credentials.get_bind_dn() is not None:
881 setup_add_ldif(secrets_ldb,
882 setup_path("secrets_simple_ldap.ldif"), {
883 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
884 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
887 setup_add_ldif(secrets_ldb,
888 setup_path("secrets_sasl_ldap.ldif"), {
889 "LDAPADMINUSER": backend_credentials.get_username(),
890 "LDAPADMINREALM": backend_credentials.get_realm(),
891 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
896 secrets_ldb.transaction_cancel()
900 def setup_privileges(path, setup_path, session_info, lp):
901 """Setup the privileges database.
903 :param path: Path to the privileges database.
904 :param setup_path: Get the path to a setup file.
905 :param session_info: Session info.
906 :param credentials: Credentials
907 :param lp: Loadparm context
908 :return: LDB handle for the created secrets database
910 if os.path.exists(path):
912 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
913 privilege_ldb.erase()
914 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
917 def setup_registry(path, setup_path, session_info, lp):
918 """Setup the registry.
920 :param path: Path to the registry database
921 :param setup_path: Function that returns the path to a setup.
922 :param session_info: Session information
923 :param credentials: Credentials
924 :param lp: Loadparm context
926 reg = samba.registry.Registry()
927 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
928 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
929 provision_reg = setup_path("provision.reg")
930 assert os.path.exists(provision_reg)
931 reg.diff_apply(provision_reg)
934 def setup_idmapdb(path, setup_path, session_info, lp):
935 """Setup the idmap database.
937 :param path: path to the idmap database
938 :param setup_path: Function that returns a path to a setup file
939 :param session_info: Session information
940 :param credentials: Credentials
941 :param lp: Loadparm context
943 if os.path.exists(path):
946 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
948 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
952 def setup_samdb_rootdse(samdb, setup_path, names):
953 """Setup the SamDB rootdse.
955 :param samdb: Sam Database handle
956 :param setup_path: Obtain setup path
958 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
959 "SCHEMADN": names.schemadn,
960 "DOMAINDN": names.domaindn,
961 "ROOTDN": names.rootdn,
962 "CONFIGDN": names.configdn,
963 "SERVERDN": names.serverdn,
967 def setup_self_join(samdb, names, machinepass, dnspass,
968 domainsid, next_rid, invocationid, setup_path,
969 policyguid, policyguid_dc, domainControllerFunctionality,
971 """Join a host to its own domain."""
972 assert isinstance(invocationid, str)
973 if ntdsguid is not None:
974 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
977 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
978 "CONFIGDN": names.configdn,
979 "SCHEMADN": names.schemadn,
980 "DOMAINDN": names.domaindn,
981 "SERVERDN": names.serverdn,
982 "INVOCATIONID": invocationid,
983 "NETBIOSNAME": names.netbiosname,
984 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
985 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
986 "DOMAINSID": str(domainsid),
987 "DCRID": str(next_rid),
988 "SAMBA_VERSION_STRING": version,
989 "NTDSGUID": ntdsguid_line,
990 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
991 domainControllerFunctionality)})
993 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
994 "POLICYGUID": policyguid,
995 "POLICYGUID_DC": policyguid_dc,
996 "DNSDOMAIN": names.dnsdomain,
997 "DOMAINDN": names.domaindn})
999 # add the NTDSGUID based SPNs
1000 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1001 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1002 expression="", scope=ldb.SCOPE_BASE)
1003 assert isinstance(names.ntdsguid, str)
1005 # Setup fSMORoleOwner entries to point at the newly created DC entry
1006 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1007 "DOMAINDN": names.domaindn,
1008 "CONFIGDN": names.configdn,
1009 "SCHEMADN": names.schemadn,
1010 "DEFAULTSITE": names.sitename,
1011 "SERVERDN": names.serverdn,
1012 "NETBIOSNAME": names.netbiosname,
1013 "RIDALLOCATIONSTART": str(next_rid + 100),
1014 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1017 # This is partially Samba4 specific and should be replaced by the correct
1018 # DNS AD-style setup
1019 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1020 "DNSDOMAIN": names.dnsdomain,
1021 "DOMAINDN": names.domaindn,
1022 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1023 "HOSTNAME" : names.hostname,
1024 "DNSNAME" : '%s.%s' % (
1025 names.netbiosname.lower(), names.dnsdomain.lower())
1029 def getpolicypath(sysvolpath, dnsdomain, guid):
1030 """Return the physical path of policy given its guid.
1032 :param sysvolpath: Path to the sysvol folder
1033 :param dnsdomain: DNS name of the AD domain
1034 :param guid: The GUID of the policy
1035 :return: A string with the complete path to the policy folder
1039 guid = "{%s}" % guid
1040 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1044 def create_gpo_struct(policy_path):
1045 if not os.path.exists(policy_path):
1046 os.makedirs(policy_path, 0775)
1047 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1048 "[General]\r\nVersion=0")
1049 p = os.path.join(policy_path, "MACHINE")
1050 if not os.path.exists(p):
1051 os.makedirs(p, 0775)
1052 p = os.path.join(policy_path, "USER")
1053 if not os.path.exists(p):
1054 os.makedirs(p, 0775)
1057 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1058 """Create the default GPO for a domain
1060 :param sysvolpath: Physical path for the sysvol folder
1061 :param dnsdomain: DNS domain name of the AD domain
1062 :param policyguid: GUID of the default domain policy
1063 :param policyguid_dc: GUID of the default domain controler policy
1065 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1066 create_gpo_struct(policy_path)
1068 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1069 create_gpo_struct(policy_path)
1072 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1073 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1074 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1075 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1077 """Setup a complete SAM Database.
1079 :note: This will wipe the main SAM database file!
1082 # Provision does not make much sense values larger than 1000000000
1083 # as the upper range of the rIDAvailablePool is 1073741823 and
1084 # we don't want to create a domain that cannot allocate rids.
1085 if next_rid < 1000 or next_rid > 1000000000:
1086 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1087 error += "the valid range is %u-%u. The default is %u." % (
1088 1000, 1000000000, 1000)
1089 raise ProvisioningError(error)
1091 # ATTENTION: Do NOT change these default values without discussion with the
1092 # team and/or release manager. They have a big impact on the whole program!
1093 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1095 if dom_for_fun_level is None:
1096 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1098 if dom_for_fun_level > domainControllerFunctionality:
1099 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!")
1101 domainFunctionality = dom_for_fun_level
1102 forestFunctionality = dom_for_fun_level
1104 # Also wipes the database
1105 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1106 provision_backend=provision_backend, session_info=session_info,
1107 names=names, serverrole=serverrole, schema=schema)
1110 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1112 # Load the database, but don's load the global schema and don't connect
1114 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1115 credentials=provision_backend.credentials, lp=lp,
1116 global_schema=False, am_rodc=am_rodc)
1118 logger.info("Pre-loading the Samba 4 and AD schema")
1120 # Load the schema from the one we computed earlier
1121 samdb.set_schema(schema)
1123 # Set the NTDS settings DN manually - in order to have it already around
1124 # before the provisioned tree exists and we connect
1125 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1127 # And now we can connect to the DB - the schema won't be loaded from the
1131 if fill == FILL_DRS:
1134 samdb.transaction_start()
1136 # Set the domain functionality levels onto the database.
1137 # Various module (the password_hash module in particular) need
1138 # to know what level of AD we are emulating.
1140 # These will be fixed into the database via the database
1141 # modifictions below, but we need them set from the start.
1142 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1143 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1144 samdb.set_opaque_integer("domainControllerFunctionality",
1145 domainControllerFunctionality)
1147 samdb.set_domain_sid(str(domainsid))
1148 samdb.set_invocation_id(invocationid)
1150 logger.info("Adding DomainDN: %s" % names.domaindn)
1152 # impersonate domain admin
1153 admin_session_info = admin_session(lp, str(domainsid))
1154 samdb.set_session_info(admin_session_info)
1155 if domainguid is not None:
1156 domainguid_line = "objectGUID: %s\n-" % domainguid
1158 domainguid_line = ""
1160 descr = b64encode(get_domain_descriptor(domainsid))
1161 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1162 "DOMAINDN": names.domaindn,
1163 "DOMAINSID": str(domainsid),
1164 "DESCRIPTOR": descr,
1165 "DOMAINGUID": domainguid_line
1168 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1169 "DOMAINDN": names.domaindn,
1170 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1171 "NEXTRID": str(next_rid),
1172 "DEFAULTSITE": names.sitename,
1173 "CONFIGDN": names.configdn,
1174 "POLICYGUID": policyguid,
1175 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1176 "SAMBA_VERSION_STRING": version
1179 logger.info("Adding configuration container")
1180 descr = b64encode(get_config_descriptor(domainsid))
1181 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1182 "CONFIGDN": names.configdn,
1183 "DESCRIPTOR": descr,
1186 # The LDIF here was created when the Schema object was constructed
1187 logger.info("Setting up sam.ldb schema")
1188 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1189 samdb.modify_ldif(schema.schema_dn_modify)
1190 samdb.write_prefixes_from_schema()
1191 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1192 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1193 {"SCHEMADN": names.schemadn})
1195 logger.info("Reopening sam.ldb with new schema")
1197 samdb.transaction_cancel()
1200 samdb.transaction_commit()
1202 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1203 credentials=provision_backend.credentials, lp=lp,
1204 global_schema=False, am_rodc=am_rodc)
1206 # Set the NTDS settings DN manually - in order to have it already around
1207 # before the provisioned tree exists and we connect
1208 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1211 samdb.transaction_start()
1213 samdb.invocation_id = invocationid
1215 logger.info("Setting up sam.ldb configuration data")
1216 descr = b64encode(get_sites_descriptor(domainsid))
1217 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1218 "CONFIGDN": names.configdn,
1219 "NETBIOSNAME": names.netbiosname,
1220 "DEFAULTSITE": names.sitename,
1221 "DNSDOMAIN": names.dnsdomain,
1222 "DOMAIN": names.domain,
1223 "SCHEMADN": names.schemadn,
1224 "DOMAINDN": names.domaindn,
1225 "SERVERDN": names.serverdn,
1226 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1227 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1228 "SITES_DESCRIPTOR": descr
1231 logger.info("Setting up display specifiers")
1232 display_specifiers_ldif = read_ms_ldif(
1233 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1234 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1235 {"CONFIGDN": names.configdn})
1236 check_all_substituted(display_specifiers_ldif)
1237 samdb.add_ldif(display_specifiers_ldif)
1239 logger.info("Adding users container")
1240 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1241 "DOMAINDN": names.domaindn})
1242 logger.info("Modifying users container")
1243 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1244 "DOMAINDN": names.domaindn})
1245 logger.info("Adding computers container")
1246 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1247 "DOMAINDN": names.domaindn})
1248 logger.info("Modifying computers container")
1249 setup_modify_ldif(samdb,
1250 setup_path("provision_computers_modify.ldif"), {
1251 "DOMAINDN": names.domaindn})
1252 logger.info("Setting up sam.ldb data")
1253 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1254 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1255 "DOMAINDN": names.domaindn,
1256 "NETBIOSNAME": names.netbiosname,
1257 "DEFAULTSITE": names.sitename,
1258 "CONFIGDN": names.configdn,
1259 "SERVERDN": names.serverdn,
1260 "RIDAVAILABLESTART": str(next_rid + 600),
1261 "POLICYGUID_DC": policyguid_dc
1264 setup_modify_ldif(samdb,
1265 setup_path("provision_basedn_references.ldif"), {
1266 "DOMAINDN": names.domaindn})
1268 setup_modify_ldif(samdb,
1269 setup_path("provision_configuration_references.ldif"), {
1270 "CONFIGDN": names.configdn,
1271 "SCHEMADN": names.schemadn})
1272 if fill == FILL_FULL:
1273 logger.info("Setting up sam.ldb users and groups")
1274 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1275 "DOMAINDN": names.domaindn,
1276 "DOMAINSID": str(domainsid),
1277 "CONFIGDN": names.configdn,
1278 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1279 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1282 logger.info("Setting up self join")
1283 setup_self_join(samdb, names=names, invocationid=invocationid,
1285 machinepass=machinepass,
1286 domainsid=domainsid,
1288 policyguid=policyguid,
1289 policyguid_dc=policyguid_dc,
1290 setup_path=setup_path,
1291 domainControllerFunctionality=domainControllerFunctionality,
1294 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1295 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1296 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1297 assert isinstance(names.ntdsguid, str)
1299 samdb.transaction_cancel()
1302 samdb.transaction_commit()
1307 FILL_NT4SYNC = "NT4SYNC"
1309 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1310 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)"
1313 def set_dir_acl(path, acl, lp, domsid):
1314 setntacl(lp, path, acl, domsid)
1315 for root, dirs, files in os.walk(path, topdown=False):
1317 setntacl(lp, os.path.join(root, name), acl, domsid)
1319 setntacl(lp, os.path.join(root, name), acl, domsid)
1322 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1323 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1326 :param sysvol: Physical path for the sysvol folder
1327 :param dnsdomain: The DNS name of the domain
1328 :param domainsid: The SID of the domain
1329 :param domaindn: The DN of the domain (ie. DC=...)
1330 :param samdb: An LDB object on the SAM db
1331 :param lp: an LP object
1334 # Set ACL for GPO root folder
1335 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1336 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1338 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1339 attrs=["cn", "nTSecurityDescriptor"],
1340 expression="", scope=ldb.SCOPE_ONELEVEL)
1343 acl = ndr_unpack(security.descriptor,
1344 str(policy["nTSecurityDescriptor"])).as_sddl()
1345 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1346 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1350 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1352 """Set the ACL for the sysvol share and the subfolders
1354 :param samdb: An LDB object on the SAM db
1355 :param netlogon: Physical path for the netlogon folder
1356 :param sysvol: Physical path for the sysvol folder
1357 :param gid: The GID of the "Domain adminstrators" group
1358 :param domainsid: The SID of the domain
1359 :param dnsdomain: The DNS name of the domain
1360 :param domaindn: The DN of the domain (ie. DC=...)
1364 os.chown(sysvol, -1, gid)
1370 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1371 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1372 for root, dirs, files in os.walk(sysvol, topdown=False):
1375 os.chown(os.path.join(root, name), -1, gid)
1376 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1379 os.chown(os.path.join(root, name), -1, gid)
1380 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1382 # Set acls on Policy folder and policies folders
1383 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1386 def provision(setup_dir, logger, session_info, credentials, smbconf=None,
1387 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1388 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1389 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1390 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1391 domainguid=None, policyguid=None, policyguid_dc=None,
1392 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1393 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1394 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1395 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1396 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1397 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1401 :note: caution, this wipes all existing data!
1404 def setup_path(file):
1405 return os.path.join(setup_dir, file)
1407 if domainsid is None:
1408 domainsid = security.random_sid()
1410 domainsid = security.dom_sid(domainsid)
1412 # create/adapt the group policy GUIDs
1413 # Default GUID for default policy are described at
1414 # "How Core Group Policy Works"
1415 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1416 if policyguid is None:
1417 policyguid = DEFAULT_POLICY_GUID
1418 policyguid = policyguid.upper()
1419 if policyguid_dc is None:
1420 policyguid_dc = DEFAULT_DC_POLICY_GUID
1421 policyguid_dc = policyguid_dc.upper()
1423 if adminpass is None:
1424 adminpass = samba.generate_random_password(12, 32)
1425 if krbtgtpass is None:
1426 krbtgtpass = samba.generate_random_password(128, 255)
1427 if machinepass is None:
1428 machinepass = samba.generate_random_password(128, 255)
1430 dnspass = samba.generate_random_password(128, 255)
1431 if ldapadminpass is None:
1432 # Make a new, random password between Samba and it's LDAP server
1433 ldapadminpass=samba.generate_random_password(128, 255)
1435 if backend_type is None:
1436 backend_type = "ldb"
1438 sid_generator = "internal"
1439 if backend_type == "fedora-ds":
1440 sid_generator = "backend"
1442 root_uid = findnss_uid([root or "root"])
1443 nobody_uid = findnss_uid([nobody or "nobody"])
1444 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1446 wheel_gid = findnss_gid(["wheel", "adm"])
1448 wheel_gid = findnss_gid([wheel])
1450 bind_gid = findnss_gid(["bind", "named"])
1454 if targetdir is not None:
1455 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1456 elif smbconf is None:
1457 smbconf = samba.param.default_path()
1458 if not os.path.exists(os.path.dirname(smbconf)):
1459 os.makedirs(os.path.dirname(smbconf))
1461 # only install a new smb.conf if there isn't one there already
1462 if os.path.exists(smbconf):
1463 # if Samba Team members can't figure out the weird errors
1464 # loading an empty smb.conf gives, then we need to be smarter.
1465 # Pretend it just didn't exist --abartlet
1466 data = open(smbconf, 'r').read()
1467 data = data.lstrip()
1468 if data is None or data == "":
1469 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1470 serverrole, targetdir, sid_generator, useeadb,
1473 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1474 targetdir, sid_generator, useeadb, lp=lp)
1477 lp = samba.param.LoadParm()
1479 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1480 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1481 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1483 paths = provision_paths_from_lp(lp, names.dnsdomain)
1485 paths.bind_gid = bind_gid
1488 logger.info("Looking up IPv4 addresses")
1489 hostips = samba.interface_ips(lp, False)
1490 if len(hostips) == 0:
1491 logger.warning("No external IPv4 address has been found. Using loopback.")
1492 hostip = '127.0.0.1'
1495 if len(hostips) > 1:
1496 logger.warning("More than one IPv4 address found. Using %s.",
1499 if serverrole is None:
1500 serverrole = lp.get("server role")
1502 assert serverrole in ("domain controller", "member server", "standalone")
1503 if invocationid is None:
1504 invocationid = str(uuid.uuid4())
1506 if not os.path.exists(paths.private_dir):
1507 os.mkdir(paths.private_dir)
1508 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1509 os.mkdir(os.path.join(paths.private_dir, "tls"))
1511 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1513 schema = Schema(setup_path, domainsid, invocationid=invocationid,
1514 schemadn=names.schemadn)
1516 if backend_type == "ldb":
1517 provision_backend = LDBBackend(backend_type, paths=paths,
1518 setup_path=setup_path, lp=lp, credentials=credentials,
1519 names=names, logger=logger)
1520 elif backend_type == "existing":
1521 provision_backend = ExistingBackend(backend_type, paths=paths,
1522 setup_path=setup_path, lp=lp, credentials=credentials,
1523 names=names, logger=logger,
1524 ldap_backend_forced_uri=ldap_backend_forced_uri)
1525 elif backend_type == "fedora-ds":
1526 provision_backend = FDSBackend(backend_type, paths=paths,
1527 setup_path=setup_path, lp=lp, credentials=credentials,
1528 names=names, logger=logger, domainsid=domainsid,
1529 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1530 slapd_path=slapd_path,
1531 ldap_backend_extra_port=ldap_backend_extra_port,
1532 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1533 setup_ds_path=setup_ds_path,
1534 ldap_backend_forced_uri=ldap_backend_forced_uri)
1535 elif backend_type == "openldap":
1536 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1537 setup_path=setup_path, lp=lp, credentials=credentials,
1538 names=names, logger=logger, domainsid=domainsid,
1539 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1540 slapd_path=slapd_path,
1541 ldap_backend_extra_port=ldap_backend_extra_port,
1542 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1544 ldap_backend_forced_uri=ldap_backend_forced_uri)
1546 raise ValueError("Unknown LDAP backend type selected")
1548 provision_backend.init()
1549 provision_backend.start()
1551 # only install a new shares config db if there is none
1552 if not os.path.exists(paths.shareconf):
1553 logger.info("Setting up share.ldb")
1554 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1556 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1558 logger.info("Setting up secrets.ldb")
1559 secrets_ldb = setup_secretsdb(paths, setup_path,
1560 session_info=session_info,
1561 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1564 logger.info("Setting up the registry")
1565 setup_registry(paths.hklm, setup_path, session_info,
1568 logger.info("Setting up the privileges database")
1569 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1571 logger.info("Setting up idmap db")
1572 idmap = setup_idmapdb(paths.idmapdb, setup_path,
1573 session_info=session_info, lp=lp)
1575 logger.info("Setting up SAM db")
1576 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1577 provision_backend, lp, names, logger=logger,
1578 domainsid=domainsid, schema=schema, domainguid=domainguid,
1579 policyguid=policyguid, policyguid_dc=policyguid_dc,
1580 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1581 invocationid=invocationid, machinepass=machinepass,
1582 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1583 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1586 if serverrole == "domain controller":
1587 if paths.netlogon is None:
1588 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1589 logger.info("Please either remove %s or see the template at %s" %
1590 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1591 assert paths.netlogon is not None
1593 if paths.sysvol is None:
1594 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1595 " are configuring a DC.")
1596 logger.info("Please either remove %s or see the template at %s" %
1597 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1598 assert paths.sysvol is not None
1600 if not os.path.isdir(paths.netlogon):
1601 os.makedirs(paths.netlogon, 0755)
1603 if samdb_fill == FILL_FULL:
1604 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1605 root_uid=root_uid, nobody_uid=nobody_uid,
1606 users_gid=users_gid, wheel_gid=wheel_gid)
1608 if serverrole == "domain controller":
1609 # Set up group policies (domain policy and domain controller
1611 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1613 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1614 domainsid, names.dnsdomain, names.domaindn, lp)
1616 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1617 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1619 secretsdb_self_join(secrets_ldb, domain=names.domain,
1620 realm=names.realm, dnsdomain=names.dnsdomain,
1621 netbiosname=names.netbiosname, domainsid=domainsid,
1622 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1624 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1625 # In future, this might be determined from some configuration
1626 kerberos_enctypes = str(ENC_ALL_TYPES)
1629 msg = ldb.Message(ldb.Dn(samdb,
1630 samdb.searchone("distinguishedName",
1631 expression="samAccountName=%s$" % names.netbiosname,
1632 scope=ldb.SCOPE_SUBTREE)))
1633 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1634 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1635 name="msDS-SupportedEncryptionTypes")
1637 except ldb.LdbError, (enum, estr):
1638 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1639 # It might be that this attribute does not exist in this schema
1642 if serverrole == "domain controller":
1643 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1644 paths.private_dir, realm=names.realm,
1645 dnsdomain=names.dnsdomain,
1646 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1648 domainguid = samdb.searchone(basedn=domaindn,
1649 attribute="objectGUID")
1650 assert isinstance(domainguid, str)
1652 # Only make a zone file on the first DC, it should be
1653 # replicated with DNS replication
1654 create_zone_file(lp, logger, paths, targetdir, setup_path,
1655 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1656 hostname=names.hostname, realm=names.realm,
1657 domainguid=domainguid, ntdsguid=names.ntdsguid)
1659 create_named_conf(paths, setup_path, realm=names.realm,
1660 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1662 create_named_txt(paths.namedtxt, setup_path,
1663 realm=names.realm, dnsdomain=names.dnsdomain,
1664 private_dir=paths.private_dir,
1665 keytab_name=paths.dns_keytab)
1666 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1667 logger.info("and %s for further documentation required for secure DNS "
1668 "updates", paths.namedtxt)
1670 lastProvisionUSNs = get_last_provision_usn(samdb)
1671 maxUSN = get_max_usn(samdb, str(names.rootdn))
1672 if lastProvisionUSNs is not None:
1673 update_provision_usn(samdb, 0, maxUSN, 1)
1675 set_provision_usn(samdb, 0, maxUSN)
1677 create_krb5_conf(paths.krb5conf, setup_path,
1678 dnsdomain=names.dnsdomain, hostname=names.hostname,
1680 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1681 "generated at %s", paths.krb5conf)
1683 if serverrole == "domain controller":
1684 create_dns_update_list(lp, logger, paths, setup_path)
1686 provision_backend.post_setup()
1687 provision_backend.shutdown()
1689 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1692 secrets_ldb.transaction_cancel()
1695 # Now commit the secrets.ldb to disk
1696 secrets_ldb.transaction_commit()
1698 # the commit creates the dns.keytab, now chown it
1699 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1700 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1702 os.chmod(dns_keytab_path, 0640)
1703 os.chown(dns_keytab_path, -1, paths.bind_gid)
1705 if not os.environ.has_key('SAMBA_SELFTEST'):
1706 logger.info("Failed to chown %s to bind gid %u",
1707 dns_keytab_path, paths.bind_gid)
1710 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1711 paths.phpldapadminconfig)
1713 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1714 logger.info("Server Role: %s" % serverrole)
1715 logger.info("Hostname: %s" % names.hostname)
1716 logger.info("NetBIOS Domain: %s" % names.domain)
1717 logger.info("DNS Domain: %s" % names.dnsdomain)
1718 logger.info("DOMAIN SID: %s" % str(domainsid))
1719 if samdb_fill == FILL_FULL:
1720 logger.info("Admin password: %s" % adminpass)
1721 if provision_backend.type is not "ldb":
1722 if provision_backend.credentials.get_bind_dn() is not None:
1723 logger.info("LDAP Backend Admin DN: %s" %
1724 provision_backend.credentials.get_bind_dn())
1726 logger.info("LDAP Admin User: %s" %
1727 provision_backend.credentials.get_username())
1729 logger.info("LDAP Admin Password: %s" %
1730 provision_backend.credentials.get_password())
1732 if provision_backend.slapd_command_escaped is not None:
1733 # now display slapd_command_file.txt to show how slapd must be
1735 logger.info("Use later the following commandline to start slapd, then Samba:")
1736 logger.info(provision_backend.slapd_command_escaped)
1737 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1738 provision_backend.ldapdir)
1740 result = ProvisionResult()
1741 result.domaindn = domaindn
1742 result.paths = paths
1744 result.samdb = samdb
1748 def provision_become_dc(setup_dir=None, smbconf=None, targetdir=None,
1749 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1750 serverdn=None, domain=None, hostname=None, domainsid=None,
1751 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1752 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1753 root=None, nobody=None, users=None, wheel=None, backup=None,
1754 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1755 sitename=None, debuglevel=1):
1757 logger = logging.getLogger("provision")
1758 samba.set_debug_level(debuglevel)
1760 res = provision(setup_dir, logger, system_session(), None,
1761 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1762 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1763 configdn=configdn, serverdn=serverdn, domain=domain,
1764 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1765 machinepass=machinepass, serverrole="domain controller",
1767 res.lp.set("debuglevel", str(debuglevel))
1771 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1772 """Create a PHP LDAP admin configuration file.
1774 :param path: Path to write the configuration to.
1775 :param setup_path: Function to generate setup paths.
1777 setup_file(setup_path("phpldapadmin-config.php"), path,
1778 {"S4_LDAPI_URI": ldapi_uri})
1781 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1782 hostip, hostip6, hostname, realm, domainguid,
1784 """Write out a DNS zone file, from the info in the current database.
1786 :param paths: paths object
1787 :param setup_path: Setup path function.
1788 :param dnsdomain: DNS Domain name
1789 :param domaindn: DN of the Domain
1790 :param hostip: Local IPv4 IP
1791 :param hostip6: Local IPv6 IP
1792 :param hostname: Local hostname
1793 :param realm: Realm name
1794 :param domainguid: GUID of the domain.
1795 :param ntdsguid: GUID of the hosts nTDSDSA record.
1797 assert isinstance(domainguid, str)
1799 if hostip6 is not None:
1800 hostip6_base_line = " IN AAAA " + hostip6
1801 hostip6_host_line = hostname + " IN AAAA " + hostip6
1802 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1804 hostip6_base_line = ""
1805 hostip6_host_line = ""
1806 gc_msdcs_ip6_line = ""
1808 if hostip is not None:
1809 hostip_base_line = " IN A " + hostip
1810 hostip_host_line = hostname + " IN A " + hostip
1811 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1813 hostip_base_line = ""
1814 hostip_host_line = ""
1815 gc_msdcs_ip_line = ""
1817 dns_dir = os.path.dirname(paths.dns)
1820 shutil.rmtree(dns_dir, True)
1824 os.mkdir(dns_dir, 0775)
1826 # we need to freeze the zone while we update the contents
1827 if targetdir is None:
1828 rndc = ' '.join(lp.get("rndc command"))
1829 os.system(rndc + " freeze " + lp.get("realm"))
1831 setup_file(setup_path("provision.zone"), paths.dns, {
1832 "HOSTNAME": hostname,
1833 "DNSDOMAIN": dnsdomain,
1835 "HOSTIP_BASE_LINE": hostip_base_line,
1836 "HOSTIP_HOST_LINE": hostip_host_line,
1837 "DOMAINGUID": domainguid,
1838 "DATESTRING": time.strftime("%Y%m%d%H"),
1839 "DEFAULTSITE": DEFAULTSITE,
1840 "NTDSGUID": ntdsguid,
1841 "HOSTIP6_BASE_LINE": hostip6_base_line,
1842 "HOSTIP6_HOST_LINE": hostip6_host_line,
1843 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1844 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1847 # note that we use no variable substitution on this file
1848 # the substitution is done at runtime by samba_dnsupdate
1849 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1851 # and the SPN update list
1852 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1854 if paths.bind_gid is not None:
1856 os.chown(dns_dir, -1, paths.bind_gid)
1857 os.chown(paths.dns, -1, paths.bind_gid)
1858 # chmod needed to cope with umask
1859 os.chmod(dns_dir, 0775)
1860 os.chmod(paths.dns, 0664)
1862 if not os.environ.has_key('SAMBA_SELFTEST'):
1863 logger.error("Failed to chown %s to bind gid %u" % (
1864 dns_dir, paths.bind_gid))
1866 if targetdir is None:
1867 os.system(rndc + " unfreeze " + lp.get("realm"))
1870 def create_dns_update_list(lp, logger, paths, setup_path):
1871 """Write out a dns_update_list file"""
1872 # note that we use no variable substitution on this file
1873 # the substitution is done at runtime by samba_dnsupdate
1874 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1875 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1878 def create_named_conf(paths, setup_path, realm, dnsdomain,
1880 """Write out a file containing zone statements suitable for inclusion in a
1881 named.conf file (including GSS-TSIG configuration).
1883 :param paths: all paths
1884 :param setup_path: Setup path function.
1885 :param realm: Realm name
1886 :param dnsdomain: DNS Domain name
1887 :param private_dir: Path to private directory
1888 :param keytab_name: File name of DNS keytab file
1891 setup_file(setup_path("named.conf"), paths.namedconf, {
1892 "DNSDOMAIN": dnsdomain,
1894 "ZONE_FILE": paths.dns,
1895 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1896 "NAMED_CONF": paths.namedconf,
1897 "NAMED_CONF_UPDATE": paths.namedconf_update
1900 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1903 def create_named_txt(path, setup_path, realm, dnsdomain, private_dir,
1905 """Write out a file containing zone statements suitable for inclusion in a
1906 named.conf file (including GSS-TSIG configuration).
1908 :param path: Path of the new named.conf file.
1909 :param setup_path: Setup path function.
1910 :param realm: Realm name
1911 :param dnsdomain: DNS Domain name
1912 :param private_dir: Path to private directory
1913 :param keytab_name: File name of DNS keytab file
1915 setup_file(setup_path("named.txt"), path, {
1916 "DNSDOMAIN": dnsdomain,
1918 "DNS_KEYTAB": keytab_name,
1919 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1920 "PRIVATE_DIR": private_dir
1924 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1925 """Write out a file containing zone statements suitable for inclusion in a
1926 named.conf file (including GSS-TSIG configuration).
1928 :param path: Path of the new named.conf file.
1929 :param setup_path: Setup path function.
1930 :param dnsdomain: DNS Domain name
1931 :param hostname: Local hostname
1932 :param realm: Realm name
1934 setup_file(setup_path("krb5.conf"), path, {
1935 "DNSDOMAIN": dnsdomain,
1936 "HOSTNAME": hostname,
1941 class ProvisioningError(Exception):
1942 """A generic provision error."""
1944 def __init__(self, value):
1948 return "ProvisioningError: " + self.value
1951 class InvalidNetbiosName(Exception):
1952 """A specified name was not a valid NetBIOS name."""
1953 def __init__(self, name):
1954 super(InvalidNetbiosName, self).__init__(
1955 "The name '%r' is not a valid NetBIOS name" % name)