2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
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
43 from samba
import glue
, version
, Ldb
, substitute_var
, valid_netbios_name
44 from samba
import check_all_substituted
, read_and_sub_file
, setup_file
45 from samba
import DS_DOMAIN_FUNCTION_2003
, DS_DC_FUNCTION_2008
46 from samba
.dcerpc
import security
47 from samba
.dcerpc
.misc
import SEC_CHAN_BDC
, SEC_CHAN_WKSTA
48 from samba
.idmap
import IDmapDB
49 from samba
.ntacls
import setntacl
, dsacl2fsacl
50 from samba
.ndr
import ndr_pack
,ndr_unpack
51 from samba
.schema
import Schema
52 from ms_display_specifiers
import read_ms_ldif
53 from samba
.provisionbackend
import LDBBackend
, ExistingBackend
, FDSBackend
, OpenLDAPBackend
54 from provisionexceptions
import ProvisioningError
, InvalidNetbiosName
56 __docformat__
= "restructuredText"
59 """Find the setup directory used by provision."""
60 dirname
= os
.path
.dirname(__file__
)
61 if "/site-packages/" in dirname
:
62 prefix
= "/".join(dirname
[:dirname
.index("/site-packages/")].split("/")[:-2])
63 for suffix
in ["share/setup", "share/samba/setup", "setup"]:
64 ret
= os
.path
.join(prefix
, suffix
)
65 if os
.path
.isdir(ret
):
68 ret
= os
.path
.join(dirname
, "../../../setup")
69 if os
.path
.isdir(ret
):
71 raise Exception("Unable to find setup directory.")
73 # descriptors of the naming contexts
74 # hard coded at this point, but will probably be changed when
75 # we enable different fsmo roles
77 def get_config_descriptor(domain_sid
):
78 sddl
= "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
85 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
86 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
88 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
90 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
91 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
92 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
93 sec
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
96 def get_domain_descriptor(domain_sid
):
97 sddl
= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
98 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
99 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
101 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
109 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
113 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
119 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
120 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
121 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
122 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
125 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
127 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
133 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
134 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
135 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
138 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
140 "(A;;RPLCLORC;;;ED)" \
141 "(A;;RPLCLORC;;;AU)" \
142 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
143 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
146 sec
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
149 DEFAULTSITE
= "Default-First-Site-Name"
153 class ProvisionPaths(object):
155 self
.shareconf
= None
166 self
.dns_keytab
= None
169 self
.private_dir
= None
172 class ProvisionNames(object):
178 self
.ldapmanagerdn
= None
179 self
.dnsdomain
= None
181 self
.netbiosname
= None
188 class ProvisionResult(object):
195 def check_install(lp
, session_info
, credentials
):
196 """Check whether the current install seems ok.
198 :param lp: Loadparm context
199 :param session_info: Session information
200 :param credentials: Credentials
202 if lp
.get("realm") == "":
203 raise Exception("Realm empty")
204 ldb
= Ldb(lp
.get("sam database"), session_info
=session_info
,
205 credentials
=credentials
, lp
=lp
)
206 if len(ldb
.search("(cn=Administrator)")) != 1:
207 raise ProvisioningError("No administrator account found")
210 def findnss(nssfn
, names
):
211 """Find a user or group from a list of possibilities.
213 :param nssfn: NSS Function to try (should raise KeyError if not found)
214 :param names: Names to check.
215 :return: Value return by first names list.
222 raise KeyError("Unable to find user/group %r" % names
)
225 findnss_uid
= lambda names
: findnss(pwd
.getpwnam
, names
)[2]
226 findnss_gid
= lambda names
: findnss(grp
.getgrnam
, names
)[2]
229 def setup_add_ldif(ldb
, ldif_path
, subst_vars
=None,controls
=["relax:0"]):
230 """Setup a ldb in the private dir.
232 :param ldb: LDB file to import data into
233 :param ldif_path: Path of the LDIF file to load
234 :param subst_vars: Optional variables to subsitute in LDIF.
235 :param nocontrols: Optional list of controls, can be None for no controls
237 assert isinstance(ldif_path
, str)
238 data
= read_and_sub_file(ldif_path
, subst_vars
)
239 ldb
.add_ldif(data
,controls
)
242 def setup_modify_ldif(ldb
, ldif_path
, subst_vars
=None):
243 """Modify a ldb in the private dir.
245 :param ldb: LDB object.
246 :param ldif_path: LDIF file path.
247 :param subst_vars: Optional dictionary with substitution variables.
249 data
= read_and_sub_file(ldif_path
, subst_vars
)
251 ldb
.modify_ldif(data
)
254 def setup_ldb(ldb
, ldif_path
, subst_vars
):
255 """Import a LDIF a file into a LDB handle, optionally substituting variables.
257 :note: Either all LDIF data will be added or none (using transactions).
259 :param ldb: LDB file to import into.
260 :param ldif_path: Path to the LDIF file.
261 :param subst_vars: Dictionary with substitution variables.
263 assert ldb
is not None
264 ldb
.transaction_start()
266 setup_add_ldif(ldb
, ldif_path
, subst_vars
)
268 ldb
.transaction_cancel()
270 ldb
.transaction_commit()
273 def provision_paths_from_lp(lp
, dnsdomain
):
274 """Set the default paths for provisioning.
276 :param lp: Loadparm context.
277 :param dnsdomain: DNS Domain name
279 paths
= ProvisionPaths()
280 paths
.private_dir
= lp
.get("private dir")
281 paths
.dns_keytab
= "dns.keytab"
283 paths
.shareconf
= os
.path
.join(paths
.private_dir
, "share.ldb")
284 paths
.samdb
= os
.path
.join(paths
.private_dir
, lp
.get("sam database") or "samdb.ldb")
285 paths
.idmapdb
= os
.path
.join(paths
.private_dir
, lp
.get("idmap database") or "idmap.ldb")
286 paths
.secrets
= os
.path
.join(paths
.private_dir
, lp
.get("secrets database") or "secrets.ldb")
287 paths
.privilege
= os
.path
.join(paths
.private_dir
, "privilege.ldb")
288 paths
.dns
= os
.path
.join(paths
.private_dir
, "dns", dnsdomain
+ ".zone")
289 paths
.dns_update_list
= os
.path
.join(paths
.private_dir
, "dns_update_list")
290 paths
.namedconf
= os
.path
.join(paths
.private_dir
, "named.conf")
291 paths
.namedconf_update
= os
.path
.join(paths
.private_dir
, "named.conf.update")
292 paths
.namedtxt
= os
.path
.join(paths
.private_dir
, "named.txt")
293 paths
.krb5conf
= os
.path
.join(paths
.private_dir
, "krb5.conf")
294 paths
.winsdb
= os
.path
.join(paths
.private_dir
, "wins.ldb")
295 paths
.s4_ldapi_path
= os
.path
.join(paths
.private_dir
, "ldapi")
296 paths
.phpldapadminconfig
= os
.path
.join(paths
.private_dir
,
297 "phpldapadmin-config.php")
298 paths
.hklm
= "hklm.ldb"
299 paths
.hkcr
= "hkcr.ldb"
300 paths
.hkcu
= "hkcu.ldb"
301 paths
.hku
= "hku.ldb"
302 paths
.hkpd
= "hkpd.ldb"
303 paths
.hkpt
= "hkpt.ldb"
305 paths
.sysvol
= lp
.get("path", "sysvol")
307 paths
.netlogon
= lp
.get("path", "netlogon")
309 paths
.smbconf
= lp
.configfile
314 def guess_names(lp
=None, hostname
=None, domain
=None, dnsdomain
=None,
315 serverrole
=None, rootdn
=None, domaindn
=None, configdn
=None,
316 schemadn
=None, serverdn
=None, sitename
=None):
317 """Guess configuration settings to use."""
320 hostname
= socket
.gethostname().split(".")[0]
322 netbiosname
= lp
.get("netbios name")
323 if netbiosname
is None:
324 netbiosname
= hostname
325 assert netbiosname
is not None
326 netbiosname
= netbiosname
.upper()
327 if not valid_netbios_name(netbiosname
):
328 raise InvalidNetbiosName(netbiosname
)
330 if dnsdomain
is None:
331 dnsdomain
= lp
.get("realm")
332 if dnsdomain
is None or dnsdomain
== "":
333 raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
335 dnsdomain
= dnsdomain
.lower()
337 if serverrole
is None:
338 serverrole
= lp
.get("server role")
339 if serverrole
is None:
340 raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
342 serverrole
= serverrole
.lower()
344 realm
= dnsdomain
.upper()
346 if lp
.get("realm").upper() != realm
:
347 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp
.get("realm").upper(), realm
)
349 if lp
.get("server role").lower() != serverrole
:
350 raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp
.get("server role").upper(), serverrole
)
352 if serverrole
== "domain controller":
354 domain
= lp
.get("workgroup")
356 raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
357 domain
= domain
.upper()
359 if lp
.get("workgroup").upper() != domain
:
360 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp
.get("workgroup").upper(), domain
)
363 domaindn
= "DC=" + dnsdomain
.replace(".", ",DC=")
367 domaindn
= "DC=" + netbiosname
369 if not valid_netbios_name(domain
):
370 raise InvalidNetbiosName(domain
)
372 if hostname
.upper() == realm
:
373 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm
, hostname
)
374 if netbiosname
== realm
:
375 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm
, netbiosname
)
377 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm
, domain
)
383 configdn
= "CN=Configuration," + rootdn
385 schemadn
= "CN=Schema," + configdn
390 names
= ProvisionNames()
391 names
.rootdn
= rootdn
392 names
.domaindn
= domaindn
393 names
.configdn
= configdn
394 names
.schemadn
= schemadn
395 names
.ldapmanagerdn
= "CN=Manager," + rootdn
396 names
.dnsdomain
= dnsdomain
397 names
.domain
= domain
399 names
.netbiosname
= netbiosname
400 names
.hostname
= hostname
401 names
.sitename
= sitename
402 names
.serverdn
= "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname
, sitename
, configdn
)
407 def make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
, serverrole
,
408 targetdir
, sid_generator
,eadb
):
409 """Create a new smb.conf file based on a couple of basic settings.
411 assert smbconf
is not None
413 hostname
= socket
.gethostname().split(".")[0]
414 netbiosname
= hostname
.upper()
416 if serverrole
is None:
417 serverrole
= "standalone"
419 assert serverrole
in ("domain controller", "member server", "standalone")
420 if serverrole
== "domain controller":
422 elif serverrole
== "member server":
423 smbconfsuffix
= "member"
424 elif serverrole
== "standalone":
425 smbconfsuffix
= "standalone"
427 if sid_generator
is None:
428 sid_generator
= "internal"
430 assert domain
is not None
431 domain
= domain
.upper()
433 assert realm
is not None
434 realm
= realm
.upper()
436 default_lp
= param
.LoadParm()
437 #Load non-existant file
438 if os
.path
.exists(smbconf
):
439 default_lp
.load(smbconf
)
441 if targetdir
is not None:
442 privdir
= os
.path
.join(targetdir
, "private")
444 privdir
= default_lp
.get("private dir")
445 posixeadb_line
= "posix:eadb = " + os
.path
.abspath(os
.path
.join(privdir
,"eadb.tdb"))
449 if targetdir
is not None:
450 privatedir_line
= "private dir = " + os
.path
.abspath(os
.path
.join(targetdir
, "private"))
451 lockdir_line
= "lock dir = " + os
.path
.abspath(targetdir
)
453 default_lp
.set("lock dir", os
.path
.abspath(targetdir
))
458 if sid_generator
== "internal":
459 sid_generator_line
= ""
461 sid_generator_line
= "sid generator = " + sid_generator
463 sysvol
= os
.path
.join(default_lp
.get("lock dir"), "sysvol")
464 netlogon
= os
.path
.join(sysvol
, realm
.lower(), "scripts")
466 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix
),
468 "NETBIOS_NAME": netbiosname
,
471 "SERVERROLE": serverrole
,
472 "NETLOGONPATH": netlogon
,
473 "SYSVOLPATH": sysvol
,
474 "SIDGENERATOR_LINE": sid_generator_line
,
475 "PRIVATEDIR_LINE": privatedir_line
,
476 "LOCKDIR_LINE": lockdir_line
,
477 "POSIXEADB_LINE": posixeadb_line
481 def setup_name_mappings(samdb
, idmap
, sid
, domaindn
, root_uid
, nobody_uid
,
482 users_gid
, wheel_gid
):
483 """setup reasonable name mappings for sam names to unix names.
485 :param samdb: SamDB object.
486 :param idmap: IDmap db object.
487 :param sid: The domain sid.
488 :param domaindn: The domain DN.
489 :param root_uid: uid of the UNIX root user.
490 :param nobody_uid: uid of the UNIX nobody user.
491 :param users_gid: gid of the UNIX users group.
492 :param wheel_gid: gid of the UNIX wheel group."""
494 idmap
.setup_name_mapping("S-1-5-7", idmap
.TYPE_UID
, nobody_uid
)
495 idmap
.setup_name_mapping("S-1-5-32-544", idmap
.TYPE_GID
, wheel_gid
)
497 idmap
.setup_name_mapping(sid
+ "-500", idmap
.TYPE_UID
, root_uid
)
498 idmap
.setup_name_mapping(sid
+ "-513", idmap
.TYPE_GID
, users_gid
)
500 def setup_samdb_partitions(samdb_path
, setup_path
, message
, lp
, session_info
,
501 provision_backend
, names
, schema
,
504 """Setup the partitions for the SAM database.
506 Alternatively, provision() may call this, and then populate the database.
508 :note: This will wipe the Sam Database!
510 :note: This function always removes the local SAM LDB file. The erase
511 parameter controls whether to erase the existing data, which
512 may not be stored locally but in LDAP.
515 assert session_info
is not None
517 # We use options=["modules:"] to stop the modules loading - we
518 # just want to wipe and re-initialise the database, not start it up
521 os
.unlink(samdb_path
)
525 samdb
= Ldb(url
=samdb_path
, session_info
=session_info
,
526 lp
=lp
, options
=["modules:"])
528 ldap_backend_line
= "# No LDAP backend"
529 if provision_backend
.type is not "ldb":
530 ldap_backend_line
= "ldapBackend: %s" % provision_backend
.ldapi_uri
532 samdb
.transaction_start()
534 message("Setting up sam.ldb partitions and settings")
535 setup_add_ldif(samdb
, setup_path("provision_partitions.ldif"), {
536 "SCHEMADN": ldb
.Dn(schema
.ldb
, names
.schemadn
).get_casefold(),
537 "CONFIGDN": ldb
.Dn(schema
.ldb
, names
.configdn
).get_casefold(),
538 "DOMAINDN": ldb
.Dn(schema
.ldb
, names
.domaindn
).get_casefold(),
539 "LDAP_BACKEND_LINE": ldap_backend_line
,
543 setup_add_ldif(samdb
, setup_path("provision_init.ldif"), {
544 "BACKEND_TYPE": provision_backend
.type,
545 "SERVER_ROLE": serverrole
548 message("Setting up sam.ldb rootDSE")
549 setup_samdb_rootdse(samdb
, setup_path
, names
)
552 samdb
.transaction_cancel()
555 samdb
.transaction_commit()
558 def secretsdb_self_join(secretsdb
, domain
,
559 netbiosname
, machinepass
, domainsid
=None,
560 realm
=None, dnsdomain
=None,
562 key_version_number
=1,
563 secure_channel_type
=SEC_CHAN_WKSTA
):
564 """Add domain join-specific bits to a secrets database.
566 :param secretsdb: Ldb Handle to the secrets database
567 :param machinepass: Machine password
569 attrs
=["whenChanged",
577 msg
= ldb
.Message(ldb
.Dn(secretsdb
, "flatname=%s,cn=Primary Domains" % domain
))
578 msg
["secureChannelType"] = str(secure_channel_type
)
579 msg
["flatname"] = [domain
]
580 msg
["objectClass"] = ["top", "primaryDomain"]
581 if realm
is not None:
582 if dnsdomain
is None:
583 dnsdomain
= realm
.lower()
584 msg
["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
586 msg
["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname
.lower(), dnsdomain
.lower(), realm
.upper())
587 msg
["msDS-KeyVersionNumber"] = [str(key_version_number
)]
588 msg
["privateKeytab"] = ["secrets.keytab"]
591 msg
["secret"] = [machinepass
]
592 msg
["samAccountName"] = ["%s$" % netbiosname
]
593 msg
["secureChannelType"] = [str(secure_channel_type
)]
594 if domainsid
is not None:
595 msg
["objectSid"] = [ndr_pack(domainsid
)]
597 res
= secretsdb
.search(base
="cn=Primary Domains",
599 expression
=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain
, realm
, str(domainsid
))),
600 scope
=ldb
.SCOPE_ONELEVEL
)
603 if del_msg
.dn
is not msg
.dn
:
604 secretsdb
.delete(del_msg
.dn
)
606 res
= secretsdb
.search(base
=msg
.dn
, attrs
=attrs
, scope
=ldb
.SCOPE_BASE
)
609 msg
["priorSecret"] = res
[0]["secret"]
610 msg
["priorWhenChanged"] = res
[0]["whenChanged"]
612 if res
["privateKeytab"] is not None:
613 msg
["privateKeytab"] = res
[0]["privateKeytab"]
615 if res
["krb5Keytab"] is not None:
616 msg
["krb5Keytab"] = res
[0]["krb5Keytab"]
619 el
.set_flags(ldb
.FLAG_MOD_REPLACE
)
620 secretsdb
.modify(msg
)
625 def secretsdb_setup_dns(secretsdb
, setup_path
, private_dir
,
627 dns_keytab_path
, dnspass
):
628 """Add DNS specific bits to a secrets database.
630 :param secretsdb: Ldb Handle to the secrets database
631 :param setup_path: Setup path function
632 :param machinepass: Machine password
635 os
.unlink(os
.path
.join(private_dir
, dns_keytab_path
))
639 setup_ldb(secretsdb
, setup_path("secrets_dns.ldif"), {
641 "DNSDOMAIN": dnsdomain
,
642 "DNS_KEYTAB": dns_keytab_path
,
643 "DNSPASS_B64": b64encode(dnspass
),
647 def setup_secretsdb(path
, setup_path
, session_info
, backend_credentials
, lp
):
648 """Setup the secrets database.
650 :param path: Path to the secrets database.
651 :param setup_path: Get the path to a setup file.
652 :param session_info: Session info.
653 :param credentials: Credentials
654 :param lp: Loadparm context
655 :return: LDB handle for the created secrets database
657 if os
.path
.exists(path
):
659 secrets_ldb
= Ldb(path
, session_info
=session_info
,
662 secrets_ldb
.load_ldif_file_add(setup_path("secrets_init.ldif"))
663 secrets_ldb
= Ldb(path
, session_info
=session_info
,
665 secrets_ldb
.transaction_start()
666 secrets_ldb
.load_ldif_file_add(setup_path("secrets.ldif"))
668 if backend_credentials
is not None and backend_credentials
.authentication_requested():
669 if backend_credentials
.get_bind_dn() is not None:
670 setup_add_ldif(secrets_ldb
, setup_path("secrets_simple_ldap.ldif"), {
671 "LDAPMANAGERDN": backend_credentials
.get_bind_dn(),
672 "LDAPMANAGERPASS_B64": b64encode(backend_credentials
.get_password())
675 setup_add_ldif(secrets_ldb
, setup_path("secrets_sasl_ldap.ldif"), {
676 "LDAPADMINUSER": backend_credentials
.get_username(),
677 "LDAPADMINREALM": backend_credentials
.get_realm(),
678 "LDAPADMINPASS_B64": b64encode(backend_credentials
.get_password())
683 def setup_privileges(path
, setup_path
, session_info
, lp
):
684 """Setup the privileges database.
686 :param path: Path to the privileges database.
687 :param setup_path: Get the path to a setup file.
688 :param session_info: Session info.
689 :param credentials: Credentials
690 :param lp: Loadparm context
691 :return: LDB handle for the created secrets database
693 if os
.path
.exists(path
):
695 privilege_ldb
= Ldb(path
, session_info
=session_info
, lp
=lp
)
696 privilege_ldb
.erase()
697 privilege_ldb
.load_ldif_file_add(setup_path("provision_privilege.ldif"))
700 def setup_registry(path
, setup_path
, session_info
, lp
):
701 """Setup the registry.
703 :param path: Path to the registry database
704 :param setup_path: Function that returns the path to a setup.
705 :param session_info: Session information
706 :param credentials: Credentials
707 :param lp: Loadparm context
709 reg
= registry
.Registry()
710 hive
= registry
.open_ldb(path
, session_info
=session_info
,
712 reg
.mount_hive(hive
, registry
.HKEY_LOCAL_MACHINE
)
713 provision_reg
= setup_path("provision.reg")
714 assert os
.path
.exists(provision_reg
)
715 reg
.diff_apply(provision_reg
)
718 def setup_idmapdb(path
, setup_path
, session_info
, lp
):
719 """Setup the idmap database.
721 :param path: path to the idmap database
722 :param setup_path: Function that returns a path to a setup file
723 :param session_info: Session information
724 :param credentials: Credentials
725 :param lp: Loadparm context
727 if os
.path
.exists(path
):
730 idmap_ldb
= IDmapDB(path
, session_info
=session_info
,
734 idmap_ldb
.load_ldif_file_add(setup_path("idmap_init.ldif"))
738 def setup_samdb_rootdse(samdb
, setup_path
, names
):
739 """Setup the SamDB rootdse.
741 :param samdb: Sam Database handle
742 :param setup_path: Obtain setup path
744 setup_add_ldif(samdb
, setup_path("provision_rootdse_add.ldif"), {
745 "SCHEMADN": names
.schemadn
,
746 "NETBIOSNAME": names
.netbiosname
,
747 "DNSDOMAIN": names
.dnsdomain
,
748 "REALM": names
.realm
,
749 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
750 "DOMAINDN": names
.domaindn
,
751 "ROOTDN": names
.rootdn
,
752 "CONFIGDN": names
.configdn
,
753 "SERVERDN": names
.serverdn
,
757 def setup_self_join(samdb
, names
,
758 machinepass
, dnspass
,
759 domainsid
, invocationid
, setup_path
,
760 policyguid
, policyguid_dc
, domainControllerFunctionality
,
762 """Join a host to its own domain."""
763 assert isinstance(invocationid
, str)
764 if ntdsguid
is not None:
765 ntdsguid_line
= "objectGUID: %s\n"%ntdsguid
768 setup_add_ldif(samdb
, setup_path("provision_self_join.ldif"), {
769 "CONFIGDN": names
.configdn
,
770 "SCHEMADN": names
.schemadn
,
771 "DOMAINDN": names
.domaindn
,
772 "SERVERDN": names
.serverdn
,
773 "INVOCATIONID": invocationid
,
774 "NETBIOSNAME": names
.netbiosname
,
775 "DEFAULTSITE": names
.sitename
,
776 "DNSNAME": "%s.%s" % (names
.hostname
, names
.dnsdomain
),
777 "MACHINEPASS_B64": b64encode(machinepass
),
778 "REALM": names
.realm
,
779 "DOMAIN": names
.domain
,
780 "DOMAINSID": str(domainsid
),
781 "DNSDOMAIN": names
.dnsdomain
,
782 "SAMBA_VERSION_STRING": version
,
783 "NTDSGUID": ntdsguid_line
,
784 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality
)})
786 setup_add_ldif(samdb
, setup_path("provision_group_policy.ldif"), {
787 "POLICYGUID": policyguid
,
788 "POLICYGUID_DC": policyguid_dc
,
789 "DNSDOMAIN": names
.dnsdomain
,
790 "DOMAINSID": str(domainsid
),
791 "DOMAINDN": names
.domaindn
})
793 # add the NTDSGUID based SPNs
794 ntds_dn
= "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names
.hostname
, names
.domaindn
)
795 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
, attribute
="objectGUID",
796 expression
="", scope
=ldb
.SCOPE_BASE
)
797 assert isinstance(names
.ntdsguid
, str)
799 # Setup fSMORoleOwner entries to point at the newly created DC entry
800 setup_modify_ldif(samdb
, setup_path("provision_self_join_modify.ldif"), {
801 "DOMAIN": names
.domain
,
802 "DNSDOMAIN": names
.dnsdomain
,
803 "DOMAINDN": names
.domaindn
,
804 "CONFIGDN": names
.configdn
,
805 "SCHEMADN": names
.schemadn
,
806 "DEFAULTSITE": names
.sitename
,
807 "SERVERDN": names
.serverdn
,
808 "NETBIOSNAME": names
.netbiosname
,
809 "NTDSGUID": names
.ntdsguid
,
810 "DNSPASS_B64": b64encode(dnspass
),
814 def setup_gpo(paths
,names
,samdb
,policyguid
,policyguid_dc
,domainsid
):
815 policy_path
= os
.path
.join(paths
.sysvol
, names
.dnsdomain
, "Policies",
816 "{" + policyguid
+ "}")
817 os
.makedirs(policy_path
, 0755)
818 open(os
.path
.join(policy_path
, "GPT.INI"), 'w').write(
819 "[General]\r\nVersion=65543")
820 os
.makedirs(os
.path
.join(policy_path
, "MACHINE"), 0755)
821 os
.makedirs(os
.path
.join(policy_path
, "USER"), 0755)
823 policy_path_dc
= os
.path
.join(paths
.sysvol
, names
.dnsdomain
, "Policies",
824 "{" + policyguid_dc
+ "}")
825 os
.makedirs(policy_path_dc
, 0755)
826 open(os
.path
.join(policy_path_dc
, "GPT.INI"), 'w').write(
827 "[General]\r\nVersion=2")
828 os
.makedirs(os
.path
.join(policy_path_dc
, "MACHINE"), 0755)
829 os
.makedirs(os
.path
.join(policy_path_dc
, "USER"), 0755)
832 def setup_samdb(path
, setup_path
, session_info
, provision_backend
, lp
,
834 domainsid
, domainguid
, policyguid
, policyguid_dc
,
835 fill
, adminpass
, krbtgtpass
,
836 machinepass
, invocationid
, dnspass
, ntdsguid
,
837 serverrole
, dom_for_fun_level
=None,
839 """Setup a complete SAM Database.
841 :note: This will wipe the main SAM database file!
844 # ATTENTION: Do NOT change these default values without discussion with the
845 # team and/or release manager. They have a big impact on the whole program!
846 domainControllerFunctionality
= DS_DC_FUNCTION_2008
848 if dom_for_fun_level
is None:
849 dom_for_fun_level
= DS_DOMAIN_FUNCTION_2003
850 if dom_for_fun_level
< DS_DOMAIN_FUNCTION_2003
:
851 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
853 if dom_for_fun_level
> domainControllerFunctionality
:
854 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!")
856 domainFunctionality
= dom_for_fun_level
857 forestFunctionality
= dom_for_fun_level
859 # Also wipes the database
860 setup_samdb_partitions(path
, setup_path
, message
=message
, lp
=lp
,
861 provision_backend
=provision_backend
, session_info
=session_info
,
863 serverrole
=serverrole
, schema
=schema
)
866 schema
= Schema(setup_path
, domainsid
, schemadn
=names
.schemadn
, serverdn
=names
.serverdn
)
868 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
869 samdb
= Ldb(session_info
=session_info
,
870 credentials
=provision_backend
.credentials
, lp
=lp
)
872 message("Pre-loading the Samba 4 and AD schema")
874 # Load the schema from the one we computed earlier
875 samdb
.set_schema_from_ldb(schema
.ldb
)
877 # And now we can connect to the DB - the schema won't be loaded from the DB
883 samdb
.transaction_start()
885 # Set the domain functionality levels onto the database.
886 # Various module (the password_hash module in particular) need
887 # to know what level of AD we are emulating.
889 # These will be fixed into the database via the database
890 # modifictions below, but we need them set from the start.
891 samdb
.set_opaque_integer("domainFunctionality", domainFunctionality
)
892 samdb
.set_opaque_integer("forestFunctionality", forestFunctionality
)
893 samdb
.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality
)
895 samdb
.set_domain_sid(str(domainsid
))
896 samdb
.set_invocation_id(invocationid
)
898 message("Adding DomainDN: %s" % names
.domaindn
)
900 #impersonate domain admin
901 admin_session_info
= admin_session(lp
, str(domainsid
))
902 samdb
.set_session_info(admin_session_info
)
903 if domainguid
is not None:
904 domainguid_line
= "objectGUID: %s\n-" % domainguid
908 descr
= b64encode(get_domain_descriptor(domainsid
))
909 setup_add_ldif(samdb
, setup_path("provision_basedn.ldif"), {
910 "DOMAINDN": names
.domaindn
,
911 "DOMAINGUID": domainguid_line
,
916 setup_modify_ldif(samdb
, setup_path("provision_basedn_modify.ldif"), {
917 "CREATTIME": str(int(time
.time() * 1e7
)), # seconds -> ticks
918 "DOMAINSID": str(domainsid
),
919 "SCHEMADN": names
.schemadn
,
920 "NETBIOSNAME": names
.netbiosname
,
921 "DEFAULTSITE": names
.sitename
,
922 "CONFIGDN": names
.configdn
,
923 "SERVERDN": names
.serverdn
,
924 "POLICYGUID": policyguid
,
925 "DOMAINDN": names
.domaindn
,
926 "DOMAIN_FUNCTIONALITY": str(domainFunctionality
),
927 "SAMBA_VERSION_STRING": version
930 message("Adding configuration container")
931 descr
= b64encode(get_config_descriptor(domainsid
))
932 setup_add_ldif(samdb
, setup_path("provision_configuration_basedn.ldif"), {
933 "CONFIGDN": names
.configdn
,
937 # The LDIF here was created when the Schema object was constructed
938 message("Setting up sam.ldb schema")
939 samdb
.add_ldif(schema
.schema_dn_add
, controls
=["relax:0"])
940 samdb
.modify_ldif(schema
.schema_dn_modify
)
941 samdb
.write_prefixes_from_schema()
942 samdb
.add_ldif(schema
.schema_data
, controls
=["relax:0"])
943 setup_add_ldif(samdb
, setup_path("aggregate_schema.ldif"),
944 {"SCHEMADN": names
.schemadn
})
946 message("Reopening sam.ldb with new schema")
947 samdb
.transaction_commit()
948 samdb
= Ldb(session_info
=admin_session_info
,
949 credentials
=provision_backend
.credentials
, lp
=lp
)
951 samdb
.transaction_start()
952 samdb
.set_invocation_id(invocationid
)
954 message("Setting up sam.ldb configuration data")
955 setup_add_ldif(samdb
, setup_path("provision_configuration.ldif"), {
956 "CONFIGDN": names
.configdn
,
957 "NETBIOSNAME": names
.netbiosname
,
958 "DEFAULTSITE": names
.sitename
,
959 "DNSDOMAIN": names
.dnsdomain
,
960 "DOMAIN": names
.domain
,
961 "SCHEMADN": names
.schemadn
,
962 "DOMAINDN": names
.domaindn
,
963 "SERVERDN": names
.serverdn
,
964 "FOREST_FUNCTIONALALITY": str(forestFunctionality
)
967 message("Setting up display specifiers")
968 display_specifiers_ldif
= read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
969 display_specifiers_ldif
= substitute_var(display_specifiers_ldif
, {"CONFIGDN": names
.configdn
})
970 check_all_substituted(display_specifiers_ldif
)
971 samdb
.add_ldif(display_specifiers_ldif
)
973 message("Adding users container")
974 setup_add_ldif(samdb
, setup_path("provision_users_add.ldif"), {
975 "DOMAINDN": names
.domaindn
})
976 message("Modifying users container")
977 setup_modify_ldif(samdb
, setup_path("provision_users_modify.ldif"), {
978 "DOMAINDN": names
.domaindn
})
979 message("Adding computers container")
980 setup_add_ldif(samdb
, setup_path("provision_computers_add.ldif"), {
981 "DOMAINDN": names
.domaindn
})
982 message("Modifying computers container")
983 setup_modify_ldif(samdb
, setup_path("provision_computers_modify.ldif"), {
984 "DOMAINDN": names
.domaindn
})
985 message("Setting up sam.ldb data")
986 setup_add_ldif(samdb
, setup_path("provision.ldif"), {
987 "CREATTIME": str(int(time
.time() * 1e7
)), # seconds -> ticks
988 "DOMAINDN": names
.domaindn
,
989 "NETBIOSNAME": names
.netbiosname
,
990 "DEFAULTSITE": names
.sitename
,
991 "CONFIGDN": names
.configdn
,
992 "SERVERDN": names
.serverdn
,
993 "POLICYGUID_DC": policyguid_dc
996 setup_modify_ldif(samdb
, setup_path("provision_basedn_references.ldif"), {
997 "DOMAINDN": names
.domaindn
})
999 setup_modify_ldif(samdb
, setup_path("provision_configuration_references.ldif"), {
1000 "CONFIGDN": names
.configdn
,
1001 "SCHEMADN": names
.schemadn
})
1002 if fill
== FILL_FULL
:
1003 message("Setting up sam.ldb users and groups")
1004 setup_add_ldif(samdb
, setup_path("provision_users.ldif"), {
1005 "DOMAINDN": names
.domaindn
,
1006 "DOMAINSID": str(domainsid
),
1007 "CONFIGDN": names
.configdn
,
1008 "ADMINPASS_B64": b64encode(adminpass
),
1009 "KRBTGTPASS_B64": b64encode(krbtgtpass
),
1012 message("Setting up self join")
1013 setup_self_join(samdb
, names
=names
, invocationid
=invocationid
,
1015 machinepass
=machinepass
,
1016 domainsid
=domainsid
, policyguid
=policyguid
,
1017 policyguid_dc
=policyguid_dc
,
1018 setup_path
=setup_path
,
1019 domainControllerFunctionality
=domainControllerFunctionality
,
1022 ntds_dn
= "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names
.hostname
, names
.domaindn
)
1023 names
.ntdsguid
= samdb
.searchone(basedn
=ntds_dn
,
1024 attribute
="objectGUID", expression
="", scope
=ldb
.SCOPE_BASE
)
1025 assert isinstance(names
.ntdsguid
, str)
1028 samdb
.transaction_cancel()
1031 samdb
.transaction_commit()
1036 FILL_NT4SYNC
= "NT4SYNC"
1038 SYSVOL_ACL
= "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1039 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)"
1041 def set_gpo_acl(path
,acl
,lp
,domsid
):
1042 setntacl(lp
,path
,acl
,domsid
)
1043 for root
, dirs
, files
in os
.walk(path
, topdown
=False):
1045 setntacl(lp
,os
.path
.join(root
, name
),acl
,domsid
)
1047 setntacl(lp
,os
.path
.join(root
, name
),acl
,domsid
)
1049 def setsysvolacl(samdb
,names
,netlogon
,sysvol
,gid
,domainsid
,lp
):
1052 os
.chown(sysvol
,-1,gid
)
1056 setntacl(lp
,sysvol
,SYSVOL_ACL
,str(domainsid
))
1057 for root
, dirs
, files
in os
.walk(sysvol
, topdown
=False):
1060 os
.chown(os
.path
.join(root
, name
),-1,gid
)
1061 setntacl(lp
,os
.path
.join(root
, name
),SYSVOL_ACL
,str(domainsid
))
1064 os
.chown(os
.path
.join(root
, name
),-1,gid
)
1065 setntacl(lp
,os
.path
.join(root
, name
),SYSVOL_ACL
,str(domainsid
))
1068 policy_path
= os
.path
.join(sysvol
, names
.dnsdomain
, "Policies")
1069 set_gpo_acl(policy_path
,dsacl2fsacl(POLICIES_ACL
,str(domainsid
)),lp
,str(domainsid
))
1070 res
= samdb
.search(base
="CN=Policies,CN=System,%s"%(names
.domaindn
),
1071 attrs
=["cn","nTSecurityDescriptor"],
1072 expression
="", scope
=ldb
.SCOPE_ONELEVEL
)
1074 acl
= ndr_unpack(security
.descriptor
,str(policy
["nTSecurityDescriptor"])).as_sddl()
1075 policy_path
= os
.path
.join(sysvol
, names
.dnsdomain
, "Policies",
1077 set_gpo_acl(policy_path
,dsacl2fsacl(acl
,str(domainsid
)),lp
,str(domainsid
))
1081 def provision(setup_dir
, message
, session_info
,
1082 credentials
, smbconf
=None, targetdir
=None, samdb_fill
=FILL_FULL
,
1084 rootdn
=None, domaindn
=None, schemadn
=None, configdn
=None,
1086 domain
=None, hostname
=None, hostip
=None, hostip6
=None,
1087 domainsid
=None, adminpass
=None, ldapadminpass
=None,
1088 krbtgtpass
=None, domainguid
=None,
1089 policyguid
=None, policyguid_dc
=None, invocationid
=None,
1090 machinepass
=None, ntdsguid
=None,
1091 dnspass
=None, root
=None, nobody
=None, users
=None,
1092 wheel
=None, backup
=None, aci
=None, serverrole
=None,
1093 dom_for_fun_level
=None,
1094 ldap_backend_extra_port
=None, backend_type
=None,
1096 ol_mmr_urls
=None, ol_olc
=None,
1097 setup_ds_path
=None, slapd_path
=None, nosync
=False,
1098 ldap_dryrun_mode
=False,useeadb
=False):
1101 :note: caution, this wipes all existing data!
1104 def setup_path(file):
1105 return os
.path
.join(setup_dir
, file)
1107 if domainsid
is None:
1108 domainsid
= security
.random_sid()
1110 domainsid
= security
.dom_sid(domainsid
)
1112 # create/adapt the group policy GUIDs
1113 if policyguid
is None:
1114 policyguid
= str(uuid
.uuid4())
1115 policyguid
= policyguid
.upper()
1116 if policyguid_dc
is None:
1117 policyguid_dc
= str(uuid
.uuid4())
1118 policyguid_dc
= policyguid_dc
.upper()
1120 if adminpass
is None:
1121 adminpass
= glue
.generate_random_password(12, 32)
1122 if krbtgtpass
is None:
1123 krbtgtpass
= glue
.generate_random_password(128, 255)
1124 if machinepass
is None:
1125 machinepass
= glue
.generate_random_password(128, 255)
1127 dnspass
= glue
.generate_random_password(128, 255)
1128 if ldapadminpass
is None:
1129 #Make a new, random password between Samba and it's LDAP server
1130 ldapadminpass
=glue
.generate_random_password(128, 255)
1132 if backend_type
is None:
1133 backend_type
= "ldb"
1135 sid_generator
= "internal"
1136 if backend_type
== "fedora-ds":
1137 sid_generator
= "backend"
1139 root_uid
= findnss_uid([root
or "root"])
1140 nobody_uid
= findnss_uid([nobody
or "nobody"])
1141 users_gid
= findnss_gid([users
or "users"])
1143 wheel_gid
= findnss_gid(["wheel", "adm"])
1145 wheel_gid
= findnss_gid([wheel
])
1147 bind_gid
= findnss_gid(["bind", "named"])
1151 if targetdir
is not None:
1152 if (not os
.path
.exists(os
.path
.join(targetdir
, "etc"))):
1153 os
.makedirs(os
.path
.join(targetdir
, "etc"))
1154 smbconf
= os
.path
.join(targetdir
, "etc", "smb.conf")
1155 elif smbconf
is None:
1156 smbconf
= param
.default_path()
1158 # only install a new smb.conf if there isn't one there already
1159 if os
.path
.exists(smbconf
):
1160 # if Samba Team members can't figure out the weird errors
1161 # loading an empty smb.conf gives, then we need to be smarter.
1162 # Pretend it just didn't exist --abartlet
1163 data
= open(smbconf
, 'r').read()
1164 data
= data
.lstrip()
1165 if data
is None or data
== "":
1166 make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
, serverrole
,
1167 targetdir
, sid_generator
, useeadb
)
1169 make_smbconf(smbconf
, setup_path
, hostname
, domain
, realm
, serverrole
,
1170 targetdir
, sid_generator
, useeadb
)
1172 lp
= param
.LoadParm()
1175 names
= guess_names(lp
=lp
, hostname
=hostname
, domain
=domain
,
1176 dnsdomain
=realm
, serverrole
=serverrole
,
1177 domaindn
=domaindn
, configdn
=configdn
, schemadn
=schemadn
,
1178 serverdn
=serverdn
, sitename
=sitename
)
1180 paths
= provision_paths_from_lp(lp
, names
.dnsdomain
)
1182 paths
.bind_gid
= bind_gid
1185 hostips
= glue
.interface_ips(lp
, False)
1186 if len(hostips
) == 0:
1187 message("No external IPv4 address has been found: I use the loopback.")
1188 hostip
= '127.0.0.1'
1191 if len(hostips
) > 1:
1192 message("More than one IPv4 address found: I use " + hostip
+ ".")
1196 for ip
in socket
.getaddrinfo(names
.hostname
, None, socket
.AF_INET6
, socket
.AI_CANONNAME
, socket
.IPPROTO_IP
):
1199 if hostip6
== '::1' and ip
[-1][0] != '::1':
1201 except socket
.gaierror
, (socket
.EAI_NODATA
, msg
):
1204 if serverrole
is None:
1205 serverrole
= lp
.get("server role")
1207 assert serverrole
in ("domain controller", "member server", "standalone")
1208 if invocationid
is None:
1209 invocationid
= str(uuid
.uuid4())
1211 if not os
.path
.exists(paths
.private_dir
):
1212 os
.mkdir(paths
.private_dir
)
1213 if not os
.path
.exists(os
.path
.join(paths
.private_dir
,"tls")):
1214 os
.mkdir(os
.path
.join(paths
.private_dir
,"tls"))
1216 ldapi_url
= "ldapi://%s" % urllib
.quote(paths
.s4_ldapi_path
, safe
="")
1218 schema
= Schema(setup_path
, domainsid
, schemadn
=names
.schemadn
, serverdn
=names
.serverdn
)
1220 if backend_type
== "ldb":
1221 provision_backend
= LDBBackend(backend_type
,
1222 paths
=paths
, setup_path
=setup_path
,
1223 lp
=lp
, credentials
=credentials
,
1226 elif backend_type
== "existing":
1227 provision_backend
= ExistingBackend(backend_type
,
1228 paths
=paths
, setup_path
=setup_path
,
1229 lp
=lp
, credentials
=credentials
,
1232 ldapi_url
=ldapi_url
)
1233 elif backend_type
== "fedora-ds":
1234 provision_backend
= FDSBackend(backend_type
,
1235 paths
=paths
, setup_path
=setup_path
,
1236 lp
=lp
, credentials
=credentials
,
1239 domainsid
=domainsid
,
1242 ldapadminpass
=ldapadminpass
,
1243 slapd_path
=slapd_path
,
1244 ldap_backend_extra_port
=ldap_backend_extra_port
,
1245 ldap_dryrun_mode
=ldap_dryrun_mode
,
1247 setup_ds_path
=setup_ds_path
)
1248 elif backend_type
== "openldap":
1249 provision_backend
= OpenLDAPBackend(backend_type
,
1250 paths
=paths
, setup_path
=setup_path
,
1251 lp
=lp
, credentials
=credentials
,
1254 domainsid
=domainsid
,
1257 ldapadminpass
=ldapadminpass
,
1258 slapd_path
=slapd_path
,
1259 ldap_backend_extra_port
=ldap_backend_extra_port
,
1260 ldap_dryrun_mode
=ldap_dryrun_mode
,
1261 ol_mmr_urls
=ol_mmr_urls
,
1264 raise ProvisioningError("Unknown LDAP backend type selected")
1266 provision_backend
.init()
1267 provision_backend
.start()
1269 # only install a new shares config db if there is none
1270 if not os
.path
.exists(paths
.shareconf
):
1271 message("Setting up share.ldb")
1272 share_ldb
= Ldb(paths
.shareconf
, session_info
=session_info
,
1274 share_ldb
.load_ldif_file_add(setup_path("share.ldif"))
1277 message("Setting up secrets.ldb")
1278 secrets_ldb
= setup_secretsdb(paths
.secrets
, setup_path
,
1279 session_info
=session_info
,
1280 backend_credentials
=provision_backend
.secrets_credentials
, lp
=lp
)
1282 message("Setting up the registry")
1283 setup_registry(paths
.hklm
, setup_path
, session_info
,
1286 message("Setting up the privileges database")
1287 setup_privileges(paths
.privilege
, setup_path
, session_info
, lp
=lp
)
1289 message("Setting up idmap db")
1290 idmap
= setup_idmapdb(paths
.idmapdb
, setup_path
, session_info
=session_info
,
1293 message("Setting up SAM db")
1294 samdb
= setup_samdb(paths
.samdb
, setup_path
, session_info
,
1295 provision_backend
, lp
, names
,
1297 domainsid
=domainsid
,
1298 schema
=schema
, domainguid
=domainguid
,
1299 policyguid
=policyguid
, policyguid_dc
=policyguid_dc
,
1301 adminpass
=adminpass
, krbtgtpass
=krbtgtpass
,
1302 invocationid
=invocationid
,
1303 machinepass
=machinepass
, dnspass
=dnspass
,
1304 ntdsguid
=ntdsguid
, serverrole
=serverrole
,
1305 dom_for_fun_level
=dom_for_fun_level
)
1307 if serverrole
== "domain controller":
1308 if paths
.netlogon
is None:
1309 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1310 message("Please either remove %s or see the template at %s" %
1311 ( paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1312 assert(paths
.netlogon
is not None)
1314 if paths
.sysvol
is None:
1315 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1316 message("Please either remove %s or see the template at %s" %
1317 (paths
.smbconf
, setup_path("provision.smb.conf.dc")))
1318 assert(paths
.sysvol
is not None)
1321 if not os
.path
.isdir(paths
.netlogon
):
1322 os
.makedirs(paths
.netlogon
, 0755)
1324 if samdb_fill
== FILL_FULL
:
1325 setup_name_mappings(samdb
, idmap
, str(domainsid
), names
.domaindn
,
1326 root_uid
=root_uid
, nobody_uid
=nobody_uid
,
1327 users_gid
=users_gid
, wheel_gid
=wheel_gid
)
1329 if serverrole
== "domain controller":
1330 # Set up group policies (domain policy and domain controller policy)
1331 setup_gpo(paths
,names
,samdb
,policyguid
,policyguid_dc
,domainsid
)
1332 setsysvolacl(samdb
,names
,paths
.netlogon
,paths
.sysvol
,wheel_gid
,domainsid
,lp
)
1334 message("Setting up sam.ldb rootDSE marking as synchronized")
1335 setup_modify_ldif(samdb
, setup_path("provision_rootdse_modify.ldif"))
1337 secretsdb_self_join(secrets_ldb
, domain
=names
.domain
,
1339 dnsdomain
=names
.dnsdomain
,
1340 netbiosname
=names
.netbiosname
,
1341 domainsid
=domainsid
,
1342 machinepass
=machinepass
,
1343 secure_channel_type
=SEC_CHAN_BDC
)
1345 if serverrole
== "domain controller":
1346 secretsdb_setup_dns(secrets_ldb
, setup_path
,
1348 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1349 dns_keytab_path
=paths
.dns_keytab
,
1352 domainguid
= samdb
.searchone(basedn
=domaindn
, attribute
="objectGUID")
1353 assert isinstance(domainguid
, str)
1355 # Only make a zone file on the first DC, it should be replicated
1356 # with DNS replication
1357 create_zone_file(lp
, message
, paths
, targetdir
, setup_path
, dnsdomain
=names
.dnsdomain
,
1359 hostip6
=hostip6
, hostname
=names
.hostname
,
1361 domainguid
=domainguid
, ntdsguid
=names
.ntdsguid
)
1363 create_named_conf(paths
, setup_path
, realm
=names
.realm
,
1364 dnsdomain
=names
.dnsdomain
, private_dir
=paths
.private_dir
)
1366 create_named_txt(paths
.namedtxt
, setup_path
, realm
=names
.realm
,
1367 dnsdomain
=names
.dnsdomain
, private_dir
=paths
.private_dir
,
1368 keytab_name
=paths
.dns_keytab
)
1369 message("See %s for an example configuration include file for BIND" % paths
.namedconf
)
1370 message("and %s for further documentation required for secure DNS updates" % paths
.namedtxt
)
1372 create_krb5_conf(paths
.krb5conf
, setup_path
,
1373 dnsdomain
=names
.dnsdomain
, hostname
=names
.hostname
,
1375 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths
.krb5conf
)
1377 if serverrole
== "domain controller":
1378 create_dns_update_list(lp
, message
, paths
, setup_path
)
1380 provision_backend
.post_setup()
1381 provision_backend
.shutdown()
1383 create_phpldapadmin_config(paths
.phpldapadminconfig
, setup_path
,
1386 #Now commit the secrets.ldb to disk
1387 secrets_ldb
.transaction_commit()
1389 # the commit creates the dns.keytab, now chown it
1390 dns_keytab_path
= os
.path
.join(paths
.private_dir
, paths
.dns_keytab
)
1391 if (os
.path
.isfile(dns_keytab_path
) and paths
.bind_gid
is not None):
1393 os
.chmod(dns_keytab_path
, 0640)
1394 os
.chown(dns_keytab_path
, -1, paths
.bind_gid
)
1396 message("Failed to chown %s to bind gid %u" % (dns_keytab_path
, paths
.bind_gid
))
1399 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths
.phpldapadminconfig
)
1401 message("Once the above files are installed, your Samba4 server will be ready to use")
1402 message("Server Role: %s" % serverrole
)
1403 message("Hostname: %s" % names
.hostname
)
1404 message("NetBIOS Domain: %s" % names
.domain
)
1405 message("DNS Domain: %s" % names
.dnsdomain
)
1406 message("DOMAIN SID: %s" % str(domainsid
))
1407 if samdb_fill
== FILL_FULL
:
1408 message("Admin password: %s" % adminpass
)
1409 if provision_backend
.type is not "ldb":
1410 if provision_backend
.credentials
.get_bind_dn() is not None:
1411 message("LDAP Backend Admin DN: %s" % provision_backend
.credentials
.get_bind_dn())
1413 message("LDAP Admin User: %s" % provision_backend
.credentials
.get_username())
1415 message("LDAP Admin Password: %s" % provision_backend
.credentials
.get_password())
1417 if provision_backend
.slapd_command_escaped
is not None:
1418 # now display slapd_command_file.txt to show how slapd must be started next time
1419 message("Use later the following commandline to start slapd, then Samba:")
1420 message(provision_backend
.slapd_command_escaped
)
1421 message("This slapd-Commandline is also stored under: " + provision_backend
.ldapdir
+ "/ldap_backend_startup.sh")
1424 result
= ProvisionResult()
1425 result
.domaindn
= domaindn
1426 result
.paths
= paths
1428 result
.samdb
= samdb
1433 def provision_become_dc(setup_dir
=None,
1434 smbconf
=None, targetdir
=None, realm
=None,
1435 rootdn
=None, domaindn
=None, schemadn
=None,
1436 configdn
=None, serverdn
=None,
1437 domain
=None, hostname
=None, domainsid
=None,
1438 adminpass
=None, krbtgtpass
=None, domainguid
=None,
1439 policyguid
=None, policyguid_dc
=None, invocationid
=None,
1441 dnspass
=None, root
=None, nobody
=None, users
=None,
1442 wheel
=None, backup
=None, serverrole
=None,
1443 ldap_backend
=None, ldap_backend_type
=None,
1444 sitename
=None, debuglevel
=1):
1447 """print a message if quiet is not set."""
1450 glue
.set_debug_level(debuglevel
)
1452 return provision(setup_dir
, message
, system_session(), None,
1453 smbconf
=smbconf
, targetdir
=targetdir
, samdb_fill
=FILL_DRS
,
1454 realm
=realm
, rootdn
=rootdn
, domaindn
=domaindn
, schemadn
=schemadn
,
1455 configdn
=configdn
, serverdn
=serverdn
, domain
=domain
,
1456 hostname
=hostname
, hostip
="127.0.0.1", domainsid
=domainsid
,
1457 machinepass
=machinepass
, serverrole
="domain controller",
1461 def create_phpldapadmin_config(path
, setup_path
, ldapi_uri
):
1462 """Create a PHP LDAP admin configuration file.
1464 :param path: Path to write the configuration to.
1465 :param setup_path: Function to generate setup paths.
1467 setup_file(setup_path("phpldapadmin-config.php"), path
,
1468 {"S4_LDAPI_URI": ldapi_uri
})
1471 def create_zone_file(lp
, message
, paths
, targetdir
, setup_path
, dnsdomain
,
1472 hostip
, hostip6
, hostname
, realm
, domainguid
,
1474 """Write out a DNS zone file, from the info in the current database.
1476 :param paths: paths object
1477 :param setup_path: Setup path function.
1478 :param dnsdomain: DNS Domain name
1479 :param domaindn: DN of the Domain
1480 :param hostip: Local IPv4 IP
1481 :param hostip6: Local IPv6 IP
1482 :param hostname: Local hostname
1483 :param realm: Realm name
1484 :param domainguid: GUID of the domain.
1485 :param ntdsguid: GUID of the hosts nTDSDSA record.
1487 assert isinstance(domainguid
, str)
1489 if hostip6
is not None:
1490 hostip6_base_line
= " IN AAAA " + hostip6
1491 hostip6_host_line
= hostname
+ " IN AAAA " + hostip6
1493 hostip6_base_line
= ""
1494 hostip6_host_line
= ""
1496 if hostip
is not None:
1497 hostip_base_line
= " IN A " + hostip
1498 hostip_host_line
= hostname
+ " IN A " + hostip
1500 hostip_base_line
= ""
1501 hostip_host_line
= ""
1503 dns_dir
= os
.path
.dirname(paths
.dns
)
1506 shutil
.rmtree(dns_dir
, True)
1510 os
.mkdir(dns_dir
, 0775)
1512 # we need to freeze the zone while we update the contents
1513 if targetdir
is None:
1514 rndc
= ' '.join(lp
.get("rndc command"))
1515 os
.system(rndc
+ " freeze " + lp
.get("realm"))
1517 setup_file(setup_path("provision.zone"), paths
.dns
, {
1518 "HOSTNAME": hostname
,
1519 "DNSDOMAIN": dnsdomain
,
1521 "HOSTIP_BASE_LINE": hostip_base_line
,
1522 "HOSTIP_HOST_LINE": hostip_host_line
,
1523 "DOMAINGUID": domainguid
,
1524 "DATESTRING": time
.strftime("%Y%m%d%H"),
1525 "DEFAULTSITE": DEFAULTSITE
,
1526 "NTDSGUID": ntdsguid
,
1527 "HOSTIP6_BASE_LINE": hostip6_base_line
,
1528 "HOSTIP6_HOST_LINE": hostip6_host_line
,
1531 # note that we use no variable substitution on this file
1532 # the substitution is done at runtime by samba_dnsupdate
1533 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
1535 if paths
.bind_gid
is not None:
1537 os
.chown(dns_dir
, -1, paths
.bind_gid
)
1538 os
.chown(paths
.dns
, -1, paths
.bind_gid
)
1539 # chmod needed to cope with umask
1540 os
.chmod(dns_dir
, 0775)
1541 os
.chmod(paths
.dns
, 0664)
1543 message("Failed to chown %s to bind gid %u" % (dns_dir
, paths
.bind_gid
))
1545 if targetdir
is None:
1546 os
.system(rndc
+ " unfreeze " + lp
.get("realm"))
1549 def create_dns_update_list(lp
, message
, paths
, setup_path
):
1550 """Write out a dns_update_list file"""
1551 # note that we use no variable substitution on this file
1552 # the substitution is done at runtime by samba_dnsupdate
1553 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
1556 def create_named_conf(paths
, setup_path
, realm
, dnsdomain
,
1558 """Write out a file containing zone statements suitable for inclusion in a
1559 named.conf file (including GSS-TSIG configuration).
1561 :param paths: all paths
1562 :param setup_path: Setup path function.
1563 :param realm: Realm name
1564 :param dnsdomain: DNS Domain name
1565 :param private_dir: Path to private directory
1566 :param keytab_name: File name of DNS keytab file
1569 setup_file(setup_path("named.conf"), paths
.namedconf
, {
1570 "DNSDOMAIN": dnsdomain
,
1572 "ZONE_FILE": paths
.dns
,
1573 "REALM_WC": "*." + ".".join(realm
.split(".")[1:]),
1574 "NAMED_CONF": paths
.namedconf
,
1575 "NAMED_CONF_UPDATE": paths
.namedconf_update
1578 setup_file(setup_path("named.conf.update"), paths
.namedconf_update
)
1580 def create_named_txt(path
, setup_path
, realm
, dnsdomain
,
1581 private_dir
, keytab_name
):
1582 """Write out a file containing zone statements suitable for inclusion in a
1583 named.conf file (including GSS-TSIG configuration).
1585 :param path: Path of the new named.conf file.
1586 :param setup_path: Setup path function.
1587 :param realm: Realm name
1588 :param dnsdomain: DNS Domain name
1589 :param private_dir: Path to private directory
1590 :param keytab_name: File name of DNS keytab file
1593 setup_file(setup_path("named.txt"), path
, {
1594 "DNSDOMAIN": dnsdomain
,
1596 "DNS_KEYTAB": keytab_name
,
1597 "DNS_KEYTAB_ABS": os
.path
.join(private_dir
, keytab_name
),
1598 "PRIVATE_DIR": private_dir
1601 def create_krb5_conf(path
, setup_path
, dnsdomain
, hostname
, realm
):
1602 """Write out a file containing zone statements suitable for inclusion in a
1603 named.conf file (including GSS-TSIG configuration).
1605 :param path: Path of the new named.conf file.
1606 :param setup_path: Setup path function.
1607 :param dnsdomain: DNS Domain name
1608 :param hostname: Local hostname
1609 :param realm: Realm name
1612 setup_file(setup_path("krb5.conf"), path
, {
1613 "DNSDOMAIN": dnsdomain
,
1614 "HOSTNAME": hostname
,