2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
42 from samba.auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name
45 from samba import check_all_substituted, read_and_sub_file, setup_file
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008_R2, ENC_ALL_TYPES
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ms_display_specifiers import read_ms_ldif
51 from samba.ntacls import setntacl, dsacl2fsacl
52 from samba.ndr import ndr_pack,ndr_unpack
53 from samba.provisionbackend import (
61 from samba.schema import Schema
62 from samba.samdb import SamDB
64 __docformat__ = "restructuredText"
65 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
66 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
69 """Find the setup directory used by provision."""
71 for prefix in [sys.prefix,
72 os.path.join(os.path.dirname(__file__), "../../../..")]:
73 for suffix in ["share/setup", "share/samba/setup", "setup"]:
74 ret = os.path.join(prefix, suffix)
75 if os.path.isdir(ret):
78 dirname = os.path.dirname(__file__)
79 ret = os.path.join(dirname, "../../../setup")
80 if os.path.isdir(ret):
82 raise Exception("Unable to find setup directory.")
84 # descriptors of the naming contexts
85 # hard coded at this point, but will probably be changed when
86 # we enable different fsmo roles
89 def get_config_descriptor(domain_sid):
90 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
91 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
92 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
93 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
94 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
95 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
97 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
98 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
99 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
100 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
101 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
102 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
103 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
104 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
105 sec = security.descriptor.from_sddl(sddl, domain_sid)
108 def get_domain_descriptor(domain_sid):
109 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
110 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
111 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
112 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
113 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
114 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
115 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
116 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
117 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
118 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
119 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
120 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
121 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
122 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
123 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
124 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
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 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
129 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
130 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
131 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
133 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
134 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
135 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
136 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
137 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
138 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
139 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
140 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
141 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
142 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
143 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
144 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
145 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
146 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
147 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
150 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
152 "(A;;RPLCLORC;;;ED)" \
153 "(A;;RPLCLORC;;;AU)" \
154 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
155 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
156 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
157 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
158 sec = security.descriptor.from_sddl(sddl, domain_sid)
161 DEFAULTSITE = "Default-First-Site-Name"
162 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
164 class ProvisionPaths(object):
167 self.shareconf = None
178 self.dns_keytab = None
181 self.private_dir = None
184 class ProvisionNames(object):
191 self.ldapmanagerdn = None
192 self.dnsdomain = None
194 self.netbiosname = None
201 def update_provision_usn(samdb, low, high, replace=False):
202 """Update the field provisionUSN in sam.ldb
204 This field is used to track range of USN modified by provision and
206 This value is used afterward by next provision to figure out if
207 the field have been modified since last provision.
209 :param samdb: An LDB object connect to sam.ldb
210 :param low: The lowest USN modified by this upgrade
211 :param high: The highest USN modified by this upgrade
212 :param replace: A boolean indicating if the range should replace any
213 existing one or appended (default)
218 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % \
219 LAST_PROVISION_USN_ATTRIBUTE, base="",
220 scope=ldb.SCOPE_SUBTREE,
221 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
222 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
225 tab.append("%s-%s" % (low, high))
226 delta = ldb.Message()
227 delta.dn = ldb.Dn(samdb, "@PROVISION")
228 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
229 ldb.FLAG_MOD_REPLACE,
230 LAST_PROVISION_USN_ATTRIBUTE)
234 def set_provision_usn(samdb, low, high):
235 """Set the field provisionUSN in sam.ldb
236 This field is used to track range of USN modified by provision and
238 This value is used afterward by next provision to figure out if
239 the field have been modified since last provision.
241 :param samdb: An LDB object connect to sam.ldb
242 :param low: The lowest USN modified by this upgrade
243 :param high: The highest USN modified by this upgrade"""
245 tab.append("%s-%s" % (low, high))
246 delta = ldb.Message()
247 delta.dn = ldb.Dn(samdb, "@PROVISION")
248 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
250 LAST_PROVISION_USN_ATTRIBUTE)
254 def get_max_usn(samdb,basedn):
255 """ This function return the biggest USN present in the provision
257 :param samdb: A LDB object pointing to the sam.ldb
258 :param basedn: A string containing the base DN of the provision
260 :return: The biggest USN in the provision"""
262 res = samdb.search(expression="objectClass=*",base=basedn,
263 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
264 controls=["search_options:1:2",
265 "server_sort:1:1:uSNChanged",
266 "paged_results:1:1"])
267 return res[0]["uSNChanged"]
269 def get_last_provision_usn(sam):
270 """Get the lastest USN modified by a provision or an upgradeprovision
272 :param sam: An LDB object pointing to the sam.ldb
273 :return an integer corresponding to the highest USN modified by
274 (upgrade)provision, 0 is this value is unknown"""
276 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % \
277 LAST_PROVISION_USN_ATTRIBUTE,
278 base="", scope=ldb.SCOPE_SUBTREE,
279 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
284 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
285 tab = p.split(str(r))
293 class ProvisionResult(object):
302 def check_install(lp, session_info, credentials):
303 """Check whether the current install seems ok.
305 :param lp: Loadparm context
306 :param session_info: Session information
307 :param credentials: Credentials
309 if lp.get("realm") == "":
310 raise Exception("Realm empty")
311 samdb = Ldb(lp.get("sam database"), session_info=session_info,
312 credentials=credentials, lp=lp)
313 if len(samdb.search("(cn=Administrator)")) != 1:
314 raise ProvisioningError("No administrator account found")
317 def findnss(nssfn, names):
318 """Find a user or group from a list of possibilities.
320 :param nssfn: NSS Function to try (should raise KeyError if not found)
321 :param names: Names to check.
322 :return: Value return by first names list.
329 raise KeyError("Unable to find user/group in %r" % names)
332 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
333 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
336 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
337 """Setup a ldb in the private dir.
339 :param ldb: LDB file to import data into
340 :param ldif_path: Path of the LDIF file to load
341 :param subst_vars: Optional variables to subsitute in LDIF.
342 :param nocontrols: Optional list of controls, can be None for no controls
344 assert isinstance(ldif_path, str)
345 data = read_and_sub_file(ldif_path, subst_vars)
346 ldb.add_ldif(data, controls)
349 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
350 """Modify a ldb in the private dir.
352 :param ldb: LDB object.
353 :param ldif_path: LDIF file path.
354 :param subst_vars: Optional dictionary with substitution variables.
356 data = read_and_sub_file(ldif_path, subst_vars)
357 ldb.modify_ldif(data, controls)
360 def setup_ldb(ldb, ldif_path, subst_vars):
361 """Import a LDIF a file into a LDB handle, optionally substituting variables.
363 :note: Either all LDIF data will be added or none (using transactions).
365 :param ldb: LDB file to import into.
366 :param ldif_path: Path to the LDIF file.
367 :param subst_vars: Dictionary with substitution variables.
369 assert ldb is not None
370 ldb.transaction_start()
372 setup_add_ldif(ldb, ldif_path, subst_vars)
374 ldb.transaction_cancel()
377 ldb.transaction_commit()
380 def provision_paths_from_lp(lp, dnsdomain):
381 """Set the default paths for provisioning.
383 :param lp: Loadparm context.
384 :param dnsdomain: DNS Domain name
386 paths = ProvisionPaths()
387 paths.private_dir = lp.get("private dir")
389 # This is stored without path prefix for the "privateKeytab" attribute in
390 # "secrets_dns.ldif".
391 paths.dns_keytab = "dns.keytab"
393 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
394 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
395 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
396 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
397 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
398 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
399 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
400 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
401 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
402 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
403 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
404 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
405 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
406 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
407 paths.phpldapadminconfig = os.path.join(paths.private_dir,
408 "phpldapadmin-config.php")
409 paths.hklm = "hklm.ldb"
410 paths.hkcr = "hkcr.ldb"
411 paths.hkcu = "hkcu.ldb"
412 paths.hku = "hku.ldb"
413 paths.hkpd = "hkpd.ldb"
414 paths.hkpt = "hkpt.ldb"
415 paths.sysvol = lp.get("path", "sysvol")
416 paths.netlogon = lp.get("path", "netlogon")
417 paths.smbconf = lp.configfile
421 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
422 serverrole=None, rootdn=None, domaindn=None, configdn=None,
423 schemadn=None, serverdn=None, sitename=None):
424 """Guess configuration settings to use."""
427 hostname = socket.gethostname().split(".")[0]
429 netbiosname = lp.get("netbios name")
430 if netbiosname is None:
431 netbiosname = hostname
432 assert netbiosname is not None
433 netbiosname = netbiosname.upper()
434 if not valid_netbios_name(netbiosname):
435 raise InvalidNetbiosName(netbiosname)
437 if dnsdomain is None:
438 dnsdomain = lp.get("realm")
439 if dnsdomain is None or dnsdomain == "":
440 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
442 dnsdomain = dnsdomain.lower()
444 if serverrole is None:
445 serverrole = lp.get("server role")
446 if serverrole is None:
447 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
449 serverrole = serverrole.lower()
451 realm = dnsdomain.upper()
453 if lp.get("realm") == "":
454 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
456 if lp.get("realm").upper() != realm:
457 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))
459 if lp.get("server role").lower() != serverrole:
460 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))
462 if serverrole == "domain controller":
464 # This will, for better or worse, default to 'WORKGROUP'
465 domain = lp.get("workgroup")
466 domain = domain.upper()
468 if lp.get("workgroup").upper() != domain:
469 raise ProvisioningError("guess_names: Workgroup '%s' in %s must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
472 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
476 domaindn = "DC=" + netbiosname
478 if not valid_netbios_name(domain):
479 raise InvalidNetbiosName(domain)
481 if hostname.upper() == realm:
482 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
483 if netbiosname == realm:
484 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
486 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
492 configdn = "CN=Configuration," + rootdn
494 schemadn = "CN=Schema," + configdn
499 names = ProvisionNames()
500 names.rootdn = rootdn
501 names.domaindn = domaindn
502 names.configdn = configdn
503 names.schemadn = schemadn
504 names.ldapmanagerdn = "CN=Manager," + rootdn
505 names.dnsdomain = dnsdomain
506 names.domain = domain
508 names.netbiosname = netbiosname
509 names.hostname = hostname
510 names.sitename = sitename
511 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
516 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
517 targetdir, sid_generator="internal", eadb=False):
518 """Create a new smb.conf file based on a couple of basic settings.
520 assert smbconf is not None
522 hostname = socket.gethostname().split(".")[0]
523 netbiosname = hostname.upper()
525 if serverrole is None:
526 serverrole = "standalone"
528 assert serverrole in ("domain controller", "member server", "standalone")
529 if serverrole == "domain controller":
531 elif serverrole == "member server":
532 smbconfsuffix = "member"
533 elif serverrole == "standalone":
534 smbconfsuffix = "standalone"
536 if sid_generator is None:
537 sid_generator = "internal"
539 assert domain is not None
540 domain = domain.upper()
542 assert realm is not None
543 realm = realm.upper()
545 default_lp = samba.param.LoadParm()
546 #Load non-existant file
547 if os.path.exists(smbconf):
548 default_lp.load(smbconf)
550 if targetdir is not None:
551 privdir = os.path.join(targetdir, "private")
553 privdir = default_lp.get("private dir")
554 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
558 if targetdir is not None:
559 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
560 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
562 default_lp.set("lock dir", os.path.abspath(targetdir))
567 if sid_generator == "internal":
568 sid_generator_line = ""
570 sid_generator_line = "sid generator = " + sid_generator
572 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
573 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
575 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
577 "NETBIOS_NAME": netbiosname,
580 "SERVERROLE": serverrole,
581 "NETLOGONPATH": netlogon,
582 "SYSVOLPATH": sysvol,
583 "SIDGENERATOR_LINE": sid_generator_line,
584 "PRIVATEDIR_LINE": privatedir_line,
585 "LOCKDIR_LINE": lockdir_line,
586 "POSIXEADB_LINE": posixeadb_line
590 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
591 users_gid, wheel_gid):
592 """setup reasonable name mappings for sam names to unix names.
594 :param samdb: SamDB object.
595 :param idmap: IDmap db object.
596 :param sid: The domain sid.
597 :param domaindn: The domain DN.
598 :param root_uid: uid of the UNIX root user.
599 :param nobody_uid: uid of the UNIX nobody user.
600 :param users_gid: gid of the UNIX users group.
601 :param wheel_gid: gid of the UNIX wheel group."""
602 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
603 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
605 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
606 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
609 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
610 provision_backend, names, schema, serverrole,
612 """Setup the partitions for the SAM database.
614 Alternatively, provision() may call this, and then populate the database.
616 :note: This will wipe the Sam Database!
618 :note: This function always removes the local SAM LDB file. The erase
619 parameter controls whether to erase the existing data, which
620 may not be stored locally but in LDAP.
623 assert session_info is not None
625 # We use options=["modules:"] to stop the modules loading - we
626 # just want to wipe and re-initialise the database, not start it up
629 os.unlink(samdb_path)
633 samdb = Ldb(url=samdb_path, session_info=session_info,
634 lp=lp, options=["modules:"])
636 ldap_backend_line = "# No LDAP backend"
637 if provision_backend.type is not "ldb":
638 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
640 samdb.transaction_start()
642 logger.info("Setting up sam.ldb partitions and settings")
643 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
644 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
645 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
646 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
647 "LDAP_BACKEND_LINE": ldap_backend_line,
651 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
652 "BACKEND_TYPE": provision_backend.type,
653 "SERVER_ROLE": serverrole
656 logger.info("Setting up sam.ldb rootDSE")
657 setup_samdb_rootdse(samdb, setup_path, names)
659 samdb.transaction_cancel()
662 samdb.transaction_commit()
665 def secretsdb_self_join(secretsdb, domain,
666 netbiosname, machinepass, domainsid=None,
667 realm=None, dnsdomain=None,
669 key_version_number=1,
670 secure_channel_type=SEC_CHAN_WKSTA):
671 """Add domain join-specific bits to a secrets database.
673 :param secretsdb: Ldb Handle to the secrets database
674 :param machinepass: Machine password
676 attrs=["whenChanged",
683 #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
684 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
685 msg["secureChannelType"] = [str(secure_channel_type)]
686 msg["objectClass"] = ["top", "primaryDomain"]
687 if realm is not None:
688 if dnsdomain is None:
689 dnsdomain = realm.lower()
690 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
691 msg["realm"] = [realm]
692 msg["saltPrincipal"] = ["host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())]
693 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
694 msg["privateKeytab"] = ["secrets.keytab"]
697 msg["secret"] = [machinepass]
698 msg["samAccountName"] = ["%s$" % netbiosname]
699 msg["secureChannelType"] = [str(secure_channel_type)]
700 if domainsid is not None:
701 msg["objectSid"] = [ndr_pack(domainsid)]
703 # This complex expression tries to ensure that we don't have more
704 # than one record for this SID, realm or netbios domain at a time,
705 # but we don't delete the old record that we are about to modify,
706 # because that would delete the keytab and previous password.
707 res = secretsdb.search(base="cn=Primary Domains",
709 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
710 scope=ldb.SCOPE_ONELEVEL)
713 secretsdb.delete(del_msg.dn)
715 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
718 msg["priorSecret"] = [res[0]["secret"][0]]
719 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
722 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
727 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
733 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
734 secretsdb.modify(msg)
735 secretsdb.rename(res[0].dn, msg.dn)
740 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
742 dns_keytab_path, dnspass):
743 """Add DNS specific bits to a secrets database.
745 :param secretsdb: Ldb Handle to the secrets database
746 :param setup_path: Setup path function
747 :param machinepass: Machine password
750 os.unlink(os.path.join(private_dir, dns_keytab_path))
754 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
756 "DNSDOMAIN": dnsdomain,
757 "DNS_KEYTAB": dns_keytab_path,
758 "DNSPASS_B64": b64encode(dnspass),
762 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
763 """Setup the secrets database.
765 :note: This function does not handle exceptions and transaction on purpose,
766 it's up to the caller to do this job.
768 :param path: Path to the secrets database.
769 :param setup_path: Get the path to a setup file.
770 :param session_info: Session info.
771 :param credentials: Credentials
772 :param lp: Loadparm context
773 :return: LDB handle for the created secrets database
775 if os.path.exists(path):
777 secrets_ldb = Ldb(path, session_info=session_info,
780 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
781 secrets_ldb = Ldb(path, session_info=session_info,
783 secrets_ldb.transaction_start()
785 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
787 if backend_credentials is not None and backend_credentials.authentication_requested():
788 if backend_credentials.get_bind_dn() is not None:
789 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
790 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
791 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
794 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
795 "LDAPADMINUSER": backend_credentials.get_username(),
796 "LDAPADMINREALM": backend_credentials.get_realm(),
797 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
802 secrets_ldb.transaction_cancel()
805 def setup_privileges(path, setup_path, session_info, lp):
806 """Setup the privileges database.
808 :param path: Path to the privileges database.
809 :param setup_path: Get the path to a setup file.
810 :param session_info: Session info.
811 :param credentials: Credentials
812 :param lp: Loadparm context
813 :return: LDB handle for the created secrets database
815 if os.path.exists(path):
817 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
818 privilege_ldb.erase()
819 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
822 def setup_registry(path, setup_path, session_info, lp):
823 """Setup the registry.
825 :param path: Path to the registry database
826 :param setup_path: Function that returns the path to a setup.
827 :param session_info: Session information
828 :param credentials: Credentials
829 :param lp: Loadparm context
831 reg = samba.registry.Registry()
832 hive = samba.registry.open_ldb(path, session_info=session_info,
834 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
835 provision_reg = setup_path("provision.reg")
836 assert os.path.exists(provision_reg)
837 reg.diff_apply(provision_reg)
840 def setup_idmapdb(path, setup_path, session_info, lp):
841 """Setup the idmap database.
843 :param path: path to the idmap database
844 :param setup_path: Function that returns a path to a setup file
845 :param session_info: Session information
846 :param credentials: Credentials
847 :param lp: Loadparm context
849 if os.path.exists(path):
852 idmap_ldb = IDmapDB(path, session_info=session_info,
856 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
860 def setup_samdb_rootdse(samdb, setup_path, names):
861 """Setup the SamDB rootdse.
863 :param samdb: Sam Database handle
864 :param setup_path: Obtain setup path
866 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
867 "SCHEMADN": names.schemadn,
868 "NETBIOSNAME": names.netbiosname,
869 "DNSDOMAIN": names.dnsdomain,
870 "REALM": names.realm,
871 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
872 "DOMAINDN": names.domaindn,
873 "ROOTDN": names.rootdn,
874 "CONFIGDN": names.configdn,
875 "SERVERDN": names.serverdn,
879 def setup_self_join(samdb, names,
880 machinepass, dnspass,
881 domainsid, next_rid, invocationid, setup_path,
882 policyguid, policyguid_dc, domainControllerFunctionality,
884 """Join a host to its own domain."""
885 assert isinstance(invocationid, str)
886 if ntdsguid is not None:
887 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
890 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
891 "CONFIGDN": names.configdn,
892 "SCHEMADN": names.schemadn,
893 "DOMAINDN": names.domaindn,
894 "SERVERDN": names.serverdn,
895 "INVOCATIONID": invocationid,
896 "NETBIOSNAME": names.netbiosname,
897 "DEFAULTSITE": names.sitename,
898 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
899 "MACHINEPASS_B64": b64encode(machinepass),
900 "REALM": names.realm,
901 "DOMAIN": names.domain,
902 "DOMAINSID": str(domainsid),
903 "DCRID": str(next_rid),
904 "DNSDOMAIN": names.dnsdomain,
905 "SAMBA_VERSION_STRING": version,
906 "NTDSGUID": ntdsguid_line,
907 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
909 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
910 "POLICYGUID": policyguid,
911 "POLICYGUID_DC": policyguid_dc,
912 "DNSDOMAIN": names.dnsdomain,
913 "DOMAINSID": str(domainsid),
914 "DOMAINDN": names.domaindn})
916 # add the NTDSGUID based SPNs
917 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
918 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
919 expression="", scope=ldb.SCOPE_BASE)
920 assert isinstance(names.ntdsguid, str)
922 # Setup fSMORoleOwner entries to point at the newly created DC entry
923 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
924 "DOMAIN": names.domain,
925 "DNSDOMAIN": names.dnsdomain,
926 "DOMAINDN": names.domaindn,
927 "CONFIGDN": names.configdn,
928 "SCHEMADN": names.schemadn,
929 "DEFAULTSITE": names.sitename,
930 "SERVERDN": names.serverdn,
931 "NETBIOSNAME": names.netbiosname,
932 "NTDSGUID": names.ntdsguid,
933 "RIDALLOCATIONSTART": str(next_rid + 100),
934 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
937 # This is partially Samba4 specific and should be replaced by the correct
939 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
940 "DNSDOMAIN": names.dnsdomain,
941 "DOMAINDN": names.domaindn,
942 "DNSPASS_B64": b64encode(dnspass),
945 def getpolicypath(sysvolpath, dnsdomain, guid):
948 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
951 def create_gpo_struct(policy_path):
952 os.makedirs(policy_path, 0755)
953 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
954 "[General]\r\nVersion=65543")
955 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
956 os.makedirs(os.path.join(policy_path, "USER"), 0755)
959 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
960 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
961 create_gpo_struct(policy_path)
963 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
964 create_gpo_struct(policy_path)
967 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
968 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
969 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
970 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
972 """Setup a complete SAM Database.
974 :note: This will wipe the main SAM database file!
978 # Provision does not make much sense values larger than 1000000000
979 # as the upper range of the rIDAvailablePool is 1073741823 and
980 # we don't want to create a domain that cannot allocate rids.
981 if next_rid < 1000 or next_rid > 1000000000:
982 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
983 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
984 raise ProvisioningError(error)
986 # ATTENTION: Do NOT change these default values without discussion with the
987 # team and/or release manager. They have a big impact on the whole program!
988 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
990 if dom_for_fun_level is None:
991 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
993 if dom_for_fun_level > domainControllerFunctionality:
994 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!")
996 domainFunctionality = dom_for_fun_level
997 forestFunctionality = dom_for_fun_level
999 # Also wipes the database
1000 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1001 provision_backend=provision_backend, session_info=session_info,
1002 names=names, serverrole=serverrole, schema=schema)
1005 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1007 # Load the database, but don's load the global schema and don't connect quite yet
1008 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1009 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1012 logger.info("Pre-loading the Samba 4 and AD schema")
1014 # Load the schema from the one we computed earlier
1015 samdb.set_schema(schema)
1017 # And now we can connect to the DB - the schema won't be loaded from the DB
1020 if fill == FILL_DRS:
1023 samdb.transaction_start()
1025 # Set the domain functionality levels onto the database.
1026 # Various module (the password_hash module in particular) need
1027 # to know what level of AD we are emulating.
1029 # These will be fixed into the database via the database
1030 # modifictions below, but we need them set from the start.
1031 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1032 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1033 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1035 samdb.set_domain_sid(str(domainsid))
1036 samdb.set_invocation_id(invocationid)
1037 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1039 logger.info("Adding DomainDN: %s" % names.domaindn)
1041 #impersonate domain admin
1042 admin_session_info = admin_session(lp, str(domainsid))
1043 samdb.set_session_info(admin_session_info)
1044 if domainguid is not None:
1045 domainguid_line = "objectGUID: %s\n-" % domainguid
1047 domainguid_line = ""
1049 descr = b64encode(get_domain_descriptor(domainsid))
1050 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1051 "DOMAINDN": names.domaindn,
1052 "DOMAINGUID": domainguid_line,
1057 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1058 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1059 "DOMAINSID": str(domainsid),
1060 "NEXTRID": str(next_rid),
1061 "SCHEMADN": names.schemadn,
1062 "NETBIOSNAME": names.netbiosname,
1063 "DEFAULTSITE": names.sitename,
1064 "CONFIGDN": names.configdn,
1065 "SERVERDN": names.serverdn,
1066 "POLICYGUID": policyguid,
1067 "DOMAINDN": names.domaindn,
1068 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1069 "SAMBA_VERSION_STRING": version
1072 logger.info("Adding configuration container")
1073 descr = b64encode(get_config_descriptor(domainsid))
1074 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1075 "CONFIGDN": names.configdn,
1076 "DESCRIPTOR": descr,
1079 # The LDIF here was created when the Schema object was constructed
1080 logger.info("Setting up sam.ldb schema")
1081 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1082 samdb.modify_ldif(schema.schema_dn_modify)
1083 samdb.write_prefixes_from_schema()
1084 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1085 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1086 {"SCHEMADN": names.schemadn})
1088 logger.info("Reopening sam.ldb with new schema")
1090 samdb.transaction_cancel()
1093 samdb.transaction_commit()
1095 samdb = SamDB(session_info=admin_session_info,
1096 credentials=provision_backend.credentials, lp=lp,
1097 global_schema=False, am_rodc=am_rodc)
1099 samdb.transaction_start()
1101 samdb.invocation_id = invocationid
1103 logger.info("Setting up sam.ldb configuration data")
1104 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1105 "CONFIGDN": names.configdn,
1106 "NETBIOSNAME": names.netbiosname,
1107 "DEFAULTSITE": names.sitename,
1108 "DNSDOMAIN": names.dnsdomain,
1109 "DOMAIN": names.domain,
1110 "SCHEMADN": names.schemadn,
1111 "DOMAINDN": names.domaindn,
1112 "SERVERDN": names.serverdn,
1113 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1114 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1117 logger.info("Setting up display specifiers")
1118 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1119 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1120 check_all_substituted(display_specifiers_ldif)
1121 samdb.add_ldif(display_specifiers_ldif)
1123 logger.info("Adding users container")
1124 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1125 "DOMAINDN": names.domaindn})
1126 logger.info("Modifying users container")
1127 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1128 "DOMAINDN": names.domaindn})
1129 logger.info("Adding computers container")
1130 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1131 "DOMAINDN": names.domaindn})
1132 logger.info("Modifying computers container")
1133 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1134 "DOMAINDN": names.domaindn})
1135 logger.info("Setting up sam.ldb data")
1136 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1137 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1138 "DOMAINDN": names.domaindn,
1139 "NETBIOSNAME": names.netbiosname,
1140 "DEFAULTSITE": names.sitename,
1141 "CONFIGDN": names.configdn,
1142 "SERVERDN": names.serverdn,
1143 "RIDAVAILABLESTART": str(next_rid + 600),
1144 "POLICYGUID_DC": policyguid_dc
1147 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1148 "DOMAINDN": names.domaindn})
1150 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1151 "CONFIGDN": names.configdn,
1152 "SCHEMADN": names.schemadn})
1153 if fill == FILL_FULL:
1154 logger.info("Setting up sam.ldb users and groups")
1155 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1156 "DOMAINDN": names.domaindn,
1157 "DOMAINSID": str(domainsid),
1158 "CONFIGDN": names.configdn,
1159 "ADMINPASS_B64": b64encode(adminpass),
1160 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1163 logger.info("Setting up self join")
1164 setup_self_join(samdb, names=names, invocationid=invocationid,
1166 machinepass=machinepass,
1167 domainsid=domainsid,
1169 policyguid=policyguid,
1170 policyguid_dc=policyguid_dc,
1171 setup_path=setup_path,
1172 domainControllerFunctionality=domainControllerFunctionality,
1175 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=%s,CN=Sites,CN=Configuration,%s" % (names.hostname, names.sitename, names.domaindn)
1176 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1177 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1178 assert isinstance(names.ntdsguid, str)
1180 samdb.transaction_cancel()
1183 samdb.transaction_commit()
1188 FILL_NT4SYNC = "NT4SYNC"
1190 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1191 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)"
1193 def set_dir_acl(path, acl, lp, domsid):
1194 setntacl(lp, path, acl, domsid)
1195 for root, dirs, files in os.walk(path, topdown=False):
1197 setntacl(lp, os.path.join(root, name), acl, domsid)
1199 setntacl(lp, os.path.join(root, name), acl, domsid)
1202 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1204 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1205 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1207 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1208 attrs=["cn", "nTSecurityDescriptor"],
1209 expression="", scope=ldb.SCOPE_ONELEVEL)
1211 acl = ndr_unpack(security.descriptor,
1212 str(policy["nTSecurityDescriptor"])).as_sddl()
1213 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1214 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1217 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1220 os.chown(sysvol,-1,gid)
1226 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1227 for root, dirs, files in os.walk(sysvol, topdown=False):
1230 os.chown(os.path.join(root, name),-1,gid)
1231 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1234 os.chown(os.path.join(root, name),-1,gid)
1235 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1236 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1239 def provision(setup_dir, logger, session_info,
1240 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1242 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1244 domain=None, hostname=None, hostip=None, hostip6=None,
1245 domainsid=None, next_rid=1000,
1246 adminpass=None, ldapadminpass=None,
1247 krbtgtpass=None, domainguid=None,
1248 policyguid=None, policyguid_dc=None, invocationid=None,
1249 machinepass=None, ntdsguid=None,
1250 dnspass=None, root=None, nobody=None, users=None,
1251 wheel=None, backup=None, aci=None, serverrole=None,
1252 dom_for_fun_level=None,
1253 ldap_backend_extra_port=None, backend_type=None,
1255 ol_mmr_urls=None, ol_olc=None,
1256 setup_ds_path=None, slapd_path=None, nosync=False,
1257 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1260 :note: caution, this wipes all existing data!
1263 def setup_path(file):
1264 return os.path.join(setup_dir, file)
1266 if domainsid is None:
1267 domainsid = security.random_sid()
1269 domainsid = security.dom_sid(domainsid)
1271 # create/adapt the group policy GUIDs
1272 # Default GUID for default policy are described at
1273 # "How Core Group Policy Works"
1274 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1275 if policyguid is None:
1276 policyguid = DEFAULT_POLICY_GUID
1277 policyguid = policyguid.upper()
1278 if policyguid_dc is None:
1279 policyguid_dc = DEFAULT_DC_POLICY_GUID
1280 policyguid_dc = policyguid_dc.upper()
1282 if adminpass is None:
1283 adminpass = samba.generate_random_password(12, 32)
1284 if krbtgtpass is None:
1285 krbtgtpass = samba.generate_random_password(128, 255)
1286 if machinepass is None:
1287 machinepass = samba.generate_random_password(128, 255)
1289 dnspass = samba.generate_random_password(128, 255)
1290 if ldapadminpass is None:
1291 #Make a new, random password between Samba and it's LDAP server
1292 ldapadminpass=samba.generate_random_password(128, 255)
1294 if backend_type is None:
1295 backend_type = "ldb"
1297 sid_generator = "internal"
1298 if backend_type == "fedora-ds":
1299 sid_generator = "backend"
1301 root_uid = findnss_uid([root or "root"])
1302 nobody_uid = findnss_uid([nobody or "nobody"])
1303 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1305 wheel_gid = findnss_gid(["wheel", "adm"])
1307 wheel_gid = findnss_gid([wheel])
1309 bind_gid = findnss_gid(["bind", "named"])
1313 if targetdir is not None:
1314 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1315 elif smbconf is None:
1316 smbconf = samba.param.default_path()
1317 if not os.path.exists(os.path.dirname(smbconf)):
1318 os.makedirs(os.path.dirname(smbconf))
1320 # only install a new smb.conf if there isn't one there already
1321 if os.path.exists(smbconf):
1322 # if Samba Team members can't figure out the weird errors
1323 # loading an empty smb.conf gives, then we need to be smarter.
1324 # Pretend it just didn't exist --abartlet
1325 data = open(smbconf, 'r').read()
1326 data = data.lstrip()
1327 if data is None or data == "":
1328 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1329 serverrole, targetdir, sid_generator, useeadb)
1331 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1332 targetdir, sid_generator, useeadb)
1334 lp = samba.param.LoadParm()
1337 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1338 dnsdomain=realm, serverrole=serverrole,
1339 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1340 serverdn=serverdn, sitename=sitename)
1342 paths = provision_paths_from_lp(lp, names.dnsdomain)
1344 paths.bind_gid = bind_gid
1347 hostips = samba.interface_ips(lp, False)
1348 if len(hostips) == 0:
1349 logger.warning("No external IPv4 address has been found. Using loopback.")
1350 hostip = '127.0.0.1'
1353 if len(hostips) > 1:
1354 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1358 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1361 if hostip6 == '::1' and ip[-1][0] != '::1':
1363 except socket.gaierror, (socket.EAI_NODATA, msg):
1366 if serverrole is None:
1367 serverrole = lp.get("server role")
1369 assert serverrole in ("domain controller", "member server", "standalone")
1370 if invocationid is None:
1371 invocationid = str(uuid.uuid4())
1373 if not os.path.exists(paths.private_dir):
1374 os.mkdir(paths.private_dir)
1375 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1376 os.mkdir(os.path.join(paths.private_dir, "tls"))
1378 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1380 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1381 serverdn=names.serverdn)
1383 if backend_type == "ldb":
1384 provision_backend = LDBBackend(backend_type,
1385 paths=paths, setup_path=setup_path,
1386 lp=lp, credentials=credentials,
1389 elif backend_type == "existing":
1390 provision_backend = ExistingBackend(backend_type,
1391 paths=paths, setup_path=setup_path,
1392 lp=lp, credentials=credentials,
1395 ldapi_url=ldapi_url)
1396 elif backend_type == "fedora-ds":
1397 provision_backend = FDSBackend(backend_type,
1398 paths=paths, setup_path=setup_path,
1399 lp=lp, credentials=credentials,
1402 domainsid=domainsid,
1405 ldapadminpass=ldapadminpass,
1406 slapd_path=slapd_path,
1407 ldap_backend_extra_port=ldap_backend_extra_port,
1408 ldap_dryrun_mode=ldap_dryrun_mode,
1410 setup_ds_path=setup_ds_path)
1411 elif backend_type == "openldap":
1412 provision_backend = OpenLDAPBackend(backend_type,
1413 paths=paths, setup_path=setup_path,
1414 lp=lp, credentials=credentials,
1417 domainsid=domainsid,
1420 ldapadminpass=ldapadminpass,
1421 slapd_path=slapd_path,
1422 ldap_backend_extra_port=ldap_backend_extra_port,
1423 ldap_dryrun_mode=ldap_dryrun_mode,
1424 ol_mmr_urls=ol_mmr_urls,
1427 raise ValueError("Unknown LDAP backend type selected")
1429 provision_backend.init()
1430 provision_backend.start()
1432 # only install a new shares config db if there is none
1433 if not os.path.exists(paths.shareconf):
1434 logger.info("Setting up share.ldb")
1435 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1437 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1440 logger.info("Setting up secrets.ldb")
1441 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1442 session_info=session_info,
1443 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1446 logger.info("Setting up the registry")
1447 setup_registry(paths.hklm, setup_path, session_info,
1450 logger.info("Setting up the privileges database")
1451 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1453 logger.info("Setting up idmap db")
1454 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1457 logger.info("Setting up SAM db")
1458 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1459 provision_backend, lp, names,
1461 domainsid=domainsid,
1462 schema=schema, domainguid=domainguid,
1463 policyguid=policyguid, policyguid_dc=policyguid_dc,
1465 adminpass=adminpass, krbtgtpass=krbtgtpass,
1466 invocationid=invocationid,
1467 machinepass=machinepass, dnspass=dnspass,
1468 ntdsguid=ntdsguid, serverrole=serverrole,
1469 dom_for_fun_level=dom_for_fun_level,
1470 am_rodc=am_rodc, next_rid=next_rid)
1472 if serverrole == "domain controller":
1473 if paths.netlogon is None:
1474 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1475 logger.info("Please either remove %s or see the template at %s" %
1476 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1477 assert paths.netlogon is not None
1479 if paths.sysvol is None:
1480 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1481 " are configuring a DC.")
1482 logger.info("Please either remove %s or see the template at %s" %
1483 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1484 assert paths.sysvol is not None
1486 if not os.path.isdir(paths.netlogon):
1487 os.makedirs(paths.netlogon, 0755)
1489 if samdb_fill == FILL_FULL:
1490 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1491 root_uid=root_uid, nobody_uid=nobody_uid,
1492 users_gid=users_gid, wheel_gid=wheel_gid)
1494 if serverrole == "domain controller":
1495 # Set up group policies (domain policy and domain controller policy)
1496 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1497 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1498 domainsid, names.dnsdomain, names.domaindn, lp)
1500 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1501 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1503 secretsdb_self_join(secrets_ldb, domain=names.domain,
1505 dnsdomain=names.dnsdomain,
1506 netbiosname=names.netbiosname,
1507 domainsid=domainsid,
1508 machinepass=machinepass,
1509 secure_channel_type=SEC_CHAN_BDC)
1511 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1512 # In future, this might be determined from some configuration
1513 kerberos_enctypes = str(ENC_ALL_TYPES)
1516 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1517 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1518 flags=ldb.FLAG_MOD_REPLACE,
1519 name="msDS-SupportedEncryptionTypes")
1521 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1522 # It might be that this attribute does not exist in this schema
1526 if serverrole == "domain controller":
1527 secretsdb_setup_dns(secrets_ldb, setup_path,
1529 realm=names.realm, dnsdomain=names.dnsdomain,
1530 dns_keytab_path=paths.dns_keytab,
1533 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1534 assert isinstance(domainguid, str)
1536 # Only make a zone file on the first DC, it should be replicated
1537 # with DNS replication
1538 create_zone_file(lp, logger, paths, targetdir, setup_path,
1539 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1540 hostname=names.hostname, realm=names.realm,
1541 domainguid=domainguid, ntdsguid=names.ntdsguid)
1543 create_named_conf(paths, setup_path, realm=names.realm,
1544 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1546 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1547 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1548 keytab_name=paths.dns_keytab)
1549 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1550 logger.info("and %s for further documentation required for secure DNS "
1551 "updates", paths.namedtxt)
1553 create_krb5_conf(paths.krb5conf, setup_path,
1554 dnsdomain=names.dnsdomain, hostname=names.hostname,
1556 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1557 "generated at %s", paths.krb5conf)
1559 lastProvisionUSNs = get_last_provision_usn(samdb)
1560 maxUSN = get_max_usn(samdb, str(names.rootdn))
1561 if lastProvisionUSNs is not None:
1562 update_provision_usn(samdb, 0, maxUSN, 1)
1564 set_provision_usn(samdb, 0, maxUSN)
1566 if serverrole == "domain controller":
1567 create_dns_update_list(lp, logger, paths, setup_path)
1569 provision_backend.post_setup()
1570 provision_backend.shutdown()
1572 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1575 secrets_ldb.transaction_cancel()
1578 #Now commit the secrets.ldb to disk
1579 secrets_ldb.transaction_commit()
1581 # the commit creates the dns.keytab, now chown it
1582 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1583 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1585 os.chmod(dns_keytab_path, 0640)
1586 os.chown(dns_keytab_path, -1, paths.bind_gid)
1588 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1592 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1593 paths.phpldapadminconfig)
1595 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1596 logger.info("Server Role: %s" % serverrole)
1597 logger.info("Hostname: %s" % names.hostname)
1598 logger.info("NetBIOS Domain: %s" % names.domain)
1599 logger.info("DNS Domain: %s" % names.dnsdomain)
1600 logger.info("DOMAIN SID: %s" % str(domainsid))
1601 if samdb_fill == FILL_FULL:
1602 logger.info("Admin password: %s" % adminpass)
1603 if provision_backend.type is not "ldb":
1604 if provision_backend.credentials.get_bind_dn() is not None:
1605 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1607 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1609 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1611 if provision_backend.slapd_command_escaped is not None:
1612 # now display slapd_command_file.txt to show how slapd must be started next time
1613 logger.info("Use later the following commandline to start slapd, then Samba:")
1614 logger.info(provision_backend.slapd_command_escaped)
1615 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1616 provision_backend.ldapdir)
1618 result = ProvisionResult()
1619 result.domaindn = domaindn
1620 result.paths = paths
1622 result.samdb = samdb
1626 def provision_become_dc(setup_dir=None,
1627 smbconf=None, targetdir=None, realm=None,
1628 rootdn=None, domaindn=None, schemadn=None,
1629 configdn=None, serverdn=None,
1630 domain=None, hostname=None, domainsid=None,
1631 adminpass=None, krbtgtpass=None, domainguid=None,
1632 policyguid=None, policyguid_dc=None, invocationid=None,
1634 dnspass=None, root=None, nobody=None, users=None,
1635 wheel=None, backup=None, serverrole=None,
1636 ldap_backend=None, ldap_backend_type=None,
1637 sitename=None, debuglevel=1):
1639 logger = logging.getLogger("provision")
1640 samba.set_debug_level(debuglevel)
1642 return provision(setup_dir, logger, system_session(), None,
1643 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1644 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1645 configdn=configdn, serverdn=serverdn, domain=domain,
1646 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1647 machinepass=machinepass, serverrole="domain controller",
1651 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1652 """Create a PHP LDAP admin configuration file.
1654 :param path: Path to write the configuration to.
1655 :param setup_path: Function to generate setup paths.
1657 setup_file(setup_path("phpldapadmin-config.php"), path,
1658 {"S4_LDAPI_URI": ldapi_uri})
1661 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1662 hostip, hostip6, hostname, realm, domainguid,
1664 """Write out a DNS zone file, from the info in the current database.
1666 :param paths: paths object
1667 :param setup_path: Setup path function.
1668 :param dnsdomain: DNS Domain name
1669 :param domaindn: DN of the Domain
1670 :param hostip: Local IPv4 IP
1671 :param hostip6: Local IPv6 IP
1672 :param hostname: Local hostname
1673 :param realm: Realm name
1674 :param domainguid: GUID of the domain.
1675 :param ntdsguid: GUID of the hosts nTDSDSA record.
1677 assert isinstance(domainguid, str)
1679 if hostip6 is not None:
1680 hostip6_base_line = " IN AAAA " + hostip6
1681 hostip6_host_line = hostname + " IN AAAA " + hostip6
1682 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1684 hostip6_base_line = ""
1685 hostip6_host_line = ""
1686 gc_msdcs_ip6_line = ""
1688 if hostip is not None:
1689 hostip_base_line = " IN A " + hostip
1690 hostip_host_line = hostname + " IN A " + hostip
1691 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1693 hostip_base_line = ""
1694 hostip_host_line = ""
1695 gc_msdcs_ip_line = ""
1697 dns_dir = os.path.dirname(paths.dns)
1700 shutil.rmtree(dns_dir, True)
1704 os.mkdir(dns_dir, 0775)
1706 # we need to freeze the zone while we update the contents
1707 if targetdir is None:
1708 rndc = ' '.join(lp.get("rndc command"))
1709 os.system(rndc + " freeze " + lp.get("realm"))
1711 setup_file(setup_path("provision.zone"), paths.dns, {
1712 "HOSTNAME": hostname,
1713 "DNSDOMAIN": dnsdomain,
1715 "HOSTIP_BASE_LINE": hostip_base_line,
1716 "HOSTIP_HOST_LINE": hostip_host_line,
1717 "DOMAINGUID": domainguid,
1718 "DATESTRING": time.strftime("%Y%m%d%H"),
1719 "DEFAULTSITE": DEFAULTSITE,
1720 "NTDSGUID": ntdsguid,
1721 "HOSTIP6_BASE_LINE": hostip6_base_line,
1722 "HOSTIP6_HOST_LINE": hostip6_host_line,
1723 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1724 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1727 # note that we use no variable substitution on this file
1728 # the substitution is done at runtime by samba_dnsupdate
1729 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1731 # and the SPN update list
1732 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1734 if paths.bind_gid is not None:
1736 os.chown(dns_dir, -1, paths.bind_gid)
1737 os.chown(paths.dns, -1, paths.bind_gid)
1738 # chmod needed to cope with umask
1739 os.chmod(dns_dir, 0775)
1740 os.chmod(paths.dns, 0664)
1742 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1744 if targetdir is None:
1745 os.system(rndc + " unfreeze " + lp.get("realm"))
1748 def create_dns_update_list(lp, logger, paths, setup_path):
1749 """Write out a dns_update_list file"""
1750 # note that we use no variable substitution on this file
1751 # the substitution is done at runtime by samba_dnsupdate
1752 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1753 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1756 def create_named_conf(paths, setup_path, realm, dnsdomain,
1758 """Write out a file containing zone statements suitable for inclusion in a
1759 named.conf file (including GSS-TSIG configuration).
1761 :param paths: all paths
1762 :param setup_path: Setup path function.
1763 :param realm: Realm name
1764 :param dnsdomain: DNS Domain name
1765 :param private_dir: Path to private directory
1766 :param keytab_name: File name of DNS keytab file
1769 setup_file(setup_path("named.conf"), paths.namedconf, {
1770 "DNSDOMAIN": dnsdomain,
1772 "ZONE_FILE": paths.dns,
1773 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1774 "NAMED_CONF": paths.namedconf,
1775 "NAMED_CONF_UPDATE": paths.namedconf_update
1778 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1781 def create_named_txt(path, setup_path, realm, dnsdomain,
1782 private_dir, keytab_name):
1783 """Write out a file containing zone statements suitable for inclusion in a
1784 named.conf file (including GSS-TSIG configuration).
1786 :param path: Path of the new named.conf file.
1787 :param setup_path: Setup path function.
1788 :param realm: Realm name
1789 :param dnsdomain: DNS Domain name
1790 :param private_dir: Path to private directory
1791 :param keytab_name: File name of DNS keytab file
1794 setup_file(setup_path("named.txt"), path, {
1795 "DNSDOMAIN": dnsdomain,
1797 "DNS_KEYTAB": keytab_name,
1798 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1799 "PRIVATE_DIR": private_dir
1803 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1804 """Write out a file containing zone statements suitable for inclusion in a
1805 named.conf file (including GSS-TSIG configuration).
1807 :param path: Path of the new named.conf file.
1808 :param setup_path: Setup path function.
1809 :param dnsdomain: DNS Domain name
1810 :param hostname: Local hostname
1811 :param realm: Realm name
1813 setup_file(setup_path("krb5.conf"), path, {
1814 "DNSDOMAIN": dnsdomain,
1815 "HOSTNAME": hostname,
1820 class ProvisioningError(Exception):
1821 """A generic provision error."""
1823 def __init__(self, value):
1827 return "ProvisioningError: " + self.value
1830 class InvalidNetbiosName(Exception):
1831 """A specified name was not a valid NetBIOS name."""
1832 def __init__(self, name):
1833 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)