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")
202 paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
203 paths.winsdb = os.path.join(private_dir, "wins.ldb")
204 paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
205 paths.phpldapadminconfig = os.path.join(private_dir,
206 "phpldapadmin-config.php")
207 paths.hklm = os.path.join(private_dir, "hklm.ldb")
208 paths.sysvol = lp.get("sysvol", "path")
209 if paths.sysvol is None:
210 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
212 paths.netlogon = lp.get("netlogon", "path")
213 if paths.netlogon is None:
214 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
219 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
221 """setup reasonable name mappings for sam names to unix names.
223 :param ldb: SamDB object.
224 :param sid: The domain sid.
225 :param domaindn: The domain DN.
226 :param root: Name of the UNIX root user.
227 :param nobody: Name of the UNIX nobody user.
228 :param nogroup: Name of the unix nobody group.
229 :param users: Name of the unix users group.
230 :param wheel: Name of the wheel group (users that can become root).
231 :param backup: Name of the backup group."""
232 # add some foreign sids if they are not present already
233 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
234 ldb.add_foreign(domaindn, "S-1-1-0", "World")
235 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
236 ldb.add_foreign(domaindn, "S-1-5-18", "System")
237 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
239 # some well known sids
240 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
241 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
242 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
243 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
244 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
245 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
246 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
247 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
248 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
250 # and some well known domain rids
251 ldb.setup_name_mapping(domaindn, sid + "-500", root)
252 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
253 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
254 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
255 ldb.setup_name_mapping(domaindn, sid + "-513", users)
256 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
259 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
260 credentials, configdn, schemadn, domaindn,
261 hostname, netbiosname, dnsdomain, realm,
262 rootdn, serverrole, ldap_backend=None,
263 ldap_backend_type=None, erase=False):
264 """Setup the partitions for the SAM database.
266 Alternatively, provision() may call this, and then populate the database.
268 :param erase: Remove the existing data present in the database.
271 :note: This will wipe the Sam Database!
273 :note: This function always removes the local SAM LDB file. The erase
274 parameter controls whether to erase the existing data, which
275 may not be stored locally but in LDAP.
277 assert session_info is not None
279 if os.path.exists(samdb_path):
280 os.unlink(samdb_path)
282 # Also wipes the database
283 samdb = SamDB(samdb_path, session_info=session_info,
284 credentials=credentials, lp=lp)
286 #Add modules to the list to activate them by default
287 #beware often order is important
289 # Some Known ordering constraints:
290 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
291 # - objectclass must be before password_hash, because password_hash checks
292 # that the objectclass is of type person (filled in by objectclass
293 # module when expanding the objectclass list)
294 # - partition must be last
295 # - each partition has its own module list then
296 modules_list = ["rootdse",
312 modules_list2 = ["show_deleted",
315 domaindn_ldb = "users.ldb"
316 if ldap_backend is not None:
317 domaindn_ldb = ldap_backend
318 configdn_ldb = "configuration.ldb"
319 if ldap_backend is not None:
320 configdn_ldb = ldap_backend
321 schema_ldb = "schema.ldb"
322 if ldap_backend is not None:
323 schema_ldb = ldap_backend
325 if ldap_backend_type == "fedora-ds":
326 backend_modules = ["nsuniqueid","paged_searches"]
327 elif ldap_backend_type == "openldap":
328 backend_modules = ["normalise","entryuuid","paged_searches"]
329 elif serverrole == "domain controller":
330 backend_modules = ["repl_meta_data"]
332 backend_modules = ["objectguid"]
334 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
335 "SCHEMADN": schemadn,
336 "SCHEMADN_LDB": "schema.ldb",
337 "SCHEMADN_MOD2": ",objectguid",
338 "CONFIGDN": configdn,
339 "CONFIGDN_LDB": "configuration.ldb",
340 "DOMAINDN": domaindn,
341 "DOMAINDN_LDB": "users.ldb",
342 "SCHEMADN_MOD": "schema_fsmo,instancetype",
343 "CONFIGDN_MOD": "naming_fsmo,instancetype",
344 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
345 "MODULES_LIST": ",".join(modules_list),
346 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
347 "MODULES_LIST2": ",".join(modules_list2),
348 "BACKEND_MOD": ",".join(backend_modules),
351 samdb = SamDB(samdb_path, session_info=session_info,
352 credentials=credentials, lp=lp)
354 samdb.transaction_start()
356 message("Setting up sam.ldb attributes")
357 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
359 message("Setting up sam.ldb rootDSE")
360 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
361 dnsdomain, realm, rootdn, configdn, netbiosname)
364 message("Erasing data from partitions")
365 samdb.erase_partitions()
368 samdb.transaction_cancel()
371 samdb.transaction_commit()
376 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
377 netbiosname, domainsid, keytab_path, samdb_url,
378 dns_keytab_path, dnspass, machinepass):
379 """Add DC-specific bits to a secrets database.
381 :param secretsdb: Ldb Handle to the secrets database
382 :param setup_path: Setup path function
383 :param machinepass: Machine password
385 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
386 "MACHINEPASS_B64": b64encode(machinepass),
389 "DNSDOMAIN": dnsdomain,
390 "DOMAINSID": str(domainsid),
391 "SECRETS_KEYTAB": keytab_path,
392 "NETBIOSNAME": netbiosname,
393 "SAM_LDB": samdb_url,
394 "DNS_KEYTAB": dns_keytab_path,
395 "DNSPASS_B64": b64encode(dnspass),
399 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
400 """Setup the secrets database.
402 :param path: Path to the secrets database.
403 :param setup_path: Get the path to a setup file.
404 :param session_info: Session info.
405 :param credentials: Credentials
406 :param lp: Loadparm context
407 :return: LDB handle for the created secrets database
409 if os.path.exists(path):
411 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
414 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
415 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
417 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
421 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
422 """Setup the templates database.
424 :param path: Path to the database.
425 :param setup_path: Function for obtaining the path to setup files.
426 :param session_info: Session info
427 :param credentials: Credentials
428 :param lp: Loadparm context
430 templates_ldb = SamDB(path, session_info=session_info,
431 credentials=credentials, lp=lp)
432 templates_ldb.erase()
433 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
436 def setup_registry(path, setup_path, session_info, credentials, lp):
437 """Setup the registry.
439 :param path: Path to the registry database
440 :param setup_path: Function that returns the path to a setup.
441 :param session_info: Session information
442 :param credentials: Credentials
443 :param lp: Loadparm context
445 reg = registry.Registry()
446 hive = registry.open_ldb(path, session_info=session_info,
447 credentials=credentials, lp_ctx=lp)
448 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
449 provision_reg = setup_path("provision.reg")
450 assert os.path.exists(provision_reg)
451 reg.diff_apply(provision_reg)
454 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
455 dnsdomain, realm, rootdn, configdn, netbiosname):
456 """Setup the SamDB rootdse.
458 :param samdb: Sam Database handle
459 :param setup_path: Obtain setup path
462 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
463 "SCHEMADN": schemadn,
464 "NETBIOSNAME": netbiosname,
465 "DNSDOMAIN": dnsdomain,
466 "DEFAULTSITE": DEFAULTSITE,
468 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
469 "DOMAINDN": domaindn,
471 "CONFIGDN": configdn,
472 "VERSION": samba.version(),
476 def setup_self_join(samdb, configdn, schemadn, domaindn,
477 netbiosname, hostname, dnsdomain, machinepass, dnspass,
478 realm, domainname, domainsid, invocationid, setup_path,
479 policyguid, hostguid=None):
480 """Join a host to its own domain."""
481 if hostguid is not None:
482 hostguid_add = "objectGUID: %s" % hostguid
486 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
487 "CONFIGDN": configdn,
488 "SCHEMADN": schemadn,
489 "DOMAINDN": domaindn,
490 "INVOCATIONID": invocationid,
491 "NETBIOSNAME": netbiosname,
492 "DEFAULTSITE": DEFAULTSITE,
493 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
494 "MACHINEPASS_B64": b64encode(machinepass),
495 "DNSPASS_B64": b64encode(dnspass),
497 "DOMAIN": domainname,
498 "HOSTGUID_ADD": hostguid_add,
499 "DNSDOMAIN": dnsdomain})
500 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
501 "POLICYGUID": policyguid,
502 "DNSDOMAIN": dnsdomain,
503 "DOMAINSID": str(domainsid),
504 "DOMAINDN": domaindn})
507 def setup_samdb(path, setup_path, session_info, credentials, lp,
508 schemadn, configdn, domaindn, dnsdomain, realm,
509 netbiosname, message, hostname, rootdn, erase,
510 domainsid, aci, domainguid, policyguid,
511 domainname, fill, adminpass, krbtgtpass,
512 machinepass, hostguid, invocationid, dnspass,
513 serverrole, ldap_backend=None, ldap_backend_type=None):
514 """Setup a complete SAM Database.
518 # Also wipes the database
519 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
520 domaindn=domaindn, message=message, lp=lp,
521 credentials=credentials, session_info=session_info,
522 hostname=hostname, netbiosname=netbiosname,
523 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
524 ldap_backend=ldap_backend, serverrole=serverrole,
525 ldap_backend_type=ldap_backend_type, erase=erase)
527 samdb = SamDB(path, session_info=session_info,
528 credentials=credentials, lp=lp)
531 # We want to finish here, but setup the index before we do so
532 message("Setting up sam.ldb index")
533 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
536 message("Pre-loading the Samba 4 and AD schema")
537 samdb = SamDB(path, session_info=session_info,
538 credentials=credentials, lp=lp)
539 samdb.set_domain_sid(domainsid)
540 if lp.get("server role") == "domain controller":
541 samdb.set_invocation_id(invocationid)
543 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
545 samdb.transaction_start()
548 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
549 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
550 "DOMAINDN": domaindn,
554 message("Modifying DomainDN: " + domaindn + "")
555 if domainguid is not None:
556 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
560 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
561 "LDAPTIME": timestring(int(time.time())),
562 "DOMAINSID": str(domainsid),
563 "SCHEMADN": schemadn,
564 "NETBIOSNAME": netbiosname,
565 "DEFAULTSITE": DEFAULTSITE,
566 "CONFIGDN": configdn,
567 "POLICYGUID": policyguid,
568 "DOMAINDN": domaindn,
569 "DOMAINGUID_MOD": domainguid_mod,
572 message("Adding configuration container (permitted to fail)")
573 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
574 "CONFIGDN": configdn,
576 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
578 message("Modifying configuration container")
579 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
580 "CONFIGDN": configdn,
581 "SCHEMADN": schemadn,
584 message("Adding schema container (permitted to fail)")
585 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
586 "SCHEMADN": schemadn,
588 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
590 message("Modifying schema container")
591 setup_modify_ldif(samdb,
592 setup_path("provision_schema_basedn_modify.ldif"), {
593 "SCHEMADN": schemadn,
594 "NETBIOSNAME": netbiosname,
595 "DEFAULTSITE": DEFAULTSITE,
596 "CONFIGDN": configdn,
599 message("Setting up sam.ldb Samba4 schema")
600 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
601 {"SCHEMADN": schemadn })
602 message("Setting up sam.ldb AD schema")
603 setup_add_ldif(samdb, setup_path("schema.ldif"),
604 {"SCHEMADN": schemadn})
606 message("Setting up sam.ldb configuration data")
607 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
608 "CONFIGDN": configdn,
609 "NETBIOSNAME": netbiosname,
610 "DEFAULTSITE": DEFAULTSITE,
611 "DNSDOMAIN": dnsdomain,
612 "DOMAIN": domainname,
613 "SCHEMADN": schemadn,
614 "DOMAINDN": domaindn,
617 message("Setting up display specifiers")
618 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
619 {"CONFIGDN": configdn})
621 message("Adding users container (permitted to fail)")
622 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
623 "DOMAINDN": domaindn})
624 message("Modifying users container")
625 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
626 "DOMAINDN": domaindn})
627 message("Adding computers container (permitted to fail)")
628 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
629 "DOMAINDN": domaindn})
630 message("Modifying computers container")
631 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
632 "DOMAINDN": domaindn})
633 message("Setting up sam.ldb data")
634 setup_add_ldif(samdb, setup_path("provision.ldif"), {
635 "DOMAINDN": domaindn,
636 "NETBIOSNAME": netbiosname,
637 "DEFAULTSITE": DEFAULTSITE,
638 "CONFIGDN": configdn,
641 if fill == FILL_FULL:
642 message("Setting up sam.ldb users and groups")
643 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
644 "DOMAINDN": domaindn,
645 "DOMAINSID": str(domainsid),
646 "CONFIGDN": configdn,
647 "ADMINPASS_B64": b64encode(adminpass),
648 "KRBTGTPASS_B64": b64encode(krbtgtpass),
651 if lp.get("server role") == "domain controller":
652 message("Setting up self join")
653 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
654 domaindn=domaindn, invocationid=invocationid,
655 dnspass=dnspass, netbiosname=netbiosname,
656 dnsdomain=dnsdomain, realm=realm,
657 machinepass=machinepass, domainname=domainname,
658 domainsid=domainsid, policyguid=policyguid,
659 hostname=hostname, hostguid=hostguid,
660 setup_path=setup_path)
662 #We want to setup the index last, as adds are faster unindexed
663 message("Setting up sam.ldb index")
664 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
666 samdb.transaction_cancel()
669 samdb.transaction_commit()
673 FILL_NT4SYNC = "NT4SYNC"
676 def provision(lp, setup_dir, message, paths, session_info,
677 credentials, ldapbackend, samdb_fill=FILL_FULL, realm=None, rootdn=None,
678 domain=None, hostname=None, hostip=None, domainsid=None,
679 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
680 policyguid=None, invocationid=None, machinepass=None,
681 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
682 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
683 ldap_backend=None, ldap_backend_type=None):
686 :note: caution, this wipes all existing data!
689 def setup_path(file):
690 return os.path.join(setup_dir, file)
692 if domainsid is None:
693 domainsid = security.random_sid()
694 if policyguid is None:
695 policyguid = uuid.random()
696 if adminpass is None:
697 adminpass = misc.random_password(12)
698 if krbtgtpass is None:
699 krbtgtpass = misc.random_password(12)
700 if machinepass is None:
701 machinepass = misc.random_password(12)
703 dnspass = misc.random_password(12)
705 root = findnss(pwd.getpwnam, "root")[0]
707 nobody = findnss(pwd.getpwnam, "nobody")[0]
709 nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[0]
711 users = findnss(grp.getgrnam, "users", "guest", "other", "unknown",
714 wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[0]
716 backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[0]
718 aci = "# no aci for local ldb"
719 if serverrole is None:
720 serverrole = lp.get("server role")
721 if invocationid is None and serverrole == "domain controller":
722 invocationid = uuid.random()
725 realm = lp.get("realm")
727 if lp.get("realm").upper() != realm.upper():
728 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
729 (lp.get("realm"), realm))
731 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
733 if ldap_backend == "ldapi":
734 # provision-backend will set this path suggested slapd command line / fedorads.inf
735 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
737 assert realm is not None
738 realm = realm.upper()
741 hostname = gethostname().split(".")[0].lower()
744 hostip = gethostbyname(hostname)
746 netbiosname = hostname.upper()
747 if not valid_netbios_name(netbiosname):
748 raise InvalidNetbiosName(netbiosname)
750 dnsdomain = realm.lower()
751 if serverrole == "domain controller":
752 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
754 domain = lp.get("workgroup")
756 if lp.get("workgroup").upper() != domain.upper():
757 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
758 lp.get("workgroup"), domain)
760 assert domain is not None
761 domain = domain.upper()
762 if not valid_netbios_name(domain):
763 raise InvalidNetbiosName(domain)
766 domaindn = "CN=" + netbiosname
772 configdn = "CN=Configuration," + rootdn
773 schemadn = "CN=Schema," + configdn
775 message("set DOMAIN SID: %s" % str(domainsid))
776 message("Provisioning for %s in realm %s" % (domain, realm))
777 message("Using administrator password: %s" % adminpass)
779 assert paths.smbconf is not None
781 # only install a new smb.conf if there isn't one there already
782 if not os.path.exists(paths.smbconf):
783 message("Setting up smb.conf")
784 if serverrole == "domain controller":
786 elif serverrole == "member":
787 smbconfsuffix = "member"
789 assert "Invalid server role setting: %s" % serverrole
790 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
792 "HOSTNAME": hostname,
793 "DOMAIN_CONF": domain,
795 "SERVERROLE": serverrole,
796 "NETLOGONPATH": paths.netlogon,
797 "SYSVOLPATH": paths.sysvol,
801 # only install a new shares config db if there is none
802 if not os.path.exists(paths.shareconf):
803 message("Setting up share.ldb")
804 share_ldb = Ldb(paths.shareconf, session_info=session_info,
805 credentials=credentials, lp=lp)
806 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
809 message("Setting up secrets.ldb")
810 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
811 session_info=session_info,
812 credentials=credentials, lp=lp)
814 message("Setting up the registry")
815 setup_registry(paths.hklm, setup_path, session_info,
816 credentials=credentials, lp=lp)
818 message("Setting up templates db")
819 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
820 credentials=credentials, lp=lp)
822 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
823 credentials=credentials, lp=lp, schemadn=schemadn,
824 configdn=configdn, domaindn=domaindn,
825 dnsdomain=dnsdomain, netbiosname=netbiosname,
826 realm=realm, message=message, hostname=hostname,
827 rootdn=rootdn, erase=erase, domainsid=domainsid,
828 aci=aci, domainguid=domainguid, policyguid=policyguid,
829 domainname=domain, fill=samdb_fill,
830 adminpass=adminpass, krbtgtpass=krbtgtpass,
831 hostguid=hostguid, invocationid=invocationid,
832 machinepass=machinepass, dnspass=dnspass,
833 serverrole=serverrole, ldap_backend=ldap_backend,
834 ldap_backend_type=ldap_backend_type)
836 if lp.get("server role") == "domain controller":
837 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
838 "{" + policyguid + "}")
839 os.makedirs(policy_path, 0755)
840 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
841 os.makedirs(os.path.join(policy_path, "User"), 0755)
842 if not os.path.isdir(paths.netlogon):
843 os.makedirs(paths.netlogon, 0755)
844 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
845 credentials=credentials, lp=lp)
846 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
847 netbiosname=netbiosname, domainsid=domainsid,
848 keytab_path=paths.keytab, samdb_url=paths.samdb,
849 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
850 machinepass=machinepass, dnsdomain=dnsdomain)
852 if samdb_fill == FILL_FULL:
853 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
854 nobody=nobody, nogroup=nogroup, wheel=wheel,
855 users=users, backup=backup)
857 message("Setting up sam.ldb rootDSE marking as synchronized")
858 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
860 message("Setting up phpLDAPadmin configuration")
861 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
864 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
866 if lp.get("server role") == "domain controller":
867 samdb = SamDB(paths.samdb, session_info=session_info,
868 credentials=credentials, lp=lp)
870 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
871 assert isinstance(domainguid, str)
872 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
873 expression="(&(objectClass=computer)(cn=%s))" % hostname,
875 assert isinstance(hostguid, str)
877 message("Setting up DNS zone: %s" % dnsdomain)
878 create_zone_file(paths.dns, setup_path, samdb,
879 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
880 domaindn=domaindn, dnspass=dnspass, realm=realm,
881 domainguid=domainguid, hostguid=hostguid)
882 message("Please install the zone located in %s into your DNS server" % paths.dns)
887 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
888 """Create a PHP LDAP admin configuration file.
890 :param path: Path to write the configuration to.
891 :param setup_path: Function to generate setup paths.
893 setup_file(setup_path("phpldapadmin-config.php"), path,
894 {"S4_LDAPI_URI": ldapi_uri})
897 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
898 hostip, hostname, dnspass, realm, domainguid, hostguid):
899 """Write out a DNS zone file, from the info in the current database.
901 :param path: Path of the new file.
902 :param setup_path": Setup path function.
903 :param samdb: SamDB object
904 :param dnsdomain: DNS Domain name
905 :param domaindn: DN of the Domain
906 :param hostip: Local IP
907 :param hostname: Local hostname
908 :param dnspass: Password for DNS
909 :param realm: Realm name
910 :param domainguid: GUID of the domain.
911 :param hostguid: GUID of the host.
913 assert isinstance(domainguid, str)
915 setup_file(setup_path("provision.zone"), path, {
916 "DNSPASS_B64": b64encode(dnspass),
917 "HOSTNAME": hostname,
918 "DNSDOMAIN": dnsdomain,
921 "DOMAINGUID": domainguid,
922 "DATESTRING": time.strftime("%Y%m%d%H"),
923 "DEFAULTSITE": DEFAULTSITE,
924 "HOSTGUID": hostguid,
928 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
931 :param samdb: Load a schema into a SamDB.
932 :param setup_path: Setup path function.
933 :param schemadn: DN of the schema
934 :param netbiosname: NetBIOS name of the host.
935 :param configdn: DN of the configuration
937 schema_data = open(setup_path("schema.ldif"), 'r').read()
938 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
939 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
940 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
941 head_data = substitute_var(head_data, {
942 "SCHEMADN": schemadn,
943 "NETBIOSNAME": netbiosname,
944 "CONFIGDN": configdn,
945 "DEFAULTSITE": DEFAULTSITE
947 samdb.attach_schema_from_ldif(head_data, schema_data)