2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 __docformat__ = "restructuredText"
30 from base64 import b64encode
44 from samba.auth import system_session, admin_session
48 check_all_substituted,
56 from samba.dcerpc import security
57 from samba.dcerpc.misc import (
61 from samba.dsdb import (
62 DS_DOMAIN_FUNCTION_2003,
63 DS_DOMAIN_FUNCTION_2008_R2,
66 from samba.idmap import IDmapDB
67 from samba.ms_display_specifiers import read_ms_ldif
68 from samba.ntacls import setntacl, dsacl2fsacl
69 from samba.ndr import ndr_pack, ndr_unpack
70 from samba.provision.backend import (
78 from samba.schema import Schema
79 from samba.samdb import SamDB
81 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
82 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
83 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
84 DEFAULTSITE = "Default-First-Site-Name"
85 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
89 """Find the setup directory used by provision."""
92 dirname = os.path.dirname(__file__)
93 return os.path.normpath(os.path.join(dirname, "../../../../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 = "O:EAG:EAD:AI(A;;RPLCLORC;;;AU)" \
110 "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
111 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
112 "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
113 "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
114 "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
115 "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
116 "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
117 "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
118 "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
119 sec = security.descriptor.from_sddl(sddl, domain_sid)
123 def get_config_descriptor(domain_sid):
124 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
125 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
126 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
127 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
128 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
129 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
130 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
131 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
132 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
134 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
135 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
136 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
137 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
138 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
139 sec = security.descriptor.from_sddl(sddl, domain_sid)
143 def get_domain_descriptor(domain_sid):
144 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
145 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
146 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
148 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
149 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
150 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
151 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
152 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
153 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
154 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
155 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
156 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
157 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
158 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
159 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
160 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
161 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
162 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
163 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
164 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
165 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
166 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
167 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
168 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
169 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
170 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
171 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
172 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
173 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
174 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
175 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
176 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
177 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
178 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
179 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
180 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
181 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
182 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
185 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
187 "(A;;RPLCLORC;;;ED)" \
188 "(A;;RPLCLORC;;;AU)" \
189 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
190 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
191 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
192 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
193 sec = security.descriptor.from_sddl(sddl, domain_sid)
197 class ProvisionPaths(object):
200 self.shareconf = None
211 self.dns_keytab = None
214 self.private_dir = None
217 class ProvisionNames(object):
224 self.ldapmanagerdn = None
225 self.dnsdomain = None
227 self.netbiosname = None
234 def update_provision_usn(samdb, low, high, replace=False):
235 """Update the field provisionUSN in sam.ldb
237 This field is used to track range of USN modified by provision and
239 This value is used afterward by next provision to figure out if
240 the field have been modified since last provision.
242 :param samdb: An LDB object connect to sam.ldb
243 :param low: The lowest USN modified by this upgrade
244 :param high: The highest USN modified by this upgrade
245 :param replace: A boolean indicating if the range should replace any
246 existing one or appended (default)
251 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
252 LAST_PROVISION_USN_ATTRIBUTE, base="",
253 scope=ldb.SCOPE_SUBTREE,
254 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
255 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
258 tab.append("%s-%s" % (low, high))
259 delta = ldb.Message()
260 delta.dn = ldb.Dn(samdb, "@PROVISION")
261 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
262 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
266 def set_provision_usn(samdb, low, high):
267 """Set the field provisionUSN in sam.ldb
268 This field is used to track range of USN modified by provision and
270 This value is used afterward by next provision to figure out if
271 the field have been modified since last provision.
273 :param samdb: An LDB object connect to sam.ldb
274 :param low: The lowest USN modified by this upgrade
275 :param high: The highest USN modified by this upgrade"""
277 tab.append("%s-%s" % (low, high))
278 delta = ldb.Message()
279 delta.dn = ldb.Dn(samdb, "@PROVISION")
280 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
281 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
285 def get_max_usn(samdb,basedn):
286 """ This function return the biggest USN present in the provision
288 :param samdb: A LDB object pointing to the sam.ldb
289 :param basedn: A string containing the base DN of the provision
291 :return: The biggest USN in the provision"""
293 res = samdb.search(expression="objectClass=*",base=basedn,
294 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
295 controls=["search_options:1:2",
296 "server_sort:1:1:uSNChanged",
297 "paged_results:1:1"])
298 return res[0]["uSNChanged"]
301 def get_last_provision_usn(sam):
302 """Get the lastest USN modified by a provision or an upgradeprovision
304 :param sam: An LDB object pointing to the sam.ldb
305 :return: an integer corresponding to the highest USN modified by
306 (upgrade)provision, 0 is this value is unknown
308 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
309 LAST_PROVISION_USN_ATTRIBUTE,
310 base="", scope=ldb.SCOPE_SUBTREE,
311 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
316 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
317 tab = p.split(str(r))
326 class ProvisionResult(object):
335 def check_install(lp, session_info, credentials):
336 """Check whether the current install seems ok.
338 :param lp: Loadparm context
339 :param session_info: Session information
340 :param credentials: Credentials
342 if lp.get("realm") == "":
343 raise Exception("Realm empty")
344 samdb = Ldb(lp.get("sam database"), session_info=session_info,
345 credentials=credentials, lp=lp)
346 if len(samdb.search("(cn=Administrator)")) != 1:
347 raise ProvisioningError("No administrator account found")
350 def findnss(nssfn, names):
351 """Find a user or group from a list of possibilities.
353 :param nssfn: NSS Function to try (should raise KeyError if not found)
354 :param names: Names to check.
355 :return: Value return by first names list.
362 raise KeyError("Unable to find user/group in %r" % names)
365 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
366 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
369 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
370 """Setup a ldb in the private dir.
372 :param ldb: LDB file to import data into
373 :param ldif_path: Path of the LDIF file to load
374 :param subst_vars: Optional variables to subsitute in LDIF.
375 :param nocontrols: Optional list of controls, can be None for no controls
377 assert isinstance(ldif_path, str)
378 data = read_and_sub_file(ldif_path, subst_vars)
379 ldb.add_ldif(data, controls)
382 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
383 """Modify a ldb in the private dir.
385 :param ldb: LDB object.
386 :param ldif_path: LDIF file path.
387 :param subst_vars: Optional dictionary with substitution variables.
389 data = read_and_sub_file(ldif_path, subst_vars)
390 ldb.modify_ldif(data, controls)
393 def setup_ldb(ldb, ldif_path, subst_vars):
394 """Import a LDIF a file into a LDB handle, optionally substituting
397 :note: Either all LDIF data will be added or none (using transactions).
399 :param ldb: LDB file to import into.
400 :param ldif_path: Path to the LDIF file.
401 :param subst_vars: Dictionary with substitution variables.
403 assert ldb is not None
404 ldb.transaction_start()
406 setup_add_ldif(ldb, ldif_path, subst_vars)
408 ldb.transaction_cancel()
411 ldb.transaction_commit()
414 def provision_paths_from_lp(lp, dnsdomain):
415 """Set the default paths for provisioning.
417 :param lp: Loadparm context.
418 :param dnsdomain: DNS Domain name
420 paths = ProvisionPaths()
421 paths.private_dir = lp.get("private dir")
423 # This is stored without path prefix for the "privateKeytab" attribute in
424 # "secrets_dns.ldif".
425 paths.dns_keytab = "dns.keytab"
426 paths.keytab = "secrets.keytab"
428 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
429 paths.samdb = os.path.join(paths.private_dir,
430 lp.get("sam database") or "samdb.ldb")
431 paths.idmapdb = os.path.join(paths.private_dir,
432 lp.get("idmap database") or "idmap.ldb")
433 paths.secrets = os.path.join(paths.private_dir,
434 lp.get("secrets database") or "secrets.ldb")
435 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
436 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
437 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
438 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
439 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
440 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
441 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
442 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
443 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
444 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
445 paths.phpldapadminconfig = os.path.join(paths.private_dir,
446 "phpldapadmin-config.php")
447 paths.hklm = "hklm.ldb"
448 paths.hkcr = "hkcr.ldb"
449 paths.hkcu = "hkcu.ldb"
450 paths.hku = "hku.ldb"
451 paths.hkpd = "hkpd.ldb"
452 paths.hkpt = "hkpt.ldb"
453 paths.sysvol = lp.get("path", "sysvol")
454 paths.netlogon = lp.get("path", "netlogon")
455 paths.smbconf = lp.configfile
459 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
460 serverrole=None, rootdn=None, domaindn=None, configdn=None,
461 schemadn=None, serverdn=None, sitename=None):
462 """Guess configuration settings to use."""
465 hostname = socket.gethostname().split(".")[0]
467 netbiosname = lp.get("netbios name")
468 if netbiosname is None:
469 netbiosname = hostname
470 # remove forbidden chars
472 for x in netbiosname:
473 if x.isalnum() or x in VALID_NETBIOS_CHARS:
474 newnbname = "%s%c" % (newnbname, x)
475 # force the length to be <16
476 netbiosname = newnbname[0:15]
477 assert netbiosname is not None
478 netbiosname = netbiosname.upper()
479 if not valid_netbios_name(netbiosname):
480 raise InvalidNetbiosName(netbiosname)
482 if dnsdomain is None:
483 dnsdomain = lp.get("realm")
484 if dnsdomain is None or dnsdomain == "":
485 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
487 dnsdomain = dnsdomain.lower()
489 if serverrole is None:
490 serverrole = lp.get("server role")
491 if serverrole is None:
492 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
494 serverrole = serverrole.lower()
496 realm = dnsdomain.upper()
498 if lp.get("realm") == "":
499 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
501 if lp.get("realm").upper() != realm:
502 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))
504 if lp.get("server role").lower() != serverrole:
505 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))
507 if serverrole == "domain controller":
509 # This will, for better or worse, default to 'WORKGROUP'
510 domain = lp.get("workgroup")
511 domain = domain.upper()
513 if lp.get("workgroup").upper() != domain:
514 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))
517 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
521 domaindn = "DC=" + netbiosname
523 if not valid_netbios_name(domain):
524 raise InvalidNetbiosName(domain)
526 if hostname.upper() == realm:
527 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
528 if netbiosname == realm:
529 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
531 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
537 configdn = "CN=Configuration," + rootdn
539 schemadn = "CN=Schema," + configdn
544 names = ProvisionNames()
545 names.rootdn = rootdn
546 names.domaindn = domaindn
547 names.configdn = configdn
548 names.schemadn = schemadn
549 names.ldapmanagerdn = "CN=Manager," + rootdn
550 names.dnsdomain = dnsdomain
551 names.domain = domain
553 names.netbiosname = netbiosname
554 names.hostname = hostname
555 names.sitename = sitename
556 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
557 netbiosname, sitename, configdn)
562 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
563 targetdir, sid_generator="internal", eadb=False, lp=None):
564 """Create a new smb.conf file based on a couple of basic settings.
566 assert smbconf is not None
568 hostname = socket.gethostname().split(".")[0]
569 netbiosname = hostname.upper()
570 # remove forbidden chars
572 for x in netbiosname:
573 if x.isalnum() or x in VALID_NETBIOS_CHARS:
574 newnbname = "%s%c" % (newnbname, x)
575 #force the length to be <16
576 netbiosname = newnbname[0:15]
578 netbiosname = hostname.upper()
580 if serverrole is None:
581 serverrole = "standalone"
583 assert serverrole in ("domain controller", "member server", "standalone")
584 if serverrole == "domain controller":
586 elif serverrole == "member server":
587 smbconfsuffix = "member"
588 elif serverrole == "standalone":
589 smbconfsuffix = "standalone"
591 if sid_generator is None:
592 sid_generator = "internal"
594 assert domain is not None
595 domain = domain.upper()
597 assert realm is not None
598 realm = realm.upper()
601 lp = samba.param.LoadParm()
602 #Load non-existant file
603 if os.path.exists(smbconf):
605 if eadb and not lp.get("posix:eadb"):
606 if targetdir is not None:
607 privdir = os.path.join(targetdir, "private")
609 privdir = lp.get("private dir")
610 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
612 if targetdir is not None:
613 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
614 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
616 lp.set("lock dir", os.path.abspath(targetdir))
621 if sid_generator == "internal":
622 sid_generator_line = ""
624 sid_generator_line = "sid generator = " + sid_generator
626 used_setup_dir = setup_path("")
627 default_setup_dir = lp.get("setup directory")
629 if used_setup_dir != default_setup_dir:
630 setupdir_line = "setup directory = %s" % used_setup_dir
631 lp.set("setup directory", used_setup_dir)
633 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
634 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
636 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
638 "NETBIOS_NAME": netbiosname,
641 "SERVERROLE": serverrole,
642 "NETLOGONPATH": netlogon,
643 "SYSVOLPATH": sysvol,
644 "SETUPDIRECTORY_LINE": setupdir_line,
645 "SIDGENERATOR_LINE": sid_generator_line,
646 "PRIVATEDIR_LINE": privatedir_line,
647 "LOCKDIR_LINE": lockdir_line
650 # reload the smb.conf
653 # and dump it without any values that are the default
654 # this ensures that any smb.conf parameters that were set
655 # on the provision/join command line are set in the resulting smb.conf
656 f = open(smbconf, mode='w')
662 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
663 users_gid, wheel_gid):
664 """setup reasonable name mappings for sam names to unix names.
666 :param samdb: SamDB object.
667 :param idmap: IDmap db object.
668 :param sid: The domain sid.
669 :param domaindn: The domain DN.
670 :param root_uid: uid of the UNIX root user.
671 :param nobody_uid: uid of the UNIX nobody user.
672 :param users_gid: gid of the UNIX users group.
673 :param wheel_gid: gid of the UNIX wheel group.
675 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
676 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
678 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
679 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
682 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
683 provision_backend, names, schema, serverrole,
685 """Setup the partitions for the SAM database.
687 Alternatively, provision() may call this, and then populate the database.
689 :note: This will wipe the Sam Database!
691 :note: This function always removes the local SAM LDB file. The erase
692 parameter controls whether to erase the existing data, which
693 may not be stored locally but in LDAP.
696 assert session_info is not None
698 # We use options=["modules:"] to stop the modules loading - we
699 # just want to wipe and re-initialise the database, not start it up
702 os.unlink(samdb_path)
706 samdb = Ldb(url=samdb_path, session_info=session_info,
707 lp=lp, options=["modules:"])
709 ldap_backend_line = "# No LDAP backend"
710 if provision_backend.type is not "ldb":
711 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
713 samdb.transaction_start()
715 logger.info("Setting up sam.ldb partitions and settings")
716 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
717 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
718 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
719 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
720 "LDAP_BACKEND_LINE": ldap_backend_line,
724 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
725 "BACKEND_TYPE": provision_backend.type,
726 "SERVER_ROLE": serverrole
729 logger.info("Setting up sam.ldb rootDSE")
730 setup_samdb_rootdse(samdb, setup_path, names)
732 samdb.transaction_cancel()
735 samdb.transaction_commit()
738 def secretsdb_self_join(secretsdb, domain,
739 netbiosname, machinepass, domainsid=None,
740 realm=None, dnsdomain=None,
742 key_version_number=1,
743 secure_channel_type=SEC_CHAN_WKSTA):
744 """Add domain join-specific bits to a secrets database.
746 :param secretsdb: Ldb Handle to the secrets database
747 :param machinepass: Machine password
749 attrs = ["whenChanged",
756 if realm is not None:
757 if dnsdomain is None:
758 dnsdomain = realm.lower()
759 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
762 shortname = netbiosname.lower()
764 # We don't need to set msg["flatname"] here, because rdn_name will handle
765 # it, and it causes problems for modifies anyway
766 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
767 msg["secureChannelType"] = [str(secure_channel_type)]
768 msg["objectClass"] = ["top", "primaryDomain"]
769 if dnsname is not None:
770 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
771 msg["realm"] = [realm]
772 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
773 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
774 msg["privateKeytab"] = ["secrets.keytab"]
776 msg["secret"] = [machinepass]
777 msg["samAccountName"] = ["%s$" % netbiosname]
778 msg["secureChannelType"] = [str(secure_channel_type)]
779 if domainsid is not None:
780 msg["objectSid"] = [ndr_pack(domainsid)]
782 # This complex expression tries to ensure that we don't have more
783 # than one record for this SID, realm or netbios domain at a time,
784 # but we don't delete the old record that we are about to modify,
785 # because that would delete the keytab and previous password.
786 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
787 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
788 scope=ldb.SCOPE_ONELEVEL)
791 secretsdb.delete(del_msg.dn)
793 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
796 msg["priorSecret"] = [res[0]["secret"][0]]
797 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
800 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
805 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
811 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
812 secretsdb.modify(msg)
813 secretsdb.rename(res[0].dn, msg.dn)
815 spn = [ 'HOST/%s' % shortname ]
816 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
817 # we are a domain controller then we add servicePrincipalName
818 # entries for the keytab code to update.
819 spn.extend([ 'HOST/%s' % dnsname ])
820 msg["servicePrincipalName"] = spn
825 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir, realm,
826 dnsdomain, dns_keytab_path, dnspass):
827 """Add DNS specific bits to a secrets database.
829 :param secretsdb: Ldb Handle to the secrets database
830 :param setup_path: Setup path function
831 :param machinepass: Machine password
834 os.unlink(os.path.join(private_dir, dns_keytab_path))
838 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
840 "DNSDOMAIN": dnsdomain,
841 "DNS_KEYTAB": dns_keytab_path,
842 "DNSPASS_B64": b64encode(dnspass),
843 "HOSTNAME": names.hostname,
844 "DNSNAME" : '%s.%s' % (
845 names.netbiosname.lower(), names.dnsdomain.lower())
849 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
850 """Setup the secrets database.
852 :note: This function does not handle exceptions and transaction on purpose,
853 it's up to the caller to do this job.
855 :param path: Path to the secrets database.
856 :param setup_path: Get the path to a setup file.
857 :param session_info: Session info.
858 :param credentials: Credentials
859 :param lp: Loadparm context
860 :return: LDB handle for the created secrets database
862 if os.path.exists(paths.secrets):
863 os.unlink(paths.secrets)
865 keytab_path = os.path.join(paths.private_dir, paths.keytab)
866 if os.path.exists(keytab_path):
867 os.unlink(keytab_path)
869 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
870 if os.path.exists(dns_keytab_path):
871 os.unlink(dns_keytab_path)
875 secrets_ldb = Ldb(path, session_info=session_info,
878 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
879 secrets_ldb = Ldb(path, session_info=session_info,
881 secrets_ldb.transaction_start()
883 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
885 if (backend_credentials is not None and
886 backend_credentials.authentication_requested()):
887 if backend_credentials.get_bind_dn() is not None:
888 setup_add_ldif(secrets_ldb,
889 setup_path("secrets_simple_ldap.ldif"), {
890 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
891 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
894 setup_add_ldif(secrets_ldb,
895 setup_path("secrets_sasl_ldap.ldif"), {
896 "LDAPADMINUSER": backend_credentials.get_username(),
897 "LDAPADMINREALM": backend_credentials.get_realm(),
898 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
903 secrets_ldb.transaction_cancel()
907 def setup_privileges(path, setup_path, session_info, lp):
908 """Setup the privileges database.
910 :param path: Path to the privileges database.
911 :param setup_path: Get the path to a setup file.
912 :param session_info: Session info.
913 :param credentials: Credentials
914 :param lp: Loadparm context
915 :return: LDB handle for the created secrets database
917 if os.path.exists(path):
919 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
920 privilege_ldb.erase()
921 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
924 def setup_registry(path, setup_path, session_info, lp):
925 """Setup the registry.
927 :param path: Path to the registry database
928 :param setup_path: Function that returns the path to a setup.
929 :param session_info: Session information
930 :param credentials: Credentials
931 :param lp: Loadparm context
933 reg = samba.registry.Registry()
934 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
935 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
936 provision_reg = setup_path("provision.reg")
937 assert os.path.exists(provision_reg)
938 reg.diff_apply(provision_reg)
941 def setup_idmapdb(path, setup_path, session_info, lp):
942 """Setup the idmap database.
944 :param path: path to the idmap database
945 :param setup_path: Function that returns a path to a setup file
946 :param session_info: Session information
947 :param credentials: Credentials
948 :param lp: Loadparm context
950 if os.path.exists(path):
953 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
955 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
959 def setup_samdb_rootdse(samdb, setup_path, names):
960 """Setup the SamDB rootdse.
962 :param samdb: Sam Database handle
963 :param setup_path: Obtain setup path
965 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
966 "SCHEMADN": names.schemadn,
967 "DOMAINDN": names.domaindn,
968 "ROOTDN": names.rootdn,
969 "CONFIGDN": names.configdn,
970 "SERVERDN": names.serverdn,
974 def setup_self_join(samdb, names, machinepass, dnspass,
975 domainsid, next_rid, invocationid, setup_path,
976 policyguid, policyguid_dc, domainControllerFunctionality,
978 """Join a host to its own domain."""
979 assert isinstance(invocationid, str)
980 if ntdsguid is not None:
981 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
984 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
985 "CONFIGDN": names.configdn,
986 "SCHEMADN": names.schemadn,
987 "DOMAINDN": names.domaindn,
988 "SERVERDN": names.serverdn,
989 "INVOCATIONID": invocationid,
990 "NETBIOSNAME": names.netbiosname,
991 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
992 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
993 "DOMAINSID": str(domainsid),
994 "DCRID": str(next_rid),
995 "SAMBA_VERSION_STRING": version,
996 "NTDSGUID": ntdsguid_line,
997 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
998 domainControllerFunctionality)})
1000 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1001 "POLICYGUID": policyguid,
1002 "POLICYGUID_DC": policyguid_dc,
1003 "DNSDOMAIN": names.dnsdomain,
1004 "DOMAINDN": names.domaindn})
1006 # add the NTDSGUID based SPNs
1007 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1008 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1009 expression="", scope=ldb.SCOPE_BASE)
1010 assert isinstance(names.ntdsguid, str)
1012 # Setup fSMORoleOwner entries to point at the newly created DC entry
1013 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1014 "DOMAINDN": names.domaindn,
1015 "CONFIGDN": names.configdn,
1016 "SCHEMADN": names.schemadn,
1017 "DEFAULTSITE": names.sitename,
1018 "SERVERDN": names.serverdn,
1019 "NETBIOSNAME": names.netbiosname,
1020 "RIDALLOCATIONSTART": str(next_rid + 100),
1021 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1024 # This is partially Samba4 specific and should be replaced by the correct
1025 # DNS AD-style setup
1026 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1027 "DNSDOMAIN": names.dnsdomain,
1028 "DOMAINDN": names.domaindn,
1029 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1030 "HOSTNAME" : names.hostname,
1031 "DNSNAME" : '%s.%s' % (
1032 names.netbiosname.lower(), names.dnsdomain.lower())
1036 def getpolicypath(sysvolpath, dnsdomain, guid):
1037 """Return the physical path of policy given its guid.
1039 :param sysvolpath: Path to the sysvol folder
1040 :param dnsdomain: DNS name of the AD domain
1041 :param guid: The GUID of the policy
1042 :return: A string with the complete path to the policy folder
1046 guid = "{%s}" % guid
1047 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1051 def create_gpo_struct(policy_path):
1052 if not os.path.exists(policy_path):
1053 os.makedirs(policy_path, 0775)
1054 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1055 "[General]\r\nVersion=0")
1056 p = os.path.join(policy_path, "MACHINE")
1057 if not os.path.exists(p):
1058 os.makedirs(p, 0775)
1059 p = os.path.join(policy_path, "USER")
1060 if not os.path.exists(p):
1061 os.makedirs(p, 0775)
1064 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1065 """Create the default GPO for a domain
1067 :param sysvolpath: Physical path for the sysvol folder
1068 :param dnsdomain: DNS domain name of the AD domain
1069 :param policyguid: GUID of the default domain policy
1070 :param policyguid_dc: GUID of the default domain controler policy
1072 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1073 create_gpo_struct(policy_path)
1075 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1076 create_gpo_struct(policy_path)
1079 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1080 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1081 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1082 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1084 """Setup a complete SAM Database.
1086 :note: This will wipe the main SAM database file!
1089 # Provision does not make much sense values larger than 1000000000
1090 # as the upper range of the rIDAvailablePool is 1073741823 and
1091 # we don't want to create a domain that cannot allocate rids.
1092 if next_rid < 1000 or next_rid > 1000000000:
1093 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1094 error += "the valid range is %u-%u. The default is %u." % (
1095 1000, 1000000000, 1000)
1096 raise ProvisioningError(error)
1098 # ATTENTION: Do NOT change these default values without discussion with the
1099 # team and/or release manager. They have a big impact on the whole program!
1100 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1102 if dom_for_fun_level is None:
1103 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1105 if dom_for_fun_level > domainControllerFunctionality:
1106 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
1108 domainFunctionality = dom_for_fun_level
1109 forestFunctionality = dom_for_fun_level
1111 # Also wipes the database
1112 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1113 provision_backend=provision_backend, session_info=session_info,
1114 names=names, serverrole=serverrole, schema=schema)
1117 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1119 # Load the database, but don's load the global schema and don't connect
1121 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1122 credentials=provision_backend.credentials, lp=lp,
1123 global_schema=False, am_rodc=am_rodc)
1125 logger.info("Pre-loading the Samba 4 and AD schema")
1127 # Load the schema from the one we computed earlier
1128 samdb.set_schema(schema)
1130 # Set the NTDS settings DN manually - in order to have it already around
1131 # before the provisioned tree exists and we connect
1132 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1134 # And now we can connect to the DB - the schema won't be loaded from the
1138 if fill == FILL_DRS:
1141 samdb.transaction_start()
1143 # Set the domain functionality levels onto the database.
1144 # Various module (the password_hash module in particular) need
1145 # to know what level of AD we are emulating.
1147 # These will be fixed into the database via the database
1148 # modifictions below, but we need them set from the start.
1149 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1150 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1151 samdb.set_opaque_integer("domainControllerFunctionality",
1152 domainControllerFunctionality)
1154 samdb.set_domain_sid(str(domainsid))
1155 samdb.set_invocation_id(invocationid)
1157 logger.info("Adding DomainDN: %s" % names.domaindn)
1159 # impersonate domain admin
1160 admin_session_info = admin_session(lp, str(domainsid))
1161 samdb.set_session_info(admin_session_info)
1162 if domainguid is not None:
1163 domainguid_line = "objectGUID: %s\n-" % domainguid
1165 domainguid_line = ""
1167 descr = b64encode(get_domain_descriptor(domainsid))
1168 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1169 "DOMAINDN": names.domaindn,
1170 "DOMAINSID": str(domainsid),
1171 "DESCRIPTOR": descr,
1172 "DOMAINGUID": domainguid_line
1175 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1176 "DOMAINDN": names.domaindn,
1177 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1178 "NEXTRID": str(next_rid),
1179 "DEFAULTSITE": names.sitename,
1180 "CONFIGDN": names.configdn,
1181 "POLICYGUID": policyguid,
1182 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1183 "SAMBA_VERSION_STRING": version
1186 logger.info("Adding configuration container")
1187 descr = b64encode(get_config_descriptor(domainsid))
1188 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1189 "CONFIGDN": names.configdn,
1190 "DESCRIPTOR": descr,
1193 # The LDIF here was created when the Schema object was constructed
1194 logger.info("Setting up sam.ldb schema")
1195 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1196 samdb.modify_ldif(schema.schema_dn_modify)
1197 samdb.write_prefixes_from_schema()
1198 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1199 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1200 {"SCHEMADN": names.schemadn})
1202 logger.info("Reopening sam.ldb with new schema")
1204 samdb.transaction_cancel()
1207 samdb.transaction_commit()
1209 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1210 credentials=provision_backend.credentials, lp=lp,
1211 global_schema=False, am_rodc=am_rodc)
1213 # Set the NTDS settings DN manually - in order to have it already around
1214 # before the provisioned tree exists and we connect
1215 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1218 samdb.transaction_start()
1220 samdb.invocation_id = invocationid
1222 logger.info("Setting up sam.ldb configuration data")
1223 descr = b64encode(get_sites_descriptor(domainsid))
1224 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1225 "CONFIGDN": names.configdn,
1226 "NETBIOSNAME": names.netbiosname,
1227 "DEFAULTSITE": names.sitename,
1228 "DNSDOMAIN": names.dnsdomain,
1229 "DOMAIN": names.domain,
1230 "SCHEMADN": names.schemadn,
1231 "DOMAINDN": names.domaindn,
1232 "SERVERDN": names.serverdn,
1233 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1234 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1235 "SITES_DESCRIPTOR": descr
1238 logger.info("Setting up display specifiers")
1239 display_specifiers_ldif = read_ms_ldif(
1240 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1241 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1242 {"CONFIGDN": names.configdn})
1243 check_all_substituted(display_specifiers_ldif)
1244 samdb.add_ldif(display_specifiers_ldif)
1246 logger.info("Adding users container")
1247 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1248 "DOMAINDN": names.domaindn})
1249 logger.info("Modifying users container")
1250 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1251 "DOMAINDN": names.domaindn})
1252 logger.info("Adding computers container")
1253 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1254 "DOMAINDN": names.domaindn})
1255 logger.info("Modifying computers container")
1256 setup_modify_ldif(samdb,
1257 setup_path("provision_computers_modify.ldif"), {
1258 "DOMAINDN": names.domaindn})
1259 logger.info("Setting up sam.ldb data")
1260 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1261 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1262 "DOMAINDN": names.domaindn,
1263 "NETBIOSNAME": names.netbiosname,
1264 "DEFAULTSITE": names.sitename,
1265 "CONFIGDN": names.configdn,
1266 "SERVERDN": names.serverdn,
1267 "RIDAVAILABLESTART": str(next_rid + 600),
1268 "POLICYGUID_DC": policyguid_dc
1271 setup_modify_ldif(samdb,
1272 setup_path("provision_basedn_references.ldif"), {
1273 "DOMAINDN": names.domaindn})
1275 setup_modify_ldif(samdb,
1276 setup_path("provision_configuration_references.ldif"), {
1277 "CONFIGDN": names.configdn,
1278 "SCHEMADN": names.schemadn})
1279 if fill == FILL_FULL:
1280 logger.info("Setting up sam.ldb users and groups")
1281 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1282 "DOMAINDN": names.domaindn,
1283 "DOMAINSID": str(domainsid),
1284 "CONFIGDN": names.configdn,
1285 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1286 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1289 logger.info("Setting up self join")
1290 setup_self_join(samdb, names=names, invocationid=invocationid,
1292 machinepass=machinepass,
1293 domainsid=domainsid,
1295 policyguid=policyguid,
1296 policyguid_dc=policyguid_dc,
1297 setup_path=setup_path,
1298 domainControllerFunctionality=domainControllerFunctionality,
1301 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1302 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1303 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1304 assert isinstance(names.ntdsguid, str)
1306 samdb.transaction_cancel()
1309 samdb.transaction_commit()
1314 FILL_NT4SYNC = "NT4SYNC"
1316 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1317 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1320 def set_dir_acl(path, acl, lp, domsid):
1321 setntacl(lp, path, acl, domsid)
1322 for root, dirs, files in os.walk(path, topdown=False):
1324 setntacl(lp, os.path.join(root, name), acl, domsid)
1326 setntacl(lp, os.path.join(root, name), acl, domsid)
1329 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1330 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1333 :param sysvol: Physical path for the sysvol folder
1334 :param dnsdomain: The DNS name of the domain
1335 :param domainsid: The SID of the domain
1336 :param domaindn: The DN of the domain (ie. DC=...)
1337 :param samdb: An LDB object on the SAM db
1338 :param lp: an LP object
1341 # Set ACL for GPO root folder
1342 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1343 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1345 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1346 attrs=["cn", "nTSecurityDescriptor"],
1347 expression="", scope=ldb.SCOPE_ONELEVEL)
1350 acl = ndr_unpack(security.descriptor,
1351 str(policy["nTSecurityDescriptor"])).as_sddl()
1352 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1353 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1357 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1359 """Set the ACL for the sysvol share and the subfolders
1361 :param samdb: An LDB object on the SAM db
1362 :param netlogon: Physical path for the netlogon folder
1363 :param sysvol: Physical path for the sysvol folder
1364 :param gid: The GID of the "Domain adminstrators" group
1365 :param domainsid: The SID of the domain
1366 :param dnsdomain: The DNS name of the domain
1367 :param domaindn: The DN of the domain (ie. DC=...)
1371 os.chown(sysvol, -1, gid)
1377 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1378 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1379 for root, dirs, files in os.walk(sysvol, topdown=False):
1382 os.chown(os.path.join(root, name), -1, gid)
1383 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1386 os.chown(os.path.join(root, name), -1, gid)
1387 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1389 # Set acls on Policy folder and policies folders
1390 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1393 def provision(setup_dir, logger, session_info, credentials, smbconf=None,
1394 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1395 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1396 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1397 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1398 domainguid=None, policyguid=None, policyguid_dc=None,
1399 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1400 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1401 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1402 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1403 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1404 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1408 :note: caution, this wipes all existing data!
1411 def setup_path(file):
1412 return os.path.join(setup_dir, file)
1414 if domainsid is None:
1415 domainsid = security.random_sid()
1417 domainsid = security.dom_sid(domainsid)
1419 # create/adapt the group policy GUIDs
1420 # Default GUID for default policy are described at
1421 # "How Core Group Policy Works"
1422 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1423 if policyguid is None:
1424 policyguid = DEFAULT_POLICY_GUID
1425 policyguid = policyguid.upper()
1426 if policyguid_dc is None:
1427 policyguid_dc = DEFAULT_DC_POLICY_GUID
1428 policyguid_dc = policyguid_dc.upper()
1430 if adminpass is None:
1431 adminpass = samba.generate_random_password(12, 32)
1432 if krbtgtpass is None:
1433 krbtgtpass = samba.generate_random_password(128, 255)
1434 if machinepass is None:
1435 machinepass = samba.generate_random_password(128, 255)
1437 dnspass = samba.generate_random_password(128, 255)
1438 if ldapadminpass is None:
1439 # Make a new, random password between Samba and it's LDAP server
1440 ldapadminpass=samba.generate_random_password(128, 255)
1442 if backend_type is None:
1443 backend_type = "ldb"
1445 sid_generator = "internal"
1446 if backend_type == "fedora-ds":
1447 sid_generator = "backend"
1449 root_uid = findnss_uid([root or "root"])
1450 nobody_uid = findnss_uid([nobody or "nobody"])
1451 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1453 wheel_gid = findnss_gid(["wheel", "adm"])
1455 wheel_gid = findnss_gid([wheel])
1457 bind_gid = findnss_gid(["bind", "named"])
1461 if targetdir is not None:
1462 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1463 elif smbconf is None:
1464 smbconf = samba.param.default_path()
1465 if not os.path.exists(os.path.dirname(smbconf)):
1466 os.makedirs(os.path.dirname(smbconf))
1468 # only install a new smb.conf if there isn't one there already
1469 if os.path.exists(smbconf):
1470 # if Samba Team members can't figure out the weird errors
1471 # loading an empty smb.conf gives, then we need to be smarter.
1472 # Pretend it just didn't exist --abartlet
1473 data = open(smbconf, 'r').read()
1474 data = data.lstrip()
1475 if data is None or data == "":
1476 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1477 serverrole, targetdir, sid_generator, useeadb,
1480 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1481 targetdir, sid_generator, useeadb, lp=lp)
1484 lp = samba.param.LoadParm()
1486 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1487 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1488 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1490 paths = provision_paths_from_lp(lp, names.dnsdomain)
1492 paths.bind_gid = bind_gid
1495 logger.info("Looking up IPv4 addresses")
1496 hostips = samba.interface_ips(lp, False)
1497 if len(hostips) == 0:
1498 logger.warning("No external IPv4 address has been found. Using loopback.")
1499 hostip = '127.0.0.1'
1502 if len(hostips) > 1:
1503 logger.warning("More than one IPv4 address found. Using %s.",
1506 if serverrole is None:
1507 serverrole = lp.get("server role")
1509 assert serverrole in ("domain controller", "member server", "standalone")
1510 if invocationid is None:
1511 invocationid = str(uuid.uuid4())
1513 if not os.path.exists(paths.private_dir):
1514 os.mkdir(paths.private_dir)
1515 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1516 os.mkdir(os.path.join(paths.private_dir, "tls"))
1518 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1520 schema = Schema(setup_path, domainsid, invocationid=invocationid,
1521 schemadn=names.schemadn)
1523 if backend_type == "ldb":
1524 provision_backend = LDBBackend(backend_type, paths=paths,
1525 setup_path=setup_path, lp=lp, credentials=credentials,
1526 names=names, logger=logger)
1527 elif backend_type == "existing":
1528 provision_backend = ExistingBackend(backend_type, paths=paths,
1529 setup_path=setup_path, lp=lp, credentials=credentials,
1530 names=names, logger=logger,
1531 ldap_backend_forced_uri=ldap_backend_forced_uri)
1532 elif backend_type == "fedora-ds":
1533 provision_backend = FDSBackend(backend_type, paths=paths,
1534 setup_path=setup_path, lp=lp, credentials=credentials,
1535 names=names, logger=logger, domainsid=domainsid,
1536 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1537 slapd_path=slapd_path,
1538 ldap_backend_extra_port=ldap_backend_extra_port,
1539 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1540 setup_ds_path=setup_ds_path,
1541 ldap_backend_forced_uri=ldap_backend_forced_uri)
1542 elif backend_type == "openldap":
1543 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1544 setup_path=setup_path, lp=lp, credentials=credentials,
1545 names=names, logger=logger, domainsid=domainsid,
1546 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1547 slapd_path=slapd_path,
1548 ldap_backend_extra_port=ldap_backend_extra_port,
1549 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1551 ldap_backend_forced_uri=ldap_backend_forced_uri)
1553 raise ValueError("Unknown LDAP backend type selected")
1555 provision_backend.init()
1556 provision_backend.start()
1558 # only install a new shares config db if there is none
1559 if not os.path.exists(paths.shareconf):
1560 logger.info("Setting up share.ldb")
1561 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1563 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1565 logger.info("Setting up secrets.ldb")
1566 secrets_ldb = setup_secretsdb(paths, setup_path,
1567 session_info=session_info,
1568 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1571 logger.info("Setting up the registry")
1572 setup_registry(paths.hklm, setup_path, session_info,
1575 logger.info("Setting up the privileges database")
1576 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1578 logger.info("Setting up idmap db")
1579 idmap = setup_idmapdb(paths.idmapdb, setup_path,
1580 session_info=session_info, lp=lp)
1582 logger.info("Setting up SAM db")
1583 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1584 provision_backend, lp, names, logger=logger,
1585 domainsid=domainsid, schema=schema, domainguid=domainguid,
1586 policyguid=policyguid, policyguid_dc=policyguid_dc,
1587 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1588 invocationid=invocationid, machinepass=machinepass,
1589 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1590 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1593 if serverrole == "domain controller":
1594 if paths.netlogon is None:
1595 logger.info("Existing smb.conf does not have a [netlogon] share, but you 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.netlogon is not None
1600 if paths.sysvol is None:
1601 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1602 " are configuring a DC.")
1603 logger.info("Please either remove %s or see the template at %s" %
1604 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1605 assert paths.sysvol is not None
1607 if not os.path.isdir(paths.netlogon):
1608 os.makedirs(paths.netlogon, 0755)
1610 if samdb_fill == FILL_FULL:
1611 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1612 root_uid=root_uid, nobody_uid=nobody_uid,
1613 users_gid=users_gid, wheel_gid=wheel_gid)
1615 if serverrole == "domain controller":
1616 # Set up group policies (domain policy and domain controller
1618 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1620 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1621 domainsid, names.dnsdomain, names.domaindn, lp)
1623 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1624 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1626 secretsdb_self_join(secrets_ldb, domain=names.domain,
1627 realm=names.realm, dnsdomain=names.dnsdomain,
1628 netbiosname=names.netbiosname, domainsid=domainsid,
1629 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1631 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1632 # In future, this might be determined from some configuration
1633 kerberos_enctypes = str(ENC_ALL_TYPES)
1636 msg = ldb.Message(ldb.Dn(samdb,
1637 samdb.searchone("distinguishedName",
1638 expression="samAccountName=%s$" % names.netbiosname,
1639 scope=ldb.SCOPE_SUBTREE)))
1640 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1641 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1642 name="msDS-SupportedEncryptionTypes")
1644 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1645 # It might be that this attribute does not exist in this schema
1648 if serverrole == "domain controller":
1649 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1650 paths.private_dir, realm=names.realm,
1651 dnsdomain=names.dnsdomain,
1652 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1654 domainguid = samdb.searchone(basedn=domaindn,
1655 attribute="objectGUID")
1656 assert isinstance(domainguid, str)
1658 # Only make a zone file on the first DC, it should be
1659 # replicated with DNS replication
1660 create_zone_file(lp, logger, paths, targetdir, setup_path,
1661 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1662 hostname=names.hostname, realm=names.realm,
1663 domainguid=domainguid, ntdsguid=names.ntdsguid)
1665 create_named_conf(paths, setup_path, realm=names.realm,
1666 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1668 create_named_txt(paths.namedtxt, setup_path,
1669 realm=names.realm, dnsdomain=names.dnsdomain,
1670 private_dir=paths.private_dir,
1671 keytab_name=paths.dns_keytab)
1672 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1673 logger.info("and %s for further documentation required for secure DNS "
1674 "updates", paths.namedtxt)
1676 lastProvisionUSNs = get_last_provision_usn(samdb)
1677 maxUSN = get_max_usn(samdb, str(names.rootdn))
1678 if lastProvisionUSNs is not None:
1679 update_provision_usn(samdb, 0, maxUSN, 1)
1681 set_provision_usn(samdb, 0, maxUSN)
1683 create_krb5_conf(paths.krb5conf, setup_path,
1684 dnsdomain=names.dnsdomain, hostname=names.hostname,
1686 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1687 "generated at %s", paths.krb5conf)
1689 if serverrole == "domain controller":
1690 create_dns_update_list(lp, logger, paths, setup_path)
1692 provision_backend.post_setup()
1693 provision_backend.shutdown()
1695 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1698 secrets_ldb.transaction_cancel()
1701 # Now commit the secrets.ldb to disk
1702 secrets_ldb.transaction_commit()
1704 # the commit creates the dns.keytab, now chown it
1705 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1706 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1708 os.chmod(dns_keytab_path, 0640)
1709 os.chown(dns_keytab_path, -1, paths.bind_gid)
1711 if not os.environ.has_key('SAMBA_SELFTEST'):
1712 logger.info("Failed to chown %s to bind gid %u",
1713 dns_keytab_path, paths.bind_gid)
1716 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1717 paths.phpldapadminconfig)
1719 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1720 logger.info("Server Role: %s" % serverrole)
1721 logger.info("Hostname: %s" % names.hostname)
1722 logger.info("NetBIOS Domain: %s" % names.domain)
1723 logger.info("DNS Domain: %s" % names.dnsdomain)
1724 logger.info("DOMAIN SID: %s" % str(domainsid))
1725 if samdb_fill == FILL_FULL:
1726 logger.info("Admin password: %s" % adminpass)
1727 if provision_backend.type is not "ldb":
1728 if provision_backend.credentials.get_bind_dn() is not None:
1729 logger.info("LDAP Backend Admin DN: %s" %
1730 provision_backend.credentials.get_bind_dn())
1732 logger.info("LDAP Admin User: %s" %
1733 provision_backend.credentials.get_username())
1735 logger.info("LDAP Admin Password: %s" %
1736 provision_backend.credentials.get_password())
1738 if provision_backend.slapd_command_escaped is not None:
1739 # now display slapd_command_file.txt to show how slapd must be
1741 logger.info("Use later the following commandline to start slapd, then Samba:")
1742 logger.info(provision_backend.slapd_command_escaped)
1743 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1744 provision_backend.ldapdir)
1746 result = ProvisionResult()
1747 result.domaindn = domaindn
1748 result.paths = paths
1750 result.samdb = samdb
1754 def provision_become_dc(setup_dir=None, smbconf=None, targetdir=None,
1755 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1756 serverdn=None, domain=None, hostname=None, domainsid=None,
1757 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1758 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1759 root=None, nobody=None, users=None, wheel=None, backup=None,
1760 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1761 sitename=None, debuglevel=1):
1763 logger = logging.getLogger("provision")
1764 samba.set_debug_level(debuglevel)
1766 res = provision(setup_dir, logger, system_session(), None,
1767 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1768 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1769 configdn=configdn, serverdn=serverdn, domain=domain,
1770 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1771 machinepass=machinepass, serverrole="domain controller",
1773 res.lp.set("debuglevel", str(debuglevel))
1777 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1778 """Create a PHP LDAP admin configuration file.
1780 :param path: Path to write the configuration to.
1781 :param setup_path: Function to generate setup paths.
1783 setup_file(setup_path("phpldapadmin-config.php"), path,
1784 {"S4_LDAPI_URI": ldapi_uri})
1787 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1788 hostip, hostip6, hostname, realm, domainguid,
1790 """Write out a DNS zone file, from the info in the current database.
1792 :param paths: paths object
1793 :param setup_path: Setup path function.
1794 :param dnsdomain: DNS Domain name
1795 :param domaindn: DN of the Domain
1796 :param hostip: Local IPv4 IP
1797 :param hostip6: Local IPv6 IP
1798 :param hostname: Local hostname
1799 :param realm: Realm name
1800 :param domainguid: GUID of the domain.
1801 :param ntdsguid: GUID of the hosts nTDSDSA record.
1803 assert isinstance(domainguid, str)
1805 if hostip6 is not None:
1806 hostip6_base_line = " IN AAAA " + hostip6
1807 hostip6_host_line = hostname + " IN AAAA " + hostip6
1808 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1810 hostip6_base_line = ""
1811 hostip6_host_line = ""
1812 gc_msdcs_ip6_line = ""
1814 if hostip is not None:
1815 hostip_base_line = " IN A " + hostip
1816 hostip_host_line = hostname + " IN A " + hostip
1817 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1819 hostip_base_line = ""
1820 hostip_host_line = ""
1821 gc_msdcs_ip_line = ""
1823 dns_dir = os.path.dirname(paths.dns)
1826 shutil.rmtree(dns_dir, True)
1830 os.mkdir(dns_dir, 0775)
1832 # we need to freeze the zone while we update the contents
1833 if targetdir is None:
1834 rndc = ' '.join(lp.get("rndc command"))
1835 os.system(rndc + " freeze " + lp.get("realm"))
1837 setup_file(setup_path("provision.zone"), paths.dns, {
1838 "HOSTNAME": hostname,
1839 "DNSDOMAIN": dnsdomain,
1841 "HOSTIP_BASE_LINE": hostip_base_line,
1842 "HOSTIP_HOST_LINE": hostip_host_line,
1843 "DOMAINGUID": domainguid,
1844 "DATESTRING": time.strftime("%Y%m%d%H"),
1845 "DEFAULTSITE": DEFAULTSITE,
1846 "NTDSGUID": ntdsguid,
1847 "HOSTIP6_BASE_LINE": hostip6_base_line,
1848 "HOSTIP6_HOST_LINE": hostip6_host_line,
1849 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1850 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1853 # note that we use no variable substitution on this file
1854 # the substitution is done at runtime by samba_dnsupdate
1855 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1857 # and the SPN update list
1858 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1860 if paths.bind_gid is not None:
1862 os.chown(dns_dir, -1, paths.bind_gid)
1863 os.chown(paths.dns, -1, paths.bind_gid)
1864 # chmod needed to cope with umask
1865 os.chmod(dns_dir, 0775)
1866 os.chmod(paths.dns, 0664)
1868 if not os.environ.has_key('SAMBA_SELFTEST'):
1869 logger.error("Failed to chown %s to bind gid %u" % (
1870 dns_dir, paths.bind_gid))
1872 if targetdir is None:
1873 os.system(rndc + " unfreeze " + lp.get("realm"))
1876 def create_dns_update_list(lp, logger, paths, setup_path):
1877 """Write out a dns_update_list file"""
1878 # note that we use no variable substitution on this file
1879 # the substitution is done at runtime by samba_dnsupdate
1880 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1881 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1884 def create_named_conf(paths, setup_path, realm, dnsdomain,
1886 """Write out a file containing zone statements suitable for inclusion in a
1887 named.conf file (including GSS-TSIG configuration).
1889 :param paths: all paths
1890 :param setup_path: Setup path function.
1891 :param realm: Realm name
1892 :param dnsdomain: DNS Domain name
1893 :param private_dir: Path to private directory
1894 :param keytab_name: File name of DNS keytab file
1897 setup_file(setup_path("named.conf"), paths.namedconf, {
1898 "DNSDOMAIN": dnsdomain,
1900 "ZONE_FILE": paths.dns,
1901 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1902 "NAMED_CONF": paths.namedconf,
1903 "NAMED_CONF_UPDATE": paths.namedconf_update
1906 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1909 def create_named_txt(path, setup_path, realm, dnsdomain, private_dir,
1911 """Write out a file containing zone statements suitable for inclusion in a
1912 named.conf file (including GSS-TSIG configuration).
1914 :param path: Path of the new named.conf file.
1915 :param setup_path: Setup path function.
1916 :param realm: Realm name
1917 :param dnsdomain: DNS Domain name
1918 :param private_dir: Path to private directory
1919 :param keytab_name: File name of DNS keytab file
1921 setup_file(setup_path("named.txt"), path, {
1922 "DNSDOMAIN": dnsdomain,
1924 "DNS_KEYTAB": keytab_name,
1925 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1926 "PRIVATE_DIR": private_dir
1930 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1931 """Write out a file containing zone statements suitable for inclusion in a
1932 named.conf file (including GSS-TSIG configuration).
1934 :param path: Path of the new named.conf file.
1935 :param setup_path: Setup path function.
1936 :param dnsdomain: DNS Domain name
1937 :param hostname: Local hostname
1938 :param realm: Realm name
1940 setup_file(setup_path("krb5.conf"), path, {
1941 "DNSDOMAIN": dnsdomain,
1942 "HOSTNAME": hostname,
1947 class ProvisioningError(Exception):
1948 """A generic provision error."""
1950 def __init__(self, value):
1954 return "ProvisioningError: " + self.value
1957 class InvalidNetbiosName(Exception):
1958 """A specified name was not a valid NetBIOS name."""
1959 def __init__(self, name):
1960 super(InvalidNetbiosName, self).__init__(
1961 "The name '%r' is not a valid NetBIOS name" % name)