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
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from base64 import b64encode
31 from socket import gethostname, gethostbyname
35 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
36 from samba.samdb import SamDB
39 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
40 LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
42 """Functions for setting up a Samba configuration."""
44 DEFAULTSITE = "Default-First-Site-Name"
46 class InvalidNetbiosName(Exception):
47 def __init__(self, name):
48 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
64 self.dns_keytab = None
67 self.ldap_basedn_ldif = None
68 self.ldap_config_basedn_ldif = None
69 self.ldap_schema_basedn_ldif = None
72 def check_install(lp, session_info, credentials):
73 """Check whether the current install seems ok.
75 :param lp: Loadparm context
76 :param session_info: Session information
77 :param credentials: Credentials
79 if lp.get("realm") == "":
80 raise Error("Realm empty")
81 ldb = Ldb(lp.get("sam database"), session_info=session_info,
82 credentials=credentials, lp=lp)
83 if len(ldb.search("(cn=Administrator)")) != 1:
84 raise "No administrator account found"
87 def findnss(nssfn, *names):
88 """Find a user or group from a list of possibilities."""
94 raise Exception("Unable to find user/group for %s" % arguments[1])
97 def open_ldb(session_info, credentials, lp, dbname):
98 """Open a LDB, thrashing it if it is corrupt.
100 :param session_info: auth session information
101 :param credentials: credentials
102 :param lp: Loadparm context
103 :param dbname: Path of the database to open.
104 :return: a Ldb object
106 assert session_info is not None
108 return Ldb(dbname, session_info=session_info, credentials=credentials,
113 return Ldb(dbname, session_info=session_info, credentials=credentials,
117 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
118 """Setup a ldb in the private dir.
120 :param ldb: LDB file to import data into
121 :param ldif_path: Path of the LDIF file to load
122 :param subst_vars: Optional variables to subsitute in LDIF.
124 assert isinstance(ldif_path, str)
126 data = open(ldif_path, 'r').read()
127 if subst_vars is not None:
128 data = substitute_var(data, subst_vars)
130 check_all_substituted(data)
135 def setup_modify_ldif(ldb, ldif_path, substvars=None):
136 """Modify a ldb in the private dir.
138 :param ldb: LDB object.
139 :param ldif_path: LDIF file path.
140 :param substvars: Optional dictionary with substitution variables.
142 data = open(ldif_path, 'r').read()
143 if substvars is not None:
144 data = substitute_var(data, substvars)
146 check_all_substituted(data)
148 ldb.modify_ldif(data)
151 def setup_ldb(ldb, ldif_path, subst_vars):
152 assert ldb is not None
153 ldb.transaction_start()
155 setup_add_ldif(ldb, ldif_path, subst_vars)
157 ldb.transaction_cancel()
159 ldb.transaction_commit()
162 def setup_file(template, fname, substvars):
163 """Setup a file in the private dir.
165 :param template: Path of the template file.
166 :param fname: Path of the file to create.
167 :param substvars: Substitution variables.
171 if os.path.exists(f):
174 data = open(template, 'r').read()
176 data = substitute_var(data, substvars)
177 check_all_substituted(data)
179 open(f, 'w').write(data)
182 def provision_paths_from_lp(lp, dnsdomain, private_dir=None):
183 """Set the default paths for provisioning.
185 :param lp: Loadparm context.
186 :param dnsdomain: DNS Domain name
188 paths = ProvisionPaths()
189 if private_dir is None:
190 private_dir = lp.get("private dir")
191 paths.keytab = "secrets.keytab"
192 paths.dns_keytab = "dns.keytab"
194 paths.keytab = os.path.join(private_dir, "secrets.keytab")
195 paths.dns_keytab = os.path.join(private_dir, "dns.keytab")
197 paths.shareconf = os.path.join(private_dir, "share.ldb")
198 paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
199 paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
200 paths.templates = os.path.join(private_dir, "templates.ldb")
201 paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
202 paths.winsdb = os.path.join(private_dir, "wins.ldb")
203 paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
204 paths.phpldapadminconfig = os.path.join(private_dir,
205 "phpldapadmin-config.php")
206 paths.hklm = os.path.join(private_dir, "hklm.ldb")
207 paths.sysvol = lp.get("sysvol", "path")
208 if paths.sysvol is None:
209 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
211 paths.netlogon = lp.get("netlogon", "path")
212 if paths.netlogon is None:
213 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
218 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
220 """setup reasonable name mappings for sam names to unix names.
222 :param ldb: SamDB object.
223 :param sid: The domain sid.
224 :param domaindn: The domain DN.
225 :param root: Name of the UNIX root user.
226 :param nobody: Name of the UNIX nobody user.
227 :param nogroup: Name of the unix nobody group.
228 :param users: Name of the unix users group.
229 :param wheel: Name of the wheel group (users that can become root).
230 :param backup: Name of the backup group."""
231 # add some foreign sids if they are not present already
232 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
233 ldb.add_foreign(domaindn, "S-1-1-0", "World")
234 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
235 ldb.add_foreign(domaindn, "S-1-5-18", "System")
236 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
238 # some well known sids
239 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
240 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
241 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
242 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
243 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
244 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
245 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
246 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
247 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
249 # and some well known domain rids
250 ldb.setup_name_mapping(domaindn, sid + "-500", root)
251 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
252 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
253 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
254 ldb.setup_name_mapping(domaindn, sid + "-513", users)
255 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
258 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
259 credentials, configdn, schemadn, domaindn,
260 hostname, netbiosname, dnsdomain, realm,
261 rootdn, serverrole, ldap_backend=None,
262 ldap_backend_type=None, erase=False):
263 """Setup the partitions for the SAM database.
265 Alternatively, provision() may call this, and then populate the database.
267 :param erase: Remove the existing data present in the database.
270 :note: This will wipe the Sam Database!
272 :note: This function always removes the local SAM LDB file. The erase
273 parameter controls whether to erase the existing data, which
274 may not be stored locally but in LDAP.
276 assert session_info is not None
278 if os.path.exists(samdb_path):
279 os.unlink(samdb_path)
281 # Also wipes the database
282 samdb = SamDB(samdb_path, session_info=session_info,
283 credentials=credentials, lp=lp)
285 #Add modules to the list to activate them by default
286 #beware often order is important
288 # Some Known ordering constraints:
289 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
290 # - objectclass must be before password_hash, because password_hash checks
291 # that the objectclass is of type person (filled in by objectclass
292 # module when expanding the objectclass list)
293 # - partition must be last
294 # - each partition has its own module list then
295 modules_list = ["rootdse",
311 modules_list2 = ["show_deleted",
314 domaindn_ldb = "users.ldb"
315 if ldap_backend is not None:
316 domaindn_ldb = ldap_backend
317 configdn_ldb = "configuration.ldb"
318 if ldap_backend is not None:
319 configdn_ldb = ldap_backend
320 schema_ldb = "schema.ldb"
321 if ldap_backend is not None:
322 schema_ldb = ldap_backend
324 if ldap_backend_type == "fedora-ds":
325 backend_modules = ["nsuniqueid","paged_searches"]
326 elif ldap_backend_type == "openldap":
327 backend_modules = ["normalise","entryuuid","paged_searches"]
328 elif serverrole == "domain controller":
329 backend_modules = ["repl_meta_data"]
331 backend_modules = ["objectguid"]
333 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
334 "SCHEMADN": schemadn,
335 "SCHEMADN_LDB": "schema.ldb",
336 "SCHEMADN_MOD2": ",objectguid",
337 "CONFIGDN": configdn,
338 "CONFIGDN_LDB": "configuration.ldb",
339 "DOMAINDN": domaindn,
340 "DOMAINDN_LDB": "users.ldb",
341 "SCHEMADN_MOD": "schema_fsmo,instancetype",
342 "CONFIGDN_MOD": "naming_fsmo,instancetype",
343 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
344 "MODULES_LIST": ",".join(modules_list),
345 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
346 "MODULES_LIST2": ",".join(modules_list2),
347 "BACKEND_MOD": ",".join(backend_modules),
350 samdb = SamDB(samdb_path, session_info=session_info,
351 credentials=credentials, lp=lp)
353 samdb.transaction_start()
355 message("Setting up sam.ldb attributes")
356 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
358 message("Setting up sam.ldb rootDSE")
359 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
360 dnsdomain, realm, rootdn, configdn, netbiosname)
363 message("Erasing data from partitions")
364 samdb.erase_partitions()
367 samdb.transaction_cancel()
370 samdb.transaction_commit()
375 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
376 netbiosname, domainsid, keytab_path, samdb_url,
377 dns_keytab_path, dnspass, machinepass):
378 """Add DC-specific bits to a secrets database.
380 :param secretsdb: Ldb Handle to the secrets database
381 :param setup_path: Setup path function
382 :param machinepass: Machine password
384 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
385 "MACHINEPASS_B64": b64encode(machinepass),
388 "DNSDOMAIN": dnsdomain,
389 "DOMAINSID": str(domainsid),
390 "SECRETS_KEYTAB": keytab_path,
391 "NETBIOSNAME": netbiosname,
392 "SAM_LDB": samdb_url,
393 "DNS_KEYTAB": dns_keytab_path,
394 "DNSPASS_B64": b64encode(dnspass),
398 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
399 """Setup the secrets database.
401 :param path: Path to the secrets database.
402 :param setup_path: Get the path to a setup file.
403 :param session_info: Session info.
404 :param credentials: Credentials
405 :param lp: Loadparm context
406 :return: LDB handle for the created secrets database
408 if os.path.exists(path):
410 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
413 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
414 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
416 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
420 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
421 """Setup the templates database.
423 :param path: Path to the database.
424 :param setup_path: Function for obtaining the path to setup files.
425 :param session_info: Session info
426 :param credentials: Credentials
427 :param lp: Loadparm context
429 templates_ldb = SamDB(path, session_info=session_info,
430 credentials=credentials, lp=lp)
431 templates_ldb.erase()
432 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
435 def setup_registry(path, setup_path, session_info, credentials, lp):
436 """Setup the registry.
438 :param path: Path to the registry database
439 :param setup_path: Function that returns the path to a setup.
440 :param session_info: Session information
441 :param credentials: Credentials
442 :param lp: Loadparm context
444 reg = registry.Registry()
445 hive = registry.open_ldb(path, session_info=session_info,
446 credentials=credentials, lp_ctx=lp)
447 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
448 provision_reg = setup_path("provision.reg")
449 assert os.path.exists(provision_reg)
450 reg.diff_apply(provision_reg)
453 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
454 dnsdomain, realm, rootdn, configdn, netbiosname):
455 """Setup the SamDB rootdse.
457 :param samdb: Sam Database handle
458 :param setup_path: Obtain setup path
461 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
462 "SCHEMADN": schemadn,
463 "NETBIOSNAME": netbiosname,
464 "DNSDOMAIN": dnsdomain,
465 "DEFAULTSITE": DEFAULTSITE,
467 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
468 "DOMAINDN": domaindn,
470 "CONFIGDN": configdn,
471 "VERSION": samba.version(),
475 def setup_self_join(samdb, configdn, schemadn, domaindn,
476 netbiosname, hostname, dnsdomain, machinepass, dnspass,
477 realm, domainname, domainsid, invocationid, setup_path,
478 policyguid, hostguid=None):
479 """Join a host to its own domain."""
480 if hostguid is not None:
481 hostguid_add = "objectGUID: %s" % hostguid
485 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
486 "CONFIGDN": configdn,
487 "SCHEMADN": schemadn,
488 "DOMAINDN": domaindn,
489 "INVOCATIONID": invocationid,
490 "NETBIOSNAME": netbiosname,
491 "DEFAULTSITE": DEFAULTSITE,
492 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
493 "MACHINEPASS_B64": b64encode(machinepass),
494 "DNSPASS_B64": b64encode(dnspass),
496 "DOMAIN": domainname,
497 "HOSTGUID_ADD": hostguid_add,
498 "DNSDOMAIN": dnsdomain})
499 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
500 "POLICYGUID": policyguid,
501 "DNSDOMAIN": dnsdomain,
502 "DOMAINSID": str(domainsid),
503 "DOMAINDN": domaindn})
506 def setup_samdb(path, setup_path, session_info, credentials, lp,
507 schemadn, configdn, domaindn, dnsdomain, realm,
508 netbiosname, message, hostname, rootdn, erase,
509 domainsid, aci, domainguid, policyguid,
510 domainname, fill, adminpass, krbtgtpass,
511 machinepass, hostguid, invocationid, dnspass,
512 serverrole, ldap_backend=None, ldap_backend_type=None):
513 """Setup a complete SAM Database.
517 # Also wipes the database
518 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
519 domaindn=domaindn, message=message, lp=lp,
520 credentials=credentials, session_info=session_info,
521 hostname=hostname, netbiosname=netbiosname,
522 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
523 ldap_backend=ldap_backend, serverrole=serverrole,
524 ldap_backend_type=ldap_backend_type, erase=erase)
526 samdb = SamDB(path, session_info=session_info,
527 credentials=credentials, lp=lp)
530 # We want to finish here, but setup the index before we do so
531 message("Setting up sam.ldb index")
532 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
535 message("Pre-loading the Samba 4 and AD schema")
536 samdb = SamDB(path, session_info=session_info,
537 credentials=credentials, lp=lp)
538 samdb.set_domain_sid(domainsid)
539 if lp.get("server role") == "domain controller":
540 samdb.set_invocation_id(invocationid)
542 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
544 samdb.transaction_start()
547 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
548 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
549 "DOMAINDN": domaindn,
553 message("Modifying DomainDN: " + domaindn + "")
554 if domainguid is not None:
555 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
559 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
560 "LDAPTIME": timestring(int(time.time())),
561 "DOMAINSID": str(domainsid),
562 "SCHEMADN": schemadn,
563 "NETBIOSNAME": netbiosname,
564 "DEFAULTSITE": DEFAULTSITE,
565 "CONFIGDN": configdn,
566 "POLICYGUID": policyguid,
567 "DOMAINDN": domaindn,
568 "DOMAINGUID_MOD": domainguid_mod,
571 message("Adding configuration container (permitted to fail)")
572 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
573 "CONFIGDN": configdn,
575 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
577 message("Modifying configuration container")
578 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
579 "CONFIGDN": configdn,
580 "SCHEMADN": schemadn,
583 message("Adding schema container (permitted to fail)")
584 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
585 "SCHEMADN": schemadn,
587 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
589 message("Modifying schema container")
590 setup_modify_ldif(samdb,
591 setup_path("provision_schema_basedn_modify.ldif"), {
592 "SCHEMADN": schemadn,
593 "NETBIOSNAME": netbiosname,
594 "DEFAULTSITE": DEFAULTSITE,
595 "CONFIGDN": configdn,
598 message("Setting up sam.ldb Samba4 schema")
599 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
600 {"SCHEMADN": schemadn })
601 message("Setting up sam.ldb AD schema")
602 setup_add_ldif(samdb, setup_path("schema.ldif"),
603 {"SCHEMADN": schemadn})
605 message("Setting up sam.ldb configuration data")
606 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
607 "CONFIGDN": configdn,
608 "NETBIOSNAME": netbiosname,
609 "DEFAULTSITE": DEFAULTSITE,
610 "DNSDOMAIN": dnsdomain,
611 "DOMAIN": domainname,
612 "SCHEMADN": schemadn,
613 "DOMAINDN": domaindn,
616 message("Setting up display specifiers")
617 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
618 {"CONFIGDN": configdn})
620 message("Adding users container (permitted to fail)")
621 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
622 "DOMAINDN": domaindn})
623 message("Modifying users container")
624 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
625 "DOMAINDN": domaindn})
626 message("Adding computers container (permitted to fail)")
627 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
628 "DOMAINDN": domaindn})
629 message("Modifying computers container")
630 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
631 "DOMAINDN": domaindn})
632 message("Setting up sam.ldb data")
633 setup_add_ldif(samdb, setup_path("provision.ldif"), {
634 "DOMAINDN": domaindn,
635 "NETBIOSNAME": netbiosname,
636 "DEFAULTSITE": DEFAULTSITE,
637 "CONFIGDN": configdn,
640 if fill == FILL_FULL:
641 message("Setting up sam.ldb users and groups")
642 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
643 "DOMAINDN": domaindn,
644 "DOMAINSID": str(domainsid),
645 "CONFIGDN": configdn,
646 "ADMINPASS_B64": b64encode(adminpass),
647 "KRBTGTPASS_B64": b64encode(krbtgtpass),
650 if lp.get("server role") == "domain controller":
651 message("Setting up self join")
652 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
653 domaindn=domaindn, invocationid=invocationid,
654 dnspass=dnspass, netbiosname=netbiosname,
655 dnsdomain=dnsdomain, realm=realm,
656 machinepass=machinepass, domainname=domainname,
657 domainsid=domainsid, policyguid=policyguid,
658 hostname=hostname, hostguid=hostguid,
659 setup_path=setup_path)
661 #We want to setup the index last, as adds are faster unindexed
662 message("Setting up sam.ldb index")
663 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
665 samdb.transaction_cancel()
668 samdb.transaction_commit()
672 FILL_NT4SYNC = "NT4SYNC"
675 def provision(lp, setup_dir, message, paths, session_info,
676 credentials, ldapbackend, samdb_fill=FILL_FULL, realm=None, rootdn=None,
677 domain=None, hostname=None, hostip=None, domainsid=None,
678 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
679 policyguid=None, invocationid=None, machinepass=None,
680 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
681 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
682 ldap_backend=None, ldap_backend_type=None):
685 :note: caution, this wipes all existing data!
688 def setup_path(file):
689 return os.path.join(setup_dir, file)
691 if domainsid is None:
692 domainsid = security.random_sid()
693 if policyguid is None:
694 policyguid = uuid.random()
695 if adminpass is None:
696 adminpass = misc.random_password(12)
697 if krbtgtpass is None:
698 krbtgtpass = misc.random_password(12)
699 if machinepass is None:
700 machinepass = misc.random_password(12)
702 dnspass = misc.random_password(12)
704 root = findnss(pwd.getpwnam, "root")[0]
706 nobody = findnss(pwd.getpwnam, "nobody")[0]
708 nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[0]
710 users = findnss(grp.getgrnam, "users", "guest", "other", "unknown",
713 wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[0]
715 backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[0]
717 aci = "# no aci for local ldb"
718 if serverrole is None:
719 serverrole = lp.get("server role")
720 if invocationid is None and serverrole == "domain controller":
721 invocationid = uuid.random()
724 realm = lp.get("realm")
726 if lp.get("realm").upper() != realm.upper():
727 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
728 (lp.get("realm"), realm))
730 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
732 if ldap_backend == "ldapi":
733 # provision-backend will set this path suggested slapd command line / fedorads.inf
734 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
736 assert realm is not None
737 realm = realm.upper()
740 hostname = gethostname().split(".")[0].lower()
743 hostip = gethostbyname(hostname)
745 netbiosname = hostname.upper()
746 if not valid_netbios_name(netbiosname):
747 raise InvalidNetbiosName(netbiosname)
749 dnsdomain = realm.lower()
750 if serverrole == "domain controller":
751 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
753 domain = lp.get("workgroup")
755 if lp.get("workgroup").upper() != domain.upper():
756 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
757 lp.get("workgroup"), domain)
759 assert domain is not None
760 domain = domain.upper()
761 if not valid_netbios_name(domain):
762 raise InvalidNetbiosName(domain)
765 domaindn = "CN=" + netbiosname
771 configdn = "CN=Configuration," + rootdn
772 schemadn = "CN=Schema," + configdn
774 message("set DOMAIN SID: %s" % str(domainsid))
775 message("Provisioning for %s in realm %s" % (domain, realm))
776 message("Using administrator password: %s" % adminpass)
778 assert paths.smbconf is not None
780 # only install a new smb.conf if there isn't one there already
781 if not os.path.exists(paths.smbconf):
782 message("Setting up smb.conf")
783 if serverrole == "domain controller":
785 elif serverrole == "member":
786 smbconfsuffix = "member"
788 assert "Invalid server role setting: %s" % serverrole
789 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
791 "HOSTNAME": hostname,
792 "DOMAIN_CONF": domain,
794 "SERVERROLE": serverrole,
795 "NETLOGONPATH": paths.netlogon,
796 "SYSVOLPATH": paths.sysvol,
800 # only install a new shares config db if there is none
801 if not os.path.exists(paths.shareconf):
802 message("Setting up share.ldb")
803 share_ldb = Ldb(paths.shareconf, session_info=session_info,
804 credentials=credentials, lp=lp)
805 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
808 message("Setting up secrets.ldb")
809 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
810 session_info=session_info,
811 credentials=credentials, lp=lp)
813 message("Setting up the registry")
814 setup_registry(paths.hklm, setup_path, session_info,
815 credentials=credentials, lp=lp)
817 message("Setting up templates db")
818 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
819 credentials=credentials, lp=lp)
821 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
822 credentials=credentials, lp=lp, schemadn=schemadn,
823 configdn=configdn, domaindn=domaindn,
824 dnsdomain=dnsdomain, netbiosname=netbiosname,
825 realm=realm, message=message, hostname=hostname,
826 rootdn=rootdn, erase=erase, domainsid=domainsid,
827 aci=aci, domainguid=domainguid, policyguid=policyguid,
828 domainname=domain, fill=samdb_fill,
829 adminpass=adminpass, krbtgtpass=krbtgtpass,
830 hostguid=hostguid, invocationid=invocationid,
831 machinepass=machinepass, dnspass=dnspass,
832 serverrole=serverrole, ldap_backend=ldap_backend,
833 ldap_backend_type=ldap_backend_type)
835 if lp.get("server role") == "domain controller":
836 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
837 "{" + policyguid + "}")
838 os.makedirs(policy_path, 0755)
839 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
840 os.makedirs(os.path.join(policy_path, "User"), 0755)
841 if not os.path.isdir(paths.netlogon):
842 os.makedirs(paths.netlogon, 0755)
843 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
844 credentials=credentials, lp=lp)
845 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
846 netbiosname=netbiosname, domainsid=domainsid,
847 keytab_path=paths.keytab, samdb_url=paths.samdb,
848 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
849 machinepass=machinepass, dnsdomain=dnsdomain)
851 if samdb_fill == FILL_FULL:
852 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
853 nobody=nobody, nogroup=nogroup, wheel=wheel,
854 users=users, backup=backup)
856 message("Setting up sam.ldb rootDSE marking as synchronized")
857 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
859 message("Setting up phpLDAPadmin configuration")
860 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
863 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
865 if lp.get("server role") == "domain controller":
866 samdb = SamDB(paths.samdb, session_info=session_info,
867 credentials=credentials, lp=lp)
869 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
870 assert isinstance(domainguid, str)
871 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
872 expression="(&(objectClass=computer)(cn=%s))" % hostname,
874 assert isinstance(hostguid, str)
876 message("Setting up DNS zone: %s" % dnsdomain)
877 create_zone_file(paths.dns, setup_path, samdb,
878 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
879 domaindn=domaindn, dnspass=dnspass, realm=realm,
880 domainguid=domainguid, hostguid=hostguid)
881 message("Please install the zone located in %s into your DNS server" % paths.dns)
886 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
887 """Create a PHP LDAP admin configuration file.
889 :param path: Path to write the configuration to.
890 :param setup_path: Function to generate setup paths.
892 setup_file(setup_path("phpldapadmin-config.php"), path,
893 {"S4_LDAPI_URI": ldapi_uri})
896 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
897 hostip, hostname, dnspass, realm, domainguid, hostguid):
898 """Write out a DNS zone file, from the info in the current database.
900 :param path: Path of the new file.
901 :param setup_path": Setup path function.
902 :param samdb: SamDB object
903 :param dnsdomain: DNS Domain name
904 :param domaindn: DN of the Domain
905 :param hostip: Local IP
906 :param hostname: Local hostname
907 :param dnspass: Password for DNS
908 :param realm: Realm name
909 :param domainguid: GUID of the domain.
910 :param hostguid: GUID of the host.
912 assert isinstance(domainguid, str)
914 setup_file(setup_path("provision.zone"), path, {
915 "DNSPASS_B64": b64encode(dnspass),
916 "HOSTNAME": hostname,
917 "DNSDOMAIN": dnsdomain,
920 "DOMAINGUID": domainguid,
921 "DATESTRING": time.strftime("%Y%m%d%H"),
922 "DEFAULTSITE": DEFAULTSITE,
923 "HOSTGUID": hostguid,
927 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
930 :param samdb: Load a schema into a SamDB.
931 :param setup_path: Setup path function.
932 :param schemadn: DN of the schema
933 :param netbiosname: NetBIOS name of the host.
934 :param configdn: DN of the configuration
936 schema_data = open(setup_path("schema.ldif"), 'r').read()
937 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
938 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
939 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
940 head_data = substitute_var(head_data, {
941 "SCHEMADN": schemadn,
942 "NETBIOSNAME": netbiosname,
943 "CONFIGDN": configdn,
944 "DEFAULTSITE": DEFAULTSITE
946 samdb.attach_schema_from_ldif(head_data, schema_data)