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 = "hklm.ldb"
207 paths.hkcr = "hkcr.ldb"
208 paths.hkcu = "hkcu.ldb"
209 paths.hku = "hku.ldb"
210 paths.hkpd = "hkpd.ldb"
211 paths.hkpt = "hkpt.ldb"
213 paths.sysvol = lp.get("sysvol", "path")
214 if paths.sysvol is None:
215 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
217 paths.netlogon = lp.get("netlogon", "path")
218 if paths.netlogon is None:
219 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
224 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
226 """setup reasonable name mappings for sam names to unix names.
228 :param ldb: SamDB object.
229 :param sid: The domain sid.
230 :param domaindn: The domain DN.
231 :param root: Name of the UNIX root user.
232 :param nobody: Name of the UNIX nobody user.
233 :param nogroup: Name of the unix nobody group.
234 :param users: Name of the unix users group.
235 :param wheel: Name of the wheel group (users that can become root).
236 :param backup: Name of the backup group."""
237 # add some foreign sids if they are not present already
238 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
239 ldb.add_foreign(domaindn, "S-1-1-0", "World")
240 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
241 ldb.add_foreign(domaindn, "S-1-5-18", "System")
242 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
244 # some well known sids
245 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
246 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
247 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
248 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
249 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
250 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
251 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
252 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
253 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
255 # and some well known domain rids
256 ldb.setup_name_mapping(domaindn, sid + "-500", root)
257 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
258 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
259 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
260 ldb.setup_name_mapping(domaindn, sid + "-513", users)
261 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
264 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
265 credentials, configdn, schemadn, domaindn,
266 hostname, netbiosname, dnsdomain, realm,
267 rootdn, serverrole, ldap_backend=None,
268 ldap_backend_type=None, erase=False):
269 """Setup the partitions for the SAM database.
271 Alternatively, provision() may call this, and then populate the database.
273 :param erase: Remove the existing data present in the database.
276 :note: This will wipe the Sam Database!
278 :note: This function always removes the local SAM LDB file. The erase
279 parameter controls whether to erase the existing data, which
280 may not be stored locally but in LDAP.
282 assert session_info is not None
284 if os.path.exists(samdb_path):
285 os.unlink(samdb_path)
287 # Also wipes the database
288 samdb = SamDB(samdb_path, session_info=session_info,
289 credentials=credentials, lp=lp)
291 #Add modules to the list to activate them by default
292 #beware often order is important
294 # Some Known ordering constraints:
295 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
296 # - objectclass must be before password_hash, because password_hash checks
297 # that the objectclass is of type person (filled in by objectclass
298 # module when expanding the objectclass list)
299 # - partition must be last
300 # - each partition has its own module list then
301 modules_list = ["rootdse",
317 modules_list2 = ["show_deleted",
320 domaindn_ldb = "users.ldb"
321 if ldap_backend is not None:
322 domaindn_ldb = ldap_backend
323 configdn_ldb = "configuration.ldb"
324 if ldap_backend is not None:
325 configdn_ldb = ldap_backend
326 schemadn_ldb = "schema.ldb"
327 if ldap_backend is not None:
328 schema_ldb = ldap_backend
330 schemadn_ldb = ldap_backend
332 if ldap_backend_type == "fedora-ds":
333 backend_modules = ["nsuniqueid","paged_searches"]
334 elif ldap_backend_type == "openldap":
335 backend_modules = ["normalise","entryuuid","paged_searches"]
336 elif serverrole == "domain controller":
337 backend_modules = ["repl_meta_data"]
339 backend_modules = ["objectguid"]
341 samdb.transaction_start()
343 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
344 "SCHEMADN": schemadn,
345 "SCHEMADN_LDB": schemadn_ldb,
346 "SCHEMADN_MOD2": ",objectguid",
347 "CONFIGDN": configdn,
348 "CONFIGDN_LDB": configdn_ldb,
349 "DOMAINDN": domaindn,
350 "DOMAINDN_LDB": domaindn_ldb,
351 "SCHEMADN_MOD": "schema_fsmo,instancetype",
352 "CONFIGDN_MOD": "naming_fsmo,instancetype",
353 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
354 "MODULES_LIST": ",".join(modules_list),
355 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
356 "MODULES_LIST2": ",".join(modules_list2),
357 "BACKEND_MOD": ",".join(backend_modules),
361 samdb.transaction_cancel()
364 samdb.transaction_commit()
366 samdb = SamDB(samdb_path, session_info=session_info,
367 credentials=credentials, lp=lp)
369 samdb.transaction_start()
371 message("Setting up sam.ldb attributes")
372 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
374 message("Setting up sam.ldb rootDSE")
375 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
376 dnsdomain, realm, rootdn, configdn, netbiosname)
379 message("Erasing data from partitions")
380 samdb.erase_partitions()
383 samdb.transaction_cancel()
386 samdb.transaction_commit()
391 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
392 netbiosname, domainsid, keytab_path, samdb_url,
393 dns_keytab_path, dnspass, machinepass):
394 """Add DC-specific bits to a secrets database.
396 :param secretsdb: Ldb Handle to the secrets database
397 :param setup_path: Setup path function
398 :param machinepass: Machine password
400 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
401 "MACHINEPASS_B64": b64encode(machinepass),
404 "DNSDOMAIN": dnsdomain,
405 "DOMAINSID": str(domainsid),
406 "SECRETS_KEYTAB": keytab_path,
407 "NETBIOSNAME": netbiosname,
408 "SAM_LDB": samdb_url,
409 "DNS_KEYTAB": dns_keytab_path,
410 "DNSPASS_B64": b64encode(dnspass),
414 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
415 """Setup the secrets database.
417 :param path: Path to the secrets database.
418 :param setup_path: Get the path to a setup file.
419 :param session_info: Session info.
420 :param credentials: Credentials
421 :param lp: Loadparm context
422 :return: LDB handle for the created secrets database
424 if os.path.exists(path):
426 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
429 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
430 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
432 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
436 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
437 """Setup the templates database.
439 :param path: Path to the database.
440 :param setup_path: Function for obtaining the path to setup files.
441 :param session_info: Session info
442 :param credentials: Credentials
443 :param lp: Loadparm context
445 templates_ldb = SamDB(path, session_info=session_info,
446 credentials=credentials, lp=lp)
447 templates_ldb.erase()
448 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
451 def setup_registry(path, setup_path, session_info, credentials, lp):
452 """Setup the registry.
454 :param path: Path to the registry database
455 :param setup_path: Function that returns the path to a setup.
456 :param session_info: Session information
457 :param credentials: Credentials
458 :param lp: Loadparm context
460 reg = registry.Registry()
462 hive = registry.open_ldb(path, session_info=session_info,
463 credentials=credentials, lp_ctx=lp)
464 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
465 provision_reg = setup_path("provision.reg")
466 assert os.path.exists(provision_reg)
467 reg.diff_apply(provision_reg)
470 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
471 dnsdomain, realm, rootdn, configdn, netbiosname):
472 """Setup the SamDB rootdse.
474 :param samdb: Sam Database handle
475 :param setup_path: Obtain setup path
478 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
479 "SCHEMADN": schemadn,
480 "NETBIOSNAME": netbiosname,
481 "DNSDOMAIN": dnsdomain,
482 "DEFAULTSITE": DEFAULTSITE,
484 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
485 "DOMAINDN": domaindn,
487 "CONFIGDN": configdn,
488 "VERSION": samba.version(),
492 def setup_self_join(samdb, configdn, schemadn, domaindn,
493 netbiosname, hostname, dnsdomain, machinepass, dnspass,
494 realm, domainname, domainsid, invocationid, setup_path,
495 policyguid, hostguid=None):
496 """Join a host to its own domain."""
497 if hostguid is not None:
498 hostguid_add = "objectGUID: %s" % hostguid
502 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
503 "CONFIGDN": configdn,
504 "SCHEMADN": schemadn,
505 "DOMAINDN": domaindn,
506 "INVOCATIONID": invocationid,
507 "NETBIOSNAME": netbiosname,
508 "DEFAULTSITE": DEFAULTSITE,
509 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
510 "MACHINEPASS_B64": b64encode(machinepass),
511 "DNSPASS_B64": b64encode(dnspass),
513 "DOMAIN": domainname,
514 "HOSTGUID_ADD": hostguid_add,
515 "DNSDOMAIN": dnsdomain})
516 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
517 "POLICYGUID": policyguid,
518 "DNSDOMAIN": dnsdomain,
519 "DOMAINSID": str(domainsid),
520 "DOMAINDN": domaindn})
523 def setup_samdb(path, setup_path, session_info, credentials, lp,
524 schemadn, configdn, domaindn, dnsdomain, realm,
525 netbiosname, message, hostname, rootdn, erase,
526 domainsid, aci, domainguid, policyguid,
527 domainname, fill, adminpass, krbtgtpass,
528 machinepass, hostguid, invocationid, dnspass,
529 serverrole, ldap_backend=None, ldap_backend_type=None):
530 """Setup a complete SAM Database.
534 # Also wipes the database
535 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
536 domaindn=domaindn, message=message, lp=lp,
537 credentials=credentials, session_info=session_info,
538 hostname=hostname, netbiosname=netbiosname,
539 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
540 ldap_backend=ldap_backend, serverrole=serverrole,
541 ldap_backend_type=ldap_backend_type, erase=erase)
543 samdb = SamDB(path, session_info=session_info,
544 credentials=credentials, lp=lp)
547 # We want to finish here, but setup the index before we do so
548 message("Setting up sam.ldb index")
549 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
552 message("Pre-loading the Samba 4 and AD schema")
553 samdb = SamDB(path, session_info=session_info,
554 credentials=credentials, lp=lp)
555 samdb.set_domain_sid(domainsid)
556 if lp.get("server role") == "domain controller":
557 samdb.set_invocation_id(invocationid)
559 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
561 samdb.transaction_start()
564 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
565 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
566 "DOMAINDN": domaindn,
570 message("Modifying DomainDN: " + domaindn + "")
571 if domainguid is not None:
572 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
576 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
577 "LDAPTIME": timestring(int(time.time())),
578 "DOMAINSID": str(domainsid),
579 "SCHEMADN": schemadn,
580 "NETBIOSNAME": netbiosname,
581 "DEFAULTSITE": DEFAULTSITE,
582 "CONFIGDN": configdn,
583 "POLICYGUID": policyguid,
584 "DOMAINDN": domaindn,
585 "DOMAINGUID_MOD": domainguid_mod,
588 message("Adding configuration container (permitted to fail)")
589 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
590 "CONFIGDN": configdn,
592 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
594 message("Modifying configuration container")
595 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
596 "CONFIGDN": configdn,
597 "SCHEMADN": schemadn,
600 message("Adding schema container (permitted to fail)")
601 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
602 "SCHEMADN": schemadn,
604 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
606 message("Modifying schema container")
607 setup_modify_ldif(samdb,
608 setup_path("provision_schema_basedn_modify.ldif"), {
609 "SCHEMADN": schemadn,
610 "NETBIOSNAME": netbiosname,
611 "DEFAULTSITE": DEFAULTSITE,
612 "CONFIGDN": configdn,
615 message("Setting up sam.ldb Samba4 schema")
616 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
617 {"SCHEMADN": schemadn })
618 message("Setting up sam.ldb AD schema")
619 setup_add_ldif(samdb, setup_path("schema.ldif"),
620 {"SCHEMADN": schemadn})
622 message("Setting up sam.ldb configuration data")
623 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
624 "CONFIGDN": configdn,
625 "NETBIOSNAME": netbiosname,
626 "DEFAULTSITE": DEFAULTSITE,
627 "DNSDOMAIN": dnsdomain,
628 "DOMAIN": domainname,
629 "SCHEMADN": schemadn,
630 "DOMAINDN": domaindn,
633 message("Setting up display specifiers")
634 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
635 {"CONFIGDN": configdn})
637 message("Adding users container (permitted to fail)")
638 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
639 "DOMAINDN": domaindn})
640 message("Modifying users container")
641 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
642 "DOMAINDN": domaindn})
643 message("Adding computers container (permitted to fail)")
644 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
645 "DOMAINDN": domaindn})
646 message("Modifying computers container")
647 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
648 "DOMAINDN": domaindn})
649 message("Setting up sam.ldb data")
650 setup_add_ldif(samdb, setup_path("provision.ldif"), {
651 "DOMAINDN": domaindn,
652 "NETBIOSNAME": netbiosname,
653 "DEFAULTSITE": DEFAULTSITE,
654 "CONFIGDN": configdn,
657 if fill == FILL_FULL:
658 message("Setting up sam.ldb users and groups")
659 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
660 "DOMAINDN": domaindn,
661 "DOMAINSID": str(domainsid),
662 "CONFIGDN": configdn,
663 "ADMINPASS_B64": b64encode(adminpass),
664 "KRBTGTPASS_B64": b64encode(krbtgtpass),
667 if lp.get("server role") == "domain controller":
668 message("Setting up self join")
669 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
670 domaindn=domaindn, invocationid=invocationid,
671 dnspass=dnspass, netbiosname=netbiosname,
672 dnsdomain=dnsdomain, realm=realm,
673 machinepass=machinepass, domainname=domainname,
674 domainsid=domainsid, policyguid=policyguid,
675 hostname=hostname, hostguid=hostguid,
676 setup_path=setup_path)
678 #We want to setup the index last, as adds are faster unindexed
679 message("Setting up sam.ldb index")
680 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
682 samdb.transaction_cancel()
685 samdb.transaction_commit()
689 FILL_NT4SYNC = "NT4SYNC"
692 def provision(lp, setup_dir, message, paths, session_info,
693 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
694 domain=None, hostname=None, hostip=None, domainsid=None,
695 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
696 policyguid=None, invocationid=None, machinepass=None,
697 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
698 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
699 ldap_backend=None, ldap_backend_type=None):
702 :note: caution, this wipes all existing data!
705 def setup_path(file):
706 return os.path.join(setup_dir, file)
708 if domainsid is None:
709 domainsid = security.random_sid()
710 if policyguid is None:
711 policyguid = uuid.random()
712 if adminpass is None:
713 adminpass = misc.random_password(12)
714 if krbtgtpass is None:
715 krbtgtpass = misc.random_password(12)
716 if machinepass is None:
717 machinepass = misc.random_password(12)
719 dnspass = misc.random_password(12)
721 root = findnss(pwd.getpwnam, "root")[0]
723 nobody = findnss(pwd.getpwnam, "nobody")[0]
725 nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[0]
727 users = findnss(grp.getgrnam, "users", "guest", "other", "unknown",
730 wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[0]
732 backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[0]
734 aci = "# no aci for local ldb"
735 if serverrole is None:
736 serverrole = lp.get("server role")
737 if invocationid is None and serverrole == "domain controller":
738 invocationid = uuid.random()
741 realm = lp.get("realm")
743 if lp.get("realm").upper() != realm.upper():
744 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
745 (lp.get("realm"), realm))
747 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
749 if ldap_backend == "ldapi":
750 # provision-backend will set this path suggested slapd command line / fedorads.inf
751 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
753 assert realm is not None
754 realm = realm.upper()
757 hostname = gethostname().split(".")[0].lower()
760 hostip = gethostbyname(hostname)
762 netbiosname = hostname.upper()
763 if not valid_netbios_name(netbiosname):
764 raise InvalidNetbiosName(netbiosname)
766 dnsdomain = realm.lower()
767 if serverrole == "domain controller":
768 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
770 domain = lp.get("workgroup")
772 if lp.get("workgroup").upper() != domain.upper():
773 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
774 lp.get("workgroup"), domain)
776 assert domain is not None
777 domain = domain.upper()
778 if not valid_netbios_name(domain):
779 raise InvalidNetbiosName(domain)
782 domaindn = "CN=" + netbiosname
788 configdn = "CN=Configuration," + rootdn
789 schemadn = "CN=Schema," + configdn
791 message("set DOMAIN SID: %s" % str(domainsid))
792 message("Provisioning for %s in realm %s" % (domain, realm))
793 message("Using administrator password: %s" % adminpass)
795 assert paths.smbconf is not None
797 # only install a new smb.conf if there isn't one there already
798 if not os.path.exists(paths.smbconf):
799 message("Setting up smb.conf")
800 if serverrole == "domain controller":
802 elif serverrole == "member":
803 smbconfsuffix = "member"
805 assert "Invalid server role setting: %s" % serverrole
806 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
808 "HOSTNAME": hostname,
809 "DOMAIN_CONF": domain,
811 "SERVERROLE": serverrole,
812 "NETLOGONPATH": paths.netlogon,
813 "SYSVOLPATH": paths.sysvol,
817 # only install a new shares config db if there is none
818 if not os.path.exists(paths.shareconf):
819 message("Setting up share.ldb")
820 share_ldb = Ldb(paths.shareconf, session_info=session_info,
821 credentials=credentials, lp=lp)
822 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
825 message("Setting up secrets.ldb")
826 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
827 session_info=session_info,
828 credentials=credentials, lp=lp)
830 message("Setting up the registry")
831 setup_registry(paths.hklm, setup_path, session_info,
832 credentials=credentials, lp=lp)
834 message("Setting up templates db")
835 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
836 credentials=credentials, lp=lp)
838 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
839 credentials=credentials, lp=lp, schemadn=schemadn,
840 configdn=configdn, domaindn=domaindn,
841 dnsdomain=dnsdomain, netbiosname=netbiosname,
842 realm=realm, message=message, hostname=hostname,
843 rootdn=rootdn, erase=erase, domainsid=domainsid,
844 aci=aci, domainguid=domainguid, policyguid=policyguid,
845 domainname=domain, fill=samdb_fill,
846 adminpass=adminpass, krbtgtpass=krbtgtpass,
847 hostguid=hostguid, invocationid=invocationid,
848 machinepass=machinepass, dnspass=dnspass,
849 serverrole=serverrole, ldap_backend=ldap_backend,
850 ldap_backend_type=ldap_backend_type)
852 if lp.get("server role") == "domain controller":
853 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
854 "{" + policyguid + "}")
855 os.makedirs(policy_path, 0755)
856 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
857 os.makedirs(os.path.join(policy_path, "User"), 0755)
858 if not os.path.isdir(paths.netlogon):
859 os.makedirs(paths.netlogon, 0755)
860 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
861 credentials=credentials, lp=lp)
862 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
863 netbiosname=netbiosname, domainsid=domainsid,
864 keytab_path=paths.keytab, samdb_url=paths.samdb,
865 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
866 machinepass=machinepass, dnsdomain=dnsdomain)
868 if samdb_fill == FILL_FULL:
869 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
870 nobody=nobody, nogroup=nogroup, wheel=wheel,
871 users=users, backup=backup)
873 message("Setting up sam.ldb rootDSE marking as synchronized")
874 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
876 message("Setting up phpLDAPadmin configuration")
877 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
880 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
882 if lp.get("server role") == "domain controller":
883 samdb = SamDB(paths.samdb, session_info=session_info,
884 credentials=credentials, lp=lp)
886 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
887 assert isinstance(domainguid, str)
888 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
889 expression="(&(objectClass=computer)(cn=%s))" % hostname,
891 assert isinstance(hostguid, str)
893 message("Setting up DNS zone: %s" % dnsdomain)
894 create_zone_file(paths.dns, setup_path, samdb,
895 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
896 domaindn=domaindn, dnspass=dnspass, realm=realm,
897 domainguid=domainguid, hostguid=hostguid)
898 message("Please install the zone located in %s into your DNS server" % paths.dns)
903 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
904 """Create a PHP LDAP admin configuration file.
906 :param path: Path to write the configuration to.
907 :param setup_path: Function to generate setup paths.
909 setup_file(setup_path("phpldapadmin-config.php"), path,
910 {"S4_LDAPI_URI": ldapi_uri})
913 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
914 hostip, hostname, dnspass, realm, domainguid, hostguid):
915 """Write out a DNS zone file, from the info in the current database.
917 :param path: Path of the new file.
918 :param setup_path": Setup path function.
919 :param samdb: SamDB object
920 :param dnsdomain: DNS Domain name
921 :param domaindn: DN of the Domain
922 :param hostip: Local IP
923 :param hostname: Local hostname
924 :param dnspass: Password for DNS
925 :param realm: Realm name
926 :param domainguid: GUID of the domain.
927 :param hostguid: GUID of the host.
929 assert isinstance(domainguid, str)
931 setup_file(setup_path("provision.zone"), path, {
932 "DNSPASS_B64": b64encode(dnspass),
933 "HOSTNAME": hostname,
934 "DNSDOMAIN": dnsdomain,
937 "DOMAINGUID": domainguid,
938 "DATESTRING": time.strftime("%Y%m%d%H"),
939 "DEFAULTSITE": DEFAULTSITE,
940 "HOSTGUID": hostguid,
944 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
947 :param samdb: Load a schema into a SamDB.
948 :param setup_path: Setup path function.
949 :param schemadn: DN of the schema
950 :param netbiosname: NetBIOS name of the host.
951 :param configdn: DN of the configuration
953 schema_data = open(setup_path("schema.ldif"), 'r').read()
954 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
955 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
956 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
957 head_data = substitute_var(head_data, {
958 "SCHEMADN": schemadn,
959 "NETBIOSNAME": netbiosname,
960 "CONFIGDN": configdn,
961 "DEFAULTSITE": DEFAULTSITE
963 samdb.attach_schema_from_ldif(head_data, schema_data)