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_DC_FUNCTION_2008
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
.ntacls
import setntacl
, dsacl2fsacl
51 from samba
.ndr
import ndr_pack
,ndr_unpack
52 from samba
.schema
import Schema
53 from samba
.samdb
import SamDB
54 from ms_display_specifiers
import read_ms_ldif
55 from samba
.provisionbackend
import LDBBackend
, ExistingBackend
, FDSBackend
, OpenLDAPBackend
57 __docformat__
= "restructuredText"
60 """Find the setup directory used by provision."""
61 dirname
= os
.path
.dirname(__file__
)
62 if "/site-packages/" in dirname
:
63 prefix
= "/".join(dirname
[:dirname
.index("/site-packages/")].split("/")[:-2])
64 for suffix
in ["share/setup", "share/samba/setup", "setup"]:
65 ret
= os
.path
.join(prefix
, suffix
)
66 if os
.path
.isdir(ret
):
69 ret
= os
.path
.join(dirname
, "../../../setup")
70 if os
.path
.isdir(ret
):
72 raise Exception("Unable to find setup directory.")
74 # descriptors of the naming contexts
75 # hard coded at this point, but will probably be changed when
76 # we enable different fsmo roles
78 def get_config_descriptor(domain_sid
):
79 sddl
= "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
82 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
85 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
86 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
87 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
88 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
89 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
90 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
91 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
92 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
93 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
94 sec
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
97 def get_domain_descriptor(domain_sid
):
98 sddl
= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
99 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
101 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
103 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
105 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
107 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
108 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
109 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
120 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
121 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
122 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
125 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
126 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
127 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
128 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
129 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
134 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
135 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
136 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
139 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
141 "(A;;RPLCLORC;;;ED)" \
142 "(A;;RPLCLORC;;;AU)" \
143 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
144 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
146 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
147 sec
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
150 DEFAULTSITE
= "Default-First-Site-Name"
152 class ProvisionPaths(object):
155 self
.shareconf
= None
166 self
.dns_keytab
= None
169 self
.private_dir
= None
172 class ProvisionNames(object):
179 self
.ldapmanagerdn
= None
180 self
.dnsdomain
= None
182 self
.netbiosname
= None
189 class ProvisionResult(object):
198 def check_install(lp
, session_info
, credentials
):
199 """Check whether the current install seems ok.
201 :param lp: Loadparm context
202 :param session_info: Session information
203 :param credentials: Credentials
205 if lp
.get("realm") == "":
206 raise Exception("Realm empty")
207 samdb
= Ldb(lp
.get("sam database"), session_info
=session_info
,
208 credentials
=credentials
, lp
=lp
)
209 if len(samdb
.search("(cn=Administrator)")) != 1:
210 raise ProvisioningError("No administrator account found")
213 def findnss(nssfn
, names
):
214 """Find a user or group from a list of possibilities.
216 :param nssfn: NSS Function to try (should raise KeyError if not found)
217 :param names: Names to check.
218 :return: Value return by first names list.
225 raise KeyError("Unable to find user/group in %r" % names
)
228 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
229 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
232 def setup_add_ldif(ldb
, ldif_path
, subst_vars
=None,controls
=["relax:0"]):
233 """Setup a ldb in the private dir.
235 :param ldb: LDB file to import data into
236 :param ldif_path: Path of the LDIF file to load
237 :param subst_vars: Optional variables to subsitute in LDIF.
238 :param nocontrols: Optional list of controls, can be None for no controls
240 assert isinstance(ldif_path
, str)
241 data
= read_and_sub_file(ldif_path
, subst_vars
)
242 ldb
.add_ldif(data
, controls
)
245 def setup_modify_ldif(ldb
, ldif_path
, subst_vars
=None):
246 """Modify a ldb in the private dir.
248 :param ldb: LDB object.
249 :param ldif_path: LDIF file path.
250 :param subst_vars: Optional dictionary with substitution variables.
252 data
= read_and_sub_file(ldif_path
, subst_vars
)
253 ldb
.modify_ldif(data
)
256 def setup_ldb(ldb
, ldif_path
, subst_vars
):
257 """Import a LDIF a file into a LDB handle, optionally substituting variables.
259 :note: Either all LDIF data will be added or none (using transactions).
261 :param ldb: LDB file to import into.
262 :param ldif_path: Path to the LDIF file.
263 :param subst_vars: Dictionary with substitution variables.
265 assert ldb
is not None
266 ldb
.transaction_start()
268 setup_add_ldif(ldb
, ldif_path
, subst_vars
)
270 ldb
.transaction_cancel()
273 ldb
.transaction_commit()
276 def provision_paths_from_lp(lp
, dnsdomain
):
277 """Set the default paths for provisioning.
279 :param lp: Loadparm context.
280 :param dnsdomain: DNS Domain name
282 paths
= ProvisionPaths()
283 paths
.private_dir
= lp
.get("private dir")
285 # This is stored without path prefix for the "privateKeytab" attribute in
286 # "secrets_dns.ldif".
287 paths
.dns_keytab
= "dns.keytab"
289 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
290 paths
.samdb
= os
.path
.join(paths
.private_dir
, lp
.get("sam database") or "samdb.ldb")
291 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, lp
.get("idmap database") or "idmap.ldb")
292 paths
.secrets
= os
.path
.join(paths
.private_dir
, lp
.get("secrets database") or "secrets.ldb")
293 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
294 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
295 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
296 paths
.spn_update_list
= os
.path
.join(paths
.private_dir
, "spn_update_list")
297 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
298 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
299 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
300 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
301 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
302 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
303 paths
.phpldapadminconfig
= os
.path
.join(paths
.private_dir
,
304 "phpldapadmin-config.php")
305 paths
.hklm
= "hklm.ldb"
306 paths
.hkcr
= "hkcr.ldb"
307 paths
.hkcu
= "hkcu.ldb"
308 paths
.hku
= "hku.ldb"
309 paths
.hkpd
= "hkpd.ldb"
310 paths
.hkpt
= "hkpt.ldb"
311 paths
.sysvol
= lp
.get("path", "sysvol")
312 paths
.netlogon
= lp
.get("path", "netlogon")
313 paths
.smbconf
= lp
.configfile
317 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
318 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
319 schemadn
=None, serverdn
=None, sitename
=None):
320 """Guess configuration settings to use."""
323 hostname
= socket
.gethostname().split(".")[0]
325 netbiosname
= lp
.get("netbios name")
326 if netbiosname
is None:
327 netbiosname
= hostname
328 assert netbiosname
is not None
329 netbiosname
= netbiosname
.upper()
330 if not valid_netbios_name(netbiosname
):
331 raise InvalidNetbiosName(netbiosname
)
333 if dnsdomain
is None:
334 dnsdomain
= lp
.get("realm")
335 if dnsdomain
is None or dnsdomain
== "":
336 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp
.configfile
)
338 dnsdomain
= dnsdomain
.lower()
340 if serverrole
is None:
341 serverrole
= lp
.get("server role")
342 if serverrole
is None:
343 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp
.configfile
)
345 serverrole
= serverrole
.lower()
347 realm
= dnsdomain
.upper()
349 if lp
.get("realm") == "":
350 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp
.configfile
)
352 if lp
.get("realm").upper() != realm
:
353 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
))
355 if lp
.get("server role").lower() != serverrole
:
356 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
))
358 if serverrole
== "domain controller":
360 # This will, for better or worse, default to 'WORKGROUP'
361 domain
= lp
.get("workgroup")
362 domain
= domain
.upper()
364 if lp
.get("workgroup").upper() != domain
:
365 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
))
368 domaindn
= "DC=" + dnsdomain
.replace(".", ",DC=")
372 domaindn
= "DC=" + netbiosname
374 if not valid_netbios_name(domain
):
375 raise InvalidNetbiosName(domain
)
377 if hostname
.upper() == realm
:
378 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm
, hostname
))
379 if netbiosname
== realm
:
380 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm
, netbiosname
))
382 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm
, domain
))
388 configdn
= "CN=Configuration," + rootdn
390 schemadn
= "CN=Schema," + configdn
395 names
= ProvisionNames()
396 names
.rootdn
= rootdn
397 names
.domaindn
= domaindn
398 names
.configdn
= configdn
399 names
.schemadn
= schemadn
400 names
.ldapmanagerdn
= "CN=Manager," + rootdn
401 names
.dnsdomain
= dnsdomain
402 names
.domain
= domain
404 names
.netbiosname
= netbiosname
405 names
.hostname
= hostname
406 names
.sitename
= sitename
407 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname
, sitename
, configdn
)
412 def make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
, serverrole
,
413 targetdir
, sid_generator
,eadb
):
414 """Create a new smb.conf file based on a couple of basic settings.
416 assert smbconf
is not None
418 hostname
= socket
.gethostname().split(".")[0]
419 netbiosname
= hostname
.upper()
421 if serverrole
is None:
422 serverrole
= "standalone"
424 assert serverrole
in ("domain controller", "member server", "standalone")
425 if serverrole
== "domain controller":
427 elif serverrole
== "member server":
428 smbconfsuffix
= "member"
429 elif serverrole
== "standalone":
430 smbconfsuffix
= "standalone"
432 if sid_generator
is None:
433 sid_generator
= "internal"
435 assert domain
is not None
436 domain
= domain
.upper()
438 assert realm
is not None
439 realm
= realm
.upper()
441 default_lp
= param
.LoadParm()
442 #Load non-existant file
443 if os
.path
.exists(smbconf
):
444 default_lp
.load(smbconf
)
446 if targetdir
is not None:
447 privdir
= os
.path
.join(targetdir
, "private")
449 privdir
= default_lp
.get("private dir")
450 posixeadb_line
= "posix:eadb = " + os
.path
.abspath(os
.path
.join(privdir
,"eadb.tdb"))
454 if targetdir
is not None:
455 privatedir_line
= "private dir = " + os
.path
.abspath(os
.path
.join(targetdir
, "private"))
456 lockdir_line
= "lock dir = " + os
.path
.abspath(targetdir
)
458 default_lp
.set("lock dir", os
.path
.abspath(targetdir
))
463 if sid_generator
== "internal":
464 sid_generator_line
= ""
466 sid_generator_line
= "sid generator = " + sid_generator
468 sysvol
= os
.path
.join(default_lp
.get("lock dir"), "sysvol")
469 netlogon
= os
.path
.join(sysvol
, realm
.lower(), "scripts")
471 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix
),
473 "NETBIOS_NAME": netbiosname
,
476 "SERVERROLE": serverrole
,
477 "NETLOGONPATH": netlogon
,
478 "SYSVOLPATH": sysvol
,
479 "SIDGENERATOR_LINE": sid_generator_line
,
480 "PRIVATEDIR_LINE": privatedir_line
,
481 "LOCKDIR_LINE": lockdir_line
,
482 "POSIXEADB_LINE": posixeadb_line
486 def setup_name_mappings(samdb
, idmap
, sid
, domaindn
, root_uid
, nobody_uid
,
487 users_gid
, wheel_gid
):
488 """setup reasonable name mappings for sam names to unix names.
490 :param samdb: SamDB object.
491 :param idmap: IDmap db object.
492 :param sid: The domain sid.
493 :param domaindn: The domain DN.
494 :param root_uid: uid of the UNIX root user.
495 :param nobody_uid: uid of the UNIX nobody user.
496 :param users_gid: gid of the UNIX users group.
497 :param wheel_gid: gid of the UNIX wheel group."""
498 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
499 idmap
.setup_name_mapping("S-1-5-32-544", idmap
.TYPE_GID
, wheel_gid
)
501 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
502 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
505 def setup_samdb_partitions(samdb_path
, setup_path
, message
, lp
, session_info
,
506 provision_backend
, names
, schema
,
509 """Setup the partitions for the SAM database.
511 Alternatively, provision() may call this, and then populate the database.
513 :note: This will wipe the Sam Database!
515 :note: This function always removes the local SAM LDB file. The erase
516 parameter controls whether to erase the existing data, which
517 may not be stored locally but in LDAP.
520 assert session_info
is not None
522 # We use options=["modules:"] to stop the modules loading - we
523 # just want to wipe and re-initialise the database, not start it up
526 os
.unlink(samdb_path
)
530 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
531 lp
=lp
, options
=["modules:"])
533 ldap_backend_line
= "# No LDAP backend"
534 if provision_backend
.type is not "ldb":
535 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldapi_uri
537 samdb
.transaction_start()
539 message("Setting up sam.ldb partitions and settings")
540 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
541 "SCHEMADN": ldb
.Dn(schema
.ldb
, names
.schemadn
).get_casefold(),
542 "CONFIGDN": ldb
.Dn(schema
.ldb
, names
.configdn
).get_casefold(),
543 "DOMAINDN": ldb
.Dn(schema
.ldb
, names
.domaindn
).get_casefold(),
544 "LDAP_BACKEND_LINE": ldap_backend_line
,
548 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
549 "BACKEND_TYPE": provision_backend
.type,
550 "SERVER_ROLE": serverrole
553 message("Setting up sam.ldb rootDSE")
554 setup_samdb_rootdse(samdb
, setup_path
, names
)
556 samdb
.transaction_cancel()
559 samdb
.transaction_commit()
562 def secretsdb_self_join(secretsdb
, domain
,
563 netbiosname
, machinepass
, domainsid
=None,
564 realm
=None, dnsdomain
=None,
566 key_version_number
=1,
567 secure_channel_type
=SEC_CHAN_WKSTA
):
568 """Add domain join-specific bits to a secrets database.
570 :param secretsdb: Ldb Handle to the secrets database
571 :param machinepass: Machine password
573 attrs
=["whenChanged",
581 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
582 msg
["secureChannelType"] = str(secure_channel_type
)
583 msg
["flatname"] = [domain
]
584 msg
["objectClass"] = ["top", "primaryDomain"]
585 if realm
is not None:
586 if dnsdomain
is None:
587 dnsdomain
= realm
.lower()
588 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
590 msg
["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname
.lower(), dnsdomain
.lower(), realm
.upper())
591 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
592 msg
["privateKeytab"] = ["secrets.keytab"]
595 msg
["secret"] = [machinepass
]
596 msg
["samAccountName"] = ["%s$" % netbiosname
]
597 msg
["secureChannelType"] = [str(secure_channel_type
)]
598 if domainsid
is not None:
599 msg
["objectSid"] = [ndr_pack(domainsid
)]
601 res
= secretsdb
.search(base
="cn=Primary Domains",
603 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain
, realm
, str(domainsid
))),
604 scope
=ldb
.SCOPE_ONELEVEL
)
607 if del_msg
.dn
is not msg
.dn
:
608 secretsdb
.delete(del_msg
.dn
)
610 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
613 msg
["priorSecret"] = res
[0]["secret"]
614 msg
["priorWhenChanged"] = res
[0]["whenChanged"]
616 if res
["privateKeytab"] is not None:
617 msg
["privateKeytab"] = res
[0]["privateKeytab"]
619 if res
["krb5Keytab"] is not None:
620 msg
["krb5Keytab"] = res
[0]["krb5Keytab"]
623 el
.set_flags(ldb
.FLAG_MOD_REPLACE
)
624 secretsdb
.modify(msg
)
629 def secretsdb_setup_dns(secretsdb
, setup_path
, private_dir
,
631 dns_keytab_path
, dnspass
):
632 """Add DNS specific bits to a secrets database.
634 :param secretsdb: Ldb Handle to the secrets database
635 :param setup_path: Setup path function
636 :param machinepass: Machine password
639 os
.unlink(os
.path
.join(private_dir
, dns_keytab_path
))
643 setup_ldb(secretsdb
, setup_path("secrets_dns.ldif"), {
645 "DNSDOMAIN": dnsdomain
,
646 "DNS_KEYTAB": dns_keytab_path
,
647 "DNSPASS_B64": b64encode(dnspass
),
651 def setup_secretsdb(path
, setup_path
, session_info
, backend_credentials
, lp
):
652 """Setup the secrets database.
654 :param path: Path to the secrets database.
655 :param setup_path: Get the path to a setup file.
656 :param session_info: Session info.
657 :param credentials: Credentials
658 :param lp: Loadparm context
659 :return: LDB handle for the created secrets database
661 if os
.path
.exists(path
):
663 secrets_ldb
= Ldb(path
, session_info
=session_info
,
666 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
667 secrets_ldb
= Ldb(path
, session_info
=session_info
,
669 secrets_ldb
.transaction_start()
670 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
672 if backend_credentials
is not None and backend_credentials
.authentication_requested():
673 if backend_credentials
.get_bind_dn() is not None:
674 setup_add_ldif(secrets_ldb
, setup_path("secrets_simple_ldap.ldif"), {
675 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
676 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
679 setup_add_ldif(secrets_ldb
, setup_path("secrets_sasl_ldap.ldif"), {
680 "LDAPADMINUSER": backend_credentials
.get_username(),
681 "LDAPADMINREALM": backend_credentials
.get_realm(),
682 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
687 def setup_privileges(path
, setup_path
, session_info
, lp
):
688 """Setup the privileges database.
690 :param path: Path to the privileges database.
691 :param setup_path: Get the path to a setup file.
692 :param session_info: Session info.
693 :param credentials: Credentials
694 :param lp: Loadparm context
695 :return: LDB handle for the created secrets database
697 if os
.path
.exists(path
):
699 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
700 privilege_ldb
.erase()
701 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
704 def setup_registry(path
, setup_path
, session_info
, lp
):
705 """Setup the registry.
707 :param path: Path to the registry database
708 :param setup_path: Function that returns the path to a setup.
709 :param session_info: Session information
710 :param credentials: Credentials
711 :param lp: Loadparm context
713 reg
= registry
.Registry()
714 hive
= registry
.open_ldb(path
, session_info
=session_info
,
716 reg
.mount_hive(hive
, registry
.HKEY_LOCAL_MACHINE
)
717 provision_reg
= setup_path("provision.reg")
718 assert os
.path
.exists(provision_reg
)
719 reg
.diff_apply(provision_reg
)
722 def setup_idmapdb(path
, setup_path
, session_info
, lp
):
723 """Setup the idmap database.
725 :param path: path to the idmap database
726 :param setup_path: Function that returns a path to a setup file
727 :param session_info: Session information
728 :param credentials: Credentials
729 :param lp: Loadparm context
731 if os
.path
.exists(path
):
734 idmap_ldb
= IDmapDB(path
, session_info
=session_info
,
738 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
742 def setup_samdb_rootdse(samdb
, setup_path
, names
):
743 """Setup the SamDB rootdse.
745 :param samdb: Sam Database handle
746 :param setup_path: Obtain setup path
748 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
749 "SCHEMADN": names
.schemadn
,
750 "NETBIOSNAME": names
.netbiosname
,
751 "DNSDOMAIN": names
.dnsdomain
,
752 "REALM": names
.realm
,
753 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
754 "DOMAINDN": names
.domaindn
,
755 "ROOTDN": names
.rootdn
,
756 "CONFIGDN": names
.configdn
,
757 "SERVERDN": names
.serverdn
,
761 def setup_self_join(samdb
, names
,
762 machinepass
, dnspass
,
763 domainsid
, invocationid
, setup_path
,
764 policyguid
, policyguid_dc
, domainControllerFunctionality
,
766 """Join a host to its own domain."""
767 assert isinstance(invocationid
, str)
768 if ntdsguid
is not None:
769 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
772 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
773 "CONFIGDN": names
.configdn
,
774 "SCHEMADN": names
.schemadn
,
775 "DOMAINDN": names
.domaindn
,
776 "SERVERDN": names
.serverdn
,
777 "INVOCATIONID": invocationid
,
778 "NETBIOSNAME": names
.netbiosname
,
779 "DEFAULTSITE": names
.sitename
,
780 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
781 "MACHINEPASS_B64": b64encode(machinepass
),
782 "REALM": names
.realm
,
783 "DOMAIN": names
.domain
,
784 "DOMAINSID": str(domainsid
),
785 "DNSDOMAIN": names
.dnsdomain
,
786 "SAMBA_VERSION_STRING": version
,
787 "NTDSGUID": ntdsguid_line
,
788 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality
)})
790 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
791 "POLICYGUID": policyguid
,
792 "POLICYGUID_DC": policyguid_dc
,
793 "DNSDOMAIN": names
.dnsdomain
,
794 "DOMAINSID": str(domainsid
),
795 "DOMAINDN": names
.domaindn
})
797 # add the NTDSGUID based SPNs
798 ntds_dn
= "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names
.hostname
, names
.domaindn
)
799 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
, attribute
="objectGUID",
800 expression
="", scope
=ldb
.SCOPE_BASE
)
801 assert isinstance(names
.ntdsguid
, str)
803 # Setup fSMORoleOwner entries to point at the newly created DC entry
804 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
805 "DOMAIN": names
.domain
,
806 "DNSDOMAIN": names
.dnsdomain
,
807 "DOMAINDN": names
.domaindn
,
808 "CONFIGDN": names
.configdn
,
809 "SCHEMADN": names
.schemadn
,
810 "DEFAULTSITE": names
.sitename
,
811 "SERVERDN": names
.serverdn
,
812 "NETBIOSNAME": names
.netbiosname
,
813 "NTDSGUID": names
.ntdsguid
,
814 "DNSPASS_B64": b64encode(dnspass
),
817 def getpolicypath(sysvolpath
, dnsdomain
, guid
):
820 policy_path
= os
.path
.join(sysvolpath
, dnsdomain
, "Policies", guid
)
823 def create_gpo_struct(policy_path
):
824 os
.makedirs(policy_path
, 0755)
825 open(os
.path
.join(policy_path
, "GPT.INI"), 'w').write(
826 "[General]\r\nVersion=65543")
827 os
.makedirs(os
.path
.join(policy_path
, "MACHINE"), 0755)
828 os
.makedirs(os
.path
.join(policy_path
, "USER"), 0755)
831 def setup_gpo(sysvolpath
, dnsdomain
, policyguid
, policyguid_dc
):
832 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid
)
833 create_gpo_struct(policy_path
)
835 policy_path
= getpolicypath(sysvolpath
,dnsdomain
,policyguid_dc
)
836 create_gpo_struct(policy_path
)
839 def setup_samdb(path
, setup_path
, session_info
, provision_backend
, lp
,
841 domainsid
, domainguid
, policyguid
, policyguid_dc
,
842 fill
, adminpass
, krbtgtpass
,
843 machinepass
, invocationid
, dnspass
, ntdsguid
,
844 serverrole
, am_rodc
, dom_for_fun_level
=None,
846 """Setup a complete SAM Database.
848 :note: This will wipe the main SAM database file!
851 # ATTENTION: Do NOT change these default values without discussion with the
852 # team and/or release manager. They have a big impact on the whole program!
853 domainControllerFunctionality
= DS_DC_FUNCTION_2008
855 if dom_for_fun_level
is None:
856 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
857 if dom_for_fun_level
< DS_DOMAIN_FUNCTION_2003
:
858 message("You want to run SAMBA 4 on a domain and forest function level"
859 " lower than Windows 2003 (Native). This is not recommended")
861 if dom_for_fun_level
> domainControllerFunctionality
:
862 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). This won't work!")
864 domainFunctionality
= dom_for_fun_level
865 forestFunctionality
= dom_for_fun_level
867 # Also wipes the database
868 setup_samdb_partitions(path
, setup_path
, message
=message
, lp
=lp
,
869 provision_backend
=provision_backend
, session_info
=session_info
,
870 names
=names
, serverrole
=serverrole
, schema
=schema
)
873 schema
= Schema(setup_path
, domainsid
, schemadn
=names
.schemadn
, serverdn
=names
.serverdn
,
876 # Load the database, but don's load the global schema and don't connect quite yet
877 samdb
= SamDB(session_info
=session_info
, url
=None, auto_connect
=False,
878 credentials
=provision_backend
.credentials
, lp
=lp
, global_schema
=False,
881 message("Pre-loading the Samba 4 and AD schema")
883 # Load the schema from the one we computed earlier
884 samdb
.set_schema_from_ldb(schema
.ldb
)
886 # And now we can connect to the DB - the schema won't be loaded from the DB
892 samdb
.transaction_start()
894 # Set the domain functionality levels onto the database.
895 # Various module (the password_hash module in particular) need
896 # to know what level of AD we are emulating.
898 # These will be fixed into the database via the database
899 # modifictions below, but we need them set from the start.
900 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
901 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
902 samdb
.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality
)
904 samdb
.set_domain_sid(str(domainsid
))
905 samdb
.set_invocation_id(invocationid
)
906 samdb
.set_ntds_settings_dn("CN=NTDS Settings,%s" % names
.serverdn
)
908 message("Adding DomainDN: %s" % names
.domaindn
)
910 #impersonate domain admin
911 admin_session_info
= admin_session(lp
, str(domainsid
))
912 samdb
.set_session_info(admin_session_info
)
913 if domainguid
is not None:
914 domainguid_line
= "objectGUID: %s\n-" % domainguid
918 descr
= b64encode(get_domain_descriptor(domainsid
))
919 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
920 "DOMAINDN": names
.domaindn
,
921 "DOMAINGUID": domainguid_line
,
926 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
927 "CREATTIME": str(int(time
.time() * 1e7
)), # seconds -> ticks
928 "DOMAINSID": str(domainsid
),
929 "SCHEMADN": names
.schemadn
,
930 "NETBIOSNAME": names
.netbiosname
,
931 "DEFAULTSITE": names
.sitename
,
932 "CONFIGDN": names
.configdn
,
933 "SERVERDN": names
.serverdn
,
934 "POLICYGUID": policyguid
,
935 "DOMAINDN": names
.domaindn
,
936 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
937 "SAMBA_VERSION_STRING": version
940 message("Adding configuration container")
941 descr
= b64encode(get_config_descriptor(domainsid
))
942 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
943 "CONFIGDN": names
.configdn
,
947 # The LDIF here was created when the Schema object was constructed
948 message("Setting up sam.ldb schema")
949 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
950 samdb
.modify_ldif(schema
.schema_dn_modify
)
951 samdb
.write_prefixes_from_schema()
952 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
953 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
954 {"SCHEMADN": names
.schemadn
})
956 message("Reopening sam.ldb with new schema")
958 samdb
.transaction_cancel()
961 samdb
.transaction_commit()
963 samdb
= SamDB(session_info
=admin_session_info
,
964 credentials
=provision_backend
.credentials
, lp
=lp
,
965 global_schema
=False, am_rodc
=am_rodc
)
967 samdb
.transaction_start()
969 samdb
.invocation_id
= invocationid
971 message("Setting up sam.ldb configuration data")
972 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
973 "CONFIGDN": names
.configdn
,
974 "NETBIOSNAME": names
.netbiosname
,
975 "DEFAULTSITE": names
.sitename
,
976 "DNSDOMAIN": names
.dnsdomain
,
977 "DOMAIN": names
.domain
,
978 "SCHEMADN": names
.schemadn
,
979 "DOMAINDN": names
.domaindn
,
980 "SERVERDN": names
.serverdn
,
981 "FOREST_FUNCTIONALITY": str(forestFunctionality
),
982 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
)
985 message("Setting up display specifiers")
986 display_specifiers_ldif
= read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
987 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
, {"CONFIGDN": names
.configdn
})
988 check_all_substituted(display_specifiers_ldif
)
989 samdb
.add_ldif(display_specifiers_ldif
)
991 message("Adding users container")
992 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
993 "DOMAINDN": names
.domaindn
})
994 message("Modifying users container")
995 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
996 "DOMAINDN": names
.domaindn
})
997 message("Adding computers container")
998 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
999 "DOMAINDN": names
.domaindn
})
1000 message("Modifying computers container")
1001 setup_modify_ldif(samdb
, setup_path("provision_computers_modify.ldif"), {
1002 "DOMAINDN": names
.domaindn
})
1003 message("Setting up sam.ldb data")
1004 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
1005 "CREATTIME": str(int(time
.time() * 1e7
)), # seconds -> ticks
1006 "DOMAINDN": names
.domaindn
,
1007 "NETBIOSNAME": names
.netbiosname
,
1008 "DEFAULTSITE": names
.sitename
,
1009 "CONFIGDN": names
.configdn
,
1010 "SERVERDN": names
.serverdn
,
1011 "POLICYGUID_DC": policyguid_dc
1014 setup_modify_ldif(samdb
, setup_path("provision_basedn_references.ldif"), {
1015 "DOMAINDN": names
.domaindn
})
1017 setup_modify_ldif(samdb
, setup_path("provision_configuration_references.ldif"), {
1018 "CONFIGDN": names
.configdn
,
1019 "SCHEMADN": names
.schemadn
})
1020 if fill
== FILL_FULL
:
1021 message("Setting up sam.ldb users and groups")
1022 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1023 "DOMAINDN": names
.domaindn
,
1024 "DOMAINSID": str(domainsid
),
1025 "CONFIGDN": names
.configdn
,
1026 "ADMINPASS_B64": b64encode(adminpass
),
1027 "KRBTGTPASS_B64": b64encode(krbtgtpass
),
1030 message("Setting up self join")
1031 setup_self_join(samdb
, names
=names
, invocationid
=invocationid
,
1033 machinepass
=machinepass
,
1034 domainsid
=domainsid
, policyguid
=policyguid
,
1035 policyguid_dc
=policyguid_dc
,
1036 setup_path
=setup_path
,
1037 domainControllerFunctionality
=domainControllerFunctionality
,
1040 ntds_dn
= "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names
.hostname
, names
.domaindn
)
1041 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1042 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1043 assert isinstance(names
.ntdsguid
, str)
1045 samdb
.transaction_cancel()
1048 samdb
.transaction_commit()
1053 FILL_NT4SYNC
= "NT4SYNC"
1055 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1056 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)"
1058 def set_dir_acl(path
, acl
, lp
, domsid
):
1059 setntacl(lp
, path
, acl
, domsid
)
1060 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1062 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
)
1064 setntacl(lp
, os
.path
.join(root
, name
), acl
, domsid
)
1067 def set_gpo_acl(sysvol
, dnsdomain
, domainsid
, domaindn
, samdb
, lp
):
1069 policy_path
= os
.path
.join(sysvol
, dnsdomain
, "Policies")
1070 set_dir_acl(policy_path
,dsacl2fsacl(POLICIES_ACL
, str(domainsid
)),
1072 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(domaindn),
1073 attrs
=["cn","nTSecurityDescriptor"],
1074 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1076 acl
= ndr_unpack(security
.descriptor
,
1077 str(policy
["nTSecurityDescriptor"])).as_sddl()
1078 policy_path
= getpolicypath(sysvol
,dnsdomain
,str(policy
["cn"]))
1079 set_dir_acl(policy_path
, dsacl2fsacl(acl
, str(domainsid
)), lp
,
1082 def setsysvolacl(samdb
, netlogon
, sysvol
, gid
, domainsid
, dnsdomain
, domaindn
,
1085 os
.chown(sysvol
,-1,gid
)
1091 setntacl(lp
,sysvol
,SYSVOL_ACL
,str(domainsid
))
1092 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1095 os
.chown(os
.path
.join(root
, name
),-1,gid
)
1096 setntacl(lp
,os
.path
.join(root
, name
),SYSVOL_ACL
,str(domainsid
))
1099 os
.chown(os
.path
.join(root
, name
),-1,gid
)
1100 setntacl(lp
,os
.path
.join(root
, name
),SYSVOL_ACL
,str(domainsid
))
1101 set_gpo_acl(sysvol
,dnsdomain
,domainsid
,domaindn
,samdb
,lp
)
1104 def provision(setup_dir
, message
, session_info
,
1105 credentials
, smbconf
=None, targetdir
=None, samdb_fill
=FILL_FULL
,
1107 rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
1109 domain
=None, hostname
=None, hostip
=None, hostip6
=None,
1110 domainsid
=None, adminpass
=None, ldapadminpass
=None,
1111 krbtgtpass
=None, domainguid
=None,
1112 policyguid
=None, policyguid_dc
=None, invocationid
=None,
1113 machinepass
=None, ntdsguid
=None,
1114 dnspass
=None, root
=None, nobody
=None, users
=None,
1115 wheel
=None, backup
=None, aci
=None, serverrole
=None,
1116 dom_for_fun_level
=None,
1117 ldap_backend_extra_port
=None, backend_type
=None,
1119 ol_mmr_urls
=None, ol_olc
=None,
1120 setup_ds_path
=None, slapd_path
=None, nosync
=False,
1121 ldap_dryrun_mode
=False,useeadb
=False, am_rodc
=False):
1124 :note: caution, this wipes all existing data!
1127 def setup_path(file):
1128 return os
.path
.join(setup_dir
, file)
1130 if domainsid
is None:
1131 domainsid
= security
.random_sid()
1133 domainsid
= security
.dom_sid(domainsid
)
1135 # create/adapt the group policy GUIDs
1136 if policyguid
is None:
1137 policyguid
= str(uuid
.uuid4())
1138 policyguid
= policyguid
.upper()
1139 if policyguid_dc
is None:
1140 policyguid_dc
= str(uuid
.uuid4())
1141 policyguid_dc
= policyguid_dc
.upper()
1143 if adminpass
is None:
1144 adminpass
= samba
.generate_random_password(12, 32)
1145 if krbtgtpass
is None:
1146 krbtgtpass
= samba
.generate_random_password(128, 255)
1147 if machinepass
is None:
1148 machinepass
= samba
.generate_random_password(128, 255)
1150 dnspass
= samba
.generate_random_password(128, 255)
1151 if ldapadminpass
is None:
1152 #Make a new, random password between Samba and it's LDAP server
1153 ldapadminpass
=samba
.generate_random_password(128, 255)
1155 if backend_type
is None:
1156 backend_type
= "ldb"
1158 sid_generator
= "internal"
1159 if backend_type
== "fedora-ds":
1160 sid_generator
= "backend"
1162 root_uid
= findnss_uid([root
or "root"])
1163 nobody_uid
= findnss_uid([nobody
or "nobody"])
1164 users_gid
= findnss_gid([users
or "users", 'users', 'other', 'staff'])
1166 wheel_gid
= findnss_gid(["wheel", "adm"])
1168 wheel_gid
= findnss_gid([wheel
])
1170 bind_gid
= findnss_gid(["bind", "named"])
1174 if targetdir
is not None:
1175 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1176 elif smbconf
is None:
1177 smbconf
= param
.default_path()
1178 if not os
.path
.exists(os
.path
.dirname(smbconf
)):
1179 os
.makedirs(os
.path
.dirname(smbconf
))
1181 # only install a new smb.conf if there isn't one there already
1182 if os
.path
.exists(smbconf
):
1183 # if Samba Team members can't figure out the weird errors
1184 # loading an empty smb.conf gives, then we need to be smarter.
1185 # Pretend it just didn't exist --abartlet
1186 data
= open(smbconf
, 'r').read()
1187 data
= data
.lstrip()
1188 if data
is None or data
== "":
1189 make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
,
1190 serverrole
, targetdir
, sid_generator
, useeadb
)
1192 make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
, serverrole
,
1193 targetdir
, sid_generator
, useeadb
)
1195 lp
= param
.LoadParm()
1198 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1199 dnsdomain
=realm
, serverrole
=serverrole
,
1200 domaindn
=domaindn
, configdn
=configdn
, schemadn
=schemadn
,
1201 serverdn
=serverdn
, sitename
=sitename
)
1203 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1205 paths
.bind_gid
= bind_gid
1208 hostips
= samba
.interface_ips(lp
, False)
1209 if len(hostips
) == 0:
1210 message("No external IPv4 address has been found: I use the loopback.")
1211 hostip
= '127.0.0.1'
1214 if len(hostips
) > 1:
1215 message("More than one IPv4 address found: I use " + hostip
+ ".")
1219 for ip
in socket
.getaddrinfo(names
.hostname
, None, socket
.AF_INET6
, socket
.AI_CANONNAME
, socket
.IPPROTO_IP
):
1222 if hostip6
== '::1' and ip
[-1][0] != '::1':
1224 except socket
.gaierror
, (socket
.EAI_NODATA
, msg
):
1227 if serverrole
is None:
1228 serverrole
= lp
.get("server role")
1230 assert serverrole
in ("domain controller", "member server", "standalone")
1231 if invocationid
is None:
1232 invocationid
= str(uuid
.uuid4())
1234 if not os
.path
.exists(paths
.private_dir
):
1235 os
.mkdir(paths
.private_dir
)
1236 if not os
.path
.exists(os
.path
.join(paths
.private_dir
,"tls")):
1237 os
.mkdir(os
.path
.join(paths
.private_dir
,"tls"))
1239 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1241 schema
= Schema(setup_path
, domainsid
, invocationid
=invocationid
, schemadn
=names
.schemadn
,
1242 serverdn
=names
.serverdn
, am_rodc
=am_rodc
)
1244 if backend_type
== "ldb":
1245 provision_backend
= LDBBackend(backend_type
,
1246 paths
=paths
, setup_path
=setup_path
,
1247 lp
=lp
, credentials
=credentials
,
1250 elif backend_type
== "existing":
1251 provision_backend
= ExistingBackend(backend_type
,
1252 paths
=paths
, setup_path
=setup_path
,
1253 lp
=lp
, credentials
=credentials
,
1256 ldapi_url
=ldapi_url
)
1257 elif backend_type
== "fedora-ds":
1258 provision_backend
= FDSBackend(backend_type
,
1259 paths
=paths
, setup_path
=setup_path
,
1260 lp
=lp
, credentials
=credentials
,
1263 domainsid
=domainsid
,
1266 ldapadminpass
=ldapadminpass
,
1267 slapd_path
=slapd_path
,
1268 ldap_backend_extra_port
=ldap_backend_extra_port
,
1269 ldap_dryrun_mode
=ldap_dryrun_mode
,
1271 setup_ds_path
=setup_ds_path
)
1272 elif backend_type
== "openldap":
1273 provision_backend
= OpenLDAPBackend(backend_type
,
1274 paths
=paths
, setup_path
=setup_path
,
1275 lp
=lp
, credentials
=credentials
,
1278 domainsid
=domainsid
,
1281 ldapadminpass
=ldapadminpass
,
1282 slapd_path
=slapd_path
,
1283 ldap_backend_extra_port
=ldap_backend_extra_port
,
1284 ldap_dryrun_mode
=ldap_dryrun_mode
,
1285 ol_mmr_urls
=ol_mmr_urls
,
1288 raise ValueError("Unknown LDAP backend type selected")
1290 provision_backend
.init()
1291 provision_backend
.start()
1293 # only install a new shares config db if there is none
1294 if not os
.path
.exists(paths
.shareconf
):
1295 message("Setting up share.ldb")
1296 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
,
1298 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1301 message("Setting up secrets.ldb")
1302 secrets_ldb
= setup_secretsdb(paths
.secrets
, setup_path
,
1303 session_info
=session_info
,
1304 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
1306 message("Setting up the registry")
1307 setup_registry(paths
.hklm
, setup_path
, session_info
,
1310 message("Setting up the privileges database")
1311 setup_privileges(paths
.privilege
, setup_path
, session_info
, lp
=lp
)
1313 message("Setting up idmap db")
1314 idmap
= setup_idmapdb(paths
.idmapdb
, setup_path
, session_info
=session_info
,
1317 message("Setting up SAM db")
1318 samdb
= setup_samdb(paths
.samdb
, setup_path
, session_info
,
1319 provision_backend
, lp
, names
,
1321 domainsid
=domainsid
,
1322 schema
=schema
, domainguid
=domainguid
,
1323 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1325 adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1326 invocationid
=invocationid
,
1327 machinepass
=machinepass
, dnspass
=dnspass
,
1328 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1329 dom_for_fun_level
=dom_for_fun_level
, am_rodc
=am_rodc
)
1331 if serverrole
== "domain controller":
1332 if paths
.netlogon
is None:
1333 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1334 message("Please either remove %s or see the template at %s" %
1335 (paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1336 assert paths
.netlogon
is not None
1338 if paths
.sysvol
is None:
1339 message("Existing smb.conf does not have a [sysvol] share, but you"
1340 " are configuring a DC.")
1341 message("Please either remove %s or see the template at %s" %
1342 (paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1343 assert paths
.sysvol
is not None
1345 if not os
.path
.isdir(paths
.netlogon
):
1346 os
.makedirs(paths
.netlogon
, 0755)
1348 if samdb_fill
== FILL_FULL
:
1349 setup_name_mappings(samdb
, idmap
, str(domainsid
), names
.domaindn
,
1350 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
1351 users_gid
=users_gid
, wheel_gid
=wheel_gid
)
1353 if serverrole
== "domain controller":
1354 # Set up group policies (domain policy and domain controller policy)
1355 setup_gpo(paths
.sysvol
, names
.dnsdomain
, policyguid
, policyguid_dc
)
1356 setsysvolacl(samdb
, paths
.netlogon
, paths
.sysvol
, wheel_gid
,
1357 domainsid
, names
.dnsdomain
, names
.domaindn
, lp
)
1359 message("Setting up sam.ldb rootDSE marking as synchronized")
1360 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"))
1362 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1364 dnsdomain
=names
.dnsdomain
,
1365 netbiosname
=names
.netbiosname
,
1366 domainsid
=domainsid
,
1367 machinepass
=machinepass
,
1368 secure_channel_type
=SEC_CHAN_BDC
)
1370 if serverrole
== "domain controller":
1371 secretsdb_setup_dns(secrets_ldb
, setup_path
,
1373 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1374 dns_keytab_path
=paths
.dns_keytab
,
1377 domainguid
= samdb
.searchone(basedn
=domaindn
, attribute
="objectGUID")
1378 assert isinstance(domainguid
, str)
1380 # Only make a zone file on the first DC, it should be replicated
1381 # with DNS replication
1382 create_zone_file(lp
, message
, paths
, targetdir
, setup_path
,
1383 dnsdomain
=names
.dnsdomain
, hostip
=hostip
, hostip6
=hostip6
,
1384 hostname
=names
.hostname
, realm
=names
.realm
,
1385 domainguid
=domainguid
, ntdsguid
=names
.ntdsguid
)
1387 create_named_conf(paths
, setup_path
, realm
=names
.realm
,
1388 dnsdomain
=names
.dnsdomain
, private_dir
=paths
.private_dir
)
1390 create_named_txt(paths
.namedtxt
, setup_path
, realm
=names
.realm
,
1391 dnsdomain
=names
.dnsdomain
, private_dir
=paths
.private_dir
,
1392 keytab_name
=paths
.dns_keytab
)
1393 message("See %s for an example configuration include file for BIND" % paths
.namedconf
)
1394 message("and %s for further documentation required for secure DNS "
1395 "updates" % paths
.namedtxt
)
1397 create_krb5_conf(paths
.krb5conf
, setup_path
,
1398 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
1400 message("A Kerberos configuration suitable for Samba 4 has been "
1401 "generated at %s" % paths
.krb5conf
)
1403 if serverrole
== "domain controller":
1404 create_dns_update_list(lp
, message
, paths
, setup_path
)
1406 provision_backend
.post_setup()
1407 provision_backend
.shutdown()
1409 create_phpldapadmin_config(paths
.phpldapadminconfig
, setup_path
,
1412 #Now commit the secrets.ldb to disk
1413 secrets_ldb
.transaction_commit()
1415 # the commit creates the dns.keytab, now chown it
1416 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
1417 if (os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None):
1419 os
.chmod(dns_keytab_path
, 0640)
1420 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
1422 message("Failed to chown %s to bind gid %u" % (dns_keytab_path
,
1426 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths
.phpldapadminconfig
)
1428 message("Once the above files are installed, your Samba4 server will be ready to use")
1429 message("Server Role: %s" % serverrole
)
1430 message("Hostname: %s" % names
.hostname
)
1431 message("NetBIOS Domain: %s" % names
.domain
)
1432 message("DNS Domain: %s" % names
.dnsdomain
)
1433 message("DOMAIN SID: %s" % str(domainsid
))
1434 if samdb_fill
== FILL_FULL
:
1435 message("Admin password: %s" % adminpass
)
1436 if provision_backend
.type is not "ldb":
1437 if provision_backend
.credentials
.get_bind_dn() is not None:
1438 message("LDAP Backend Admin DN: %s" % provision_backend
.credentials
.get_bind_dn())
1440 message("LDAP Admin User: %s" % provision_backend
.credentials
.get_username())
1442 message("LDAP Admin Password: %s" % provision_backend
.credentials
.get_password())
1444 if provision_backend
.slapd_command_escaped
is not None:
1445 # now display slapd_command_file.txt to show how slapd must be started next time
1446 message("Use later the following commandline to start slapd, then Samba:")
1447 message(provision_backend
.slapd_command_escaped
)
1448 message("This slapd-Commandline is also stored under: " + provision_backend
.ldapdir
+ "/ldap_backend_startup.sh")
1451 result
= ProvisionResult()
1452 result
.domaindn
= domaindn
1453 result
.paths
= paths
1455 result
.samdb
= samdb
1459 def provision_become_dc(setup_dir
=None,
1460 smbconf
=None, targetdir
=None, realm
=None,
1461 rootdn
=None, domaindn
=None, schemadn
=None,
1462 configdn
=None, serverdn
=None,
1463 domain
=None, hostname
=None, domainsid
=None,
1464 adminpass
=None, krbtgtpass
=None, domainguid
=None,
1465 policyguid
=None, policyguid_dc
=None, invocationid
=None,
1467 dnspass
=None, root
=None, nobody
=None, users
=None,
1468 wheel
=None, backup
=None, serverrole
=None,
1469 ldap_backend
=None, ldap_backend_type
=None,
1470 sitename
=None, debuglevel
=1):
1473 """print a message if quiet is not set."""
1476 samba
.set_debug_level(debuglevel
)
1478 return provision(setup_dir
, message
, system_session(), None,
1479 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
1480 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
1481 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
1482 hostname
=hostname
, hostip
="127.0.0.1", domainsid
=domainsid
,
1483 machinepass
=machinepass
, serverrole
="domain controller",
1487 def create_phpldapadmin_config(path
, setup_path
, ldapi_uri
):
1488 """Create a PHP LDAP admin configuration file.
1490 :param path: Path to write the configuration to.
1491 :param setup_path: Function to generate setup paths.
1493 setup_file(setup_path("phpldapadmin-config.php"), path
,
1494 {"S4_LDAPI_URI": ldapi_uri
})
1497 def create_zone_file(lp
, message
, paths
, targetdir
, setup_path
, dnsdomain
,
1498 hostip
, hostip6
, hostname
, realm
, domainguid
,
1500 """Write out a DNS zone file, from the info in the current database.
1502 :param paths: paths object
1503 :param setup_path: Setup path function.
1504 :param dnsdomain: DNS Domain name
1505 :param domaindn: DN of the Domain
1506 :param hostip: Local IPv4 IP
1507 :param hostip6: Local IPv6 IP
1508 :param hostname: Local hostname
1509 :param realm: Realm name
1510 :param domainguid: GUID of the domain.
1511 :param ntdsguid: GUID of the hosts nTDSDSA record.
1513 assert isinstance(domainguid
, str)
1515 if hostip6
is not None:
1516 hostip6_base_line
= " IN AAAA " + hostip6
1517 hostip6_host_line
= hostname
+ " IN AAAA " + hostip6
1519 hostip6_base_line
= ""
1520 hostip6_host_line
= ""
1522 if hostip
is not None:
1523 hostip_base_line
= " IN A " + hostip
1524 hostip_host_line
= hostname
+ " IN A " + hostip
1526 hostip_base_line
= ""
1527 hostip_host_line
= ""
1529 dns_dir
= os
.path
.dirname(paths
.dns
)
1532 shutil
.rmtree(dns_dir
, True)
1536 os
.mkdir(dns_dir
, 0775)
1538 # we need to freeze the zone while we update the contents
1539 if targetdir
is None:
1540 rndc
= ' '.join(lp
.get("rndc command"))
1541 os
.system(rndc
+ " freeze " + lp
.get("realm"))
1543 setup_file(setup_path("provision.zone"), paths
.dns
, {
1544 "HOSTNAME": hostname
,
1545 "DNSDOMAIN": dnsdomain
,
1547 "HOSTIP_BASE_LINE": hostip_base_line
,
1548 "HOSTIP_HOST_LINE": hostip_host_line
,
1549 "DOMAINGUID": domainguid
,
1550 "DATESTRING": time
.strftime("%Y%m%d%H"),
1551 "DEFAULTSITE": DEFAULTSITE
,
1552 "NTDSGUID": ntdsguid
,
1553 "HOSTIP6_BASE_LINE": hostip6_base_line
,
1554 "HOSTIP6_HOST_LINE": hostip6_host_line
,
1557 # note that we use no variable substitution on this file
1558 # the substitution is done at runtime by samba_dnsupdate
1559 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
1561 # and the SPN update list
1562 setup_file(setup_path("spn_update_list"), paths
.spn_update_list
, None)
1564 if paths
.bind_gid
is not None:
1566 os
.chown(dns_dir
, -1, paths
.bind_gid
)
1567 os
.chown(paths
.dns
, -1, paths
.bind_gid
)
1568 # chmod needed to cope with umask
1569 os
.chmod(dns_dir
, 0775)
1570 os
.chmod(paths
.dns
, 0664)
1572 message("Failed to chown %s to bind gid %u" % (dns_dir
, paths
.bind_gid
))
1574 if targetdir
is None:
1575 os
.system(rndc
+ " unfreeze " + lp
.get("realm"))
1578 def create_dns_update_list(lp
, message
, paths
, setup_path
):
1579 """Write out a dns_update_list file"""
1580 # note that we use no variable substitution on this file
1581 # the substitution is done at runtime by samba_dnsupdate
1582 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
1583 setup_file(setup_path("spn_update_list"), paths
.spn_update_list
, None)
1585 def create_named_conf(paths
, setup_path
, realm
, dnsdomain
,
1587 """Write out a file containing zone statements suitable for inclusion in a
1588 named.conf file (including GSS-TSIG configuration).
1590 :param paths: all paths
1591 :param setup_path: Setup path function.
1592 :param realm: Realm name
1593 :param dnsdomain: DNS Domain name
1594 :param private_dir: Path to private directory
1595 :param keytab_name: File name of DNS keytab file
1598 setup_file(setup_path("named.conf"), paths
.namedconf
, {
1599 "DNSDOMAIN": dnsdomain
,
1601 "ZONE_FILE": paths
.dns
,
1602 "REALM_WC": "*." + ".".join(realm
.split(".")[1:]),
1603 "NAMED_CONF": paths
.namedconf
,
1604 "NAMED_CONF_UPDATE": paths
.namedconf_update
1607 setup_file(setup_path("named.conf.update"), paths
.namedconf_update
)
1609 def create_named_txt(path
, setup_path
, realm
, dnsdomain
,
1610 private_dir
, keytab_name
):
1611 """Write out a file containing zone statements suitable for inclusion in a
1612 named.conf file (including GSS-TSIG configuration).
1614 :param path: Path of the new named.conf file.
1615 :param setup_path: Setup path function.
1616 :param realm: Realm name
1617 :param dnsdomain: DNS Domain name
1618 :param private_dir: Path to private directory
1619 :param keytab_name: File name of DNS keytab file
1622 setup_file(setup_path("named.txt"), path
, {
1623 "DNSDOMAIN": dnsdomain
,
1625 "DNS_KEYTAB": keytab_name
,
1626 "DNS_KEYTAB_ABS": os
.path
.join(private_dir
, keytab_name
),
1627 "PRIVATE_DIR": private_dir
1630 def create_krb5_conf(path
, setup_path
, dnsdomain
, hostname
, realm
):
1631 """Write out a file containing zone statements suitable for inclusion in a
1632 named.conf file (including GSS-TSIG configuration).
1634 :param path: Path of the new named.conf file.
1635 :param setup_path: Setup path function.
1636 :param dnsdomain: DNS Domain name
1637 :param hostname: Local hostname
1638 :param realm: Realm name
1640 setup_file(setup_path("krb5.conf"), path
, {
1641 "DNSDOMAIN": dnsdomain
,
1642 "HOSTNAME": hostname
,
1647 class ProvisioningError(Exception):
1648 """A generic provision error."""
1650 def __init__(self
, value
):
1654 return "ProvisioningError: " + self
.value
1657 class InvalidNetbiosName(Exception):
1658 """A specified name was not a valid NetBIOS name."""
1659 def __init__(self
, name
):
1660 super(InvalidNetbiosName
, self
).__init
__("The name '%r' is not a valid NetBIOS name" % name
)