diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2008-02-09 20:06:54 +0100 |
---|---|---|
committer | Jelmer Vernooij <jelmer@samba.org> | 2008-02-09 20:06:54 +0100 |
commit | ec106a14216ee034b7e04c177c89703e63dfd107 (patch) | |
tree | 59c56cd663ee3dc03d0ce63bf4405ee87c14834a /source4/scripting | |
parent | 4075a2ba982ea56ff925e0a33839f0760fd71911 (diff) | |
parent | 1a2544a24c064e9eecb973439ccd0e7126e06e77 (diff) | |
download | samba-ec106a14216ee034b7e04c177c89703e63dfd107.tar.gz samba-ec106a14216ee034b7e04c177c89703e63dfd107.tar.bz2 samba-ec106a14216ee034b7e04c177c89703e63dfd107.zip |
Merge branch 'v4-0-trivial' into v4-0-python
(This used to be commit b874f07175ae38a041f53f0e4ac6a4050dcefeae)
Diffstat (limited to 'source4/scripting')
-rwxr-xr-x | source4/scripting/bin/winreg.py | 5 | ||||
-rw-r--r-- | source4/scripting/ejs/smbcalls_rpc.c | 2 | ||||
-rw-r--r-- | source4/scripting/libjs/provision.js | 39 | ||||
-rw-r--r-- | source4/scripting/python/STATUS | 1 | ||||
-rw-r--r-- | source4/scripting/python/config.m4 | 1 | ||||
-rw-r--r-- | source4/scripting/python/config.mk | 2 | ||||
-rw-r--r-- | source4/scripting/python/misc.i | 11 | ||||
-rw-r--r-- | source4/scripting/python/misc.py | 1 | ||||
-rw-r--r-- | source4/scripting/python/misc_wrap.c | 51 | ||||
-rw-r--r-- | source4/scripting/python/modules.c | 2 | ||||
-rw-r--r-- | source4/scripting/python/pyrpc.h | 5 | ||||
-rw-r--r-- | source4/scripting/python/samba/__init__.py | 23 | ||||
-rw-r--r-- | source4/scripting/python/samba/getopt.py | 26 | ||||
-rw-r--r-- | source4/scripting/python/samba/provision.py | 577 | ||||
-rw-r--r-- | source4/scripting/python/samba/samdb.py | 13 | ||||
-rw-r--r-- | source4/scripting/python/samba/tests/__init__.py | 8 | ||||
-rw-r--r-- | source4/scripting/python/samba/tests/provision.py | 58 | ||||
-rw-r--r-- | source4/scripting/python/samba/upgrade.py | 9 |
18 files changed, 576 insertions, 258 deletions
diff --git a/source4/scripting/bin/winreg.py b/source4/scripting/bin/winreg.py index f68f2d12f2..1e39ee8f78 100755 --- a/source4/scripting/bin/winreg.py +++ b/source4/scripting/bin/winreg.py @@ -12,7 +12,8 @@ import optparse import samba.getopt as options parser = optparse.OptionParser("%s <BINDING> [path]" % sys.argv[0]) -parser.add_option_group(options.SambaOptions(parser)) +sambaopts = options.SambaOptions(parser) +parser.add_option_group(sambaopts) parser.add_option("--createkey", type="string", metavar="KEYNAME", help="create a key") @@ -25,7 +26,7 @@ if len(args) < 1: binding = args[0] print "Connecting to " + binding -conn = winreg.winreg(binding, opts.configfile) +conn = winreg.winreg(binding, sambaopts.get_loadparm()) def list_values(key): (num_values, max_valnamelen, max_valbufsize) = conn.QueryInfoKey(key, winreg.String())[4:8] diff --git a/source4/scripting/ejs/smbcalls_rpc.c b/source4/scripting/ejs/smbcalls_rpc.c index 2bfc8b5883..44cfa16d7e 100644 --- a/source4/scripting/ejs/smbcalls_rpc.c +++ b/source4/scripting/ejs/smbcalls_rpc.c @@ -80,7 +80,7 @@ static int ejs_irpc_connect(MprVarHandle eid, int argc, char **argv) for (i=0;i<10000;i++) { p->msg_ctx = messaging_init(p, lp_messaging_path(p, global_loadparm), - cluster_id(EJS_ID_BASE + i), + cluster_id(EJS_ID_BASE, i), lp_iconv_convenience(global_loadparm), ev); if (p->msg_ctx) break; diff --git a/source4/scripting/libjs/provision.js b/source4/scripting/libjs/provision.js index 0cca49dec9..e71498010c 100644 --- a/source4/scripting/libjs/provision.js +++ b/source4/scripting/libjs/provision.js @@ -484,9 +484,6 @@ function provision_fix_subobj(subobj, paths) subobj.ADMINPASS_B64 = ldb.encode(subobj.ADMINPASS); subobj.DNSPASS_B64 = ldb.encode(subobj.DNSPASS); - var rdns = split(",", subobj.DOMAINDN); - subobj.RDN_DC = substr(rdns[0], strlen("DC=")); - subobj.SAM_LDB = "tdb://" + paths.samdb; subobj.SECRETS_KEYTAB = paths.keytab; subobj.DNS_KEYTAB = paths.dns_keytab; @@ -527,6 +524,10 @@ function provision_become_dc(subobj, message, erase, paths, session_info) var ok = provision_fix_subobj(subobj, paths); assert(ok); + if (subobj.BACKEND_MOD == undefined) { + subobj.BACKEND_MOD = "repl_meta_data"; + } + info.subobj = subobj; info.message = message; info.session_info = session_info; @@ -613,10 +614,21 @@ function provision(subobj, message, blank, paths, session_info, credentials, lda var lp = loadparm_init(); var sys = sys_init(); var info = new Object(); + random_init(local); var ok = provision_fix_subobj(subobj, paths); assert(ok); + if (strlower(subobj.SERVERROLE) == strlower("domain controller")) { + if (subobj.BACKEND_MOD == undefined) { + subobj.BACKEND_MOD = "repl_meta_data"; + } + } else { + if (subobj.BACKEND_MOD == undefined) { + subobj.BACKEND_MOD = "objectguid"; + } + } + if (subobj.DOMAINGUID != undefined) { subobj.DOMAINGUID_MOD = sprintf("replace: objectGUID\nobjectGUID: %s\n-", subobj.DOMAINGUID); } else { @@ -696,6 +708,20 @@ function provision(subobj, message, blank, paths, session_info, credentials, lda samdb.set_domain_sid(subobj.DOMAINSID); + if (strlower(subobj.SERVERROLE) == strlower("domain controller")) { + if (subobj.INVOCATIONID == undefined) { + subobj.INVOCATIONID = randguid(); + } + samdb.set_ntds_invocationId(subobj.INVOCATIONID); + if (subobj.BACKEND_MOD == undefined) { + subobj.BACKEND_MOD = "repl_meta_data"; + } + } else { + if (subobj.BACKEND_MOD == undefined) { + subobj.BACKEND_MOD = "objectguid"; + } + } + var load_schema_ok = load_schema(subobj, message, samdb); assert(load_schema_ok.is_ok); @@ -961,7 +987,6 @@ function provision_guess() subobj.VERSION = version(); subobj.HOSTIP = hostip(); subobj.DOMAINSID = randsid(); - subobj.INVOCATIONID = randguid(); subobj.POLICYGUID = randguid(); subobj.KRBTGTPASS = randpass(12); subobj.MACHINEPASS = randpass(12); @@ -969,9 +994,6 @@ function provision_guess() subobj.ADMINPASS = randpass(12); subobj.LDAPMANAGERPASS = randpass(12); subobj.DEFAULTSITE = "Default-First-Site-Name"; - subobj.NEWGUID = randguid; - subobj.NTTIME = nttime; - subobj.LDAPTIME = ldaptime; subobj.DATESTRING = datestring; subobj.ROOT = findnss(nss.getpwnam, "root"); subobj.NOBODY = findnss(nss.getpwnam, "nobody"); @@ -1016,9 +1038,6 @@ function provision_guess() subobj.DOMAINDN_MOD = "pdc_fsmo,password_hash,instancetype"; subobj.CONFIGDN_MOD = "naming_fsmo,instancetype"; subobj.SCHEMADN_MOD = "schema_fsmo,instancetype"; - subobj.DOMAINDN_MOD2 = ",objectguid"; - subobj.CONFIGDN_MOD2 = ",objectguid"; - subobj.SCHEMADN_MOD2 = ",objectguid"; subobj.ACI = "# no aci for local ldb"; diff --git a/source4/scripting/python/STATUS b/source4/scripting/python/STATUS index 6e6475bfde..ee67b8bc7a 100644 --- a/source4/scripting/python/STATUS +++ b/source4/scripting/python/STATUS @@ -1,6 +1,5 @@ dsdb/samdb/ldb_modules/tests/samba3sam.py: Fix remaining failing tests lib/ldb/tests/python/ldap.py: Fix remaining 3 FIXME's -provisioning in LDAP mode(TEST_LDAP=yes PROVISION_PYTHON=yes make test) command-line vampire provisioning: combine some of the python dictionaries finish scripting/bin/smbstatus.py diff --git a/source4/scripting/python/config.m4 b/source4/scripting/python/config.m4 index 908efd1588..1f03ec8e34 100644 --- a/source4/scripting/python/config.m4 +++ b/source4/scripting/python/config.m4 @@ -66,6 +66,7 @@ if test $working_python = yes; then SMB_ENABLE(EXT_LIB_PYTHON,YES) SMB_ENABLE(smbpython,YES) SMB_ENABLE(LIBPYTHON,YES) + AC_DEFINE(HAVE_WORKING_PYTHON, 1, [Whether we have working python support]) AC_MSG_RESULT([yes]) else AC_MSG_ERROR([Python not found. Please install Python 2.x and its development headers/libraries.]) diff --git a/source4/scripting/python/config.mk b/source4/scripting/python/config.mk index 450da0e90a..b15e1fcda7 100644 --- a/source4/scripting/python/config.mk +++ b/source4/scripting/python/config.mk @@ -33,7 +33,7 @@ pythonmods:: $(PYTHON_DSOS) $(PYTHON_PYS) PYDOCTOR_MODULES=bin/python/ldb.py bin/python/auth.py bin/python/credentials.py bin/python/registry.py bin/python/tdb.py bin/python/security.py bin/python/events.py bin/python/net.py pydoctor:: pythonmods - LD_LIBRARY_PATH=bin/shared PYTHONPATH=bin/python pydoctor --make-html --docformat=restructuredtext --add-package scripting/python/samba/ $(addprefix --add-module , $(PYDOCTOR_MODULES)) + LD_LIBRARY_PATH=bin/shared PYTHONPATH=bin/python pydoctor --project-name=Samba --make-html --docformat=restructuredtext --add-package scripting/python/samba/ $(addprefix --add-module , $(PYDOCTOR_MODULES)) installpython:: pythonmods @$(SHELL) $(srcdir)/script/installpython.sh \ diff --git a/source4/scripting/python/misc.i b/source4/scripting/python/misc.i index 2f41840670..a11b2fb825 100644 --- a/source4/scripting/python/misc.i +++ b/source4/scripting/python/misc.i @@ -66,3 +66,14 @@ WERROR dsdb_attach_schema_from_ldif_file(struct ldb_context *ldb, const char *pf const char *samba_version_string(void); int dsdb_set_global_schema(struct ldb_context *ldb); int ldb_register_samba_handlers(struct ldb_context *ldb); + +%inline %{ +bool dsdb_set_ntds_invocation_id(struct ldb_context *ldb, const char *guid) +{ + struct GUID invocation_id_in; + if (NT_STATUS_IS_ERR(GUID_from_string(guid, &invocation_id_in))) { + return false; + } + return samdb_set_ntds_invocation_id(ldb, &invocation_id_in); +} +%} diff --git a/source4/scripting/python/misc.py b/source4/scripting/python/misc.py index ae900a1f62..2fc7fe37e7 100644 --- a/source4/scripting/python/misc.py +++ b/source4/scripting/python/misc.py @@ -70,5 +70,6 @@ dsdb_attach_schema_from_ldif_file = _misc.dsdb_attach_schema_from_ldif_file version = _misc.version dsdb_set_global_schema = _misc.dsdb_set_global_schema ldb_register_samba_handlers = _misc.ldb_register_samba_handlers +dsdb_set_ntds_invocation_id = _misc.dsdb_set_ntds_invocation_id diff --git a/source4/scripting/python/misc_wrap.c b/source4/scripting/python/misc_wrap.c index a7493550cc..f467f851bd 100644 --- a/source4/scripting/python/misc_wrap.c +++ b/source4/scripting/python/misc_wrap.c @@ -2794,6 +2794,16 @@ SWIG_From_int (int value) return SWIG_From_long (value); } + +bool dsdb_set_ntds_invocation_id(struct ldb_context *ldb, const char *guid) +{ + struct GUID invocation_id_in; + if (NT_STATUS_IS_ERR(GUID_from_string(guid, &invocation_id_in))) { + return false; + } + return samdb_set_ntds_invocation_id(ldb, &invocation_id_in); +} + #ifdef __cplusplus extern "C" { #endif @@ -3102,6 +3112,46 @@ fail: } +SWIGINTERN PyObject *_wrap_dsdb_set_ntds_invocation_id(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) { + PyObject *resultobj = 0; + struct ldb_context *arg1 = (struct ldb_context *) 0 ; + char *arg2 = (char *) 0 ; + bool result; + void *argp1 = 0 ; + int res1 = 0 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + char * kwnames[] = { + (char *) "ldb",(char *) "guid", NULL + }; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OO:dsdb_set_ntds_invocation_id",kwnames,&obj0,&obj1)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ldb_context, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "dsdb_set_ntds_invocation_id" "', argument " "1"" of type '" "struct ldb_context *""'"); + } + arg1 = (struct ldb_context *)(argp1); + res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "dsdb_set_ntds_invocation_id" "', argument " "2"" of type '" "char const *""'"); + } + arg2 = (char *)(buf2); + if (arg1 == NULL) + SWIG_exception(SWIG_ValueError, + "ldb context must be non-NULL"); + result = (bool)dsdb_set_ntds_invocation_id(arg1,(char const *)arg2); + resultobj = SWIG_From_bool((bool)(result)); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return resultobj; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return NULL; +} + + static PyMethodDef SwigMethods[] = { { (char *)"random_password", (PyCFunction) _wrap_random_password, METH_VARARGS | METH_KEYWORDS, NULL}, { (char *)"ldb_set_credentials", (PyCFunction) _wrap_ldb_set_credentials, METH_VARARGS | METH_KEYWORDS, NULL}, @@ -3112,6 +3162,7 @@ static PyMethodDef SwigMethods[] = { { (char *)"version", (PyCFunction)_wrap_version, METH_NOARGS, NULL}, { (char *)"dsdb_set_global_schema", (PyCFunction) _wrap_dsdb_set_global_schema, METH_VARARGS | METH_KEYWORDS, NULL}, { (char *)"ldb_register_samba_handlers", (PyCFunction) _wrap_ldb_register_samba_handlers, METH_VARARGS | METH_KEYWORDS, NULL}, + { (char *)"dsdb_set_ntds_invocation_id", (PyCFunction) _wrap_dsdb_set_ntds_invocation_id, METH_VARARGS | METH_KEYWORDS, NULL}, { NULL, NULL, 0, NULL } }; diff --git a/source4/scripting/python/modules.c b/source4/scripting/python/modules.c index fff981e941..2ecad20b8e 100644 --- a/source4/scripting/python/modules.c +++ b/source4/scripting/python/modules.c @@ -62,7 +62,7 @@ void py_load_samba_modules(void) void py_update_path(const char *bindir) { char *newpath; - asprintf(&newpath, "%s:%s/python:%s/../scripting/python", Py_GetPath(), bindir, bindir); + asprintf(&newpath, "%s/python:%s/../scripting/python:%s", bindir, bindir, Py_GetPath()); PySys_SetPath(newpath); free(newpath); } diff --git a/source4/scripting/python/pyrpc.h b/source4/scripting/python/pyrpc.h index 5390c6923d..3a5d235cfc 100644 --- a/source4/scripting/python/pyrpc.h +++ b/source4/scripting/python/pyrpc.h @@ -27,3 +27,8 @@ #define dom_sid28_Type dom_sid_Type #define dom_sid2_Check dom_sid_Check #define dom_sid28_Check dom_sid28_Check + +/* This macro is only provided by Python >= 2.3 */ +#ifndef PyAPI_DATA +# define PyAPI_DATA(RTYPE) extern RTYPE +#endif diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py index 483929661d..b041165800 100644 --- a/source4/scripting/python/samba/__init__.py +++ b/source4/scripting/python/samba/__init__.py @@ -1,8 +1,10 @@ #!/usr/bin/python # Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 +# +# Based on the original in EJS: # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005 -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -89,7 +91,7 @@ class Ldb(ldb.Ldb): set_session_info = misc.ldb_set_session_info set_loadparm = misc.ldb_set_loadparm - def searchone(self, basedn, attribute, expression=None, + def searchone(self, attribute, basedn=None, expression=None, scope=ldb.SCOPE_BASE): """Search for one attribute as a string. @@ -104,7 +106,7 @@ class Ldb(ldb.Ldb): return None values = set(res[0][attribute]) assert len(values) == 1 - return values.pop() + return self.schema_format_value(attribute, values.pop()) def erase(self): """Erase this ldb, removing all records.""" @@ -192,6 +194,21 @@ def substitute_var(text, values): return text +def check_all_substituted(text): + """Make sure that all substitution variables in a string have been replaced. + If not, raise an exception. + + :param text: The text to search for substitution variables + """ + if not "${" in text: + return + + var_start = text.find("${") + var_end = text.find("}", var_start) + + raise Exception("Not all variables substituted: %s" % text[var_start:var_end+1]) + + def valid_netbios_name(name): """Check whether a name is valid as a NetBIOS name. """ # FIXME: There are probably more constraints here. diff --git a/source4/scripting/python/samba/getopt.py b/source4/scripting/python/samba/getopt.py index a087974a69..088a5acf6f 100644 --- a/source4/scripting/python/samba/getopt.py +++ b/source4/scripting/python/samba/getopt.py @@ -23,9 +23,25 @@ from credentials import Credentials class SambaOptions(optparse.OptionGroup): def __init__(self, parser): optparse.OptionGroup.__init__(self, parser, "Samba Common Options") - self.add_option("-s", "--configfile", type="string", metavar="FILE", - help="Configuration file") + self.add_option("-s", "--configfile", action="callback", + type=str, metavar="FILE", help="Configuration file", + callback=self._load_configfile) + self._configfile = None + def get_loadparm_path(self): + return self._configfile + + def _load_configfile(self, option, opt_str, arg, parser): + self._configfile = arg + + def get_loadparm(self): + import param + lp = param.LoadParm() + if self._configfile is None: + lp.load_default() + else: + lp.load(self._configfile) + return lp class VersionOptions(optparse.OptionGroup): def __init__(self, parser): @@ -34,6 +50,7 @@ class VersionOptions(optparse.OptionGroup): class CredentialsOptions(optparse.OptionGroup): def __init__(self, parser): + self.no_pass = False optparse.OptionGroup.__init__(self, parser, "Credentials Options") self.add_option("--simple-bind-dn", metavar="DN", action="callback", callback=self._set_simple_bind_dn, type=str, @@ -46,6 +63,8 @@ class CredentialsOptions(optparse.OptionGroup): self.add_option("-W", "--workgroup", metavar="WORKGROUP", action="callback", type=str, help="Workgroup", callback=self._parse_workgroup) + self.add_option("-N", "--no-pass", action="store_true", + help="Don't ask for a password") self.creds = Credentials() def _parse_username(self, option, opt_str, arg, parser): @@ -61,4 +80,7 @@ class CredentialsOptions(optparse.OptionGroup): self.creds.set_bind_dn(arg) def get_credentials(self): + self.creds.guess() + if not self.no_pass: + self.creds.set_cmdline_callbacks() return self.creds diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index d59cea121e..4f52d36167 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -1,10 +1,25 @@ # -# backend code for provisioning a Samba4 server -# Released under the GNU GPL v3 or later -# Copyright Jelmer Vernooij 2007 +# Unix SMB/CIFS implementation. +# backend code for provisioning a Samba4 server + +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 +# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008 # # Based on the original in EJS: -# Copyright Andrew Tridgell 2005 +# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. # from base64 import b64encode @@ -17,9 +32,10 @@ from socket import gethostname, gethostbyname import param import registry import samba -from samba import Ldb, substitute_var, valid_netbios_name +from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted from samba.samdb import SamDB import security +import urllib from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \ LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE @@ -48,12 +64,9 @@ class ProvisionPaths: self.dns_keytab = None self.dns = None self.winsdb = None - self.ldap_basedn_ldif = None - self.ldap_config_basedn_ldif = None - self.ldap_schema_basedn_ldif = None -def install_ok(lp, session_info, credentials): +def check_install(lp, session_info, credentials): """Check whether the current install seems ok. :param lp: Loadparm context @@ -61,22 +74,26 @@ def install_ok(lp, session_info, credentials): :param credentials: Credentials """ if lp.get("realm") == "": - return False + raise Error("Realm empty") ldb = Ldb(lp.get("sam database"), session_info=session_info, credentials=credentials, lp=lp) if len(ldb.search("(cn=Administrator)")) != 1: - return False - return True + raise "No administrator account found" -def findnss(nssfn, *names): - """Find a user or group from a list of possibilities.""" +def findnss(nssfn, names): + """Find a user or group from a list of possibilities. + + :param nssfn: NSS Function to try (should raise KeyError if not found) + :param names: Names to check. + :return: Value return by first names list. + """ for name in names: try: return nssfn(name) except KeyError: pass - raise Exception("Unable to find user/group for %s" % arguments[1]) + raise KeyError("Unable to find user/group %r" % names) def open_ldb(session_info, credentials, lp, dbname): @@ -112,7 +129,7 @@ def setup_add_ldif(ldb, ldif_path, subst_vars=None): if subst_vars is not None: data = substitute_var(data, subst_vars) - assert "${" not in data + check_all_substituted(data) ldb.add_ldif(data) @@ -128,12 +145,20 @@ def setup_modify_ldif(ldb, ldif_path, substvars=None): if substvars is not None: data = substitute_var(data, substvars) - assert "${" not in data + check_all_substituted(data) ldb.modify_ldif(data) def setup_ldb(ldb, ldif_path, subst_vars): + """Import a LDIF a file into a LDB handle, optionally substituting variables. + + :note: Either all LDIF data will be added or none (using transactions). + + :param ldb: LDB file to import into. + :param ldif_path: Path to the LDIF file. + :param subst_vars: Dictionary with substitution variables. + """ assert ldb is not None ldb.transaction_start() try: @@ -159,7 +184,7 @@ def setup_file(template, fname, substvars): data = open(template, 'r').read() if substvars: data = substitute_var(data, substvars) - assert not "${" in data + check_all_substituted(data) open(f, 'w').write(data) @@ -172,18 +197,26 @@ def provision_paths_from_lp(lp, dnsdomain): """ paths = ProvisionPaths() private_dir = lp.get("private dir") + paths.keytab = "secrets.keytab" + paths.dns_keytab = "dns.keytab" + paths.shareconf = os.path.join(private_dir, "share.ldb") paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb") paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb") paths.templates = os.path.join(private_dir, "templates.ldb") - paths.keytab = os.path.join(private_dir, "secrets.keytab") - paths.dns_keytab = os.path.join(private_dir, "dns.keytab") paths.dns = os.path.join(private_dir, dnsdomain + ".zone") paths.winsdb = os.path.join(private_dir, "wins.ldb") paths.s4_ldapi_path = os.path.join(private_dir, "ldapi") + paths.smbconf = os.path.join(private_dir, "smb.conf") paths.phpldapadminconfig = os.path.join(private_dir, "phpldapadmin-config.php") - paths.hklm = os.path.join(private_dir, "hklm.ldb") + paths.hklm = "hklm.ldb" + paths.hkcr = "hkcr.ldb" + paths.hkcu = "hkcu.ldb" + paths.hku = "hku.ldb" + paths.hkpd = "hkpd.ldb" + paths.hkpt = "hkpt.ldb" + paths.sysvol = lp.get("sysvol", "path") if paths.sysvol is None: paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol") @@ -235,77 +268,201 @@ def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users, ldb.setup_name_mapping(domaindn, sid + "-520", wheel) -def provision_become_dc(setup_dir, message, paths, lp, session_info, - credentials): +def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, + credentials, configdn, schemadn, domaindn, + hostname, netbiosname, dnsdomain, realm, + rootdn, serverrole, ldap_backend=None, + ldap_backend_type=None, erase=False): + """Setup the partitions for the SAM database. + + Alternatively, provision() may call this, and then populate the database. + + :param erase: Remove the existing data present in the database. + + :note: This will wipe the Sam Database! + + :note: This function always removes the local SAM LDB file. The erase + parameter controls whether to erase the existing data, which + may not be stored locally but in LDAP. + """ assert session_info is not None - erase = False - - def setup_path(file): - return os.path.join(setup_dir, file) - os.path.unlink(paths.samdb) - message("Setting up templates db") - setup_templatesdb(paths.templates, setup_path, session_info=session_info, - credentials=credentials, lp=lp) + if os.path.exists(samdb_path): + os.unlink(samdb_path) # Also wipes the database - message("Setting up sam.ldb") - samdb = SamDB(paths.samdb, session_info=session_info, + samdb = SamDB(samdb_path, session_info=session_info, credentials=credentials, lp=lp) - message("Setting up sam.ldb partitions") - setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn) + #Add modules to the list to activate them by default + #beware often order is important + # + # Some Known ordering constraints: + # - rootdse must be first, as it makes redirects from "" -> cn=rootdse + # - objectclass must be before password_hash, because password_hash checks + # that the objectclass is of type person (filled in by objectclass + # module when expanding the objectclass list) + # - partition must be last + # - each partition has its own module list then + modules_list = ["rootdse", + "paged_results", + "ranged_results", + "anr", + "server_sort", + "extended_dn", + "asq", + "samldb", + "rdn_name", + "objectclass", + "kludge_acl", + "operational"] + tdb_modules_list = [ + "subtree_rename", + "subtree_delete", + "linked_attributes"] + modules_list2 = ["show_deleted", + "partition"] + + domaindn_ldb = "users.ldb" + if ldap_backend is not None: + domaindn_ldb = ldap_backend + configdn_ldb = "configuration.ldb" + if ldap_backend is not None: + configdn_ldb = ldap_backend + schemadn_ldb = "schema.ldb" + if ldap_backend is not None: + schema_ldb = ldap_backend + + schemadn_ldb = ldap_backend + + if ldap_backend_type == "fedora-ds": + backend_modules = ["nsuniqueid","paged_searches"] + elif ldap_backend_type == "openldap": + backend_modules = ["normalise","entryuuid","paged_searches"] + elif serverrole == "domain controller": + backend_modules = ["repl_meta_data"] + else: + backend_modules = ["objectguid"] + + samdb.transaction_start() + try: + setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), { + "SCHEMADN": schemadn, + "SCHEMADN_LDB": schemadn_ldb, + "SCHEMADN_MOD2": ",objectguid", + "CONFIGDN": configdn, + "CONFIGDN_LDB": configdn_ldb, + "DOMAINDN": domaindn, + "DOMAINDN_LDB": domaindn_ldb, + "SCHEMADN_MOD": "schema_fsmo,instancetype", + "CONFIGDN_MOD": "naming_fsmo,instancetype", + "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype", + "MODULES_LIST": ",".join(modules_list), + "TDB_MODULES_LIST": ","+",".join(tdb_modules_list), + "MODULES_LIST2": ",".join(modules_list2), + "BACKEND_MOD": ",".join(backend_modules), + }) + + except: + samdb.transaction_cancel() + raise - samdb = SamDB(paths.samdb, session_info=session_info, + samdb.transaction_commit() + + samdb = SamDB(samdb_path, session_info=session_info, credentials=credentials, lp=lp) - ldb.transaction_start() + samdb.transaction_start() try: message("Setting up sam.ldb attributes") samdb.load_ldif_file_add(setup_path("provision_init.ldif")) message("Setting up sam.ldb rootDSE") - setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, - hostname, dnsdomain, realm, rootdn, configdn, - netbiosname) + setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname, + dnsdomain, realm, rootdn, configdn, netbiosname) if erase: message("Erasing data from partitions") samdb.erase_partitions() - message("Setting up sam.ldb indexes") - samdb.load_ldif_file_add(setup_path("provision_index.ldif")) except: samdb.transaction_cancel() raise samdb.transaction_commit() + + return samdb + - message("Setting up %s" % paths.secrets) - secrets_ldb = setup_secretsdb(paths.secrets, setup_path, session_info, - credentials, lp) - setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), - { "MACHINEPASS_B64": b64encode(machinepass) }) +def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, + netbiosname, domainsid, keytab_path, samdb_url, + dns_keytab_path, dnspass, machinepass): + """Add DC-specific bits to a secrets database. + + :param secretsdb: Ldb Handle to the secrets database + :param setup_path: Setup path function + :param machinepass: Machine password + """ + setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { + "MACHINEPASS_B64": b64encode(machinepass), + "DOMAIN": domain, + "REALM": realm, + "DNSDOMAIN": dnsdomain, + "DOMAINSID": str(domainsid), + "SECRETS_KEYTAB": keytab_path, + "NETBIOSNAME": netbiosname, + "SAM_LDB": samdb_url, + "DNS_KEYTAB": dns_keytab_path, + "DNSPASS_B64": b64encode(dnspass), + }) def setup_secretsdb(path, setup_path, session_info, credentials, lp): + """Setup the secrets database. + + :param path: Path to the secrets database. + :param setup_path: Get the path to a setup file. + :param session_info: Session info. + :param credentials: Credentials + :param lp: Loadparm context + :return: LDB handle for the created secrets database + """ if os.path.exists(path): os.unlink(path) - secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, lp=lp) + secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, + lp=lp) secrets_ldb.erase() secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif")) + secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, + lp=lp) secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif")) return secrets_ldb def setup_templatesdb(path, setup_path, session_info, credentials, lp): + """Setup the templates database. + + :param path: Path to the database. + :param setup_path: Function for obtaining the path to setup files. + :param session_info: Session info + :param credentials: Credentials + :param lp: Loadparm context + """ templates_ldb = SamDB(path, session_info=session_info, - credentials=credentials, lp=lp) + credentials=credentials, lp=lp) templates_ldb.erase() templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif")) def setup_registry(path, setup_path, session_info, credentials, lp): + """Setup the registry. + + :param path: Path to the registry database + :param setup_path: Function that returns the path to a setup. + :param session_info: Session information + :param credentials: Credentials + :param lp: Loadparm context + """ reg = registry.Registry() hive = registry.open_ldb(path, session_info=session_info, credentials=credentials, lp_ctx=lp) @@ -317,6 +474,11 @@ def setup_registry(path, setup_path, session_info, credentials, lp): def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname, dnsdomain, realm, rootdn, configdn, netbiosname): + """Setup the SamDB rootdse. + + :param samdb: Sam Database handle + :param setup_path: Obtain setup path + """ setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), { "SCHEMADN": schemadn, "NETBIOSNAME": netbiosname, @@ -329,61 +491,13 @@ def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname, "CONFIGDN": configdn, "VERSION": samba.version(), }) - - -def setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn): - #Add modules to the list to activate them by default - #beware often order is important - # - # Some Known ordering constraints: - # - rootdse must be first, as it makes redirects from "" -> cn=rootdse - # - objectclass must be before password_hash, because password_hash checks - # that the objectclass is of type person (filled in by objectclass - # module when expanding the objectclass list) - # - partition must be last - # - each partition has its own module list then - modules_list = ["rootdse", - "paged_results", - "ranged_results", - "anr", - "server_sort", - "extended_dn", - "asq", - "samldb", - "rdn_name", - "objectclass", - "kludge_acl", - "operational"] - tdb_modules_list = [ - "subtree_rename", - "subtree_delete", - "linked_attributes"] - modules_list2 = ["show_deleted", - "partition"] - - setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), { - "SCHEMADN": schemadn, - "SCHEMADN_LDB": "schema.ldb", - "SCHEMADN_MOD2": ",objectguid", - "CONFIGDN": configdn, - "CONFIGDN_LDB": "configuration.ldb", - "DOMAINDN": domaindn, - "DOMAINDN_LDB": "users.ldb", - "SCHEMADN_MOD": "schema_fsmo", - "CONFIGDN_MOD": "naming_fsmo", - "CONFIGDN_MOD2": ",objectguid", - "DOMAINDN_MOD": "pdc_fsmo,password_hash", - "DOMAINDN_MOD2": ",objectguid", - "MODULES_LIST": ",".join(modules_list), - "TDB_MODULES_LIST": ","+",".join(tdb_modules_list), - "MODULES_LIST2": ",".join(modules_list2), - }) - + def setup_self_join(samdb, configdn, schemadn, domaindn, netbiosname, hostname, dnsdomain, machinepass, dnspass, realm, domainname, domainsid, invocationid, setup_path, policyguid, hostguid=None): + """Join a host to its own domain.""" if hostguid is not None: hostguid_add = "objectGUID: %s" % hostguid else: @@ -413,43 +527,40 @@ def setup_self_join(samdb, configdn, schemadn, domaindn, def setup_samdb(path, setup_path, session_info, credentials, lp, schemadn, configdn, domaindn, dnsdomain, realm, netbiosname, message, hostname, rootdn, erase, - domainsid, aci, rdn_dc, domainguid, policyguid, - domainname, blank, adminpass, krbtgtpass, - machinepass, hostguid, invocationid, dnspass): - # Also wipes the database - message("Setting up sam.ldb") - samdb = SamDB(path, session_info=session_info, - credentials=credentials, lp=lp) + domainsid, aci, domainguid, policyguid, + domainname, fill, adminpass, krbtgtpass, + machinepass, hostguid, invocationid, dnspass, + serverrole, ldap_backend=None, ldap_backend_type=None): + """Setup a complete SAM Database. + + :note: This will wipe the main SAM database file! + """ - message("Setting up sam.ldb partitions") - setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn) + # Also wipes the database + setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn, + domaindn=domaindn, message=message, lp=lp, + credentials=credentials, session_info=session_info, + hostname=hostname, netbiosname=netbiosname, + dnsdomain=dnsdomain, realm=realm, rootdn=rootdn, + ldap_backend=ldap_backend, serverrole=serverrole, + ldap_backend_type=ldap_backend_type, erase=erase) samdb = SamDB(path, session_info=session_info, credentials=credentials, lp=lp) - samdb.transaction_start() - try: - message("Setting up sam.ldb attributes") - samdb.load_ldif_file_add(setup_path("provision_init.ldif")) - - message("Setting up sam.ldb rootDSE") - setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, - hostname, dnsdomain, realm, rootdn, configdn, - netbiosname) - - if erase: - message("Erasing data from partitions") - samdb.erase_partitions() - except: - samdb.transaction_cancel() - raise - - samdb.transaction_commit() + if fill == FILL_DRS: + # We want to finish here, but setup the index before we do so + message("Setting up sam.ldb index") + samdb.load_ldif_file_add(setup_path("provision_index.ldif")) + return samdb message("Pre-loading the Samba 4 and AD schema") samdb = SamDB(path, session_info=session_info, credentials=credentials, lp=lp) samdb.set_domain_sid(domainsid) + if lp.get("server role") == "domain controller": + samdb.set_invocation_id(invocationid) + load_schema(setup_path, samdb, schemadn, netbiosname, configdn) samdb.transaction_start() @@ -459,7 +570,6 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), { "DOMAINDN": domaindn, "ACI": aci, - "RDN_DC": rdn_dc, }) message("Modifying DomainDN: " + domaindn + "") @@ -469,7 +579,6 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, domainguid_mod = "" setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), { - "RDN_DC": rdn_dc, "LDAPTIME": timestring(int(time.time())), "DOMAINSID": str(domainsid), "SCHEMADN": schemadn, @@ -500,7 +609,8 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb" }) message("Modifying schema container") - setup_modify_ldif(samdb, setup_path("provision_schema_basedn_modify.ldif"), { + setup_modify_ldif(samdb, + setup_path("provision_schema_basedn_modify.ldif"), { "SCHEMADN": schemadn, "NETBIOSNAME": netbiosname, "DEFAULTSITE": DEFAULTSITE, @@ -549,7 +659,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, "CONFIGDN": configdn, }) - if not blank: + if fill == FILL_FULL: message("Setting up sam.ldb users and groups") setup_add_ldif(samdb, setup_path("provision_users.ldif"), { "DOMAINDN": domaindn, @@ -561,17 +671,18 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, if lp.get("server role") == "domain controller": message("Setting up self join") - setup_self_join(samdb, configdn=configdn, schemadn=schemadn, domaindn=domaindn, - invocationid=invocationid, dnspass=dnspass, netbiosname=netbiosname, - dnsdomain=dnsdomain, realm=realm, machinepass=machinepass, - domainname=domainname, domainsid=domainsid, policyguid=policyguid, - hostname=hostname, hostguid=hostguid, setup_path=setup_path) - + setup_self_join(samdb, configdn=configdn, schemadn=schemadn, + domaindn=domaindn, invocationid=invocationid, + dnspass=dnspass, netbiosname=netbiosname, + dnsdomain=dnsdomain, realm=realm, + machinepass=machinepass, domainname=domainname, + domainsid=domainsid, policyguid=policyguid, + hostname=hostname, hostguid=hostguid, + setup_path=setup_path) + + #We want to setup the index last, as adds are faster unindexed message("Setting up sam.ldb index") samdb.load_ldif_file_add(setup_path("provision_index.ldif")) - - message("Setting up sam.ldb rootDSE marking as synchronized") - setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif")) except: samdb.transaction_cancel() raise @@ -579,14 +690,18 @@ def setup_samdb(path, setup_path, session_info, credentials, lp, samdb.transaction_commit() return samdb - -def provision(lp, setup_dir, message, blank, paths, session_info, - credentials, ldapbackend, realm=None, domain=None, hostname=None, - hostip=None, domainsid=None, hostguid=None, adminpass=None, - krbtgtpass=None, domainguid=None, policyguid=None, - invocationid=None, machinepass=None, dnspass=None, root=None, - nobody=None, nogroup=None, users=None, wheel=None, backup=None, - aci=None, serverrole=None): +FILL_FULL = "FULL" +FILL_NT4SYNC = "NT4SYNC" +FILL_DRS = "DRS" + +def provision(lp, setup_dir, message, paths, session_info, + credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None, + domain=None, hostname=None, hostip=None, domainsid=None, + hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None, + policyguid=None, invocationid=None, machinepass=None, + dnspass=None, root=None, nobody=None, nogroup=None, users=None, + wheel=None, backup=None, aci=None, serverrole=None, erase=False, + ldap_backend=None, ldap_backend_type=None): """Provision samba4 :note: caution, this wipes all existing data! @@ -595,14 +710,10 @@ def provision(lp, setup_dir, message, blank, paths, session_info, def setup_path(file): return os.path.join(setup_dir, file) - erase = False - if domainsid is None: domainsid = security.random_sid() if policyguid is None: policyguid = uuid.random() - if invocationid is None: - invocationid = uuid.random() if adminpass is None: adminpass = misc.random_password(12) if krbtgtpass is None: @@ -612,45 +723,42 @@ def provision(lp, setup_dir, message, blank, paths, session_info, if dnspass is None: dnspass = misc.random_password(12) if root is None: - root = findnss(pwd.getpwnam, "root")[4] + root = findnss(pwd.getpwnam, ["root"])[0] if nobody is None: - nobody = findnss(pwd.getpwnam, "nobody")[4] + nobody = findnss(pwd.getpwnam, ["nobody"])[0] if nogroup is None: - nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2] + nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0] if users is None: - users = findnss(grp.getgrnam, "users", "guest", "other", "unknown", - "usr")[2] + users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown", + "usr"])[0] if wheel is None: - wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2] + wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0] if backup is None: - backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2] + backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0] if aci is None: aci = "# no aci for local ldb" if serverrole is None: serverrole = lp.get("server role") + assert serverrole in ("domain controller", "member server") + if invocationid is None and serverrole == "domain controller": + invocationid = uuid.random() if realm is None: realm = lp.get("realm") - else: - if lp.get("realm").upper() != realm.upper(): - raise Exception("realm '%s' in smb.conf must match chosen realm '%s'\n" % + + if lp.get("realm").upper() != realm.upper(): + raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" % (lp.get("realm"), realm)) + ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="") + + if ldap_backend == "ldapi": + # provision-backend will set this path suggested slapd command line / fedorads.inf + ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="") + assert realm is not None realm = realm.upper() - if domain is None: - domain = lp.get("workgroup") - else: - if lp.get("workgroup").upper() != domain.upper(): - raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'\n", - lp.get("workgroup"), domain) - - assert domain is not None - domain = domain.upper() - if not valid_netbios_name(domain): - raise InvalidNetbiosName(domain) - if hostname is None: hostname = gethostname().split(".")[0].lower() @@ -661,13 +769,29 @@ def provision(lp, setup_dir, message, blank, paths, session_info, if not valid_netbios_name(netbiosname): raise InvalidNetbiosName(netbiosname) - dnsdomain = realm.lower() - domaindn = "DC=" + dnsdomain.replace(".", ",DC=") - rootdn = domaindn - configdn = "CN=Configuration," + rootdn - schemadn = "CN=Schema," + configdn + dnsdomain = realm.lower() + if serverrole == "domain controller": + domaindn = "DC=" + dnsdomain.replace(".", ",DC=") + if domain is None: + domain = lp.get("workgroup") + + if lp.get("workgroup").upper() != domain.upper(): + raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'", + lp.get("workgroup"), domain) - rdn_dc = domaindn.split(",")[0][len("DC="):] + assert domain is not None + domain = domain.upper() + if not valid_netbios_name(domain): + raise InvalidNetbiosName(domain) + else: + domaindn = "CN=" + netbiosname + domain = netbiosname + + if rootdn is None: + rootdn = domaindn + + configdn = "CN=Configuration," + rootdn + schemadn = "CN=Schema," + configdn message("set DOMAIN SID: %s" % str(domainsid)) message("Provisioning for %s in realm %s" % (domain, realm)) @@ -682,9 +806,8 @@ def provision(lp, setup_dir, message, blank, paths, session_info, smbconfsuffix = "dc" elif serverrole == "member": smbconfsuffix = "member" - else: - assert "Invalid server role setting: %s" % serverrole - setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), paths.smbconf, { + setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), + paths.smbconf, { "HOSTNAME": hostname, "DOMAIN_CONF": domain, "REALM_CONF": realm, @@ -692,7 +815,7 @@ def provision(lp, setup_dir, message, blank, paths, session_info, "NETLOGONPATH": paths.netlogon, "SYSVOLPATH": paths.sysvol, }) - lp.reload() + lp.load(paths.smbconf) # only install a new shares config db if there is none if not os.path.exists(paths.shareconf): @@ -701,6 +824,7 @@ def provision(lp, setup_dir, message, blank, paths, session_info, credentials=credentials, lp=lp) share_ldb.load_ldif_file_add(setup_path("share.ldif")) + message("Setting up secrets.ldb") secrets_ldb = setup_secretsdb(paths.secrets, setup_path, session_info=session_info, @@ -714,44 +838,47 @@ def provision(lp, setup_dir, message, blank, paths, session_info, setup_templatesdb(paths.templates, setup_path, session_info=session_info, credentials=credentials, lp=lp) - samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, credentials=credentials, - lp=lp, schemadn=schemadn, configdn=configdn, domaindn=domaindn, - dnsdomain=dnsdomain, netbiosname=netbiosname, realm=realm, message=message, - hostname=hostname, rootdn=rootdn, erase=erase, domainsid=domainsid, aci=aci, - rdn_dc=rdn_dc, domainguid=domainguid, policyguid=policyguid, - domainname=domain, blank=blank, adminpass=adminpass, krbtgtpass=krbtgtpass, - hostguid=hostguid, invocationid=invocationid, machinepass=machinepass, - dnspass=dnspass) + samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, + credentials=credentials, lp=lp, schemadn=schemadn, + configdn=configdn, domaindn=domaindn, + dnsdomain=dnsdomain, netbiosname=netbiosname, + realm=realm, message=message, hostname=hostname, + rootdn=rootdn, erase=erase, domainsid=domainsid, + aci=aci, domainguid=domainguid, policyguid=policyguid, + domainname=domain, fill=samdb_fill, + adminpass=adminpass, krbtgtpass=krbtgtpass, + hostguid=hostguid, invocationid=invocationid, + machinepass=machinepass, dnspass=dnspass, + serverrole=serverrole, ldap_backend=ldap_backend, + ldap_backend_type=ldap_backend_type) if lp.get("server role") == "domain controller": - os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}"), 0755) - os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "Machine"), 0755) - os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "User"), 0755) - if not os.path.isdir(paths.netlogon): + policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies", + "{" + policyguid + "}") + os.makedirs(policy_path, 0755) + os.makedirs(os.path.join(policy_path, "Machine"), 0755) + os.makedirs(os.path.join(policy_path, "User"), 0755) + if not os.path.isdir(paths.netlogon): os.makedirs(paths.netlogon, 0755) - secrets_ldb = Ldb(paths.secrets, session_info=session_info, credentials=credentials, lp=lp) - setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), { - "MACHINEPASS_B64": b64encode(machinepass), - "DOMAIN": domain, - "REALM": realm, - "LDAPTIME": timestring(int(time.time())), - "DNSDOMAIN": dnsdomain, - "DOMAINSID": str(domainsid), - "SECRETS_KEYTAB": paths.keytab, - "NETBIOSNAME": netbiosname, - "SAM_LDB": paths.samdb, - "DNS_KEYTAB": paths.dns_keytab, - "DNSPASS_B64": b64encode(dnspass), - }) - - if not blank: - setup_name_mappings(samdb, str(domainsid), - domaindn, root=root, nobody=nobody, - nogroup=nogroup, wheel=wheel, users=users, - backup=backup) + secrets_ldb = Ldb(paths.secrets, session_info=session_info, + credentials=credentials, lp=lp) + secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm, + netbiosname=netbiosname, domainsid=domainsid, + keytab_path=paths.keytab, samdb_url=paths.samdb, + dns_keytab_path=paths.dns_keytab, dnspass=dnspass, + machinepass=machinepass, dnsdomain=dnsdomain) + + if samdb_fill == FILL_FULL: + setup_name_mappings(samdb, str(domainsid), domaindn, root=root, + nobody=nobody, nogroup=nogroup, wheel=wheel, + users=users, backup=backup) + + message("Setting up sam.ldb rootDSE marking as synchronized") + setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif")) message("Setting up phpLDAPadmin configuration") - create_phplpapdadmin_config(paths.phpldapadminconfig, setup_path, paths.s4_ldapi_path) + create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, + ldapi_url) message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig) @@ -759,9 +886,9 @@ def provision(lp, setup_dir, message, blank, paths, session_info, samdb = SamDB(paths.samdb, session_info=session_info, credentials=credentials, lp=lp) - domainguid = samdb.searchone(domaindn, "objectGUID") + domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID") assert isinstance(domainguid, str) - hostguid = samdb.searchone(domaindn, "objectGUID", + hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID", expression="(&(objectClass=computer)(cn=%s))" % hostname, scope=SCOPE_SUBTREE) assert isinstance(hostguid, str) @@ -775,15 +902,15 @@ def provision(lp, setup_dir, message, blank, paths, session_info, return domaindn -def create_phplpapdadmin_config(path, setup_path, s4_ldapi_path): + +def create_phpldapadmin_config(path, setup_path, ldapi_uri): """Create a PHP LDAP admin configuration file. :param path: Path to write the configuration to. :param setup_path: Function to generate setup paths. - :param s4_ldapi_path: Path to Samba 4 LDAPI socket. """ - setup_file(setup_path("phpldapadmin-config.php"), - path, {"S4_LDAPI_URI": "ldapi://%s" % s4_ldapi_path.replace("/", "%2F")}) + setup_file(setup_path("phpldapadmin-config.php"), path, + {"S4_LDAPI_URI": ldapi_uri}) def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, @@ -802,6 +929,7 @@ def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, :param domainguid: GUID of the domain. :param hostguid: GUID of the host. """ + assert isinstance(domainguid, str) setup_file(setup_path("provision.zone"), path, { "DNSPASS_B64": b64encode(dnspass), @@ -817,7 +945,7 @@ def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn, def load_schema(setup_path, samdb, schemadn, netbiosname, configdn): - """Load schema. + """Load schema for the SamDB. :param samdb: Load a schema into a SamDB. :param setup_path: Setup path function. @@ -833,6 +961,7 @@ def load_schema(setup_path, samdb, schemadn, netbiosname, configdn): "SCHEMADN": schemadn, "NETBIOSNAME": netbiosname, "CONFIGDN": configdn, - "DEFAULTSITE": DEFAULTSITE}) + "DEFAULTSITE": DEFAULTSITE + }) samdb.attach_schema_from_ldif(head_data, schema_data) diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index 353eaee198..c11fabf553 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -1,10 +1,10 @@ #!/usr/bin/python # Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 # # Based on the original in EJS: -# Copyright (C) Andrew Tridgell 2005 +# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -105,7 +105,7 @@ userAccountControl: %u assert(len(res) == 1 and res[0].defaultNamingContext is not None) domain_dn = res[0]["defaultNamingContext"][0] assert(domain_dn is not None) - dom_users = self.searchone(domain_dn, "dn", "name=Domain Users") + dom_users = self.searchone(basedn=domain_dn, attribute="dn", expression="name=Domain Users") assert(dom_users is not None) user_dn = "CN=%s,CN=Users,%s" % (username, domain_dn) @@ -145,3 +145,10 @@ member: %s def attach_schema_from_ldif(self, pf, df): misc.dsdb_attach_schema_from_ldif_file(self, pf, df) + + def set_invocation_id(self, invocation_id): + """Set the invocation id for this SamDB handle. + + :param invocation_id: GUID of the invocation id. + """ + misc.dsdb_set_ntds_invocation_id(self, invocation_id) diff --git a/source4/scripting/python/samba/tests/__init__.py b/source4/scripting/python/samba/tests/__init__.py index ad8a2524b5..9402002674 100644 --- a/source4/scripting/python/samba/tests/__init__.py +++ b/source4/scripting/python/samba/tests/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/python # Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -67,6 +67,10 @@ class SubstituteVarTestCase(unittest.TestCase): def test_unknown_var(self): self.assertEquals("foo ${bla} gsff", samba.substitute_var("foo ${bla} gsff", {"bar": "bla"})) + + def test_check_all_substituted(self): + samba.check_all_substituted("nothing to see here") + self.assertRaises(Exception, samba.check_all_substituted, "Not subsituted: ${FOOBAR}") class LdbExtensionTests(TestCaseInTempDir): @@ -75,7 +79,7 @@ class LdbExtensionTests(TestCaseInTempDir): l = samba.Ldb(path) try: l.add({"dn": "foo=dc", "bar": "bla"}) - self.assertEquals("bla", l.searchone(ldb.Dn(l, "foo=dc"), "bar")) + self.assertEquals("bla", l.searchone(basedn=ldb.Dn(l, "foo=dc"), attribute="bar")) finally: del l os.unlink(path) diff --git a/source4/scripting/python/samba/tests/provision.py b/source4/scripting/python/samba/tests/provision.py index f5a0339c1f..54a7782b3d 100644 --- a/source4/scripting/python/samba/tests/provision.py +++ b/source4/scripting/python/samba/tests/provision.py @@ -1,7 +1,7 @@ #!/usr/bin/python # Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,9 +18,14 @@ # import os -from samba.provision import setup_secretsdb +from samba.provision import setup_secretsdb, secretsdb_become_dc, findnss import samba.tests from ldb import Dn +import param +import unittest + +lp = param.LoadParm() +lp.load("st/dc/etc/smb.conf") setup_dir = "setup" def setup_path(file): @@ -28,15 +33,59 @@ def setup_path(file): class ProvisionTestCase(samba.tests.TestCaseInTempDir): + """Some simple tests for individual functions in the provisioning code. + """ def test_setup_secretsdb(self): path = os.path.join(self.tempdir, "secrets.ldb") - ldb = setup_secretsdb(path, setup_path, None, None, None) + ldb = setup_secretsdb(path, setup_path, None, None, lp=lp) try: self.assertEquals("LSA Secrets", - ldb.searchone(Dn(ldb, "CN=LSA Secrets"), "CN")) + ldb.searchone(basedn="CN=LSA Secrets", attribute="CN")) finally: del ldb os.unlink(path) + + def test_become_dc(self): + path = os.path.join(self.tempdir, "secrets.ldb") + secrets_ldb = setup_secretsdb(path, setup_path, None, None, lp=lp) + try: + secretsdb_become_dc(secrets_ldb, setup_path, domain="EXAMPLE", + realm="example", netbiosname="myhost", + domainsid="S-5-22", keytab_path="keytab.path", + samdb_url="ldap://url/", + dns_keytab_path="dns.keytab", dnspass="bla", + machinepass="machinepass", dnsdomain="example.com") + self.assertEquals(1, + len(secrets_ldb.search("samAccountName=krbtgt,flatname=EXAMPLE,CN=Principals"))) + self.assertEquals("keytab.path", + secrets_ldb.searchone(basedn="flatname=EXAMPLE,CN=primary domains", + expression="(privateKeytab=*)", + attribute="privateKeytab")) + self.assertEquals("S-5-22", + secrets_ldb.searchone(basedn="flatname=EXAMPLE,CN=primary domains", + expression="objectSid=*", attribute="objectSid")) + + finally: + del secrets_ldb + os.unlink(path) + + +class FindNssTests(unittest.TestCase): + """Test findnss() function.""" + def test_nothing(self): + def x(y): + raise KeyError + self.assertRaises(KeyError, findnss, x, []) + + def test_first(self): + self.assertEquals("bla", findnss(lambda x: "bla", ["bla"])) + + def test_skip_first(self): + def x(y): + if y != "bla": + raise KeyError + return "ha" + self.assertEquals("ha", findnss(x, ["bloe", "bla"])) class Disabled: @@ -73,3 +122,4 @@ class Disabled: def test_erase_partitions(self): raise NotImplementedError(self.test_erase_partitions) + diff --git a/source4/scripting/python/samba/upgrade.py b/source4/scripting/python/samba/upgrade.py index abf1127c36..8bf75d776e 100644 --- a/source4/scripting/python/samba/upgrade.py +++ b/source4/scripting/python/samba/upgrade.py @@ -7,9 +7,10 @@ """Support code for upgrading from Samba 3 to Samba 4.""" -from provision import findnss, provision +from provision import findnss, provision, FILL_DRS import grp import ldb +import time import pwd import uuid import registry @@ -162,7 +163,6 @@ def import_wins(samba4_winsdb, samba3_winsdb): :param samba3_winsdb: WINS database to import from """ version_id = 0 - import time for (name, (ttl, ips, nb_flags)) in samba3_winsdb.items(): version_id+=1 @@ -245,8 +245,9 @@ def upgrade_provision(samba3, setup_dir, message, credentials, session_info, lp, else: machinepass = None - domaindn = provision(lp=lp, setup_dir=setup_dir, message=message, blank=True, ldapbackend=None, - paths=paths, session_info=session_info, credentials=credentials, realm=realm, + domaindn = provision(lp=lp, setup_dir=setup_dir, message=message, + samdb_fill=FILL_DRS, paths=paths, session_info=session_info, + credentials=credentials, realm=realm, domain=domainname, domainsid=domainsid, domainguid=domainguid, machinepass=machinepass, serverrole=serverrole) |