diff options
Diffstat (limited to 'source4/scripting/python')
93 files changed, 4234 insertions, 13389 deletions
diff --git a/source4/scripting/python/config.mk b/source4/scripting/python/config.mk new file mode 100644 index 0000000000..a5e3f25d59 --- /dev/null +++ b/source4/scripting/python/config.mk @@ -0,0 +1,35 @@ +[SUBSYSTEM::LIBPYTHON] +PUBLIC_DEPENDENCIES = EXT_LIB_PYTHON +PRIVATE_DEPENDENCIES = PYTALLOC +INIT_FUNCTION_SENTINEL = { NULL, NULL } + +LIBPYTHON_OBJ_FILES = $(addprefix $(pyscriptsrcdir)/, modules.o) + +[SUBSYSTEM::PYTALLOC] +PUBLIC_DEPENDENCIES = EXT_LIB_PYTHON LIBTALLOC + +PYTALLOC_OBJ_FILES = ../lib/talloc/pytalloc.o + +[PYTHON::python_uuid] +PRIVATE_DEPENDENCIES = LIBNDR + +python_uuid_OBJ_FILES = $(pyscriptsrcdir)/uuidmodule.o + +[PYTHON::python_glue] +LIBRARY_REALNAME = samba/glue.$(SHLIBEXT) +PRIVATE_DEPENDENCIES = LIBNDR LIBLDB SAMDB CREDENTIALS pyldb python_dcerpc_misc python_dcerpc_security pyauth pyldb_util pyparam_util + +python_glue_OBJ_FILES = $(pyscriptsrcdir)/pyglue.o + +$(python_glue_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir) + +_PY_FILES = $(shell find $(pyscriptsrcdir)/samba ../lib/subunit/python -name "*.py") + +$(eval $(foreach pyfile, $(_PY_FILES),$(call python_py_module_template,$(patsubst $(pyscriptsrcdir)/%,%,$(subst ../lib/subunit/python,,$(pyfile))),$(pyfile)))) + +EPYDOC_OPTIONS = --no-private --url http://www.samba.org/ --no-sourcecode + +epydoc:: pythonmods + PYTHONPATH=$(pythonbuilddir):../lib/subunit/python epydoc $(EPYDOC_OPTIONS) samba tdb ldb subunit + +install:: installpython diff --git a/source4/scripting/python/examples/netbios.py b/source4/scripting/python/examples/netbios.py index a29b09f051..3671076a59 100644 --- a/source4/scripting/python/examples/netbios.py +++ b/source4/scripting/python/examples/netbios.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 diff --git a/source4/scripting/python/examples/samr.py b/source4/scripting/python/examples/samr.py index 9b8e31e434..c0e3167a97 100755 --- a/source4/scripting/python/examples/samr.py +++ b/source4/scripting/python/examples/samr.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: utf-8 -*- # Unix SMB/CIFS implementation. diff --git a/source4/scripting/python/examples/winreg.py b/source4/scripting/python/examples/winreg.py index 484a69b842..80b48ecfd7 100755 --- a/source4/scripting/python/examples/winreg.py +++ b/source4/scripting/python/examples/winreg.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # # tool to manipulate a remote registry # Copyright Andrew Tridgell 2005 diff --git a/source4/scripting/python/modules.c b/source4/scripting/python/modules.c index 78cdbc0d87..e53f4cfaf2 100644 --- a/source4/scripting/python/modules.c +++ b/source4/scripting/python/modules.c @@ -17,47 +17,54 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <Python.h> #include "includes.h" #include "scripting/python/modules.h" -#include "dynconfig/dynconfig.h" +#include <Python.h> -static bool PySys_PathPrepend(PyObject *list, const char *path) -{ - PyObject *py_path = PyString_FromString(path); - if (py_path == NULL) - return false; +extern void init_ldb(void); +extern void init_security(void); +extern void init_registry(void); +extern void init_param(void); +extern void init_misc(void); +extern void init_ldb(void); +extern void init_auth(void); +extern void init_credentials(void); +extern void init_tdb(void); +extern void init_dcerpc(void); +extern void init_events(void); +extern void inituuid(void); +extern void init_net(void); +extern void initecho(void); +extern void initdfs(void); +extern void initdrsuapi(void); +extern void initwinreg(void); +extern void initepmapper(void); +extern void initinitshutdown(void); +extern void initmgmt(void); +extern void initnet(void); +extern void initatsvc(void); +extern void initsamr(void); +extern void initlsa(void); +extern void initsvcctl(void); +extern void initwkssvc(void); +extern void initunixinfo(void); +extern void init_libcli_nbt(void); +extern void init_libcli_smb(void); - return (PyList_Insert(list, 0, py_path) == 0); -} +static struct _inittab py_modules[] = { STATIC_LIBPYTHON_MODULES }; -bool py_update_path(void) +void py_load_samba_modules(void) { - PyObject *mod_sys, *py_path; - - mod_sys = PyImport_ImportModule("sys"); - if (mod_sys == NULL) { - return false; - } - - py_path = PyObject_GetAttrString(mod_sys, "path"); - if (py_path == NULL) { - return false; - } - - if (!PyList_Check(py_path)) { - return false; - } - - if (!PySys_PathPrepend(py_path, dyn_PYTHONDIR)) { - return false; - } - - if (strcmp(dyn_PYTHONARCHDIR, dyn_PYTHONDIR) != 0) { - if (!PySys_PathPrepend(py_path, dyn_PYTHONARCHDIR)) { - return false; - } + int i; + for (i = 0; i < ARRAY_SIZE(py_modules); i++) { + PyImport_ExtendInittab(&py_modules[i]); } +} - return true; +void py_update_path(const char *bindir) +{ + char *newpath; + asprintf(&newpath, "%s/python:%s/../scripting/python:%s", bindir, bindir, Py_GetPath()); + PySys_SetPath(newpath); + free(newpath); } diff --git a/source4/scripting/python/modules.h b/source4/scripting/python/modules.h index e7e97aa1bf..6b242ee257 100644 --- a/source4/scripting/python/modules.h +++ b/source4/scripting/python/modules.h @@ -20,6 +20,9 @@ #ifndef __SAMBA_PYTHON_MODULES_H__ #define __SAMBA_PYTHON_MODULES_H__ -bool py_update_path(void); +void py_load_samba_modules(void); +void py_update_path(const char *bindir); + +#define py_iconv_convenience(mem_ctx) smb_iconv_convenience_init(mem_ctx, "ASCII", PyUnicode_GetDefaultEncoding(), true) #endif /* __SAMBA_PYTHON_MODULES_H__ */ diff --git a/source4/scripting/python/pyglue.c b/source4/scripting/python/pyglue.c index f89785f971..753f2df464 100644 --- a/source4/scripting/python/pyglue.c +++ b/source4/scripting/python/pyglue.c @@ -17,13 +17,55 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <Python.h> #include "includes.h" +#include "ldb.h" +#include "ldb_errors.h" +#include "ldb_wrap.h" +#include "param/param.h" +#include "auth/credentials/credentials.h" +#include "dsdb/samdb/samdb.h" +#include "lib/ldb-samba/ldif_handlers.h" +#include "librpc/ndr/libndr.h" #include "version.h" +#include <Python.h> +#include "lib/ldb/pyldb.h" +#include "libcli/util/pyerrors.h" +#include "libcli/security/security.h" +#include "auth/pyauth.h" #include "param/pyparam.h" -#include "lib/socket/netif.h" +#include "auth/credentials/pycredentials.h" + +#ifndef Py_RETURN_NONE +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +/* FIXME: These should be in a header file somewhere, once we finish moving + * away from SWIG .. */ +#define PyErr_LDB_OR_RAISE(py_ldb, ldb) \ +/* if (!PyLdb_Check(py_ldb)) { \ + PyErr_SetString(py_ldb_get_exception(), "Ldb connection object required"); \ + return NULL; \ + } */\ + ldb = PyLdb_AsLdbContext(py_ldb); + +static void PyErr_SetLdbError(PyObject *error, int ret, struct ldb_context *ldb_ctx) +{ + if (ret == LDB_ERR_PYTHON_EXCEPTION) + return; /* Python exception should already be set, just keep that */ + + PyErr_SetObject(error, + Py_BuildValue(discard_const_p(char, "(i,s)"), ret, + ldb_ctx == NULL?ldb_strerror(ret):ldb_errstring(ldb_ctx))); +} -void init_glue(void); +static PyObject *py_ldb_get_exception(void) +{ + PyObject *mod = PyImport_ImportModule("ldb"); + if (mod == NULL) + return NULL; + + return PyObject_GetAttrString(mod, "LdbError"); +} static PyObject *py_generate_random_str(PyObject *self, PyObject *args) { @@ -39,181 +81,473 @@ static PyObject *py_generate_random_str(PyObject *self, PyObject *args) return ret; } -static PyObject *py_generate_random_password(PyObject *self, PyObject *args) +static PyObject *py_unix2nttime(PyObject *self, PyObject *args) +{ + time_t t; + NTTIME nt; + if (!PyArg_ParseTuple(args, "I", &t)) + return NULL; + + unix_to_nt_time(&nt, t); + + return PyInt_FromLong((uint64_t)nt); +} + +static PyObject *py_set_debug_level(PyObject *self, PyObject *args) +{ + unsigned level; + if (!PyArg_ParseTuple(args, "I", &level)) + return NULL; + (DEBUGLEVEL) = level; + Py_RETURN_NONE; +} + +static PyObject *py_ldb_set_credentials(PyObject *self, PyObject *args) +{ + PyObject *py_creds, *py_ldb; + struct cli_credentials *creds; + struct ldb_context *ldb; + if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_creds)) + return NULL; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + creds = cli_credentials_from_py_object(py_creds); + if (creds == NULL) { + PyErr_SetString(PyExc_TypeError, "Expected credentials object"); + return NULL; + } + + ldb_set_opaque(ldb, "credentials", creds); + + Py_RETURN_NONE; +} + +static PyObject *py_ldb_set_loadparm(PyObject *self, PyObject *args) { - int min, max; + PyObject *py_lp_ctx, *py_ldb; + struct loadparm_context *lp_ctx; + struct ldb_context *ldb; + if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_lp_ctx)) + return NULL; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + lp_ctx = lp_from_py_object(py_lp_ctx); + if (lp_ctx == NULL) { + PyErr_SetString(PyExc_TypeError, "Expected loadparm object"); + return NULL; + } + + ldb_set_opaque(ldb, "loadparm", lp_ctx); + + Py_RETURN_NONE; +} + + +static PyObject *py_ldb_set_session_info(PyObject *self, PyObject *args) +{ + PyObject *py_session_info, *py_ldb; + struct auth_session_info *info; + struct ldb_context *ldb; + if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_session_info)) + return NULL; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); + /*if (!PyAuthSession_Check(py_session_info)) { + PyErr_SetString(PyExc_TypeError, "Expected session info object"); + return NULL; + }*/ + + info = PyAuthSession_AsSession(py_session_info); + + ldb_set_opaque(ldb, "sessionInfo", info); + + Py_RETURN_NONE; +} + +static PyObject *py_ldb_set_utf8_casefold(PyObject *self, PyObject *args) +{ + PyObject *py_ldb; + struct ldb_context *ldb; + + if (!PyArg_ParseTuple(args, "O", &py_ldb)) + return NULL; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + ldb_set_utf8_fns(ldb, NULL, wrap_casefold); + + Py_RETURN_NONE; +} + +static PyObject *py_samdb_set_domain_sid(PyLdbObject *self, PyObject *args) +{ + PyObject *py_ldb, *py_sid; + struct ldb_context *ldb; + struct dom_sid *sid; + bool ret; + + if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_sid)) + return NULL; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + sid = dom_sid_parse_talloc(NULL, PyString_AsString(py_sid)); + + ret = samdb_set_domain_sid(ldb, sid); + if (!ret) { + PyErr_SetString(PyExc_RuntimeError, "set_domain_sid failed"); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *py_samdb_get_domain_sid(PyLdbObject *self, PyObject *args) +{ + PyObject *py_ldb; + struct ldb_context *ldb; + const struct dom_sid *sid; PyObject *ret; char *retstr; - if (!PyArg_ParseTuple(args, "ii", &min, &max)) + + if (!PyArg_ParseTuple(args, "O", &py_ldb)) return NULL; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); - retstr = generate_random_password(NULL, min, max); - if (retstr == NULL) { + sid = samdb_domain_sid(ldb); + if (!sid) { + PyErr_SetString(PyExc_RuntimeError, "samdb_domain_sid failed"); return NULL; - } + } + retstr = dom_sid_string(NULL, sid); ret = PyString_FromString(retstr); talloc_free(retstr); return ret; } -static PyObject *py_unix2nttime(PyObject *self, PyObject *args) +static PyObject *py_ldb_register_samba_handlers(PyObject *self, PyObject *args) { - time_t t; - NTTIME nt; - if (!PyArg_ParseTuple(args, "I", &t)) + PyObject *py_ldb; + struct ldb_context *ldb; + int ret; + + if (!PyArg_ParseTuple(args, "O", &py_ldb)) return NULL; - unix_to_nt_time(&nt, t); + PyErr_LDB_OR_RAISE(py_ldb, ldb); + ret = ldb_register_samba_handlers(ldb); - return PyLong_FromLongLong((uint64_t)nt); + PyErr_LDB_ERROR_IS_ERR_RAISE(py_ldb_get_exception(), ret, ldb); + Py_RETURN_NONE; } -static PyObject *py_nttime2unix(PyObject *self, PyObject *args) +static PyObject *py_dsdb_set_ntds_invocation_id(PyObject *self, PyObject *args) { - time_t t; - NTTIME nt; - if (!PyArg_ParseTuple(args, "K", &nt)) + PyObject *py_ldb, *py_guid; + bool ret; + struct GUID guid; + struct ldb_context *ldb; + if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_guid)) return NULL; - t = nt_time_to_unix(nt); + PyErr_LDB_OR_RAISE(py_ldb, ldb); + GUID_from_string(PyString_AsString(py_guid), &guid); - return PyInt_FromLong((uint64_t)t); + ret = samdb_set_ntds_invocation_id(ldb, &guid); + if (!ret) { + PyErr_SetString(PyExc_RuntimeError, "set_ntds_invocation_id failed"); + return NULL; + } + Py_RETURN_NONE; } -static PyObject *py_nttime2string(PyObject *self, PyObject *args) +static PyObject *py_dsdb_set_opaque_integer(PyObject *self, PyObject *args) { - PyObject *ret; - NTTIME nt; + PyObject *py_ldb; + int value; + int *old_val, *new_val; + char *py_opaque_name, *opaque_name_talloc; + struct ldb_context *ldb; TALLOC_CTX *tmp_ctx; - const char *string; - if (!PyArg_ParseTuple(args, "K", &nt)) + + if (!PyArg_ParseTuple(args, "Osi", &py_ldb, &py_opaque_name, &value)) return NULL; - tmp_ctx = talloc_new(NULL); + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + /* see if we have a cached copy */ + old_val = (int *)ldb_get_opaque(ldb, + py_opaque_name); + + if (old_val) { + *old_val = value; + Py_RETURN_NONE; + } + + tmp_ctx = talloc_new(ldb); if (tmp_ctx == NULL) { - PyErr_NoMemory(); - return NULL; + goto failed; + } + + new_val = talloc(tmp_ctx, int); + if (!new_val) { + goto failed; + } + + opaque_name_talloc = talloc_strdup(tmp_ctx, py_opaque_name); + if (!opaque_name_talloc) { + goto failed; } + + *new_val = value; - string = nt_time_string(tmp_ctx, nt); - ret = PyString_FromString(string); + /* cache the domain_sid in the ldb */ + if (ldb_set_opaque(ldb, opaque_name_talloc, new_val) != LDB_SUCCESS) { + goto failed; + } + talloc_steal(ldb, new_val); + talloc_steal(ldb, opaque_name_talloc); talloc_free(tmp_ctx); - return ret; + Py_RETURN_NONE; + +failed: + talloc_free(tmp_ctx); + PyErr_SetString(PyExc_RuntimeError, "Failed to set opaque integer into the ldb!\n"); + return NULL; } -static PyObject *py_set_debug_level(PyObject *self, PyObject *args) +static PyObject *py_dsdb_set_global_schema(PyObject *self, PyObject *args) { - unsigned level; - if (!PyArg_ParseTuple(args, "I", &level)) + PyObject *py_ldb; + struct ldb_context *ldb; + int ret; + if (!PyArg_ParseTuple(args, "O", &py_ldb)) return NULL; - (DEBUGLEVEL) = level; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + ret = dsdb_set_global_schema(ldb); + PyErr_LDB_ERROR_IS_ERR_RAISE(py_ldb_get_exception(), ret, ldb); + Py_RETURN_NONE; } -static PyObject *py_get_debug_level(PyObject *self) +static PyObject *py_dsdb_set_schema_from_ldif(PyObject *self, PyObject *args) { - return PyInt_FromLong(DEBUGLEVEL); + WERROR result; + char *pf, *df; + PyObject *py_ldb; + struct ldb_context *ldb; + + if (!PyArg_ParseTuple(args, "Oss", &py_ldb, &pf, &df)) + return NULL; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + result = dsdb_set_schema_from_ldif(ldb, pf, df); + PyErr_WERROR_IS_ERR_RAISE(result); + + Py_RETURN_NONE; } -/* - return the list of interface IPs we have configured - takes an loadparm context, returns a list of IPs in string form +static PyObject *py_dsdb_convert_schema_to_openldap(PyObject *self, PyObject *args) +{ + char *target_str, *mapping; + PyObject *py_ldb; + struct ldb_context *ldb; + PyObject *ret; + char *retstr; + + if (!PyArg_ParseTuple(args, "Oss", &py_ldb, &target_str, &mapping)) + return NULL; + + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + retstr = dsdb_convert_schema_to_openldap(ldb, target_str, mapping); + if (!retstr) { + PyErr_SetString(PyExc_RuntimeError, "dsdb_convert_schema_to_openldap failed"); + return NULL; + } + ret = PyString_FromString(retstr); + talloc_free(retstr); + return ret; +} - Does not return addresses on 127.0.0.0/8 - */ -static PyObject *py_interface_ips(PyObject *self, PyObject *args) +static PyObject *py_dsdb_write_prefixes_from_schema_to_ldb(PyObject *self, PyObject *args) { - PyObject *pylist; - int count; - TALLOC_CTX *tmp_ctx; - PyObject *py_lp_ctx; - struct loadparm_context *lp_ctx; - struct interface *ifaces; - int i, ifcount; - int all_interfaces; + PyObject *py_ldb; + struct ldb_context *ldb; + WERROR result; + struct dsdb_schema *schema; - if (!PyArg_ParseTuple(args, "Oi", &py_lp_ctx, &all_interfaces)) + if (!PyArg_ParseTuple(args, "O", &py_ldb)) return NULL; - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - PyErr_NoMemory(); + PyErr_LDB_OR_RAISE(py_ldb, ldb); + + schema = dsdb_get_schema(ldb); + if (!schema) { + PyErr_SetString(PyExc_RuntimeError, "Failed to set find a schema on ldb!\n"); return NULL; } - lp_ctx = lpcfg_from_py_object(tmp_ctx, py_lp_ctx); - if (lp_ctx == NULL) { - talloc_free(tmp_ctx); + result = dsdb_write_prefixes_from_schema_to_ldb(NULL, ldb, schema); + PyErr_WERROR_IS_ERR_RAISE(result); + + Py_RETURN_NONE; +} + +static PyObject *py_dsdb_set_schema_from_ldb(PyObject *self, PyObject *args) +{ + PyObject *py_ldb; + struct ldb_context *ldb; + PyObject *py_from_ldb; + struct ldb_context *from_ldb; + struct dsdb_schema *schema; + int ret; + if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_from_ldb)) return NULL; - } - load_interfaces(tmp_ctx, lpcfg_interfaces(lp_ctx), &ifaces); + PyErr_LDB_OR_RAISE(py_ldb, ldb); - count = iface_count(ifaces); + PyErr_LDB_OR_RAISE(py_from_ldb, from_ldb); - /* first count how many are not loopback addresses */ - for (ifcount = i = 0; i<count; i++) { - const char *ip = iface_n_ip(ifaces, i); - if (!(!all_interfaces && iface_same_net(ip, "127.0.0.1", "255.0.0.0"))) { - ifcount++; - } + schema = dsdb_get_schema(from_ldb); + if (!schema) { + PyErr_SetString(PyExc_RuntimeError, "Failed to set find a schema on 'from' ldb!\n"); + return NULL; } - pylist = PyList_New(ifcount); - for (ifcount = i = 0; i<count; i++) { - const char *ip = iface_n_ip(ifaces, i); - if (!(!all_interfaces && iface_same_net(ip, "127.0.0.1", "255.0.0.0"))) { - PyList_SetItem(pylist, ifcount, PyString_FromString(ip)); - ifcount++; - } + ret = dsdb_reference_schema(ldb, schema, true); + PyErr_LDB_ERROR_IS_ERR_RAISE(py_ldb_get_exception(), ret, ldb); + + Py_RETURN_NONE; +} + +static PyObject *py_dom_sid_to_rid(PyLdbObject *self, PyObject *args) +{ + PyObject *py_sid; + struct dom_sid *sid; + uint32_t rid; + NTSTATUS status; + + if(!PyArg_ParseTuple(args, "O", &py_sid)) + return NULL; + + sid = dom_sid_parse_talloc(NULL, PyString_AsString(py_sid)); + + status = dom_sid_split_rid(NULL, sid, NULL, &rid); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetString(PyExc_RuntimeError, "dom_sid_split_rid failed"); + return NULL; } - talloc_free(tmp_ctx); - return pylist; + + return PyInt_FromLong(rid); } static PyMethodDef py_misc_methods[] = { { "generate_random_str", (PyCFunction)py_generate_random_str, METH_VARARGS, - "generate_random_str(len) -> string\n" - "Generate random string with specified length." }, - { "generate_random_password", (PyCFunction)py_generate_random_password, - METH_VARARGS, "generate_random_password(min, max) -> string\n" - "Generate random password with a length >= min and <= max." }, + "random_password(len) -> string\n" + "Generate random password with specified length." }, { "unix2nttime", (PyCFunction)py_unix2nttime, METH_VARARGS, "unix2nttime(timestamp) -> nttime" }, - { "nttime2unix", (PyCFunction)py_nttime2unix, METH_VARARGS, - "nttime2unix(nttime) -> timestamp" }, - { "nttime2string", (PyCFunction)py_nttime2string, METH_VARARGS, - "nttime2string(nttime) -> string" }, + { "ldb_set_credentials", (PyCFunction)py_ldb_set_credentials, METH_VARARGS, + "ldb_set_credentials(ldb, credentials) -> None\n" + "Set credentials to use when connecting." }, + { "ldb_set_session_info", (PyCFunction)py_ldb_set_session_info, METH_VARARGS, + "ldb_set_session_info(ldb, session_info)\n" + "Set session info to use when connecting." }, + { "ldb_set_loadparm", (PyCFunction)py_ldb_set_loadparm, METH_VARARGS, + "ldb_set_loadparm(ldb, session_info)\n" + "Set loadparm context to use when connecting." }, + { "samdb_set_domain_sid", (PyCFunction)py_samdb_set_domain_sid, METH_VARARGS, + "samdb_set_domain_sid(samdb, sid)\n" + "Set SID of domain to use." }, + { "samdb_get_domain_sid", (PyCFunction)py_samdb_get_domain_sid, METH_VARARGS, + "samdb_get_domain_sid(samdb)\n" + "Get SID of domain in use." }, + { "ldb_register_samba_handlers", (PyCFunction)py_ldb_register_samba_handlers, METH_VARARGS, + "ldb_register_samba_handlers(ldb)\n" + "Register Samba-specific LDB modules and schemas." }, + { "ldb_set_utf8_casefold", (PyCFunction)py_ldb_set_utf8_casefold, METH_VARARGS, + "ldb_set_utf8_casefold(ldb)\n" + "Set the right Samba casefolding function for UTF8 charset." }, + { "dsdb_set_ntds_invocation_id", (PyCFunction)py_dsdb_set_ntds_invocation_id, METH_VARARGS, + NULL }, + { "dsdb_set_opaque_integer", (PyCFunction)py_dsdb_set_opaque_integer, METH_VARARGS, + NULL }, + { "dsdb_set_global_schema", (PyCFunction)py_dsdb_set_global_schema, METH_VARARGS, + NULL }, + { "dsdb_set_schema_from_ldif", (PyCFunction)py_dsdb_set_schema_from_ldif, METH_VARARGS, + NULL }, + { "dsdb_write_prefixes_from_schema_to_ldb", (PyCFunction)py_dsdb_write_prefixes_from_schema_to_ldb, METH_VARARGS, + NULL }, + { "dsdb_set_schema_from_ldb", (PyCFunction)py_dsdb_set_schema_from_ldb, METH_VARARGS, + NULL }, + { "dsdb_convert_schema_to_openldap", (PyCFunction)py_dsdb_convert_schema_to_openldap, METH_VARARGS, + NULL }, + { "dom_sid_to_rid", (PyCFunction)py_dom_sid_to_rid, METH_VARARGS, + NULL }, { "set_debug_level", (PyCFunction)py_set_debug_level, METH_VARARGS, "set debug level" }, - { "get_debug_level", (PyCFunction)py_get_debug_level, METH_NOARGS, - "get debug level" }, - { "interface_ips", (PyCFunction)py_interface_ips, METH_VARARGS, - "get interface IP address list"}, { NULL } }; -void init_glue(void) +void initglue(void) { PyObject *m; - debug_setup_talloc_log(); - - m = Py_InitModule3("_glue", py_misc_methods, + m = Py_InitModule3("glue", py_misc_methods, "Python bindings for miscellaneous Samba functions."); if (m == NULL) return; - PyModule_AddObject(m, "version", - PyString_FromString(SAMBA_VERSION_STRING)); - - /* one of the most annoying things about python scripts is - that they don't die when you hit control-C. This fixes that - sillyness. As we do all database operations using - transactions, this is also safe. - */ - signal(SIGINT, SIG_DFL); + PyModule_AddObject(m, "version", PyString_FromString(SAMBA_VERSION_STRING)); + + /* "userAccountControl" flags */ + PyModule_AddObject(m, "UF_NORMAL_ACCOUNT", PyInt_FromLong(UF_NORMAL_ACCOUNT)); + PyModule_AddObject(m, "UF_TEMP_DUPLICATE_ACCOUNT", PyInt_FromLong(UF_TEMP_DUPLICATE_ACCOUNT)); + PyModule_AddObject(m, "UF_SERVER_TRUST_ACCOUNT", PyInt_FromLong(UF_SERVER_TRUST_ACCOUNT)); + PyModule_AddObject(m, "UF_WORKSTATION_TRUST_ACCOUNT", PyInt_FromLong(UF_WORKSTATION_TRUST_ACCOUNT)); + PyModule_AddObject(m, "UF_INTERDOMAIN_TRUST_ACCOUNT", PyInt_FromLong(UF_INTERDOMAIN_TRUST_ACCOUNT)); + PyModule_AddObject(m, "UF_PASSWD_NOTREQD", PyInt_FromLong(UF_PASSWD_NOTREQD)); + PyModule_AddObject(m, "UF_ACCOUNTDISABLE", PyInt_FromLong(UF_ACCOUNTDISABLE)); + + /* "groupType" flags */ + PyModule_AddObject(m, "GTYPE_SECURITY_BUILTIN_LOCAL_GROUP", PyInt_FromLong(GTYPE_SECURITY_BUILTIN_LOCAL_GROUP)); + PyModule_AddObject(m, "GTYPE_SECURITY_GLOBAL_GROUP", PyInt_FromLong(GTYPE_SECURITY_GLOBAL_GROUP)); + PyModule_AddObject(m, "GTYPE_SECURITY_DOMAIN_LOCAL_GROUP", PyInt_FromLong(GTYPE_SECURITY_DOMAIN_LOCAL_GROUP)); + PyModule_AddObject(m, "GTYPE_SECURITY_UNIVERSAL_GROUP", PyInt_FromLong(GTYPE_SECURITY_UNIVERSAL_GROUP)); + PyModule_AddObject(m, "GTYPE_DISTRIBUTION_GLOBAL_GROUP", PyInt_FromLong(GTYPE_DISTRIBUTION_GLOBAL_GROUP)); + PyModule_AddObject(m, "GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP", PyInt_FromLong(GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP)); + PyModule_AddObject(m, "GTYPE_DISTRIBUTION_UNIVERSAL_GROUP", PyInt_FromLong(GTYPE_DISTRIBUTION_UNIVERSAL_GROUP)); + + /* "sAMAccountType" flags */ + PyModule_AddObject(m, "ATYPE_NORMAL_ACCOUNT", PyInt_FromLong(ATYPE_NORMAL_ACCOUNT)); + PyModule_AddObject(m, "ATYPE_WORKSTATION_TRUST", PyInt_FromLong(ATYPE_WORKSTATION_TRUST)); + PyModule_AddObject(m, "ATYPE_INTERDOMAIN_TRUST", PyInt_FromLong(ATYPE_INTERDOMAIN_TRUST)); + PyModule_AddObject(m, "ATYPE_SECURITY_GLOBAL_GROUP", PyInt_FromLong(ATYPE_SECURITY_GLOBAL_GROUP)); + PyModule_AddObject(m, "ATYPE_SECURITY_LOCAL_GROUP", PyInt_FromLong(ATYPE_SECURITY_LOCAL_GROUP)); + PyModule_AddObject(m, "ATYPE_SECURITY_UNIVERSAL_GROUP", PyInt_FromLong(ATYPE_SECURITY_UNIVERSAL_GROUP)); + PyModule_AddObject(m, "ATYPE_DISTRIBUTION_GLOBAL_GROUP", PyInt_FromLong(ATYPE_DISTRIBUTION_GLOBAL_GROUP)); + PyModule_AddObject(m, "ATYPE_DISTRIBUTION_LOCAL_GROUP", PyInt_FromLong(ATYPE_DISTRIBUTION_LOCAL_GROUP)); + PyModule_AddObject(m, "ATYPE_DISTRIBUTION_UNIVERSAL_GROUP", PyInt_FromLong(ATYPE_DISTRIBUTION_UNIVERSAL_GROUP)); + + /* "domainFunctionality", "forestFunctionality" flags in the rootDSE */ + PyModule_AddObject(m, "DS_DOMAIN_FUNCTION_2000", PyInt_FromLong(DS_DOMAIN_FUNCTION_2000)); + PyModule_AddObject(m, "DS_DOMAIN_FUNCTION_2003_MIXED", PyInt_FromLong(DS_DOMAIN_FUNCTION_2003_MIXED)); + PyModule_AddObject(m, "DS_DOMAIN_FUNCTION_2003", PyInt_FromLong(DS_DOMAIN_FUNCTION_2003)); + PyModule_AddObject(m, "DS_DOMAIN_FUNCTION_2008", PyInt_FromLong(DS_DOMAIN_FUNCTION_2008)); + PyModule_AddObject(m, "DS_DOMAIN_FUNCTION_2008_R2", PyInt_FromLong(DS_DOMAIN_FUNCTION_2008_R2)); + + /* "domainControllerFunctionality" flags in the rootDSE */ + PyModule_AddObject(m, "DS_DC_FUNCTION_2000", PyInt_FromLong(DS_DC_FUNCTION_2000)); + PyModule_AddObject(m, "DS_DC_FUNCTION_2003", PyInt_FromLong(DS_DC_FUNCTION_2003)); + PyModule_AddObject(m, "DS_DC_FUNCTION_2008", PyInt_FromLong(DS_DC_FUNCTION_2008)); + PyModule_AddObject(m, "DS_DC_FUNCTION_2008_R2", PyInt_FromLong(DS_DC_FUNCTION_2008_R2)); } diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py index 2a54f47d2b..82df4960cf 100644 --- a/source4/scripting/python/samba/__init__.py +++ b/source4/scripting/python/samba/__init__.py @@ -1,21 +1,21 @@ -#!/usr/bin/env python +#!/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 -# +# # 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/>. # @@ -25,39 +25,33 @@ __docformat__ = "restructuredText" import os -import sys - -def source_tree_topdir(): - '''return the top level directory (the one containing the source4 directory)''' - paths = [ "../../..", "../../../.." ] - for p in paths: - topdir = os.path.normpath(os.path.join(os.path.dirname(__file__), p)) - if os.path.exists(os.path.join(topdir, 'source4')): - return topdir - raise RuntimeError("unable to find top level source directory") - -def in_source_tree(): - '''return True if we are running from within the samba source tree''' - try: - topdir = source_tree_topdir() - except RuntimeError: - return False - return True +def _in_source_tree(): + """Check whether the script is being run from the source dir. """ + return os.path.exists("%s/../../../selftest/skip" % os.path.dirname(__file__)) + + +# When running, in-tree, make sure bin/python is in the PYTHONPATH +if _in_source_tree(): + import sys + srcdir = "%s/../../.." % os.path.dirname(__file__) + sys.path.append("%s/bin/python" % srcdir) + default_ldb_modules_dir = "%s/bin/modules/ldb" % srcdir +else: + default_ldb_modules_dir = None import ldb -from samba._ldb import Ldb as _Ldb +import glue -class Ldb(_Ldb): - """Simple Samba-specific LDB subclass that takes care +class Ldb(ldb.Ldb): + """Simple Samba-specific LDB subclass that takes care of setting up the modules dir, credentials pointers, etc. - - Please note that this is intended to be for all Samba LDB files, - not necessarily the Sam database. For Sam-specific helper + + Please note that this is intended to be for all Samba LDB files, + not necessarily the Sam database. For Sam-specific helper functions see samdb.py. """ - def __init__(self, url=None, lp=None, modules_dir=None, session_info=None, credentials=None, flags=0, options=None): """Opens a Samba Ldb file. @@ -71,12 +65,14 @@ class Ldb(_Ldb): :param options: Additional options (optional) This is different from a regular Ldb file in that the Samba-specific - modules-dir is used by default and that credentials and session_info + modules-dir is used by default and that credentials and session_info can be passed through (required by some modules). """ if modules_dir is not None: self.set_modules_dir(modules_dir) + elif default_ldb_modules_dir is not None: + self.set_modules_dir(default_ldb_modules_dir) elif lp is not None: self.set_modules_dir(os.path.join(lp.get("modules dir"), "ldb")) @@ -92,30 +88,37 @@ class Ldb(_Ldb): # This must be done before we load the schema, as these handlers for # objectSid and objectGUID etc must take precedence over the 'binary # attribute' declaration in the schema - self.register_samba_handlers() + glue.ldb_register_samba_handlers(self) # TODO set debug - def msg(l, text): + def msg(l,text): print text #self.set_debug(msg) - self.set_utf8_casefold() + glue.ldb_set_utf8_casefold(self) # Allow admins to force non-sync ldb for all databases if lp is not None: nosync_p = lp.get("nosync", "ldb") - if nosync_p is not None and nosync_p == True: - flags |= ldb.FLG_NOSYNC - - self.set_create_perms(0600) + if nosync_p is not None and nosync_p == true: + flags |= FLG_NOSYNC if url is not None: self.connect(url, flags, options) - def searchone(self, attribute, basedn=None, expression=None, + def set_credentials(self, credentials): + glue.ldb_set_credentials(self, credentials) + + def set_session_info(self, session_info): + glue.ldb_set_session_info(self, session_info) + + def set_loadparm(self, lp_ctx): + glue.ldb_set_loadparm(self, lp_ctx) + + def searchone(self, attribute, basedn=None, expression=None, scope=ldb.SCOPE_BASE): """Search for one attribute as a string. - + :param basedn: BaseDN for the search. :param attribute: Name of the attribute :param expression: Optional search expression. @@ -130,36 +133,25 @@ class Ldb(_Ldb): return self.schema_format_value(attribute, values.pop()) def erase_users_computers(self, dn): - """Erases user and computer objects from our AD. - - This is needed since the 'samldb' module denies the deletion of primary - groups. Therefore all groups shouldn't be primary somewhere anymore. - """ + """Erases user and computer objects from our AD. This is needed since the 'samldb' module denies the deletion of primary groups. Therefore all groups shouldn't be primary somewhere anymore.""" try: res = self.search(base=dn, scope=ldb.SCOPE_SUBTREE, attrs=[], expression="(|(objectclass=user)(objectclass=computer))") - except ldb.LdbError, (errno, _): - if errno == ldb.ERR_NO_SUCH_OBJECT: - # Ignore no such object errors - return - else: - raise + except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _): + # Ignore no such object errors + return + pass try: for msg in res: - self.delete(msg.dn, ["relax:0"]) - except ldb.LdbError, (errno, _): - if errno != ldb.ERR_NO_SUCH_OBJECT: - # Ignore no such object errors - raise + self.delete(msg.dn) + except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _): + # Ignore no such object errors + return def erase_except_schema_controlled(self): - """Erase this ldb. - - :note: Removes all records, except those that are controlled by - Samba4's schema. - """ + """Erase this ldb, removing all records, except those that are controlled by Samba4's schema.""" basedn = "" @@ -167,42 +159,73 @@ class Ldb(_Ldb): self.erase_users_computers(basedn) # Delete the 'visible' records, and the invisble 'deleted' records (if this DB supports it) - for msg in self.search(basedn, ldb.SCOPE_SUBTREE, - "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))", - [], controls=["show_deleted:0", "show_recycled:0"]): + for msg in self.search(basedn, ldb.SCOPE_SUBTREE, + "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))", + [], controls=["show_deleted:0"]): try: - self.delete(msg.dn, ["relax:0"]) - except ldb.LdbError, (errno, _): - if errno != ldb.ERR_NO_SUCH_OBJECT: - # Ignore no such object errors - raise - - res = self.search(basedn, ldb.SCOPE_SUBTREE, - "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))", [], controls=["show_deleted:0", "show_recycled:0"]) + self.delete(msg.dn) + except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _): + # Ignore no such object errors + pass + + res = self.search(basedn, ldb.SCOPE_SUBTREE, + "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))", + [], controls=["show_deleted:0"]) assert len(res) == 0 # delete the specials - for attr in ["@SUBCLASSES", "@MODULES", + for attr in ["@SUBCLASSES", "@MODULES", "@OPTIONS", "@PARTITION", "@KLUDGEACL"]: try: - self.delete(attr, ["relax:0"]) - except ldb.LdbError, (errno, _): - if errno != ldb.ERR_NO_SUCH_OBJECT: - # Ignore missing dn errors - raise + self.delete(attr) + except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _): + # Ignore missing dn errors + pass def erase(self): """Erase this ldb, removing all records.""" + self.erase_except_schema_controlled() # delete the specials for attr in ["@INDEXLIST", "@ATTRIBUTES"]: try: - self.delete(attr, ["relax:0"]) - except ldb.LdbError, (errno, _): - if errno != ldb.ERR_NO_SUCH_OBJECT: - # Ignore missing dn errors - raise + self.delete(attr) + except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _): + # Ignore missing dn errors + pass + + def erase_partitions(self): + """Erase an ldb, removing all records.""" + + def erase_recursive(self, dn): + try: + res = self.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=[], + controls=["show_deleted:0"]) + except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _): + # Ignore no such object errors + return + pass + + for msg in res: + erase_recursive(self, msg.dn) + + try: + self.delete(dn) + except ldb.LdbError, (ldb.ERR_NO_SUCH_OBJECT, _): + # Ignore no such object errors + pass + + res = self.search("", ldb.SCOPE_BASE, "(objectClass=*)", + ["namingContexts"]) + assert len(res) == 1 + if not "namingContexts" in res[0]: + return + for basedn in res[0]["namingContexts"]: + # Try to delete user/computer accounts to allow deletion of groups + self.erase_users_computers(basedn) + # Try and erase from the bottom-up in the tree + erase_recursive(self, basedn) def load_ldif_file_add(self, ldif_path): """Load a LDIF file. @@ -211,31 +234,68 @@ class Ldb(_Ldb): """ self.add_ldif(open(ldif_path, 'r').read()) - def add_ldif(self, ldif, controls=None): + def add_ldif(self, ldif): """Add data based on a LDIF string. :param ldif: LDIF text. """ for changetype, msg in self.parse_ldif(ldif): assert changetype == ldb.CHANGETYPE_NONE - self.add(msg, controls) + self.add(msg) - def modify_ldif(self, ldif, controls=None): + def modify_ldif(self, ldif): """Modify database based on a LDIF string. :param ldif: LDIF text. """ for changetype, msg in self.parse_ldif(ldif): - if changetype == ldb.CHANGETYPE_ADD: - self.add(msg, controls) - else: - self.modify(msg, controls) + self.modify(msg) + def set_domain_sid(self, sid): + """Change the domain SID used by this LDB. -def substitute_var(text, values): - """Substitute strings of the form ${NAME} in str, replacing - with substitutions from values. + :param sid: The new domain sid to use. + """ + glue.samdb_set_domain_sid(self, sid) + + def domain_sid(self): + """Read the domain SID used by this LDB. + + """ + glue.samdb_get_domain_sid(self) + + def set_schema_from_ldif(self, pf, df): + glue.dsdb_set_schema_from_ldif(self, pf, df) + + def set_schema_from_ldb(self, ldb): + glue.dsdb_set_schema_from_ldb(self, ldb) + + def write_prefixes_from_schema(self): + glue.dsdb_write_prefixes_from_schema_to_ldb(self) + def convert_schema_to_openldap(self, target, mapping): + return glue.dsdb_convert_schema_to_openldap(self, target, mapping) + + def set_invocation_id(self, invocation_id): + """Set the invocation id for this SamDB handle. + + :param invocation_id: GUID of the invocation id. + """ + glue.dsdb_set_ntds_invocation_id(self, invocation_id) + + def set_opaque_integer(self, name, value): + """Set an integer as an opaque (a flag or other value) value on the database + + :param name: The name for the opaque value + :param value: The integer value + """ + glue.dsdb_set_opaque_integer(self, name, value) + + +def substitute_var(text, values): + """substitute strings of the form ${NAME} in str, replacing + with substitutions from subobj. + :param text: Text in which to subsitute. :param values: Dictionary with keys and values. """ @@ -249,51 +309,18 @@ def substitute_var(text, values): def check_all_substituted(text): - """Check that all substitution variables in a string have been replaced. - + """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 read_and_sub_file(file_name, subst_vars): - """Read a file and sub in variables found in it - - :param file_name: File to be read (typically from setup directory) - param subst_vars: Optional variables to subsitute in the file. - """ - data = open(file_name, 'r').read() - if subst_vars is not None: - data = substitute_var(data, subst_vars) - check_all_substituted(data) - return data - - -def setup_file(template, fname, subst_vars=None): - """Setup a file in the private dir. - - :param template: Path of the template file. - :param fname: Path of the file to create. - :param subst_vars: Substitution variables. - """ - if os.path.exists(fname): - os.unlink(fname) - - data = read_and_sub_file(template, subst_vars) - f = open(fname, 'w') - try: - f.write(data) - finally: - f.close() + + raise Exception("Not all variables substituted: %s" % text[var_start:var_end+1]) def valid_netbios_name(name): @@ -307,44 +334,56 @@ def valid_netbios_name(name): return True -def import_bundled_package(modulename, location): - """Import the bundled version of a package. +def dom_sid_to_rid(sid_str): + """Converts a domain SID to the relative RID. - :note: This should only be called if the system version of the package - is not adequate. - - :param modulename: Module name to import - :param location: Location to add to sys.path (can be relative to - ${srcdir}/lib) + :param sid_str: The domain SID formatted as string """ - if in_source_tree(): - sys.path.insert(0, os.path.join(source_tree_topdir(), "lib", location)) - sys.modules[modulename] = __import__(modulename) - else: - sys.modules[modulename] = __import__( - "samba.external.%s" % modulename, fromlist=["samba.external"]) - -def ensure_external_module(modulename, location): - """Add a location to sys.path if an external dependency can't be found. + return glue.dom_sid_to_rid(sid_str) + + +version = glue.version + +# "userAccountControl" flags +UF_NORMAL_ACCOUNT = glue.UF_NORMAL_ACCOUNT +UF_TEMP_DUPLICATE_ACCOUNT = glue.UF_TEMP_DUPLICATE_ACCOUNT +UF_SERVER_TRUST_ACCOUNT = glue.UF_SERVER_TRUST_ACCOUNT +UF_WORKSTATION_TRUST_ACCOUNT = glue.UF_WORKSTATION_TRUST_ACCOUNT +UF_INTERDOMAIN_TRUST_ACCOUNT = glue.UF_INTERDOMAIN_TRUST_ACCOUNT +UF_PASSWD_NOTREQD = glue.UF_PASSWD_NOTREQD +UF_ACCOUNTDISABLE = glue.UF_ACCOUNTDISABLE + +# "groupType" flags +GTYPE_SECURITY_BUILTIN_LOCAL_GROUP = glue.GTYPE_SECURITY_BUILTIN_LOCAL_GROUP +GTYPE_SECURITY_GLOBAL_GROUP = glue.GTYPE_SECURITY_GLOBAL_GROUP +GTYPE_SECURITY_DOMAIN_LOCAL_GROUP = glue.GTYPE_SECURITY_DOMAIN_LOCAL_GROUP +GTYPE_SECURITY_UNIVERSAL_GROUP = glue.GTYPE_SECURITY_UNIVERSAL_GROUP +GTYPE_DISTRIBUTION_GLOBAL_GROUP = glue.GTYPE_DISTRIBUTION_GLOBAL_GROUP +GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP = glue.GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP +GTYPE_DISTRIBUTION_UNIVERSAL_GROUP = glue.GTYPE_DISTRIBUTION_UNIVERSAL_GROUP + +# "sAMAccountType" flags +ATYPE_NORMAL_ACCOUNT = glue.ATYPE_NORMAL_ACCOUNT +ATYPE_WORKSTATION_TRUST = glue.ATYPE_WORKSTATION_TRUST +ATYPE_INTERDOMAIN_TRUST = glue.ATYPE_INTERDOMAIN_TRUST +ATYPE_SECURITY_GLOBAL_GROUP = glue.ATYPE_SECURITY_GLOBAL_GROUP +ATYPE_SECURITY_LOCAL_GROUP = glue.ATYPE_SECURITY_LOCAL_GROUP +ATYPE_SECURITY_UNIVERSAL_GROUP = glue.ATYPE_SECURITY_UNIVERSAL_GROUP +ATYPE_DISTRIBUTION_GLOBAL_GROUP = glue.ATYPE_DISTRIBUTION_GLOBAL_GROUP +ATYPE_DISTRIBUTION_LOCAL_GROUP = glue.ATYPE_DISTRIBUTION_LOCAL_GROUP +ATYPE_DISTRIBUTION_UNIVERSAL_GROUP = glue.ATYPE_DISTRIBUTION_UNIVERSAL_GROUP + +# "domainFunctionality", "forestFunctionality" flags in the rootDSE */ +DS_DOMAIN_FUNCTION_2000 = glue.DS_DOMAIN_FUNCTION_2000 +DS_DOMAIN_FUNCTION_2003_MIXED = glue.DS_DOMAIN_FUNCTION_2003_MIXED +DS_DOMAIN_FUNCTION_2003 = glue.DS_DOMAIN_FUNCTION_2003 +DS_DOMAIN_FUNCTION_2008 = glue.DS_DOMAIN_FUNCTION_2008 +DS_DOMAIN_FUNCTION_2008_R2 = glue.DS_DOMAIN_FUNCTION_2008_R2 + +# "domainControllerFunctionality" flags in the rootDSE */ +DS_DC_FUNCTION_2000 = glue.DS_DC_FUNCTION_2000 +DS_DC_FUNCTION_2003 = glue.DS_DC_FUNCTION_2003 +DS_DC_FUNCTION_2008 = glue.DS_DC_FUNCTION_2008 +DS_DC_FUNCTION_2008_R2 = glue.DS_DC_FUNCTION_2008_R2 - :param modulename: Module name to import - :param location: Location to add to sys.path (can be relative to - ${srcdir}/lib) - """ - try: - __import__(modulename) - except ImportError: - import_bundled_package(modulename, location) - - -from samba import _glue -version = _glue.version -interface_ips = _glue.interface_ips -set_debug_level = _glue.set_debug_level -get_debug_level = _glue.get_debug_level -unix2nttime = _glue.unix2nttime -nttime2string = _glue.nttime2string -nttime2unix = _glue.nttime2unix -unix2nttime = _glue.unix2nttime -generate_random_password = _glue.generate_random_password diff --git a/source4/scripting/python/samba/drs_utils.py b/source4/scripting/python/samba/drs_utils.py deleted file mode 100644 index 77f415ed17..0000000000 --- a/source4/scripting/python/samba/drs_utils.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python -# -# DRS utility code -# -# Copyright Andrew Tridgell 2010 -# Copyright Andrew Bartlett 2010 -# -# 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 samba.dcerpc import drsuapi, misc -from samba.net import Net -import samba, ldb - - -def drs_DsBind(drs): - '''make a DsBind call, returning the binding handle''' - bind_info = drsuapi.DsBindInfoCtr() - bind_info.length = 28 - bind_info.info = drsuapi.DsBindInfo28() - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_BASE - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7 - bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT - (info, handle) = drs.DsBind(misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID), bind_info) - - return (handle, info.info.supported_extensions) - - -class drs_Replicate: - '''DRS replication calls''' - - def __init__(self, binding_string, lp, creds, samdb): - self.drs = drsuapi.drsuapi(binding_string, lp, creds) - (self.drs_handle, self.supported_extensions) = drs_DsBind(self.drs) - self.net = Net(creds=creds, lp=lp) - self.samdb = samdb - self.replication_state = self.net.replicate_init(self.samdb, lp, self.drs) - - def drs_get_rodc_partial_attribute_set(self): - '''get a list of attributes for RODC replication''' - partial_attribute_set = drsuapi.DsPartialAttributeSet() - partial_attribute_set.version = 1 - - attids = [] - - # the exact list of attids we send is quite critical. Note that - # we do ask for the secret attributes, but set SPECIAL_SECRET_PROCESSING - # to zero them out - schema_dn = self.samdb.get_schema_basedn() - res = self.samdb.search(base=schema_dn, scope=ldb.SCOPE_SUBTREE, - expression="objectClass=attributeSchema", - attrs=["lDAPDisplayName", "systemFlags", - "searchFlags"]) - - for r in res: - ldap_display_name = r["lDAPDisplayName"][0] - if "systemFlags" in r: - system_flags = r["systemFlags"][0] - if (int(system_flags) & (samba.dsdb.DS_FLAG_ATTR_NOT_REPLICATED | - samba.dsdb.DS_FLAG_ATTR_IS_CONSTRUCTED)): - continue - if "searchFlags" in r: - search_flags = r["searchFlags"][0] - if (int(search_flags) & samba.dsdb.SEARCH_FLAG_RODC_ATTRIBUTE): - continue - attid = self.samdb.get_attid_from_lDAPDisplayName(ldap_display_name) - attids.append(int(attid)) - - # the attids do need to be sorted, or windows doesn't return - # all the attributes we need - attids.sort() - partial_attribute_set.attids = attids - partial_attribute_set.num_attids = len(attids) - return partial_attribute_set - - def replicate(self, dn, source_dsa_invocation_id, destination_dsa_guid, - schema=False, exop=drsuapi.DRSUAPI_EXOP_NONE, rodc=False, - replica_flags=None): - '''replicate a single DN''' - - # setup for a GetNCChanges call - req8 = drsuapi.DsGetNCChangesRequest8() - - req8.destination_dsa_guid = destination_dsa_guid - req8.source_dsa_invocation_id = source_dsa_invocation_id - req8.naming_context = drsuapi.DsReplicaObjectIdentifier() - req8.naming_context.dn = dn - req8.highwatermark = drsuapi.DsReplicaHighWaterMark() - req8.highwatermark.tmp_highest_usn = 0 - req8.highwatermark.reserved_usn = 0 - req8.highwatermark.highest_usn = 0 - req8.uptodateness_vector = None - if replica_flags is not None: - req8.replica_flags = replica_flags - elif exop == drsuapi.DRSUAPI_EXOP_REPL_SECRET: - req8.replica_flags = 0 - else: - req8.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC | - drsuapi.DRSUAPI_DRS_PER_SYNC | - drsuapi.DRSUAPI_DRS_GET_ANC | - drsuapi.DRSUAPI_DRS_NEVER_SYNCED) - if rodc: - req8.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING - else: - req8.replica_flags |= drsuapi.DRSUAPI_DRS_WRIT_REP - req8.max_object_count = 402 - req8.max_ndr_size = 402116 - req8.extended_op = exop - req8.fsmo_info = 0 - req8.partial_attribute_set = None - req8.partial_attribute_set_ex = None - req8.mapping_ctr.num_mappings = 0 - req8.mapping_ctr.mappings = None - - if not schema and rodc: - req8.partial_attribute_set = self.drs_get_rodc_partial_attribute_set() - - if self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8: - req_level = 8 - req = req8 - else: - req_level = 5 - req5 = drsuapi.DsGetNCChangesRequest5() - for a in dir(req5): - if a[0] != '_': - setattr(req5, a, getattr(req8, a)) - req = req5 - - - while True: - (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, req_level, req) - if ctr.first_object == None and ctr.object_count != 0: - raise RuntimeError("DsGetNCChanges: NULL first_object with object_count=%u" % (ctr.object_count)) - self.net.replicate_chunk(self.replication_state, level, ctr, schema=schema) - if ctr.more_data == 0: - break - req.highwatermark.tmp_highest_usn = ctr.new_highwatermark.tmp_highest_usn diff --git a/source4/scripting/python/samba/getopt.py b/source4/scripting/python/samba/getopt.py index 671142b552..8b756b2d6f 100644 --- a/source4/scripting/python/samba/getopt.py +++ b/source4/scripting/python/samba/getopt.py @@ -1,56 +1,38 @@ -#!/usr/bin/env python +#!/usr/bin/python # Samba-specific bits for optparse # 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 # 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/>. # """Support for parsing Samba-related command-line options.""" -__docformat__ = "restructuredText" - -import optparse, os -from samba.credentials import ( - Credentials, - DONT_USE_KERBEROS, - MUST_USE_KERBEROS, - ) -from samba.hostconfig import Hostconfig -import sys +import optparse +from credentials import Credentials, DONT_USE_KERBEROS, MUST_USE_KERBEROS +from hostconfig import Hostconfig +__docformat__ = "restructuredText" class SambaOptions(optparse.OptionGroup): """General Samba-related command line options.""" - def __init__(self, parser): - from samba.param import LoadParm optparse.OptionGroup.__init__(self, parser, "Samba Common Options") self.add_option("-s", "--configfile", action="callback", - type=str, metavar="FILE", help="Configuration file", + type=str, metavar="FILE", help="Configuration file", callback=self._load_configfile) - self.add_option("-d", "--debuglevel", action="callback", - type=int, metavar="DEBUGLEVEL", help="debug level", - callback=self._set_debuglevel) - self.add_option("--option", action="callback", - type=str, metavar="OPTION", help="set smb.conf option from command line", - callback=self._set_option) - self.add_option("--realm", action="callback", - type=str, metavar="REALM", help="set the realm name", - callback=self._set_realm) self._configfile = None - self._lp = LoadParm() def get_loadparm_path(self): """Return the path to the smb.conf file specified on the command line. """ @@ -59,28 +41,17 @@ class SambaOptions(optparse.OptionGroup): def _load_configfile(self, option, opt_str, arg, parser): self._configfile = arg - def _set_debuglevel(self, option, opt_str, arg, parser): - self._lp.set('debug level', str(arg)) - - def _set_realm(self, option, opt_str, arg, parser): - self._lp.set('realm', arg) - - def _set_option(self, option, opt_str, arg, parser): - if arg.find('=') == -1: - print("--option takes a 'a=b' argument") - sys.exit(1) - a = arg.split('=') - self._lp.set(a[0], a[1]) - def get_loadparm(self): """Return a loadparm object with data specified on the command line. """ + import os, param + lp = param.LoadParm() if self._configfile is not None: - self._lp.load(self._configfile) + lp.load(self._configfile) elif os.getenv("SMB_CONF_PATH") is not None: - self._lp.load(os.getenv("SMB_CONF_PATH")) + lp.load(os.getenv("SMB_CONF_PATH")) else: - self._lp.load_default() - return self._lp + lp.load_default() + return lp def get_hostconfig(self): return Hostconfig(self.get_loadparm()) @@ -90,41 +61,29 @@ class VersionOptions(optparse.OptionGroup): """Command line option for printing Samba version.""" def __init__(self, parser): optparse.OptionGroup.__init__(self, parser, "Version Options") - self.add_option("--version", action="callback", - callback=self._display_version, - help="Display version number") - - def _display_version(self, option, opt_str, arg, parser): - import samba - print samba.version - sys.exit(0) class CredentialsOptions(optparse.OptionGroup): """Command line options for specifying credentials.""" def __init__(self, parser): - self.no_pass = True - self.ipaddress = None + 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, help="DN to use for a simple bind") self.add_option("--password", metavar="PASSWORD", action="callback", help="Password", type=str, callback=self._set_password) - self.add_option("-U", "--username", metavar="USERNAME", + self.add_option("-U", "--username", metavar="USERNAME", action="callback", type=str, help="Username", callback=self._parse_username) - self.add_option("-W", "--workgroup", metavar="WORKGROUP", + 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.add_option("-k", "--kerberos", metavar="KERBEROS", + self.add_option("-k", "--kerberos", metavar="KERBEROS", action="callback", type=str, help="Use Kerberos", callback=self._set_kerberos) - self.add_option("", "--ipaddress", metavar="IPADDRESS", - action="callback", type=str, - help="IP address of server", callback=self._set_ipaddress) self.creds = Credentials() def _parse_username(self, option, opt_str, arg, parser): @@ -135,96 +94,23 @@ class CredentialsOptions(optparse.OptionGroup): def _set_password(self, option, opt_str, arg, parser): self.creds.set_password(arg) - self.no_pass = False - - def _set_ipaddress(self, option, opt_str, arg, parser): - self.ipaddress = arg def _set_kerberos(self, option, opt_str, arg, parser): - if arg.lower() in ["yes", 'true', '1']: + if bool(arg) or arg.lower() == "yes": self.creds.set_kerberos_state(MUST_USE_KERBEROS) - elif arg.lower() in ["no", 'false', '0']: - self.creds.set_kerberos_state(DONT_USE_KERBEROS) else: - raise optparse.BadOptionErr("invalid kerberos option: %s" % arg) + self.creds.set_kerberos_state(DONT_USE_KERBEROS) def _set_simple_bind_dn(self, option, opt_str, arg, parser): self.creds.set_bind_dn(arg) - def get_credentials(self, lp, fallback_machine=False): + def get_credentials(self, lp): """Obtain the credentials set on the command-line. :param lp: Loadparm object to use. :return: Credentials object """ self.creds.guess(lp) - if self.no_pass: + if not self.no_pass: self.creds.set_cmdline_callbacks() - - # possibly fallback to using the machine account, if we have - # access to the secrets db - if fallback_machine and not self.creds.authentication_requested(): - try: - self.creds.set_machine_account(lp) - except Exception: - pass - return self.creds - -class CredentialsOptionsDouble(CredentialsOptions): - """Command line options for specifying credentials of two servers.""" - def __init__(self, parser): - CredentialsOptions.__init__(self, parser) - self.no_pass2 = True - self.add_option("--simple-bind-dn2", metavar="DN2", action="callback", - callback=self._set_simple_bind_dn2, type=str, - help="DN to use for a simple bind") - self.add_option("--password2", metavar="PASSWORD2", action="callback", - help="Password", type=str, callback=self._set_password2) - self.add_option("--username2", metavar="USERNAME2", - action="callback", type=str, - help="Username for second server", callback=self._parse_username2) - self.add_option("--workgroup2", metavar="WORKGROUP2", - action="callback", type=str, - help="Workgroup for second server", callback=self._parse_workgroup2) - self.add_option("--no-pass2", action="store_true", - help="Don't ask for a password for the second server") - self.add_option("--kerberos2", metavar="KERBEROS2", - action="callback", type=str, - help="Use Kerberos", callback=self._set_kerberos2) - self.creds2 = Credentials() - - def _parse_username2(self, option, opt_str, arg, parser): - self.creds2.parse_string(arg) - - def _parse_workgroup2(self, option, opt_str, arg, parser): - self.creds2.set_domain(arg) - - def _set_password2(self, option, opt_str, arg, parser): - self.creds2.set_password(arg) - self.no_pass2 = False - - def _set_kerberos2(self, option, opt_str, arg, parser): - if bool(arg) or arg.lower() == "yes": - self.creds2.set_kerberos_state(MUST_USE_KERBEROS) - else: - self.creds2.set_kerberos_state(DONT_USE_KERBEROS) - - def _set_simple_bind_dn2(self, option, opt_str, arg, parser): - self.creds2.set_bind_dn(arg) - - def get_credentials2(self, lp, guess=True): - """Obtain the credentials set on the command-line. - - :param lp: Loadparm object to use. - :param guess: Try guess Credentials from environment - :return: Credentials object - """ - if guess: - self.creds2.guess(lp) - elif not self.creds2.get_username(): - self.creds2.set_anonymous() - - if self.no_pass2: - self.creds2.set_cmdline_callbacks() - return self.creds2 diff --git a/source4/scripting/python/samba/hostconfig.py b/source4/scripting/python/samba/hostconfig.py index 3e6dc6b1dd..313e3420b0 100644 --- a/source4/scripting/python/samba/hostconfig.py +++ b/source4/scripting/python/samba/hostconfig.py @@ -1,83 +1,33 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 # 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/>. # -"""Local host configuration.""" - from samdb import SamDB class Hostconfig(object): - """Aggregate object that contains all information about the configuration + """Aggregate object that contains all information about the configuration of a Samba host.""" - def __init__(self, lp): + def __init__(self, lp): self.lp = lp - def get_shares(self): - return SharesContainer(self.lp) - def get_samdb(self, session_info, credentials): - """Access the SamDB host. - - :param session_info: Session info to use - :param credentials: Credentials to access the SamDB with - """ - return SamDB(url=self.lp.get("sam database"), - session_info=session_info, credentials=credentials, + return SamDB(url=self.lp.get("sam database"), + session_info=session_info, credentials=credentials, lp=self.lp) - -# TODO: Rather than accessing Loadparm directly here, we should really -# have bindings to the param/shares.c and use those. - - -class SharesContainer(object): - """A shares container.""" - - def __init__(self, lp): - self._lp = lp - - def __getitem__(self, name): - if name == "global": - # [global] is not a share - raise KeyError - return Share(self._lp[name]) - - def __len__(self): - if "global" in self._lp.services(): - return len(self._lp)-1 - return len(self._lp) - - def keys(self): - return [name for name in self._lp.services() if name != "global"] - - def __iter__(self): - return iter(self.keys()) - - -class Share(object): - """A file share.""" - - def __init__(self, service): - self._service = service - - def __getitem__(self, name): - return self._service[name] - - def __setitem__(self, name, value): - self._service[name] = value diff --git a/source4/scripting/python/samba/idmap.py b/source4/scripting/python/samba/idmap.py index 93fca46edd..ad209f42de 100644 --- a/source4/scripting/python/samba/idmap.py +++ b/source4/scripting/python/samba/idmap.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) 2008 Kai Blin <kai@samba.org> @@ -22,7 +22,6 @@ __docformat__ = "restructuredText" -import ldb import samba class IDmapDB(samba.Ldb): @@ -51,34 +50,12 @@ class IDmapDB(samba.Ldb): super(IDmapDB, self).connect(url=self.lp.private_path(url), flags=flags, options=options) - - def increment_xid(self): - """Increment xidNumber, if not present it create and assign it to the lowerBound - - :return xid can that be used for SID/unixid mapping - """ - res = self.search(expression="dn=CN=CONFIG", base="", - scope=ldb.SCOPE_SUBTREE) - id = res[0].get("xidNumber") - flag = ldb.FLAG_MOD_REPLACE - if id is None: - id = res[0].get("lowerBound") - flag = ldb.FLAG_MOD_ADD - newid = int(str(id)) + 1 - msg = ldb.Message() - msg.dn = ldb.Dn(self, "CN=CONFIG") - msg["xidNumber"] = ldb.MessageElement(str(newid), flag, "xidNumber") - self.modify(msg) - return id - - def setup_name_mapping(self, sid, type, unixid=None): + def setup_name_mapping(self, sid, type, unixid): """Setup a mapping between a sam name and a unix name. :param sid: SID of the NT-side of the mapping. - :param unixname: Unix id to map to, if none supplied the next one will be selected + :param unixname: Unix name to map to. """ - if unixid is None: - unixid = self.increment_xid() type_string = "" if type == self.TYPE_UID: type_string = "ID_TYPE_UID" diff --git a/source4/scripting/python/samba/join.py b/source4/scripting/python/samba/join.py deleted file mode 100644 index 401f262154..0000000000 --- a/source4/scripting/python/samba/join.py +++ /dev/null @@ -1,582 +0,0 @@ -#!/usr/bin/env python -# -# python join code -# Copyright Andrew Tridgell 2010 -# Copyright Andrew Bartlett 2010 -# -# 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/>. -# - -"""Joining a domain.""" - -from samba.auth import system_session -from samba.samdb import SamDB -from samba import gensec, Ldb, drs_utils -import ldb, samba, sys, os, uuid -from samba.ndr import ndr_pack -from samba.dcerpc import security, drsuapi, misc, nbt -from samba.credentials import Credentials, DONT_USE_KERBEROS -from samba.provision import secretsdb_self_join, provision, FILL_DRS -from samba.schema import Schema -from samba.net import Net -import logging -import talloc - -# this makes debugging easier -talloc.enable_null_tracking() - - -class dc_join(object): - '''perform a DC join''' - - def __init__(ctx, server=None, creds=None, lp=None, site=None, - netbios_name=None, targetdir=None, domain=None): - ctx.creds = creds - ctx.lp = lp - ctx.site = site - ctx.netbios_name = netbios_name - ctx.targetdir = targetdir - - ctx.creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL) - ctx.net = Net(creds=ctx.creds, lp=ctx.lp) - - if server is not None: - ctx.server = server - else: - print("Finding a writeable DC for domain '%s'" % domain) - ctx.server = ctx.find_dc(domain) - print("Found DC %s" % ctx.server) - - ctx.samdb = SamDB(url="ldap://%s" % ctx.server, - session_info=system_session(), - credentials=ctx.creds, lp=ctx.lp) - - ctx.myname = netbios_name - ctx.samname = "%s$" % ctx.myname - ctx.base_dn = str(ctx.samdb.get_default_basedn()) - ctx.root_dn = str(ctx.samdb.get_root_basedn()) - ctx.schema_dn = str(ctx.samdb.get_schema_basedn()) - ctx.config_dn = str(ctx.samdb.get_config_basedn()) - ctx.domsid = ctx.samdb.get_domain_sid() - ctx.domain_name = ctx.get_domain_name() - - lp.set("workgroup", ctx.domain_name) - print("workgroup is %s" % ctx.domain_name) - - ctx.dc_ntds_dn = ctx.get_dsServiceName() - ctx.dc_dnsHostName = ctx.get_dnsHostName() - ctx.behavior_version = ctx.get_behavior_version() - - ctx.acct_pass = samba.generate_random_password(32, 40) - - # work out the DNs of all the objects we will be adding - ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn) - ctx.ntds_dn = "CN=NTDS Settings,%s" % ctx.server_dn - topology_base = "CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % ctx.base_dn - if ctx.dn_exists(topology_base): - ctx.topology_dn = "CN=%s,%s" % (ctx.myname, topology_base) - else: - ctx.topology_dn = None - - ctx.dnsdomain = ldb.Dn(ctx.samdb, ctx.base_dn).canonical_str().split('/')[0] - - ctx.realm = ctx.dnsdomain - lp.set("realm", ctx.realm) - - print("realm is %s" % ctx.realm) - - ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain) - - ctx.acct_dn = "CN=%s,OU=Domain Controllers,%s" % (ctx.myname, ctx.base_dn) - - ctx.tmp_samdb = None - - ctx.SPNs = [ "HOST/%s" % ctx.myname, - "HOST/%s" % ctx.dnshostname, - "GC/%s/%s" % (ctx.dnshostname, ctx.dnsdomain) ] - - # these elements are optional - ctx.never_reveal_sid = None - ctx.reveal_sid = None - ctx.connection_dn = None - ctx.RODC = False - ctx.krbtgt_dn = None - ctx.drsuapi = None - ctx.managedby = None - - - def del_noerror(ctx, dn, recursive=False): - if recursive: - try: - res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=["dn"]) - except Exception: - return - for r in res: - ctx.del_noerror(r.dn, recursive=True) - try: - ctx.samdb.delete(dn) - print "Deleted %s" % dn - except Exception: - pass - - def cleanup_old_join(ctx): - '''remove any DNs from a previous join''' - try: - # find the krbtgt link - print("checking samaccountname") - res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(), - expression='samAccountName=%s' % ctx.samname, - attrs=["msDS-krbTgtLink"]) - if res: - ctx.del_noerror(res[0].dn, recursive=True) - if ctx.connection_dn is not None: - ctx.del_noerror(ctx.connection_dn) - if ctx.krbtgt_dn is not None: - ctx.del_noerror(ctx.krbtgt_dn) - ctx.del_noerror(ctx.ntds_dn) - ctx.del_noerror(ctx.server_dn, recursive=True) - if ctx.topology_dn: - ctx.del_noerror(ctx.topology_dn) - if res: - ctx.new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0] - ctx.del_noerror(ctx.new_krbtgt_dn) - except Exception: - pass - - def find_dc(ctx, domain): - '''find a writeable DC for the given domain''' - try: - ctx.cldap_ret = ctx.net.finddc(domain, nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE) - except Exception: - raise Exception("Failed to find a writeable DC for domain '%s'" % domain) - if ctx.cldap_ret.client_site is not None and ctx.cldap_ret.client_site != "": - ctx.site = ctx.cldap_ret.client_site - return ctx.cldap_ret.pdc_dns_name - - - def get_dsServiceName(ctx): - res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) - return res[0]["dsServiceName"][0] - - def get_behavior_version(ctx): - res = ctx.samdb.search(base=ctx.base_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"]) - if "msDS-Behavior-Version" in res[0]: - return int(res[0]["msDS-Behavior-Version"][0]) - else: - return samba.dsdb.DS_DOMAIN_FUNCTION_2000 - - def get_dnsHostName(ctx): - res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"]) - return res[0]["dnsHostName"][0] - - def get_domain_name(ctx): - '''get netbios name of the domain from the partitions record''' - partitions_dn = ctx.samdb.get_partitions_dn() - res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"], - expression='ncName=%s' % ctx.samdb.get_default_basedn()) - return res[0]["nETBIOSName"][0] - - def get_mysid(ctx): - '''get the SID of the connected user. Only works with w2k8 and later, - so only used for RODC join''' - res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"]) - binsid = res[0]["tokenGroups"][0] - return ctx.samdb.schema_format_value("objectSID", binsid) - - def dn_exists(ctx, dn): - '''check if a DN exists''' - try: - res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[]) - except ldb.LdbError, (enum, estr): - if enum == ldb.ERR_NO_SUCH_OBJECT: - return False - raise - return True - - def add_krbtgt_account(ctx): - '''RODCs need a special krbtgt account''' - print "Adding %s" % ctx.krbtgt_dn - rec = { - "dn" : ctx.krbtgt_dn, - "objectclass" : "user", - "useraccountcontrol" : str(samba.dsdb.UF_NORMAL_ACCOUNT | - samba.dsdb.UF_ACCOUNTDISABLE), - "showinadvancedviewonly" : "TRUE", - "description" : "krbtgt for %s" % ctx.samname} - ctx.samdb.add(rec, ["rodc_join:1:1"]) - - # now we need to search for the samAccountName attribute on the krbtgt DN, - # as this will have been magically set to the krbtgt number - res = ctx.samdb.search(base=ctx.krbtgt_dn, scope=ldb.SCOPE_BASE, attrs=["samAccountName"]) - ctx.krbtgt_name = res[0]["samAccountName"][0] - - print "Got krbtgt_name=%s" % ctx.krbtgt_name - - m = ldb.Message() - m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn) - m["msDS-krbTgtLink"] = ldb.MessageElement(ctx.krbtgt_dn, - ldb.FLAG_MOD_REPLACE, "msDS-krbTgtLink") - ctx.samdb.modify(m) - - ctx.new_krbtgt_dn = "CN=%s,CN=Users,%s" % (ctx.krbtgt_name, ctx.base_dn) - print "Renaming %s to %s" % (ctx.krbtgt_dn, ctx.new_krbtgt_dn) - ctx.samdb.rename(ctx.krbtgt_dn, ctx.new_krbtgt_dn) - - def drsuapi_connect(ctx): - '''make a DRSUAPI connection to the server''' - binding_options = "seal" - if ctx.lp.get("log level") >= 5: - binding_options += ",print" - binding_string = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options) - ctx.drsuapi = drsuapi.drsuapi(binding_string, ctx.lp, ctx.creds) - (ctx.drsuapi_handle, ctx.bind_supported_extensions) = drs_utils.drs_DsBind(ctx.drsuapi) - - def create_tmp_samdb(ctx): - '''create a temporary samdb object for schema queries''' - ctx.tmp_schema = Schema(security.dom_sid(ctx.domsid), - schemadn=ctx.schema_dn) - ctx.tmp_samdb = SamDB(session_info=system_session(), url=None, auto_connect=False, - credentials=ctx.creds, lp=ctx.lp, global_schema=False, - am_rodc=False) - ctx.tmp_samdb.set_schema(ctx.tmp_schema) - - def build_DsReplicaAttribute(ctx, attrname, attrvalue): - '''build a DsReplicaAttributeCtr object''' - r = drsuapi.DsReplicaAttribute() - r.attid = ctx.tmp_samdb.get_attid_from_lDAPDisplayName(attrname) - r.value_ctr = 1 - - - def DsAddEntry(ctx, rec): - '''add a record via the DRSUAPI DsAddEntry call''' - if ctx.drsuapi is None: - ctx.drsuapi_connect() - if ctx.tmp_samdb is None: - ctx.create_tmp_samdb() - - id = drsuapi.DsReplicaObjectIdentifier() - id.dn = rec['dn'] - - attrs = [] - for a in rec: - if a == 'dn': - continue - if not isinstance(rec[a], list): - v = [rec[a]] - else: - v = rec[a] - rattr = ctx.tmp_samdb.dsdb_DsReplicaAttribute(ctx.tmp_samdb, a, v) - attrs.append(rattr) - - attribute_ctr = drsuapi.DsReplicaAttributeCtr() - attribute_ctr.num_attributes = len(attrs) - attribute_ctr.attributes = attrs - - object = drsuapi.DsReplicaObject() - object.identifier = id - object.attribute_ctr = attribute_ctr - - first_object = drsuapi.DsReplicaObjectListItem() - first_object.object = object - - req2 = drsuapi.DsAddEntryRequest2() - req2.first_object = first_object - - (level, ctr) = ctx.drsuapi.DsAddEntry(ctx.drsuapi_handle, 2, req2) - if ctr.err_ver != 1: - raise RuntimeError("expected err_ver 1, got %u" % ctr.err_ver) - if ctr.err_data.status != (0, 'WERR_OK'): - print("DsAddEntry failed with status %s info %s" % (ctr.err_data.status, - ctr.err_data.info.extended_err)) - raise RuntimeError("DsAddEntry failed") - - def join_add_objects(ctx): - '''add the various objects needed for the join''' - print "Adding %s" % ctx.acct_dn - rec = { - "dn" : ctx.acct_dn, - "objectClass": "computer", - "displayname": ctx.samname, - "samaccountname" : ctx.samname, - "userAccountControl" : str(ctx.userAccountControl | samba.dsdb.UF_ACCOUNTDISABLE), - "dnshostname" : ctx.dnshostname} - if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2008: - rec['msDS-SupportedEncryptionTypes'] = str(samba.dsdb.ENC_ALL_TYPES) - if ctx.managedby: - rec["managedby"] = ctx.managedby - if ctx.never_reveal_sid: - rec["msDS-NeverRevealGroup"] = ctx.never_reveal_sid - if ctx.reveal_sid: - rec["msDS-RevealOnDemandGroup"] = ctx.reveal_sid - ctx.samdb.add(rec) - - if ctx.krbtgt_dn: - ctx.add_krbtgt_account() - - print "Adding %s" % ctx.server_dn - rec = { - "dn": ctx.server_dn, - "objectclass" : "server", - "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME | - samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE | - samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE), - "serverReference" : ctx.acct_dn, - "dnsHostName" : ctx.dnshostname} - ctx.samdb.add(rec) - - # FIXME: the partition (NC) assignment has to be made dynamic - print "Adding %s" % ctx.ntds_dn - rec = { - "dn" : ctx.ntds_dn, - "objectclass" : "nTDSDSA", - "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE), - "dMDLocation" : ctx.schema_dn} - - if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: - rec["msDS-Behavior-Version"] = str(ctx.behavior_version) - - if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: - rec["msDS-HasDomainNCs"] = ctx.base_dn - - if ctx.RODC: - rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn - rec["msDS-HasFullReplicaNCs"] = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ] - rec["options"] = "37" - ctx.samdb.add(rec, ["rodc_join:1:1"]) - else: - rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn - rec["HasMasterNCs"] = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ] - if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003: - rec["msDS-HasMasterNCs"] = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ] - rec["options"] = "1" - rec["invocationId"] = ndr_pack(misc.GUID(str(uuid.uuid4()))) - ctx.DsAddEntry(rec) - - # find the GUID of our NTDS DN - res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) - ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0])) - - if ctx.connection_dn is not None: - print "Adding %s" % ctx.connection_dn - rec = { - "dn" : ctx.connection_dn, - "objectclass" : "nTDSConnection", - "enabledconnection" : "TRUE", - "options" : "65", - "fromServer" : ctx.dc_ntds_dn} - ctx.samdb.add(rec) - - if ctx.topology_dn: - print "Adding %s" % ctx.topology_dn - rec = { - "dn" : ctx.topology_dn, - "objectclass" : "msDFSR-Member", - "msDFSR-ComputerReference" : ctx.acct_dn, - "serverReference" : ctx.ntds_dn} - ctx.samdb.add(rec) - - print "Adding SPNs to %s" % ctx.acct_dn - m = ldb.Message() - m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn) - for i in range(len(ctx.SPNs)): - ctx.SPNs[i] = ctx.SPNs[i].replace("$NTDSGUID", str(ctx.ntds_guid)) - m["servicePrincipalName"] = ldb.MessageElement(ctx.SPNs, - ldb.FLAG_MOD_ADD, - "servicePrincipalName") - ctx.samdb.modify(m) - - print "Setting account password for %s" % ctx.samname - ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))" % ctx.samname, - ctx.acct_pass, - force_change_at_next_login=False, - username=ctx.samname) - res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-keyVersionNumber"]) - ctx.key_version_number = int(res[0]["msDS-keyVersionNumber"][0]) - - print("Enabling account") - m = ldb.Message() - m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn) - m["userAccountControl"] = ldb.MessageElement(str(ctx.userAccountControl), - ldb.FLAG_MOD_REPLACE, - "userAccountControl") - ctx.samdb.modify(m) - - def join_provision(ctx): - '''provision the local SAM''' - - print "Calling bare provision" - - logger = logging.getLogger("provision") - logger.addHandler(logging.StreamHandler(sys.stdout)) - smbconf = ctx.lp.configfile - - presult = provision(logger, system_session(), None, - smbconf=smbconf, targetdir=ctx.targetdir, samdb_fill=FILL_DRS, - realm=ctx.realm, rootdn=ctx.root_dn, domaindn=ctx.base_dn, - schemadn=ctx.schema_dn, - configdn=ctx.config_dn, - serverdn=ctx.server_dn, domain=ctx.domain_name, - hostname=ctx.myname, domainsid=ctx.domsid, - machinepass=ctx.acct_pass, serverrole="domain controller", - sitename=ctx.site, lp=ctx.lp) - print "Provision OK for domain DN %s" % presult.domaindn - ctx.local_samdb = presult.samdb - ctx.lp = presult.lp - ctx.paths = presult.paths - - - def join_replicate(ctx): - '''replicate the SAM''' - - print "Starting replication" - ctx.local_samdb.transaction_start() - try: - source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id()) - destination_dsa_guid = ctx.ntds_guid - - if ctx.RODC: - repl_creds = Credentials() - repl_creds.guess(ctx.lp) - repl_creds.set_kerberos_state(DONT_USE_KERBEROS) - repl_creds.set_username(ctx.samname) - repl_creds.set_password(ctx.acct_pass) - else: - repl_creds = ctx.creds - - binding_options = "seal" - if ctx.lp.get("debug level") >= 5: - binding_options += ",print" - repl = drs_utils.drs_Replicate( - "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options), - ctx.lp, repl_creds, ctx.local_samdb) - - repl.replicate(ctx.schema_dn, source_dsa_invocation_id, - destination_dsa_guid, schema=True, rodc=ctx.RODC, - replica_flags=ctx.replica_flags) - repl.replicate(ctx.config_dn, source_dsa_invocation_id, - destination_dsa_guid, rodc=ctx.RODC, - replica_flags=ctx.replica_flags) - repl.replicate(ctx.base_dn, source_dsa_invocation_id, - destination_dsa_guid, rodc=ctx.RODC, - replica_flags=ctx.replica_flags) - if ctx.RODC: - repl.replicate(ctx.acct_dn, source_dsa_invocation_id, - destination_dsa_guid, - exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) - repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id, - destination_dsa_guid, - exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) - - print "Committing SAM database" - except: - ctx.local_samdb.transaction_cancel() - raise - else: - ctx.local_samdb.transaction_commit() - - - def join_finalise(ctx): - '''finalise the join, mark us synchronised and setup secrets db''' - - print "Setting isSynchronized" - m = ldb.Message() - m.dn = ldb.Dn(ctx.samdb, '@ROOTDSE') - m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized") - ctx.samdb.modify(m) - - secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp) - - print "Setting up secrets database" - secretsdb_self_join(secrets_ldb, domain=ctx.domain_name, - realm=ctx.realm, - dnsdomain=ctx.dnsdomain, - netbiosname=ctx.myname, - domainsid=security.dom_sid(ctx.domsid), - machinepass=ctx.acct_pass, - secure_channel_type=ctx.secure_channel_type, - key_version_number=ctx.key_version_number) - - def do_join(ctx): - ctx.cleanup_old_join() - try: - ctx.join_add_objects() - ctx.join_provision() - ctx.join_replicate() - ctx.join_finalise() - except Exception: - print "Join failed - cleaning up" - ctx.cleanup_old_join() - raise - - -def join_RODC(server=None, creds=None, lp=None, site=None, netbios_name=None, - targetdir=None, domain=None): - """join as a RODC""" - - ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain) - - ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn) - - # setup some defaults for accounts that should be replicated to this RODC - ctx.never_reveal_sid = [ "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY), - "<SID=%s>" % security.SID_BUILTIN_ADMINISTRATORS, - "<SID=%s>" % security.SID_BUILTIN_SERVER_OPERATORS, - "<SID=%s>" % security.SID_BUILTIN_BACKUP_OPERATORS, - "<SID=%s>" % security.SID_BUILTIN_ACCOUNT_OPERATORS ] - ctx.reveal_sid = "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW) - - mysid = ctx.get_mysid() - admin_dn = "<SID=%s>" % mysid - ctx.managedby = admin_dn - - ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT | - samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION | - samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT) - - ctx.SPNs.extend([ "RestrictedKrbHost/%s" % ctx.myname, - "RestrictedKrbHost/%s" % ctx.dnshostname ]) - - ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn - ctx.secure_channel_type = misc.SEC_CHAN_RODC - ctx.RODC = True - ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC | - drsuapi.DRSUAPI_DRS_PER_SYNC | - drsuapi.DRSUAPI_DRS_GET_ANC | - drsuapi.DRSUAPI_DRS_NEVER_SYNCED | - drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING | - drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP) - ctx.do_join() - - - print "Joined domain %s (SID %s) as an RODC" % (ctx.domain_name, ctx.domsid) - - -def join_DC(server=None, creds=None, lp=None, site=None, netbios_name=None, - targetdir=None, domain=None): - """join as a DC""" - ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, domain) - - ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION - - ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain) - ctx.secure_channel_type = misc.SEC_CHAN_BDC - - ctx.replica_flags = (drsuapi.DRSUAPI_DRS_WRIT_REP | - drsuapi.DRSUAPI_DRS_INIT_SYNC | - drsuapi.DRSUAPI_DRS_PER_SYNC | - drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS | - drsuapi.DRSUAPI_DRS_NEVER_SYNCED) - - ctx.do_join() - print "Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid) diff --git a/source4/scripting/python/samba/ms_display_specifiers.py b/source4/scripting/python/samba/ms_display_specifiers.py index fd92b20e66..2a54e4ae0e 100644 --- a/source4/scripting/python/samba/ms_display_specifiers.py +++ b/source4/scripting/python/samba/ms_display_specifiers.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # # Create DisplaySpecifiers LDIF (as a string) from the documents provided by # Microsoft under the WSPP. diff --git a/source4/scripting/python/samba/ms_schema.py b/source4/scripting/python/samba/ms_schema.py index 64bb28a967..a0abc337ce 100644 --- a/source4/scripting/python/samba/ms_schema.py +++ b/source4/scripting/python/samba/ms_schema.py @@ -1,14 +1,12 @@ +#!/usr/bin/env python # # create schema.ldif (as a string) from WSPP documentation # # based on minschema.py and minschema_wspp # -"""Generate LDIF from WSPP documentation.""" - import re import base64 -import uuid bitFields = {} @@ -35,19 +33,19 @@ bitFields["searchflags"] = { # ADTS: 2.2.10 bitFields["systemflags"] = { - 'FLAG_ATTR_NOT_REPLICATED': 31, 'FLAG_CR_NTDS_NC': 31, # NR - 'FLAG_ATTR_REQ_PARTIAL_SET_MEMBER': 30, 'FLAG_CR_NTDS_DOMAIN': 30, # PS - 'FLAG_ATTR_IS_CONSTRUCTED': 29, 'FLAG_CR_NTDS_NOT_GC_REPLICATED': 29, # CS - 'FLAG_ATTR_IS_OPERATIONAL': 28, # OP - 'FLAG_SCHEMA_BASE_OBJECT': 27, # BS - 'FLAG_ATTR_IS_RDN': 26, # RD - 'FLAG_DISALLOW_MOVE_ON_DELETE': 6, # DE - 'FLAG_DOMAIN_DISALLOW_MOVE': 5, # DM - 'FLAG_DOMAIN_DISALLOW_RENAME': 4, # DR - 'FLAG_CONFIG_ALLOW_LIMITED_MOVE': 3, # AL - 'FLAG_CONFIG_ALLOW_MOVE': 2, # AM - 'FLAG_CONFIG_ALLOW_RENAME': 1, # AR - 'FLAG_DISALLOW_DELETE': 0 # DD + 'FLAG_ATTR_NOT_REPLICATED': 31, 'FLAG_CR_NTDS_NC': 31, # NR + 'FLAG_ATTR_REQ_PARTIAL_SET_MEMBER': 30, 'FLAG_CR_NTDS_DOMAIN': 30, # PS + 'FLAG_ATTR_IS_CONSTRUCTED': 29, 'FLAG_CR_NTDS_NOT_GC_REPLICATED': 29, # CS + 'FLAG_ATTR_IS_OPERATIONAL': 28, # OP + 'FLAG_SCHEMA_BASE_OBJECT': 27, # BS + 'FLAG_ATTR_IS_RDN': 26, # RD + 'FLAG_DISALLOW_MOVE_ON_DELETE': 6, # DE + 'FLAG_DOMAIN_DISALLOW_MOVE': 5, # DM + 'FLAG_DOMAIN_DISALLOW_RENAME': 4, # DR + 'FLAG_CONFIG_ALLOW_LIMITED_MOVE': 3, # AL + 'FLAG_CONFIG_ALLOW_MOVE': 2, # AM + 'FLAG_CONFIG_ALLOW_RENAME': 1, # AR + 'FLAG_DISALLOW_DELETE': 0 # DD } # ADTS: 2.2.11 @@ -229,9 +227,6 @@ def __transform_entry(entry, objectClass): entry.insert(0, ["dn", "CN=%s,${SCHEMADN}" % cn]) entry.insert(1, ["objectClass", ["top", objectClass]]) entry.insert(2, ["cn", cn]) - entry.insert(2, ["objectGUID", str(uuid.uuid4())]) - entry.insert(2, ["adminDescription", cn]) - entry.insert(2, ["adminDisplayName", cn]) for l in entry: key = l[0].lower() @@ -277,3 +272,5 @@ if __name__ == '__main__': sys.exit(1) print read_ms_schema(attr_file, classes_file) + + diff --git a/source4/scripting/python/samba/ndr.py b/source4/scripting/python/samba/ndr.py index 112668523f..e718ff3422 100644 --- a/source4/scripting/python/samba/ndr.py +++ b/source4/scripting/python/samba/ndr.py @@ -1,49 +1,28 @@ +#!/usr/bin/python # -*- coding: utf-8 -*- # Unix SMB/CIFS implementation. # Copyright © Jelmer Vernooij <jelmer@samba.org> 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 # 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/>. # - -"""Network Data Representation (NDR) marshalling and unmarshalling.""" - - def ndr_pack(object): - """Pack a NDR object. - - :param object: Object to pack - :return: String object with marshalled object. - """ - ndr_pack = getattr(object, "__ndr_pack__", None) - if ndr_pack is None: - raise TypeError("%r is not a NDR object" % object) - return ndr_pack() + return object.__ndr_pack__() def ndr_unpack(cls, data): - """NDR unpack an object. - - :param cls: Class of the object to unpack - :param data: Buffer to unpack - :return: Unpacked object - """ object = cls() object.__ndr_unpack__(data) return object - - -def ndr_print(object): - return object.__ndr_print__() diff --git a/source4/scripting/python/samba/netcmd/__init__.py b/source4/scripting/python/samba/netcmd/__init__.py deleted file mode 100644 index aa74f657b2..0000000000 --- a/source4/scripting/python/samba/netcmd/__init__.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009 -# -# 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/>. -# - -import optparse, samba -from samba import getopt as options -from ldb import LdbError -import sys, traceback - - -class Option(optparse.Option): - pass - - -class Command(object): - """A net command.""" - - def _get_description(self): - return self.__doc__.splitlines()[0].rstrip("\n") - - def _get_name(self): - name = self.__class__.__name__ - if name.startswith("cmd_"): - return name[4:] - return name - - name = property(_get_name) - - def usage(self, *args): - parser, _ = self._create_parser() - parser.print_usage() - - description = property(_get_description) - - def _get_synopsis(self): - ret = self.name - if self.takes_args: - ret += " " + " ".join([x.upper() for x in self.takes_args]) - return ret - - def show_command_error(self, e): - '''display a command error''' - if isinstance(e, CommandError): - (etype, evalue, etraceback) = e.exception_info - inner_exception = e.inner_exception - message = e.message - force_traceback = False - else: - (etype, evalue, etraceback) = sys.exc_info() - inner_exception = e - message = "uncaught exception" - force_traceback = True - - if isinstance(inner_exception, LdbError): - (ldb_ecode, ldb_emsg) = inner_exception - print >>sys.stderr, "ERROR(ldb): %s - %s" % (message, ldb_emsg) - elif isinstance(inner_exception, AssertionError): - print >>sys.stderr, "ERROR(assert): %s" % message - force_traceback = True - elif isinstance(inner_exception, RuntimeError): - print >>sys.stderr, "ERROR(runtime): %s - %s" % (message, evalue) - elif type(inner_exception) is Exception: - print >>sys.stderr, "ERROR(exception): %s - %s" % (message, evalue) - force_traceback = True - elif inner_exception is None: - print >>sys.stderr, "ERROR: %s" % (message) - else: - print >>sys.stderr, "ERROR(%s): %s - %s" % (str(etype), message, evalue) - force_traceback = True - - if force_traceback or samba.get_debug_level() >= 3: - traceback.print_tb(etraceback) - - - synopsis = property(_get_synopsis) - - outf = sys.stdout - - takes_args = [] - takes_options = [] - takes_optiongroups = {} - - def _create_parser(self): - parser = optparse.OptionParser(self.synopsis) - parser.prog = "net" - parser.add_options(self.takes_options) - optiongroups = {} - for name, optiongroup in self.takes_optiongroups.iteritems(): - optiongroups[name] = optiongroup(parser) - parser.add_option_group(optiongroups[name]) - return parser, optiongroups - - def message(self, text): - print text - - def _run(self, *argv): - parser, optiongroups = self._create_parser() - opts, args = parser.parse_args(list(argv)) - # Filter out options from option groups - args = args[1:] - kwargs = dict(opts.__dict__) - for option_group in parser.option_groups: - for option in option_group.option_list: - if option.dest is not None: - del kwargs[option.dest] - kwargs.update(optiongroups) - min_args = 0 - max_args = 0 - for i, arg in enumerate(self.takes_args): - if arg[-1] not in ("?", "*"): - min_args += 1 - max_args += 1 - if arg[-1] == "*": - max_args = -1 - if len(args) < min_args or (max_args != -1 and len(args) > max_args): - self.usage(*args) - return -1 - try: - return self.run(*args, **kwargs) - except Exception, e: - self.show_command_error(e) - return -1 - - def run(self): - """Run the command. This should be overriden by all subclasses.""" - raise NotImplementedError(self.run) - - -class SuperCommand(Command): - """A command with subcommands.""" - - subcommands = {} - - def _run(self, myname, subcommand=None, *args): - if subcommand in self.subcommands: - return self.subcommands[subcommand]._run(subcommand, *args) - print "Available subcommands:" - for cmd in self.subcommands: - print "\t%-20s - %s" % (cmd, self.subcommands[cmd].description) - if subcommand in [None, 'help', '-h', '--help' ]: - return 0 - raise CommandError("No such subcommand '%s'" % subcommand) - - def usage(self, myname, subcommand=None, *args): - if subcommand is None or not subcommand in self.subcommands: - print "Usage: %s (%s) [options]" % (myname, - " | ".join(self.subcommands.keys())) - else: - return self.subcommands[subcommand].usage(*args) - - -class CommandError(Exception): - '''an exception class for netcmd errors''' - def __init__(self, message, inner_exception=None): - self.message = message - self.inner_exception = inner_exception - self.exception_info = sys.exc_info() - - -commands = {} -from samba.netcmd.pwsettings import cmd_pwsettings -commands["pwsettings"] = cmd_pwsettings() -from samba.netcmd.domainlevel import cmd_domainlevel -commands["domainlevel"] = cmd_domainlevel() -from samba.netcmd.setpassword import cmd_setpassword -commands["setpassword"] = cmd_setpassword() -from samba.netcmd.setexpiry import cmd_setexpiry -commands["setexpiry"] = cmd_setexpiry() -from samba.netcmd.enableaccount import cmd_enableaccount -commands["enableaccount"] = cmd_enableaccount() -from samba.netcmd.newuser import cmd_newuser -commands["newuser"] = cmd_newuser() -from samba.netcmd.netacl import cmd_acl -commands["acl"] = cmd_acl() -from samba.netcmd.fsmo import cmd_fsmo -commands["fsmo"] = cmd_fsmo() -from samba.netcmd.export import cmd_export -commands["export"] = cmd_export() -from samba.netcmd.time import cmd_time -commands["time"] = cmd_time() -from samba.netcmd.user import cmd_user -commands["user"] = cmd_user() -from samba.netcmd.vampire import cmd_vampire -commands["vampire"] = cmd_vampire() -from samba.netcmd.machinepw import cmd_machinepw -commands["machinepw"] = cmd_machinepw() -from samba.netcmd.spn import cmd_spn -commands["spn"] = cmd_spn() -from samba.netcmd.group import cmd_group -commands["group"] = cmd_group() -from samba.netcmd.join import cmd_join -commands["join"] = cmd_join() -from samba.netcmd.rodc import cmd_rodc -commands["rodc"] = cmd_rodc() -from samba.netcmd.drs import cmd_drs -commands["drs"] = cmd_drs() -from samba.netcmd.gpo import cmd_gpo -commands["gpo2"] = cmd_gpo() -from samba.netcmd.ldapcmp import cmd_ldapcmp -commands["ldapcmp"] = cmd_ldapcmp() diff --git a/source4/scripting/python/samba/netcmd/common.py b/source4/scripting/python/samba/netcmd/common.py deleted file mode 100644 index bd72c8f361..0000000000 --- a/source4/scripting/python/samba/netcmd/common.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -# common functions for samba-tool python commands -# -# Copyright Andrew Tridgell 2010 -# -# 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/>. -# - -def netcmd_dnsname(lp): - '''return the full DNS name of our own host. Used as a default - for hostname when running status queries''' - return lp.get('netbios name').lower() + "." + lp.get('realm').lower() diff --git a/source4/scripting/python/samba/netcmd/domainlevel.py b/source4/scripting/python/samba/netcmd/domainlevel.py deleted file mode 100644 index 3d50ccea78..0000000000 --- a/source4/scripting/python/samba/netcmd/domainlevel.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env python -# -# Raises domain and forest function levels -# -# Copyright Matthias Dieter Wallnoefer 2009 -# Copyright Jelmer Vernooij 2009 -# -# 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/>. -# - -# Notice: At the moment we have some more checks to do here on the special -# attributes (consider attribute "msDS-Behavior-Version). This is due to the -# fact that we on s4 LDB don't implement their change policy (only certain -# values, only increments possible...) yet. - -import samba.getopt as options -import ldb - -from samba.auth import system_session -from samba.netcmd import ( - Command, - CommandError, - Option, - ) -from samba.samdb import SamDB -from samba.dsdb import ( - DS_DOMAIN_FUNCTION_2000, - DS_DOMAIN_FUNCTION_2003, - DS_DOMAIN_FUNCTION_2003_MIXED, - DS_DOMAIN_FUNCTION_2008, - DS_DOMAIN_FUNCTION_2008_R2, - ) - -class cmd_domainlevel(Command): - """Raises domain and forest function levels""" - - synopsis = "(show | raise <options>)" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - Option("--quiet", help="Be quiet", action="store_true"), - Option("--forest", type="choice", choices=["2003", "2008", "2008_R2"], - help="The forest function level (2003 | 2008 | 2008_R2)"), - Option("--domain", type="choice", choices=["2003", "2008", "2008_R2"], - help="The domain function level (2003 | 2008 | 2008_R2)"), - ] - - takes_args = ["subcommand"] - - def run(self, subcommand, H=None, forest=None, domain=None, quiet=False, - credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - - domain_dn = samdb.domain_dn() - - res_forest = samdb.search("CN=Partitions,CN=Configuration," + domain_dn, - scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"]) - assert len(res_forest) == 1 - - res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE, - attrs=["msDS-Behavior-Version", "nTMixedDomain"]) - assert len(res_domain) == 1 - - res_dc_s = samdb.search("CN=Sites,CN=Configuration," + domain_dn, - scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)", - attrs=["msDS-Behavior-Version"]) - assert len(res_dc_s) >= 1 - - try: - level_forest = int(res_forest[0]["msDS-Behavior-Version"][0]) - level_domain = int(res_domain[0]["msDS-Behavior-Version"][0]) - level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0]) - - min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value - for msg in res_dc_s: - if int(msg["msDS-Behavior-Version"][0]) < min_level_dc: - min_level_dc = int(msg["msDS-Behavior-Version"][0]) - - if level_forest < 0 or level_domain < 0: - raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!") - if min_level_dc < 0: - raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!") - if level_forest > level_domain: - raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!") - if level_domain > min_level_dc: - raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!") - - except KeyError: - raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!") - - if subcommand == "show": - self.message("Domain and forest function level for domain '%s'" % domain_dn) - if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0: - self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!") - if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0: - self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!") - if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0: - self.message("\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!") - - self.message("") - - if level_forest == DS_DOMAIN_FUNCTION_2000: - outstr = "2000" - elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED: - outstr = "2003 with mixed domains/interim (NT4 DC support)" - elif level_forest == DS_DOMAIN_FUNCTION_2003: - outstr = "2003" - elif level_forest == DS_DOMAIN_FUNCTION_2008: - outstr = "2008" - elif level_forest == DS_DOMAIN_FUNCTION_2008_R2: - outstr = "2008 R2" - else: - outstr = "higher than 2008 R2" - self.message("Forest function level: (Windows) " + outstr) - - if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0: - outstr = "2000 mixed (NT4 DC support)" - elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0: - outstr = "2000" - elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED: - outstr = "2003 with mixed domains/interim (NT4 DC support)" - elif level_domain == DS_DOMAIN_FUNCTION_2003: - outstr = "2003" - elif level_domain == DS_DOMAIN_FUNCTION_2008: - outstr = "2008" - elif level_domain == DS_DOMAIN_FUNCTION_2008_R2: - outstr = "2008 R2" - else: - outstr = "higher than 2008 R2" - self.message("Domain function level: (Windows) " + outstr) - - if min_level_dc == DS_DOMAIN_FUNCTION_2000: - outstr = "2000" - elif min_level_dc == DS_DOMAIN_FUNCTION_2003: - outstr = "2003" - elif min_level_dc == DS_DOMAIN_FUNCTION_2008: - outstr = "2008" - elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2: - outstr = "2008 R2" - else: - outstr = "higher than 2008 R2" - self.message("Lowest function level of a DC: (Windows) " + outstr) - - elif subcommand == "raise": - msgs = [] - - if domain is not None: - if domain == "2003": - new_level_domain = DS_DOMAIN_FUNCTION_2003 - elif domain == "2008": - new_level_domain = DS_DOMAIN_FUNCTION_2008 - elif domain == "2008_R2": - new_level_domain = DS_DOMAIN_FUNCTION_2008_R2 - - if new_level_domain <= level_domain and level_domain_mixed == 0: - raise CommandError("Domain function level can't be smaller equal to the actual one!") - - if new_level_domain > min_level_dc: - raise CommandError("Domain function level can't be higher than the lowest function level of a DC!") - - # Deactivate mixed/interim domain support - if level_domain_mixed != 0: - # Directly on the base DN - m = ldb.Message() - m.dn = ldb.Dn(samdb, domain_dn) - m["nTMixedDomain"] = ldb.MessageElement("0", - ldb.FLAG_MOD_REPLACE, "nTMixedDomain") - samdb.modify(m) - # Under partitions - m = ldb.Message() - m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") - + ",CN=Partitions,CN=Configuration," + domain_dn) - m["nTMixedDomain"] = ldb.MessageElement("0", - ldb.FLAG_MOD_REPLACE, "nTMixedDomain") - try: - samdb.modify(m) - except ldb.LdbError, (enum, emsg): - if enum != ldb.ERR_UNWILLING_TO_PERFORM: - raise - - # Directly on the base DN - m = ldb.Message() - m.dn = ldb.Dn(samdb, domain_dn) - m["msDS-Behavior-Version"]= ldb.MessageElement( - str(new_level_domain), ldb.FLAG_MOD_REPLACE, - "msDS-Behavior-Version") - samdb.modify(m) - # Under partitions - m = ldb.Message() - m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") - + ",CN=Partitions,CN=Configuration," + domain_dn) - m["msDS-Behavior-Version"]= ldb.MessageElement( - str(new_level_domain), ldb.FLAG_MOD_REPLACE, - "msDS-Behavior-Version") - try: - samdb.modify(m) - except ldb.LdbError, (enum, emsg): - if enum != ldb.ERR_UNWILLING_TO_PERFORM: - raise - - level_domain = new_level_domain - msgs.append("Domain function level changed!") - - if forest is not None: - if forest == "2003": - new_level_forest = DS_DOMAIN_FUNCTION_2003 - elif forest == "2008": - new_level_forest = DS_DOMAIN_FUNCTION_2008 - elif forest == "2008_R2": - new_level_forest = DS_DOMAIN_FUNCTION_2008_R2 - if new_level_forest <= level_forest: - raise CommandError("Forest function level can't be smaller equal to the actual one!") - if new_level_forest > level_domain: - raise CommandError("Forest function level can't be higher than the domain function level(s). Please raise it/them first!") - m = ldb.Message() - m.dn = ldb.Dn(samdb, "CN=Partitions,CN=Configuration," - + domain_dn) - m["msDS-Behavior-Version"]= ldb.MessageElement( - str(new_level_forest), ldb.FLAG_MOD_REPLACE, - "msDS-Behavior-Version") - samdb.modify(m) - msgs.append("Forest function level changed!") - msgs.append("All changes applied successfully!") - self.message("\n".join(msgs)) - else: - raise CommandError("Wrong argument '%s'!" % subcommand) diff --git a/source4/scripting/python/samba/netcmd/drs.py b/source4/scripting/python/samba/netcmd/drs.py deleted file mode 100644 index 7dea9de856..0000000000 --- a/source4/scripting/python/samba/netcmd/drs.py +++ /dev/null @@ -1,477 +0,0 @@ -#!/usr/bin/env python -# -# implement samba_tool drs commands -# -# Copyright Andrew Tridgell 2010 -# -# based on C implementation by Kamen Mazdrashki <kamen.mazdrashki@postpath.com> -# -# 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/>. -# - -import samba.getopt as options -import ldb - -from samba.auth import system_session -from samba.netcmd import ( - Command, - CommandError, - Option, - SuperCommand, - ) -from samba.samdb import SamDB -from samba import drs_utils, nttime2string, dsdb -from samba.dcerpc import drsuapi, misc -import common - -def drsuapi_connect(ctx): - '''make a DRSUAPI connection to the server''' - binding_options = "seal" - if ctx.lp.get("log level") >= 5: - binding_options += ",print" - binding_string = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options) - try: - ctx.drsuapi = drsuapi.drsuapi(binding_string, ctx.lp, ctx.creds) - (ctx.drsuapi_handle, ctx.bind_supported_extensions) = drs_utils.drs_DsBind(ctx.drsuapi) - except Exception, e: - raise CommandError("DRS connection to %s failed" % ctx.server, e) - - -def samdb_connect(ctx): - '''make a ldap connection to the server''' - try: - ctx.samdb = SamDB(url="ldap://%s" % ctx.server, - session_info=system_session(), - credentials=ctx.creds, lp=ctx.lp) - except Exception, e: - raise CommandError("LDAP connection to %s failed" % ctx.server, e) - - -def drs_errmsg(werr): - '''return "was successful" or an error string''' - (ecode, estring) = werr - if ecode == 0: - return "was successful" - return "failed, result %u (%s)" % (ecode, estring) - - -def attr_default(msg, attrname, default): - '''get an attribute from a ldap msg with a default''' - if attrname in msg: - return msg[attrname][0] - return default - - -def drs_parse_ntds_dn(ntds_dn): - '''parse a NTDS DN returning a site and server''' - a = ntds_dn.split(',') - if a[0] != "CN=NTDS Settings" or a[2] != "CN=Servers" or a[4] != 'CN=Sites': - raise RuntimeError("bad NTDS DN %s" % ntds_dn) - server = a[1].split('=')[1] - site = a[3].split('=')[1] - return (site, server) - - -def get_dsServiceName(samdb): - '''get the NTDS DN from the rootDSE''' - res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) - return res[0]["dsServiceName"][0] - - -class cmd_drs_showrepl(Command): - """show replication status""" - - synopsis = "%prog drs showrepl <DC>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_args = ["DC?"] - - def print_neighbour(self, n): - '''print one set of neighbour information''' - self.message("%s" % n.naming_context_dn) - try: - (site, server) = drs_parse_ntds_dn(n.source_dsa_obj_dn) - self.message("\t%s\%s via RPC" % (site, server)) - except RuntimeError: - self.message("\tNTDS DN: %s" % n.source_dsa_obj_dn) - self.message("\t\tDSA object GUID: %s" % n.source_dsa_obj_guid) - self.message("\t\tLast attempt @ %s %s" % (nttime2string(n.last_attempt), - drs_errmsg(n.result_last_attempt))) - self.message("\t\t%u consecutive failure(s)." % n.consecutive_sync_failures) - self.message("\t\tLast success @ %s" % nttime2string(n.last_success)) - self.message("") - - def drsuapi_ReplicaInfo(ctx, info_type): - '''call a DsReplicaInfo''' - - req1 = drsuapi.DsReplicaGetInfoRequest1() - req1.info_type = info_type - try: - (info_type, info) = ctx.drsuapi.DsReplicaGetInfo(ctx.drsuapi_handle, 1, req1) - except Exception, e: - raise CommandError("DsReplicaGetInfo of type %u failed" % info_type, e) - return (info_type, info) - - - def run(self, DC=None, sambaopts=None, - credopts=None, versionopts=None, server=None): - - self.lp = sambaopts.get_loadparm() - if DC is None: - DC = common.netcmd_dnsname(self.lp) - self.server = DC - self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - - drsuapi_connect(self) - samdb_connect(self) - - # show domain information - ntds_dn = get_dsServiceName(self.samdb) - server_dns = self.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"])[0]['dnsHostName'][0] - - (site, server) = drs_parse_ntds_dn(ntds_dn) - try: - ntds = self.samdb.search(base=ntds_dn, scope=ldb.SCOPE_BASE, attrs=['options', 'objectGUID', 'invocationId']) - except Exception, e: - raise CommandError("Failed to search NTDS DN %s" % ntds_dn) - conn = self.samdb.search(base=ntds_dn, expression="(objectClass=nTDSConnection)") - - self.message("%s\\%s" % (site, server)) - self.message("DSA Options: 0x%08x" % int(attr_default(ntds[0], "options", 0))) - self.message("DSA object GUID: %s" % self.samdb.schema_format_value("objectGUID", ntds[0]["objectGUID"][0])) - self.message("DSA invocationId: %s\n" % self.samdb.schema_format_value("objectGUID", ntds[0]["invocationId"][0])) - - self.message("==== INBOUND NEIGHBORS ====\n") - (info_type, info) = self.drsuapi_ReplicaInfo(drsuapi.DRSUAPI_DS_REPLICA_INFO_NEIGHBORS) - for n in info.array: - self.print_neighbour(n) - - - self.message("==== OUTBOUND NEIGHBORS ====\n") - (info_type, info) = self.drsuapi_ReplicaInfo(drsuapi.DRSUAPI_DS_REPLICA_INFO_REPSTO) - for n in info.array: - self.print_neighbour(n) - - reasons = ['NTDSCONN_KCC_GC_TOPOLOGY', - 'NTDSCONN_KCC_RING_TOPOLOGY', - 'NTDSCONN_KCC_MINIMIZE_HOPS_TOPOLOGY', - 'NTDSCONN_KCC_STALE_SERVERS_TOPOLOGY', - 'NTDSCONN_KCC_OSCILLATING_CONNECTION_TOPOLOGY', - 'NTDSCONN_KCC_INTERSITE_GC_TOPOLOGY', - 'NTDSCONN_KCC_INTERSITE_TOPOLOGY', - 'NTDSCONN_KCC_SERVER_FAILOVER_TOPOLOGY', - 'NTDSCONN_KCC_SITE_FAILOVER_TOPOLOGY', - 'NTDSCONN_KCC_REDUNDANT_SERVER_TOPOLOGY'] - - self.message("==== KCC CONNECTION OBJECTS ====\n") - for c in conn: - self.message("Connection --") - self.message("\tConnection name: %s" % c['name'][0]) - self.message("\tEnabled : %s" % attr_default(c, 'enabledConnection', 'TRUE')) - self.message("\tServer DNS name : %s" % server_dns) - self.message("\tServer DN name : %s" % c['fromServer'][0]) - self.message("\t\tTransportType: RPC") - self.message("\t\toptions: 0x%08X" % int(attr_default(c, 'options', 0))) - if not 'mS-DS-ReplicatesNCReason' in c: - self.message("Warning: No NC replicated for Connection!") - continue - for r in c['mS-DS-ReplicatesNCReason']: - a = str(r).split(':') - self.message("\t\tReplicatesNC: %s" % a[3]) - self.message("\t\tReason: 0x%08x" % int(a[2])) - for s in reasons: - if getattr(dsdb, s, 0) & int(a[2]): - self.message("\t\t\t%s" % s) - - -class cmd_drs_kcc(Command): - """trigger knowledge consistency center run""" - - synopsis = "%prog drs kcc <DC>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_args = ["DC?"] - - def run(self, DC=None, sambaopts=None, - credopts=None, versionopts=None, server=None): - - self.lp = sambaopts.get_loadparm() - if DC is None: - DC = common.netcmd_dnsname(self.lp) - self.server = DC - - self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - - drsuapi_connect(self) - - req1 = drsuapi.DsExecuteKCC1() - try: - self.drsuapi.DsExecuteKCC(self.drsuapi_handle, 1, req1) - except Exception, e: - raise CommandError("DsExecuteKCC failed", e) - self.message("Consistency check on %s successful." % DC) - - - -class cmd_drs_replicate(Command): - """replicate a naming context between two DCs""" - - synopsis = "%prog drs replicate <DEST_DC> <SOURCE_DC> <NC>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_args = ["DEST_DC", "SOURCE_DC", "NC"] - - takes_options = [ - Option("--add-ref", help="use ADD_REF to add to repsTo on source", action="store_true"), - Option("--sync-forced", help="use SYNC_FORCED to force inbound replication", action="store_true"), - ] - - def run(self, DEST_DC, SOURCE_DC, NC, add_ref=False, sync_forced=False, - sambaopts=None, - credopts=None, versionopts=None, server=None): - - self.server = DEST_DC - self.lp = sambaopts.get_loadparm() - - self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - - drsuapi_connect(self) - samdb_connect(self) - - # we need to find the NTDS GUID of the source DC - msg = self.samdb.search(base=self.samdb.get_config_basedn(), - expression="(&(objectCategory=server)(|(name=%s)(dNSHostName=%s)))" % (SOURCE_DC, - SOURCE_DC), - attrs=[]) - if len(msg) == 0: - raise CommandError("Failed to find source DC %s" % SOURCE_DC) - server_dn = msg[0]['dn'] - - msg = self.samdb.search(base=server_dn, scope=ldb.SCOPE_ONELEVEL, - expression="(|(objectCategory=nTDSDSA)(objectCategory=nTDSDSARO))", - attrs=['objectGUID', 'options']) - if len(msg) == 0: - raise CommandError("Failed to find source NTDS DN %s" % SOURCE_DC) - source_dsa_guid = msg[0]['objectGUID'][0] - options = int(attr_default(msg, 'options', 0)) - - nc = drsuapi.DsReplicaObjectIdentifier() - nc.dn = NC - - req1 = drsuapi.DsReplicaSyncRequest1() - req1.naming_context = nc; - req1.options = 0 - if not (options & dsdb.DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL): - req1.options |= drsuapi.DRSUAPI_DRS_WRIT_REP - if add_ref: - req1.options |= drsuapi.DRSUAPI_DRS_ADD_REF - if sync_forced: - req1.options |= drsuapi.DRSUAPI_DRS_SYNC_FORCED - req1.source_dsa_guid = misc.GUID(source_dsa_guid) - - try: - self.drsuapi.DsReplicaSync(self.drsuapi_handle, 1, req1) - except Exception, estr: - raise CommandError("DsReplicaSync failed", estr) - self.message("Replicate from %s to %s was successful." % (SOURCE_DC, DEST_DC)) - - - -class cmd_drs_bind(Command): - """show DRS capabilities of a server""" - - synopsis = "%prog drs bind <DC>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_args = ["DC?"] - - def run(self, DC=None, sambaopts=None, - credopts=None, versionopts=None, server=None): - - self.lp = sambaopts.get_loadparm() - if DC is None: - DC = common.netcmd_dnsname(self.lp) - self.server = DC - self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - - drsuapi_connect(self) - samdb_connect(self) - - bind_info = drsuapi.DsBindInfoCtr() - bind_info.length = 28 - bind_info.info = drsuapi.DsBindInfo28() - (info, handle) = self.drsuapi.DsBind(misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID), bind_info) - - optmap = [ - ("DRSUAPI_SUPPORTED_EXTENSION_BASE" , "DRS_EXT_BASE"), - ("DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION" , "DRS_EXT_ASYNCREPL"), - ("DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI" , "DRS_EXT_REMOVEAPI"), - ("DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2" , "DRS_EXT_MOVEREQ_V2"), - ("DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS" , "DRS_EXT_GETCHG_DEFLATE"), - ("DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1" , "DRS_EXT_DCINFO_V1"), - ("DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION" , "DRS_EXT_RESTORE_USN_OPTIMIZATION"), - ("DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY" , "DRS_EXT_ADDENTRY"), - ("DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE" , "DRS_EXT_KCC_EXECUTE"), - ("DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2" , "DRS_EXT_ADDENTRY_V2"), - ("DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION" , "DRS_EXT_LINKED_VALUE_REPLICATION"), - ("DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2" , "DRS_EXT_DCINFO_V2"), - ("DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD","DRS_EXT_INSTANCE_TYPE_NOT_REQ_ON_MOD"), - ("DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND" , "DRS_EXT_CRYPTO_BIND"), - ("DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO" , "DRS_EXT_GET_REPL_INFO"), - ("DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION" , "DRS_EXT_STRONG_ENCRYPTION"), - ("DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01" , "DRS_EXT_DCINFO_VFFFFFFFF"), - ("DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP" , "DRS_EXT_TRANSITIVE_MEMBERSHIP"), - ("DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY" , "DRS_EXT_ADD_SID_HISTORY"), - ("DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3" , "DRS_EXT_POST_BETA3"), - ("DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V5" , "DRS_EXT_GETCHGREQ_V5"), - ("DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2" , "DRS_EXT_GETMEMBERSHIPS2"), - ("DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6" , "DRS_EXT_GETCHGREQ_V6"), - ("DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS" , "DRS_EXT_NONDOMAIN_NCS"), - ("DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8" , "DRS_EXT_GETCHGREQ_V8"), - ("DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5" , "DRS_EXT_GETCHGREPLY_V5"), - ("DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6" , "DRS_EXT_GETCHGREPLY_V6"), - ("DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3" , "DRS_EXT_WHISTLER_BETA3"), - ("DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7" , "DRS_EXT_WHISTLER_BETA3"), - ("DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT" , "DRS_EXT_WHISTLER_BETA3"), - ("DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS" , "DRS_EXT_W2K3_DEFLATE"), - ("DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10" , "DRS_EXT_GETCHGREQ_V10"), - ("DRSUAPI_SUPPORTED_EXTENSION_RESERVED_PART2" , "DRS_EXT_RESERVED_FOR_WIN2K_OR_DOTNET_PART2"), - ("DRSUAPI_SUPPORTED_EXTENSION_RESERVED_PART3" , "DRS_EXT_RESERVED_FOR_WIN2K_OR_DOTNET_PART3") - ] - - optmap_ext = [ - ("DRSUAPI_SUPPORTED_EXTENSION_ADAM", "DRS_EXT_ADAM"), - ("DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2", "DRS_EXT_LH_BETA2"), - ("DRSUAPI_SUPPORTED_EXTENSION_RECYCLE_BIN", "DRS_EXT_RECYCLE_BIN")] - - self.message("Bind to %s succeeded." % DC) - self.message("Extensions supported:") - for (opt, str) in optmap: - optval = getattr(drsuapi, opt, 0) - if info.info.supported_extensions & optval: - yesno = "Yes" - else: - yesno = "No " - self.message(" %-60s: %s (%s)" % (opt, yesno, str)) - - if isinstance(info.info, drsuapi.DsBindInfo48): - self.message("\nExtended Extensions supported:") - for (opt, str) in optmap_ext: - optval = getattr(drsuapi, opt, 0) - if info.info.supported_extensions_ext & optval: - yesno = "Yes" - else: - yesno = "No " - self.message(" %-60s: %s (%s)" % (opt, yesno, str)) - - self.message("\nSite GUID: %s" % info.info.site_guid) - self.message("Repl epoch: %u" % info.info.repl_epoch) - if isinstance(info.info, drsuapi.DsBindInfo48): - self.message("Forest GUID: %s" % info.info.config_dn_guid) - - - -class cmd_drs_options(Command): - """query or change 'options' for NTDS Settings object of a domain controller""" - - synopsis = ("%prog drs options <DC>" - " [--dsa-option={+|-}IS_GC | {+|-}DISABLE_INBOUND_REPL" - " |{+|-}DISABLE_OUTBOUND_REPL | {+|-}DISABLE_NTDSCONN_XLATE]") - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_args = ["DC"] - - takes_options = [ - Option("--dsa-option", help="DSA option to enable/disable", type="str"), - ] - - option_map = {"IS_GC": 0x00000001, - "DISABLE_INBOUND_REPL": 0x00000002, - "DISABLE_OUTBOUND_REPL": 0x00000004, - "DISABLE_NTDSCONN_XLATE": 0x00000008} - - def run(self, DC, dsa_option=None, - sambaopts=None, credopts=None, versionopts=None): - - self.lp = sambaopts.get_loadparm() - if DC is None: - DC = common.netcmd_dnsname(self.lp) - self.server = DC - self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - - samdb_connect(self) - - ntds_dn = get_dsServiceName(self.samdb) - res = self.samdb.search(base=ntds_dn, scope=ldb.SCOPE_BASE, attrs=["options"]) - dsa_opts = int(res[0]["options"][0]) - - # print out current DSA options - cur_opts = [x for x in self.option_map if self.option_map[x] & dsa_opts] - self.message("Current DSA options: " + ", ".join(cur_opts)) - - # modify options - if dsa_option: - if dsa_option[:1] not in ("+", "-"): - raise CommandError("Unknown option %s" % dsa_option) - flag = dsa_option[1:] - if flag not in self.option_map.keys(): - raise CommandError("Unknown option %s" % dsa_option) - if dsa_option[:1] == "+": - dsa_opts |= self.option_map[flag] - else: - dsa_opts &= ~self.option_map[flag] - #save new options - m = ldb.Message() - m.dn = ldb.Dn(self.samdb, ntds_dn) - m["options"]= ldb.MessageElement(str(dsa_opts), ldb.FLAG_MOD_REPLACE, "options") - self.samdb.modify(m) - # print out new DSA options - cur_opts = [x for x in self.option_map if self.option_map[x] & dsa_opts] - self.message("New DSA options: " + ", ".join(cur_opts)) - - -class cmd_drs(SuperCommand): - """DRS commands""" - - subcommands = {} - subcommands["bind"] = cmd_drs_bind() - subcommands["kcc"] = cmd_drs_kcc() - subcommands["replicate"] = cmd_drs_replicate() - subcommands["showrepl"] = cmd_drs_showrepl() - subcommands["options"] = cmd_drs_options() diff --git a/source4/scripting/python/samba/netcmd/dsacl.py b/source4/scripting/python/samba/netcmd/dsacl.py deleted file mode 100644 index 58a3552687..0000000000 --- a/source4/scripting/python/samba/netcmd/dsacl.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env python -# -# Manipulate ACLs on directory objects -# -# Copyright (C) Nadezhda Ivanova <nivanova@samba.org> 2010 -# -# 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/>. -# - -import samba.getopt as options -from samba.dcerpc import security -from samba.samdb import SamDB -from samba.ndr import ndr_unpack, ndr_pack -from samba.dcerpc.security import ( - GUID_DRS_ALLOCATE_RIDS, GUID_DRS_CHANGE_DOMAIN_MASTER, - GUID_DRS_CHANGE_INFR_MASTER, GUID_DRS_CHANGE_PDC, - GUID_DRS_CHANGE_RID_MASTER, GUID_DRS_CHANGE_SCHEMA_MASTER, - GUID_DRS_GET_CHANGES, GUID_DRS_GET_ALL_CHANGES, - GUID_DRS_GET_FILTERED_ATTRIBUTES, GUID_DRS_MANAGE_TOPOLOGY, - GUID_DRS_MONITOR_TOPOLOGY, GUID_DRS_REPL_SYNCRONIZE, - GUID_DRS_RO_REPL_SECRET_SYNC) - - -import ldb -from ldb import SCOPE_BASE -import re - -from samba.auth import system_session -from samba.netcmd import ( - Command, - CommandError, - SuperCommand, - Option, - ) - -class cmd_ds_acl_set(Command): - """Modify access list on a directory object""" - - synopsis = "set --objectdn=objectdn --car=control right --action=[deny|allow] --trusteedn=trustee-dn" - car_help = """ The access control right to allow or deny """ - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_options = [ - Option("--host", help="LDB URL for database or target server", - type=str), - Option("--car", type="choice", choices=["change-rid", - "change-pdc", - "change-infrastructure", - "change-schema", - "change-naming", - "allocate_rids", - "get-changes", - "get-changes-all", - "get-changes-filtered", - "topology-manage", - "topology-monitor", - "repl-sync", - "ro-repl-secret-sync"], - help=car_help), - Option("--action", type="choice", choices=["allow", "deny"], - help="""Deny or allow access"""), - Option("--objectdn", help="DN of the object whose SD to modify", - type="string"), - Option("--trusteedn", help="DN of the entity that gets access", - type="string"), - Option("--sddl", help="An ACE or group of ACEs to be added on the object", - type="string"), - ] - - def find_trustee_sid(self, samdb, trusteedn): - res = samdb.search(base=trusteedn, expression="(objectClass=*)", - scope=SCOPE_BASE) - assert(len(res) == 1) - return ndr_unpack( security.dom_sid,res[0]["objectSid"][0]) - - def modify_descriptor(self, samdb, object_dn, desc, controls=None): - assert(isinstance(desc, security.descriptor)) - m = ldb.Message() - m.dn = ldb.Dn(samdb, object_dn) - m["nTSecurityDescriptor"]= ldb.MessageElement( - (ndr_pack(desc)), ldb.FLAG_MOD_REPLACE, - "nTSecurityDescriptor") - samdb.modify(m) - - def read_descriptor(self, samdb, object_dn): - res = samdb.search(base=object_dn, scope=SCOPE_BASE, - attrs=["nTSecurityDescriptor"]) - # we should theoretically always have an SD - assert(len(res) == 1) - desc = res[0]["nTSecurityDescriptor"][0] - return ndr_unpack(security.descriptor, desc) - - def get_domain_sid(self, samdb): - res = samdb.search(base=samdb.domain_dn(), - expression="(objectClass=*)", scope=SCOPE_BASE) - return ndr_unpack( security.dom_sid,res[0]["objectSid"][0]) - - def add_ace(self, samdb, object_dn, new_ace): - """Add new ace explicitly.""" - desc = self.read_descriptor(samdb, object_dn) - desc_sddl = desc.as_sddl(self.get_domain_sid(samdb)) - #TODO add bindings for descriptor manipulation and get rid of this - desc_aces = re.findall("\(.*?\)", desc_sddl) - for ace in desc_aces: - if ("ID" in ace): - desc_sddl = desc_sddl.replace(ace, "") - if new_ace in desc_sddl: - return - if desc_sddl.find("(") >= 0: - desc_sddl = desc_sddl[:desc_sddl.index("(")] + new_ace + desc_sddl[desc_sddl.index("("):] - else: - desc_sddl = desc_sddl + new_ace - desc = security.descriptor.from_sddl(desc_sddl, self.get_domain_sid(samdb)) - self.modify_descriptor(samdb, object_dn, desc) - - def print_new_acl(self, samdb, object_dn): - desc = self.read_descriptor(samdb, object_dn) - desc_sddl = desc.as_sddl(self.get_domain_sid(samdb)) - print "new descriptor for %s:" % object_dn - print desc_sddl - - def run(self, car, action, objectdn, trusteedn, sddl, - host=None, credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - - if sddl is None and (car is None or action is None - or objectdn is None or trusteedn is None): - return self.usage() - - samdb = SamDB(url=host, session_info=system_session(), - credentials=creds, lp=lp) - cars = {'change-rid' : GUID_DRS_CHANGE_RID_MASTER, - 'change-pdc' : GUID_DRS_CHANGE_PDC, - 'change-infrastructure' : GUID_DRS_CHANGE_INFR_MASTER, - 'change-schema' : GUID_DRS_CHANGE_SCHEMA_MASTER, - 'change-naming' : GUID_DRS_CHANGE_DOMAIN_MASTER, - 'allocate_rids' : GUID_DRS_ALLOCATE_RIDS, - 'get-changes' : GUID_DRS_GET_CHANGES, - 'get-changes-all' : GUID_DRS_GET_ALL_CHANGES, - 'get-changes-filtered' : GUID_DRS_GET_FILTERED_ATTRIBUTES, - 'topology-manage' : GUID_DRS_MANAGE_TOPOLOGY, - 'topology-monitor' : GUID_DRS_MONITOR_TOPOLOGY, - 'repl-sync' : GUID_DRS_REPL_SYNCRONIZE, - 'ro-repl-secret-sync' : GUID_DRS_RO_REPL_SECRET_SYNC, - } - sid = self.find_trustee_sid(samdb, trusteedn) - if sddl: - new_ace = sddl - elif action == "allow": - new_ace = "(OA;;CR;%s;;%s)" % (cars[car], str(sid)) - elif action == "deny": - new_ace = "(OD;;CR;%s;;%s)" % (cars[car], str(sid)) - else: - raise CommandError("Wrong argument '%s'!" % action) - - self.print_new_acl(samdb, objectdn) - self.add_ace(samdb, objectdn, new_ace) - self.print_new_acl(samdb, objectdn) - - -class cmd_ds_acl(SuperCommand): - """DS ACLs manipulation""" - - subcommands = {} - subcommands["set"] = cmd_ds_acl_set() diff --git a/source4/scripting/python/samba/netcmd/enableaccount.py b/source4/scripting/python/samba/netcmd/enableaccount.py deleted file mode 100644 index 3ceddb3fd9..0000000000 --- a/source4/scripting/python/samba/netcmd/enableaccount.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python -# -# Enables an user account on a Samba4 server -# Copyright Jelmer Vernooij 2008 -# -# Based on the original in EJS: -# Copyright Andrew Tridgell 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/>. -# - -import samba.getopt as options - -from samba.auth import system_session -from samba.netcmd import Command, CommandError, Option -from samba.samdb import SamDB - -class cmd_enableaccount(Command): - """Enables a user""" - - synopsis = "enableaccount [username] [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - Option("--filter", help="LDAP Filter to set password on", type=str), - ] - - takes_args = ["username?"] - - def run(self, username=None, sambaopts=None, credopts=None, - versionopts=None, filter=None, H=None): - if username is None and filter is None: - raise CommandError("Either the username or '--filter' must be specified!") - - if filter is None: - filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username) - - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - samdb.enable_account(filter) diff --git a/source4/scripting/python/samba/netcmd/export.py b/source4/scripting/python/samba/netcmd/export.py deleted file mode 100644 index 649a2621b1..0000000000 --- a/source4/scripting/python/samba/netcmd/export.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -# -# Export keytab -# -# Copyright Jelmer Vernooij 2010 <jelmer@samba.org> -# -# 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/>. -# - -import samba.getopt as options - -from samba.net import Net - -from samba.netcmd import ( - Command, - SuperCommand, - ) - -class cmd_export_keytab(Command): - """Dumps kerberos keys of the domain into a keytab""" - synopsis = "%prog export keytab <keytab>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_options = [ - ] - - takes_args = ["keytab"] - - def run(self, keytab, credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - net = Net(None, lp, server=credopts.ipaddress) - net.export_keytab(keytab=keytab) - - -class cmd_export(SuperCommand): - """Dumps the sam of the domain we are joined to [server connection needed]""" - - subcommands = {} - subcommands["keytab"] = cmd_export_keytab() - diff --git a/source4/scripting/python/samba/netcmd/fsmo.py b/source4/scripting/python/samba/netcmd/fsmo.py deleted file mode 100644 index 0c8d17c74f..0000000000 --- a/source4/scripting/python/samba/netcmd/fsmo.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python -# -# Changes a FSMO role owner -# -# Copyright Nadezhda Ivanova 2009 -# Copyright Jelmer Vernooij 2009 -# -# 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/>. -# - -import samba.getopt as options -import ldb -from ldb import LdbError - -from samba.auth import system_session -from samba.netcmd import ( - Command, - CommandError, - Option, - ) -from samba.samdb import SamDB - -class cmd_fsmo(Command): - """Makes the targer DC transfer or seize a fsmo role [server connection needed]""" - - synopsis = "(show | transfer <options> | seize <options>)" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_options = [ - Option("--host", help="LDB URL for database or target server", type=str), - Option("--force", help="Force seizing of the role without attempting to transfer first.", action="store_true"), - Option("--role", type="choice", choices=["rid", "pdc", "infrastructure","schema","naming","all"], - help="""The FSMO role to seize or transfer.\n -rid=RidAllocationMasterRole\n -schema=SchemaMasterRole\n -pdc=PdcEmulationMasterRole\n -naming=DomainNamingMasterRole\n -infrastructure=InfrastructureMasterRole\n -all=all of the above"""), - ] - - takes_args = ["subcommand"] - - def transfer_role(self, role, samdb): - m = ldb.Message() - m.dn = ldb.Dn(samdb, "") - if role == "rid": - m["becomeRidMaster"]= ldb.MessageElement( - "1", ldb.FLAG_MOD_REPLACE, - "becomeRidMaster") - elif role == "pdc": - domain_dn = samdb.domain_dn() - res = samdb.search(domain_dn, - scope=ldb.SCOPE_BASE, attrs=["objectSid"]) - assert len(res) == 1 - sid = res[0]["objectSid"][0] - m["becomePdc"]= ldb.MessageElement( - sid, ldb.FLAG_MOD_REPLACE, - "becomePdc") - elif role == "naming": - m["becomeDomainMaster"]= ldb.MessageElement( - "1", ldb.FLAG_MOD_REPLACE, - "becomeDomainMaster") - samdb.modify(m) - elif role == "infrastructure": - m["becomeInfrastructureMaster"]= ldb.MessageElement( - "1", ldb.FLAG_MOD_REPLACE, - "becomeInfrastructureMaster") - elif role == "schema": - m["becomeSchemaMaster"]= ldb.MessageElement( - "1", ldb.FLAG_MOD_REPLACE, - "becomeSchemaMaster") - else: - raise CommandError("Invalid FSMO role.") - samdb.modify(m) - - def seize_role(self, role, samdb, force): - res = samdb.search("", - scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) - assert len(res) == 1 - serviceName = res[0]["dsServiceName"][0] - domain_dn = samdb.domain_dn() - m = ldb.Message() - if role == "rid": - m.dn = ldb.Dn(samdb, self.rid_dn) - elif role == "pdc": - m.dn = ldb.Dn(samdb, domain_dn) - elif role == "naming": - m.dn = ldb.Dn(samdb, self.naming_dn) - elif role == "infrastructure": - m.dn = ldb.Dn(samdb, self.infrastructure_dn) - elif role == "schema": - m.dn = ldb.Dn(samdb, self.schema_dn) - else: - raise CommandError("Invalid FSMO role.") - #first try to transfer to avoid problem if the owner is still active - if force is None: - self.message("Attempting transfer...") - try: - self.transfer_role(role, samdb) - except LdbError, (num, _): - #transfer failed, use the big axe... - self.message("Transfer unsuccessfull, seizing...") - m["fSMORoleOwner"]= ldb.MessageElement( - serviceName, ldb.FLAG_MOD_REPLACE, - "fSMORoleOwner") - samdb.modify(m) - else: - self.message("Transfer succeeded.") - else: - self.message("Will not attempt transfer, seizing...") - m["fSMORoleOwner"]= ldb.MessageElement( - serviceName, ldb.FLAG_MOD_REPLACE, - "fSMORoleOwner") - samdb.modify(m) - - def run(self, subcommand, force=None, host=None, role=None, - credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - - samdb = SamDB(url=host, session_info=system_session(), - credentials=creds, lp=lp) - - domain_dn = samdb.domain_dn() - self.infrastructure_dn = "CN=Infrastructure," + domain_dn - self.naming_dn = "CN=Partitions,CN=Configuration," + domain_dn - self.schema_dn = "CN=Schema,CN=Configuration," + domain_dn - self.rid_dn = "CN=RID Manager$,CN=System," + domain_dn - - res = samdb.search(self.infrastructure_dn, - scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"]) - assert len(res) == 1 - self.infrastructureMaster = res[0]["fSMORoleOwner"][0] - - res = samdb.search(domain_dn, - scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"]) - assert len(res) == 1 - self.pdcEmulator = res[0]["fSMORoleOwner"][0] - - res = samdb.search(self.naming_dn, - scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"]) - assert len(res) == 1 - self.namingMaster = res[0]["fSMORoleOwner"][0] - - res = samdb.search(self.schema_dn, - scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"]) - assert len(res) == 1 - self.schemaMaster = res[0]["fSMORoleOwner"][0] - - res = samdb.search(self.rid_dn, - scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"]) - assert len(res) == 1 - self.ridMaster = res[0]["fSMORoleOwner"][0] - - if subcommand == "show": - self.message("InfrastructureMasterRole owner: " + self.infrastructureMaster) - self.message("RidAllocationMasterRole owner: " + self.ridMaster) - self.message("PdcEmulationMasterRole owner: " + self.pdcEmulator) - self.message("DomainNamingMasterRole owner: " + self.namingMaster) - self.message("SchemaMasterRole owner: " + self.schemaMaster) - elif subcommand == "transfer": - if role == "all": - self.transfer_role("rid", samdb) - self.transfer_role("pdc", samdb) - self.transfer_role("naming", samdb) - self.transfer_role("infrastructure", samdb) - self.transfer_role("schema", samdb) - else: - self.transfer_role(role, samdb) - elif subcommand == "seize": - if role == "all": - self.seize_role("rid", samdb, force) - self.seize_role("pdc", samdb, force) - self.seize_role("naming", samdb, force) - self.seize_role("infrastructure", samdb, force) - self.seize_role("schema", samdb, force) - else: - self.seize_role(role, samdb, force) - else: - raise CommandError("Wrong argument '%s'!" % subcommand) diff --git a/source4/scripting/python/samba/netcmd/gpo.py b/source4/scripting/python/samba/netcmd/gpo.py deleted file mode 100644 index 19007b361c..0000000000 --- a/source4/scripting/python/samba/netcmd/gpo.py +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env python -# -# implement samba_tool gpo commands -# -# Copyright Andrew Tridgell 2010 -# -# based on C implementation by Guenther Deschner and Wilco Baan Hofman -# -# 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/>. -# - -import samba.getopt as options -import ldb - -from samba.auth import system_session -from samba.netcmd import ( - Command, - CommandError, - Option, - SuperCommand, - ) -from samba.samdb import SamDB -from samba import drs_utils, nttime2string, dsdb, dcerpc -from samba.dcerpc import misc -from samba.ndr import ndr_unpack -import samba.security -import samba.auth -from samba.auth import AUTH_SESSION_INFO_DEFAULT_GROUPS, AUTH_SESSION_INFO_AUTHENTICATED, AUTH_SESSION_INFO_SIMPLE_PRIVILEGES - -def samdb_connect(ctx): - '''make a ldap connection to the server''' - try: - ctx.samdb = SamDB(url=ctx.url, - session_info=system_session(), - credentials=ctx.creds, lp=ctx.lp) - except Exception, e: - raise CommandError("LDAP connection to %s failed " % ctx.url, e) - - -def attr_default(msg, attrname, default): - '''get an attribute from a ldap msg with a default''' - if attrname in msg: - return msg[attrname][0] - return default - - -def flags_string(flags, value): - '''return a set of flags as a string''' - if value == 0: - return 'NONE' - ret = '' - for (str, val) in flags: - if val & value: - ret += str + ' ' - value &= ~val - if value != 0: - ret += '0x%08x' % value - return ret.rstrip() - - -def parse_gplink(gplink): - '''parse a gPLink into an array of dn and options''' - ret = [] - a = gplink.split(']') - for g in a: - if not g: - continue - d = g.split(';') - if len(d) != 2 or not d[0].startswith("[LDAP://"): - raise RuntimeError("Badly formed gPLink '%s'" % g) - ret.append({ 'dn' : d[0][8:], 'options' : int(d[1])}) - return ret - - -class cmd_listall(Command): - """list all GPOs""" - - synopsis = "%prog gpo listall" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str) - ] - - def run(self, H=None, sambaopts=None, - credopts=None, versionopts=None, server=None): - - self.url = H - self.lp = sambaopts.get_loadparm() - - self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - - samdb_connect(self) - - policies_dn = self.samdb.get_default_basedn() - policies_dn.add_child(ldb.Dn(self.samdb, "CN=Policies,CN=System")) - - gpo_flags = [ - ("GPO_FLAG_USER_DISABLE", dsdb.GPO_FLAG_USER_DISABLE ), - ( "GPO_FLAG_MACHINE_DISABLE", dsdb.GPO_FLAG_MACHINE_DISABLE ) ] - - try: - msg = self.samdb.search(base=policies_dn, scope=ldb.SCOPE_ONELEVEL, - expression="(objectClass=groupPolicyContainer)", - attrs=['nTSecurityDescriptor', 'versionNumber', 'flags', 'name', 'displayName', 'gPCFileSysPath']) - except Exception, e: - raise CommandError("Failed to list policies in %s" % policies_dn, e) - for m in msg: - print("GPO : %s" % m['name'][0]) - print("display name : %s" % m['displayName'][0]) - print("path : %s" % m['gPCFileSysPath'][0]) - print("dn : %s" % m.dn) - print("version : %s" % attr_default(m, 'version', '0')) - print("flags : %s" % flags_string(gpo_flags, int(attr_default(m, 'flags', 0)))) - print("") - - -class cmd_list(Command): - """list GPOs for a user""" - - synopsis = "%prog gpo list <username>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_args = [ 'username' ] - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str) - ] - - def run(self, username, H=None, sambaopts=None, - credopts=None, versionopts=None, server=None): - - self.url = H - self.lp = sambaopts.get_loadparm() - - self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - - samdb_connect(self) - - try: - user_dn = self.samdb.search(expression='(&(samAccountName=%s)(objectclass=User))' % username)[0].dn - except Exception, e: - raise CommandError("Failed to find user %s" % username, e) - - # check if its a computer account - try: - msg = self.samdb.search(base=user_dn, scope=ldb.SCOPE_BASE, attrs=['objectClass'])[0] - is_computer = 'computer' in msg['objectClass'] - except Exception, e: - raise CommandError("Failed to find objectClass for user %s" % username, e) - - session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS | - AUTH_SESSION_INFO_AUTHENTICATED ) - - # When connecting to a remote server, don't look up the local privilege DB - if self.url is not None and self.url.startswith('ldap'): - session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES - - session = samba.auth.user_session(self.samdb, lp_ctx=self.lp, dn=user_dn, - session_info_flags=session_info_flags) - - token = session.security_token - - gpos = [] - - inherit = True - dn = ldb.Dn(self.samdb, str(user_dn)).parent() - while True: - msg = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['gPLink', 'gPOptions'])[0] - if 'gPLink' in msg: - glist = parse_gplink(msg['gPLink'][0]) - for g in glist: - if not inherit and not (g['options'] & dsdb.GPLINK_OPT_ENFORCE): - continue - if g['options'] & dsdb.GPLINK_OPT_DISABLE: - continue - - try: - gmsg = self.samdb.search(base=g['dn'], scope=ldb.SCOPE_BASE, - attrs=['flags', 'ntSecurityDescriptor']) - except Exception: - print "Failed to fetch gpo object %s" % g['dn'] - continue - - secdesc_ndr = gmsg[0]['ntSecurityDescriptor'][0] - secdesc = ndr_unpack(dcerpc.security.descriptor, secdesc_ndr) - - try: - samba.security.access_check(secdesc, token, - dcerpc.security.SEC_STD_READ_CONTROL | - dcerpc.security.SEC_ADS_LIST | - dcerpc.security.SEC_ADS_READ_PROP) - except RuntimeError: - print "Failed access check on %s" % msg.dn - continue - - # check the flags on the GPO - flags = int(attr_default(gmsg[0], 'flags', 0)) - if is_computer and (flags & dsdb.GPO_FLAG_MACHINE_DISABLE): - continue - if not is_computer and (flags & dsdb.GPO_FLAG_USER_DISABLE): - continue - gpos.append(g) - - # check if this blocks inheritance - gpoptions = int(attr_default(msg, 'gPOptions', 0)) - if gpoptions & dsdb.GPO_BLOCK_INHERITANCE: - inherit = False - - if dn == self.samdb.get_default_basedn(): - break - dn = dn.parent() - - print("GPO's for user %s" % username) - for g in gpos: - print("\t%s" % g['dn']) - - -class cmd_gpo(SuperCommand): - """GPO commands""" - - subcommands = {} - subcommands["listall"] = cmd_listall() - subcommands["list"] = cmd_list() diff --git a/source4/scripting/python/samba/netcmd/group.py b/source4/scripting/python/samba/netcmd/group.py deleted file mode 100644 index 620a7be866..0000000000 --- a/source4/scripting/python/samba/netcmd/group.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env python -# -# Adds a new user to a Samba4 server -# Copyright Jelmer Vernooij 2008 -# -# Based on the original in EJS: -# Copyright Andrew Tridgell 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/>. - -import samba.getopt as options -from samba.netcmd import Command, SuperCommand, CommandError, Option -import ldb - -from getpass import getpass -from samba.auth import system_session -from samba.samdb import SamDB -from samba.dsdb import ( - GTYPE_SECURITY_DOMAIN_LOCAL_GROUP, - GTYPE_SECURITY_GLOBAL_GROUP, - GTYPE_SECURITY_UNIVERSAL_GROUP, - GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP, - GTYPE_DISTRIBUTION_GLOBAL_GROUP, - GTYPE_DISTRIBUTION_UNIVERSAL_GROUP, -) - -security_group = dict({"Domain": GTYPE_SECURITY_DOMAIN_LOCAL_GROUP, "Global": GTYPE_SECURITY_GLOBAL_GROUP, "Universal": GTYPE_SECURITY_UNIVERSAL_GROUP}) -distribution_group = dict({"Domain": GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP, "Global": GTYPE_DISTRIBUTION_GLOBAL_GROUP, "Universal": GTYPE_DISTRIBUTION_UNIVERSAL_GROUP}) - - -class cmd_group_add(Command): - """Creates a new group""" - - synopsis = "%prog group add [options] <groupname>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - Option("--groupou", - help="Alternative location (without domainDN counterpart) to default CN=Users in which new user object will be created", - type=str), - Option("--group-scope", type="choice", choices=["Domain", "Global", "Universal"], - help="Group scope (Domain | Global | Universal)"), - Option("--group-type", type="choice", choices=["Security", "Distribution"], - help="Group type (Security | Distribution)"), - Option("--description", help="Group's description", type=str), - Option("--mail-address", help="Group's email address", type=str), - Option("--notes", help="Groups's notes", type=str), - ] - - takes_args = ["groupname"] - - def run(self, groupname, credopts=None, sambaopts=None, - versionopts=None, H=None, groupou=None, group_scope=None, - group_type=None, description=None, mail_address=None, notes=None): - - if (group_type or "Security") == "Security": - gtype = security_group.get(group_scope, GTYPE_SECURITY_GLOBAL_GROUP) - else: - gtype = distribution_group.get(group_scope, GTYPE_DISTRIBUTION_GLOBAL_GROUP) - - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - - try: - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - samdb.newgroup(groupname, groupou=groupou, grouptype = gtype, - description=description, mailaddress=mail_address, notes=notes) - except Exception, e: - raise CommandError('Failed to create group "%s"' % groupname, e) - - -class cmd_group_delete(Command): - """Delete a group""" - - synopsis = "%prog group delete <groupname>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - ] - - takes_args = ["groupname"] - - def run(self, groupname, credopts=None, sambaopts=None, versionopts=None, H=None): - - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - - try: - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - samdb.deletegroup(groupname) - except Exception, e: - raise CommandError('Failed to remove group "%s"' % groupname, e) - - -class cmd_group_add_members(Command): - """Add (comma-separated list of) group members""" - - synopsis = "%prog group addmembers <groupname> <listofmembers>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - ] - - takes_args = ["groupname", "listofmembers"] - - def run(self, groupname, listofmembers, credopts=None, sambaopts=None, - versionopts=None, H=None): - - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - - try: - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - samdb.add_remove_group_members(groupname, listofmembers, add_members_operation=True) - except Exception, e: - raise CommandError('Failed to add members "%s" to group "%s"' % (listofmembers, groupname), e) - - -class cmd_group_remove_members(Command): - """Remove (comma-separated list of) group members""" - - synopsis = "%prog group removemembers <groupname> <listofmembers>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - ] - - takes_args = ["groupname", "listofmembers"] - - def run(self, groupname, listofmembers, credopts=None, sambaopts=None, - versionopts=None, H=None): - - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - - try: - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - samdb.add_remove_group_members(groupname, listofmembers, add_members_operation=False) - except Exception, e: - raise CommandError('Failed to remove members "%s" from group "%s"' % (listofmembers, groupname), e) - - -class cmd_group(SuperCommand): - """Group management""" - - subcommands = {} - subcommands["add"] = cmd_group_add() - subcommands["delete"] = cmd_group_delete() - subcommands["addmembers"] = cmd_group_add_members() - subcommands["removemembers"] = cmd_group_remove_members() diff --git a/source4/scripting/python/samba/netcmd/join.py b/source4/scripting/python/samba/netcmd/join.py deleted file mode 100644 index 507253ab81..0000000000 --- a/source4/scripting/python/samba/netcmd/join.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python -# -# joins -# -# Copyright Jelmer Vernooij 2010 -# -# 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/>. -# - -import samba.getopt as options - -from samba.net import Net, LIBNET_JOIN_AUTOMATIC -from samba.netcmd import Command, CommandError, Option -from samba.dcerpc.misc import SEC_CHAN_WKSTA, SEC_CHAN_BDC -from samba.join import join_RODC, join_DC - -class cmd_join(Command): - """Joins domain as either member or backup domain controller [server connection needed]""" - - synopsis = "%prog join <dnsdomain> [DC | RODC | MEMBER] [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("--server", help="DC to join", type=str), - Option("--site", help="site to join", type=str), - ] - - takes_args = ["domain", "role?"] - - def run(self, domain, role=None, sambaopts=None, credopts=None, - versionopts=None, server=None, site=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - net = Net(creds, lp, server=credopts.ipaddress) - - if site is None: - site = "Default-First-Site-Name" - - netbios_name = lp.get("netbios name") - - if not role is None: - role = role.upper() - - if role is None or role == "MEMBER": - secure_channel_type = SEC_CHAN_WKSTA - elif role == "DC": - join_DC(server=server, creds=creds, lp=lp, domain=domain, - site=site, netbios_name=netbios_name) - return - elif role == "RODC": - join_RODC(server=server, creds=creds, lp=lp, domain=domain, - site=site, netbios_name=netbios_name) - return - else: - raise CommandError("Invalid role %s (possible values: MEMBER, BDC, RODC)" % role) - - (join_password, sid, domain_name) = net.join(domain, - netbios_name, - secure_channel_type, - LIBNET_JOIN_AUTOMATIC) - - self.outf.write("Joined domain %s (%s)\n" % (domain_name, sid)) diff --git a/source4/scripting/python/samba/netcmd/ldapcmp.py b/source4/scripting/python/samba/netcmd/ldapcmp.py deleted file mode 100755 index 160aa31258..0000000000 --- a/source4/scripting/python/samba/netcmd/ldapcmp.py +++ /dev/null @@ -1,949 +0,0 @@ -#!/usr/bin/env python -# -# Unix SMB/CIFS implementation. -# A command to compare differences of objects and attributes between -# two LDAP servers both running at the same time. It generally compares -# one of the three pratitions DOMAIN, CONFIGURATION or SCHEMA. Users -# that have to be provided sheould be able to read objects in any of the -# above partitions. - -# Copyright (C) Zahari Zahariev <zahari.zahariev@postpath.com> 2009, 2010 -# -# 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/>. -# - -import os -import re -import sys - -import samba -import samba.getopt as options -from samba import Ldb -from samba.ndr import ndr_pack, ndr_unpack -from samba.dcerpc import security -from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, ERR_NO_SUCH_OBJECT, LdbError -from samba.netcmd import ( - Command, - CommandError, - Option, - SuperCommand, - ) - -global summary -summary = {} - -class LDAPBase(object): - - def __init__(self, host, creds, lp, - two=False, quiet=False, descriptor=False, sort_aces=False, verbose=False, - view="section", base="", scope="SUB"): - ldb_options = [] - samdb_url = host - if not "://" in host: - if os.path.isfile(host): - samdb_url = "tdb://%s" % host - else: - samdb_url = "ldap://%s" % host - # use 'paged_search' module when connecting remotely - if samdb_url.lower().startswith("ldap://"): - ldb_options = ["modules:paged_searches"] - self.ldb = Ldb(url=samdb_url, - credentials=creds, - lp=lp, - options=ldb_options) - self.search_base = base - self.search_scope = scope - self.two_domains = two - self.quiet = quiet - self.descriptor = descriptor - self.sort_aces = sort_aces - self.view = view - self.verbose = verbose - self.host = host - self.base_dn = self.find_basedn() - self.domain_netbios = self.find_netbios() - self.server_names = self.find_servers() - self.domain_name = re.sub("[Dd][Cc]=", "", self.base_dn).replace(",", ".") - self.domain_sid = self.find_domain_sid() - self.get_guid_map() - self.get_sid_map() - # - # Log some domain controller specific place-holers that are being used - # when compare content of two DCs. Uncomment for DEBUG purposes. - if self.two_domains and not self.quiet: - print "\n* Place-holders for %s:" % self.host - print 4*" " + "${DOMAIN_DN} => %s" % self.base_dn - print 4*" " + "${DOMAIN_NETBIOS} => %s" % self.domain_netbios - print 4*" " + "${SERVER_NAME} => %s" % self.server_names - print 4*" " + "${DOMAIN_NAME} => %s" % self.domain_name - - def find_domain_sid(self): - res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE) - return ndr_unpack(security.dom_sid,res[0]["objectSid"][0]) - - def find_servers(self): - """ - """ - res = self.ldb.search(base="OU=Domain Controllers,%s" % self.base_dn, \ - scope=SCOPE_SUBTREE, expression="(objectClass=computer)", attrs=["cn"]) - assert len(res) > 0 - srv = [] - for x in res: - srv.append(x["cn"][0]) - return srv - - def find_netbios(self): - res = self.ldb.search(base="CN=Partitions,CN=Configuration,%s" % self.base_dn, \ - scope=SCOPE_SUBTREE, attrs=["nETBIOSName"]) - assert len(res) > 0 - for x in res: - if "nETBIOSName" in x.keys(): - return x["nETBIOSName"][0] - - def find_basedn(self): - res = self.ldb.search(base="", expression="(objectClass=*)", scope=SCOPE_BASE, - attrs=["defaultNamingContext"]) - assert len(res) == 1 - return res[0]["defaultNamingContext"][0] - - def object_exists(self, object_dn): - res = None - try: - res = self.ldb.search(base=object_dn, scope=SCOPE_BASE) - except LdbError, (enum, estr): - if enum == ERR_NO_SUCH_OBJECT: - return False - raise - return len(res) == 1 - - def delete_force(self, object_dn): - try: - self.ldb.delete(object_dn) - except Ldb.LdbError, e: - assert "No such object" in str(e) - - def get_attribute_name(self, key): - """ Returns the real attribute name - It resolved ranged results e.g. member;range=0-1499 - """ - - r = re.compile("^([^;]+);range=(\d+)-(\d+|\*)$") - - m = r.match(key) - if m is None: - return key - - return m.group(1) - - def get_attribute_values(self, object_dn, key, vals): - """ Returns list with all attribute values - It resolved ranged results e.g. member;range=0-1499 - """ - - r = re.compile("^([^;]+);range=(\d+)-(\d+|\*)$") - - m = r.match(key) - if m is None: - # no range, just return the values - return vals - - attr = m.group(1) - hi = int(m.group(3)) - - # get additional values in a loop - # until we get a response with '*' at the end - while True: - - n = "%s;range=%d-*" % (attr, hi + 1) - res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=[n]) - assert len(res) == 1 - res = dict(res[0]) - del res["dn"] - - fm = None - fvals = None - - for key in res.keys(): - m = r.match(key) - - if m is None: - continue - - if m.group(1) != attr: - continue - - fm = m - fvals = list(res[key]) - break - - if fm is None: - break - - vals.extend(fvals) - if fm.group(3) == "*": - # if we got "*" we're done - break - - assert int(fm.group(2)) == hi + 1 - hi = int(fm.group(3)) - - return vals - - def get_attributes(self, object_dn): - """ Returns dict with all default visible attributes - """ - res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=["*"]) - assert len(res) == 1 - res = dict(res[0]) - # 'Dn' element is not iterable and we have it as 'distinguishedName' - del res["dn"] - for key in res.keys(): - vals = list(res[key]) - del res[key] - name = self.get_attribute_name(key) - res[name] = self.get_attribute_values(object_dn, key, vals) - - return res - - def get_descriptor_sddl(self, object_dn): - res = self.ldb.search(base=object_dn, scope=SCOPE_BASE, attrs=["nTSecurityDescriptor"]) - desc = res[0]["nTSecurityDescriptor"][0] - desc = ndr_unpack(security.descriptor, desc) - return desc.as_sddl(self.domain_sid) - - def guid_as_string(self, guid_blob): - """ Translate binary representation of schemaIDGUID to standard string representation. - @gid_blob: binary schemaIDGUID - """ - blob = "%s" % guid_blob - stops = [4, 2, 2, 2, 6] - index = 0 - res = "" - x = 0 - while x < len(stops): - tmp = "" - y = 0 - while y < stops[x]: - c = hex(ord(blob[index])).replace("0x", "") - c = [None, "0" + c, c][len(c)] - if 2 * index < len(blob): - tmp = c + tmp - else: - tmp += c - index += 1 - y += 1 - res += tmp + " " - x += 1 - assert index == len(blob) - return res.strip().replace(" ", "-") - - def get_guid_map(self): - """ Build dictionary that maps GUID to 'name' attribute found in Schema or Extended-Rights. - """ - self.guid_map = {} - res = self.ldb.search(base="cn=schema,cn=configuration,%s" % self.base_dn, \ - expression="(schemaIdGuid=*)", scope=SCOPE_SUBTREE, attrs=["schemaIdGuid", "name"]) - for item in res: - self.guid_map[self.guid_as_string(item["schemaIdGuid"]).lower()] = item["name"][0] - # - res = self.ldb.search(base="cn=extended-rights,cn=configuration,%s" % self.base_dn, \ - expression="(rightsGuid=*)", scope=SCOPE_SUBTREE, attrs=["rightsGuid", "name"]) - for item in res: - self.guid_map[str(item["rightsGuid"]).lower()] = item["name"][0] - - def get_sid_map(self): - """ Build dictionary that maps GUID to 'name' attribute found in Schema or Extended-Rights. - """ - self.sid_map = {} - res = self.ldb.search(base="%s" % self.base_dn, \ - expression="(objectSid=*)", scope=SCOPE_SUBTREE, attrs=["objectSid", "sAMAccountName"]) - for item in res: - try: - self.sid_map["%s" % ndr_unpack(security.dom_sid, item["objectSid"][0])] = item["sAMAccountName"][0] - except KeyError: - pass - -class Descriptor(object): - def __init__(self, connection, dn): - self.con = connection - self.dn = dn - self.sddl = self.con.get_descriptor_sddl(self.dn) - self.dacl_list = self.extract_dacl() - if self.con.sort_aces: - self.dacl_list.sort() - - def extract_dacl(self): - """ Extracts the DACL as a list of ACE string (with the brakets). - """ - try: - if "S:" in self.sddl: - res = re.search("D:(.*?)(\(.*?\))S:", self.sddl).group(2) - else: - res = re.search("D:(.*?)(\(.*\))", self.sddl).group(2) - except AttributeError: - return [] - return re.findall("(\(.*?\))", res) - - def fix_guid(self, ace): - res = "%s" % ace - guids = re.findall("[a-z0-9]+?-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+", res) - # If there are not GUIDs to replace return the same ACE - if len(guids) == 0: - return res - for guid in guids: - try: - name = self.con.guid_map[guid.lower()] - res = res.replace(guid, name) - except KeyError: - # Do not bother if the GUID is not found in - # cn=Schema or cn=Extended-Rights - pass - return res - - def fix_sid(self, ace): - res = "%s" % ace - sids = re.findall("S-[-0-9]+", res) - # If there are not SIDs to replace return the same ACE - if len(sids) == 0: - return res - for sid in sids: - try: - name = self.con.sid_map[sid] - res = res.replace(sid, name) - except KeyError: - # Do not bother if the SID is not found in baseDN - pass - return res - - def fixit(self, ace): - """ Combine all replacement methods in one - """ - res = "%s" % ace - res = self.fix_guid(res) - res = self.fix_sid(res) - return res - - def diff_1(self, other): - res = "" - if len(self.dacl_list) != len(other.dacl_list): - res += 4*" " + "Difference in ACE count:\n" - res += 8*" " + "=> %s\n" % len(self.dacl_list) - res += 8*" " + "=> %s\n" % len(other.dacl_list) - # - i = 0 - flag = True - while True: - self_ace = None - other_ace = None - try: - self_ace = "%s" % self.dacl_list[i] - except IndexError: - self_ace = "" - # - try: - other_ace = "%s" % other.dacl_list[i] - except IndexError: - other_ace = "" - if len(self_ace) + len(other_ace) == 0: - break - self_ace_fixed = "%s" % self.fixit(self_ace) - other_ace_fixed = "%s" % other.fixit(other_ace) - if self_ace_fixed != other_ace_fixed: - res += "%60s * %s\n" % ( self_ace_fixed, other_ace_fixed ) - flag = False - else: - res += "%60s | %s\n" % ( self_ace_fixed, other_ace_fixed ) - i += 1 - return (flag, res) - - def diff_2(self, other): - res = "" - if len(self.dacl_list) != len(other.dacl_list): - res += 4*" " + "Difference in ACE count:\n" - res += 8*" " + "=> %s\n" % len(self.dacl_list) - res += 8*" " + "=> %s\n" % len(other.dacl_list) - # - common_aces = [] - self_aces = [] - other_aces = [] - self_dacl_list_fixed = [] - other_dacl_list_fixed = [] - [self_dacl_list_fixed.append( self.fixit(ace) ) for ace in self.dacl_list] - [other_dacl_list_fixed.append( other.fixit(ace) ) for ace in other.dacl_list] - for ace in self_dacl_list_fixed: - try: - other_dacl_list_fixed.index(ace) - except ValueError: - self_aces.append(ace) - else: - common_aces.append(ace) - self_aces = sorted(self_aces) - if len(self_aces) > 0: - res += 4*" " + "ACEs found only in %s:\n" % self.con.host - for ace in self_aces: - res += 8*" " + ace + "\n" - # - for ace in other_dacl_list_fixed: - try: - self_dacl_list_fixed.index(ace) - except ValueError: - other_aces.append(ace) - else: - common_aces.append(ace) - other_aces = sorted(other_aces) - if len(other_aces) > 0: - res += 4*" " + "ACEs found only in %s:\n" % other.con.host - for ace in other_aces: - res += 8*" " + ace + "\n" - # - common_aces = sorted(list(set(common_aces))) - if self.con.verbose: - res += 4*" " + "ACEs found in both:\n" - for ace in common_aces: - res += 8*" " + ace + "\n" - return (self_aces == [] and other_aces == [], res) - -class LDAPObject(object): - def __init__(self, connection, dn, summary): - self.con = connection - self.two_domains = self.con.two_domains - self.quiet = self.con.quiet - self.verbose = self.con.verbose - self.summary = summary - self.dn = dn.replace("${DOMAIN_DN}", self.con.base_dn) - self.dn = self.dn.replace("CN=${DOMAIN_NETBIOS}", "CN=%s" % self.con.domain_netbios) - for x in self.con.server_names: - self.dn = self.dn.replace("CN=${SERVER_NAME}", "CN=%s" % x) - self.attributes = self.con.get_attributes(self.dn) - # Attributes that are considered always to be different e.g based on timestamp etc. - # - # One domain - two domain controllers - self.ignore_attributes = [ - # Default Naming Context - "lastLogon", "lastLogoff", "badPwdCount", "logonCount", "badPasswordTime", "modifiedCount", - "operatingSystemVersion","oEMInformation", - # Configuration Naming Context - "repsFrom", "dSCorePropagationData", "msExchServer1HighestUSN", - "replUpToDateVector", "repsTo", "whenChanged", "uSNChanged", "uSNCreated", - # Schema Naming Context - "prefixMap",] - self.dn_attributes = [] - self.domain_attributes = [] - self.servername_attributes = [] - self.netbios_attributes = [] - self.other_attributes = [] - # Two domains - two domain controllers - - if self.two_domains: - self.ignore_attributes += [ - "objectCategory", "objectGUID", "objectSid", "whenCreated", "pwdLastSet", "uSNCreated", "creationTime", - "modifiedCount", "priorSetTime", "rIDManagerReference", "gPLink", "ipsecNFAReference", - "fRSPrimaryMember", "fSMORoleOwner", "masteredBy", "ipsecOwnersReference", "wellKnownObjects", - "badPwdCount", "ipsecISAKMPReference", "ipsecFilterReference", "msDs-masteredBy", "lastSetTime", - "ipsecNegotiationPolicyReference", "subRefs", "gPCFileSysPath", "accountExpires", "invocationId", - # After Exchange preps - "targetAddress", "msExchMailboxGuid", "siteFolderGUID"] - # - # Attributes that contain the unique DN tail part e.g. 'DC=samba,DC=org' - self.dn_attributes = [ - "distinguishedName", "defaultObjectCategory", "member", "memberOf", "siteList", "nCName", - "homeMDB", "homeMTA", "interSiteTopologyGenerator", "serverReference", - "msDS-HasInstantiatedNCs", "hasMasterNCs", "msDS-hasMasterNCs", "msDS-HasDomainNCs", "dMDLocation", - "msDS-IsDomainFor", "rIDSetReferences", "serverReferenceBL", - # After Exchange preps - "msExchHomeRoutingGroup", "msExchResponsibleMTAServer", "siteFolderServer", "msExchRoutingMasterDN", - "msExchRoutingGroupMembersBL", "homeMDBBL", "msExchHomePublicMDB", "msExchOwningServer", "templateRoots", - "addressBookRoots", "msExchPolicyRoots", "globalAddressList", "msExchOwningPFTree", - "msExchResponsibleMTAServerBL", "msExchOwningPFTreeBL",] - self.dn_attributes = [x.upper() for x in self.dn_attributes] - # - # Attributes that contain the Domain name e.g. 'samba.org' - self.domain_attributes = [ - "proxyAddresses", "mail", "userPrincipalName", "msExchSmtpFullyQualifiedDomainName", - "dnsHostName", "networkAddress", "dnsRoot", "servicePrincipalName",] - self.domain_attributes = [x.upper() for x in self.domain_attributes] - # - # May contain DOMAIN_NETBIOS and SERVER_NAME - self.servername_attributes = [ "distinguishedName", "name", "CN", "sAMAccountName", "dNSHostName", - "servicePrincipalName", "rIDSetReferences", "serverReference", "serverReferenceBL", - "msDS-IsDomainFor", "interSiteTopologyGenerator",] - self.servername_attributes = [x.upper() for x in self.servername_attributes] - # - self.netbios_attributes = [ "servicePrincipalName", "CN", "distinguishedName", "nETBIOSName", "name",] - self.netbios_attributes = [x.upper() for x in self.netbios_attributes] - # - self.other_attributes = [ "name", "DC",] - self.other_attributes = [x.upper() for x in self.other_attributes] - # - self.ignore_attributes = [x.upper() for x in self.ignore_attributes] - - def log(self, msg): - """ - Log on the screen if there is no --quiet oprion set - """ - if not self.quiet: - print msg - - def fix_dn(self, s): - res = "%s" % s - if not self.two_domains: - return res - if res.upper().endswith(self.con.base_dn.upper()): - res = res[:len(res)-len(self.con.base_dn)] + "${DOMAIN_DN}" - return res - - def fix_domain_name(self, s): - res = "%s" % s - if not self.two_domains: - return res - res = res.replace(self.con.domain_name.lower(), self.con.domain_name.upper()) - res = res.replace(self.con.domain_name.upper(), "${DOMAIN_NAME}") - return res - - def fix_domain_netbios(self, s): - res = "%s" % s - if not self.two_domains: - return res - res = res.replace(self.con.domain_netbios.lower(), self.con.domain_netbios.upper()) - res = res.replace(self.con.domain_netbios.upper(), "${DOMAIN_NETBIOS}") - return res - - def fix_server_name(self, s): - res = "%s" % s - if not self.two_domains or len(self.con.server_names) > 1: - return res - for x in self.con.server_names: - res = res.upper().replace(x, "${SERVER_NAME}") - return res - - def __eq__(self, other): - if self.con.descriptor: - return self.cmp_desc(other) - return self.cmp_attrs(other) - - def cmp_desc(self, other): - d1 = Descriptor(self.con, self.dn) - d2 = Descriptor(other.con, other.dn) - if self.con.view == "section": - res = d1.diff_2(d2) - elif self.con.view == "collision": - res = d1.diff_1(d2) - else: - raise Exception("Unknown --view option value.") - # - self.screen_output = res[1][:-1] - other.screen_output = res[1][:-1] - # - return res[0] - - def cmp_attrs(self, other): - res = "" - self.unique_attrs = [] - self.df_value_attrs = [] - other.unique_attrs = [] - if self.attributes.keys() != other.attributes.keys(): - # - title = 4*" " + "Attributes found only in %s:" % self.con.host - for x in self.attributes.keys(): - if not x in other.attributes.keys() and \ - not x.upper() in [q.upper() for q in other.ignore_attributes]: - if title: - res += title + "\n" - title = None - res += 8*" " + x + "\n" - self.unique_attrs.append(x) - # - title = 4*" " + "Attributes found only in %s:" % other.con.host - for x in other.attributes.keys(): - if not x in self.attributes.keys() and \ - not x.upper() in [q.upper() for q in self.ignore_attributes]: - if title: - res += title + "\n" - title = None - res += 8*" " + x + "\n" - other.unique_attrs.append(x) - # - missing_attrs = [x.upper() for x in self.unique_attrs] - missing_attrs += [x.upper() for x in other.unique_attrs] - title = 4*" " + "Difference in attribute values:" - for x in self.attributes.keys(): - if x.upper() in self.ignore_attributes or x.upper() in missing_attrs: - continue - if isinstance(self.attributes[x], list) and isinstance(other.attributes[x], list): - self.attributes[x] = sorted(self.attributes[x]) - other.attributes[x] = sorted(other.attributes[x]) - if self.attributes[x] != other.attributes[x]: - p = None - q = None - m = None - n = None - # First check if the difference can be fixed but shunting the first part - # of the DomainHostName e.g. 'mysamba4.test.local' => 'mysamba4' - if x.upper() in self.other_attributes: - p = [self.con.domain_name.split(".")[0] == j for j in self.attributes[x]] - q = [other.con.domain_name.split(".")[0] == j for j in other.attributes[x]] - if p == q: - continue - # Attribute values that are list that contain DN based values that may differ - elif x.upper() in self.dn_attributes: - m = p - n = q - if not p and not q: - m = self.attributes[x] - n = other.attributes[x] - p = [self.fix_dn(j) for j in m] - q = [other.fix_dn(j) for j in n] - if p == q: - continue - # Attributes that contain the Domain name in them - if x.upper() in self.domain_attributes: - m = p - n = q - if not p and not q: - m = self.attributes[x] - n = other.attributes[x] - p = [self.fix_domain_name(j) for j in m] - q = [other.fix_domain_name(j) for j in n] - if p == q: - continue - # - if x.upper() in self.servername_attributes: - # Attributes with SERVER_NAME - m = p - n = q - if not p and not q: - m = self.attributes[x] - n = other.attributes[x] - p = [self.fix_server_name(j) for j in m] - q = [other.fix_server_name(j) for j in n] - if p == q: - continue - # - if x.upper() in self.netbios_attributes: - # Attributes with NETBIOS Domain name - m = p - n = q - if not p and not q: - m = self.attributes[x] - n = other.attributes[x] - p = [self.fix_domain_netbios(j) for j in m] - q = [other.fix_domain_netbios(j) for j in n] - if p == q: - continue - # - if title: - res += title + "\n" - title = None - if p and q: - res += 8*" " + x + " => \n%s\n%s" % (p, q) + "\n" - else: - res += 8*" " + x + " => \n%s\n%s" % (self.attributes[x], other.attributes[x]) + "\n" - self.df_value_attrs.append(x) - # - if self.unique_attrs + other.unique_attrs != []: - assert self.unique_attrs != other.unique_attrs - self.summary["unique_attrs"] += self.unique_attrs - self.summary["df_value_attrs"] += self.df_value_attrs - other.summary["unique_attrs"] += other.unique_attrs - other.summary["df_value_attrs"] += self.df_value_attrs # they are the same - # - self.screen_output = res[:-1] - other.screen_output = res[:-1] - # - return res == "" - - -class LDAPBundel(object): - def __init__(self, connection, context, dn_list=None): - self.con = connection - self.two_domains = self.con.two_domains - self.quiet = self.con.quiet - self.verbose = self.con.verbose - self.search_base = self.con.search_base - self.search_scope = self.con.search_scope - self.summary = {} - self.summary["unique_attrs"] = [] - self.summary["df_value_attrs"] = [] - self.summary["known_ignored_dn"] = [] - self.summary["abnormal_ignored_dn"] = [] - if dn_list: - self.dn_list = dn_list - elif context.upper() in ["DOMAIN", "CONFIGURATION", "SCHEMA"]: - self.context = context.upper() - self.dn_list = self.get_dn_list(context) - else: - raise Exception("Unknown initialization data for LDAPBundel().") - counter = 0 - while counter < len(self.dn_list) and self.two_domains: - # Use alias reference - tmp = self.dn_list[counter] - tmp = tmp[:len(tmp)-len(self.con.base_dn)] + "${DOMAIN_DN}" - tmp = tmp.replace("CN=%s" % self.con.domain_netbios, "CN=${DOMAIN_NETBIOS}") - if len(self.con.server_names) == 1: - for x in self.con.server_names: - tmp = tmp.replace("CN=%s" % x, "CN=${SERVER_NAME}") - self.dn_list[counter] = tmp - counter += 1 - self.dn_list = list(set(self.dn_list)) - self.dn_list = sorted(self.dn_list) - self.size = len(self.dn_list) - - def log(self, msg): - """ - Log on the screen if there is no --quiet oprion set - """ - if not self.quiet: - print msg - - def update_size(self): - self.size = len(self.dn_list) - self.dn_list = sorted(self.dn_list) - - def __eq__(self, other): - res = True - if self.size != other.size: - self.log( "\n* DN lists have different size: %s != %s" % (self.size, other.size) ) - res = False - # - # This is the case where we want to explicitly compare two objects with different DNs. - # It does not matter if they are in the same DC, in two DC in one domain or in two - # different domains. - if self.search_scope != SCOPE_BASE: - title= "\n* DNs found only in %s:" % self.con.host - for x in self.dn_list: - if not x.upper() in [q.upper() for q in other.dn_list]: - if title: - self.log( title ) - title = None - res = False - self.log( 4*" " + x ) - self.dn_list[self.dn_list.index(x)] = "" - self.dn_list = [x for x in self.dn_list if x] - # - title= "\n* DNs found only in %s:" % other.con.host - for x in other.dn_list: - if not x.upper() in [q.upper() for q in self.dn_list]: - if title: - self.log( title ) - title = None - res = False - self.log( 4*" " + x ) - other.dn_list[other.dn_list.index(x)] = "" - other.dn_list = [x for x in other.dn_list if x] - # - self.update_size() - other.update_size() - assert self.size == other.size - assert sorted([x.upper() for x in self.dn_list]) == sorted([x.upper() for x in other.dn_list]) - self.log( "\n* Objects to be compared: %s" % self.size ) - - index = 0 - while index < self.size: - skip = False - try: - object1 = LDAPObject(connection=self.con, - dn=self.dn_list[index], - summary=self.summary) - except LdbError, (enum, estr): - if enum == ERR_NO_SUCH_OBJECT: - self.log( "\n!!! Object not found: %s" % self.dn_list[index] ) - skip = True - raise - try: - object2 = LDAPObject(connection=other.con, - dn=other.dn_list[index], - summary=other.summary) - except LdbError, (enum, estr): - if enum == ERR_NO_SUCH_OBJECT: - self.log( "\n!!! Object not found: %s" % other.dn_list[index] ) - skip = True - raise - if skip: - index += 1 - continue - if object1 == object2: - if self.con.verbose: - self.log( "\nComparing:" ) - self.log( "'%s' [%s]" % (object1.dn, object1.con.host) ) - self.log( "'%s' [%s]" % (object2.dn, object2.con.host) ) - self.log( 4*" " + "OK" ) - else: - self.log( "\nComparing:" ) - self.log( "'%s' [%s]" % (object1.dn, object1.con.host) ) - self.log( "'%s' [%s]" % (object2.dn, object2.con.host) ) - self.log( object1.screen_output ) - self.log( 4*" " + "FAILED" ) - res = False - self.summary = object1.summary - other.summary = object2.summary - index += 1 - # - return res - - def get_dn_list(self, context): - """ Query LDAP server about the DNs of certain naming self.con.ext Domain (or Default), Configuration, Schema. - Parse all DNs and filter those that are 'strange' or abnormal. - """ - if context.upper() == "DOMAIN": - search_base = "%s" % self.con.base_dn - elif context.upper() == "CONFIGURATION": - search_base = "CN=Configuration,%s" % self.con.base_dn - elif context.upper() == "SCHEMA": - search_base = "CN=Schema,CN=Configuration,%s" % self.con.base_dn - - dn_list = [] - if not self.search_base: - self.search_base = search_base - self.search_scope = self.search_scope.upper() - if self.search_scope == "SUB": - self.search_scope = SCOPE_SUBTREE - elif self.search_scope == "BASE": - self.search_scope = SCOPE_BASE - elif self.search_scope == "ONE": - self.search_scope = SCOPE_ONELEVEL - else: - raise StandardError("Wrong 'scope' given. Choose from: SUB, ONE, BASE") - if not self.search_base.upper().endswith(search_base.upper()): - raise StandardError("Invalid search base specified: %s" % self.search_base) - res = self.con.ldb.search(base=self.search_base, scope=self.search_scope, attrs=["dn"]) - for x in res: - dn_list.append(x["dn"].get_linearized()) - # - global summary - # - return dn_list - - def print_summary(self): - self.summary["unique_attrs"] = list(set(self.summary["unique_attrs"])) - self.summary["df_value_attrs"] = list(set(self.summary["df_value_attrs"])) - # - if self.summary["unique_attrs"]: - self.log( "\nAttributes found only in %s:" % self.con.host ) - self.log( "".join([str("\n" + 4*" " + x) for x in self.summary["unique_attrs"]]) ) - # - if self.summary["df_value_attrs"]: - self.log( "\nAttributes with different values:" ) - self.log( "".join([str("\n" + 4*" " + x) for x in self.summary["df_value_attrs"]]) ) - self.summary["df_value_attrs"] = [] - -class cmd_ldapcmp(Command): - """compare two ldap databases""" - synopsis = "ldapcmp URL1 URL2 <domain|configuration|schema> [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptionsDouble, - } - - takes_args = ["URL1", "URL2", "context1?", "context2?", "context3?"] - - takes_options = [ - Option("-w", "--two", dest="two", action="store_true", default=False, - help="Hosts are in two different domains"), - Option("-q", "--quiet", dest="quiet", action="store_true", default=False, - help="Do not print anything but relay on just exit code"), - Option("-v", "--verbose", dest="verbose", action="store_true", default=False, - help="Print all DN pairs that have been compared"), - Option("--sd", dest="descriptor", action="store_true", default=False, - help="Compare nTSecurityDescriptor attibutes only"), - Option("--sort-aces", dest="sort_aces", action="store_true", default=False, - help="Sort ACEs before comparison of nTSecurityDescriptor attribute"), - Option("--view", dest="view", default="section", - help="Display mode for nTSecurityDescriptor results. Possible values: section or collision."), - Option("--base", dest="base", default="", - help="Pass search base that will build DN list for the first DC."), - Option("--base2", dest="base2", default="", - help="Pass search base that will build DN list for the second DC. Used when --two or when compare two different DNs."), - Option("--scope", dest="scope", default="SUB", - help="Pass search scope that builds DN list. Options: SUB, ONE, BASE"), - ] - - def run(self, URL1, URL2, - context1=None, context2=None, context3=None, - two=False, quiet=False, verbose=False, descriptor=False, sort_aces=False, view="section", - base="", base2="", scope="SUB", credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - creds2 = credopts.get_credentials2(lp, guess=False) - if creds2.is_anonymous(): - creds2 = creds - else: - creds2.set_domain("") - creds2.set_workstation("") - if not creds.authentication_requested(): - raise CommandError("You must supply at least one username/password pair") - - # make a list of contexts to compare in - contexts = [] - if context1 is None: - if base and base2: - # If search bases are specified context is defaulted to - # DOMAIN so the given search bases can be verified. - contexts = ["DOMAIN"] - else: - # if no argument given, we compare all contexts - contexts = ["DOMAIN", "CONFIGURATION", "SCHEMA"] - else: - for c in [context1, context2, context3]: - if c is None: - continue - if not c.upper() in ["DOMAIN", "CONFIGURATION", "SCHEMA"]: - raise CommandError("Incorrect argument: %s" % c) - contexts.append(c.upper()) - - if verbose and quiet: - raise CommandError("You cannot set --verbose and --quiet together") - if (not base and base2) or (base and not base2): - raise CommandError("You need to specify both --base and --base2 at the same time") - if descriptor and view.upper() not in ["SECTION", "COLLISION"]: - raise CommandError("Invalid --view value. Choose from: section or collision") - if not scope.upper() in ["SUB", "ONE", "BASE"]: - raise CommandError("Invalid --scope value. Choose from: SUB, ONE, BASE") - - con1 = LDAPBase(URL1, creds, lp, - two=two, quiet=quiet, descriptor=descriptor, sort_aces=sort_aces, - verbose=verbose,view=view, base=base, scope=scope) - assert len(con1.base_dn) > 0 - - con2 = LDAPBase(URL2, creds2, lp, - two=two, quiet=quiet, descriptor=descriptor, sort_aces=sort_aces, - verbose=verbose, view=view, base=base2, scope=scope) - assert len(con2.base_dn) > 0 - - status = 0 - for context in contexts: - if not quiet: - print "\n* Comparing [%s] context..." % context - - b1 = LDAPBundel(con1, context=context) - b2 = LDAPBundel(con2, context=context) - - if b1 == b2: - if not quiet: - print "\n* Result for [%s]: SUCCESS" % context - else: - if not quiet: - print "\n* Result for [%s]: FAILURE" % context - if not descriptor: - assert len(b1.summary["df_value_attrs"]) == len(b2.summary["df_value_attrs"]) - b2.summary["df_value_attrs"] = [] - print "\nSUMMARY" - print "---------" - b1.print_summary() - b2.print_summary() - # mark exit status as FAILURE if a least one comparison failed - status = -1 - if status != 0: - raise CommandError("Compare failed: %d" % status) diff --git a/source4/scripting/python/samba/netcmd/machinepw.py b/source4/scripting/python/samba/netcmd/machinepw.py deleted file mode 100644 index d822b22794..0000000000 --- a/source4/scripting/python/samba/netcmd/machinepw.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# -# Machine passwords -# Copyright Jelmer Vernooij 2010 -# -# 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/>. -# - -import samba.getopt as options - -from samba import Ldb -from samba.auth import system_session -from samba.netcmd import Command, CommandError - - -class cmd_machinepw(Command): - """Gets a machine password out of our SAM""" - - synopsis = "%prog machinepw <accountname>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_args = ["secret"] - - def run(self, secret, sambaopts=None, credopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - url = lp.get("secrets database") - secretsdb = Ldb(url=url, session_info=system_session(), - credentials=creds, lp=lp) - - result = secretsdb.search(attrs=["secret"], - expression="(&(objectclass=primaryDomain)(samaccountname=%s))" % secret) - - if len(result) != 1: - raise CommandError("search returned %d records, expected 1" % len(result)) - - self.outf.write("%s\n" % result[0]["secret"]) diff --git a/source4/scripting/python/samba/netcmd/netacl.py b/source4/scripting/python/samba/netcmd/netacl.py deleted file mode 100644 index 3f68ee756c..0000000000 --- a/source4/scripting/python/samba/netcmd/netacl.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# -# Manipulate ACLs -# -# Copyright (C) Matthieu Patou <mat@matws.net> 2010 -# Copyright (C) Nadezhda Ivanova <nivanova@samba.org> 2010 -# -# 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 samba.netcmd import ( - SuperCommand, - ) -from samba.netcmd.ntacl import cmd_nt_acl -from samba.netcmd.dsacl import cmd_ds_acl - -class cmd_acl(SuperCommand): - """NT ACLs manipulation""" - - subcommands = {} - subcommands["nt"] = cmd_nt_acl() - subcommands["ds"] = cmd_ds_acl() diff --git a/source4/scripting/python/samba/netcmd/newuser.py b/source4/scripting/python/samba/netcmd/newuser.py deleted file mode 100644 index 3581340577..0000000000 --- a/source4/scripting/python/samba/netcmd/newuser.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python -# -# Adds a new user to a Samba4 server -# Copyright Jelmer Vernooij 2008 -# -# Based on the original in EJS: -# Copyright Andrew Tridgell 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/>. - -import samba.getopt as options -from samba.netcmd import Command, CommandError, Option -import ldb - -from getpass import getpass -from samba.auth import system_session -from samba.samdb import SamDB - -class cmd_newuser(Command): - """Creates a new user""" - - synopsis = "newuser [options] <username> [<password>]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - Option("--must-change-at-next-login", - help="Force password to be changed on next login", - action="store_true"), - Option("--use-username-as-cn", - help="Force use of username as user's CN", - action="store_true"), - Option("--userou", - help="Alternative location (without domainDN counterpart) to default CN=Users in which new user object will be created", - type=str), - Option("--surname", help="User's surname", type=str), - Option("--given-name", help="User's given name", type=str), - Option("--initials", help="User's initials", type=str), - Option("--profile-path", help="User's profile path", type=str), - Option("--script-path", help="User's logon script path", type=str), - Option("--home-drive", help="User's home drive letter", type=str), - Option("--home-directory", help="User's home directory path", type=str), - Option("--job-title", help="User's job title", type=str), - Option("--department", help="User's department", type=str), - Option("--company", help="User's company", type=str), - Option("--description", help="User's description", type=str), - Option("--mail-address", help="User's email address", type=str), - Option("--internet-address", help="User's home page", type=str), - Option("--telephone-number", help="User's phone number", type=str), - Option("--physical-delivery-office", help="User's office location", type=str), - ] - - takes_args = ["username", "password?"] - - def run(self, username, password=None, credopts=None, sambaopts=None, - versionopts=None, H=None, must_change_at_next_login=None, - use_username_as_cn=None, userou=None, surname=None, given_name=None, initials=None, - profile_path=None, script_path=None, home_drive=None, home_directory=None, - job_title=None, department=None, company=None, description=None, - mail_address=None, internet_address=None, telephone_number=None, physical_delivery_office=None): - - if password is None: - password = getpass("New Password: ") - - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - - try: - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - samdb.newuser(username, password, - force_password_change_at_next_login_req=must_change_at_next_login, - useusernameascn=use_username_as_cn, userou=userou, surname=surname, givenname=given_name, initials=initials, - profilepath=profile_path, homedrive=home_drive, scriptpath=script_path, homedirectory=home_directory, - jobtitle=job_title, department=department, company=company, description=description, - mailaddress=mail_address, internetaddress=internet_address, - telephonenumber=telephone_number, physicaldeliveryoffice=physical_delivery_office) - except Exception, e: - raise CommandError('Failed to create user "%s"' % username, e) - - print("User %s created successfully" % username) diff --git a/source4/scripting/python/samba/netcmd/ntacl.py b/source4/scripting/python/samba/netcmd/ntacl.py deleted file mode 100644 index 49f8fbc77f..0000000000 --- a/source4/scripting/python/samba/netcmd/ntacl.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -# -# Manipulate file NT ACLs -# -# Copyright Matthieu Patou 2010 <mat@matws.net> -# -# 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 samba.credentials import DONT_USE_KERBEROS -import samba.getopt as options -from samba.dcerpc import security -from samba.ntacls import setntacl, getntacl -from samba import Ldb -from samba.ndr import ndr_unpack - -from ldb import SCOPE_BASE -import os - -from samba.auth import system_session -from samba.netcmd import ( - Command, - CommandError, - SuperCommand, - Option, - ) - -class cmd_acl_set(Command): - """Set ACLs on a file""" - synopsis = "%prog set <acl> <file> [--xattr-backend=native|tdb] [--eadb-file=file] [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_options = [ - Option("--quiet", help="Be quiet", action="store_true"), - Option("--xattr-backend", type="choice", help="xattr backend type (native fs or tdb)", - choices=["native","tdb"]), - Option("--eadb-file", help="Name of the tdb file where attributes are stored", type="string"), - ] - - takes_args = ["acl","file"] - - def run(self, acl, file, quiet=False,xattr_backend=None,eadb_file=None, - credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - path = os.path.join(lp.get("private dir"), lp.get("secrets database") or "secrets.ldb") - creds = credopts.get_credentials(lp) - creds.set_kerberos_state(DONT_USE_KERBEROS) - try: - ldb = Ldb(path, session_info=system_session(), credentials=creds, - lp=lp) - except Exception, e: - raise CommandError("Unable to read domain SID from configuration files", e) - attrs = ["objectSid"] - print lp.get("realm") - res = ldb.search(expression="(objectClass=*)", - base="flatname=%s,cn=Primary Domains" % lp.get("workgroup"), - scope=SCOPE_BASE, attrs=attrs) - if len(res) !=0: - domainsid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) - setntacl(lp, file, acl, str(domainsid), xattr_backend, eadb_file) - else: - raise CommandError("Unable to read domain SID from configuration files") - - -class cmd_acl_get(Command): - """Set ACLs on a file""" - synopsis = "%prog get <file> [--as-sddl] [--xattr-backend=native|tdb] [--eadb-file=file] [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_options = [ - Option("--as-sddl", help="Output ACL in the SDDL format", action="store_true"), - Option("--xattr-backend", type="choice", help="xattr backend type (native fs or tdb)", - choices=["native","tdb"]), - Option("--eadb-file", help="Name of the tdb file where attributes are stored", type="string"), - ] - - takes_args = ["file"] - - def run(self, file, as_sddl=False, xattr_backend=None, eadb_file=None, - credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - acl = getntacl(lp, file, xattr_backend, eadb_file) - if as_sddl: - anysid = security.dom_sid(security.SID_NT_SELF) - print acl.info.as_sddl(anysid) - else: - acl.dump() - - -class cmd_nt_acl(SuperCommand): - """NT ACLs manipulation""" - - subcommands = {} - subcommands["set"] = cmd_acl_set() - subcommands["get"] = cmd_acl_get() - diff --git a/source4/scripting/python/samba/netcmd/pwsettings.py b/source4/scripting/python/samba/netcmd/pwsettings.py deleted file mode 100644 index 3f1a8e189d..0000000000 --- a/source4/scripting/python/samba/netcmd/pwsettings.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python -# -# Sets password settings. -# (Password complexity, history length, minimum password length, the minimum -# and maximum password age) on a Samba4 server -# -# Copyright Matthias Dieter Wallnoefer 2009 -# Copyright Andrew Kroeger 2009 -# Copyright Jelmer Vernooij 2009 -# -# 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/>. -# - -import samba.getopt as options -import ldb - -from samba.auth import system_session -from samba.samdb import SamDB -from samba.dcerpc.samr import DOMAIN_PASSWORD_COMPLEX, DOMAIN_PASSWORD_STORE_CLEARTEXT -from samba.netcmd import Command, CommandError, Option - -class cmd_pwsettings(Command): - """Sets password settings - - Password complexity, history length, minimum password length, the minimum - and maximum password age) on a Samba4 server. - """ - - synopsis = "(show | set <options>)" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - Option("--quiet", help="Be quiet", action="store_true"), - Option("--complexity", type="choice", choices=["on","off","default"], - help="The password complexity (on | off | default). Default is 'on'"), - Option("--store-plaintext", type="choice", choices=["on","off","default"], - help="Store plaintext passwords where account have 'store passwords with reversible encryption' set (on | off | default). Default is 'off'"), - Option("--history-length", - help="The password history length (<integer> | default). Default is 24.", type=str), - Option("--min-pwd-length", - help="The minimum password length (<integer> | default). Default is 7.", type=str), - Option("--min-pwd-age", - help="The minimum password age (<integer in days> | default). Default is 1.", type=str), - Option("--max-pwd-age", - help="The maximum password age (<integer in days> | default). Default is 43.", type=str), - ] - - takes_args = ["subcommand"] - - def run(self, subcommand, H=None, min_pwd_age=None, max_pwd_age=None, - quiet=False, complexity=None, store_plaintext=None, history_length=None, - min_pwd_length=None, credopts=None, sambaopts=None, - versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - - domain_dn = samdb.domain_dn() - res = samdb.search(domain_dn, scope=ldb.SCOPE_BASE, - attrs=["pwdProperties", "pwdHistoryLength", "minPwdLength", - "minPwdAge", "maxPwdAge"]) - assert(len(res) == 1) - try: - pwd_props = int(res[0]["pwdProperties"][0]) - pwd_hist_len = int(res[0]["pwdHistoryLength"][0]) - cur_min_pwd_len = int(res[0]["minPwdLength"][0]) - # ticks -> days - cur_min_pwd_age = int(abs(int(res[0]["minPwdAge"][0])) / (1e7 * 60 * 60 * 24)) - cur_max_pwd_age = int(abs(int(res[0]["maxPwdAge"][0])) / (1e7 * 60 * 60 * 24)) - except Exception, e: - raise CommandError("Could not retrieve password properties!", e) - - if subcommand == "show": - self.message("Password informations for domain '%s'" % domain_dn) - self.message("") - if pwd_props & DOMAIN_PASSWORD_COMPLEX != 0: - self.message("Password complexity: on") - else: - self.message("Password complexity: off") - if pwd_props & DOMAIN_PASSWORD_STORE_CLEARTEXT != 0: - self.message("Store plaintext passwords: on") - else: - self.message("Store plaintext passwords: off") - self.message("Password history length: %d" % pwd_hist_len) - self.message("Minimum password length: %d" % cur_min_pwd_len) - self.message("Minimum password age (days): %d" % cur_min_pwd_age) - self.message("Maximum password age (days): %d" % cur_max_pwd_age) - elif subcommand == "set": - msgs = [] - m = ldb.Message() - m.dn = ldb.Dn(samdb, domain_dn) - - if complexity is not None: - if complexity == "on" or complexity == "default": - pwd_props = pwd_props | DOMAIN_PASSWORD_COMPLEX - msgs.append("Password complexity activated!") - elif complexity == "off": - pwd_props = pwd_props & (~DOMAIN_PASSWORD_COMPLEX) - msgs.append("Password complexity deactivated!") - - if store_plaintext is not None: - if store_plaintext == "on" or store_plaintext == "default": - pwd_props = pwd_props | DOMAIN_PASSWORD_STORE_CLEARTEXT - msgs.append("Plaintext password storage for changed passwords activated!") - elif store_plaintext == "off": - pwd_props = pwd_props & (~DOMAIN_PASSWORD_STORE_CLEARTEXT) - msgs.append("Plaintext password storage for changed passwords deactivated!") - - if complexity is not None or store_plaintext is not None: - m["pwdProperties"] = ldb.MessageElement(str(pwd_props), - ldb.FLAG_MOD_REPLACE, "pwdProperties") - - if history_length is not None: - if history_length == "default": - pwd_hist_len = 24 - else: - pwd_hist_len = int(history_length) - - if pwd_hist_len < 0 or pwd_hist_len > 24: - raise CommandError("Password history length must be in the range of 0 to 24!") - - m["pwdHistoryLength"] = ldb.MessageElement(str(pwd_hist_len), - ldb.FLAG_MOD_REPLACE, "pwdHistoryLength") - msgs.append("Password history length changed!") - - if min_pwd_length is not None: - if min_pwd_length == "default": - min_pwd_len = 7 - else: - min_pwd_len = int(min_pwd_length) - - if min_pwd_len < 0 or min_pwd_len > 14: - raise CommandError("Minimum password length must be in the range of 0 to 14!") - - m["minPwdLength"] = ldb.MessageElement(str(min_pwd_len), - ldb.FLAG_MOD_REPLACE, "minPwdLength") - msgs.append("Minimum password length changed!") - - if min_pwd_age is not None: - if min_pwd_age == "default": - min_pwd_age = 1 - else: - min_pwd_age = int(min_pwd_age) - - if min_pwd_age < 0 or min_pwd_age > 998: - raise CommandError("Minimum password age must be in the range of 0 to 998!") - - # days -> ticks - min_pwd_age_ticks = -int(min_pwd_age * (24 * 60 * 60 * 1e7)) - - m["minPwdAge"] = ldb.MessageElement(str(min_pwd_age_ticks), - ldb.FLAG_MOD_REPLACE, "minPwdAge") - msgs.append("Minimum password age changed!") - - if max_pwd_age is not None: - if max_pwd_age == "default": - max_pwd_age = 43 - else: - max_pwd_age = int(max_pwd_age) - - if max_pwd_age < 0 or max_pwd_age > 999: - raise CommandError("Maximum password age must be in the range of 0 to 999!") - - # days -> ticks - max_pwd_age_ticks = -int(max_pwd_age * (24 * 60 * 60 * 1e7)) - - m["maxPwdAge"] = ldb.MessageElement(str(max_pwd_age_ticks), - ldb.FLAG_MOD_REPLACE, "maxPwdAge") - msgs.append("Maximum password age changed!") - - if max_pwd_age > 0 and min_pwd_age >= max_pwd_age: - raise CommandError("Maximum password age (%d) must be greater than minimum password age (%d)!" % (max_pwd_age, min_pwd_age)) - - samdb.modify(m) - msgs.append("All changes applied successfully!") - self.message("\n".join(msgs)) - else: - raise CommandError("Wrong argument '%s'!" % subcommand) diff --git a/source4/scripting/python/samba/netcmd/rodc.py b/source4/scripting/python/samba/netcmd/rodc.py deleted file mode 100644 index f4daac853d..0000000000 --- a/source4/scripting/python/samba/netcmd/rodc.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python -# -# rodc related commands -# -# Copyright Andrew Tridgell 2010 -# -# 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 samba.netcmd import Command, CommandError, Option, SuperCommand -import samba.getopt as options -from samba.samdb import SamDB -from samba.auth import system_session -import ldb -from samba.dcerpc import misc, drsuapi -from samba.credentials import Credentials -from samba.drs_utils import drs_Replicate - -class cmd_rodc_preload(Command): - """Preload one account for an RODC""" - - synopsis = "%prog rodc preload <SID|DN|accountname>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("--server", help="DC to use", type=str), - ] - - takes_args = ["account"] - - def get_dn(self, samdb, account): - '''work out what DN they meant''' - - # we accept the account in SID, accountname or DN form - if account[0:2] == 'S-': - res = samdb.search(base="<SID=%s>" % account, - expression="objectclass=user", - scope=ldb.SCOPE_BASE, attrs=[]) - elif account.find('=') >= 0: - res = samdb.search(base=account, - expression="objectclass=user", - scope=ldb.SCOPE_BASE, attrs=[]) - else: - res = samdb.search(expression="(&(samAccountName=%s)(objectclass=user))" % account, - scope=ldb.SCOPE_SUBTREE, attrs=[]) - if len(res) != 1: - raise Exception("Failed to find account '%s'" % account) - return str(res[0]["dn"]) - - - def get_dsServiceName(self, samdb): - res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) - return res[0]["dsServiceName"][0] - - - def run(self, account, sambaopts=None, - credopts=None, versionopts=None, server=None): - - if server is None: - raise Exception("You must supply a server") - - lp = sambaopts.get_loadparm() - - creds = credopts.get_credentials(lp, fallback_machine=True) - - # connect to the remote and local SAMs - samdb = SamDB(url="ldap://%s" % server, - session_info=system_session(), - credentials=creds, lp=lp) - - local_samdb = SamDB(url=None, session_info=system_session(), - credentials=creds, lp=lp) - - # work out the source and destination GUIDs - dc_ntds_dn = self.get_dsServiceName(samdb) - res = samdb.search(base=dc_ntds_dn, scope=ldb.SCOPE_BASE, attrs=["invocationId"]) - source_dsa_invocation_id = misc.GUID(local_samdb.schema_format_value("objectGUID", res[0]["invocationId"][0])) - - dn = self.get_dn(samdb, account) - print "Replicating DN %s" % dn - - destination_dsa_guid = misc.GUID(local_samdb.get_ntds_GUID()) - - local_samdb.transaction_start() - repl = drs_Replicate("ncacn_ip_tcp:%s[seal,print]" % server, lp, creds, local_samdb) - try: - repl.replicate(dn, source_dsa_invocation_id, destination_dsa_guid, - exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) - except Exception, e: - raise CommandError("Error replicating DN %s" % dn, e) - local_samdb.transaction_commit() - - - -class cmd_rodc(SuperCommand): - """RODC commands""" - - subcommands = {} - subcommands["preload"] = cmd_rodc_preload() diff --git a/source4/scripting/python/samba/netcmd/setexpiry.py b/source4/scripting/python/samba/netcmd/setexpiry.py deleted file mode 100644 index bd8ea166fa..0000000000 --- a/source4/scripting/python/samba/netcmd/setexpiry.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python -# -# Sets the user password expiry on a Samba4 server -# Copyright Jelmer Vernooij 2008 -# -# Based on the original in EJS: -# Copyright Andrew Tridgell 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 samba.netcmd import Command, CommandError, Option - -import samba.getopt as options - -from samba.auth import system_session -from samba.samdb import SamDB - -class cmd_setexpiry(Command): - """Sets the expiration of a user account""" - - synopsis = "setexpiry [username] [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - Option("--filter", help="LDAP Filter to set password on", type=str), - Option("--days", help="Days to expiry", type=int), - Option("--noexpiry", help="Password does never expire", action="store_true"), - ] - - takes_args = ["username?"] - - def run(self, username=None, sambaopts=None, credopts=None, - versionopts=None, H=None, filter=None, days=None, noexpiry=None): - if username is None and filter is None: - raise CommandError("Either the username or '--filter' must be specified!") - - if filter is None: - filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username) - - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - - if days is None: - days = 0 - - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - - samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry) diff --git a/source4/scripting/python/samba/netcmd/setpassword.py b/source4/scripting/python/samba/netcmd/setpassword.py deleted file mode 100644 index b32b651822..0000000000 --- a/source4/scripting/python/samba/netcmd/setpassword.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python -# -# Sets a user password on a Samba4 server -# Copyright Jelmer Vernooij 2008 -# -# Based on the original in EJS: -# Copyright Andrew Tridgell 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/>. -# - -import samba.getopt as options -from samba.netcmd import Command, CommandError, Option -from getpass import getpass -from samba.auth import system_session -from samba.samdb import SamDB -from samba import gensec -import ldb - -class cmd_setpassword(Command): - """(Re)sets the password on a user account""" - - synopsis = "setpassword [username] [options]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "versionopts": options.VersionOptions, - "credopts": options.CredentialsOptions, - } - - takes_options = [ - Option("-H", help="LDB URL for database or target server", type=str), - Option("--filter", help="LDAP Filter to set password on", type=str), - Option("--newpassword", help="Set password", type=str), - Option("--must-change-at-next-login", - help="Force password to be changed on next login", - action="store_true"), - ] - - takes_args = ["username?"] - - def run(self, username=None, filter=None, credopts=None, sambaopts=None, - versionopts=None, H=None, newpassword=None, - must_change_at_next_login=None): - if filter is None and username is None: - raise CommandError("Either the username or '--filter' must be specified!") - - password = newpassword - if password is None: - password = getpass("New Password: ") - - if filter is None: - filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username) - - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - - creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL) - - samdb = SamDB(url=H, session_info=system_session(), - credentials=creds, lp=lp) - - try: - samdb.setpassword(filter, password, - force_change_at_next_login=must_change_at_next_login, - username=username) - except Exception, e: - raise CommandError('Failed to set password for user "%s"' % username, e) - print "Changed password OK" diff --git a/source4/scripting/python/samba/netcmd/spn.py b/source4/scripting/python/samba/netcmd/spn.py deleted file mode 100644 index 4cfa21fa03..0000000000 --- a/source4/scripting/python/samba/netcmd/spn.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env python -# -# spn management -# -# Copyright Matthieu Patou mat@samba.org 2010 -# -# 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/>. -# - -import samba.getopt as options -import ldb -import re -from samba import provision -from samba.samdb import SamDB -from samba.auth import system_session -from samba.netcmd import ( - Command, - CommandError, - SuperCommand, - Option - ) - -def _get_user_realm_domain(user): - """ get the realm or the domain and the base user - from user like: - * username - * DOMAIN\username - * username@REALM - """ - baseuser = user - realm = "" - domain = "" - m = re.match(r"(\w+)\\(\w+$)", user) - if m: - domain = m.group(1) - baseuser = m.group(2) - return (baseuser.lower(), domain.upper(), realm) - m = re.match(r"(\w+)@(\w+)", user) - if m: - baseuser = m.group(1) - realm = m.group(2) - return (baseuser.lower(), domain, realm.upper()) - -class cmd_spn_list(Command): - """List spns of a given user.""" - synopsis = "%prog spn list <user>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_args = ["user"] - - def run(self, user, credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - paths = provision.provision_paths_from_lp(lp, lp.get("realm")) - sam = SamDB(paths.samdb, session_info=system_session(), - credentials=creds, lp=lp) - # TODO once I understand how, use the domain info to naildown - # to the correct domain - (cleaneduser, realm, domain) = _get_user_realm_domain(user) - print cleaneduser - res = sam.search(expression="samaccountname=%s" % cleaneduser, - scope=ldb.SCOPE_SUBTREE, - attrs=["servicePrincipalName"]) - if len(res) >0: - spns = res[0].get("servicePrincipalName") - found = False - flag = ldb.FLAG_MOD_ADD - if spns != None: - print "User %s has the following servicePrincipalName: " % str(res[0].dn) - for e in spns: - print "\t %s" % (str(e)) - - else: - print "User %s has no servicePrincipalName" % str(res[0].dn) - else: - raise CommandError("User %s not found" % user) - -class cmd_spn_add(Command): - """Create a new spn.""" - synopsis = "%prog spn add [--force] <name> <user>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - takes_options = [ - Option("--force", help="Force the addition of the spn"\ - " even it exists already", action="store_true"), - ] - takes_args = ["name", "user"] - - def run(self, name, user, force=False, credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - paths = provision.provision_paths_from_lp(lp, lp.get("realm")) - sam = SamDB(paths.samdb, session_info=system_session(), - credentials=creds, lp=lp) - res = sam.search(expression="servicePrincipalName=%s" % name, - scope=ldb.SCOPE_SUBTREE, - ) - if len(res) != 0 and not force: - raise CommandError("Service principal %s already" - " affected to another user" % name) - - (cleaneduser, realm, domain) = _get_user_realm_domain(user) - res = sam.search(expression="samaccountname=%s" % cleaneduser, - scope=ldb.SCOPE_SUBTREE, - attrs=["servicePrincipalName"]) - if len(res) >0: - res[0].dn - msg = ldb.Message() - spns = res[0].get("servicePrincipalName") - tab = [] - found = False - flag = ldb.FLAG_MOD_ADD - if spns != None: - for e in spns: - if str(e) == name: - found = True - tab.append(str(e)) - flag = ldb.FLAG_MOD_REPLACE - tab.append(name) - msg.dn = res[0].dn - msg["servicePrincipalName"] = ldb.MessageElement(tab, flag, - "servicePrincipalName") - if not found: - sam.modify(msg) - else: - raise CommandError("Service principal %s already" - " affected to %s" % (name, user)) - else: - raise CommandError("User %s not found" % user) - - -class cmd_spn_delete(Command): - """Delete a spn.""" - synopsis = "%prog spn delete <name> [user]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_args = ["name", "user?"] - - def run(self, name, user=None, credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - paths = provision.provision_paths_from_lp(lp, lp.get("realm")) - sam = SamDB(paths.samdb, session_info=system_session(), - credentials=creds, lp=lp) - res = sam.search(expression="servicePrincipalName=%s" % name, - scope=ldb.SCOPE_SUBTREE, - attrs=["servicePrincipalName", "samAccountName"]) - if len(res) >0: - result = None - if user is not None: - (cleaneduser, realm, domain) = _get_user_realm_domain(user) - for elem in res: - if str(elem["samAccountName"]).lower() == cleaneduser: - result = elem - if result is None: - raise CommandError("Unable to find user %s with" - " spn %s" % (user, name)) - else: - if len(res) != 1: - listUser = "" - for r in res: - listUser = "%s\n%s" % (listUser, str(r.dn)) - raise CommandError("More than one user has the spn %s "\ - "and no specific user was specified, list of users"\ - " with this spn:%s" % (name, listUser)) - else: - result=res[0] - - - msg = ldb.Message() - spns = result.get("servicePrincipalName") - tab = [] - if spns != None: - for e in spns: - if str(e) != name: - tab.append(str(e)) - flag = ldb.FLAG_MOD_REPLACE - msg.dn = result.dn - msg["servicePrincipalName"] = ldb.MessageElement(tab, flag, - "servicePrincipalName") - sam.modify(msg) - else: - raise CommandError("Service principal %s not affected" % name) - -class cmd_spn(SuperCommand): - """SPN management [server connection needed]""" - - subcommands = {} - subcommands["add"] = cmd_spn_add() - subcommands["list"] = cmd_spn_list() - subcommands["delete"] = cmd_spn_delete() - diff --git a/source4/scripting/python/samba/netcmd/time.py b/source4/scripting/python/samba/netcmd/time.py deleted file mode 100644 index e13d3df96c..0000000000 --- a/source4/scripting/python/samba/netcmd/time.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -# -# time -# -# Copyright Jelmer Vernooij 2010 <jelmer@samba.org> -# -# 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/>. -# - -import samba.getopt as options -import common -from samba.net import Net - -from samba.netcmd import ( - Command, - ) - -class cmd_time(Command): - """Retrieve the time on a remote server [server connection needed]""" - synopsis = "%prog time <server-name>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_args = ["server_name?"] - - def run(self, server_name=None, credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - net = Net(creds, lp, server=credopts.ipaddress) - if server_name is None: - server_name = common.netcmd_dnsname(lp) - print net.time(server_name) diff --git a/source4/scripting/python/samba/netcmd/user.py b/source4/scripting/python/samba/netcmd/user.py deleted file mode 100644 index bbc972bcc7..0000000000 --- a/source4/scripting/python/samba/netcmd/user.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python -# -# user management -# -# Copyright Jelmer Vernooij 2010 <jelmer@samba.org> -# -# 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/>. -# - -import samba.getopt as options - -from samba.net import Net - -from samba.netcmd import ( - Command, - SuperCommand, - ) - -class cmd_user_add(Command): - """Create a new user.""" - synopsis = "%prog user add <name> [<password>]" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_args = ["name", "password?"] - - def run(self, name, password=None, credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp ) - net = Net(creds, lp, server=credopts.ipaddress) - net.create_user(name) - if password is not None: - net.set_password(name, creds.get_domain(), password, creds) - - -class cmd_user_delete(Command): - """Delete a user.""" - synopsis = "%prog user delete <name>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_args = ["name"] - - def run(self, name, credopts=None, sambaopts=None, versionopts=None): - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp, fallback_machine=True) - net = Net(creds, lp, server=credopts.ipaddress) - net.delete_user(name) - - -class cmd_user(SuperCommand): - """User management [server connection needed]""" - - subcommands = {} - subcommands["add"] = cmd_user_add() - subcommands["delete"] = cmd_user_delete() - diff --git a/source4/scripting/python/samba/netcmd/vampire.py b/source4/scripting/python/samba/netcmd/vampire.py deleted file mode 100644 index 509aa8aacd..0000000000 --- a/source4/scripting/python/samba/netcmd/vampire.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -# -# Vampire -# -# Copyright Jelmer Vernooij 2010 <jelmer@samba.org> -# -# 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/>. -# - -import samba.getopt as options - -from samba.net import Net - -from samba.netcmd import ( - Command, - Option, - SuperCommand, - CommandError - ) - -class cmd_vampire(Command): - """Join and synchronise a remote AD domain to the local server [server connection needed]""" - synopsis = "%prog vampire [options] <domain>" - - takes_optiongroups = { - "sambaopts": options.SambaOptions, - "credopts": options.CredentialsOptions, - "versionopts": options.VersionOptions, - } - - takes_options = [ - Option("--target-dir", help="Target directory.", type=str), - Option("--force", help="force run", action='store_true', default=False), - ] - - takes_args = ["domain"] - - def run(self, domain, target_dir=None, credopts=None, sambaopts=None, versionopts=None, force=False): - if not force: - raise CommandError("samba-tool vampire is deprecated, please use samba-tool join. Use --force to override") - lp = sambaopts.get_loadparm() - creds = credopts.get_credentials(lp) - net = Net(creds, lp, server=credopts.ipaddress) - (domain_name, domain_sid) = net.vampire(domain=domain, target_dir=target_dir) - self.outf.write("Vampired domain %s (%s)\n" % (domain_name, domain_sid)) diff --git a/source4/scripting/python/samba/ntacls.py b/source4/scripting/python/samba/ntacls.py deleted file mode 100644 index 78365e98b8..0000000000 --- a/source4/scripting/python/samba/ntacls.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010 -# -# -# 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/>. -# - -"""NT Acls.""" - - -import os -import samba.xattr_native, samba.xattr_tdb -from samba.dcerpc import security, xattr -from samba.ndr import ndr_pack, ndr_unpack - -class XattrBackendError(Exception): - """A generic xattr backend error.""" - - -def checkset_backend(lp, backend, eadbfile): - '''return the path to the eadb, or None''' - if backend is None: - return lp.get("posix:eadb") - elif backend == "native": - return None - elif backend == "tdb": - if eadbfile is not None: - return eadbfile - else: - return os.path.abspath(os.path.join(lp.get("private dir"), "eadb.tdb")) - else: - raise XattrBackendError("Invalid xattr backend choice %s"%backend) - - -def getntacl(lp, file, backend=None, eadbfile=None): - eadbname = checkset_backend(lp, backend, eadbfile) - if eadbname is not None: - try: - attribute = samba.xattr_tdb.wrap_getxattr(eadbname, file, - xattr.XATTR_NTACL_NAME) - except Exception: - # FIXME: Don't catch all exceptions, just those related to opening - # xattrdb - print "Fail to open %s" % eadbname - attribute = samba.xattr_native.wrap_getxattr(file, - xattr.XATTR_NTACL_NAME) - else: - attribute = samba.xattr_native.wrap_getxattr(file, - xattr.XATTR_NTACL_NAME) - ntacl = ndr_unpack(xattr.NTACL, attribute) - return ntacl - - -def setntacl(lp, file, sddl, domsid, backend=None, eadbfile=None): - eadbname = checkset_backend(lp, backend, eadbfile) - ntacl = xattr.NTACL() - ntacl.version = 1 - sid = security.dom_sid(domsid) - sd = security.descriptor.from_sddl(sddl, sid) - ntacl.info = sd - if eadbname is not None: - try: - samba.xattr_tdb.wrap_setxattr(eadbname, - file, xattr.XATTR_NTACL_NAME, ndr_pack(ntacl)) - except Exception: - # FIXME: Don't catch all exceptions, just those related to opening - # xattrdb - print "Fail to open %s" % eadbname - samba.xattr_native.wrap_setxattr(file, xattr.XATTR_NTACL_NAME, - ndr_pack(ntacl)) - else: - samba.xattr_native.wrap_setxattr(file, xattr.XATTR_NTACL_NAME, - ndr_pack(ntacl)) - - -def ldapmask2filemask(ldm): - """Takes the access mask of a DS ACE and transform them in a File ACE mask. - """ - RIGHT_DS_CREATE_CHILD = 0x00000001 - RIGHT_DS_DELETE_CHILD = 0x00000002 - RIGHT_DS_LIST_CONTENTS = 0x00000004 - ACTRL_DS_SELF = 0x00000008 - RIGHT_DS_READ_PROPERTY = 0x00000010 - RIGHT_DS_WRITE_PROPERTY = 0x00000020 - RIGHT_DS_DELETE_TREE = 0x00000040 - RIGHT_DS_LIST_OBJECT = 0x00000080 - RIGHT_DS_CONTROL_ACCESS = 0x00000100 - FILE_READ_DATA = 0x0001 - FILE_LIST_DIRECTORY = 0x0001 - FILE_WRITE_DATA = 0x0002 - FILE_ADD_FILE = 0x0002 - FILE_APPEND_DATA = 0x0004 - FILE_ADD_SUBDIRECTORY = 0x0004 - FILE_CREATE_PIPE_INSTANCE = 0x0004 - FILE_READ_EA = 0x0008 - FILE_WRITE_EA = 0x0010 - FILE_EXECUTE = 0x0020 - FILE_TRAVERSE = 0x0020 - FILE_DELETE_CHILD = 0x0040 - FILE_READ_ATTRIBUTES = 0x0080 - FILE_WRITE_ATTRIBUTES = 0x0100 - DELETE = 0x00010000 - READ_CONTROL = 0x00020000 - WRITE_DAC = 0x00040000 - WRITE_OWNER = 0x00080000 - SYNCHRONIZE = 0x00100000 - STANDARD_RIGHTS_ALL = 0x001F0000 - - filemask = ldm & STANDARD_RIGHTS_ALL - - if (ldm & RIGHT_DS_READ_PROPERTY) and (ldm & RIGHT_DS_LIST_CONTENTS): - filemask = filemask | (SYNCHRONIZE | FILE_LIST_DIRECTORY |\ - FILE_READ_ATTRIBUTES | FILE_READ_EA |\ - FILE_READ_DATA | FILE_EXECUTE) - - if ldm & RIGHT_DS_WRITE_PROPERTY: - filemask = filemask | (SYNCHRONIZE | FILE_WRITE_DATA |\ - FILE_APPEND_DATA | FILE_WRITE_EA |\ - FILE_WRITE_ATTRIBUTES | FILE_ADD_FILE |\ - FILE_ADD_SUBDIRECTORY) - - if ldm & RIGHT_DS_CREATE_CHILD: - filemask = filemask | (FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE) - - if ldm & RIGHT_DS_DELETE_CHILD: - filemask = filemask | FILE_DELETE_CHILD - - return filemask - - -def dsacl2fsacl(dssddl, domsid): - """ - - This function takes an the SDDL representation of a DS - ACL and return the SDDL representation of this ACL adapted - for files. It's used for Policy object provision - """ - sid = security.dom_sid(domsid) - ref = security.descriptor.from_sddl(dssddl, sid) - fdescr = security.descriptor() - fdescr.owner_sid = ref.owner_sid - fdescr.group_sid = ref.group_sid - fdescr.type = ref.type - fdescr.revision = ref.revision - fdescr.sacl = ref.sacl - aces = ref.dacl.aces - for i in range(0, len(aces)): - ace = aces[i] - if not ace.type & security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT and str(ace.trustee) != security.SID_BUILTIN_PREW2K: - # if fdescr.type & security.SEC_DESC_DACL_AUTO_INHERITED: - ace.flags = ace.flags | security.SEC_ACE_FLAG_OBJECT_INHERIT | security.SEC_ACE_FLAG_CONTAINER_INHERIT - if str(ace.trustee) == security.SID_CREATOR_OWNER: - # For Creator/Owner the IO flag is set as this ACE has only a sense for child objects - ace.flags = ace.flags | security.SEC_ACE_FLAG_INHERIT_ONLY - ace.access_mask = ldapmask2filemask(ace.access_mask) - fdescr.dacl_add(ace) - - return fdescr.as_sddl(sid) diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py new file mode 100644 index 0000000000..64491c2b18 --- /dev/null +++ b/source4/scripting/python/samba/provision.py @@ -0,0 +1,2067 @@ +# +# 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-2009 +# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009 +# +# Based on the original in EJS: +# 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/>. +# + +"""Functions for setting up a Samba configuration.""" + +from base64 import b64encode +import os +import sys +import pwd +import grp +import time +import uuid, glue +import socket +import param +import registry +import samba +import subprocess +import ldb + +import shutil +from credentials import Credentials, DONT_USE_KERBEROS +from auth import system_session, admin_session +from samba import version, Ldb, substitute_var, valid_netbios_name +from samba import check_all_substituted +from samba import DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008, DS_DC_FUNCTION_2008_R2 +from samba.samdb import SamDB +from samba.idmap import IDmapDB +from samba.dcerpc import security +from samba.ndr import ndr_pack +import urllib +from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring +from ms_schema import read_ms_schema +from ms_display_specifiers import read_ms_ldif +from signal import SIGTERM +from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA + +__docformat__ = "restructuredText" + +def find_setup_dir(): + """Find the setup directory used by provision.""" + dirname = os.path.dirname(__file__) + if "/site-packages/" in dirname: + prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2]) + for suffix in ["share/setup", "share/samba/setup", "setup"]: + ret = os.path.join(prefix, suffix) + if os.path.isdir(ret): + return ret + # In source tree + ret = os.path.join(dirname, "../../../setup") + if os.path.isdir(ret): + return ret + raise Exception("Unable to find setup directory.") + +def get_schema_descriptor(domain_sid): + sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \ + "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ + "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \ + "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \ + "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \ + "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)" + sec = security.descriptor.from_sddl(sddl, domain_sid) + return b64encode(ndr_pack(sec)) + +def get_config_descriptor(domain_sid): + sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \ + "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ + "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ + "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ + "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \ + "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \ + "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)" + sec = security.descriptor.from_sddl(sddl, domain_sid) + return b64encode(ndr_pack(sec)) + + +DEFAULTSITE = "Default-First-Site-Name" + +# Exception classes + +class ProvisioningError(Exception): + """A generic provision error.""" + +class InvalidNetbiosName(Exception): + """A specified name was not a valid NetBIOS name.""" + def __init__(self, name): + super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name) + + +class ProvisionPaths(object): + def __init__(self): + self.shareconf = None + self.hklm = None + self.hkcu = None + self.hkcr = None + self.hku = None + self.hkpd = None + self.hkpt = None + self.samdb = None + self.idmapdb = None + self.secrets = None + self.keytab = None + self.dns_keytab = None + self.dns = None + self.winsdb = None + self.private_dir = None + self.ldapdir = None + self.slapdconf = None + self.modulesconf = None + self.memberofconf = None + self.fedoradsinf = None + self.fedoradspartitions = None + self.fedoradssasl = None + self.olmmron = None + self.olmmrserveridsconf = None + self.olmmrsyncreplconf = None + self.olcdir = None + self.olslapd = None + self.olcseedldif = None + + +class ProvisionNames(object): + def __init__(self): + self.rootdn = None + self.domaindn = None + self.configdn = None + self.schemadn = None + self.sambadn = None + self.ldapmanagerdn = None + self.dnsdomain = None + self.realm = None + self.netbiosname = None + self.domain = None + self.hostname = None + self.sitename = None + self.smbconf = None + + +class ProvisionResult(object): + def __init__(self): + self.paths = None + self.domaindn = None + self.lp = None + self.samdb = None + +class Schema(object): + def __init__(self, setup_path, domain_sid, schemadn=None, + serverdn=None, sambadn=None, ldap_backend_type=None): + """Load schema for the SamDB from the AD schema files and samba4_schema.ldif + + :param samdb: Load a schema into a SamDB. + :param setup_path: Setup path function. + :param schemadn: DN of the schema + :param serverdn: DN of the server + + Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db + """ + + self.ldb = Ldb() + self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'), + setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt')) + self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read() + self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn}) + check_all_substituted(self.schema_data) + + self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"), + {"SCHEMADN": schemadn, + "SERVERDN": serverdn, + }) + + descr = get_schema_descriptor(domain_sid) + self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"), + {"SCHEMADN": schemadn, + "DESCRIPTOR": descr + }) + + prefixmap = open(setup_path("prefixMap.txt"), 'r').read() + prefixmap = b64encode(prefixmap) + + # We don't actually add this ldif, just parse it + prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap + self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data) + + +# Return a hash with the forward attribute as a key and the back as the value +def get_linked_attributes(schemadn,schemaldb): + attrs = ["linkID", "lDAPDisplayName"] + res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) + attributes = {} + for i in range (0, len(res)): + expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1) + target = schemaldb.searchone(basedn=schemadn, + expression=expression, + attribute="lDAPDisplayName", + scope=SCOPE_SUBTREE) + if target is not None: + attributes[str(res[i]["lDAPDisplayName"])]=str(target) + + return attributes + +def get_dnsyntax_attributes(schemadn,schemaldb): + attrs = ["linkID", "lDAPDisplayName"] + res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) + attributes = [] + for i in range (0, len(res)): + attributes.append(str(res[i]["lDAPDisplayName"])) + + return attributes + + +def check_install(lp, session_info, credentials): + """Check whether the current install seems ok. + + :param lp: Loadparm context + :param session_info: Session information + :param credentials: Credentials + """ + if lp.get("realm") == "": + raise Exception("Realm empty") + ldb = Ldb(lp.get("sam database"), session_info=session_info, + credentials=credentials, lp=lp) + if len(ldb.search("(cn=Administrator)")) != 1: + raise ProvisioningError("No administrator account found") + + +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 KeyError("Unable to find user/group %r" % names) + + +findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2] +findnss_gid = lambda names: findnss(grp.getgrnam, names)[2] + + +def read_and_sub_file(file, subst_vars): + """Read a file and sub in variables found in it + + :param file: File to be read (typically from setup directory) + param subst_vars: Optional variables to subsitute in the file. + """ + data = open(file, 'r').read() + if subst_vars is not None: + data = substitute_var(data, subst_vars) + check_all_substituted(data) + return data + + +def setup_add_ldif(ldb, ldif_path, subst_vars=None): + """Setup a ldb in the private dir. + + :param ldb: LDB file to import data into + :param ldif_path: Path of the LDIF file to load + :param subst_vars: Optional variables to subsitute in LDIF. + """ + assert isinstance(ldif_path, str) + + data = read_and_sub_file(ldif_path, subst_vars) + ldb.add_ldif(data) + + +def setup_modify_ldif(ldb, ldif_path, subst_vars=None): + """Modify a ldb in the private dir. + + :param ldb: LDB object. + :param ldif_path: LDIF file path. + :param subst_vars: Optional dictionary with substitution variables. + """ + data = read_and_sub_file(ldif_path, subst_vars) + + 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: + setup_add_ldif(ldb, ldif_path, subst_vars) + except: + ldb.transaction_cancel() + raise + ldb.transaction_commit() + + +def setup_file(template, fname, subst_vars): + """Setup a file in the private dir. + + :param template: Path of the template file. + :param fname: Path of the file to create. + :param subst_vars: Substitution variables. + """ + f = fname + + if os.path.exists(f): + os.unlink(f) + + data = read_and_sub_file(template, subst_vars) + open(f, 'w').write(data) + + +def provision_paths_from_lp(lp, dnsdomain): + """Set the default paths for provisioning. + + :param lp: Loadparm context. + :param dnsdomain: DNS Domain name + """ + paths = ProvisionPaths() + paths.private_dir = lp.get("private dir") + paths.dns_keytab = "dns.keytab" + + paths.shareconf = os.path.join(paths.private_dir, "share.ldb") + paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb") + paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb") + paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb") + paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone") + paths.namedconf = os.path.join(paths.private_dir, "named.conf") + paths.namedtxt = os.path.join(paths.private_dir, "named.txt") + paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf") + paths.winsdb = os.path.join(paths.private_dir, "wins.ldb") + paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi") + paths.phpldapadminconfig = os.path.join(paths.private_dir, + "phpldapadmin-config.php") + paths.ldapdir = os.path.join(paths.private_dir, + "ldap") + paths.slapdconf = os.path.join(paths.ldapdir, + "slapd.conf") + paths.slapdpid = os.path.join(paths.ldapdir, + "slapd.pid") + paths.modulesconf = os.path.join(paths.ldapdir, + "modules.conf") + paths.memberofconf = os.path.join(paths.ldapdir, + "memberof.conf") + paths.fedoradsinf = os.path.join(paths.ldapdir, + "fedorads.inf") + paths.fedoradspartitions = os.path.join(paths.ldapdir, + "fedorads-partitions.ldif") + paths.fedoradssasl = os.path.join(paths.ldapdir, + "fedorads-sasl.ldif") + paths.fedoradssamba = os.path.join(paths.ldapdir, + "fedorads-samba.ldif") + paths.olmmrserveridsconf = os.path.join(paths.ldapdir, + "mmr_serverids.conf") + paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, + "mmr_syncrepl.conf") + paths.olcdir = os.path.join(paths.ldapdir, + "slapd.d") + paths.olcseedldif = os.path.join(paths.ldapdir, + "olc_seed.ldif") + 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("path", "sysvol") + + paths.netlogon = lp.get("path", "netlogon") + + paths.smbconf = lp.configfile + + return paths + + +def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, + serverrole=None, rootdn=None, domaindn=None, configdn=None, + schemadn=None, serverdn=None, sitename=None, sambadn=None): + """Guess configuration settings to use.""" + + if hostname is None: + hostname = socket.gethostname().split(".")[0].lower() + + netbiosname = hostname.upper() + if not valid_netbios_name(netbiosname): + raise InvalidNetbiosName(netbiosname) + + hostname = hostname.lower() + + if dnsdomain is None: + dnsdomain = lp.get("realm") + + if serverrole is None: + serverrole = lp.get("server role") + + assert dnsdomain is not None + realm = dnsdomain.upper() + + if lp.get("realm").upper() != realm: + raise Exception("realm '%s' in %s must match chosen realm '%s'" % + (lp.get("realm"), lp.configfile, realm)) + + dnsdomain = dnsdomain.lower() + + if serverrole == "domain controller": + if domain is None: + domain = lp.get("workgroup") + if domaindn is None: + domaindn = "DC=" + dnsdomain.replace(".", ",DC=") + if lp.get("workgroup").upper() != domain.upper(): + raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'", + lp.get("workgroup"), domain) + else: + domain = netbiosname + if domaindn is None: + domaindn = "CN=" + netbiosname + + assert domain is not None + domain = domain.upper() + if not valid_netbios_name(domain): + raise InvalidNetbiosName(domain) + + if netbiosname.upper() == realm.upper(): + raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname) + + if hostname.upper() == realm.upper(): + raise Exception("realm %s must not be equal to hostname %s", realm, hostname) + + if domain.upper() == realm.upper(): + raise Exception("realm %s must not be equal to domain name %s", realm, domain) + + if rootdn is None: + rootdn = domaindn + + if configdn is None: + configdn = "CN=Configuration," + rootdn + if schemadn is None: + schemadn = "CN=Schema," + configdn + if sambadn is None: + sambadn = "CN=Samba" + + if sitename is None: + sitename=DEFAULTSITE + + names = ProvisionNames() + names.rootdn = rootdn + names.domaindn = domaindn + names.configdn = configdn + names.schemadn = schemadn + names.sambadn = sambadn + names.ldapmanagerdn = "CN=Manager," + rootdn + names.dnsdomain = dnsdomain + names.domain = domain + names.realm = realm + names.netbiosname = netbiosname + names.hostname = hostname + names.sitename = sitename + names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn) + + return names + + +def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, + targetdir): + """Create a new smb.conf file based on a couple of basic settings. + """ + assert smbconf is not None + if hostname is None: + hostname = socket.gethostname().split(".")[0].lower() + + if serverrole is None: + serverrole = "standalone" + + assert serverrole in ("domain controller", "member server", "standalone") + if serverrole == "domain controller": + smbconfsuffix = "dc" + elif serverrole == "member server": + smbconfsuffix = "member" + elif serverrole == "standalone": + smbconfsuffix = "standalone" + + assert domain is not None + assert realm is not None + + default_lp = param.LoadParm() + #Load non-existant file + if os.path.exists(smbconf): + default_lp.load(smbconf) + + if targetdir is not None: + privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private")) + lockdir_line = "lock dir = " + os.path.abspath(targetdir) + + default_lp.set("lock dir", os.path.abspath(targetdir)) + else: + privatedir_line = "" + lockdir_line = "" + + sysvol = os.path.join(default_lp.get("lock dir"), "sysvol") + netlogon = os.path.join(sysvol, realm.lower(), "scripts") + + setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), + smbconf, { + "HOSTNAME": hostname, + "DOMAIN": domain, + "REALM": realm, + "SERVERROLE": serverrole, + "NETLOGONPATH": netlogon, + "SYSVOLPATH": sysvol, + "PRIVATEDIR_LINE": privatedir_line, + "LOCKDIR_LINE": lockdir_line + }) + + +def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid, + users_gid, wheel_gid): + """setup reasonable name mappings for sam names to unix names. + + :param samdb: SamDB object. + :param idmap: IDmap db object. + :param sid: The domain sid. + :param domaindn: The domain DN. + :param root_uid: uid of the UNIX root user. + :param nobody_uid: uid of the UNIX nobody user. + :param users_gid: gid of the UNIX users group. + :param wheel_gid: gid of the UNIX wheel group.""" + + idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid) + idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid) + + idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid) + idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid) + +def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, + credentials, names, + serverrole, ldap_backend=None, + erase=False): + """Setup the partitions for the SAM database. + + Alternatively, provision() may call this, and then populate 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 + + # We use options=["modules:"] to stop the modules loading - we + # just want to wipe and re-initialise the database, not start it up + + try: + samdb = Ldb(url=samdb_path, session_info=session_info, + credentials=credentials, lp=lp, options=["modules:"]) + # Wipes the database + samdb.erase_except_schema_controlled() + except LdbError: + os.unlink(samdb_path) + samdb = Ldb(url=samdb_path, session_info=session_info, + credentials=credentials, lp=lp, options=["modules:"]) + # Wipes the database + samdb.erase_except_schema_controlled() + + + #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 = ["resolve_oids", + "rootdse", + "acl", + "paged_results", + "ranged_results", + "anr", + "server_sort", + "asq", + "extended_dn_store", + "extended_dn_in", + "rdn_name", + "objectclass", + "descriptor", + "samldb", + "password_hash", + "operational", + "kludge_acl"] + tdb_modules_list = [ + "subtree_rename", + "subtree_delete", + "linked_attributes", + "extended_dn_out_ldb"] + modules_list2 = ["show_deleted", + "partition"] + + domaindn_ldb = "users.ldb" + configdn_ldb = "configuration.ldb" + schemadn_ldb = "schema.ldb" + if ldap_backend is not None: + domaindn_ldb = ldap_backend.ldapi_uri + configdn_ldb = ldap_backend.ldapi_uri + schemadn_ldb = ldap_backend.ldapi_uri + + if ldap_backend.ldap_backend_type == "fedora-ds": + backend_modules = ["nsuniqueid", "paged_searches"] + # We can handle linked attributes here, as we don't have directory-side subtree operations + tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"] + elif ldap_backend.ldap_backend_type == "openldap": + backend_modules = ["entryuuid", "paged_searches"] + # OpenLDAP handles subtree renames, so we don't want to do any of these things + tdb_modules_list = ["extended_dn_out_dereference"] + + elif serverrole == "domain controller": + tdb_modules_list.insert(0, "repl_meta_data") + backend_modules = [] + else: + backend_modules = ["objectguid"] + + if tdb_modules_list is None: + tdb_modules_list_as_string = "" + else: + tdb_modules_list_as_string = ","+",".join(tdb_modules_list) + + samdb.transaction_start() + try: + message("Setting up sam.ldb partitions and settings") + setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), { + "SCHEMADN": names.schemadn, + "SCHEMADN_LDB": schemadn_ldb, + "SCHEMADN_MOD2": ",objectguid", + "CONFIGDN": names.configdn, + "CONFIGDN_LDB": configdn_ldb, + "DOMAINDN": names.domaindn, + "DOMAINDN_LDB": domaindn_ldb, + "SCHEMADN_MOD": "schema_fsmo,instancetype", + "CONFIGDN_MOD": "naming_fsmo,instancetype", + "DOMAINDN_MOD": "pdc_fsmo,instancetype", + "MODULES_LIST": ",".join(modules_list), + "TDB_MODULES_LIST": tdb_modules_list_as_string, + "MODULES_LIST2": ",".join(modules_list2), + "BACKEND_MOD": ",".join(backend_modules), + }) + + samdb.load_ldif_file_add(setup_path("provision_init.ldif")) + + message("Setting up sam.ldb rootDSE") + setup_samdb_rootdse(samdb, setup_path, names) + + except: + samdb.transaction_cancel() + raise + + samdb.transaction_commit() + +def secretsdb_self_join(secretsdb, domain, + netbiosname, domainsid, machinepass, + realm=None, dnsdomain=None, + keytab_path=None, + key_version_number=1, + secure_channel_type=SEC_CHAN_WKSTA): + """Add domain join-specific bits to a secrets database. + + :param secretsdb: Ldb Handle to the secrets database + :param machinepass: Machine password + """ + attrs=["whenChanged", + "secret", + "priorSecret", + "priorChanged", + "krb5Keytab", + "privateKeytab"] + + + msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain)); + msg["secureChannelType"] = str(secure_channel_type) + msg["flatname"] = [domain] + msg["objectClass"] = ["top", "primaryDomain"] + if realm is not None: + if dnsdomain is None: + dnsdomain = realm.lower() + msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"] + msg["realm"] = realm + msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper()) + msg["msDS-KeyVersionNumber"] = [str(key_version_number)] + msg["privateKeytab"] = ["secrets.keytab"]; + + + msg["secret"] = [machinepass] + msg["samAccountName"] = ["%s$" % netbiosname] + msg["secureChannelType"] = [str(secure_channel_type)] + msg["objectSid"] = [ndr_pack(domainsid)] + + res = secretsdb.search(base="cn=Primary Domains", + attrs=attrs, + expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), + scope=SCOPE_ONELEVEL) + + for del_msg in res: + if del_msg.dn is not msg.dn: + secretsdb.delete(del_msg.dn) + + res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE) + + if len(res) == 1: + msg["priorSecret"] = res[0]["secret"] + msg["priorWhenChanged"] = res[0]["whenChanged"] + + if res["privateKeytab"] is not None: + msg["privateKeytab"] = res[0]["privateKeytab"] + + if res["krb5Keytab"] is not None: + msg["krb5Keytab"] = res[0]["krb5Keytab"] + + for el in msg: + el.set_flags(ldb.FLAG_MOD_REPLACE) + secretsdb.modify(msg) + else: + secretsdb.add(msg) + + +def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, + dns_keytab_path, dnspass): + """Add DNS 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_dns.ldif"), { + "REALM": realm, + "DNSDOMAIN": dnsdomain, + "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.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.transaction_start() + secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif")) + + if credentials is not None and credentials.authentication_requested(): + if credentials.get_bind_dn() is not None: + setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), { + "LDAPMANAGERDN": credentials.get_bind_dn(), + "LDAPMANAGERPASS_B64": b64encode(credentials.get_password()) + }) + else: + setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), { + "LDAPADMINUSER": credentials.get_username(), + "LDAPADMINREALM": credentials.get_realm(), + "LDAPADMINPASS_B64": b64encode(credentials.get_password()) + }) + + return secrets_ldb + +def setup_registry(path, setup_path, session_info, 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, + lp_ctx=lp) + reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE) + provision_reg = setup_path("provision.reg") + assert os.path.exists(provision_reg) + reg.diff_apply(provision_reg) + + +def setup_idmapdb(path, setup_path, session_info, lp): + """Setup the idmap database. + + :param path: path to the idmap database + :param setup_path: Function that returns a path to a setup file + :param session_info: Session information + :param credentials: Credentials + :param lp: Loadparm context + """ + if os.path.exists(path): + os.unlink(path) + + idmap_ldb = IDmapDB(path, session_info=session_info, + lp=lp) + + idmap_ldb.erase() + idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif")) + return idmap_ldb + + +def setup_samdb_rootdse(samdb, setup_path, names): + """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": names.schemadn, + "NETBIOSNAME": names.netbiosname, + "DNSDOMAIN": names.dnsdomain, + "REALM": names.realm, + "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain), + "DOMAINDN": names.domaindn, + "ROOTDN": names.rootdn, + "CONFIGDN": names.configdn, + "SERVERDN": names.serverdn, + }) + + +def setup_self_join(samdb, names, + machinepass, dnspass, + domainsid, invocationid, setup_path, + policyguid, policyguid_dc, domainControllerFunctionality): + """Join a host to its own domain.""" + assert isinstance(invocationid, str) + setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { + "CONFIGDN": names.configdn, + "SCHEMADN": names.schemadn, + "DOMAINDN": names.domaindn, + "SERVERDN": names.serverdn, + "INVOCATIONID": invocationid, + "NETBIOSNAME": names.netbiosname, + "DEFAULTSITE": names.sitename, + "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain), + "MACHINEPASS_B64": b64encode(machinepass), + "DNSPASS_B64": b64encode(dnspass), + "REALM": names.realm, + "DOMAIN": names.domain, + "DNSDOMAIN": names.dnsdomain, + "SAMBA_VERSION_STRING": version, + "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)}) + + setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { + "POLICYGUID": policyguid, + "POLICYGUID_DC": policyguid_dc, + "DNSDOMAIN": names.dnsdomain, + "DOMAINSID": str(domainsid), + "DOMAINDN": names.domaindn}) + + # add the NTDSGUID based SPNs + ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn) + names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID", + expression="", scope=SCOPE_BASE) + assert isinstance(names.ntdsguid, str) + + # Setup fSMORoleOwner entries to point at the newly created DC entry + setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), { + "DOMAIN": names.domain, + "DNSDOMAIN": names.dnsdomain, + "DOMAINDN": names.domaindn, + "CONFIGDN": names.configdn, + "SCHEMADN": names.schemadn, + "DEFAULTSITE": names.sitename, + "SERVERDN": names.serverdn, + "NETBIOSNAME": names.netbiosname, + "NTDSGUID": names.ntdsguid + }) + + +def setup_samdb(path, setup_path, session_info, credentials, lp, + names, message, + domainsid, domainguid, policyguid, policyguid_dc, + fill, adminpass, krbtgtpass, + machinepass, invocationid, dnspass, + serverrole, schema=None, ldap_backend=None): + """Setup a complete SAM Database. + + :note: This will wipe the main SAM database file! + """ + + # Do NOT change these default values without discussion with the team and reslease manager. + domainFunctionality = DS_DOMAIN_FUNCTION_2008 + forestFunctionality = DS_DOMAIN_FUNCTION_2008 + domainControllerFunctionality = DS_DC_FUNCTION_2008 + + # Also wipes the database + setup_samdb_partitions(path, setup_path, message=message, lp=lp, + credentials=credentials, session_info=session_info, + names=names, + ldap_backend=ldap_backend, serverrole=serverrole) + + if (schema == None): + schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn, + sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type) + + # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema + samdb = Ldb(session_info=session_info, + credentials=credentials, lp=lp) + + message("Pre-loading the Samba 4 and AD schema") + + # Load the schema from the one we computed earlier + samdb.set_schema_from_ldb(schema.ldb) + + # And now we can connect to the DB - the schema won't be loaded from the DB + samdb.connect(path) + + # Load @OPTIONS + samdb.load_ldif_file_add(setup_path("provision_options.ldif")) + + if fill == FILL_DRS: + return samdb + + samdb.transaction_start() + try: + message("Erasing data from partitions") + # Load the schema (again). This time it will force a reindex, + # and will therefore make the erase_partitions() below + # computationally sane + samdb.set_schema_from_ldb(schema.ldb) + samdb.erase_partitions() + + # Set the domain functionality levels onto the database. + # Various module (the password_hash module in particular) need + # to know what level of AD we are emulating. + + # These will be fixed into the database via the database + # modifictions below, but we need them set from the start. + samdb.set_opaque_integer("domainFunctionality", domainFunctionality) + samdb.set_opaque_integer("forestFunctionality", forestFunctionality) + samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality) + + samdb.set_domain_sid(str(domainsid)) + if serverrole == "domain controller": + samdb.set_invocation_id(invocationid) + + message("Adding DomainDN: %s" % names.domaindn) + if serverrole == "domain controller": + domain_oc = "domainDNS" + else: + domain_oc = "samba4LocalDomain" + +#impersonate domain admin + admin_session_info = admin_session(lp, str(domainsid)) + samdb.set_session_info(admin_session_info) + + setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), { + "DOMAINDN": names.domaindn, + "DOMAIN_OC": domain_oc + }) + + message("Modifying DomainDN: " + names.domaindn + "") + if domainguid is not None: + domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid + else: + domainguid_mod = "" + + setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), { + "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks + "DOMAINSID": str(domainsid), + "SCHEMADN": names.schemadn, + "NETBIOSNAME": names.netbiosname, + "DEFAULTSITE": names.sitename, + "CONFIGDN": names.configdn, + "SERVERDN": names.serverdn, + "POLICYGUID": policyguid, + "DOMAINDN": names.domaindn, + "DOMAINGUID_MOD": domainguid_mod, + "DOMAIN_FUNCTIONALITY": str(domainFunctionality), + "SAMBA_VERSION_STRING": version + }) + + message("Adding configuration container") + descr = get_config_descriptor(domainsid); + setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), { + "CONFIGDN": names.configdn, + "DESCRIPTOR": descr, + }) + message("Modifying configuration container") + setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), { + "CONFIGDN": names.configdn, + "SCHEMADN": names.schemadn, + }) + + # The LDIF here was created when the Schema object was constructed + message("Setting up sam.ldb schema") + samdb.add_ldif(schema.schema_dn_add) + samdb.modify_ldif(schema.schema_dn_modify) + samdb.write_prefixes_from_schema() + samdb.add_ldif(schema.schema_data) + setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), + {"SCHEMADN": names.schemadn}) + + message("Setting up sam.ldb configuration data") + setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), { + "CONFIGDN": names.configdn, + "NETBIOSNAME": names.netbiosname, + "DEFAULTSITE": names.sitename, + "DNSDOMAIN": names.dnsdomain, + "DOMAIN": names.domain, + "SCHEMADN": names.schemadn, + "DOMAINDN": names.domaindn, + "SERVERDN": names.serverdn, + "FOREST_FUNCTIONALALITY": str(forestFunctionality) + }) + + message("Setting up display specifiers") + display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt')) + display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn}) + check_all_substituted(display_specifiers_ldif) + samdb.add_ldif(display_specifiers_ldif) + + message("Adding users container") + setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), { + "DOMAINDN": names.domaindn}) + message("Modifying users container") + setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), { + "DOMAINDN": names.domaindn}) + message("Adding computers container") + setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), { + "DOMAINDN": names.domaindn}) + message("Modifying computers container") + setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), { + "DOMAINDN": names.domaindn}) + message("Setting up sam.ldb data") + setup_add_ldif(samdb, setup_path("provision.ldif"), { + "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks + "DOMAINDN": names.domaindn, + "NETBIOSNAME": names.netbiosname, + "DEFAULTSITE": names.sitename, + "CONFIGDN": names.configdn, + "SERVERDN": names.serverdn, + "POLICYGUID_DC": policyguid_dc + }) + + if fill == FILL_FULL: + message("Setting up sam.ldb users and groups") + setup_add_ldif(samdb, setup_path("provision_users.ldif"), { + "DOMAINDN": names.domaindn, + "DOMAINSID": str(domainsid), + "CONFIGDN": names.configdn, + "ADMINPASS_B64": b64encode(adminpass), + "KRBTGTPASS_B64": b64encode(krbtgtpass), + }) + + if serverrole == "domain controller": + message("Setting up self join") + setup_self_join(samdb, names=names, invocationid=invocationid, + dnspass=dnspass, + machinepass=machinepass, + domainsid=domainsid, policyguid=policyguid, + policyguid_dc=policyguid_dc, + setup_path=setup_path, + domainControllerFunctionality=domainControllerFunctionality) + + ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn) + names.ntdsguid = samdb.searchone(basedn=ntds_dn, + attribute="objectGUID", expression="", scope=SCOPE_BASE) + assert isinstance(names.ntdsguid, str) + + except: + samdb.transaction_cancel() + raise + + samdb.transaction_commit() + return samdb + + +FILL_FULL = "FULL" +FILL_NT4SYNC = "NT4SYNC" +FILL_DRS = "DRS" + + +def provision(setup_dir, message, session_info, + credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, + realm=None, + rootdn=None, domaindn=None, schemadn=None, configdn=None, + serverdn=None, + domain=None, hostname=None, hostip=None, hostip6=None, + domainsid=None, adminpass=None, ldapadminpass=None, + krbtgtpass=None, domainguid=None, + policyguid=None, policyguid_dc=None, invocationid=None, + machinepass=None, + dnspass=None, root=None, nobody=None, users=None, + wheel=None, backup=None, aci=None, serverrole=None, + ldap_backend_extra_port=None, ldap_backend_type=None, + sitename=None, + ol_mmr_urls=None, ol_olc=None, + setup_ds_path=None, slapd_path=None, nosync=False, + ldap_dryrun_mode=False): + """Provision samba4 + + :note: caution, this wipes all existing data! + """ + + def setup_path(file): + return os.path.join(setup_dir, file) + + if domainsid is None: + domainsid = security.random_sid() + else: + domainsid = security.dom_sid(domainsid) + + + # create/adapt the group policy GUIDs + if policyguid is None: + policyguid = str(uuid.uuid4()) + policyguid = policyguid.upper() + if policyguid_dc is None: + policyguid_dc = str(uuid.uuid4()) + policyguid_dc = policyguid_dc.upper() + + if adminpass is None: + adminpass = glue.generate_random_str(12) + if krbtgtpass is None: + krbtgtpass = glue.generate_random_str(12) + if machinepass is None: + machinepass = glue.generate_random_str(12) + if dnspass is None: + dnspass = glue.generate_random_str(12) + if ldapadminpass is None: + #Make a new, random password between Samba and it's LDAP server + ldapadminpass=glue.generate_random_str(12) + + + root_uid = findnss_uid([root or "root"]) + nobody_uid = findnss_uid([nobody or "nobody"]) + users_gid = findnss_gid([users or "users"]) + if wheel is None: + wheel_gid = findnss_gid(["wheel", "adm"]) + else: + wheel_gid = findnss_gid([wheel]) + + if targetdir is not None: + if (not os.path.exists(os.path.join(targetdir, "etc"))): + os.makedirs(os.path.join(targetdir, "etc")) + smbconf = os.path.join(targetdir, "etc", "smb.conf") + elif smbconf is None: + smbconf = param.default_path() + + # only install a new smb.conf if there isn't one there already + if not os.path.exists(smbconf): + make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, + targetdir) + + lp = param.LoadParm() + lp.load(smbconf) + + names = guess_names(lp=lp, hostname=hostname, domain=domain, + dnsdomain=realm, serverrole=serverrole, sitename=sitename, + rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn, + serverdn=serverdn) + + paths = provision_paths_from_lp(lp, names.dnsdomain) + + if hostip is None: + try: + hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0] + except socket.gaierror, (socket.EAI_NODATA, msg): + hostip = None + + if hostip6 is None: + try: + hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0] + except socket.gaierror, (socket.EAI_NODATA, msg): + hostip6 = None + + if serverrole is None: + serverrole = lp.get("server role") + + assert serverrole in ("domain controller", "member server", "standalone") + if invocationid is None and serverrole == "domain controller": + invocationid = str(uuid.uuid4()) + + if not os.path.exists(paths.private_dir): + os.mkdir(paths.private_dir) + + ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="") + + schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn, + sambadn=names.sambadn, ldap_backend_type=ldap_backend_type) + + secrets_credentials = credentials + provision_backend = None + if ldap_backend_type: + # We only support an LDAP backend over ldapi:// + + provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path, + lp=lp, credentials=credentials, + names=names, + message=message, hostname=hostname, + root=root, schema=schema, + ldap_backend_type=ldap_backend_type, + ldapadminpass=ldapadminpass, + ldap_backend_extra_port=ldap_backend_extra_port, + ol_mmr_urls=ol_mmr_urls, + slapd_path=slapd_path, + setup_ds_path=setup_ds_path, + ldap_dryrun_mode=ldap_dryrun_mode) + + # Now use the backend credentials to access the databases + credentials = provision_backend.credentials + secrets_credentials = provision_backend.adminCredentials + ldapi_url = provision_backend.ldapi_uri + + # only install a new shares config db if there is none + if not os.path.exists(paths.shareconf): + message("Setting up share.ldb") + share_ldb = Ldb(paths.shareconf, session_info=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, + credentials=secrets_credentials, lp=lp) + + message("Setting up the registry") + setup_registry(paths.hklm, setup_path, session_info, + lp=lp) + + message("Setting up idmap db") + idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info, + lp=lp) + + message("Setting up SAM db") + samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, + credentials=credentials, lp=lp, names=names, + message=message, + domainsid=domainsid, + schema=schema, domainguid=domainguid, + policyguid=policyguid, policyguid_dc=policyguid_dc, + fill=samdb_fill, + adminpass=adminpass, krbtgtpass=krbtgtpass, + invocationid=invocationid, + machinepass=machinepass, dnspass=dnspass, + serverrole=serverrole, ldap_backend=provision_backend) + + if serverrole == "domain controller": + if paths.netlogon is None: + message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.") + message("Please either remove %s or see the template at %s" % + ( paths.smbconf, setup_path("provision.smb.conf.dc"))) + assert(paths.netlogon is not None) + + if paths.sysvol is None: + message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.") + message("Please either remove %s or see the template at %s" % + (paths.smbconf, setup_path("provision.smb.conf.dc"))) + assert(paths.sysvol is not None) + + # Set up group policies (domain policy and domain controller policy) + + policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", + "{" + policyguid + "}") + os.makedirs(policy_path, 0755) + open(os.path.join(policy_path, "GPT.INI"), 'w').write( + "[General]\r\nVersion=65543") + os.makedirs(os.path.join(policy_path, "MACHINE"), 0755) + os.makedirs(os.path.join(policy_path, "USER"), 0755) + + policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies", + "{" + policyguid_dc + "}") + os.makedirs(policy_path_dc, 0755) + open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write( + "[General]\r\nVersion=2") + os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755) + os.makedirs(os.path.join(policy_path_dc, "USER"), 0755) + + if not os.path.isdir(paths.netlogon): + os.makedirs(paths.netlogon, 0755) + + if samdb_fill == FILL_FULL: + setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn, + root_uid=root_uid, nobody_uid=nobody_uid, + users_gid=users_gid, wheel_gid=wheel_gid) + + message("Setting up sam.ldb rootDSE marking as synchronized") + setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif")) + + # Only make a zone file on the first DC, it should be replicated with DNS replication + if serverrole == "domain controller": + secretsdb_self_join(secrets_ldb, domain=domain, + realm=names.realm, + dnsdomain=names.dnsdomain, + netbiosname=names.netbiosname, + domainsid=domainsid, + machinepass=machinepass, + secure_channel_type=SEC_CHAN_BDC) + + secretsdb_setup_dns(secrets_ldb, setup_path, + realm=names.realm, dnsdomain=names.dnsdomain, + dns_keytab_path=paths.dns_keytab, + dnspass=dnspass) + + domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID") + assert isinstance(domainguid, str) + + create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain, + domaindn=names.domaindn, hostip=hostip, + hostip6=hostip6, hostname=names.hostname, + dnspass=dnspass, realm=names.realm, + domainguid=domainguid, ntdsguid=names.ntdsguid) + + create_named_conf(paths.namedconf, setup_path, realm=names.realm, + dnsdomain=names.dnsdomain, private_dir=paths.private_dir) + + create_named_txt(paths.namedtxt, setup_path, realm=names.realm, + dnsdomain=names.dnsdomain, private_dir=paths.private_dir, + keytab_name=paths.dns_keytab) + message("See %s for an example configuration include file for BIND" % paths.namedconf) + message("and %s for further documentation required for secure DNS updates" % paths.namedtxt) + + create_krb5_conf(paths.krb5conf, setup_path, + dnsdomain=names.dnsdomain, hostname=names.hostname, + realm=names.realm) + message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf) + + #Now commit the secrets.ldb to disk + secrets_ldb.transaction_commit() + + if provision_backend is not None: + if ldap_backend_type == "fedora-ds": + ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials) + + # delete default SASL mappings + res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"]) + + # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket) + for i in range (0, len(res)): + dn = str(res[i]["dn"]) + ldapi_db.delete(dn) + + aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn + + m = ldb.Message() + m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci") + + m.dn = ldb.Dn(1, names.domaindn) + ldapi_db.modify(m) + + m.dn = ldb.Dn(1, names.configdn) + ldapi_db.modify(m) + + m.dn = ldb.Dn(1, names.schemadn) + ldapi_db.modify(m) + + # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination + if provision_backend.slapd.poll() is None: + #Kill the slapd + if hasattr(provision_backend.slapd, "terminate"): + provision_backend.slapd.terminate() + else: + # Older python versions don't have .terminate() + import signal + os.kill(provision_backend.slapd.pid, signal.SIGTERM) + + #and now wait for it to die + provision_backend.slapd.communicate() + + # now display slapd_command_file.txt to show how slapd must be started next time + message("Use later the following commandline to start slapd, then Samba:") + slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'" + message(slapd_command) + message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh") + + setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", { + "SLAPD_COMMAND" : slapd_command}) + + + 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) + + message("Once the above files are installed, your Samba4 server will be ready to use") + message("Server Role: %s" % serverrole) + message("Hostname: %s" % names.hostname) + message("NetBIOS Domain: %s" % names.domain) + message("DNS Domain: %s" % names.dnsdomain) + message("DOMAIN SID: %s" % str(domainsid)) + if samdb_fill == FILL_FULL: + message("Admin password: %s" % adminpass) + if provision_backend: + if provision_backend.credentials.get_bind_dn() is not None: + message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn()) + else: + message("LDAP Admin User: %s" % provision_backend.credentials.get_username()) + + message("LDAP Admin Password: %s" % provision_backend.credentials.get_password()) + + result = ProvisionResult() + result.domaindn = domaindn + result.paths = paths + result.lp = lp + result.samdb = samdb + return result + + + +def provision_become_dc(setup_dir=None, + smbconf=None, targetdir=None, realm=None, + rootdn=None, domaindn=None, schemadn=None, + configdn=None, serverdn=None, + domain=None, hostname=None, domainsid=None, + adminpass=None, krbtgtpass=None, domainguid=None, + policyguid=None, policyguid_dc=None, invocationid=None, + machinepass=None, + dnspass=None, root=None, nobody=None, users=None, + wheel=None, backup=None, serverrole=None, + ldap_backend=None, ldap_backend_type=None, + sitename=None, debuglevel=1): + + def message(text): + """print a message if quiet is not set.""" + print text + + glue.set_debug_level(debuglevel) + + return provision(setup_dir, message, system_session(), None, + smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, + realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, + configdn=configdn, serverdn=serverdn, domain=domain, + hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, + machinepass=machinepass, serverrole="domain controller", + sitename=sitename) + + +def setup_db_config(setup_path, dbdir): + """Setup a Berkeley database. + + :param setup_path: Setup path function. + :param dbdir: Database directory.""" + if not os.path.isdir(os.path.join(dbdir, "bdb-logs")): + os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700) + if not os.path.isdir(os.path.join(dbdir, "tmp")): + os.makedirs(os.path.join(dbdir, "tmp"), 0700) + + setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"), + {"LDAPDBDIR": dbdir}) + +class ProvisionBackend(object): + def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, + names=None, message=None, + hostname=None, root=None, + schema=None, ldapadminpass=None, + ldap_backend_type=None, ldap_backend_extra_port=None, + ol_mmr_urls=None, + setup_ds_path=None, slapd_path=None, + nosync=False, ldap_dryrun_mode=False): + """Provision an LDAP backend for samba4 + + This works for OpenLDAP and Fedora DS + """ + + self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="") + + if not os.path.isdir(paths.ldapdir): + os.makedirs(paths.ldapdir, 0700) + + if ldap_backend_type == "existing": + #Check to see that this 'existing' LDAP backend in fact exists + ldapi_db = Ldb(self.ldapi_uri, credentials=credentials) + search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE, + expression="(objectClass=OpenLDAProotDSE)") + + # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied + # This caused them to be set into the long-term database later in the script. + self.credentials = credentials + self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP + return + + # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri + # if another instance of slapd is already running + try: + ldapi_db = Ldb(self.ldapi_uri) + search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE, + expression="(objectClass=OpenLDAProotDSE)"); + try: + f = open(paths.slapdpid, "r") + p = f.read() + f.close() + message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.") + except: + pass + + raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ") + + except LdbError, e: + pass + + # Try to print helpful messages when the user has not specified the path to slapd + if slapd_path is None: + raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!") + if not os.path.exists(slapd_path): + message (slapd_path) + raise ProvisioningError("Warning: Given Path to slapd does not exist!") + + schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb") + try: + os.unlink(schemadb_path) + except OSError: + pass + + + # Put the LDIF of the schema into a database so we can search on + # it to generate schema-dependent configurations in Fedora DS and + # OpenLDAP + os.path.join(paths.ldapdir, "schema-tmp.ldb") + schema.ldb.connect(schemadb_path) + schema.ldb.transaction_start() + + # These bits of LDIF are supplied when the Schema object is created + schema.ldb.add_ldif(schema.schema_dn_add) + schema.ldb.modify_ldif(schema.schema_dn_modify) + schema.ldb.add_ldif(schema.schema_data) + schema.ldb.transaction_commit() + + self.credentials = Credentials() + self.credentials.guess(lp) + #Kerberos to an ldapi:// backend makes no sense + self.credentials.set_kerberos_state(DONT_USE_KERBEROS) + + self.adminCredentials = Credentials() + self.adminCredentials.guess(lp) + #Kerberos to an ldapi:// backend makes no sense + self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS) + + self.ldap_backend_type = ldap_backend_type + + if ldap_backend_type == "fedora-ds": + provision_fds_backend(self, paths=paths, setup_path=setup_path, + names=names, message=message, + hostname=hostname, + ldapadminpass=ldapadminpass, root=root, + schema=schema, + ldap_backend_extra_port=ldap_backend_extra_port, + setup_ds_path=setup_ds_path, + slapd_path=slapd_path, + nosync=nosync, + ldap_dryrun_mode=ldap_dryrun_mode) + + elif ldap_backend_type == "openldap": + provision_openldap_backend(self, paths=paths, setup_path=setup_path, + names=names, message=message, + hostname=hostname, + ldapadminpass=ldapadminpass, root=root, + schema=schema, + ldap_backend_extra_port=ldap_backend_extra_port, + ol_mmr_urls=ol_mmr_urls, + slapd_path=slapd_path, + nosync=nosync, + ldap_dryrun_mode=ldap_dryrun_mode) + else: + raise ProvisioningError("Unknown LDAP backend type selected") + + self.credentials.set_password(ldapadminpass) + self.adminCredentials.set_username("samba-admin") + self.adminCredentials.set_password(ldapadminpass) + + # Now start the slapd, so we can provision onto it. We keep the + # subprocess context around, to kill this off at the successful + # end of the script + self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False) + + while self.slapd.poll() is None: + # Wait until the socket appears + try: + ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials) + search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE, + expression="(objectClass=OpenLDAProotDSE)") + # If we have got here, then we must have a valid connection to the LDAP server! + return + except LdbError, e: + time.sleep(1) + pass + + raise ProvisioningError("slapd died before we could make a connection to it") + + +def provision_openldap_backend(result, paths=None, setup_path=None, names=None, + message=None, + hostname=None, ldapadminpass=None, root=None, + schema=None, + ldap_backend_extra_port=None, + ol_mmr_urls=None, + slapd_path=None, nosync=False, + ldap_dryrun_mode=False): + + #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB + nosync_config = "" + if nosync: + nosync_config = "dbnosync" + + lnkattr = get_linked_attributes(names.schemadn,schema.ldb) + refint_attributes = "" + memberof_config = "# Generated from Samba4 schema\n" + for att in lnkattr.keys(): + if lnkattr[att] is not None: + refint_attributes = refint_attributes + " " + att + + memberof_config += read_and_sub_file(setup_path("memberof.conf"), + { "MEMBER_ATTR" : att , + "MEMBEROF_ATTR" : lnkattr[att] }) + + refint_config = read_and_sub_file(setup_path("refint.conf"), + { "LINK_ATTRS" : refint_attributes}) + + attrs = ["linkID", "lDAPDisplayName"] + res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) + index_config = "" + for i in range (0, len(res)): + index_attr = res[i]["lDAPDisplayName"][0] + if index_attr == "objectGUID": + index_attr = "entryUUID" + + index_config += "index " + index_attr + " eq\n" + +# generate serverids, ldap-urls and syncrepl-blocks for mmr hosts + mmr_on_config = "" + mmr_replicator_acl = "" + mmr_serverids_config = "" + mmr_syncrepl_schema_config = "" + mmr_syncrepl_config_config = "" + mmr_syncrepl_user_config = "" + + + if ol_mmr_urls is not None: + # For now, make these equal + mmr_pass = ldapadminpass + + url_list=filter(None,ol_mmr_urls.split(' ')) + if (len(url_list) == 1): + url_list=filter(None,ol_mmr_urls.split(',')) + + + mmr_on_config = "MirrorMode On" + mmr_replicator_acl = " by dn=cn=replicator,cn=samba read" + serverid=0 + for url in url_list: + serverid=serverid+1 + mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"), + { "SERVERID" : str(serverid), + "LDAPSERVER" : url }) + rid=serverid*10 + rid=rid+1 + mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"), + { "RID" : str(rid), + "MMRDN": names.schemadn, + "LDAPSERVER" : url, + "MMR_PASSWORD": mmr_pass}) + + rid=rid+1 + mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"), + { "RID" : str(rid), + "MMRDN": names.configdn, + "LDAPSERVER" : url, + "MMR_PASSWORD": mmr_pass}) + + rid=rid+1 + mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"), + { "RID" : str(rid), + "MMRDN": names.domaindn, + "LDAPSERVER" : url, + "MMR_PASSWORD": mmr_pass }) + # OpenLDAP cn=config initialisation + olc_syncrepl_config = "" + olc_mmr_config = "" + # if mmr = yes, generate cn=config-replication directives + # and olc_seed.lif for the other mmr-servers + if ol_mmr_urls is not None: + serverid=0 + olc_serverids_config = "" + olc_syncrepl_seed_config = "" + olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{}) + rid=1000 + for url in url_list: + serverid=serverid+1 + olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"), + { "SERVERID" : str(serverid), + "LDAPSERVER" : url }) + + rid=rid+1 + olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"), + { "RID" : str(rid), + "LDAPSERVER" : url, + "MMR_PASSWORD": mmr_pass}) + + olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"), + { "RID" : str(rid), + "LDAPSERVER" : url}) + + setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif, + {"OLC_SERVER_ID_CONF": olc_serverids_config, + "OLC_PW": ldapadminpass, + "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config}) + # end olc + + setup_file(setup_path("slapd.conf"), paths.slapdconf, + {"DNSDOMAIN": names.dnsdomain, + "LDAPDIR": paths.ldapdir, + "DOMAINDN": names.domaindn, + "CONFIGDN": names.configdn, + "SCHEMADN": names.schemadn, + "MEMBEROF_CONFIG": memberof_config, + "MIRRORMODE": mmr_on_config, + "REPLICATOR_ACL": mmr_replicator_acl, + "MMR_SERVERIDS_CONFIG": mmr_serverids_config, + "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config, + "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config, + "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config, + "OLC_SYNCREPL_CONFIG": olc_syncrepl_config, + "OLC_MMR_CONFIG": olc_mmr_config, + "REFINT_CONFIG": refint_config, + "INDEX_CONFIG": index_config, + "NOSYNC": nosync_config}) + + setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user")) + setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config")) + setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema")) + + if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")): + os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700) + + setup_file(setup_path("cn=samba.ldif"), + os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"), + { "UUID": str(uuid.uuid4()), + "LDAPTIME": timestring(int(time.time()))} ) + setup_file(setup_path("cn=samba-admin.ldif"), + os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"), + {"LDAPADMINPASS_B64": b64encode(ldapadminpass), + "UUID": str(uuid.uuid4()), + "LDAPTIME": timestring(int(time.time()))} ) + + if ol_mmr_urls is not None: + setup_file(setup_path("cn=replicator.ldif"), + os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"), + {"MMR_PASSWORD_B64": b64encode(mmr_pass), + "UUID": str(uuid.uuid4()), + "LDAPTIME": timestring(int(time.time()))} ) + + + mapping = "schema-map-openldap-2.3" + backend_schema = "backend-schema.schema" + + backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read()) + assert backend_schema_data is not None + open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data) + + # now we generate the needed strings to start slapd automatically, + # first ldapi_uri... + if ldap_backend_extra_port is not None: + # When we use MMR, we can't use 0.0.0.0 as it uses the name + # specified there as part of it's clue as to it's own name, + # and not to replicate to itself + if ol_mmr_urls is None: + server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port + else: + server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port + else: + server_port_string = "" + + # Prepare the 'result' information - the commands to return in particular + result.slapd_provision_command = [slapd_path] + + result.slapd_provision_command.append("-F" + paths.olcdir) + + result.slapd_provision_command.append("-h") + + # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands + result.slapd_command = list(result.slapd_provision_command) + + result.slapd_provision_command.append(result.ldapi_uri) + result.slapd_provision_command.append("-d0") + + uris = result.ldapi_uri + if server_port_string is not "": + uris = uris + " " + server_port_string + + result.slapd_command.append(uris) + + # Set the username - done here because Fedora DS still uses the admin DN and simple bind + result.credentials.set_username("samba-admin") + + # If we were just looking for crashes up to this point, it's a + # good time to exit before we realise we don't have OpenLDAP on + # this system + if ldap_dryrun_mode: + sys.exit(0) + + # Finally, convert the configuration into cn=config style! + if not os.path.isdir(paths.olcdir): + os.makedirs(paths.olcdir, 0770) + + retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False) + +# We can't do this, as OpenLDAP is strange. It gives an error +# output to the above, but does the conversion sucessfully... +# +# if retcode != 0: +# raise ProvisioningError("conversion from slapd.conf to cn=config failed") + + if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")): + raise ProvisioningError("conversion from slapd.conf to cn=config failed") + + # Don't confuse the admin by leaving the slapd.conf around + os.remove(paths.slapdconf) + + +def provision_fds_backend(result, paths=None, setup_path=None, names=None, + message=None, + hostname=None, ldapadminpass=None, root=None, + schema=None, + ldap_backend_extra_port=None, + setup_ds_path=None, + slapd_path=None, + nosync=False, + ldap_dryrun_mode=False): + + if ldap_backend_extra_port is not None: + serverport = "ServerPort=%d" % ldap_backend_extra_port + else: + serverport = "" + + setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, + {"ROOT": root, + "HOSTNAME": hostname, + "DNSDOMAIN": names.dnsdomain, + "LDAPDIR": paths.ldapdir, + "DOMAINDN": names.domaindn, + "LDAPMANAGERDN": names.ldapmanagerdn, + "LDAPMANAGERPASS": ldapadminpass, + "SERVERPORT": serverport}) + + setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, + {"CONFIGDN": names.configdn, + "SCHEMADN": names.schemadn, + "SAMBADN": names.sambadn, + }) + + setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl, + {"SAMBADN": names.sambadn, + }) + + setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba, + {"SAMBADN": names.sambadn, + "LDAPADMINPASS": ldapadminpass + }) + + mapping = "schema-map-fedora-ds-1.0" + backend_schema = "99_ad.ldif" + + # Build a schema file in Fedora DS format + backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read()) + assert backend_schema_data is not None + open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data) + + result.credentials.set_bind_dn(names.ldapmanagerdn) + + # Destory the target directory, or else setup-ds.pl will complain + fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4") + shutil.rmtree(fedora_ds_dir, True) + + result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid]; + #In the 'provision' command line, stay in the foreground so we can easily kill it + result.slapd_provision_command.append("-d0") + + #the command for the final run is the normal script + result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")] + + # If we were just looking for crashes up to this point, it's a + # good time to exit before we realise we don't have Fedora DS on + if ldap_dryrun_mode: + sys.exit(0) + + # Try to print helpful messages when the user has not specified the path to the setup-ds tool + if setup_ds_path is None: + raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!") + if not os.path.exists(setup_ds_path): + message (setup_ds_path) + raise ProvisioningError("Warning: Given Path to slapd does not exist!") + + # Run the Fedora DS setup utility + retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False) + if retcode != 0: + raise ProvisioningError("setup-ds failed") + + # Load samba-admin + retcode = subprocess.call([ + os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba], + close_fds=True, shell=False) + if retcode != 0: + raise("ldib2db failed") + +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. + """ + setup_file(setup_path("phpldapadmin-config.php"), path, + {"S4_LDAPI_URI": ldapi_uri}) + + +def create_zone_file(path, setup_path, dnsdomain, domaindn, + hostip, hostip6, hostname, dnspass, realm, domainguid, + ntdsguid): + """Write out a DNS zone file, from the info in the current database. + + :param path: Path of the new zone file. + :param setup_path: Setup path function. + :param dnsdomain: DNS Domain name + :param domaindn: DN of the Domain + :param hostip: Local IPv4 IP + :param hostip6: Local IPv6 IP + :param hostname: Local hostname + :param dnspass: Password for DNS + :param realm: Realm name + :param domainguid: GUID of the domain. + :param ntdsguid: GUID of the hosts nTDSDSA record. + """ + assert isinstance(domainguid, str) + + if hostip6 is not None: + hostip6_base_line = " IN AAAA " + hostip6 + hostip6_host_line = hostname + " IN AAAA " + hostip6 + else: + hostip6_base_line = "" + hostip6_host_line = "" + + if hostip is not None: + hostip_base_line = " IN A " + hostip + hostip_host_line = hostname + " IN A " + hostip + else: + hostip_base_line = "" + hostip_host_line = "" + + setup_file(setup_path("provision.zone"), path, { + "DNSPASS_B64": b64encode(dnspass), + "HOSTNAME": hostname, + "DNSDOMAIN": dnsdomain, + "REALM": realm, + "HOSTIP_BASE_LINE": hostip_base_line, + "HOSTIP_HOST_LINE": hostip_host_line, + "DOMAINGUID": domainguid, + "DATESTRING": time.strftime("%Y%m%d%H"), + "DEFAULTSITE": DEFAULTSITE, + "NTDSGUID": ntdsguid, + "HOSTIP6_BASE_LINE": hostip6_base_line, + "HOSTIP6_HOST_LINE": hostip6_host_line, + }) + + +def create_named_conf(path, setup_path, realm, dnsdomain, + private_dir): + """Write out a file containing zone statements suitable for inclusion in a + named.conf file (including GSS-TSIG configuration). + + :param path: Path of the new named.conf file. + :param setup_path: Setup path function. + :param realm: Realm name + :param dnsdomain: DNS Domain name + :param private_dir: Path to private directory + :param keytab_name: File name of DNS keytab file + """ + + setup_file(setup_path("named.conf"), path, { + "DNSDOMAIN": dnsdomain, + "REALM": realm, + "REALM_WC": "*." + ".".join(realm.split(".")[1:]), + "PRIVATE_DIR": private_dir + }) + +def create_named_txt(path, setup_path, realm, dnsdomain, + private_dir, keytab_name): + """Write out a file containing zone statements suitable for inclusion in a + named.conf file (including GSS-TSIG configuration). + + :param path: Path of the new named.conf file. + :param setup_path: Setup path function. + :param realm: Realm name + :param dnsdomain: DNS Domain name + :param private_dir: Path to private directory + :param keytab_name: File name of DNS keytab file + """ + + setup_file(setup_path("named.txt"), path, { + "DNSDOMAIN": dnsdomain, + "REALM": realm, + "DNS_KEYTAB": keytab_name, + "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name), + "PRIVATE_DIR": private_dir + }) + +def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm): + """Write out a file containing zone statements suitable for inclusion in a + named.conf file (including GSS-TSIG configuration). + + :param path: Path of the new named.conf file. + :param setup_path: Setup path function. + :param dnsdomain: DNS Domain name + :param hostname: Local hostname + :param realm: Realm name + """ + + setup_file(setup_path("krb5.conf"), path, { + "DNSDOMAIN": dnsdomain, + "HOSTNAME": hostname, + "REALM": realm, + }) + + diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py deleted file mode 100644 index 55774c225b..0000000000 --- a/source4/scripting/python/samba/provision/__init__.py +++ /dev/null @@ -1,1929 +0,0 @@ - -# Unix SMB/CIFS implementation. -# backend code for provisioning a Samba4 server - -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010 -# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009 -# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009 -# -# Based on the original in EJS: -# 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/>. -# - -"""Functions for setting up a Samba configuration.""" - -__docformat__ = "restructuredText" - -from base64 import b64encode -import os -import re -import pwd -import grp -import logging -import time -import uuid -import socket -import urllib -import shutil - -import ldb - -from samba.auth import system_session, admin_session -import samba -from samba import ( - Ldb, - check_all_substituted, - in_source_tree, - source_tree_topdir, - read_and_sub_file, - setup_file, - substitute_var, - valid_netbios_name, - version, - ) -from samba.dcerpc import security -from samba.dcerpc.misc import ( - SEC_CHAN_BDC, - SEC_CHAN_WKSTA, - ) -from samba.dsdb import ( - DS_DOMAIN_FUNCTION_2003, - DS_DOMAIN_FUNCTION_2008_R2, - ENC_ALL_TYPES, - ) -from samba.idmap import IDmapDB -from samba.ms_display_specifiers import read_ms_ldif -from samba.ntacls import setntacl, dsacl2fsacl -from samba.ndr import ndr_pack, ndr_unpack -from samba.provision.backend import ( - ExistingBackend, - FDSBackend, - LDBBackend, - OpenLDAPBackend, - ) -import samba.param -import samba.registry -from samba.schema import Schema -from samba.samdb import SamDB - -VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~" -DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9" -DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9" -DEFAULTSITE = "Default-First-Site-Name" -LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN" - - -def setup_path(file): - """Return an absolute path to the provision tempate file specified by file""" - return os.path.join(samba.param.setup_dir(), file) - -# Descriptors of naming contexts and other important objects - -# "get_schema_descriptor" is located in "schema.py" - -def get_sites_descriptor(domain_sid): - sddl = "D:(A;;RPLCLORC;;;AU)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "S:AI(AU;CISA;CCDCSDDT;;;WD)" \ - "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \ - "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \ - "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \ - "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)" - sec = security.descriptor.from_sddl(sddl, domain_sid) - return ndr_pack(sec) - - -def get_config_descriptor(domain_sid): - sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \ - "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \ - "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)" - sec = security.descriptor.from_sddl(sddl, domain_sid) - return ndr_pack(sec) - - -def get_domain_descriptor(domain_sid): - sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ - "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ - "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ - "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ - "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ - "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ - "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ - "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ - "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ - "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \ - "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \ - "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \ - "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \ - "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \ - "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \ - "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \ - "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \ - "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \ - "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ - "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \ - "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \ - "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \ - "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \ - "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \ - "(A;;RPRC;;;RU)" \ - "(A;CI;LC;;;RU)" \ - "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \ - "(A;;RP;;;WD)" \ - "(A;;RPLCLORC;;;ED)" \ - "(A;;RPLCLORC;;;AU)" \ - "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \ - "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \ - "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)" - sec = security.descriptor.from_sddl(sddl, domain_sid) - return ndr_pack(sec) - - -class ProvisionPaths(object): - - def __init__(self): - self.shareconf = None - self.hklm = None - self.hkcu = None - self.hkcr = None - self.hku = None - self.hkpd = None - self.hkpt = None - self.samdb = None - self.idmapdb = None - self.secrets = None - self.keytab = None - self.dns_keytab = None - self.dns = None - self.winsdb = None - self.private_dir = None - - -class ProvisionNames(object): - - def __init__(self): - self.rootdn = None - self.domaindn = None - self.configdn = None - self.schemadn = None - self.ldapmanagerdn = None - self.dnsdomain = None - self.realm = None - self.netbiosname = None - self.domain = None - self.hostname = None - self.sitename = None - self.smbconf = None - - -def update_provision_usn(samdb, low, high, replace=False): - """Update the field provisionUSN in sam.ldb - - This field is used to track range of USN modified by provision and - upgradeprovision. - This value is used afterward by next provision to figure out if - the field have been modified since last provision. - - :param samdb: An LDB object connect to sam.ldb - :param low: The lowest USN modified by this upgrade - :param high: The highest USN modified by this upgrade - :param replace: A boolean indicating if the range should replace any - existing one or appended (default) - """ - - tab = [] - if not replace: - entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % - LAST_PROVISION_USN_ATTRIBUTE, base="", - scope=ldb.SCOPE_SUBTREE, - attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"]) - for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]: - tab.append(str(e)) - - tab.append("%s-%s" % (low, high)) - delta = ldb.Message() - delta.dn = ldb.Dn(samdb, "@PROVISION") - delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab, - ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE) - samdb.modify(delta) - - -def set_provision_usn(samdb, low, high): - """Set the field provisionUSN in sam.ldb - This field is used to track range of USN modified by provision and - upgradeprovision. - This value is used afterward by next provision to figure out if - the field have been modified since last provision. - - :param samdb: An LDB object connect to sam.ldb - :param low: The lowest USN modified by this upgrade - :param high: The highest USN modified by this upgrade""" - tab = [] - tab.append("%s-%s" % (low, high)) - delta = ldb.Message() - delta.dn = ldb.Dn(samdb, "@PROVISION") - delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab, - ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE) - samdb.add(delta) - - -def get_max_usn(samdb,basedn): - """ This function return the biggest USN present in the provision - - :param samdb: A LDB object pointing to the sam.ldb - :param basedn: A string containing the base DN of the provision - (ie. DC=foo, DC=bar) - :return: The biggest USN in the provision""" - - res = samdb.search(expression="objectClass=*",base=basedn, - scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"], - controls=["search_options:1:2", - "server_sort:1:1:uSNChanged", - "paged_results:1:1"]) - return res[0]["uSNChanged"] - - -def get_last_provision_usn(sam): - """Get the lastest USN modified by a provision or an upgradeprovision - - :param sam: An LDB object pointing to the sam.ldb - :return: an integer corresponding to the highest USN modified by - (upgrade)provision, 0 is this value is unknown - """ - entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % - LAST_PROVISION_USN_ATTRIBUTE, - base="", scope=ldb.SCOPE_SUBTREE, - attrs=[LAST_PROVISION_USN_ATTRIBUTE]) - if len(entry): - range = [] - idx = 0 - p = re.compile(r'-') - for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]: - tab = p.split(str(r)) - range.append(tab[0]) - range.append(tab[1]) - idx = idx + 1 - return range - else: - return None - - -class ProvisionResult(object): - - def __init__(self): - self.paths = None - self.domaindn = None - self.lp = None - self.samdb = None - - -def check_install(lp, session_info, credentials): - """Check whether the current install seems ok. - - :param lp: Loadparm context - :param session_info: Session information - :param credentials: Credentials - """ - if lp.get("realm") == "": - raise Exception("Realm empty") - samdb = Ldb(lp.get("sam database"), session_info=session_info, - credentials=credentials, lp=lp) - if len(samdb.search("(cn=Administrator)")) != 1: - raise ProvisioningError("No administrator account found") - - -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 KeyError("Unable to find user/group in %r" % names) - - -findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2] -findnss_gid = lambda names: findnss(grp.getgrnam, names)[2] - - -def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]): - """Setup a ldb in the private dir. - - :param ldb: LDB file to import data into - :param ldif_path: Path of the LDIF file to load - :param subst_vars: Optional variables to subsitute in LDIF. - :param nocontrols: Optional list of controls, can be None for no controls - """ - assert isinstance(ldif_path, str) - data = read_and_sub_file(ldif_path, subst_vars) - ldb.add_ldif(data, controls) - - -def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]): - """Modify a ldb in the private dir. - - :param ldb: LDB object. - :param ldif_path: LDIF file path. - :param subst_vars: Optional dictionary with substitution variables. - """ - data = read_and_sub_file(ldif_path, subst_vars) - ldb.modify_ldif(data, controls) - - -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: - setup_add_ldif(ldb, ldif_path, subst_vars) - except Exception: - ldb.transaction_cancel() - raise - else: - ldb.transaction_commit() - - -def provision_paths_from_lp(lp, dnsdomain): - """Set the default paths for provisioning. - - :param lp: Loadparm context. - :param dnsdomain: DNS Domain name - """ - paths = ProvisionPaths() - paths.private_dir = lp.get("private dir") - - # This is stored without path prefix for the "privateKeytab" attribute in - # "secrets_dns.ldif". - paths.dns_keytab = "dns.keytab" - paths.keytab = "secrets.keytab" - - paths.shareconf = os.path.join(paths.private_dir, "share.ldb") - paths.samdb = os.path.join(paths.private_dir, - lp.get("sam database") or "samdb.ldb") - paths.idmapdb = os.path.join(paths.private_dir, - lp.get("idmap database") or "idmap.ldb") - paths.secrets = os.path.join(paths.private_dir, - lp.get("secrets database") or "secrets.ldb") - paths.privilege = os.path.join(paths.private_dir, "privilege.ldb") - paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone") - paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list") - paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list") - paths.namedconf = os.path.join(paths.private_dir, "named.conf") - paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update") - paths.namedtxt = os.path.join(paths.private_dir, "named.txt") - paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf") - paths.winsdb = os.path.join(paths.private_dir, "wins.ldb") - paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi") - paths.phpldapadminconfig = os.path.join(paths.private_dir, - "phpldapadmin-config.php") - 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("path", "sysvol") - paths.netlogon = lp.get("path", "netlogon") - paths.smbconf = lp.configfile - return paths - - -def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, - serverrole=None, rootdn=None, domaindn=None, configdn=None, - schemadn=None, serverdn=None, sitename=None): - """Guess configuration settings to use.""" - - if hostname is None: - hostname = socket.gethostname().split(".")[0] - - netbiosname = lp.get("netbios name") - if netbiosname is None: - netbiosname = hostname - # remove forbidden chars - newnbname = "" - for x in netbiosname: - if x.isalnum() or x in VALID_NETBIOS_CHARS: - newnbname = "%s%c" % (newnbname, x) - # force the length to be <16 - netbiosname = newnbname[0:15] - assert netbiosname is not None - netbiosname = netbiosname.upper() - if not valid_netbios_name(netbiosname): - raise InvalidNetbiosName(netbiosname) - - if dnsdomain is None: - dnsdomain = lp.get("realm") - if dnsdomain is None or dnsdomain == "": - raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile) - - dnsdomain = dnsdomain.lower() - - if serverrole is None: - serverrole = lp.get("server role") - if serverrole is None: - raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile) - - serverrole = serverrole.lower() - - realm = dnsdomain.upper() - - if lp.get("realm") == "": - raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile) - - if lp.get("realm").upper() != realm: - raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile)) - - if lp.get("server role").lower() != serverrole: - raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role").upper(), serverrole, lp.configfile)) - - if serverrole == "domain controller": - if domain is None: - # This will, for better or worse, default to 'WORKGROUP' - domain = lp.get("workgroup") - domain = domain.upper() - - if lp.get("workgroup").upper() != domain: - raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile)) - - if domaindn is None: - domaindn = "DC=" + dnsdomain.replace(".", ",DC=") - - if domain == netbiosname: - raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname)) - else: - domain = netbiosname - if domaindn is None: - domaindn = "DC=" + netbiosname - - if not valid_netbios_name(domain): - raise InvalidNetbiosName(domain) - - if hostname.upper() == realm: - raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname)) - if netbiosname.upper() == realm: - raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname)) - if domain == realm: - raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain)) - - if rootdn is None: - rootdn = domaindn - - if configdn is None: - configdn = "CN=Configuration," + rootdn - if schemadn is None: - schemadn = "CN=Schema," + configdn - - if sitename is None: - sitename=DEFAULTSITE - - names = ProvisionNames() - names.rootdn = rootdn - names.domaindn = domaindn - names.configdn = configdn - names.schemadn = schemadn - names.ldapmanagerdn = "CN=Manager," + rootdn - names.dnsdomain = dnsdomain - names.domain = domain - names.realm = realm - names.netbiosname = netbiosname - names.hostname = hostname - names.sitename = sitename - names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % ( - netbiosname, sitename, configdn) - - return names - - -def make_smbconf(smbconf, hostname, domain, realm, serverrole, - targetdir, sid_generator="internal", eadb=False, lp=None): - """Create a new smb.conf file based on a couple of basic settings. - """ - assert smbconf is not None - if hostname is None: - hostname = socket.gethostname().split(".")[0] - netbiosname = hostname.upper() - # remove forbidden chars - newnbname = "" - for x in netbiosname: - if x.isalnum() or x in VALID_NETBIOS_CHARS: - newnbname = "%s%c" % (newnbname, x) - #force the length to be <16 - netbiosname = newnbname[0:15] - else: - netbiosname = hostname.upper() - - if serverrole is None: - serverrole = "standalone" - - assert serverrole in ("domain controller", "member server", "standalone") - if serverrole == "domain controller": - smbconfsuffix = "dc" - elif serverrole == "member server": - smbconfsuffix = "member" - elif serverrole == "standalone": - smbconfsuffix = "standalone" - - if sid_generator is None: - sid_generator = "internal" - - assert domain is not None - domain = domain.upper() - - assert realm is not None - realm = realm.upper() - - if lp is None: - lp = samba.param.LoadParm() - #Load non-existant file - if os.path.exists(smbconf): - lp.load(smbconf) - if eadb and not lp.get("posix:eadb"): - if targetdir is not None: - privdir = os.path.join(targetdir, "private") - else: - privdir = lp.get("private dir") - lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb"))) - - if targetdir is not None: - privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private")) - lockdir_line = "lock dir = " + os.path.abspath(targetdir) - - lp.set("lock dir", os.path.abspath(targetdir)) - else: - privatedir_line = "" - lockdir_line = "" - - if sid_generator == "internal": - sid_generator_line = "" - else: - sid_generator_line = "sid generator = " + sid_generator - - sysvol = os.path.join(lp.get("lock dir"), "sysvol") - netlogon = os.path.join(sysvol, realm.lower(), "scripts") - - setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), - smbconf, { - "NETBIOS_NAME": netbiosname, - "DOMAIN": domain, - "REALM": realm, - "SERVERROLE": serverrole, - "NETLOGONPATH": netlogon, - "SYSVOLPATH": sysvol, - "SIDGENERATOR_LINE": sid_generator_line, - "PRIVATEDIR_LINE": privatedir_line, - "LOCKDIR_LINE": lockdir_line - }) - - # reload the smb.conf - lp.load(smbconf) - - # and dump it without any values that are the default - # this ensures that any smb.conf parameters that were set - # on the provision/join command line are set in the resulting smb.conf - f = open(smbconf, mode='w') - lp.dump(f, False) - f.close() - - - -def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid, - users_gid, wheel_gid): - """setup reasonable name mappings for sam names to unix names. - - :param samdb: SamDB object. - :param idmap: IDmap db object. - :param sid: The domain sid. - :param domaindn: The domain DN. - :param root_uid: uid of the UNIX root user. - :param nobody_uid: uid of the UNIX nobody user. - :param users_gid: gid of the UNIX users group. - :param wheel_gid: gid of the UNIX wheel group. - """ - idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid) - idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid) - - idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid) - idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid) - - -def setup_samdb_partitions(samdb_path, logger, lp, session_info, - provision_backend, names, schema, serverrole, - erase=False): - """Setup the partitions for the SAM database. - - Alternatively, provision() may call this, and then populate 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 - - # We use options=["modules:"] to stop the modules loading - we - # just want to wipe and re-initialise the database, not start it up - - try: - os.unlink(samdb_path) - except OSError: - pass - - samdb = Ldb(url=samdb_path, session_info=session_info, - lp=lp, options=["modules:"]) - - ldap_backend_line = "# No LDAP backend" - if provision_backend.type is not "ldb": - ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri - - samdb.transaction_start() - try: - logger.info("Setting up sam.ldb partitions and settings") - setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), { - "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(), - "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(), - "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(), - "LDAP_BACKEND_LINE": ldap_backend_line, - }) - - - setup_add_ldif(samdb, setup_path("provision_init.ldif"), { - "BACKEND_TYPE": provision_backend.type, - "SERVER_ROLE": serverrole - }) - - logger.info("Setting up sam.ldb rootDSE") - setup_samdb_rootdse(samdb, names) - except Exception: - samdb.transaction_cancel() - raise - else: - samdb.transaction_commit() - - -def secretsdb_self_join(secretsdb, domain, - netbiosname, machinepass, domainsid=None, - realm=None, dnsdomain=None, - keytab_path=None, - key_version_number=1, - secure_channel_type=SEC_CHAN_WKSTA): - """Add domain join-specific bits to a secrets database. - - :param secretsdb: Ldb Handle to the secrets database - :param machinepass: Machine password - """ - attrs = ["whenChanged", - "secret", - "priorSecret", - "priorChanged", - "krb5Keytab", - "privateKeytab"] - - if realm is not None: - if dnsdomain is None: - dnsdomain = realm.lower() - dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower()) - else: - dnsname = None - shortname = netbiosname.lower() - - # We don't need to set msg["flatname"] here, because rdn_name will handle - # it, and it causes problems for modifies anyway - msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain)) - msg["secureChannelType"] = [str(secure_channel_type)] - msg["objectClass"] = ["top", "primaryDomain"] - if dnsname is not None: - msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"] - msg["realm"] = [realm] - msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())] - msg["msDS-KeyVersionNumber"] = [str(key_version_number)] - msg["privateKeytab"] = ["secrets.keytab"] - - msg["secret"] = [machinepass] - msg["samAccountName"] = ["%s$" % netbiosname] - msg["secureChannelType"] = [str(secure_channel_type)] - if domainsid is not None: - msg["objectSid"] = [ndr_pack(domainsid)] - - # This complex expression tries to ensure that we don't have more - # than one record for this SID, realm or netbios domain at a time, - # but we don't delete the old record that we are about to modify, - # because that would delete the keytab and previous password. - res = secretsdb.search(base="cn=Primary Domains", attrs=attrs, - expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))), - scope=ldb.SCOPE_ONELEVEL) - - for del_msg in res: - secretsdb.delete(del_msg.dn) - - res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE) - - if len(res) == 1: - msg["priorSecret"] = [res[0]["secret"][0]] - msg["priorWhenChanged"] = [res[0]["whenChanged"][0]] - - try: - msg["privateKeytab"] = [res[0]["privateKeytab"][0]] - except KeyError: - pass - - try: - msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]] - except KeyError: - pass - - for el in msg: - if el != 'dn': - msg[el].set_flags(ldb.FLAG_MOD_REPLACE) - secretsdb.modify(msg) - secretsdb.rename(res[0].dn, msg.dn) - else: - spn = [ 'HOST/%s' % shortname ] - if secure_channel_type == SEC_CHAN_BDC and dnsname is not None: - # we are a domain controller then we add servicePrincipalName - # entries for the keytab code to update. - spn.extend([ 'HOST/%s' % dnsname ]) - msg["servicePrincipalName"] = spn - - secretsdb.add(msg) - - -def secretsdb_setup_dns(secretsdb, names, private_dir, realm, - dnsdomain, dns_keytab_path, dnspass): - """Add DNS specific bits to a secrets database. - - :param secretsdb: Ldb Handle to the secrets database - :param machinepass: Machine password - """ - try: - os.unlink(os.path.join(private_dir, dns_keytab_path)) - except OSError: - pass - - setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { - "REALM": realm, - "DNSDOMAIN": dnsdomain, - "DNS_KEYTAB": dns_keytab_path, - "DNSPASS_B64": b64encode(dnspass), - "HOSTNAME": names.hostname, - "DNSNAME" : '%s.%s' % ( - names.netbiosname.lower(), names.dnsdomain.lower()) - }) - - -def setup_secretsdb(paths, session_info, backend_credentials, lp): - """Setup the secrets database. - - :note: This function does not handle exceptions and transaction on purpose, - it's up to the caller to do this job. - - :param path: Path to the secrets database. - :param session_info: Session info. - :param credentials: Credentials - :param lp: Loadparm context - :return: LDB handle for the created secrets database - """ - if os.path.exists(paths.secrets): - os.unlink(paths.secrets) - - keytab_path = os.path.join(paths.private_dir, paths.keytab) - if os.path.exists(keytab_path): - os.unlink(keytab_path) - - dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab) - if os.path.exists(dns_keytab_path): - os.unlink(dns_keytab_path) - - path = paths.secrets - - secrets_ldb = Ldb(path, session_info=session_info, - lp=lp) - secrets_ldb.erase() - secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif")) - secrets_ldb = Ldb(path, session_info=session_info, - lp=lp) - secrets_ldb.transaction_start() - try: - secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif")) - - if (backend_credentials is not None and - backend_credentials.authentication_requested()): - if backend_credentials.get_bind_dn() is not None: - setup_add_ldif(secrets_ldb, - setup_path("secrets_simple_ldap.ldif"), { - "LDAPMANAGERDN": backend_credentials.get_bind_dn(), - "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password()) - }) - else: - setup_add_ldif(secrets_ldb, - setup_path("secrets_sasl_ldap.ldif"), { - "LDAPADMINUSER": backend_credentials.get_username(), - "LDAPADMINREALM": backend_credentials.get_realm(), - "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password()) - }) - - return secrets_ldb - except Exception: - secrets_ldb.transaction_cancel() - raise - - -def setup_privileges(path, session_info, lp): - """Setup the privileges database. - - :param path: Path to the privileges database. - :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) - privilege_ldb = Ldb(path, session_info=session_info, lp=lp) - privilege_ldb.erase() - privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif")) - - -def setup_registry(path, session_info, lp): - """Setup the registry. - - :param path: Path to the registry database - :param session_info: Session information - :param credentials: Credentials - :param lp: Loadparm context - """ - reg = samba.registry.Registry() - hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp) - reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE) - provision_reg = setup_path("provision.reg") - assert os.path.exists(provision_reg) - reg.diff_apply(provision_reg) - - -def setup_idmapdb(path, session_info, lp): - """Setup the idmap database. - - :param path: path to the idmap database - :param session_info: Session information - :param credentials: Credentials - :param lp: Loadparm context - """ - if os.path.exists(path): - os.unlink(path) - - idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp) - idmap_ldb.erase() - idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif")) - return idmap_ldb - - -def setup_samdb_rootdse(samdb, names): - """Setup the SamDB rootdse. - - :param samdb: Sam Database handle - """ - setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), { - "SCHEMADN": names.schemadn, - "DOMAINDN": names.domaindn, - "ROOTDN": names.rootdn, - "CONFIGDN": names.configdn, - "SERVERDN": names.serverdn, - }) - - -def setup_self_join(samdb, names, machinepass, dnspass, - domainsid, next_rid, invocationid, - policyguid, policyguid_dc, domainControllerFunctionality, - ntdsguid): - """Join a host to its own domain.""" - assert isinstance(invocationid, str) - if ntdsguid is not None: - ntdsguid_line = "objectGUID: %s\n"%ntdsguid - else: - ntdsguid_line = "" - setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - "DOMAINDN": names.domaindn, - "SERVERDN": names.serverdn, - "INVOCATIONID": invocationid, - "NETBIOSNAME": names.netbiosname, - "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain), - "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')), - "DOMAINSID": str(domainsid), - "DCRID": str(next_rid), - "SAMBA_VERSION_STRING": version, - "NTDSGUID": ntdsguid_line, - "DOMAIN_CONTROLLER_FUNCTIONALITY": str( - domainControllerFunctionality)}) - - setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { - "POLICYGUID": policyguid, - "POLICYGUID_DC": policyguid_dc, - "DNSDOMAIN": names.dnsdomain, - "DOMAINDN": names.domaindn}) - - # add the NTDSGUID based SPNs - ntds_dn = "CN=NTDS Settings,%s" % names.serverdn - names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID", - expression="", scope=ldb.SCOPE_BASE) - assert isinstance(names.ntdsguid, str) - - # Setup fSMORoleOwner entries to point at the newly created DC entry - setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), { - "DOMAINDN": names.domaindn, - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn, - "DEFAULTSITE": names.sitename, - "SERVERDN": names.serverdn, - "NETBIOSNAME": names.netbiosname, - "RIDALLOCATIONSTART": str(next_rid + 100), - "RIDALLOCATIONEND": str(next_rid + 100 + 499), - }) - - # This is partially Samba4 specific and should be replaced by the correct - # DNS AD-style setup - setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), { - "DNSDOMAIN": names.dnsdomain, - "DOMAINDN": names.domaindn, - "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')), - "HOSTNAME" : names.hostname, - "DNSNAME" : '%s.%s' % ( - names.netbiosname.lower(), names.dnsdomain.lower()) - }) - - -def getpolicypath(sysvolpath, dnsdomain, guid): - """Return the physical path of policy given its guid. - - :param sysvolpath: Path to the sysvol folder - :param dnsdomain: DNS name of the AD domain - :param guid: The GUID of the policy - :return: A string with the complete path to the policy folder - """ - - if guid[0] != "{": - guid = "{%s}" % guid - policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid) - return policy_path - - -def create_gpo_struct(policy_path): - if not os.path.exists(policy_path): - os.makedirs(policy_path, 0775) - open(os.path.join(policy_path, "GPT.INI"), 'w').write( - "[General]\r\nVersion=0") - p = os.path.join(policy_path, "MACHINE") - if not os.path.exists(p): - os.makedirs(p, 0775) - p = os.path.join(policy_path, "USER") - if not os.path.exists(p): - os.makedirs(p, 0775) - - -def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc): - """Create the default GPO for a domain - - :param sysvolpath: Physical path for the sysvol folder - :param dnsdomain: DNS domain name of the AD domain - :param policyguid: GUID of the default domain policy - :param policyguid_dc: GUID of the default domain controler policy - """ - policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid) - create_gpo_struct(policy_path) - - policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc) - create_gpo_struct(policy_path) - - -def setup_samdb(path, session_info, provision_backend, lp, names, - logger, domainsid, domainguid, policyguid, policyguid_dc, fill, - adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid, - serverrole, am_rodc=False, dom_for_fun_level=None, schema=None, - next_rid=1000): - """Setup a complete SAM Database. - - :note: This will wipe the main SAM database file! - """ - - # Provision does not make much sense values larger than 1000000000 - # as the upper range of the rIDAvailablePool is 1073741823 and - # we don't want to create a domain that cannot allocate rids. - if next_rid < 1000 or next_rid > 1000000000: - error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid) - error += "the valid range is %u-%u. The default is %u." % ( - 1000, 1000000000, 1000) - raise ProvisioningError(error) - - # ATTENTION: Do NOT change these default values without discussion with the - # team and/or release manager. They have a big impact on the whole program! - domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2 - - if dom_for_fun_level is None: - dom_for_fun_level = DS_DOMAIN_FUNCTION_2003 - - if dom_for_fun_level > domainControllerFunctionality: - raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!") - - domainFunctionality = dom_for_fun_level - forestFunctionality = dom_for_fun_level - - # Also wipes the database - setup_samdb_partitions(path, logger=logger, lp=lp, - provision_backend=provision_backend, session_info=session_info, - names=names, serverrole=serverrole, schema=schema) - - if schema is None: - schema = Schema(domainsid, schemadn=names.schemadn) - - # Load the database, but don's load the global schema and don't connect - # quite yet - samdb = SamDB(session_info=session_info, url=None, auto_connect=False, - credentials=provision_backend.credentials, lp=lp, - global_schema=False, am_rodc=am_rodc) - - logger.info("Pre-loading the Samba 4 and AD schema") - - # Load the schema from the one we computed earlier - samdb.set_schema(schema) - - # Set the NTDS settings DN manually - in order to have it already around - # before the provisioned tree exists and we connect - samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn) - - # And now we can connect to the DB - the schema won't be loaded from the - # DB - samdb.connect(path) - - if fill == FILL_DRS: - return samdb - - samdb.transaction_start() - try: - # Set the domain functionality levels onto the database. - # Various module (the password_hash module in particular) need - # to know what level of AD we are emulating. - - # These will be fixed into the database via the database - # modifictions below, but we need them set from the start. - samdb.set_opaque_integer("domainFunctionality", domainFunctionality) - samdb.set_opaque_integer("forestFunctionality", forestFunctionality) - samdb.set_opaque_integer("domainControllerFunctionality", - domainControllerFunctionality) - - samdb.set_domain_sid(str(domainsid)) - samdb.set_invocation_id(invocationid) - - logger.info("Adding DomainDN: %s" % names.domaindn) - - # impersonate domain admin - admin_session_info = admin_session(lp, str(domainsid)) - samdb.set_session_info(admin_session_info) - if domainguid is not None: - domainguid_line = "objectGUID: %s\n-" % domainguid - else: - domainguid_line = "" - - descr = b64encode(get_domain_descriptor(domainsid)) - setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), { - "DOMAINDN": names.domaindn, - "DOMAINSID": str(domainsid), - "DESCRIPTOR": descr, - "DOMAINGUID": domainguid_line - }) - - setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), { - "DOMAINDN": names.domaindn, - "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks - "NEXTRID": str(next_rid), - "DEFAULTSITE": names.sitename, - "CONFIGDN": names.configdn, - "POLICYGUID": policyguid, - "DOMAIN_FUNCTIONALITY": str(domainFunctionality), - "SAMBA_VERSION_STRING": version - }) - - logger.info("Adding configuration container") - descr = b64encode(get_config_descriptor(domainsid)) - setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), { - "CONFIGDN": names.configdn, - "DESCRIPTOR": descr, - }) - - # The LDIF here was created when the Schema object was constructed - logger.info("Setting up sam.ldb schema") - samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"]) - samdb.modify_ldif(schema.schema_dn_modify) - samdb.write_prefixes_from_schema() - samdb.add_ldif(schema.schema_data, controls=["relax:0"]) - setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), - {"SCHEMADN": names.schemadn}) - - logger.info("Reopening sam.ldb with new schema") - except Exception: - samdb.transaction_cancel() - raise - else: - samdb.transaction_commit() - - samdb = SamDB(session_info=admin_session_info, auto_connect=False, - credentials=provision_backend.credentials, lp=lp, - global_schema=False, am_rodc=am_rodc) - - # Set the NTDS settings DN manually - in order to have it already around - # before the provisioned tree exists and we connect - samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn) - samdb.connect(path) - - samdb.transaction_start() - try: - samdb.invocation_id = invocationid - - logger.info("Setting up sam.ldb configuration data") - descr = b64encode(get_sites_descriptor(domainsid)) - setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), { - "CONFIGDN": names.configdn, - "NETBIOSNAME": names.netbiosname, - "DEFAULTSITE": names.sitename, - "DNSDOMAIN": names.dnsdomain, - "DOMAIN": names.domain, - "SCHEMADN": names.schemadn, - "DOMAINDN": names.domaindn, - "SERVERDN": names.serverdn, - "FOREST_FUNCTIONALITY": str(forestFunctionality), - "DOMAIN_FUNCTIONALITY": str(domainFunctionality), - "SITES_DESCRIPTOR": descr - }) - - logger.info("Setting up display specifiers") - display_specifiers_ldif = read_ms_ldif( - setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt')) - display_specifiers_ldif = substitute_var(display_specifiers_ldif, - {"CONFIGDN": names.configdn}) - check_all_substituted(display_specifiers_ldif) - samdb.add_ldif(display_specifiers_ldif) - - logger.info("Adding users container") - setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), { - "DOMAINDN": names.domaindn}) - logger.info("Modifying users container") - setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), { - "DOMAINDN": names.domaindn}) - logger.info("Adding computers container") - setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), { - "DOMAINDN": names.domaindn}) - logger.info("Modifying computers container") - setup_modify_ldif(samdb, - setup_path("provision_computers_modify.ldif"), { - "DOMAINDN": names.domaindn}) - logger.info("Setting up sam.ldb data") - setup_add_ldif(samdb, setup_path("provision.ldif"), { - "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks - "DOMAINDN": names.domaindn, - "NETBIOSNAME": names.netbiosname, - "DEFAULTSITE": names.sitename, - "CONFIGDN": names.configdn, - "SERVERDN": names.serverdn, - "RIDAVAILABLESTART": str(next_rid + 600), - "POLICYGUID_DC": policyguid_dc - }) - - setup_modify_ldif(samdb, - setup_path("provision_basedn_references.ldif"), { - "DOMAINDN": names.domaindn}) - - setup_modify_ldif(samdb, - setup_path("provision_configuration_references.ldif"), { - "CONFIGDN": names.configdn, - "SCHEMADN": names.schemadn}) - if fill == FILL_FULL: - logger.info("Setting up sam.ldb users and groups") - setup_add_ldif(samdb, setup_path("provision_users.ldif"), { - "DOMAINDN": names.domaindn, - "DOMAINSID": str(domainsid), - "CONFIGDN": names.configdn, - "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')), - "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le')) - }) - - logger.info("Setting up self join") - setup_self_join(samdb, names=names, invocationid=invocationid, - dnspass=dnspass, - machinepass=machinepass, - domainsid=domainsid, - next_rid=next_rid, - policyguid=policyguid, - policyguid_dc=policyguid_dc, - domainControllerFunctionality=domainControllerFunctionality, - ntdsguid=ntdsguid) - - ntds_dn = "CN=NTDS Settings,%s" % names.serverdn - names.ntdsguid = samdb.searchone(basedn=ntds_dn, - attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE) - assert isinstance(names.ntdsguid, str) - except Exception: - samdb.transaction_cancel() - raise - else: - samdb.transaction_commit() - return samdb - - -FILL_FULL = "FULL" -FILL_NT4SYNC = "NT4SYNC" -FILL_DRS = "DRS" -SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)" -POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)" - - -def set_dir_acl(path, acl, lp, domsid): - setntacl(lp, path, acl, domsid) - for root, dirs, files in os.walk(path, topdown=False): - for name in files: - setntacl(lp, os.path.join(root, name), acl, domsid) - for name in dirs: - setntacl(lp, os.path.join(root, name), acl, domsid) - - -def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp): - """Set ACL on the sysvol/<dnsname>/Policies folder and the policy - folders beneath. - - :param sysvol: Physical path for the sysvol folder - :param dnsdomain: The DNS name of the domain - :param domainsid: The SID of the domain - :param domaindn: The DN of the domain (ie. DC=...) - :param samdb: An LDB object on the SAM db - :param lp: an LP object - """ - - # Set ACL for GPO root folder - root_policy_path = os.path.join(sysvol, dnsdomain, "Policies") - setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid)) - - res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn), - attrs=["cn", "nTSecurityDescriptor"], - expression="", scope=ldb.SCOPE_ONELEVEL) - - for policy in res: - acl = ndr_unpack(security.descriptor, - str(policy["nTSecurityDescriptor"])).as_sddl() - policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"])) - set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp, - str(domainsid)) - - -def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn, - lp): - """Set the ACL for the sysvol share and the subfolders - - :param samdb: An LDB object on the SAM db - :param netlogon: Physical path for the netlogon folder - :param sysvol: Physical path for the sysvol folder - :param gid: The GID of the "Domain adminstrators" group - :param domainsid: The SID of the domain - :param dnsdomain: The DNS name of the domain - :param domaindn: The DN of the domain (ie. DC=...) - """ - - try: - os.chown(sysvol, -1, gid) - except OSError: - canchown = False - else: - canchown = True - - # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level) - setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid)) - for root, dirs, files in os.walk(sysvol, topdown=False): - for name in files: - if canchown: - os.chown(os.path.join(root, name), -1, gid) - setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid)) - for name in dirs: - if canchown: - os.chown(os.path.join(root, name), -1, gid) - setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid)) - - # Set acls on Policy folder and policies folders - set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp) - - -def provision(logger, session_info, credentials, smbconf=None, - targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None, - domaindn=None, schemadn=None, configdn=None, serverdn=None, - domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None, - next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None, - domainguid=None, policyguid=None, policyguid_dc=None, - invocationid=None, machinepass=None, ntdsguid=None, dnspass=None, - root=None, nobody=None, users=None, wheel=None, backup=None, aci=None, - serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None, - ldap_backend_forced_uri=None, backend_type=None, sitename=None, - ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None, - nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False, - lp=None): - """Provision samba4 - - :note: caution, this wipes all existing data! - """ - - if domainsid is None: - domainsid = security.random_sid() - else: - domainsid = security.dom_sid(domainsid) - - # create/adapt the group policy GUIDs - # Default GUID for default policy are described at - # "How Core Group Policy Works" - # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx - if policyguid is None: - policyguid = DEFAULT_POLICY_GUID - policyguid = policyguid.upper() - if policyguid_dc is None: - policyguid_dc = DEFAULT_DC_POLICY_GUID - policyguid_dc = policyguid_dc.upper() - - if adminpass is None: - adminpass = samba.generate_random_password(12, 32) - if krbtgtpass is None: - krbtgtpass = samba.generate_random_password(128, 255) - if machinepass is None: - machinepass = samba.generate_random_password(128, 255) - if dnspass is None: - dnspass = samba.generate_random_password(128, 255) - if ldapadminpass is None: - # Make a new, random password between Samba and it's LDAP server - ldapadminpass=samba.generate_random_password(128, 255) - - if backend_type is None: - backend_type = "ldb" - - sid_generator = "internal" - if backend_type == "fedora-ds": - sid_generator = "backend" - - root_uid = findnss_uid([root or "root"]) - nobody_uid = findnss_uid([nobody or "nobody"]) - users_gid = findnss_gid([users or "users", 'users', 'other', 'staff']) - if wheel is None: - wheel_gid = findnss_gid(["wheel", "adm"]) - else: - wheel_gid = findnss_gid([wheel]) - try: - bind_gid = findnss_gid(["bind", "named"]) - except KeyError: - bind_gid = None - - if targetdir is not None: - smbconf = os.path.join(targetdir, "etc", "smb.conf") - elif smbconf is None: - smbconf = samba.param.default_path() - if not os.path.exists(os.path.dirname(smbconf)): - os.makedirs(os.path.dirname(smbconf)) - - # only install a new smb.conf if there isn't one there already - if os.path.exists(smbconf): - # if Samba Team members can't figure out the weird errors - # loading an empty smb.conf gives, then we need to be smarter. - # Pretend it just didn't exist --abartlet - data = open(smbconf, 'r').read() - data = data.lstrip() - if data is None or data == "": - make_smbconf(smbconf, hostname, domain, realm, - serverrole, targetdir, sid_generator, useeadb, - lp=lp) - else: - make_smbconf(smbconf, hostname, domain, realm, serverrole, - targetdir, sid_generator, useeadb, lp=lp) - - if lp is None: - lp = samba.param.LoadParm() - lp.load(smbconf) - names = guess_names(lp=lp, hostname=hostname, domain=domain, - dnsdomain=realm, serverrole=serverrole, domaindn=domaindn, - configdn=configdn, schemadn=schemadn, serverdn=serverdn, - sitename=sitename) - paths = provision_paths_from_lp(lp, names.dnsdomain) - - paths.bind_gid = bind_gid - - if hostip is None: - logger.info("Looking up IPv4 addresses") - hostips = samba.interface_ips(lp, False) - if len(hostips) == 0: - logger.warning("No external IPv4 address has been found. Using loopback.") - hostip = '127.0.0.1' - else: - hostip = hostips[0] - if len(hostips) > 1: - logger.warning("More than one IPv4 address found. Using %s.", - hostip) - - if serverrole is None: - serverrole = lp.get("server role") - - assert serverrole in ("domain controller", "member server", "standalone") - if invocationid is None: - invocationid = str(uuid.uuid4()) - - if not os.path.exists(paths.private_dir): - os.mkdir(paths.private_dir) - if not os.path.exists(os.path.join(paths.private_dir, "tls")): - os.mkdir(os.path.join(paths.private_dir, "tls")) - - ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="") - - schema = Schema(domainsid, invocationid=invocationid, - schemadn=names.schemadn) - - if backend_type == "ldb": - provision_backend = LDBBackend(backend_type, paths=paths, - lp=lp, credentials=credentials, - names=names, logger=logger) - elif backend_type == "existing": - provision_backend = ExistingBackend(backend_type, paths=paths, - lp=lp, credentials=credentials, - names=names, logger=logger, - ldap_backend_forced_uri=ldap_backend_forced_uri) - elif backend_type == "fedora-ds": - provision_backend = FDSBackend(backend_type, paths=paths, - lp=lp, credentials=credentials, - names=names, logger=logger, domainsid=domainsid, - schema=schema, hostname=hostname, ldapadminpass=ldapadminpass, - slapd_path=slapd_path, - ldap_backend_extra_port=ldap_backend_extra_port, - ldap_dryrun_mode=ldap_dryrun_mode, root=root, - setup_ds_path=setup_ds_path, - ldap_backend_forced_uri=ldap_backend_forced_uri) - elif backend_type == "openldap": - provision_backend = OpenLDAPBackend(backend_type, paths=paths, - lp=lp, credentials=credentials, - names=names, logger=logger, domainsid=domainsid, - schema=schema, hostname=hostname, ldapadminpass=ldapadminpass, - slapd_path=slapd_path, - ldap_backend_extra_port=ldap_backend_extra_port, - ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls, - nosync=nosync, - ldap_backend_forced_uri=ldap_backend_forced_uri) - else: - raise ValueError("Unknown LDAP backend type selected") - - provision_backend.init() - provision_backend.start() - - # only install a new shares config db if there is none - if not os.path.exists(paths.shareconf): - logger.info("Setting up share.ldb") - share_ldb = Ldb(paths.shareconf, session_info=session_info, - lp=lp) - share_ldb.load_ldif_file_add(setup_path("share.ldif")) - - logger.info("Setting up secrets.ldb") - secrets_ldb = setup_secretsdb(paths, - session_info=session_info, - backend_credentials=provision_backend.secrets_credentials, lp=lp) - - try: - logger.info("Setting up the registry") - setup_registry(paths.hklm, session_info, - lp=lp) - - logger.info("Setting up the privileges database") - setup_privileges(paths.privilege, session_info, lp=lp) - - logger.info("Setting up idmap db") - idmap = setup_idmapdb(paths.idmapdb, - session_info=session_info, lp=lp) - - logger.info("Setting up SAM db") - samdb = setup_samdb(paths.samdb, session_info, - provision_backend, lp, names, logger=logger, - domainsid=domainsid, schema=schema, domainguid=domainguid, - policyguid=policyguid, policyguid_dc=policyguid_dc, - fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass, - invocationid=invocationid, machinepass=machinepass, - dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole, - dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc, - next_rid=next_rid) - - if serverrole == "domain controller": - if paths.netlogon is None: - logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.") - logger.info("Please either remove %s or see the template at %s" % - (paths.smbconf, setup_path("provision.smb.conf.dc"))) - assert paths.netlogon is not None - - if paths.sysvol is None: - logger.info("Existing smb.conf does not have a [sysvol] share, but you" - " are configuring a DC.") - logger.info("Please either remove %s or see the template at %s" % - (paths.smbconf, setup_path("provision.smb.conf.dc"))) - assert paths.sysvol is not None - - if not os.path.isdir(paths.netlogon): - os.makedirs(paths.netlogon, 0755) - - if samdb_fill == FILL_FULL: - setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn, - root_uid=root_uid, nobody_uid=nobody_uid, - users_gid=users_gid, wheel_gid=wheel_gid) - - if serverrole == "domain controller": - # Set up group policies (domain policy and domain controller - # policy) - create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, - policyguid_dc) - setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid, - domainsid, names.dnsdomain, names.domaindn, lp) - - logger.info("Setting up sam.ldb rootDSE marking as synchronized") - setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif")) - - secretsdb_self_join(secrets_ldb, domain=names.domain, - realm=names.realm, dnsdomain=names.dnsdomain, - netbiosname=names.netbiosname, domainsid=domainsid, - machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC) - - # Now set up the right msDS-SupportedEncryptionTypes into the DB - # In future, this might be determined from some configuration - kerberos_enctypes = str(ENC_ALL_TYPES) - - try: - msg = ldb.Message(ldb.Dn(samdb, - samdb.searchone("distinguishedName", - expression="samAccountName=%s$" % names.netbiosname, - scope=ldb.SCOPE_SUBTREE))) - msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement( - elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE, - name="msDS-SupportedEncryptionTypes") - samdb.modify(msg) - except ldb.LdbError, (enum, estr): - if enum != ldb.ERR_NO_SUCH_ATTRIBUTE: - # It might be that this attribute does not exist in this schema - raise - - if serverrole == "domain controller": - secretsdb_setup_dns(secrets_ldb, names, - paths.private_dir, realm=names.realm, - dnsdomain=names.dnsdomain, - dns_keytab_path=paths.dns_keytab, dnspass=dnspass) - - domainguid = samdb.searchone(basedn=domaindn, - attribute="objectGUID") - assert isinstance(domainguid, str) - - # Only make a zone file on the first DC, it should be - # replicated with DNS replication - create_zone_file(lp, logger, paths, targetdir, - dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6, - hostname=names.hostname, realm=names.realm, - domainguid=domainguid, ntdsguid=names.ntdsguid) - - create_named_conf(paths, realm=names.realm, - dnsdomain=names.dnsdomain, private_dir=paths.private_dir) - - create_named_txt(paths.namedtxt, - realm=names.realm, dnsdomain=names.dnsdomain, - private_dir=paths.private_dir, - keytab_name=paths.dns_keytab) - logger.info("See %s for an example configuration include file for BIND", paths.namedconf) - logger.info("and %s for further documentation required for secure DNS " - "updates", paths.namedtxt) - - lastProvisionUSNs = get_last_provision_usn(samdb) - maxUSN = get_max_usn(samdb, str(names.rootdn)) - if lastProvisionUSNs is not None: - update_provision_usn(samdb, 0, maxUSN, 1) - else: - set_provision_usn(samdb, 0, maxUSN) - - create_krb5_conf(paths.krb5conf, - dnsdomain=names.dnsdomain, hostname=names.hostname, - realm=names.realm) - logger.info("A Kerberos configuration suitable for Samba 4 has been " - "generated at %s", paths.krb5conf) - - if serverrole == "domain controller": - create_dns_update_list(lp, logger, paths) - - provision_backend.post_setup() - provision_backend.shutdown() - - create_phpldapadmin_config(paths.phpldapadminconfig, - ldapi_url) - except Exception: - secrets_ldb.transaction_cancel() - raise - - # Now commit the secrets.ldb to disk - secrets_ldb.transaction_commit() - - # the commit creates the dns.keytab, now chown it - dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab) - if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None: - try: - os.chmod(dns_keytab_path, 0640) - os.chown(dns_keytab_path, -1, paths.bind_gid) - except OSError: - if not os.environ.has_key('SAMBA_SELFTEST'): - logger.info("Failed to chown %s to bind gid %u", - dns_keytab_path, paths.bind_gid) - - - logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php", - paths.phpldapadminconfig) - - logger.info("Once the above files are installed, your Samba4 server will be ready to use") - logger.info("Server Role: %s" % serverrole) - logger.info("Hostname: %s" % names.hostname) - logger.info("NetBIOS Domain: %s" % names.domain) - logger.info("DNS Domain: %s" % names.dnsdomain) - logger.info("DOMAIN SID: %s" % str(domainsid)) - if samdb_fill == FILL_FULL: - logger.info("Admin password: %s" % adminpass) - if provision_backend.type is not "ldb": - if provision_backend.credentials.get_bind_dn() is not None: - logger.info("LDAP Backend Admin DN: %s" % - provision_backend.credentials.get_bind_dn()) - else: - logger.info("LDAP Admin User: %s" % - provision_backend.credentials.get_username()) - - logger.info("LDAP Admin Password: %s" % - provision_backend.credentials.get_password()) - - if provision_backend.slapd_command_escaped is not None: - # now display slapd_command_file.txt to show how slapd must be - # started next time - logger.info("Use later the following commandline to start slapd, then Samba:") - logger.info(provision_backend.slapd_command_escaped) - logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh", - provision_backend.ldapdir) - - result = ProvisionResult() - result.domaindn = domaindn - result.paths = paths - result.lp = lp - result.samdb = samdb - return result - - -def provision_become_dc(smbconf=None, targetdir=None, - realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None, - serverdn=None, domain=None, hostname=None, domainsid=None, - adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None, - policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None, - root=None, nobody=None, users=None, wheel=None, backup=None, - serverrole=None, ldap_backend=None, ldap_backend_type=None, - sitename=None, debuglevel=1): - - logger = logging.getLogger("provision") - samba.set_debug_level(debuglevel) - - res = provision(logger, system_session(), None, - smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, - realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, - configdn=configdn, serverdn=serverdn, domain=domain, - hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, - machinepass=machinepass, serverrole="domain controller", - sitename=sitename) - res.lp.set("debuglevel", str(debuglevel)) - return res - - -def create_phpldapadmin_config(path, ldapi_uri): - """Create a PHP LDAP admin configuration file. - - :param path: Path to write the configuration to. - """ - setup_file(setup_path("phpldapadmin-config.php"), path, - {"S4_LDAPI_URI": ldapi_uri}) - - -def create_zone_file(lp, logger, paths, targetdir, dnsdomain, - hostip, hostip6, hostname, realm, domainguid, - ntdsguid): - """Write out a DNS zone file, from the info in the current database. - - :param paths: paths object - :param dnsdomain: DNS Domain name - :param domaindn: DN of the Domain - :param hostip: Local IPv4 IP - :param hostip6: Local IPv6 IP - :param hostname: Local hostname - :param realm: Realm name - :param domainguid: GUID of the domain. - :param ntdsguid: GUID of the hosts nTDSDSA record. - """ - assert isinstance(domainguid, str) - - if hostip6 is not None: - hostip6_base_line = " IN AAAA " + hostip6 - hostip6_host_line = hostname + " IN AAAA " + hostip6 - gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6 - else: - hostip6_base_line = "" - hostip6_host_line = "" - gc_msdcs_ip6_line = "" - - if hostip is not None: - hostip_base_line = " IN A " + hostip - hostip_host_line = hostname + " IN A " + hostip - gc_msdcs_ip_line = "gc._msdcs IN A " + hostip - else: - hostip_base_line = "" - hostip_host_line = "" - gc_msdcs_ip_line = "" - - dns_dir = os.path.dirname(paths.dns) - - try: - shutil.rmtree(dns_dir, True) - except OSError: - pass - - os.mkdir(dns_dir, 0775) - - # we need to freeze the zone while we update the contents - if targetdir is None: - rndc = ' '.join(lp.get("rndc command")) - os.system(rndc + " freeze " + lp.get("realm")) - - setup_file(setup_path("provision.zone"), paths.dns, { - "HOSTNAME": hostname, - "DNSDOMAIN": dnsdomain, - "REALM": realm, - "HOSTIP_BASE_LINE": hostip_base_line, - "HOSTIP_HOST_LINE": hostip_host_line, - "DOMAINGUID": domainguid, - "DATESTRING": time.strftime("%Y%m%d%H"), - "DEFAULTSITE": DEFAULTSITE, - "NTDSGUID": ntdsguid, - "HOSTIP6_BASE_LINE": hostip6_base_line, - "HOSTIP6_HOST_LINE": hostip6_host_line, - "GC_MSDCS_IP_LINE": gc_msdcs_ip_line, - "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line, - }) - - # note that we use no variable substitution on this file - # the substitution is done at runtime by samba_dnsupdate - setup_file(setup_path("dns_update_list"), paths.dns_update_list, None) - - # and the SPN update list - setup_file(setup_path("spn_update_list"), paths.spn_update_list, None) - - if paths.bind_gid is not None: - try: - os.chown(dns_dir, -1, paths.bind_gid) - os.chown(paths.dns, -1, paths.bind_gid) - # chmod needed to cope with umask - os.chmod(dns_dir, 0775) - os.chmod(paths.dns, 0664) - except OSError: - if not os.environ.has_key('SAMBA_SELFTEST'): - logger.error("Failed to chown %s to bind gid %u" % ( - dns_dir, paths.bind_gid)) - - if targetdir is None: - os.system(rndc + " unfreeze " + lp.get("realm")) - - -def create_dns_update_list(lp, logger, paths): - """Write out a dns_update_list file""" - # note that we use no variable substitution on this file - # the substitution is done at runtime by samba_dnsupdate - setup_file(setup_path("dns_update_list"), paths.dns_update_list, None) - setup_file(setup_path("spn_update_list"), paths.spn_update_list, None) - - -def create_named_conf(paths, realm, dnsdomain, - private_dir): - """Write out a file containing zone statements suitable for inclusion in a - named.conf file (including GSS-TSIG configuration). - - :param paths: all paths - :param realm: Realm name - :param dnsdomain: DNS Domain name - :param private_dir: Path to private directory - :param keytab_name: File name of DNS keytab file - """ - - setup_file(setup_path("named.conf"), paths.namedconf, { - "DNSDOMAIN": dnsdomain, - "REALM": realm, - "ZONE_FILE": paths.dns, - "REALM_WC": "*." + ".".join(realm.split(".")[1:]), - "NAMED_CONF": paths.namedconf, - "NAMED_CONF_UPDATE": paths.namedconf_update - }) - - setup_file(setup_path("named.conf.update"), paths.namedconf_update) - - -def create_named_txt(path, realm, dnsdomain, private_dir, - keytab_name): - """Write out a file containing zone statements suitable for inclusion in a - named.conf file (including GSS-TSIG configuration). - - :param path: Path of the new named.conf file. - :param realm: Realm name - :param dnsdomain: DNS Domain name - :param private_dir: Path to private directory - :param keytab_name: File name of DNS keytab file - """ - setup_file(setup_path("named.txt"), path, { - "DNSDOMAIN": dnsdomain, - "REALM": realm, - "DNS_KEYTAB": keytab_name, - "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name), - "PRIVATE_DIR": private_dir - }) - - -def create_krb5_conf(path, dnsdomain, hostname, realm): - """Write out a file containing zone statements suitable for inclusion in a - named.conf file (including GSS-TSIG configuration). - - :param path: Path of the new named.conf file. - :param dnsdomain: DNS Domain name - :param hostname: Local hostname - :param realm: Realm name - """ - setup_file(setup_path("krb5.conf"), path, { - "DNSDOMAIN": dnsdomain, - "HOSTNAME": hostname, - "REALM": realm, - }) - - -class ProvisioningError(Exception): - """A generic provision error.""" - - def __init__(self, value): - self.value = value - - def __str__(self): - return "ProvisioningError: " + self.value - - -class InvalidNetbiosName(Exception): - """A specified name was not a valid NetBIOS name.""" - def __init__(self, name): - super(InvalidNetbiosName, self).__init__( - "The name '%r' is not a valid NetBIOS name" % name) diff --git a/source4/scripting/python/samba/provision/backend.py b/source4/scripting/python/samba/provision/backend.py deleted file mode 100644 index f9dbba85f6..0000000000 --- a/source4/scripting/python/samba/provision/backend.py +++ /dev/null @@ -1,772 +0,0 @@ -# -# 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-2009 -# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009 -# -# Based on the original in EJS: -# 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/>. -# - -"""Functions for setting up a Samba configuration (LDB and LDAP backends).""" - -from base64 import b64encode -import errno -import ldb -import os -import sys -import uuid -import time -import shutil -import subprocess -import urllib - -from ldb import SCOPE_BASE, SCOPE_ONELEVEL, LdbError, timestring - -from samba import Ldb, read_and_sub_file, setup_file -from samba.credentials import Credentials, DONT_USE_KERBEROS -from samba.schema import Schema - -class SlapdAlreadyRunning(Exception): - - def __init__(self, uri): - self.ldapi_uri = uri - super(SlapdAlreadyRunning, self).__init__("Another slapd Instance " - "seems already running on this host, listening to %s." % - self.ldapi_uri) - - -class ProvisionBackend(object): - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None): - """Provision a backend for samba4""" - self.paths = paths - self.lp = lp - self.credentials = credentials - self.names = names - self.logger = logger - - self.type = backend_type - - # Set a default - the code for "existing" below replaces this - self.ldap_backend_type = backend_type - - def init(self): - """Initialize the backend.""" - raise NotImplementedError(self.init) - - def start(self): - """Start the backend.""" - raise NotImplementedError(self.start) - - def shutdown(self): - """Shutdown the backend.""" - raise NotImplementedError(self.shutdown) - - def post_setup(self): - """Post setup.""" - raise NotImplementedError(self.post_setup) - - -class LDBBackend(ProvisionBackend): - - def init(self): - self.credentials = None - self.secrets_credentials = None - - # Wipe the old sam.ldb databases away - shutil.rmtree(self.paths.samdb + ".d", True) - - def start(self): - pass - - def shutdown(self): - pass - - def post_setup(self): - pass - - -class ExistingBackend(ProvisionBackend): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None, ldapi_uri=None): - - super(ExistingBackend, self).__init__(backend_type=backend_type, - paths=paths, lp=lp, - credentials=credentials, names=names, logger=logger, - ldap_backend_forced_uri=ldapi_uri) - - def init(self): - # Check to see that this 'existing' LDAP backend in fact exists - ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials) - ldapi_db.search(base="", scope=SCOPE_BASE, - expression="(objectClass=OpenLDAProotDSE)") - - # If we have got here, then we must have a valid connection to the LDAP - # server, with valid credentials supplied This caused them to be set - # into the long-term database later in the script. - self.secrets_credentials = self.credentials - - # For now, assume existing backends at least emulate OpenLDAP - self.ldap_backend_type = "openldap" - - -class LDAPBackend(ProvisionBackend): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None, domainsid=None, - schema=None, hostname=None, ldapadminpass=None, - slapd_path=None, ldap_backend_extra_port=None, - ldap_backend_forced_uri=None, ldap_dryrun_mode=False): - - super(LDAPBackend, self).__init__(backend_type=backend_type, - paths=paths, lp=lp, - credentials=credentials, names=names, logger=logger) - - self.domainsid = domainsid - self.schema = schema - self.hostname = hostname - - self.ldapdir = os.path.join(paths.private_dir, "ldap") - self.ldapadminpass = ldapadminpass - - self.slapd_path = slapd_path - self.slapd_command = None - self.slapd_command_escaped = None - self.slapd_pid = os.path.join(self.ldapdir, "slapd.pid") - - self.ldap_backend_extra_port = ldap_backend_extra_port - self.ldap_dryrun_mode = ldap_dryrun_mode - - if ldap_backend_forced_uri is not None: - self.ldap_uri = ldap_backend_forced_uri - else: - self.ldap_uri = "ldapi://%s" % urllib.quote( - os.path.join(self.ldapdir, "ldapi"), safe="") - - if not os.path.exists(self.ldapdir): - os.mkdir(self.ldapdir) - - def init(self): - from samba.provision import ProvisioningError - # we will shortly start slapd with ldapi for final provisioning. first - # check with ldapsearch -> rootDSE via self.ldap_uri if another - # instance of slapd is already running - try: - ldapi_db = Ldb(self.ldap_uri) - ldapi_db.search(base="", scope=SCOPE_BASE, - expression="(objectClass=OpenLDAProotDSE)") - try: - f = open(self.slapd_pid, "r") - except IOError, err: - if err != errno.ENOENT: - raise - else: - p = f.read() - f.close() - self.logger.info("Check for slapd Process with PID: %s and terminate it manually." % p) - raise SlapdAlreadyRunning(self.ldap_uri) - except LdbError: - # XXX: We should never be catching all Ldb errors - pass - - # Try to print helpful messages when the user has not specified the - # path to slapd - if self.slapd_path is None: - raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!") - if not os.path.exists(self.slapd_path): - self.logger.warning("Path (%s) to slapd does not exist!", - self.slapd_path) - - if not os.path.isdir(self.ldapdir): - os.makedirs(self.ldapdir, 0700) - - # Put the LDIF of the schema into a database so we can search on - # it to generate schema-dependent configurations in Fedora DS and - # OpenLDAP - schemadb_path = os.path.join(self.ldapdir, "schema-tmp.ldb") - try: - os.unlink(schemadb_path) - except OSError: - pass - - self.schema.write_to_tmp_ldb(schemadb_path) - - self.credentials = Credentials() - self.credentials.guess(self.lp) - # Kerberos to an ldapi:// backend makes no sense - self.credentials.set_kerberos_state(DONT_USE_KERBEROS) - self.credentials.set_password(self.ldapadminpass) - - self.secrets_credentials = Credentials() - self.secrets_credentials.guess(self.lp) - # Kerberos to an ldapi:// backend makes no sense - self.secrets_credentials.set_kerberos_state(DONT_USE_KERBEROS) - self.secrets_credentials.set_username("samba-admin") - self.secrets_credentials.set_password(self.ldapadminpass) - - self.provision() - - def provision(self): - pass - - def start(self): - from samba.provision import ProvisioningError - self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'" - f = open(os.path.join(self.ldapdir, "ldap_backend_startup.sh"), 'w') - try: - f.write("#!/bin/sh\n" + self.slapd_command_escaped + "\n") - finally: - f.close() - - # Now start the slapd, so we can provision onto it. We keep the - # subprocess context around, to kill this off at the successful - # end of the script - self.slapd = subprocess.Popen(self.slapd_provision_command, - close_fds=True, shell=False) - - count = 0 - while self.slapd.poll() is None: - # Wait until the socket appears - try: - ldapi_db = Ldb(self.ldap_uri, lp=self.lp, credentials=self.credentials) - ldapi_db.search(base="", scope=SCOPE_BASE, - expression="(objectClass=OpenLDAProotDSE)") - # If we have got here, then we must have a valid connection to - # the LDAP server! - return - except LdbError: - time.sleep(1) - count = count + 1 - - if count > 15: - self.logger.error("Could not connect to slapd started with: %s" % "\'" + "\' \'".join(self.slapd_provision_command) + "\'") - raise ProvisioningError("slapd never accepted a connection within 15 seconds of starting") - - self.logger.error("Could not start slapd with: %s" % "\'" + "\' \'".join(self.slapd_provision_command) + "\'") - raise ProvisioningError("slapd died before we could make a connection to it") - - def shutdown(self): - # if an LDAP backend is in use, terminate slapd after final provision - # and check its proper termination - if self.slapd.poll() is None: - # Kill the slapd - if getattr(self.slapd, "terminate", None) is not None: - self.slapd.terminate() - else: - # Older python versions don't have .terminate() - import signal - os.kill(self.slapd.pid, signal.SIGTERM) - - # and now wait for it to die - self.slapd.communicate() - - def post_setup(self): - pass - - -class OpenLDAPBackend(LDAPBackend): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None, domainsid=None, - schema=None, hostname=None, ldapadminpass=None, slapd_path=None, - ldap_backend_extra_port=None, ldap_dryrun_mode=False, - ol_mmr_urls=None, nosync=False, ldap_backend_forced_uri=None): - from samba.provision import setup_path - super(OpenLDAPBackend, self).__init__( backend_type=backend_type, - paths=paths, lp=lp, - credentials=credentials, names=names, logger=logger, - domainsid=domainsid, schema=schema, hostname=hostname, - ldapadminpass=ldapadminpass, slapd_path=slapd_path, - ldap_backend_extra_port=ldap_backend_extra_port, - ldap_backend_forced_uri=ldap_backend_forced_uri, - ldap_dryrun_mode=ldap_dryrun_mode) - - self.ol_mmr_urls = ol_mmr_urls - self.nosync = nosync - - self.slapdconf = os.path.join(self.ldapdir, "slapd.conf") - self.modulesconf = os.path.join(self.ldapdir, "modules.conf") - self.memberofconf = os.path.join(self.ldapdir, "memberof.conf") - self.olmmrserveridsconf = os.path.join(self.ldapdir, "mmr_serverids.conf") - self.olmmrsyncreplconf = os.path.join(self.ldapdir, "mmr_syncrepl.conf") - self.olcdir = os.path.join(self.ldapdir, "slapd.d") - self.olcseedldif = os.path.join(self.ldapdir, "olc_seed.ldif") - - self.schema = Schema(self.domainsid, - schemadn=self.names.schemadn, files=[ - setup_path("schema_samba4.ldif")]) - - def setup_db_config(self, dbdir): - """Setup a Berkeley database. - - :param dbdir: Database directory. - """ - from samba.provision import setup_path - if not os.path.isdir(os.path.join(dbdir, "bdb-logs")): - os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700) - if not os.path.isdir(os.path.join(dbdir, "tmp")): - os.makedirs(os.path.join(dbdir, "tmp"), 0700) - - setup_file(setup_path("DB_CONFIG"), - os.path.join(dbdir, "DB_CONFIG"), {"LDAPDBDIR": dbdir}) - - def provision(self): - from samba.provision import ProvisioningError, setup_path - # Wipe the directories so we can start - shutil.rmtree(os.path.join(self.ldapdir, "db"), True) - - # Allow the test scripts to turn off fsync() for OpenLDAP as for TDB - # and LDB - nosync_config = "" - if self.nosync: - nosync_config = "dbnosync" - - lnkattr = self.schema.linked_attributes() - refint_attributes = "" - memberof_config = "# Generated from Samba4 schema\n" - for att in lnkattr.keys(): - if lnkattr[att] is not None: - refint_attributes = refint_attributes + " " + att - - memberof_config += read_and_sub_file( - setup_path("memberof.conf"), { - "MEMBER_ATTR": att, - "MEMBEROF_ATTR" : lnkattr[att] }) - - refint_config = read_and_sub_file(setup_path("refint.conf"), - { "LINK_ATTRS" : refint_attributes}) - - attrs = ["linkID", "lDAPDisplayName"] - res = self.schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=self.names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) - index_config = "" - for i in range (0, len(res)): - index_attr = res[i]["lDAPDisplayName"][0] - if index_attr == "objectGUID": - index_attr = "entryUUID" - - index_config += "index " + index_attr + " eq\n" - - # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts - mmr_on_config = "" - mmr_replicator_acl = "" - mmr_serverids_config = "" - mmr_syncrepl_schema_config = "" - mmr_syncrepl_config_config = "" - mmr_syncrepl_user_config = "" - - if self.ol_mmr_urls is not None: - # For now, make these equal - mmr_pass = self.ldapadminpass - - url_list = filter(None,self.ol_mmr_urls.split(',')) - for url in url_list: - self.logger.info("Using LDAP-URL: "+url) - if len(url_list) == 1: - raise ProvisioningError("At least 2 LDAP-URLs needed for MMR!") - - mmr_on_config = "MirrorMode On" - mmr_replicator_acl = " by dn=cn=replicator,cn=samba read" - serverid = 0 - for url in url_list: - serverid = serverid + 1 - mmr_serverids_config += read_and_sub_file( - setup_path("mmr_serverids.conf"), { - "SERVERID": str(serverid), - "LDAPSERVER": url }) - rid = serverid * 10 - rid = rid + 1 - mmr_syncrepl_schema_config += read_and_sub_file( - setup_path("mmr_syncrepl.conf"), { - "RID" : str(rid), - "MMRDN": self.names.schemadn, - "LDAPSERVER" : url, - "MMR_PASSWORD": mmr_pass}) - - rid = rid + 1 - mmr_syncrepl_config_config += read_and_sub_file( - setup_path("mmr_syncrepl.conf"), { - "RID" : str(rid), - "MMRDN": self.names.configdn, - "LDAPSERVER" : url, - "MMR_PASSWORD": mmr_pass}) - - rid = rid + 1 - mmr_syncrepl_user_config += read_and_sub_file( - setup_path("mmr_syncrepl.conf"), { - "RID" : str(rid), - "MMRDN": self.names.domaindn, - "LDAPSERVER" : url, - "MMR_PASSWORD": mmr_pass }) - # OpenLDAP cn=config initialisation - olc_syncrepl_config = "" - olc_mmr_config = "" - # if mmr = yes, generate cn=config-replication directives - # and olc_seed.lif for the other mmr-servers - if self.ol_mmr_urls is not None: - serverid = 0 - olc_serverids_config = "" - olc_syncrepl_seed_config = "" - olc_mmr_config += read_and_sub_file( - setup_path("olc_mmr.conf"), {}) - rid = 500 - for url in url_list: - serverid = serverid + 1 - olc_serverids_config += read_and_sub_file( - setup_path("olc_serverid.conf"), { - "SERVERID" : str(serverid), "LDAPSERVER" : url }) - - rid = rid + 1 - olc_syncrepl_config += read_and_sub_file( - setup_path("olc_syncrepl.conf"), { - "RID" : str(rid), "LDAPSERVER" : url, - "MMR_PASSWORD": mmr_pass}) - - olc_syncrepl_seed_config += read_and_sub_file( - setup_path("olc_syncrepl_seed.conf"), { - "RID" : str(rid), "LDAPSERVER" : url}) - - setup_file(setup_path("olc_seed.ldif"), self.olcseedldif, - {"OLC_SERVER_ID_CONF": olc_serverids_config, - "OLC_PW": self.ldapadminpass, - "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config}) - # end olc - - setup_file(setup_path("slapd.conf"), self.slapdconf, - {"DNSDOMAIN": self.names.dnsdomain, - "LDAPDIR": self.ldapdir, - "DOMAINDN": self.names.domaindn, - "CONFIGDN": self.names.configdn, - "SCHEMADN": self.names.schemadn, - "MEMBEROF_CONFIG": memberof_config, - "MIRRORMODE": mmr_on_config, - "REPLICATOR_ACL": mmr_replicator_acl, - "MMR_SERVERIDS_CONFIG": mmr_serverids_config, - "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config, - "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config, - "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config, - "OLC_SYNCREPL_CONFIG": olc_syncrepl_config, - "OLC_MMR_CONFIG": olc_mmr_config, - "REFINT_CONFIG": refint_config, - "INDEX_CONFIG": index_config, - "NOSYNC": nosync_config}) - - self.setup_db_config(os.path.join(self.ldapdir, "db", "user")) - self.setup_db_config(os.path.join(self.ldapdir, "db", "config")) - self.setup_db_config(os.path.join(self.ldapdir, "db", "schema")) - - if not os.path.exists(os.path.join(self.ldapdir, "db", "samba", "cn=samba")): - os.makedirs(os.path.join(self.ldapdir, "db", "samba", "cn=samba"), 0700) - - setup_file(setup_path("cn=samba.ldif"), - os.path.join(self.ldapdir, "db", "samba", "cn=samba.ldif"), - { "UUID": str(uuid.uuid4()), - "LDAPTIME": timestring(int(time.time()))} ) - setup_file(setup_path("cn=samba-admin.ldif"), - os.path.join(self.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"), - {"LDAPADMINPASS_B64": b64encode(self.ldapadminpass), - "UUID": str(uuid.uuid4()), - "LDAPTIME": timestring(int(time.time()))} ) - - if self.ol_mmr_urls is not None: - setup_file(setup_path("cn=replicator.ldif"), - os.path.join(self.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"), - {"MMR_PASSWORD_B64": b64encode(mmr_pass), - "UUID": str(uuid.uuid4()), - "LDAPTIME": timestring(int(time.time()))} ) - - mapping = "schema-map-openldap-2.3" - backend_schema = "backend-schema.schema" - - f = open(setup_path(mapping), 'r') - backend_schema_data = self.schema.convert_to_openldap( - "openldap", f.read()) - assert backend_schema_data is not None - f = open(os.path.join(self.ldapdir, backend_schema), 'w') - try: - f.write(backend_schema_data) - finally: - f.close() - - # now we generate the needed strings to start slapd automatically, - if self.ldap_backend_extra_port is not None: - # When we use MMR, we can't use 0.0.0.0 as it uses the name - # specified there as part of it's clue as to it's own name, - # and not to replicate to itself - if self.ol_mmr_urls is None: - server_port_string = "ldap://0.0.0.0:%d" % self.ldap_backend_extra_port - else: - server_port_string = "ldap://%s.%s:%d" (self.names.hostname, - self.names.dnsdomain, self.ldap_backend_extra_port) - else: - server_port_string = "" - - # Prepare the 'result' information - the commands to return in - # particular - self.slapd_provision_command = [self.slapd_path, "-F" + self.olcdir, - "-h"] - - # copy this command so we have two version, one with -d0 and only - # ldapi (or the forced ldap_uri), and one with all the listen commands - self.slapd_command = list(self.slapd_provision_command) - - self.slapd_provision_command.extend([self.ldap_uri, "-d0"]) - - uris = self.ldap_uri - if server_port_string is not "": - uris = uris + " " + server_port_string - - self.slapd_command.append(uris) - - # Set the username - done here because Fedora DS still uses the admin - # DN and simple bind - self.credentials.set_username("samba-admin") - - # Wipe the old sam.ldb databases away - shutil.rmtree(self.olcdir, True) - os.makedirs(self.olcdir, 0770) - - # If we were just looking for crashes up to this point, it's a - # good time to exit before we realise we don't have OpenLDAP on - # this system - if self.ldap_dryrun_mode: - sys.exit(0) - - slapd_cmd = [self.slapd_path, "-Ttest", "-n", "0", "-f", - self.slapdconf, "-F", self.olcdir] - retcode = subprocess.call(slapd_cmd, close_fds=True, shell=False) - - if retcode != 0: - self.logger.error("conversion from slapd.conf to cn=config failed slapd started with: %s" % "\'" + "\' \'".join(slapd_cmd) + "\'") - raise ProvisioningError("conversion from slapd.conf to cn=config failed") - - if not os.path.exists(os.path.join(self.olcdir, "cn=config.ldif")): - raise ProvisioningError("conversion from slapd.conf to cn=config failed") - - # Don't confuse the admin by leaving the slapd.conf around - os.remove(self.slapdconf) - - -class FDSBackend(LDAPBackend): - - def __init__(self, backend_type, paths=None, lp=None, - credentials=None, names=None, logger=None, domainsid=None, - schema=None, hostname=None, ldapadminpass=None, slapd_path=None, - ldap_backend_extra_port=None, ldap_dryrun_mode=False, root=None, - setup_ds_path=None): - - from samba.provision import setup_path - - super(FDSBackend, self).__init__(backend_type=backend_type, - paths=paths, lp=lp, - credentials=credentials, names=names, logger=logger, - domainsid=domainsid, schema=schema, hostname=hostname, - ldapadminpass=ldapadminpass, slapd_path=slapd_path, - ldap_backend_extra_port=ldap_backend_extra_port, - ldap_backend_forced_uri=ldap_backend_forced_uri, - ldap_dryrun_mode=ldap_dryrun_mode) - - self.root = root - self.setup_ds_path = setup_ds_path - self.ldap_instance = self.names.netbiosname.lower() - - self.sambadn = "CN=Samba" - - self.fedoradsinf = os.path.join(self.ldapdir, "fedorads.inf") - self.partitions_ldif = os.path.join(self.ldapdir, - "fedorads-partitions.ldif") - self.sasl_ldif = os.path.join(self.ldapdir, "fedorads-sasl.ldif") - self.dna_ldif = os.path.join(self.ldapdir, "fedorads-dna.ldif") - self.pam_ldif = os.path.join(self.ldapdir, "fedorads-pam.ldif") - self.refint_ldif = os.path.join(self.ldapdir, "fedorads-refint.ldif") - self.linked_attrs_ldif = os.path.join(self.ldapdir, - "fedorads-linked-attributes.ldif") - self.index_ldif = os.path.join(self.ldapdir, "fedorads-index.ldif") - self.samba_ldif = os.path.join(self.ldapdir, "fedorads-samba.ldif") - - self.samba3_schema = setup_path( - "../../examples/LDAP/samba.schema") - self.samba3_ldif = os.path.join(self.ldapdir, "samba3.ldif") - - self.retcode = subprocess.call(["bin/oLschema2ldif", - "-I", self.samba3_schema, - "-O", self.samba3_ldif, - "-b", self.names.domaindn], - close_fds=True, shell=False) - - if self.retcode != 0: - raise Exception("Unable to convert Samba 3 schema.") - - self.schema = Schema( - self.domainsid, - schemadn=self.names.schemadn, - files=[setup_path("schema_samba4.ldif"), self.samba3_ldif], - additional_prefixmap=["1000:1.3.6.1.4.1.7165.2.1", - "1001:1.3.6.1.4.1.7165.2.2"]) - - def provision(self): - from samba.provision import ProvisioningError, setup_path - if self.ldap_backend_extra_port is not None: - serverport = "ServerPort=%d" % self.ldap_backend_extra_port - else: - serverport = "" - - setup_file(setup_path("fedorads.inf"), self.fedoradsinf, - {"ROOT": self.root, - "HOSTNAME": self.hostname, - "DNSDOMAIN": self.names.dnsdomain, - "LDAPDIR": self.ldapdir, - "DOMAINDN": self.names.domaindn, - "LDAP_INSTANCE": self.ldap_instance, - "LDAPMANAGERDN": self.names.ldapmanagerdn, - "LDAPMANAGERPASS": self.ldapadminpass, - "SERVERPORT": serverport}) - - setup_file(setup_path("fedorads-partitions.ldif"), - self.partitions_ldif, - {"CONFIGDN": self.names.configdn, - "SCHEMADN": self.names.schemadn, - "SAMBADN": self.sambadn, - }) - - setup_file(setup_path("fedorads-sasl.ldif"), self.sasl_ldif, - {"SAMBADN": self.sambadn, - }) - - setup_file(setup_path("fedorads-dna.ldif"), self.dna_ldif, - {"DOMAINDN": self.names.domaindn, - "SAMBADN": self.sambadn, - "DOMAINSID": str(self.domainsid), - }) - - setup_file(setup_path("fedorads-pam.ldif"), self.pam_ldif) - - lnkattr = self.schema.linked_attributes() - - refint_config = open(setup_path("fedorads-refint-delete.ldif"), 'r').read() - memberof_config = "" - index_config = "" - argnum = 3 - - for attr in lnkattr.keys(): - if lnkattr[attr] is not None: - refint_config += read_and_sub_file( - setup_path("fedorads-refint-add.ldif"), - { "ARG_NUMBER" : str(argnum), - "LINK_ATTR" : attr }) - memberof_config += read_and_sub_file( - setup_path("fedorads-linked-attributes.ldif"), - { "MEMBER_ATTR" : attr, - "MEMBEROF_ATTR" : lnkattr[attr] }) - index_config += read_and_sub_file( - setup_path("fedorads-index.ldif"), { "ATTR" : attr }) - argnum += 1 - - open(self.refint_ldif, 'w').write(refint_config) - open(self.linked_attrs_ldif, 'w').write(memberof_config) - - attrs = ["lDAPDisplayName"] - res = self.schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=self.names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) - - for i in range (0, len(res)): - attr = res[i]["lDAPDisplayName"][0] - - if attr == "objectGUID": - attr = "nsUniqueId" - - index_config += read_and_sub_file( - setup_path("fedorads-index.ldif"), { "ATTR" : attr }) - - open(self.index_ldif, 'w').write(index_config) - - setup_file(setup_path("fedorads-samba.ldif"), self.samba_ldif, { - "SAMBADN": self.sambadn, - "LDAPADMINPASS": self.ldapadminpass - }) - - mapping = "schema-map-fedora-ds-1.0" - backend_schema = "99_ad.ldif" - - # Build a schema file in Fedora DS format - backend_schema_data = self.schema.convert_to_openldap("fedora-ds", - open(setup_path(mapping), 'r').read()) - assert backend_schema_data is not None - f = open(os.path.join(self.ldapdir, backend_schema), 'w') - try: - f.write(backend_schema_data) - finally: - f.close() - - self.credentials.set_bind_dn(self.names.ldapmanagerdn) - - # Destory the target directory, or else setup-ds.pl will complain - fedora_ds_dir = os.path.join(self.ldapdir, - "slapd-" + self.ldap_instance) - shutil.rmtree(fedora_ds_dir, True) - - self.slapd_provision_command = [self.slapd_path, "-D", fedora_ds_dir, - "-i", self.slapd_pid] - # In the 'provision' command line, stay in the foreground so we can - # easily kill it - self.slapd_provision_command.append("-d0") - - #the command for the final run is the normal script - self.slapd_command = [os.path.join(self.ldapdir, - "slapd-" + self.ldap_instance, "start-slapd")] - - # If we were just looking for crashes up to this point, it's a - # good time to exit before we realise we don't have Fedora DS on - if self.ldap_dryrun_mode: - sys.exit(0) - - # Try to print helpful messages when the user has not specified the - # path to the setup-ds tool - if self.setup_ds_path is None: - raise ProvisioningError("Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!") - if not os.path.exists(self.setup_ds_path): - self.logger.warning("Path (%s) to slapd does not exist!", - self.setup_ds_path) - - # Run the Fedora DS setup utility - retcode = subprocess.call([self.setup_ds_path, "--silent", "--file", - self.fedoradsinf], close_fds=True, shell=False) - if retcode != 0: - raise ProvisioningError("setup-ds failed") - - # Load samba-admin - retcode = subprocess.call([ - os.path.join(self.ldapdir, "slapd-" + self.ldap_instance, "ldif2db"), "-s", self.sambadn, "-i", self.samba_ldif], - close_fds=True, shell=False) - if retcode != 0: - raise ProvisioningError("ldif2db failed") - - def post_setup(self): - ldapi_db = Ldb(self.ldap_uri, credentials=self.credentials) - - # configure in-directory access control on Fedora DS via the aci - # attribute (over a direct ldapi:// socket) - aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % self.sambadn - - m = ldb.Message() - m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci") - - for dnstring in (self.names.domaindn, self.names.configdn, - self.names.schemadn): - m.dn = ldb.Dn(ldapi_db, dnstring) - ldapi_db.modify(m) diff --git a/source4/scripting/python/samba/samba3.py b/source4/scripting/python/samba/samba3.py index 2c323bd0b4..179efa2700 100644 --- a/source4/scripting/python/samba/samba3.py +++ b/source4/scripting/python/samba/samba3.py @@ -1,16 +1,18 @@ +#!/usr/bin/python + # Unix SMB/CIFS implementation. # 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 # 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/>. # @@ -65,7 +67,7 @@ class TdbDatabase(object): class Registry(TdbDatabase): """Simple read-only support for reading the Samba3 registry. - + :note: This object uses the same syntax for registry key paths as Samba 3. This particular format uses forward slashes for key path separators and abbreviations for the predefined key names. @@ -681,7 +683,7 @@ class ParamFile(object): section = None for i, l in enumerate(open(filename, 'r').xreadlines()): l = l.strip() - if not l or l[0] == '#' or l[0] == ';': + if not l: continue if l[0] == "[" and l[-1] == "]": section = self._sanitize_name(l[1:-1]) @@ -767,7 +769,7 @@ class Samba3(object): def get_policy_db(self): return PolicyDatabase(self.libdir_path("account_policy.tdb")) - + def get_registry(self): return Registry(self.libdir_path("registry.tdb")) diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index 99f141e664..39cf1d6c40 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -1,22 +1,22 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010 +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 # Copyright (C) Matthias Dieter Wallnoefer 2009 # # Based on the original in EJS: # 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/>. # @@ -24,71 +24,62 @@ """Convenience functions for using the SAM.""" import samba +import glue import ldb +from samba.idmap import IDmapDB +import pwd import time import base64 -from samba import dsdb -from samba.ndr import ndr_unpack, ndr_pack -from samba.dcerpc import drsblobs, misc __docformat__ = "restructuredText" - class SamDB(samba.Ldb): """The SAM database.""" - hash_oid_name = {} - def __init__(self, url=None, lp=None, modules_dir=None, session_info=None, - credentials=None, flags=0, options=None, global_schema=True, - auto_connect=True, am_rodc=None): + credentials=None, flags=0, options=None): + """Opens the SAM Database + For parameter meanings see the super class (samba.Ldb) + """ + self.lp = lp - if not auto_connect: - url = None - elif url is None and lp is not None: - url = lp.get("sam database") + if url is None: + url = lp.get("sam database") super(SamDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir, - session_info=session_info, credentials=credentials, flags=flags, - options=options) - - if global_schema: - dsdb._dsdb_set_global_schema(self) + session_info=session_info, credentials=credentials, flags=flags, + options=options) - if am_rodc is not None: - dsdb._dsdb_set_am_rodc(self, am_rodc) + glue.dsdb_set_global_schema(self) def connect(self, url=None, flags=0, options=None): - if self.lp is not None: - url = self.lp.private_path(url) - - super(SamDB, self).connect(url=url, flags=flags, + super(SamDB, self).connect(url=self.lp.private_path(url), flags=flags, options=options) - def am_rodc(self): - return dsdb._am_rodc(self) - def domain_dn(self): - return str(self.get_default_basedn()) - - def enable_account(self, search_filter): + # find the DNs for the domain + res = self.search(base="", + scope=ldb.SCOPE_BASE, + expression="(defaultNamingContext=*)", + attrs=["defaultNamingContext"]) + assert(len(res) == 1 and res[0]["defaultNamingContext"] is not None) + return res[0]["defaultNamingContext"][0] + + def enable_account(self, filter): """Enables an account - - :param search_filter: LDAP filter to find the user (eg - samccountname=name) + + :param filter: LDAP filter to find the user (eg samccountname=name) """ res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, - expression=search_filter, attrs=["userAccountControl"]) + expression=filter, attrs=["userAccountControl"]) assert(len(res) == 1) user_dn = res[0].dn userAccountControl = int(res[0]["userAccountControl"][0]) - if userAccountControl & 0x2: - # remove disabled bit - userAccountControl = userAccountControl & ~0x2 - if userAccountControl & 0x20: - # remove 'no password required' bit - userAccountControl = userAccountControl & ~0x20 + if (userAccountControl & 0x2): + userAccountControl = userAccountControl & ~0x2 # remove disabled bit + if (userAccountControl & 0x20): + userAccountControl = userAccountControl & ~0x20 # remove 'no password required' bit mod = """ dn: %s @@ -97,15 +88,14 @@ replace: userAccountControl userAccountControl: %u """ % (user_dn, userAccountControl) self.modify_ldif(mod) - - def force_password_change_at_next_login(self, search_filter): + + def force_password_change_at_next_login(self, filter): """Forces a password change at next login - - :param search_filter: LDAP filter to find the user (eg - samccountname=name) + + :param filter: LDAP filter to find the user (eg samccountname=name) """ res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, - expression=search_filter, attrs=[]) + expression=filter, attrs=[]) assert(len(res) == 1) user_dn = res[0].dn @@ -117,297 +107,102 @@ pwdLastSet: 0 """ % (user_dn) self.modify_ldif(mod) - def newgroup(self, groupname, groupou=None, grouptype=None, - description=None, mailaddress=None, notes=None, sd=None): - """Adds a new group with additional parameters - - :param groupname: Name of the new group - :param grouptype: Type of the new group - :param description: Description of the new group - :param mailaddress: Email address of the new group - :param notes: Notes of the new group - :param sd: security descriptor of the object - """ - - group_dn = "CN=%s,%s,%s" % (groupname, (groupou or "CN=Users"), self.domain_dn()) - - # The new user record. Note the reliance on the SAMLDB module which - # fills in the default informations - ldbmessage = {"dn": group_dn, - "sAMAccountName": groupname, - "objectClass": "group"} - - if grouptype is not None: - ldbmessage["groupType"] = "%d" % grouptype - - if description is not None: - ldbmessage["description"] = description - - if mailaddress is not None: - ldbmessage["mail"] = mailaddress - - if notes is not None: - ldbmessage["info"] = notes - - if sd is not None: - ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd) - - self.add(ldbmessage) - - def deletegroup(self, groupname): - """Deletes a group - - :param groupname: Name of the target group - """ - - groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (groupname, "CN=Group,CN=Schema,CN=Configuration", self.domain_dn()) - self.transaction_start() - try: - targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, - expression=groupfilter, attrs=[]) - if len(targetgroup) == 0: - raise Exception('Unable to find group "%s"' % groupname) - assert(len(targetgroup) == 1) - self.delete(targetgroup[0].dn) - except Exception: - self.transaction_cancel() - raise - else: - self.transaction_commit() - - def add_remove_group_members(self, groupname, listofmembers, - add_members_operation=True): - """Adds or removes group members - - :param groupname: Name of the target group - :param listofmembers: Comma-separated list of group members - :param add_members_operation: Defines if its an add or remove - operation - """ - - groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (groupname, "CN=Group,CN=Schema,CN=Configuration", self.domain_dn()) - groupmembers = listofmembers.split(',') - - self.transaction_start() - try: - targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, - expression=groupfilter, attrs=['member']) - if len(targetgroup) == 0: - raise Exception('Unable to find group "%s"' % groupname) - assert(len(targetgroup) == 1) - - modified = False - - addtargettogroup = """ -dn: %s -changetype: modify -""" % (str(targetgroup[0].dn)) - - for member in groupmembers: - targetmember = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, - expression="(|(sAMAccountName=%s)(CN=%s))" % (member, member), attrs=[]) - - if len(targetmember) != 1: - continue - - if add_members_operation is True and (targetgroup[0].get('member') is None or str(targetmember[0].dn) not in targetgroup[0]['member']): - modified = True - addtargettogroup += """add: member -member: %s -""" % (str(targetmember[0].dn)) - - elif add_members_operation is False and (targetgroup[0].get('member') is not None and str(targetmember[0].dn) in targetgroup[0]['member']): - modified = True - addtargettogroup += """delete: member -member: %s -""" % (str(targetmember[0].dn)) - - if modified is True: - self.modify_ldif(addtargettogroup) - - except Exception: - self.transaction_cancel() - raise - else: - self.transaction_commit() - - def newuser(self, username, password, - force_password_change_at_next_login_req=False, - useusernameascn=False, userou=None, surname=None, givenname=None, - initials=None, profilepath=None, scriptpath=None, homedrive=None, - homedirectory=None, jobtitle=None, department=None, company=None, - description=None, mailaddress=None, internetaddress=None, - telephonenumber=None, physicaldeliveryoffice=None, sd=None, - setpassword=True): - """Adds a new user with additional parameters + def newuser(self, username, unixname, password, force_password_change_at_next_login_req=False): + """Adds a new user + Note: This call adds also the ID mapping for winbind; therefore it works + *only* on SAMBA 4. + :param username: Name of the new user + :param unixname: Name of the unix user to map to :param password: Password for the new user :param force_password_change_at_next_login_req: Force password change - :param useusernameascn: Use username as cn rather that firstname + - initials + lastname - :param userou: Object container (without domainDN postfix) for new user - :param surname: Surname of the new user - :param givenname: First name of the new user - :param initials: Initials of the new user - :param profilepath: Profile path of the new user - :param scriptpath: Logon script path of the new user - :param homedrive: Home drive of the new user - :param homedirectory: Home directory of the new user - :param jobtitle: Job title of the new user - :param department: Department of the new user - :param company: Company of the new user - :param description: of the new user - :param mailaddress: Email address of the new user - :param internetaddress: Home page of the new user - :param telephonenumber: Phone number of the new user - :param physicaldeliveryoffice: Office location of the new user - :param sd: security descriptor of the object - :param setpassword: optionally disable password reset """ - - displayname = "" - if givenname is not None: - displayname += givenname - - if initials is not None: - displayname += ' %s.' % initials - - if surname is not None: - displayname += ' %s' % surname - - cn = username - if useusernameascn is None and displayname is not "": - cn = displayname - - user_dn = "CN=%s,%s,%s" % (cn, (userou or "CN=Users"), self.domain_dn()) - - dnsdomain = ldb.Dn(self, self.domain_dn()).canonical_str().replace("/", "") - user_principal_name = "%s@%s" % (username, dnsdomain) - # The new user record. Note the reliance on the SAMLDB module which - # fills in the default informations - ldbmessage = {"dn": user_dn, - "sAMAccountName": username, - "userPrincipalName": user_principal_name, - "objectClass": "user"} - - if surname is not None: - ldbmessage["sn"] = surname - - if givenname is not None: - ldbmessage["givenName"] = givenname - - if displayname is not "": - ldbmessage["displayName"] = displayname - ldbmessage["name"] = displayname - - if initials is not None: - ldbmessage["initials"] = '%s.' % initials - - if profilepath is not None: - ldbmessage["profilePath"] = profilepath - - if scriptpath is not None: - ldbmessage["scriptPath"] = scriptpath - - if homedrive is not None: - ldbmessage["homeDrive"] = homedrive - - if homedirectory is not None: - ldbmessage["homeDirectory"] = homedirectory - - if jobtitle is not None: - ldbmessage["title"] = jobtitle - - if department is not None: - ldbmessage["department"] = department - - if company is not None: - ldbmessage["company"] = company - - if description is not None: - ldbmessage["description"] = description - - if mailaddress is not None: - ldbmessage["mail"] = mailaddress - - if internetaddress is not None: - ldbmessage["wWWHomePage"] = internetaddress - - if telephonenumber is not None: - ldbmessage["telephoneNumber"] = telephonenumber - - if physicaldeliveryoffice is not None: - ldbmessage["physicalDeliveryOfficeName"] = physicaldeliveryoffice - - if sd is not None: - ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd) - self.transaction_start() try: - self.add(ldbmessage) + user_dn = "CN=%s,CN=Users,%s" % (username, self.domain_dn()) + + # The new user record. Note the reliance on the SAMLDB module which + # fills in the default informations + self.add({"dn": user_dn, + "sAMAccountName": username, + "objectClass": "user"}) # Sets the password for it - if setpassword: - self.setpassword("(samAccountName=%s)" % username, password, - force_password_change_at_next_login_req) - except Exception: + self.setpassword("(dn=" + user_dn + ")", password, + force_password_change_at_next_login_req) + + # Gets the user SID (for the account mapping setup) + res = self.search(user_dn, scope=ldb.SCOPE_BASE, + expression="objectclass=*", + attrs=["objectSid"]) + assert len(res) == 1 + user_sid = self.schema_format_value("objectSid", res[0]["objectSid"][0]) + + try: + idmap = IDmapDB(lp=self.lp) + + user = pwd.getpwnam(unixname) + + # setup ID mapping for this UID + idmap.setup_name_mapping(user_sid, idmap.TYPE_UID, user[2]) + + except KeyError: + pass + except: self.transaction_cancel() raise - else: - self.transaction_commit() + self.transaction_commit() - def setpassword(self, search_filter, password, - force_change_at_next_login=False, username=None): + def setpassword(self, filter, password, force_password_change_at_next_login_req=False): """Sets the password for a user + + Note: This call uses the "userPassword" attribute to set the password. + This works correctly on SAMBA 4 and on Windows DCs with + "2003 Native" or higer domain function level. - :param search_filter: LDAP filter to find the user (eg - samccountname=name) + :param filter: LDAP filter to find the user (eg samccountname=name) :param password: Password for the user - :param force_change_at_next_login: Force password change + :param force_password_change_at_next_login_req: Force password change """ self.transaction_start() try: res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, - expression=search_filter, attrs=[]) - if len(res) == 0: - raise Exception('Unable to find user "%s"' % (username or search_filter)) - if len(res) > 1: - raise Exception('Matched %u multiple users with filter "%s"' % (len(res), search_filter)) + expression=filter, attrs=[]) + assert(len(res) == 1) user_dn = res[0].dn + setpw = """ dn: %s changetype: modify -replace: unicodePwd -unicodePwd:: %s -""" % (user_dn, base64.b64encode(("\"" + password + "\"").encode('utf-16-le'))) +replace: userPassword +userPassword:: %s +""" % (user_dn, base64.b64encode(password)) self.modify_ldif(setpw) - if force_change_at_next_login: + if force_password_change_at_next_login_req: self.force_password_change_at_next_login( "(dn=" + str(user_dn) + ")") # modify the userAccountControl to remove the disabled bit - self.enable_account(search_filter) - except Exception: + self.enable_account(filter) + except: self.transaction_cancel() raise - else: - self.transaction_commit() + self.transaction_commit() - def setexpiry(self, search_filter, expiry_seconds, no_expiry_req=False): + def setexpiry(self, filter, expiry_seconds, no_expiry_req=False): """Sets the account expiry for a user - - :param search_filter: LDAP filter to find the user (eg - samaccountname=name) + + :param filter: LDAP filter to find the user (eg samccountname=name) :param expiry_seconds: expiry time from now in seconds :param no_expiry_req: if set, then don't expire password """ self.transaction_start() try: res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, - expression=search_filter, + expression=filter, attrs=["userAccountControl", "accountExpires"]) assert(len(res) == 1) user_dn = res[0].dn @@ -419,7 +214,7 @@ unicodePwd:: %s accountExpires = 0 else: userAccountControl = userAccountControl & ~0x10000 - accountExpires = samba.unix2nttime(expiry_seconds + int(time.time())) + accountExpires = glue.unix2nttime(expiry_seconds + int(time.time())) setexp = """ dn: %s @@ -431,283 +226,8 @@ accountExpires: %u """ % (user_dn, userAccountControl, accountExpires) self.modify_ldif(setexp) - except Exception: + except: self.transaction_cancel() raise - else: - self.transaction_commit() - - def set_domain_sid(self, sid): - """Change the domain SID used by this LDB. - - :param sid: The new domain sid to use. - """ - dsdb._samdb_set_domain_sid(self, sid) - - def get_domain_sid(self): - """Read the domain SID used by this LDB. """ - return dsdb._samdb_get_domain_sid(self) - - domain_sid = property(get_domain_sid, set_domain_sid, - "SID for the domain") - - def set_invocation_id(self, invocation_id): - """Set the invocation id for this SamDB handle. - - :param invocation_id: GUID of the invocation id. - """ - dsdb._dsdb_set_ntds_invocation_id(self, invocation_id) - - def get_invocation_id(self): - """Get the invocation_id id""" - return dsdb._samdb_ntds_invocation_id(self) - - invocation_id = property(get_invocation_id, set_invocation_id, - "Invocation ID GUID") - - def get_oid_from_attid(self, attid): - return dsdb._dsdb_get_oid_from_attid(self, attid) - - def get_attid_from_lDAPDisplayName(self, ldap_display_name, - is_schema_nc=False): - return dsdb._dsdb_get_attid_from_lDAPDisplayName(self, - ldap_display_name, is_schema_nc) - - def set_ntds_settings_dn(self, ntds_settings_dn): - """Set the NTDS Settings DN, as would be returned on the dsServiceName - rootDSE attribute. - - This allows the DN to be set before the database fully exists - - :param ntds_settings_dn: The new DN to use - """ - dsdb._samdb_set_ntds_settings_dn(self, ntds_settings_dn) - - def get_ntds_GUID(self): - """Get the NTDS objectGUID""" - return dsdb._samdb_ntds_objectGUID(self) - - def server_site_name(self): - """Get the server site name""" - return dsdb._samdb_server_site_name(self) - - def load_partition_usn(self, base_dn): - return dsdb._dsdb_load_partition_usn(self, base_dn) - - def set_schema(self, schema): - self.set_schema_from_ldb(schema.ldb) - - def set_schema_from_ldb(self, ldb_conn): - dsdb._dsdb_set_schema_from_ldb(self, ldb_conn) - - def dsdb_DsReplicaAttribute(self, ldb, ldap_display_name, ldif_elements): - return dsdb._dsdb_DsReplicaAttribute(ldb, ldap_display_name, ldif_elements) - - def get_attribute_from_attid(self, attid): - """ Get from an attid the associated attribute - - :param attid: The attribute id for searched attribute - :return: The name of the attribute associated with this id - """ - if len(self.hash_oid_name.keys()) == 0: - self._populate_oid_attid() - if self.hash_oid_name.has_key(self.get_oid_from_attid(attid)): - return self.hash_oid_name[self.get_oid_from_attid(attid)] - else: - return None - - def _populate_oid_attid(self): - """Populate the hash hash_oid_name. - - This hash contains the oid of the attribute as a key and - its display name as a value - """ - self.hash_oid_name = {} - res = self.search(expression="objectClass=attributeSchema", - controls=["search_options:1:2"], - attrs=["attributeID", - "lDAPDisplayName"]) - if len(res) > 0: - for e in res: - strDisplay = str(e.get("lDAPDisplayName")) - self.hash_oid_name[str(e.get("attributeID"))] = strDisplay - - def get_attribute_replmetadata_version(self, dn, att): - """Get the version field trom the replPropertyMetaData for - the given field - - :param dn: The on which we want to get the version - :param att: The name of the attribute - :return: The value of the version field in the replPropertyMetaData - for the given attribute. None if the attribute is not replicated - """ - - res = self.search(expression="dn=%s" % dn, - scope=ldb.SCOPE_SUBTREE, - controls=["search_options:1:2"], - attrs=["replPropertyMetaData"]) - if len(res) == 0: - return None - - repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, - str(res[0]["replPropertyMetaData"])) - ctr = repl.ctr - if len(self.hash_oid_name.keys()) == 0: - self._populate_oid_attid() - for o in ctr.array: - # Search for Description - att_oid = self.get_oid_from_attid(o.attid) - if self.hash_oid_name.has_key(att_oid) and\ - att.lower() == self.hash_oid_name[att_oid].lower(): - return o.version - return None - - def set_attribute_replmetadata_version(self, dn, att, value, - addifnotexist=False): - res = self.search(expression="dn=%s" % dn, - scope=ldb.SCOPE_SUBTREE, - controls=["search_options:1:2"], - attrs=["replPropertyMetaData"]) - if len(res) == 0: - return None - - repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, - str(res[0]["replPropertyMetaData"])) - ctr = repl.ctr - now = samba.unix2nttime(int(time.time())) - found = False - if len(self.hash_oid_name.keys()) == 0: - self._populate_oid_attid() - for o in ctr.array: - # Search for Description - att_oid = self.get_oid_from_attid(o.attid) - if self.hash_oid_name.has_key(att_oid) and\ - att.lower() == self.hash_oid_name[att_oid].lower(): - found = True - seq = self.sequence_number(ldb.SEQ_NEXT) - o.version = value - o.originating_change_time = now - o.originating_invocation_id = misc.GUID(self.get_invocation_id()) - o.originating_usn = seq - o.local_usn = seq - - if not found and addifnotexist and len(ctr.array) >0: - o2 = drsblobs.replPropertyMetaData1() - o2.attid = 589914 - att_oid = self.get_oid_from_attid(o2.attid) - seq = self.sequence_number(ldb.SEQ_NEXT) - o2.version = value - o2.originating_change_time = now - o2.originating_invocation_id = misc.GUID(self.get_invocation_id()) - o2.originating_usn = seq - o2.local_usn = seq - found = True - tab = ctr.array - tab.append(o2) - ctr.count = ctr.count + 1 - ctr.array = tab - - if found : - replBlob = ndr_pack(repl) - msg = ldb.Message() - msg.dn = res[0].dn - msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, - ldb.FLAG_MOD_REPLACE, - "replPropertyMetaData") - self.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) - - def write_prefixes_from_schema(self): - dsdb._dsdb_write_prefixes_from_schema_to_ldb(self) - - def get_partitions_dn(self): - return dsdb._dsdb_get_partitions_dn(self) - - def set_minPwdAge(self, value): - m = ldb.Message() - m.dn = ldb.Dn(self, self.domain_dn()) - m["minPwdAge"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdAge") - self.modify(m) - - def get_minPwdAge(self): - res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdAge"]) - if len(res) == 0: - return None - elif not "minPwdAge" in res[0]: - return None - else: - return res[0]["minPwdAge"][0] - - def set_minPwdLength(self, value): - m = ldb.Message() - m.dn = ldb.Dn(self, self.domain_dn()) - m["minPwdLength"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdLength") - self.modify(m) - - def get_minPwdLength(self): - res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdLength"]) - if len(res) == 0: - return None - elif not "minPwdLength" in res[0]: - return None - else: - return res[0]["minPwdLength"][0] - - def set_pwdProperties(self, value): - m = ldb.Message() - m.dn = ldb.Dn(self, self.domain_dn()) - m["pwdProperties"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "pwdProperties") - self.modify(m) - - def get_pwdProperties(self): - res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["pwdProperties"]) - if len(res) == 0: - return None - elif not "pwdProperties" in res[0]: - return None - else: - return res[0]["pwdProperties"][0] - - def set_dsheuristics(self, dsheuristics): - m = ldb.Message() - m.dn = ldb.Dn(self, "CN=Directory Service,CN=Windows NT,CN=Services,%s" - % self.get_config_basedn().get_linearized()) - if dsheuristics is not None: - m["dSHeuristics"] = ldb.MessageElement(dsheuristics, - ldb.FLAG_MOD_REPLACE, "dSHeuristics") - else: - m["dSHeuristics"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE, - "dSHeuristics") - self.modify(m) - - def get_dsheuristics(self): - res = self.search("CN=Directory Service,CN=Windows NT,CN=Services,%s" - % self.get_config_basedn().get_linearized(), - scope=ldb.SCOPE_BASE, attrs=["dSHeuristics"]) - if len(res) == 0: - dsheuristics = None - elif "dSHeuristics" in res[0]: - dsheuristics = res[0]["dSHeuristics"][0] - else: - dsheuristics = None - - return dsheuristics - - def create_ou(self, ou_dn, description=None, name=None, sd=None): - """Creates an organizationalUnit object - :param ou_dn: dn of the new object - :param description: description attribute - :param name: name atttribute - :param sd: security descriptor of the object, can be - an SDDL string or security.descriptor type - """ - m = {"dn": ou_dn, - "objectClass": "organizationalUnit"} - - if description: - m["description"] = description - if name: - m["name"] = name + self.transaction_commit(); - if sd: - m["nTSecurityDescriptor"] = ndr_pack(sd) - self.add(m) diff --git a/source4/scripting/python/samba/schema.py b/source4/scripting/python/samba/schema.py deleted file mode 100644 index 8bac26e24f..0000000000 --- a/source4/scripting/python/samba/schema.py +++ /dev/null @@ -1,204 +0,0 @@ -# -# 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-2009 -# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009 -# -# 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/>. -# - -"""Functions for setting up a Samba Schema.""" - -from base64 import b64encode -from samba import read_and_sub_file, substitute_var, check_all_substituted -from samba.dcerpc import security -from samba.ms_schema import read_ms_schema -from samba.ndr import ndr_pack -from samba.samdb import SamDB -from samba import dsdb -from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL -import os - -def get_schema_descriptor(domain_sid): - sddl = "O:SAG:SAD:AI(OA;;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;SA)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(A;CI;RPLCLORC;;;AU)" \ - "(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \ - "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \ - "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \ - "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \ - "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ER)" \ - "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \ - "(AU;CISA;WP;;;WD)" \ - "(AU;SA;CR;;;BA)" \ - "(AU;SA;CR;;;DU)" \ - "(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \ - "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)" - sec = security.descriptor.from_sddl(sddl, domain_sid) - return ndr_pack(sec) - - -class Schema(object): - - def __init__(self, domain_sid, invocationid=None, schemadn=None, - files=None, override_prefixmap=None, additional_prefixmap=None): - from samba.provision import setup_path - - """Load schema for the SamDB from the AD schema files and - samba4_schema.ldif - - :param samdb: Load a schema into a SamDB. - :param schemadn: DN of the schema - - Returns the schema data loaded, to avoid double-parsing when then - needing to add it to the db - """ - - self.schemadn = schemadn - # We need to have the am_rodc=False just to keep some warnings quiet - - # this isn't a real SAM, so it's meaningless. - self.ldb = SamDB(global_schema=False, am_rodc=False) - if invocationid is not None: - self.ldb.set_invocation_id(invocationid) - - self.schema_data = read_ms_schema( - setup_path('ad-schema/MS-AD_Schema_2K8_R2_Attributes.txt'), - setup_path('ad-schema/MS-AD_Schema_2K8_R2_Classes.txt')) - - if files is not None: - for file in files: - self.schema_data += open(file, 'r').read() - - self.schema_data = substitute_var(self.schema_data, - {"SCHEMADN": schemadn}) - check_all_substituted(self.schema_data) - - self.schema_dn_modify = read_and_sub_file( - setup_path("provision_schema_basedn_modify.ldif"), - {"SCHEMADN": schemadn}) - - descr = b64encode(get_schema_descriptor(domain_sid)) - self.schema_dn_add = read_and_sub_file( - setup_path("provision_schema_basedn.ldif"), - {"SCHEMADN": schemadn, "DESCRIPTOR": descr}) - - if override_prefixmap is not None: - self.prefixmap_data = override_prefixmap - else: - self.prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read() - - if additional_prefixmap is not None: - for map in additional_prefixmap: - self.prefixmap_data += "%s\n" % map - - self.prefixmap_data = b64encode(self.prefixmap_data) - - # We don't actually add this ldif, just parse it - prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % self.prefixmap_data - self.set_from_ldif(prefixmap_ldif, self.schema_data) - - def set_from_ldif(self, pf, df): - dsdb._dsdb_set_schema_from_ldif(self.ldb, pf, df) - - def write_to_tmp_ldb(self, schemadb_path): - self.ldb.connect(url=schemadb_path) - self.ldb.transaction_start() - try: - self.ldb.add_ldif("""dn: @ATTRIBUTES -linkID: INTEGER - -dn: @INDEXLIST -@IDXATTR: linkID -@IDXATTR: attributeSyntax -""") - # These bits of LDIF are supplied when the Schema object is created - self.ldb.add_ldif(self.schema_dn_add) - self.ldb.modify_ldif(self.schema_dn_modify) - self.ldb.add_ldif(self.schema_data) - except Exception: - self.ldb.transaction_cancel() - raise - else: - self.ldb.transaction_commit() - - # Return a hash with the forward attribute as a key and the back as the - # value - def linked_attributes(self): - return get_linked_attributes(self.schemadn, self.ldb) - - def dnsyntax_attributes(self): - return get_dnsyntax_attributes(self.schemadn, self.ldb) - - def convert_to_openldap(self, target, mapping): - return dsdb._dsdb_convert_schema_to_openldap(self.ldb, target, mapping) - - -# Return a hash with the forward attribute as a key and the back as the value -def get_linked_attributes(schemadn,schemaldb): - attrs = ["linkID", "lDAPDisplayName"] - res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs) - attributes = {} - for i in range (0, len(res)): - expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1) - target = schemaldb.searchone(basedn=schemadn, - expression=expression, - attribute="lDAPDisplayName", - scope=SCOPE_SUBTREE) - if target is not None: - attributes[str(res[i]["lDAPDisplayName"])]=str(target) - - return attributes - - -def get_dnsyntax_attributes(schemadn,schemaldb): - res = schemaldb.search( - expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", - base=schemadn, scope=SCOPE_ONELEVEL, - attrs=["linkID", "lDAPDisplayName"]) - attributes = [] - for i in range (0, len(res)): - attributes.append(str(res[i]["lDAPDisplayName"])) - return attributes - - -def ldb_with_schema(schemadn="cn=schema,cn=configuration,dc=example,dc=com", - domainsid=None, - override_prefixmap=None): - """Load schema for the SamDB from the AD schema files and samba4_schema.ldif - - :param schemadn: DN of the schema - :param serverdn: DN of the server - - Returns the schema data loaded as an object, with .ldb being a - new ldb with the schema loaded. This allows certain tests to - operate without a remote or local schema. - """ - - if domainsid is None: - domainsid = security.random_sid() - else: - domainsid = security.dom_sid(domainsid) - return Schema(domainsid, schemadn=schemadn, - override_prefixmap=override_prefixmap) diff --git a/source4/scripting/python/samba/sd_utils.py b/source4/scripting/python/samba/sd_utils.py deleted file mode 100644 index 4694f4cc76..0000000000 --- a/source4/scripting/python/samba/sd_utils.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python -# -# Utility methods for security descriptor manipulation -# -# Copyright Nadezhda Ivanova 2010 <nivanova@samba.org> -# -# 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/>. -# - -import samba -from ldb import Message, MessageElement, Dn -from ldb import FLAG_MOD_REPLACE, SCOPE_BASE -from samba.ndr import ndr_pack, ndr_unpack -from samba.dcerpc import security - -class SDUtils: - '''Some utilities for manipulation of security descriptors - on objects''' - - def __init__(self, samdb): - self.ldb = samdb - self.domain_sid = security.dom_sid(self.ldb.get_domain_sid()) - - def modify_sd_on_dn(self, object_dn, sd, controls=None): - """ Modify security descriptor using either SDDL string - or security.descriptor object - """ - m = Message() - m.dn = Dn(self.ldb, object_dn) - assert(isinstance(sd, str) or isinstance(sd, security.descriptor)) - if isinstance(sd, str): - tmp_desc = security.descriptor.from_sddl(sd, self.domain_sid) - elif isinstance(sd, security.descriptor): - tmp_desc = sd - - m["nTSecurityDescriptor"] = MessageElement(ndr_pack(tmp_desc), - FLAG_MOD_REPLACE, - "nTSecurityDescriptor") - self.ldb.modify(m, controls) - - def read_sd_on_dn(self, object_dn, controls=None): - res = self.ldb.search(object_dn, SCOPE_BASE, None, - ["nTSecurityDescriptor"], controls=controls) - desc = res[0]["nTSecurityDescriptor"][0] - return ndr_unpack(security.descriptor, desc) - - def get_object_sid(self, object_dn): - res = self.ldb.search(object_dn) - return ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) - - def dacl_add_ace(self, object_dn, ace): - """ Adds an ACE to an objects security descriptor - """ - desc = self.read_sd_on_dn(object_dn) - desc_sddl = desc.as_sddl(self.domain_sid) - if ace in desc_sddl: - return - if desc_sddl.find("(") >= 0: - desc_sddl = desc_sddl[:desc_sddl.index("(")] + ace + desc_sddl[desc_sddl.index("("):] - else: - desc_sddl = desc_sddl + ace - self.modify_sd_on_dn(object_dn, desc_sddl) - - def get_sd_as_sddl(self, object_dn, controls=None): - """ Return object nTSecutiryDescriptor in SDDL format - """ - desc = self.read_sd_on_dn(object_dn, controls=controls) - return desc.as_sddl(self.domain_sid) diff --git a/source4/scripting/python/samba/shares.py b/source4/scripting/python/samba/shares.py new file mode 100644 index 0000000000..89a312e855 --- /dev/null +++ b/source4/scripting/python/samba/shares.py @@ -0,0 +1,61 @@ +#!/usr/bin/python + +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009 +# +# 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/>. +# + +"""Share management.""" + + +# TODO: Rather than accessing Loadparm directly here, we should really +# have bindings to the param/shares.c and use those. + + +class SharesContainer(object): + """A shares container.""" + + def __init__(self, lp): + self._lp = lp + + def __getitem__(self, name): + if name == "global": + # [global] is not a share + raise KeyError + return Share(self._lp[name]) + + def __len__(self): + if "global" in self._lp: + return len(self._lp)-1 + return len(self._lp) + + def keys(self): + return [name for name in self._lp.services() if name != "global"] + + def __iter__(self): + return iter(self.keys()) + + +class Share(object): + """A file share.""" + + def __init__(self, service): + self._service = service + + def __getitem__(self, name): + return self._service[name] + + def __setitem__(self, name, value): + self._service[name] = value diff --git a/source4/scripting/python/samba/tests/__init__.py b/source4/scripting/python/samba/tests/__init__.py index 58e4130998..ae7a707e35 100644 --- a/source4/scripting/python/samba/tests/__init__.py +++ b/source4/scripting/python/samba/tests/__init__.py @@ -1,18 +1,18 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010 -# +# 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 # 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/>. # @@ -22,43 +22,13 @@ import os import ldb import samba -import samba.auth -from samba import param -from samba.samdb import SamDB -import subprocess import tempfile +import unittest -# Other modules import these two classes from here, for convenience: -from testtools.testcase import ( - TestCase as TesttoolsTestCase, - TestSkipped, - ) - - -class TestCase(TesttoolsTestCase): - """A Samba test case.""" - - def setUp(self): - super(TestCase, self).setUp() - test_debug_level = os.getenv("TEST_DEBUG_LEVEL") - if test_debug_level is not None: - test_debug_level = int(test_debug_level) - self._old_debug_level = samba.get_debug_level() - samba.set_debug_level(test_debug_level) - self.addCleanup(samba.set_debug_level, test_debug_level) - - def get_loadparm(self): - return env_loadparm() - - def get_credentials(self): - return cmdline_credentials - +class LdbTestCase(unittest.TestCase): -class LdbTestCase(TesttoolsTestCase): """Trivial test case for running tests against a LDB.""" - def setUp(self): - super(LdbTestCase, self).setUp() self.filename = os.tempnam() self.ldb = samba.Ldb(self.filename) @@ -71,7 +41,7 @@ class LdbTestCase(TesttoolsTestCase): self.ldb = samba.Ldb(self.filename) -class TestCaseInTempDir(TestCase): +class TestCaseInTempDir(unittest.TestCase): def setUp(self): super(TestCaseInTempDir, self).setUp() @@ -83,33 +53,57 @@ class TestCaseInTempDir(TestCase): os.rmdir(self.tempdir) -def env_loadparm(): - lp = param.LoadParm() - try: - lp.load(os.environ["SMB_CONF_PATH"]) - except KeyError: - raise Exception("SMB_CONF_PATH not set") - return lp +class SubstituteVarTestCase(unittest.TestCase): + + def test_empty(self): + self.assertEquals("", samba.substitute_var("", {})) + + def test_nothing(self): + self.assertEquals("foo bar", samba.substitute_var("foo bar", {"bar": "bla"})) + + def test_replace(self): + self.assertEquals("foo bla", samba.substitute_var("foo ${bar}", {"bar": "bla"})) + def test_broken(self): + self.assertEquals("foo ${bdkjfhsdkfh sdkfh ", + samba.substitute_var("foo ${bdkjfhsdkfh sdkfh ", {"bar": "bla"})) -def env_get_var_value(var_name): - """Returns value for variable in os.environ + 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}") - Function throws AssertionError if variable is defined. - Unit-test based python tests require certain input params - to be set in environment, otherwise they can't be run - """ - assert var_name in os.environ.keys(), "Please supply %s in environment" % var_name - return os.environ[var_name] +class LdbExtensionTests(TestCaseInTempDir): + def test_searchone(self): + path = self.tempdir + "/searchone.ldb" + l = samba.Ldb(path) + try: + l.add({"dn": "foo=dc", "bar": "bla"}) + self.assertEquals("bla", l.searchone(basedn=ldb.Dn(l, "foo=dc"), attribute="bar")) + finally: + del l + os.unlink(path) + + +cmdline_loadparm = None cmdline_credentials = None -class RpcInterfaceTestCase(TestCase): - """DCE/RPC Test case.""" +class RpcInterfaceTestCase(unittest.TestCase): + def get_loadparm(self): + assert cmdline_loadparm is not None + return cmdline_loadparm + + def get_credentials(self): + return cmdline_credentials -class ValidNetbiosNameTests(TestCase): + +class ValidNetbiosNameTests(unittest.TestCase): def test_valid(self): self.assertTrue(samba.valid_netbios_name("FOO")) @@ -119,110 +113,3 @@ class ValidNetbiosNameTests(TestCase): def test_invalid_characters(self): self.assertFalse(samba.valid_netbios_name("*BLA")) - - -class BlackboxProcessError(subprocess.CalledProcessError): - """This exception is raised when a process run by check_output() returns - a non-zero exit status. Exception instance should contain - the exact exit code (S.returncode), command line (S.cmd), - process output (S.stdout) and process error stream (S.stderr)""" - def __init__(self, returncode, cmd, stdout, stderr): - super(BlackboxProcessError, self).__init__(returncode, cmd) - self.stdout = stdout - self.stderr = stderr - def __str__(self): - return "Command '%s'; exit status %d; stdout: '%s'; stderr: '%s'" % (self.cmd, self.returncode, - self.stdout, self.stderr) - -class BlackboxTestCase(TestCase): - """Base test case for blackbox tests.""" - - def _make_cmdline(self, line): - bindir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../bin")) - parts = line.split(" ") - if os.path.exists(os.path.join(bindir, parts[0])): - parts[0] = os.path.join(bindir, parts[0]) - line = " ".join(parts) - return line - - def check_run(self, line): - line = self._make_cmdline(line) - subprocess.check_call(line, shell=True) - - def check_output(self, line): - line = self._make_cmdline(line) - p = subprocess.Popen(line, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, close_fds=True) - retcode = p.wait() - if retcode: - raise BlackboxProcessError(retcode, line, p.stdout.read(), p.stderr.read()) - return p.stdout.read() - -def connect_samdb(samdb_url, lp=None, session_info=None, credentials=None, - flags=0, ldb_options=None, ldap_only=False): - """Create SamDB instance and connects to samdb_url database. - - :param samdb_url: Url for database to connect to. - :param lp: Optional loadparm object - :param session_info: Optional session information - :param credentials: Optional credentials, defaults to anonymous. - :param flags: Optional LDB flags - :param ldap_only: If set, only remote LDAP connection will be created. - - Added value for tests is that we have a shorthand function - to make proper URL for ldb.connect() while using default - parameters for connection based on test environment - """ - samdb_url = samdb_url.lower() - if not "://" in samdb_url: - if not ldap_only and os.path.isfile(samdb_url): - samdb_url = "tdb://%s" % samdb_url - else: - samdb_url = "ldap://%s" % samdb_url - # use 'paged_search' module when connecting remotely - if samdb_url.startswith("ldap://"): - ldb_options = ["modules:paged_searches"] - elif ldap_only: - raise AssertionError("Trying to connect to %s while remote " - "connection is required" % samdb_url) - - # set defaults for test environment - if lp is None: - lp = env_loadparm() - if session_info is None: - session_info = samba.auth.system_session(lp) - if credentials is None: - credentials = cmdline_credentials - - return SamDB(url=samdb_url, - lp=lp, - session_info=session_info, - credentials=credentials, - flags=flags, - options=ldb_options) - - -def connect_samdb_ex(samdb_url, lp=None, session_info=None, credentials=None, - flags=0, ldb_options=None, ldap_only=False): - """Connects to samdb_url database - - :param samdb_url: Url for database to connect to. - :param lp: Optional loadparm object - :param session_info: Optional session information - :param credentials: Optional credentials, defaults to anonymous. - :param flags: Optional LDB flags - :param ldap_only: If set, only remote LDAP connection will be created. - :return: (sam_db_connection, rootDse_record) tuple - """ - sam_db = connect_samdb(samdb_url, lp, session_info, credentials, - flags, ldb_options, ldap_only) - # fetch RootDse - res = sam_db.search(base="", expression="", scope=ldb.SCOPE_BASE, - attrs=["*"]) - return (sam_db, res[0]) - - -def delete_force(samdb, dn): - try: - samdb.delete(dn) - except ldb.LdbError, (num, _): - assert(num == ldb.ERR_NO_SUCH_OBJECT) diff --git a/source4/scripting/python/samba/tests/auth.py b/source4/scripting/python/samba/tests/auth.py deleted file mode 100644 index 6ecfc2047f..0000000000 --- a/source4/scripting/python/samba/tests/auth.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# 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 -# 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/>. -# - -"""Tests for the Auth Python bindings. - -Note that this just tests the bindings work. It does not intend to test -the functionality, that's already done in other tests. -""" - -from samba import auth -import samba.tests - -class AuthTests(samba.tests.TestCase): - - def test_system_session(self): - auth.system_session() - diff --git a/source4/scripting/python/samba/tests/blackbox/__init__.py b/source4/scripting/python/samba/tests/blackbox/__init__.py deleted file mode 100644 index 8569cb55bd..0000000000 --- a/source4/scripting/python/samba/tests/blackbox/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Blackbox tests. """ diff --git a/source4/scripting/python/samba/tests/blackbox/ndrdump.py b/source4/scripting/python/samba/tests/blackbox/ndrdump.py deleted file mode 100755 index c07e32a24f..0000000000 --- a/source4/scripting/python/samba/tests/blackbox/ndrdump.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/python -# Blackbox tests for ndrdump -# Copyright (C) 2008 Andrew Tridgell <tridge@samba.org> -# Copyright (C) 2008 Andrew Bartlett <abartlet@samba.org> -# Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org> -# based on test_smbclient.sh - -"""Blackbox tests for ndrdump.""" - -import os -from samba.tests import BlackboxTestCase - -for p in [ "../../../../../source4/librpc/tests", "../../../../../librpc/tests"]: - data_path_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), p)) - print data_path_dir - if os.path.exists(data_path_dir): - break - - -class NdrDumpTests(BlackboxTestCase): - """Blackbox tests for ndrdump.""" - - def data_path(self, name): - return os.path.join(data_path_dir, name) - - def test_ndrdump_with_in(self): - self.check_run("ndrdump samr samr_CreateUser in %s" % (self.data_path("samr-CreateUser-in.dat"))) - - def test_ndrdump_with_out(self): - self.check_run("ndrdump samr samr_CreateUser out %s" % (self.data_path("samr-CreateUser-out.dat"))) - - def test_ndrdump_context_file(self): - self.check_run("ndrdump --context-file %s samr samr_CreateUser out %s" % (self.data_path("samr-CreateUser-in.dat"), self.data_path("samr-CreateUser-out.dat"))) - - def test_ndrdump_with_validate(self): - self.check_run("ndrdump --validate samr samr_CreateUser in %s" % (self.data_path("samr-CreateUser-in.dat"))) diff --git a/source4/scripting/python/samba/tests/blackbox/samba_tool_drs.py b/source4/scripting/python/samba/tests/blackbox/samba_tool_drs.py deleted file mode 100644 index 51274cc9bd..0000000000 --- a/source4/scripting/python/samba/tests/blackbox/samba_tool_drs.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python - -# Blackbox tests for "samba-tool drs" command -# Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2011 -# -# 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/>. -# - -"""Blackbox tests for samba-tool drs.""" - -import os -import samba.tests - - -class SambaToolDrsTests(samba.tests.BlackboxTestCase): - """Blackbox test case for samba-tool drs.""" - - def setUp(self): - super(SambaToolDrsTests, self).setUp() - - self.dc1 = samba.tests.env_get_var_value("DC1") - self.dc2 = samba.tests.env_get_var_value("DC2") - - creds = self.get_credentials() - self.cmdline_creds = "-U%s/%s%%%s" % (creds.get_domain(), - creds.get_username(), creds.get_password()) - - def _get_rootDSE(self, dc): - samdb = samba.tests.connect_samdb(dc, lp=self.get_loadparm(), - credentials=self.get_credentials(), - ldap_only=True) - return samdb.search(base="", scope=samba.tests.ldb.SCOPE_BASE)[0] - - def test_samba_tool_bind(self): - """Tests 'samba-tool drs bind' command - Output should be like: - Extensions supported: - <list-of-supported-extensions> - Site GUID: <GUID> - Repl epoch: 0""" - out = self.check_output("samba-tool drs bind %s %s" % (self.dc1, - self.cmdline_creds)) - self.assertTrue("Site GUID:" in out) - self.assertTrue("Repl epoch:" in out) - - def test_samba_tool_kcc(self): - """Tests 'samba-tool drs kcc' command - Output should be like 'Consistency check on <DC> successful.'""" - out = self.check_output("samba-tool drs kcc %s %s" % (self.dc1, - self.cmdline_creds)) - self.assertTrue("Consistency check on" in out) - self.assertTrue("successful" in out) - - def test_samba_tool_showrepl(self): - """Tests 'samba-tool drs showrepl' command - Output should be like: - <site-name>/<domain-name> - DSA Options: <hex-options> - DSA object GUID: <DSA-object-GUID> - DSA invocationId: <DSA-invocationId> - <Inbound-connections-list> - <Outbound-connections-list> - <KCC-objects> - ... - TODO: Perhaps we should check at least for - DSA's objectGUDI and invocationId""" - out = self.check_output("samba-tool drs showrepl %s %s" % (self.dc1, - self.cmdline_creds)) - self.assertTrue("DSA Options:" in out) - self.assertTrue("DSA object GUID:" in out) - self.assertTrue("DSA invocationId:" in out) - - def test_samba_tool_options(self): - """Tests 'samba-tool drs options' command - Output should be like 'Current DSA options: IS_GC <OTHER_FLAGS>'""" - out = self.check_output("samba-tool drs options %s %s" % (self.dc1, - self.cmdline_creds)) - self.assertTrue("Current DSA options:" in out) - - def test_samba_tool_replicate(self): - """Tests 'samba-tool drs replicate' command - Output should be like 'Replicate from <DC-SRC> to <DC-DEST> was successful.'""" - nc_name = self._get_rootDSE(self.dc1)["defaultNamingContext"] - out = self.check_output("samba-tool drs replicate %s %s %s %s" % (self.dc1, - self.dc2, - nc_name, - self.cmdline_creds)) - self.assertTrue("Replicate from" in out) - self.assertTrue("was successful" in out) diff --git a/source4/scripting/python/samba/tests/core.py b/source4/scripting/python/samba/tests/core.py deleted file mode 100644 index 1c3d7dbe44..0000000000 --- a/source4/scripting/python/samba/tests/core.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# 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 -# 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/>. -# - -"""Samba Python tests.""" - -import ldb -import os -import samba -from samba.tests import TestCase, TestCaseInTempDir - -class SubstituteVarTestCase(TestCase): - - def test_empty(self): - self.assertEquals("", samba.substitute_var("", {})) - - def test_nothing(self): - self.assertEquals("foo bar", - samba.substitute_var("foo bar", {"bar": "bla"})) - - def test_replace(self): - self.assertEquals("foo bla", - samba.substitute_var("foo ${bar}", {"bar": "bla"})) - - def test_broken(self): - self.assertEquals("foo ${bdkjfhsdkfh sdkfh ", - samba.substitute_var("foo ${bdkjfhsdkfh sdkfh ", {"bar": "bla"})) - - 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): - - def test_searchone(self): - path = self.tempdir + "/searchone.ldb" - l = samba.Ldb(path) - try: - l.add({"dn": "foo=dc", "bar": "bla"}) - 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/credentials.py b/source4/scripting/python/samba/tests/credentials.py deleted file mode 100644 index 5ed61c6b21..0000000000 --- a/source4/scripting/python/samba/tests/credentials.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# 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 -# 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/>. -# - -"""Tests for the Credentials Python bindings. - -Note that this just tests the bindings work. It does not intend to test -the functionality, that's already done in other tests. -""" - -from samba import credentials -import samba.tests - -class CredentialsTests(samba.tests.TestCase): - - def setUp(self): - super(CredentialsTests, self).setUp() - self.creds = credentials.Credentials() - - def test_set_username(self): - self.creds.set_username("somebody") - self.assertEquals("somebody", self.creds.get_username()) - - def test_set_password(self): - self.creds.set_password("S3CreT") - self.assertEquals("S3CreT", self.creds.get_password()) - - def test_set_domain(self): - self.creds.set_domain("ABMAS") - self.assertEquals("ABMAS", self.creds.get_domain()) - - def test_set_realm(self): - self.creds.set_realm("myrealm") - self.assertEquals("MYREALM", self.creds.get_realm()) - - def test_parse_string_anon(self): - self.creds.parse_string("%") - self.assertEquals("", self.creds.get_username()) - self.assertEquals(None, self.creds.get_password()) - - def test_parse_string_user_pw_domain(self): - self.creds.parse_string("dom\\someone%secr") - self.assertEquals("someone", self.creds.get_username()) - self.assertEquals("secr", self.creds.get_password()) - self.assertEquals("DOM", self.creds.get_domain()) - - def test_bind_dn(self): - self.assertEquals(None, self.creds.get_bind_dn()) - self.creds.set_bind_dn("dc=foo,cn=bar") - self.assertEquals("dc=foo,cn=bar", self.creds.get_bind_dn()) - - def test_is_anon(self): - self.creds.set_username("") - self.assertTrue(self.creds.is_anonymous()) - self.creds.set_username("somebody") - self.assertFalse(self.creds.is_anonymous()) - self.creds.set_anonymous() - self.assertTrue(self.creds.is_anonymous()) - - def test_workstation(self): - # FIXME: This is uninitialised, it should be None - #self.assertEquals(None, self.creds.get_workstation()) - self.creds.set_workstation("myworksta") - self.assertEquals("myworksta", self.creds.get_workstation()) - - def test_get_nt_hash(self): - self.creds.set_password("geheim") - self.assertEquals('\xc2\xae\x1f\xe6\xe6H\x84cRE>\x81o*\xeb\x93', - self.creds.get_nt_hash()) - - def test_guess(self): - # Just check the method is there and doesn't raise an exception - self.creds.guess() - - def test_set_cmdline_callbacks(self): - self.creds.set_cmdline_callbacks() - - def test_authentication_requested(self): - self.creds.set_username("") - self.assertFalse(self.creds.authentication_requested()) - self.creds.set_username("somebody") - self.assertTrue(self.creds.authentication_requested()) - - def test_wrong_password(self): - self.assertFalse(self.creds.wrong_password()) diff --git a/source4/scripting/python/samba/tests/dcerpc/__init__.py b/source4/scripting/python/samba/tests/dcerpc/__init__.py index 0f9625570a..c64c9dc9f6 100644 --- a/source4/scripting/python/samba/tests/dcerpc/__init__.py +++ b/source4/scripting/python/samba/tests/dcerpc/__init__.py @@ -1,14 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: utf-8 -*- # Unix SMB/CIFS implementation. # Copyright © Jelmer Vernooij <jelmer@samba.org> 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 # 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 @@ -16,6 +16,5 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. - -"""Tests for the DCE/RPC Python bindings.""" +# diff --git a/source4/scripting/python/samba/tests/dcerpc/bare.py b/source4/scripting/python/samba/tests/dcerpc/bare.py index 22163697f8..6cadad89f1 100644 --- a/source4/scripting/python/samba/tests/dcerpc/bare.py +++ b/source4/scripting/python/samba/tests/dcerpc/bare.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: utf-8 -*- # Unix SMB/CIFS implementation. @@ -18,35 +18,32 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.dcerpc.bare.""" - from samba.dcerpc import ClientConnection -import samba.tests +from unittest import TestCase +from samba.tests import cmdline_loadparm + -class BareTestCase(samba.tests.TestCase): +class BareTestCase(TestCase): def test_bare(self): # Connect to the echo pipe x = ClientConnection("ncalrpc:localhost[DEFAULT]", - ("60a15ec5-4de8-11d7-a637-005056a20182", 1), - lp_ctx=samba.tests.env_loadparm()) + ("60a15ec5-4de8-11d7-a637-005056a20182", 1), lp_ctx=cmdline_loadparm) self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4)) def test_alter_context(self): x = ClientConnection("ncalrpc:localhost[DEFAULT]", - ("12345778-1234-abcd-ef00-0123456789ac", 1), - lp_ctx=samba.tests.env_loadparm()) + ("12345778-1234-abcd-ef00-0123456789ac", 1), lp_ctx=cmdline_loadparm) y = ClientConnection("ncalrpc:localhost", ("60a15ec5-4de8-11d7-a637-005056a20182", 1), - basis_connection=x, lp_ctx=samba.tests.env_loadparm()) + basis_connection=x, lp_ctx=cmdline_loadparm) x.alter_context(("60a15ec5-4de8-11d7-a637-005056a20182", 1)) # FIXME: self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4)) def test_two_connections(self): x = ClientConnection("ncalrpc:localhost[DEFAULT]", - ("60a15ec5-4de8-11d7-a637-005056a20182", 1), - lp_ctx=samba.tests.env_loadparm()) + ("60a15ec5-4de8-11d7-a637-005056a20182", 1), lp_ctx=cmdline_loadparm) y = ClientConnection("ncalrpc:localhost", ("60a15ec5-4de8-11d7-a637-005056a20182", 1), - basis_connection=x, lp_ctx=samba.tests.env_loadparm()) + basis_connection=x, lp_ctx=cmdline_loadparm) self.assertEquals("\x01\x00\x00\x00", y.request(0, chr(0) * 4)) diff --git a/source4/scripting/python/samba/tests/dcerpc/misc.py b/source4/scripting/python/samba/tests/dcerpc/misc.py index 37a647a842..ab76efc536 100644 --- a/source4/scripting/python/samba/tests/dcerpc/misc.py +++ b/source4/scripting/python/samba/tests/dcerpc/misc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 @@ -17,15 +17,13 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.dcerpc.misc.""" - +import unittest from samba.dcerpc import misc -import samba.tests text1 = "76f53846-a7c2-476a-ae2c-20e2b80d7b34" text2 = "344edffa-330a-4b39-b96e-2c34da52e8b1" -class GUIDTests(samba.tests.TestCase): +class GUIDTests(unittest.TestCase): def test_str(self): guid = misc.GUID(text1) @@ -45,9 +43,9 @@ class GUIDTests(samba.tests.TestCase): guid2 = misc.GUID(text1) self.assertEquals(0, cmp(guid1, guid2)) self.assertEquals(guid1, guid2) - - -class PolicyHandleTests(samba.tests.TestCase): + + +class PolicyHandleTests(unittest.TestCase): def test_init(self): x = misc.policy_handle(text1, 1) diff --git a/source4/scripting/python/samba/tests/dcerpc/registry.py b/source4/scripting/python/samba/tests/dcerpc/registry.py index f0716d5ffb..f1cd83790d 100644 --- a/source4/scripting/python/samba/tests/dcerpc/registry.py +++ b/source4/scripting/python/samba/tests/dcerpc/registry.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 @@ -17,8 +17,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.dcerpc.registry.""" - from samba.dcerpc import winreg from samba.tests import RpcInterfaceTestCase @@ -26,7 +24,6 @@ from samba.tests import RpcInterfaceTestCase class WinregTests(RpcInterfaceTestCase): def setUp(self): - super(WinregTests, self).setUp() self.conn = winreg.winreg("ncalrpc:", self.get_loadparm(), self.get_credentials()) diff --git a/source4/scripting/python/samba/tests/dcerpc/rpc_talloc.py b/source4/scripting/python/samba/tests/dcerpc/rpc_talloc.py deleted file mode 100755 index fe0f93ef9b..0000000000 --- a/source4/scripting/python/samba/tests/dcerpc/rpc_talloc.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python -# test generated python code from pidl -# Andrew Tridgell August 2010 -# -# to run this test, use one of these: -# -# python -m testtools.run samba.tests.dcerpc.rpc_talloc -# -# or if you have trial installed (from twisted), use -# -# trial samba.tests.dcerpc.rpc_talloc - -"""Tests for the talloc handling in the generated Python DCE/RPC bindings.""" - -import sys - -sys.path.insert(0, "bin/python") - -import samba -import samba.tests -from samba.dcerpc import drsuapi -import talloc - -talloc.enable_null_tracking() - -class TallocTests(samba.tests.TestCase): - '''test talloc behaviour of pidl generated python code''' - - def check_blocks(self, object, num_expected): - '''check that the number of allocated blocks is correct''' - nblocks = talloc.total_blocks(object) - if object is None: - nblocks -= self.initial_blocks - self.assertEquals(nblocks, num_expected) - - def get_rodc_partial_attribute_set(self): - '''get a list of attributes for RODC replication''' - partial_attribute_set = drsuapi.DsPartialAttributeSet() - - # we expect one block for the object, and one for the structure - self.check_blocks(partial_attribute_set, 2) - - attids = [ 1, 2, 3] - partial_attribute_set.version = 1 - partial_attribute_set.attids = attids - partial_attribute_set.num_attids = len(attids) - - # we expect one block object, a structure, an ARRAY, and a - # reference to the array - self.check_blocks(partial_attribute_set, 3) - - return partial_attribute_set - - def pas_test(self): - pas = self.get_rodc_partial_attribute_set() - self.check_blocks(pas, 3) - req8 = drsuapi.DsGetNCChangesRequest8() - self.check_blocks(req8, 2) - self.check_blocks(None, 5) - req8.partial_attribute_set = pas - if req8.partial_attribute_set.attids[1] != 2: - raise Exception("Wrong value in attids[2]") - # we now get an additional reference - self.check_blocks(None, 6) - - def test_run(self): - self.initial_blocks = talloc.total_blocks(None) - self.check_blocks(None, 0) - self.pas_test() - self.check_blocks(None, 0) diff --git a/source4/scripting/python/samba/tests/dcerpc/rpcecho.py b/source4/scripting/python/samba/tests/dcerpc/rpcecho.py index ba7ad9688b..72eb87d750 100644 --- a/source4/scripting/python/samba/tests/dcerpc/rpcecho.py +++ b/source4/scripting/python/samba/tests/dcerpc/rpcecho.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 @@ -17,17 +17,15 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.dceprc.rpcecho.""" - from samba.dcerpc import echo from samba.ndr import ndr_pack, ndr_unpack -from samba.tests import RpcInterfaceTestCase, TestCase +import unittest +from samba.tests import RpcInterfaceTestCase class RpcEchoTests(RpcInterfaceTestCase): def setUp(self): - super(RpcEchoTests, self).setUp() self.conn = echo.rpcecho("ncalrpc:", self.get_loadparm()) def test_two_contexts(self): @@ -35,7 +33,7 @@ class RpcEchoTests(RpcInterfaceTestCase): self.assertEquals(3, self.conn2.AddOne(2)) def test_abstract_syntax(self): - self.assertEquals(("60a15ec5-4de8-11d7-a637-005056a20182", 1), + self.assertEquals(("60a15ec5-4de8-11d7-a637-005056a20182", 1), self.conn.abstract_syntax) def test_addone(self): @@ -61,7 +59,7 @@ class RpcEchoTests(RpcInterfaceTestCase): self.assertEquals(None, self.conn.server_name) -class NdrEchoTests(TestCase): +class NdrEchoTests(unittest.TestCase): def test_info1_push(self): x = echo.info1() diff --git a/source4/scripting/python/samba/tests/dcerpc/sam.py b/source4/scripting/python/samba/tests/dcerpc/sam.py index 38b381d3aa..9532333a00 100644 --- a/source4/scripting/python/samba/tests/dcerpc/sam.py +++ b/source4/scripting/python/samba/tests/dcerpc/sam.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # -*- coding: utf-8 -*- # Unix SMB/CIFS implementation. @@ -18,8 +18,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.dcerpc.sam.""" - from samba.dcerpc import samr, security from samba.tests import RpcInterfaceTestCase @@ -34,7 +32,6 @@ def toArray((handle, array, num_entries)): class SamrTests(RpcInterfaceTestCase): def setUp(self): - super(SamrTests, self).setUp() self.conn = samr.samr("ncalrpc:", self.get_loadparm()) def test_connect5(self): @@ -42,7 +39,6 @@ class SamrTests(RpcInterfaceTestCase): def test_connect2(self): handle = self.conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED) - self.assertTrue(handle is not None) def test_EnumDomains(self): handle = self.conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED) diff --git a/source4/scripting/python/samba/tests/dcerpc/testrpc.py b/source4/scripting/python/samba/tests/dcerpc/testrpc.py deleted file mode 100644 index 2f3a6a0aef..0000000000 --- a/source4/scripting/python/samba/tests/dcerpc/testrpc.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python -# -# test generated python code from pidl -# Andrew Tridgell August 2010 -# -import sys - -sys.path.insert(0, "bin/python") - -import samba -import samba.tests -from samba.dcerpc import drsuapi -import talloc - -talloc.enable_null_tracking() - -class RpcTests(object): - '''test type behaviour of pidl generated python RPC code''' - - def check_blocks(self, object, num_expected): - '''check that the number of allocated blocks is correct''' - nblocks = talloc.total_blocks(object) - if object is None: - nblocks -= self.initial_blocks - leaked_blocks = (nblocks - num_expected) - if leaked_blocks != 0: - print "Leaked %d blocks" % leaked_blocks - - def check_type(self, interface, typename, type): - print "Checking type %s" % typename - v = type() - for n in dir(v): - if n[0] == '_': - continue - try: - value = getattr(v, n) - except TypeError, errstr: - if str(errstr) == "unknown union level": - print "ERROR: Unknown union level in %s.%s" % (typename, n) - self.errcount += 1 - continue - print str(errstr)[1:21] - if str(errstr)[0:21] == "Can not convert C Type": - print "ERROR: Unknown C type for %s.%s" % (typename, n) - self.errcount += 1 - continue - else: - print "ERROR: Failed to instantiate %s.%s" % (typename, n) - self.errcount += 1 - continue - except: - print "ERROR: Failed to instantiate %s.%s" % (typename, n) - self.errcount += 1 - continue - - # now try setting the value back - try: - print "Setting %s.%s" % (typename, n) - setattr(v, n, value) - except Exception, e: - if isinstance(e, AttributeError) and str(e).endswith("is read-only"): - # readonly, ignore - continue - else: - print "ERROR: Failed to set %s.%s: %r: %s" % (typename, n, e.__class__, e) - self.errcount += 1 - continue - - # and try a comparison - try: - if value != getattr(v, n): - print "ERROR: Comparison failed for %s.%s: %r != %r" % (typename, n, value, getattr(v, n)) - continue - except Exception, e: - print "ERROR: compare exception for %s.%s: %r: %s" % (typename, n, e.__class__, e) - continue - - def check_interface(self, interface, iname): - errcount = self.errcount - for n in dir(interface): - if n[0] == '_' or n == iname: - # skip the special ones - continue - value = getattr(interface, n) - if isinstance(value, str): - #print "%s=\"%s\"" % (n, value) - pass - elif isinstance(value, int) or isinstance(value, long): - #print "%s=%d" % (n, value) - pass - elif isinstance(value, type): - try: - initial_blocks = talloc.total_blocks(None) - self.check_type(interface, n, value) - self.check_blocks(None, initial_blocks) - except Exception, e: - print "ERROR: Failed to check_type %s.%s: %r: %s" % (iname, n, e.__class__, e) - self.errcount += 1 - elif callable(value): - pass # Method - else: - print "UNKNOWN: %s=%s" % (n, value) - if self.errcount - errcount != 0: - print "Found %d errors in %s" % (self.errcount - errcount, iname) - - def check_all_interfaces(self): - for iname in dir(samba.dcerpc): - if iname[0] == '_': - continue - if iname == 'ClientConnection' or iname == 'base': - continue - print "Checking interface %s" % iname - iface = getattr(samba.dcerpc, iname) - initial_blocks = talloc.total_blocks(None) - self.check_interface(iface, iname) - self.check_blocks(None, initial_blocks) - - def run(self): - self.initial_blocks = talloc.total_blocks(None) - self.errcount = 0 - self.check_all_interfaces() - return self.errcount - -tests = RpcTests() -errcount = tests.run() -if errcount == 0: - sys.exit(0) -else: - print "%d failures" % errcount - sys.exit(1) diff --git a/source4/scripting/python/samba/tests/dcerpc/unix.py b/source4/scripting/python/samba/tests/dcerpc/unix.py index 16bf37e749..62169ad12c 100644 --- a/source4/scripting/python/samba/tests/dcerpc/unix.py +++ b/source4/scripting/python/samba/tests/dcerpc/unix.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 @@ -17,35 +17,22 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.dcerpc.unixinfo.""" - - from samba.dcerpc import unixinfo from samba.tests import RpcInterfaceTestCase class UnixinfoTests(RpcInterfaceTestCase): def setUp(self): - super(UnixinfoTests, self).setUp() self.conn = unixinfo.unixinfo("ncalrpc:", self.get_loadparm()) - def test_getpwuid_int(self): - infos = self.conn.GetPWUid(range(512)) - self.assertEquals(512, len(infos)) - self.assertEquals("/bin/false", infos[0].shell) - self.assertTrue(isinstance(infos[0].homedir, unicode)) - def test_getpwuid(self): - infos = self.conn.GetPWUid(map(long, range(512))) + infos = self.conn.GetPWUid(range(512)) self.assertEquals(512, len(infos)) self.assertEquals("/bin/false", infos[0].shell) self.assertTrue(isinstance(infos[0].homedir, unicode)) def test_gidtosid(self): - self.conn.GidToSid(1000L) + self.conn.GidToSid(1000) def test_uidtosid(self): self.conn.UidToSid(1000) - - def test_uidtosid_fail(self): - self.assertRaises(TypeError, self.conn.UidToSid, "100") diff --git a/source4/scripting/python/samba/tests/dsdb.py b/source4/scripting/python/samba/tests/dsdb.py deleted file mode 100644 index d4331f3ef5..0000000000 --- a/source4/scripting/python/samba/tests/dsdb.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. Tests for dsdb -# Copyright (C) Matthieu Patou <mat@matws.net> 2010 -# -# 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/>. -# - -"""Tests for samba.dsdb.""" - -from samba.credentials import Credentials -from samba.samdb import SamDB -from samba.auth import system_session -from testtools.testcase import TestCase -from samba.ndr import ndr_unpack, ndr_pack -from samba.dcerpc import drsblobs -import ldb -import os -import samba - - -class DsdbTests(TestCase): - - - def setUp(self): - super(DsdbTests, self).setUp() - self.lp = samba.param.LoadParm() - self.lp.load(os.path.join(os.path.join(self.baseprovpath(), "etc"), "smb.conf")) - self.creds = Credentials() - self.creds.guess(self.lp) - self.session = system_session() - self.samdb = SamDB(os.path.join(self.baseprovpath(), "private", "sam.ldb"), - session_info=self.session, credentials=self.creds,lp=self.lp) - - - def baseprovpath(self): - return os.path.join(os.environ['SELFTEST_PREFIX'], "dc") - - - def test_get_oid_from_attrid(self): - oid = self.samdb.get_oid_from_attid(591614) - self.assertEquals(oid, "1.2.840.113556.1.4.1790") - - def test_error_replpropertymetadata(self): - res = self.samdb.search(expression="cn=Administrator", - scope=ldb.SCOPE_SUBTREE, - attrs=["replPropertyMetaData"]) - repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, - str(res[0]["replPropertyMetaData"])) - ctr = repl.ctr - for o in ctr.array: - # Search for Description - if o.attid == 13: - old_version = o.version - o.version = o.version + 1 - replBlob = ndr_pack(repl) - msg = ldb.Message() - msg.dn = res[0].dn - msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData") - self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) - - def test_twoatt_replpropertymetadata(self): - res = self.samdb.search(expression="cn=Administrator", - scope=ldb.SCOPE_SUBTREE, - attrs=["replPropertyMetaData", "uSNChanged"]) - repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, - str(res[0]["replPropertyMetaData"])) - ctr = repl.ctr - for o in ctr.array: - # Search for Description - if o.attid == 13: - old_version = o.version - o.version = o.version + 1 - o.local_usn = long(str(res[0]["uSNChanged"])) + 1 - replBlob = ndr_pack(repl) - msg = ldb.Message() - msg.dn = res[0].dn - msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData") - msg["description"] = ldb.MessageElement("new val", ldb.FLAG_MOD_REPLACE, "description") - self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) - - def test_set_replpropertymetadata(self): - res = self.samdb.search(expression="cn=Administrator", - scope=ldb.SCOPE_SUBTREE, - attrs=["replPropertyMetaData", "uSNChanged"]) - repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, - str(res[0]["replPropertyMetaData"])) - ctr = repl.ctr - for o in ctr.array: - # Search for Description - if o.attid == 13: - old_version = o.version - o.version = o.version + 1 - o.local_usn = long(str(res[0]["uSNChanged"])) + 1 - o.originating_usn = long(str(res[0]["uSNChanged"])) + 1 - replBlob = ndr_pack(repl) - msg = ldb.Message() - msg.dn = res[0].dn - msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData") - self.samdb.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"]) - - def test_ok_get_attribute_from_attid(self): - self.assertEquals(self.samdb.get_attribute_from_attid(13), "description") - - def test_ko_get_attribute_from_attid(self): - self.assertEquals(self.samdb.get_attribute_from_attid(11979), None) - - def test_get_attribute_replmetadata_version(self): - res = self.samdb.search(expression="cn=Administrator", - scope=ldb.SCOPE_SUBTREE, - attrs=["dn"]) - self.assertEquals(len(res), 1) - dn = str(res[0].dn) - self.assertEqual(self.samdb.get_attribute_replmetadata_version(dn, "unicodePwd"), 1) - - def test_set_attribute_replmetadata_version(self): - res = self.samdb.search(expression="cn=Administrator", - scope=ldb.SCOPE_SUBTREE, - attrs=["dn"]) - self.assertEquals(len(res), 1) - dn = str(res[0].dn) - version = self.samdb.get_attribute_replmetadata_version(dn, "description") - self.samdb.set_attribute_replmetadata_version(dn, "description", version + 2) - self.assertEqual(self.samdb.get_attribute_replmetadata_version(dn, "description"), version + 2) diff --git a/source4/scripting/python/samba/tests/gensec.py b/source4/scripting/python/samba/tests/gensec.py deleted file mode 100644 index ddca0df980..0000000000 --- a/source4/scripting/python/samba/tests/gensec.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009 -# -# 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/>. -# - -"""Tests for GENSEC. - -Note that this just tests the bindings work. It does not intend to test -the functionality, that's already done in other tests. -""" - -from samba.credentials import Credentials -from samba import gensec -import samba.tests - -class GensecTests(samba.tests.TestCase): - - def setUp(self): - super(GensecTests, self).setUp() - self.settings = {} - self.settings["lp_ctx"] = self.lp_ctx = samba.tests.env_loadparm() - self.settings["target_hostname"] = self.lp_ctx.get("netbios name") - """This is just for the API tests""" - self.gensec = gensec.Security.start_client(self.settings) - - def test_start_mech_by_unknown_name(self): - self.assertRaises(RuntimeError, self.gensec.start_mech_by_name, "foo") - - def test_start_mech_by_name(self): - self.gensec.start_mech_by_name("spnego") - - def test_info_uninitialized(self): - self.assertRaises(RuntimeError, self.gensec.session_info) - - def test_update(self): - """Test GENSEC by doing an exchange with ourselves using GSSAPI against a KDC""" - - """Start up a client and server GENSEC instance to test things with""" - - self.gensec_client = gensec.Security.start_client(self.settings) - self.gensec_client.set_credentials(self.get_credentials()) - self.gensec_client.want_feature(gensec.FEATURE_SEAL) - self.gensec_client.start_mech_by_sasl_name("GSSAPI") - - self.gensec_server = gensec.Security.start_server(self.settings) - creds = Credentials() - creds.guess(self.lp_ctx) - creds.set_machine_account(self.lp_ctx) - self.gensec_server.set_credentials(creds) - - self.gensec_server.want_feature(gensec.FEATURE_SEAL) - self.gensec_server.start_mech_by_sasl_name("GSSAPI") - - client_finished = False - server_finished = False - server_to_client = "" - - """Run the actual call loop""" - while client_finished == False and server_finished == False: - if not client_finished: - print "running client gensec_update" - (client_finished, client_to_server) = self.gensec_client.update(server_to_client) - if not server_finished: - print "running server gensec_update" - (server_finished, server_to_client) = self.gensec_server.update(client_to_server) - session_info = self.gensec_server.session_info() - - test_string = "Hello Server" - test_wrapped = self.gensec_client.wrap(test_string) - test_unwrapped = self.gensec_server.unwrap(test_wrapped) - self.assertEqual(test_string, test_unwrapped) - test_string = "Hello Client" - test_wrapped = self.gensec_server.wrap(test_string) - test_unwrapped = self.gensec_client.unwrap(test_wrapped) - self.assertEqual(test_string, test_unwrapped) - diff --git a/source4/scripting/python/samba/tests/messaging.py b/source4/scripting/python/samba/tests/messaging.py deleted file mode 100644 index d2a0b73775..0000000000 --- a/source4/scripting/python/samba/tests/messaging.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Unix SMB/CIFS implementation. -# Copyright © Jelmer Vernooij <jelmer@samba.org> 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 -# 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/>. -# - -"""Tests for samba.messaging.""" - -from samba.messaging import Messaging -from samba.tests import TestCase - -class MessagingTests(TestCase): - - def get_context(self, *args, **kwargs): - kwargs["messaging_path"] = "." - return Messaging(*args, **kwargs) - - def test_register(self): - x = self.get_context() - def callback(): - pass - msg_type = x.register(callback) - x.deregister(callback, msg_type) - - def test_assign_server_id(self): - x = self.get_context() - self.assertTrue(isinstance(x.server_id, tuple)) - self.assertEquals(3, len(x.server_id)) - - def test_ping_speed(self): - server_ctx = self.get_context((0, 1)) - def ping_callback(src, data): - server_ctx.send(src, data) - def exit_callback(): - print "received exit" - msg_ping = server_ctx.register(ping_callback) - msg_exit = server_ctx.register(exit_callback) - - def pong_callback(): - print "received pong" - client_ctx = self.get_context((0, 2)) - msg_pong = client_ctx.register(pong_callback) - - client_ctx.send((0, 1), msg_ping, "testing") - client_ctx.send((0, 1), msg_ping, "") - diff --git a/source4/scripting/python/samba/tests/netcmd.py b/source4/scripting/python/samba/tests/netcmd.py deleted file mode 100644 index 787bcd5a72..0000000000 --- a/source4/scripting/python/samba/tests/netcmd.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009 -# -# 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/>. -# - -"""Tests for samba.netcmd.""" - -from samba.netcmd import Command -import samba.tests - -class CommandTests(samba.tests.TestCase): - - def test_name(self): - class cmd_foo(Command): - pass - self.assertEquals("foo", cmd_foo().name) - - def test_description(self): - class cmd_foo(Command): - """Mydescription""" - self.assertEquals("Mydescription", cmd_foo().description) diff --git a/source4/scripting/python/samba/tests/ntacls.py b/source4/scripting/python/samba/tests/ntacls.py deleted file mode 100644 index 2d8d6b9d67..0000000000 --- a/source4/scripting/python/samba/tests/ntacls.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. Tests for ntacls manipulation -# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010 -# -# 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/>. -# - -"""Tests for samba.ntacls.""" - -from samba.ntacls import setntacl, getntacl, XattrBackendError -from samba.dcerpc import xattr, security -from samba.param import LoadParm -from samba.tests import TestCase, TestSkipped -import random -import os - -class NtaclsTests(TestCase): - - def test_setntacl(self): - random.seed() - lp = LoadParm() - path = os.environ['SELFTEST_PREFIX'] - acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)" - tempf = os.path.join(path,"pytests"+str(int(100000*random.random()))) - ntacl = xattr.NTACL() - ntacl.version = 1 - open(tempf, 'w').write("empty") - lp.set("posix:eadb",os.path.join(path,"eadbtest.tdb")) - setntacl(lp, tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467") - os.unlink(tempf) - - def test_setntacl_getntacl(self): - random.seed() - lp = LoadParm() - path = None - path = os.environ['SELFTEST_PREFIX'] - acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)" - tempf = os.path.join(path,"pytests"+str(int(100000*random.random()))) - ntacl = xattr.NTACL() - ntacl.version = 1 - open(tempf, 'w').write("empty") - lp.set("posix:eadb",os.path.join(path,"eadbtest.tdb")) - setntacl(lp,tempf,acl,"S-1-5-21-2212615479-2695158682-2101375467") - facl = getntacl(lp,tempf) - anysid = security.dom_sid(security.SID_NT_SELF) - self.assertEquals(facl.info.as_sddl(anysid),acl) - os.unlink(tempf) - - def test_setntacl_getntacl_param(self): - random.seed() - lp = LoadParm() - acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)" - path = os.environ['SELFTEST_PREFIX'] - tempf = os.path.join(path,"pytests"+str(int(100000*random.random()))) - ntacl = xattr.NTACL() - ntacl.version = 1 - open(tempf, 'w').write("empty") - setntacl(lp,tempf,acl,"S-1-5-21-2212615479-2695158682-2101375467","tdb",os.path.join(path,"eadbtest.tdb")) - facl=getntacl(lp,tempf,"tdb",os.path.join(path,"eadbtest.tdb")) - domsid=security.dom_sid(security.SID_NT_SELF) - self.assertEquals(facl.info.as_sddl(domsid),acl) - os.unlink(tempf) - - def test_setntacl_invalidbackend(self): - random.seed() - lp = LoadParm() - acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)" - path = os.environ['SELFTEST_PREFIX'] - tempf = os.path.join(path,"pytests"+str(int(100000*random.random()))) - ntacl = xattr.NTACL() - ntacl.version = 1 - open(tempf, 'w').write("empty") - self.assertRaises(XattrBackendError, setntacl, lp, tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467","ttdb", os.path.join(path,"eadbtest.tdb")) - - def test_setntacl_forcenative(self): - if os.getuid() == 0: - raise TestSkipped("Running test as root, test skipped") - random.seed() - lp = LoadParm() - acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)" - path = os.environ['SELFTEST_PREFIX'] - tempf = os.path.join(path,"pytests"+str(int(100000*random.random()))) - ntacl = xattr.NTACL() - ntacl.version = 1 - open(tempf, 'w').write("empty") - lp.set("posix:eadb", os.path.join(path,"eadbtest.tdb")) - self.assertRaises(Exception, setntacl, lp, tempf ,acl, - "S-1-5-21-2212615479-2695158682-2101375467","native") - os.unlink(tempf) diff --git a/source4/scripting/python/samba/tests/param.py b/source4/scripting/python/samba/tests/param.py deleted file mode 100644 index 7848e1c23b..0000000000 --- a/source4/scripting/python/samba/tests/param.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# 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 -# 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/>. -# - -"""Tests for samba.param.""" - -from samba import param -import samba.tests - -class LoadParmTestCase(samba.tests.TestCase): - - def test_init(self): - file = param.LoadParm() - self.assertTrue(file is not None) - - def test_length(self): - file = param.LoadParm() - self.assertEquals(0, len(file)) - - def test_set_workgroup(self): - file = param.LoadParm() - file.set("workgroup", "bla") - self.assertEquals("BLA", file.get("workgroup")) - - def test_is_mydomain(self): - file = param.LoadParm() - file.set("workgroup", "bla") - self.assertTrue(file.is_mydomain("BLA")) - self.assertFalse(file.is_mydomain("FOOBAR")) - - def test_is_myname(self): - file = param.LoadParm() - file.set("netbios name", "bla") - self.assertTrue(file.is_myname("BLA")) - self.assertFalse(file.is_myname("FOOBAR")) - - def test_load_default(self): - file = param.LoadParm() - file.load_default() - - def test_section_nonexistant(self): - samba_lp = param.LoadParm() - samba_lp.load_default() - self.assertRaises(KeyError, samba_lp.__getitem__, "nonexistant") diff --git a/source4/scripting/python/samba/tests/provision.py b/source4/scripting/python/samba/tests/provision.py index 39a01606bb..f34073504c 100644 --- a/source4/scripting/python/samba/tests/provision.py +++ b/source4/scripting/python/samba/tests/provision.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 @@ -17,44 +17,26 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.provision.""" - import os -from samba.provision import setup_secretsdb, findnss, ProvisionPaths +from samba.provision import setup_secretsdb, findnss import samba.tests -from samba.tests import env_loadparm, TestCase +from ldb import Dn +from samba import param +import unittest -def create_dummy_secretsdb(path, lp=None): - """Create a dummy secrets database for use in tests. +lp = samba.tests.cmdline_loadparm - :param path: Path to store the secrets db - :param lp: Optional loadparm context. A simple one will - be generated if not specified. - """ - if lp is None: - lp = env_loadparm() - paths = ProvisionPaths() - paths.secrets = path - paths.private_dir = os.path.dirname(path) - paths.keytab = "no.keytab" - paths.dns_keytab = "no.dns.keytab" - secrets_ldb = setup_secretsdb(paths, None, None, lp=lp) - secrets_ldb.transaction_commit() - return secrets_ldb +setup_dir = "setup" +def setup_path(file): + return os.path.join(setup_dir, 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") - paths = ProvisionPaths() - paths.secrets = path - paths.private_dir = os.path.dirname(path) - paths.keytab = "no.keytab" - paths.dns_keytab = "no.dns.keytab" - ldb = setup_secretsdb(paths, None, None, lp=env_loadparm()) + ldb = setup_secretsdb(path, setup_path, None, None, lp=lp) try: self.assertEquals("LSA Secrets", ldb.searchone(basedn="CN=LSA Secrets", attribute="CN")) @@ -63,9 +45,8 @@ class ProvisionTestCase(samba.tests.TestCaseInTempDir): os.unlink(path) -class FindNssTests(TestCase): +class FindNssTests(unittest.TestCase): """Test findnss() function.""" - def test_nothing(self): def x(y): raise KeyError @@ -83,7 +64,6 @@ class FindNssTests(TestCase): class Disabled(object): - def test_setup_templatesdb(self): raise NotImplementedError(self.test_setup_templatesdb) @@ -114,4 +94,7 @@ class Disabled(object): def test_vampire(self): raise NotImplementedError(self.test_vampire) + def test_erase_partitions(self): + raise NotImplementedError(self.test_erase_partitions) + diff --git a/source4/scripting/python/samba/tests/registry.py b/source4/scripting/python/samba/tests/registry.py deleted file mode 100644 index 97926850d2..0000000000 --- a/source4/scripting/python/samba/tests/registry.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# 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 -# 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/>. -# - -"""Tests for samba.registry.""" - -import os -from samba import registry -import samba.tests - -class HelperTests(samba.tests.TestCase): - - def test_predef_to_name(self): - self.assertEquals("HKEY_LOCAL_MACHINE", - registry.get_predef_name(0x80000002)) - - def test_str_regtype(self): - self.assertEquals("REG_DWORD", registry.str_regtype(4)) - - - -class HiveTests(samba.tests.TestCaseInTempDir): - - def setUp(self): - super(HiveTests, self).setUp() - self.hive_path = os.path.join(self.tempdir, "ldb_new.ldb") - self.hive = registry.open_ldb(self.hive_path) - - def tearDown(self): - del self.hive - os.unlink(self.hive_path) - super(HiveTests, self).tearDown() - - def test_ldb_new(self): - self.assertTrue(self.hive is not None) - - #def test_flush(self): - # self.hive.flush() - - #def test_del_value(self): - # self.hive.del_value("FOO") - - -class RegistryTests(samba.tests.TestCase): - - def test_new(self): - self.registry = registry.Registry() diff --git a/source4/scripting/python/samba/tests/samba3.py b/source4/scripting/python/samba/tests/samba3.py index 3a4b851c75..71e08bdd7f 100644 --- a/source4/scripting/python/samba/tests/samba3.py +++ b/source4/scripting/python/samba/tests/samba3.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 @@ -17,30 +17,20 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.samba3.""" - -from samba.samba3 import (GroupMappingDatabase, Registry, PolicyDatabase, - SecretsDatabase, TdbSam) -from samba.samba3 import (WinsDatabase, SmbpasswdFile, ACB_NORMAL, - IdmapDatabase, SAMUser, ParamFile) -from samba.tests import TestCase +import unittest +from samba.samba3 import GroupMappingDatabase, Registry, PolicyDatabase, SecretsDatabase, TdbSam +from samba.samba3 import WinsDatabase, SmbpasswdFile, ACB_NORMAL, IdmapDatabase, SAMUser, ParamFile import os -for p in [ "../../../../../testdata/samba3", "../../../../testdata/samba3" ]: - DATADIR = os.path.join(os.path.dirname(__file__), p) - if os.path.exists(DATADIR): - break - - -class RegistryTestCase(TestCase): +DATADIR=os.path.join(os.path.dirname(__file__), "../../../../../testdata/samba3") +print "Samba 3 data dir: %s" % DATADIR +class RegistryTestCase(unittest.TestCase): def setUp(self): - super(RegistryTestCase, self).setUp() self.registry = Registry(os.path.join(DATADIR, "registry.tdb")) def tearDown(self): self.registry.close() - super(RegistryTestCase, self).tearDown() def test_length(self): self.assertEquals(28, len(self.registry)) @@ -57,10 +47,8 @@ class RegistryTestCase(TestCase): self.registry.values("HKLM/SYSTEM/CURRENTCONTROLSET/SERVICES/EVENTLOG")) -class PolicyTestCase(TestCase): - +class PolicyTestCase(unittest.TestCase): def setUp(self): - super(PolicyTestCase, self).setUp() self.policy = PolicyDatabase(os.path.join(DATADIR, "account_policy.tdb")) def test_policy(self): @@ -76,15 +64,12 @@ class PolicyTestCase(TestCase): self.assertEquals(self.policy.bad_lockout_minutes, None) -class GroupsTestCase(TestCase): - +class GroupsTestCase(unittest.TestCase): def setUp(self): - super(GroupsTestCase, self).setUp() self.groupdb = GroupMappingDatabase(os.path.join(DATADIR, "group_mapping.tdb")) def tearDown(self): self.groupdb.close() - super(GroupsTestCase, self).tearDown() def test_group_length(self): self.assertEquals(13, len(list(self.groupdb.groupsids()))) @@ -100,29 +85,23 @@ class GroupsTestCase(TestCase): self.assertEquals(0, len(list(self.groupdb.aliases()))) -class SecretsDbTestCase(TestCase): - +class SecretsDbTestCase(unittest.TestCase): def setUp(self): - super(SecretsDbTestCase, self).setUp() self.secretsdb = SecretsDatabase(os.path.join(DATADIR, "secrets.tdb")) def tearDown(self): self.secretsdb.close() - super(SecretsDbTestCase, self).tearDown() def test_get_sid(self): self.assertTrue(self.secretsdb.get_sid("BEDWYR") is not None) -class TdbSamTestCase(TestCase): - +class TdbSamTestCase(unittest.TestCase): def setUp(self): - super(TdbSamTestCase, self).setUp() self.samdb = TdbSam(os.path.join(DATADIR, "passdb.tdb")) def tearDown(self): self.samdb.close() - super(TdbSamTestCase, self).tearDown() def test_usernames(self): self.assertEquals(3, len(list(self.samdb.usernames()))) @@ -161,10 +140,8 @@ class TdbSamTestCase(TestCase): self.assertEquals(user, other) -class WinsDatabaseTestCase(TestCase): - +class WinsDatabaseTestCase(unittest.TestCase): def setUp(self): - super(WinsDatabaseTestCase, self).setUp() self.winsdb = WinsDatabase(os.path.join(DATADIR, "wins.dat")) def test_length(self): @@ -175,13 +152,10 @@ class WinsDatabaseTestCase(TestCase): def tearDown(self): self.winsdb.close() - super(WinsDatabaseTestCase, self).tearDown() -class SmbpasswdTestCase(TestCase): - +class SmbpasswdTestCase(unittest.TestCase): def setUp(self): - super(SmbpasswdTestCase, self).setUp() self.samdb = SmbpasswdFile(os.path.join(DATADIR, "smbpasswd")) def test_length(self): @@ -198,15 +172,11 @@ class SmbpasswdTestCase(TestCase): def tearDown(self): self.samdb.close() - super(SmbpasswdTestCase, self).tearDown() - -class IdmapDbTestCase(TestCase): +class IdmapDbTestCase(unittest.TestCase): def setUp(self): - super(IdmapDbTestCase, self).setUp() - self.idmapdb = IdmapDatabase(os.path.join(DATADIR, - "winbindd_idmap.tdb")) + self.idmapdb = IdmapDatabase(os.path.join(DATADIR, "winbindd_idmap.tdb")) def test_user_hwm(self): self.assertEquals(10000, self.idmapdb.get_user_hwm()) @@ -228,11 +198,19 @@ class IdmapDbTestCase(TestCase): def tearDown(self): self.idmapdb.close() - super(IdmapDbTestCase, self).tearDown() -class ParamTestCase(TestCase): +class ShareInfoTestCase(unittest.TestCase): + def setUp(self): + self.shareinfodb = ShareInfoDatabase(os.path.join(DATADIR, "share_info.tdb")) + + # FIXME: needs proper data so it can be tested + + def tearDown(self): + self.shareinfodb.close() + +class ParamTestCase(unittest.TestCase): def test_init(self): file = ParamFile() self.assertTrue(file is not None) diff --git a/source4/scripting/python/samba/tests/samba3sam.py b/source4/scripting/python/samba/tests/samba3sam.py deleted file mode 100644 index a34f0f620c..0000000000 --- a/source4/scripting/python/samba/tests/samba3sam.py +++ /dev/null @@ -1,1096 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2005-2008 -# Copyright (C) Martin Kuehl <mkhl@samba.org> 2006 -# -# This is a Python port of the original in testprogs/ejs/samba3sam.js -# -# 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/>. -# - -"""Tests for the samba3sam LDB module, which maps Samba3 LDAP to AD LDAP.""" - -import os -import ldb -from ldb import SCOPE_DEFAULT, SCOPE_BASE -from samba import Ldb, substitute_var -from samba.tests import TestCaseInTempDir, env_loadparm -import samba.dcerpc.security -import samba.ndr -from samba.auth import system_session - - -def read_datafile(filename): - paths = [ "../../../../../testdata/samba3", - "../../../../testdata/samba3" ] - for p in paths: - datadir = os.path.join(os.path.dirname(__file__), p) - if os.path.exists(datadir): - break - return open(os.path.join(datadir, filename), 'r').read() - -def ldb_debug(l, text): - print text - - -class MapBaseTestCase(TestCaseInTempDir): - """Base test case for mapping tests.""" - - def setup_modules(self, ldb, s3, s4): - ldb.add({"dn": "@MAP=samba3sam", - "@FROM": s4.basedn, - "@TO": "sambaDomainName=TESTS," + s3.basedn}) - - ldb.add({"dn": "@MODULES", - "@LIST": "rootdse,paged_results,server_sort,asq,samldb,password_hash,operational,objectguid,rdn_name,samba3sam,samba3sid,partition"}) - - ldb.add({"dn": "@PARTITION", - "partition": ["%s" % (s4.basedn_casefold), - "%s" % (s3.basedn_casefold)], - "replicateEntries": ["@ATTRIBUTES", "@INDEXLIST"], - "modules": "*:"}) - - def setUp(self): - self.lp = env_loadparm() - self.lp.set("sid generator", "backend") - self.lp.set("workgroup", "TESTS") - self.lp.set("netbios name", "TESTS") - super(MapBaseTestCase, self).setUp() - - def make_dn(basedn, rdn): - return "%s,sambaDomainName=TESTS,%s" % (rdn, basedn) - - def make_s4dn(basedn, rdn): - return "%s,%s" % (rdn, basedn) - - self.ldbfile = os.path.join(self.tempdir, "test.ldb") - self.ldburl = "tdb://" + self.ldbfile - - tempdir = self.tempdir - - class Target: - """Simple helper class that contains data for a specific SAM - connection.""" - - def __init__(self, basedn, dn, lp): - self.db = Ldb(lp=lp, session_info=system_session()) - self.basedn = basedn - self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold() - self.substvars = {"BASEDN": self.basedn} - self.file = os.path.join(tempdir, "%s.ldb" % self.basedn_casefold) - self.url = "tdb://" + self.file - self._dn = dn - - def dn(self, rdn): - return self._dn(self.basedn, rdn) - - def connect(self): - return self.db.connect(self.url) - - def setup_data(self, path): - self.add_ldif(read_datafile(path)) - - def subst(self, text): - return substitute_var(text, self.substvars) - - def add_ldif(self, ldif): - self.db.add_ldif(self.subst(ldif)) - - def modify_ldif(self, ldif): - self.db.modify_ldif(self.subst(ldif)) - - self.samba4 = Target("dc=vernstok,dc=nl", make_s4dn, self.lp) - self.samba3 = Target("cn=Samba3Sam", make_dn, self.lp) - - self.samba3.connect() - self.samba4.connect() - - def tearDown(self): - os.unlink(self.ldbfile) - os.unlink(self.samba3.file) - os.unlink(self.samba4.file) - super(MapBaseTestCase, self).tearDown() - - def assertSidEquals(self, text, ndr_sid): - sid_obj1 = samba.ndr.ndr_unpack(samba.dcerpc.security.dom_sid, - str(ndr_sid[0])) - sid_obj2 = samba.dcerpc.security.dom_sid(text) - self.assertEquals(sid_obj1, sid_obj2) - - -class Samba3SamTestCase(MapBaseTestCase): - - def setUp(self): - super(Samba3SamTestCase, self).setUp() - ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) - self.samba3.setup_data("samba3.ldif") - ldif = read_datafile("provision_samba3sam.ldif") - ldb.add_ldif(self.samba4.subst(ldif)) - self.setup_modules(ldb, self.samba3, self.samba4) - del ldb - self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) - - def test_search_non_mapped(self): - """Looking up by non-mapped attribute""" - msg = self.ldb.search(expression="(cn=Administrator)") - self.assertEquals(len(msg), 1) - self.assertEquals(msg[0]["cn"], "Administrator") - - def test_search_non_mapped(self): - """Looking up by mapped attribute""" - msg = self.ldb.search(expression="(name=Backup Operators)") - self.assertEquals(len(msg), 1) - self.assertEquals(str(msg[0]["name"]), "Backup Operators") - - def test_old_name_of_renamed(self): - """Looking up by old name of renamed attribute""" - msg = self.ldb.search(expression="(displayName=Backup Operators)") - self.assertEquals(len(msg), 0) - - def test_mapped_containing_sid(self): - """Looking up mapped entry containing SID""" - msg = self.ldb.search(expression="(cn=Replicator)") - self.assertEquals(len(msg), 1) - self.assertEquals(str(msg[0].dn), - "cn=Replicator,ou=Groups,dc=vernstok,dc=nl") - self.assertTrue("objectSid" in msg[0]) - self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552", - msg[0]["objectSid"]) - oc = set(msg[0]["objectClass"]) - self.assertEquals(oc, set(["group"])) - - def test_search_by_objclass(self): - """Looking up by objectClass""" - msg = self.ldb.search(expression="(|(objectClass=user)(cn=Administrator))") - self.assertEquals(set([str(m.dn) for m in msg]), - set(["unixName=Administrator,ou=Users,dc=vernstok,dc=nl", - "unixName=nobody,ou=Users,dc=vernstok,dc=nl"])) - - def test_s3sam_modify(self): - # Adding a record that will be fallbacked - self.ldb.add({"dn": "cn=Foo", - "foo": "bar", - "blah": "Blie", - "cn": "Foo", - "showInAdvancedViewOnly": "TRUE"} - ) - - # Checking for existence of record (local) - # TODO: This record must be searched in the local database, which is - # currently only supported for base searches - # msg = ldb.search(expression="(cn=Foo)", ['foo','blah','cn','showInAdvancedViewOnly')] - # TODO: Actually, this version should work as well but doesn't... - # - # - msg = self.ldb.search(expression="(cn=Foo)", base="cn=Foo", - scope=SCOPE_BASE, - attrs=['foo','blah','cn','showInAdvancedViewOnly']) - self.assertEquals(len(msg), 1) - self.assertEquals(str(msg[0]["showInAdvancedViewOnly"]), "TRUE") - self.assertEquals(str(msg[0]["foo"]), "bar") - self.assertEquals(str(msg[0]["blah"]), "Blie") - - # Adding record that will be mapped - self.ldb.add({"dn": "cn=Niemand,cn=Users,dc=vernstok,dc=nl", - "objectClass": "user", - "unixName": "bin", - "sambaUnicodePwd": "geheim", - "cn": "Niemand"}) - - # Checking for existence of record (remote) - msg = self.ldb.search(expression="(unixName=bin)", - attrs=['unixName','cn','dn', 'sambaUnicodePwd']) - self.assertEquals(len(msg), 1) - self.assertEquals(str(msg[0]["cn"]), "Niemand") - self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") - - # Checking for existence of record (local && remote) - msg = self.ldb.search(expression="(&(unixName=bin)(sambaUnicodePwd=geheim))", - attrs=['unixName','cn','dn', 'sambaUnicodePwd']) - self.assertEquals(len(msg), 1) # TODO: should check with more records - self.assertEquals(str(msg[0]["cn"]), "Niemand") - self.assertEquals(str(msg[0]["unixName"]), "bin") - self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") - - # Checking for existence of record (local || remote) - msg = self.ldb.search(expression="(|(unixName=bin)(sambaUnicodePwd=geheim))", - attrs=['unixName','cn','dn', 'sambaUnicodePwd']) - #print "got %d replies" % len(msg) - self.assertEquals(len(msg), 1) # TODO: should check with more records - self.assertEquals(str(msg[0]["cn"]), "Niemand") - self.assertEquals(str(msg[0]["unixName"]), "bin") - self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim") - - # Checking for data in destination database - msg = self.samba3.db.search(expression="(cn=Niemand)") - self.assertTrue(len(msg) >= 1) - self.assertEquals(str(msg[0]["sambaSID"]), - "S-1-5-21-4231626423-2410014848-2360679739-2001") - self.assertEquals(str(msg[0]["displayName"]), "Niemand") - - # Adding attribute... - self.ldb.modify_ldif(""" -dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl -changetype: modify -add: description -description: Blah -""") - - # Checking whether changes are still there... - msg = self.ldb.search(expression="(cn=Niemand)") - self.assertTrue(len(msg) >= 1) - self.assertEquals(str(msg[0]["cn"]), "Niemand") - self.assertEquals(str(msg[0]["description"]), "Blah") - - # Modifying attribute... - self.ldb.modify_ldif(""" -dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl -changetype: modify -replace: description -description: Blie -""") - - # Checking whether changes are still there... - msg = self.ldb.search(expression="(cn=Niemand)") - self.assertTrue(len(msg) >= 1) - self.assertEquals(str(msg[0]["description"]), "Blie") - - # Deleting attribute... - self.ldb.modify_ldif(""" -dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl -changetype: modify -delete: description -""") - - # Checking whether changes are no longer there... - msg = self.ldb.search(expression="(cn=Niemand)") - self.assertTrue(len(msg) >= 1) - self.assertTrue(not "description" in msg[0]) - - # Renaming record... - self.ldb.rename("cn=Niemand,cn=Users,dc=vernstok,dc=nl", - "cn=Niemand2,cn=Users,dc=vernstok,dc=nl") - - # Checking whether DN has changed... - msg = self.ldb.search(expression="(cn=Niemand2)") - self.assertEquals(len(msg), 1) - self.assertEquals(str(msg[0].dn), - "cn=Niemand2,cn=Users,dc=vernstok,dc=nl") - - # Deleting record... - self.ldb.delete("cn=Niemand2,cn=Users,dc=vernstok,dc=nl") - - # Checking whether record is gone... - msg = self.ldb.search(expression="(cn=Niemand2)") - self.assertEquals(len(msg), 0) - - -class MapTestCase(MapBaseTestCase): - - def setUp(self): - super(MapTestCase, self).setUp() - ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) - ldif = read_datafile("provision_samba3sam.ldif") - ldb.add_ldif(self.samba4.subst(ldif)) - self.setup_modules(ldb, self.samba3, self.samba4) - del ldb - self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) - - def test_map_search(self): - """Running search tests on mapped data.""" - self.samba3.db.add({ - "dn": "sambaDomainName=TESTS," + self.samba3.basedn, - "objectclass": ["sambaDomain", "top"], - "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739", - "sambaNextRid": "2000", - "sambaDomainName": "TESTS" - }) - - # Add a set of split records - self.ldb.add_ldif(""" -dn: """+ self.samba4.dn("cn=Domain Users") + """ -objectClass: group -cn: Domain Users -objectSid: S-1-5-21-4231626423-2410014848-2360679739-513 -""") - - # Add a set of split records - self.ldb.add_ldif(""" -dn: """+ self.samba4.dn("cn=X") + """ -objectClass: user -cn: X -codePage: x -revision: x -dnsHostName: x -nextRid: y -lastLogon: x -description: x -objectSid: S-1-5-21-4231626423-2410014848-2360679739-552 -""") - - self.ldb.add({ - "dn": self.samba4.dn("cn=Y"), - "objectClass": "top", - "cn": "Y", - "codePage": "x", - "revision": "x", - "dnsHostName": "y", - "nextRid": "y", - "lastLogon": "y", - "description": "x"}) - - self.ldb.add({ - "dn": self.samba4.dn("cn=Z"), - "objectClass": "top", - "cn": "Z", - "codePage": "x", - "revision": "y", - "dnsHostName": "z", - "nextRid": "y", - "lastLogon": "z", - "description": "y"}) - - # Add a set of remote records - - self.samba3.db.add({ - "dn": self.samba3.dn("cn=A"), - "objectClass": "posixAccount", - "cn": "A", - "sambaNextRid": "x", - "sambaBadPasswordCount": "x", - "sambaLogonTime": "x", - "description": "x", - "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739-552", - "sambaPrimaryGroupSID": "S-1-5-21-4231626423-2410014848-2360679739-512"}) - - self.samba3.db.add({ - "dn": self.samba3.dn("cn=B"), - "objectClass": "top", - "cn": "B", - "sambaNextRid": "x", - "sambaBadPasswordCount": "x", - "sambaLogonTime": "y", - "description": "x"}) - - self.samba3.db.add({ - "dn": self.samba3.dn("cn=C"), - "objectClass": "top", - "cn": "C", - "sambaNextRid": "x", - "sambaBadPasswordCount": "y", - "sambaLogonTime": "z", - "description": "y"}) - - # Testing search by DN - - # Search remote record by local DN - dn = self.samba4.dn("cn=A") - res = self.ldb.search(dn, scope=SCOPE_BASE, - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "x") - - # Search remote record by remote DN - dn = self.samba3.dn("cn=A") - res = self.samba3.db.search(dn, scope=SCOPE_BASE, - attrs=["dnsHostName", "lastLogon", "sambaLogonTime"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertTrue(not "lastLogon" in res[0]) - self.assertEquals(str(res[0]["sambaLogonTime"]), "x") - - # Search split record by local DN - dn = self.samba4.dn("cn=X") - res = self.ldb.search(dn, scope=SCOPE_BASE, - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["dnsHostName"]), "x") - self.assertEquals(str(res[0]["lastLogon"]), "x") - - # Search split record by remote DN - dn = self.samba3.dn("cn=X") - res = self.samba3.db.search(dn, scope=SCOPE_BASE, - attrs=["dnsHostName", "lastLogon", "sambaLogonTime"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertTrue(not "lastLogon" in res[0]) - self.assertEquals(str(res[0]["sambaLogonTime"]), "x") - - # Testing search by attribute - - # Search by ignored attribute - res = self.ldb.search(expression="(revision=x)", scope=SCOPE_DEFAULT, - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 2) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y")) - self.assertEquals(str(res[0]["dnsHostName"]), "y") - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[1]["dnsHostName"]), "x") - self.assertEquals(str(res[1]["lastLogon"]), "x") - - # Search by kept attribute - res = self.ldb.search(expression="(description=y)", - scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 2) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[0]["dnsHostName"]), "z") - self.assertEquals(str(res[0]["lastLogon"]), "z") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "z") - - # Search by renamed attribute - res = self.ldb.search(expression="(badPwdCount=x)", scope=SCOPE_DEFAULT, - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 2) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") - - # Search by converted attribute - # TODO: - # Using the SID directly in the parse tree leads to conversion - # errors, letting the search fail with no results. - #res = self.ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-552)", scope=SCOPE_DEFAULT, attrs) - res = self.ldb.search(expression="(objectSid=*)", base=None, scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon", "objectSid"]) - self.assertEquals(len(res), 4) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[0]["dnsHostName"]), "x") - self.assertEquals(str(res[0]["lastLogon"]), "x") - self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552", - res[0]["objectSid"]) - self.assertTrue("objectSid" in res[0]) - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552", - res[1]["objectSid"]) - self.assertTrue("objectSid" in res[1]) - - # Search by generated attribute - # In most cases, this even works when the mapping is missing - # a `convert_operator' by enumerating the remote db. - res = self.ldb.search(expression="(primaryGroupID=512)", - attrs=["dnsHostName", "lastLogon", "primaryGroupID"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "x") - self.assertEquals(str(res[0]["primaryGroupID"]), "512") - - # Note that Xs "objectSid" seems to be fine in the previous search for - # "objectSid"... - #res = ldb.search(expression="(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs) - #print len(res) + " results found" - #for i in range(len(res)): - # for (obj in res[i]) { - # print obj + ": " + res[i][obj] - # } - # print "---" - # - - # Search by remote name of renamed attribute */ - res = self.ldb.search(expression="(sambaBadPasswordCount=*)", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 0) - - # Search by objectClass - attrs = ["dnsHostName", "lastLogon", "objectClass"] - res = self.ldb.search(expression="(objectClass=user)", attrs=attrs) - self.assertEquals(len(res), 2) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[0]["dnsHostName"]), "x") - self.assertEquals(str(res[0]["lastLogon"]), "x") - self.assertEquals(str(res[0]["objectClass"][0]), "user") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertEquals(str(res[1]["objectClass"][0]), "user") - - # Prove that the objectClass is actually used for the search - res = self.ldb.search(expression="(|(objectClass=user)(badPwdCount=x))", - attrs=attrs) - self.assertEquals(len(res), 3) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(set(res[0]["objectClass"]), set(["top"])) - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[1]["dnsHostName"]), "x") - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertEquals(str(res[1]["objectClass"][0]), "user") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[2]) - self.assertEquals(str(res[2]["lastLogon"]), "x") - self.assertEquals(res[2]["objectClass"][0], "user") - - # Testing search by parse tree - - # Search by conjunction of local attributes - res = self.ldb.search(expression="(&(codePage=x)(revision=x))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 2) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y")) - self.assertEquals(str(res[0]["dnsHostName"]), "y") - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[1]["dnsHostName"]), "x") - self.assertEquals(str(res[1]["lastLogon"]), "x") - - # Search by conjunction of remote attributes - res = self.ldb.search(expression="(&(lastLogon=x)(description=x))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 2) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[0]["dnsHostName"]), "x") - self.assertEquals(str(res[0]["lastLogon"]), "x") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") - - # Search by conjunction of local and remote attribute - res = self.ldb.search(expression="(&(codePage=x)(description=x))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 2) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y")) - self.assertEquals(str(res[0]["dnsHostName"]), "y") - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[1]["dnsHostName"]), "x") - self.assertEquals(str(res[1]["lastLogon"]), "x") - - # Search by conjunction of local and remote attribute w/o match - attrs = ["dnsHostName", "lastLogon"] - res = self.ldb.search(expression="(&(codePage=x)(nextRid=x))", - attrs=attrs) - self.assertEquals(len(res), 0) - res = self.ldb.search(expression="(&(revision=x)(lastLogon=z))", - attrs=attrs) - self.assertEquals(len(res), 0) - - # Search by disjunction of local attributes - res = self.ldb.search(expression="(|(revision=x)(dnsHostName=x))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 2) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y")) - self.assertEquals(str(res[0]["dnsHostName"]), "y") - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[1]["dnsHostName"]), "x") - self.assertEquals(str(res[1]["lastLogon"]), "x") - - # Search by disjunction of remote attributes - res = self.ldb.search(expression="(|(badPwdCount=x)(lastLogon=x))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 3) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) - self.assertFalse("dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[1]["dnsHostName"]), "x") - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A")) - self.assertFalse("dnsHostName" in res[2]) - self.assertEquals(str(res[2]["lastLogon"]), "x") - - # Search by disjunction of local and remote attribute - res = self.ldb.search(expression="(|(revision=x)(lastLogon=y))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 3) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y")) - self.assertEquals(str(res[0]["dnsHostName"]), "y") - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) - self.assertFalse("dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "y") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[2]["dnsHostName"]), "x") - self.assertEquals(str(res[2]["lastLogon"]), "x") - - # Search by disjunction of local and remote attribute w/o match - res = self.ldb.search(expression="(|(codePage=y)(nextRid=z))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 0) - - # Search by negated local attribute - res = self.ldb.search(expression="(!(revision=x))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 6) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[2]["dnsHostName"]), "z") - self.assertEquals(str(res[2]["lastLogon"]), "z") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) - self.assertEquals(str(res[3]["lastLogon"]), "z") - - # Search by negated remote attribute - res = self.ldb.search(expression="(!(description=x))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 4) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[0]["dnsHostName"]), "z") - self.assertEquals(str(res[0]["lastLogon"]), "z") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "z") - - # Search by negated conjunction of local attributes - res = self.ldb.search(expression="(!(&(codePage=x)(revision=x)))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 6) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[2]["dnsHostName"]), "z") - self.assertEquals(str(res[2]["lastLogon"]), "z") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) - self.assertEquals(str(res[3]["lastLogon"]), "z") - - # Search by negated conjunction of remote attributes - res = self.ldb.search(expression="(!(&(lastLogon=x)(description=x)))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 6) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y")) - self.assertEquals(str(res[0]["dnsHostName"]), "y") - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "y") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[2]["dnsHostName"]), "z") - self.assertEquals(str(res[2]["lastLogon"]), "z") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) - self.assertEquals(str(res[3]["lastLogon"]), "z") - - # Search by negated conjunction of local and remote attribute - res = self.ldb.search(expression="(!(&(codePage=x)(description=x)))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 6) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[2]["dnsHostName"]), "z") - self.assertEquals(str(res[2]["lastLogon"]), "z") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) - self.assertEquals(str(res[3]["lastLogon"]), "z") - - # Search by negated disjunction of local attributes - res = self.ldb.search(expression="(!(|(revision=x)(dnsHostName=x)))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[2]["dnsHostName"]), "z") - self.assertEquals(str(res[2]["lastLogon"]), "z") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) - self.assertEquals(str(res[3]["lastLogon"]), "z") - - # Search by negated disjunction of remote attributes - res = self.ldb.search(expression="(!(|(badPwdCount=x)(lastLogon=x)))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 5) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y")) - self.assertEquals(str(res[0]["dnsHostName"]), "y") - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[1]["dnsHostName"]), "z") - self.assertEquals(str(res[1]["lastLogon"]), "z") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[2]) - self.assertEquals(str(res[2]["lastLogon"]), "z") - - # Search by negated disjunction of local and remote attribute - res = self.ldb.search(expression="(!(|(revision=x)(lastLogon=y)))", - attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 5) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "x") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[1]["dnsHostName"]), "z") - self.assertEquals(str(res[1]["lastLogon"]), "z") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[2]) - self.assertEquals(str(res[2]["lastLogon"]), "z") - - # Search by complex parse tree - res = self.ldb.search(expression="(|(&(revision=x)(dnsHostName=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", attrs=["dnsHostName", "lastLogon"]) - self.assertEquals(len(res), 7) - self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B")) - self.assertTrue(not "dnsHostName" in res[0]) - self.assertEquals(str(res[0]["lastLogon"]), "y") - self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X")) - self.assertEquals(str(res[1]["dnsHostName"]), "x") - self.assertEquals(str(res[1]["lastLogon"]), "x") - self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A")) - self.assertTrue(not "dnsHostName" in res[2]) - self.assertEquals(str(res[2]["lastLogon"]), "x") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z")) - self.assertEquals(str(res[3]["dnsHostName"]), "z") - self.assertEquals(str(res[3]["lastLogon"]), "z") - self.assertEquals(str(res[4].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[4]) - self.assertEquals(str(res[4]["lastLogon"]), "z") - - # Clean up - dns = [self.samba4.dn("cn=%s" % n) for n in ["A","B","C","X","Y","Z"]] - for dn in dns: - self.ldb.delete(dn) - - def test_map_modify_local(self): - """Modification of local records.""" - # Add local record - dn = "cn=test,dc=idealx,dc=org" - self.ldb.add({"dn": dn, - "cn": "test", - "foo": "bar", - "revision": "1", - "description": "test"}) - # Check it's there - attrs = ["foo", "revision", "description"] - res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["foo"]), "bar") - self.assertEquals(str(res[0]["revision"]), "1") - self.assertEquals(str(res[0]["description"]), "test") - # Check it's not in the local db - res = self.samba4.db.search(expression="(cn=test)", - scope=SCOPE_DEFAULT, attrs=attrs) - self.assertEquals(len(res), 0) - # Check it's not in the remote db - res = self.samba3.db.search(expression="(cn=test)", - scope=SCOPE_DEFAULT, attrs=attrs) - self.assertEquals(len(res), 0) - - # Modify local record - ldif = """ -dn: """ + dn + """ -replace: foo -foo: baz -replace: description -description: foo -""" - self.ldb.modify_ldif(ldif) - # Check in local db - res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["foo"]), "baz") - self.assertEquals(str(res[0]["revision"]), "1") - self.assertEquals(str(res[0]["description"]), "foo") - - # Rename local record - dn2 = "cn=toast,dc=idealx,dc=org" - self.ldb.rename(dn, dn2) - # Check in local db - res = self.ldb.search(dn2, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn2) - self.assertEquals(str(res[0]["foo"]), "baz") - self.assertEquals(str(res[0]["revision"]), "1") - self.assertEquals(str(res[0]["description"]), "foo") - - # Delete local record - self.ldb.delete(dn2) - # Check it's gone - res = self.ldb.search(dn2, scope=SCOPE_BASE) - self.assertEquals(len(res), 0) - - def test_map_modify_remote_remote(self): - """Modification of remote data of remote records""" - # Add remote record - dn = self.samba4.dn("cn=test") - dn2 = self.samba3.dn("cn=test") - self.samba3.db.add({"dn": dn2, - "cn": "test", - "description": "foo", - "sambaBadPasswordCount": "3", - "sambaNextRid": "1001"}) - # Check it's there - res = self.samba3.db.search(dn2, scope=SCOPE_BASE, - attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn2) - self.assertEquals(str(res[0]["description"]), "foo") - self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3") - self.assertEquals(str(res[0]["sambaNextRid"]), "1001") - # Check in mapped db - attrs = ["description", "badPwdCount", "nextRid"] - res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs, expression="") - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["description"]), "foo") - self.assertEquals(str(res[0]["badPwdCount"]), "3") - self.assertEquals(str(res[0]["nextRid"]), "1001") - # Check in local db - res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 0) - - # Modify remote data of remote record - ldif = """ -dn: """ + dn + """ -replace: description -description: test -replace: badPwdCount -badPwdCount: 4 -""" - self.ldb.modify_ldif(ldif) - # Check in mapped db - res = self.ldb.search(dn, scope=SCOPE_BASE, - attrs=["description", "badPwdCount", "nextRid"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["badPwdCount"]), "4") - self.assertEquals(str(res[0]["nextRid"]), "1001") - # Check in remote db - res = self.samba3.db.search(dn2, scope=SCOPE_BASE, - attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn2) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") - self.assertEquals(str(res[0]["sambaNextRid"]), "1001") - - # Rename remote record - dn2 = self.samba4.dn("cn=toast") - self.ldb.rename(dn, dn2) - # Check in mapped db - dn = dn2 - res = self.ldb.search(dn, scope=SCOPE_BASE, - attrs=["description", "badPwdCount", "nextRid"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["badPwdCount"]), "4") - self.assertEquals(str(res[0]["nextRid"]), "1001") - # Check in remote db - dn2 = self.samba3.dn("cn=toast") - res = self.samba3.db.search(dn2, scope=SCOPE_BASE, - attrs=["description", "sambaBadPasswordCount", "sambaNextRid"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn2) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") - self.assertEquals(str(res[0]["sambaNextRid"]), "1001") - - # Delete remote record - self.ldb.delete(dn) - # Check in mapped db that it's removed - res = self.ldb.search(dn, scope=SCOPE_BASE) - self.assertEquals(len(res), 0) - # Check in remote db - res = self.samba3.db.search(dn2, scope=SCOPE_BASE) - self.assertEquals(len(res), 0) - - def test_map_modify_remote_local(self): - """Modification of local data of remote records""" - # Add remote record (same as before) - dn = self.samba4.dn("cn=test") - dn2 = self.samba3.dn("cn=test") - self.samba3.db.add({"dn": dn2, - "cn": "test", - "description": "foo", - "sambaBadPasswordCount": "3", - "sambaNextRid": "1001"}) - - # Modify local data of remote record - ldif = """ -dn: """ + dn + """ -add: revision -revision: 1 -replace: description -description: test - -""" - self.ldb.modify_ldif(ldif) - # Check in mapped db - attrs = ["revision", "description"] - res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["revision"]), "1") - # Check in remote db - res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn2) - self.assertEquals(str(res[0]["description"]), "test") - self.assertTrue(not "revision" in res[0]) - # Check in local db - res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertTrue(not "description" in res[0]) - self.assertEquals(str(res[0]["revision"]), "1") - - # Delete (newly) split record - self.ldb.delete(dn) - - def test_map_modify_split(self): - """Testing modification of split records""" - # Add split record - dn = self.samba4.dn("cn=test") - dn2 = self.samba3.dn("cn=test") - self.ldb.add({ - "dn": dn, - "cn": "test", - "description": "foo", - "badPwdCount": "3", - "nextRid": "1001", - "revision": "1"}) - # Check it's there - attrs = ["description", "badPwdCount", "nextRid", "revision"] - res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["description"]), "foo") - self.assertEquals(str(res[0]["badPwdCount"]), "3") - self.assertEquals(str(res[0]["nextRid"]), "1001") - self.assertEquals(str(res[0]["revision"]), "1") - # Check in local db - res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertTrue(not "description" in res[0]) - self.assertTrue(not "badPwdCount" in res[0]) - self.assertTrue(not "nextRid" in res[0]) - self.assertEquals(str(res[0]["revision"]), "1") - # Check in remote db - attrs = ["description", "sambaBadPasswordCount", "sambaNextRid", - "revision"] - res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn2) - self.assertEquals(str(res[0]["description"]), "foo") - self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3") - self.assertEquals(str(res[0]["sambaNextRid"]), "1001") - self.assertTrue(not "revision" in res[0]) - - # Modify of split record - ldif = """ -dn: """ + dn + """ -replace: description -description: test -replace: badPwdCount -badPwdCount: 4 -replace: revision -revision: 2 -""" - self.ldb.modify_ldif(ldif) - # Check in mapped db - attrs = ["description", "badPwdCount", "nextRid", "revision"] - res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["badPwdCount"]), "4") - self.assertEquals(str(res[0]["nextRid"]), "1001") - self.assertEquals(str(res[0]["revision"]), "2") - # Check in local db - res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertTrue(not "description" in res[0]) - self.assertTrue(not "badPwdCount" in res[0]) - self.assertTrue(not "nextRid" in res[0]) - self.assertEquals(str(res[0]["revision"]), "2") - # Check in remote db - attrs = ["description", "sambaBadPasswordCount", "sambaNextRid", - "revision"] - res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn2) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") - self.assertEquals(str(res[0]["sambaNextRid"]), "1001") - self.assertTrue(not "revision" in res[0]) - - # Rename split record - dn2 = self.samba4.dn("cn=toast") - self.ldb.rename(dn, dn2) - # Check in mapped db - dn = dn2 - attrs = ["description", "badPwdCount", "nextRid", "revision"] - res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["badPwdCount"]), "4") - self.assertEquals(str(res[0]["nextRid"]), "1001") - self.assertEquals(str(res[0]["revision"]), "2") - # Check in local db - res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn) - self.assertTrue(not "description" in res[0]) - self.assertTrue(not "badPwdCount" in res[0]) - self.assertTrue(not "nextRid" in res[0]) - self.assertEquals(str(res[0]["revision"]), "2") - # Check in remote db - dn2 = self.samba3.dn("cn=toast") - res = self.samba3.db.search(dn2, scope=SCOPE_BASE, - attrs=["description", "sambaBadPasswordCount", "sambaNextRid", - "revision"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0].dn), dn2) - self.assertEquals(str(res[0]["description"]), "test") - self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4") - self.assertEquals(str(res[0]["sambaNextRid"]), "1001") - self.assertTrue(not "revision" in res[0]) - - # Delete split record - self.ldb.delete(dn) - # Check in mapped db - res = self.ldb.search(dn, scope=SCOPE_BASE) - self.assertEquals(len(res), 0) - # Check in local db - res = self.samba4.db.search(dn, scope=SCOPE_BASE) - self.assertEquals(len(res), 0) - # Check in remote db - res = self.samba3.db.search(dn2, scope=SCOPE_BASE) - self.assertEquals(len(res), 0) diff --git a/source4/scripting/python/samba/tests/samdb.py b/source4/scripting/python/samba/tests/samdb.py index 3df72b0840..8c7bb0ae98 100644 --- a/source4/scripting/python/samba/tests/samdb.py +++ b/source4/scripting/python/samba/tests/samdb.py @@ -1,46 +1,43 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. Tests for SamDB # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 # 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/>. # - -"""Tests for samba.samdb.""" - -import logging -import os -import uuid - from samba.auth import system_session -from samba.provision import (setup_samdb, guess_names, make_smbconf, - provision_paths_from_lp) -from samba.provision import DEFAULT_POLICY_GUID, DEFAULT_DC_POLICY_GUID -from samba.provision.backend import ProvisionBackend +from samba.credentials import Credentials +import os +from samba.provision import setup_samdb, guess_names, setup_templatesdb, make_smbconf, find_setup_dir +from samba.samdb import SamDB from samba.tests import TestCaseInTempDir from samba.dcerpc import security -from samba.schema import Schema +from unittest import TestCase +import uuid from samba import param class SamDBTestCase(TestCaseInTempDir): """Base-class for tests with a Sam Database. - + This is used by the Samba SamDB-tests, but e.g. also by the OpenChange provisioning tests (which need a Sam). """ + def setup_path(self, relpath): + return os.path.join(find_setup_dir(), relpath) + def setUp(self): super(SamDBTestCase, self).setUp() invocationid = str(uuid.uuid4()) @@ -49,8 +46,11 @@ class SamDBTestCase(TestCaseInTempDir): configdn = "CN=Configuration," + domaindn schemadn = "CN=Schema," + configdn domainguid = str(uuid.uuid4()) - policyguid = DEFAULT_POLICY_GUID + policyguid = str(uuid.uuid4()) + creds = Credentials() + creds.set_anonymous() domainsid = security.random_sid() + hostguid = str(uuid.uuid4()) path = os.path.join(self.tempdir, "samdb.ldb") session_info = system_session() @@ -58,10 +58,9 @@ class SamDBTestCase(TestCaseInTempDir): domain="EXAMPLE" dnsdomain="example.com" serverrole="domain controller" - policyguid_dc = DEFAULT_DC_POLICY_GUID smbconf = os.path.join(self.tempdir, "smb.conf") - make_smbconf(smbconf, hostname, domain, dnsdomain, + make_smbconf(smbconf, self.setup_path, hostname, domain, dnsdomain, serverrole, self.tempdir) self.lp = param.LoadParm() @@ -72,27 +71,26 @@ class SamDBTestCase(TestCaseInTempDir): serverrole=serverrole, domaindn=self.domaindn, configdn=configdn, schemadn=schemadn) - - paths = provision_paths_from_lp(self.lp, names.dnsdomain) - - logger = logging.getLogger("provision") - - provision_backend = ProvisionBackend("ldb", paths=paths, - lp=self.lp, credentials=None, - names=names, logger=logger) - - schema = Schema(domainsid, invocationid=invocationid, - schemadn=names.schemadn, serverdn=names.serverdn, - am_rodc=False) - - self.samdb = setup_samdb(path, session_info, - provision_backend, self.lp, names, logger, - domainsid, domainguid, policyguid, policyguid_dc, False, - "secret", "secret", "secret", invocationid, "secret", - None, "domain controller", schema=schema) + setup_templatesdb(os.path.join(self.tempdir, "templates.ldb"), + self.setup_path, session_info=session_info, lp=self.lp) + self.samdb = setup_samdb(path, self.setup_path, session_info, creds, + self.lp, names, + lambda x: None, domainsid, + domainguid, + policyguid, False, "secret", + "secret", "secret", invocationid, + "secret", "domain controller") def tearDown(self): - for f in ['schema.ldb', 'configuration.ldb', + for f in ['templates.ldb', 'schema.ldb', 'configuration.ldb', 'users.ldb', 'samdb.ldb', 'smb.conf']: os.remove(os.path.join(self.tempdir, f)) super(SamDBTestCase, self).tearDown() + + +# disable this test till andrew works it out ... +class SamDBTests(SamDBTestCase): + """Tests for the SamDB implementation.""" + + print "samdb add_foreign disabled for now" +# def test_add_foreign(self): diff --git a/source4/scripting/python/samba/tests/security.py b/source4/scripting/python/samba/tests/security.py deleted file mode 100644 index 59e3113068..0000000000 --- a/source4/scripting/python/samba/tests/security.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# 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 -# 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/>. -# - -"""Tests for samba.dcerpc.security.""" - -import samba.tests -from samba.dcerpc import security - -class SecurityTokenTests(samba.tests.TestCase): - - def setUp(self): - super(SecurityTokenTests, self).setUp() - self.token = security.token() - - def test_is_system(self): - self.assertFalse(self.token.is_system()) - - def test_is_anonymous(self): - self.assertFalse(self.token.is_anonymous()) - - def test_has_builtin_administrators(self): - self.assertFalse(self.token.has_builtin_administrators()) - - def test_has_nt_authenticated_users(self): - self.assertFalse(self.token.has_nt_authenticated_users()) - - def test_has_priv(self): - self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN)) - - def test_set_priv(self): - self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN)) - self.assertFalse(self.token.set_privilege(security.SEC_PRIV_SHUTDOWN)) - self.assertTrue(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN)) - - -class SecurityDescriptorTests(samba.tests.TestCase): - - def setUp(self): - super(SecurityDescriptorTests, self).setUp() - self.descriptor = security.descriptor() - - def test_from_sddl(self): - desc = security.descriptor.from_sddl("O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)", security.dom_sid("S-2-0-0")) - self.assertEquals(desc.group_sid, security.dom_sid('S-2-0-0-512')) - self.assertEquals(desc.owner_sid, security.dom_sid('S-1-5-32-548')) - self.assertEquals(desc.revision, 1) - self.assertEquals(desc.sacl, None) - self.assertEquals(desc.type, 0x8004) - - def test_from_sddl_invalidsddl(self): - self.assertRaises(TypeError,security.descriptor.from_sddl, "foo",security.dom_sid("S-2-0-0")) - - def test_from_sddl_invalidtype1(self): - self.assertRaises(TypeError, security.descriptor.from_sddl, security.dom_sid('S-2-0-0-512'),security.dom_sid("S-2-0-0")) - - def test_from_sddl_invalidtype2(self): - sddl = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)" - self.assertRaises(TypeError, security.descriptor.from_sddl, sddl, - "S-2-0-0") - - def test_as_sddl(self): - text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)" - dom = security.dom_sid("S-2-0-0") - desc1 = security.descriptor.from_sddl(text, dom) - desc2 = security.descriptor.from_sddl(desc1.as_sddl(dom), dom) - self.assertEquals(desc1.group_sid, desc2.group_sid) - self.assertEquals(desc1.owner_sid, desc2.owner_sid) - self.assertEquals(desc1.sacl, desc2.sacl) - self.assertEquals(desc1.type, desc2.type) - - def test_as_sddl_invalid(self): - text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)" - dom = security.dom_sid("S-2-0-0") - desc1 = security.descriptor.from_sddl(text, dom) - self.assertRaises(TypeError, desc1.as_sddl,text) - - - def test_as_sddl_no_domainsid(self): - dom = security.dom_sid("S-2-0-0") - text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)" - desc1 = security.descriptor.from_sddl(text, dom) - desc2 = security.descriptor.from_sddl(desc1.as_sddl(), dom) - self.assertEquals(desc1.group_sid, desc2.group_sid) - self.assertEquals(desc1.owner_sid, desc2.owner_sid) - self.assertEquals(desc1.sacl, desc2.sacl) - self.assertEquals(desc1.type, desc2.type) - - def test_domsid_nodomsid_as_sddl(self): - dom = security.dom_sid("S-2-0-0") - text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)" - desc1 = security.descriptor.from_sddl(text, dom) - self.assertNotEqual(desc1.as_sddl(), desc1.as_sddl(dom)) - - def test_split(self): - dom = security.dom_sid("S-2-0-7") - self.assertEquals((security.dom_sid("S-2-0"), 7), dom.split()) - - -class DomSidTests(samba.tests.TestCase): - - def test_parse_sid(self): - sid = security.dom_sid("S-1-5-21") - self.assertEquals("S-1-5-21", str(sid)) - - def test_sid_equal(self): - sid1 = security.dom_sid("S-1-5-21") - sid2 = security.dom_sid("S-1-5-21") - self.assertEquals(sid1, sid1) - self.assertEquals(sid1, sid2) - - def test_random(self): - sid = security.random_sid() - self.assertTrue(str(sid).startswith("S-1-5-21-")) - - def test_repr(self): - sid = security.random_sid() - self.assertTrue(repr(sid).startswith("dom_sid('S-1-5-21-")) - - -class PrivilegeTests(samba.tests.TestCase): - - def test_privilege_name(self): - self.assertEquals("SeShutdownPrivilege", - security.privilege_name(security.SEC_PRIV_SHUTDOWN)) - - def test_privilege_id(self): - self.assertEquals(security.SEC_PRIV_SHUTDOWN, - security.privilege_id("SeShutdownPrivilege")) - diff --git a/source4/scripting/python/samba/tests/hostconfig.py b/source4/scripting/python/samba/tests/shares.py index 78ca6202b2..9130c36780 100644 --- a/source4/scripting/python/samba/tests/hostconfig.py +++ b/source4/scripting/python/samba/tests/shares.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. Tests for shares # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2009 @@ -16,11 +16,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # - -"""Tests for samba.hostconfig.""" - -from samba.hostconfig import SharesContainer -from samba.tests import TestCase +from samba.shares import SharesContainer +from unittest import TestCase class MockService(object): @@ -40,6 +37,9 @@ class MockLoadParm(object): def __getitem__(self, name): return MockService(self.data[name]) + def __contains__(self, name): + return name in self.data + def __len__(self): return len(self.data) diff --git a/source4/scripting/python/samba/tests/upgrade.py b/source4/scripting/python/samba/tests/upgrade.py index 16ccbd567e..17ebfa7bf5 100644 --- a/source4/scripting/python/samba/tests/upgrade.py +++ b/source4/scripting/python/samba/tests/upgrade.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 @@ -17,13 +17,11 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -"""Tests for samba.upgrade.""" - +from samba import Ldb from samba.upgrade import import_wins from samba.tests import LdbTestCase class WinsUpgradeTests(LdbTestCase): - def test_upgrade(self): winsdb = { "FOO#20": (200, ["127.0.0.1", "127.0.0.2"], 0x60) diff --git a/source4/scripting/python/samba/tests/upgradeprovision.py b/source4/scripting/python/samba/tests/upgradeprovision.py deleted file mode 100644 index b81ee8a8ab..0000000000 --- a/source4/scripting/python/samba/tests/upgradeprovision.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# 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 -# 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/>. -# - -"""Tests for samba.upgradeprovision.""" - -import os -from samba.upgradehelpers import (usn_in_range, dn_sort, - get_diff_sddls, update_secrets, - construct_existor_expr) - -from samba.tests.provision import create_dummy_secretsdb -from samba.tests import TestCaseInTempDir -from samba import Ldb -from ldb import SCOPE_SUBTREE -import samba.tests - -def dummymessage(a=None, b=None): - pass - - -class UpgradeProvisionTestCase(TestCaseInTempDir): - """Some simple tests for individual functions in the provisioning code. - """ - def test_usn_in_range(self): - range = [5, 25, 35, 55] - - vals = [3, 26, 56] - - for v in vals: - self.assertFalse(usn_in_range(v, range)) - - vals = [5, 20, 25, 35, 36] - - for v in vals: - self.assertTrue(usn_in_range(v, range)) - - def test_dn_sort(self): - # higher level comes after lower even if lexicographicaly closer - # ie dc=tata,dc=toto (2 levels), comes after dc=toto - # even if dc=toto is lexicographicaly after dc=tata, dc=toto - self.assertEquals(dn_sort("dc=tata,dc=toto", "dc=toto"), 1) - self.assertEquals(dn_sort("dc=zata", "dc=tata"), 1) - self.assertEquals(dn_sort("dc=toto,dc=tata", - "cn=foo,dc=toto,dc=tata"), -1) - self.assertEquals(dn_sort("cn=bar, dc=toto,dc=tata", - "cn=foo, dc=toto,dc=tata"), -1) - - def test_get_diff_sddl(self): - sddl = "O:SAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\ -(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)" - sddl1 = "O:SAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\ -(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)" - sddl2 = "O:BAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\ -(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)" - sddl3 = "O:SAG:BAD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\ -(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)" - sddl4 = "O:SAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;BA)\ -(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)S:AI(AU;CIIDSA;WP;;;WD)" - sddl5 = "O:SAG:DUD:AI(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA)\ -(A;CIID;RP LCLORC;;;AU)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" - - self.assertEquals(get_diff_sddls(sddl, sddl1), "") - txt = get_diff_sddls(sddl, sddl2) - self.assertEquals(txt, "\tOwner mismatch: SA (in ref) BA(in current)\n") - txt = get_diff_sddls(sddl, sddl3) - self.assertEquals(txt, "\tGroup mismatch: DU (in ref) BA(in current)\n") - txt = get_diff_sddls(sddl, sddl4) - txtmsg = "\tPart dacl is different between reference and current here\ - is the detail:\n\t\t(A;CIID;RPWPCRCCLCLORCWOWDSW;;;BA) ACE is not present in\ - the reference\n\t\t(A;CIID;RPWPCRCCLCLORCWOWDSW;;;SA) ACE is not present in\ - the current\n" - self.assertEquals(txt, txtmsg) - txt = get_diff_sddls(sddl, sddl5) - self.assertEquals(txt, "\tCurrent ACL hasn't a sacl part\n") - - def test_construct_existor_expr(self): - res = construct_existor_expr([]) - self.assertEquals(res, "") - - res = construct_existor_expr(["foo"]) - self.assertEquals(res, "(|(foo=*))") - - res = construct_existor_expr(["foo", "bar"]) - self.assertEquals(res, "(|(foo=*)(bar=*))") - - -class UpdateSecretsTests(samba.tests.TestCaseInTempDir): - - def setUp(self): - super(UpdateSecretsTests, self).setUp() - self.referencedb = create_dummy_secretsdb( - os.path.join(self.tempdir, "ref.ldb")) - - def _getEmptyDb(self): - return Ldb(os.path.join(self.tempdir, "secrets.ldb")) - - def _getCurrentFormatDb(self): - return create_dummy_secretsdb( - os.path.join(self.tempdir, "secrets.ldb")) - - def test_trivial(self): - # Test that updating an already up-to-date secretsdb works fine - self.secretsdb = self._getCurrentFormatDb() - self.assertEquals(None, - update_secrets(self.referencedb, self.secretsdb, dummymessage)) - - def test_update_modules(self): - empty_db = self._getEmptyDb() - update_secrets(self.referencedb, empty_db, dummymessage) - newmodules = empty_db.search( - expression="dn=@MODULES", base="", scope=SCOPE_SUBTREE) - refmodules = self.referencedb.search( - expression="dn=@MODULES", base="", scope=SCOPE_SUBTREE) - self.assertEquals(newmodules.msgs, refmodules.msgs) - - def tearDown(self): - for name in ["ref.ldb", "secrets.ldb"]: - path = os.path.join(self.tempdir, name) - if os.path.exists(path): - os.unlink(path) - super(UpdateSecretsTests, self).tearDown() - - diff --git a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py b/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py deleted file mode 100644 index 3a9c78e0dc..0000000000 --- a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. -# 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 -# 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/>. -# - -"""Tests for samba.upgradeprovision that need a DC.""" - -import os -import re -import shutil - -from samba import param -from samba.credentials import Credentials -from samba.auth import system_session -from samba.provision import getpolicypath -from samba.upgradehelpers import (get_paths, get_ldbs, - find_provision_key_parameters, identic_rename, - updateOEMInfo, getOEMInfo, update_gpo, - delta_update_basesamdb, - update_dns_account_password, - search_constructed_attrs_stored, - increment_calculated_keyversion_number) -from samba.tests import env_loadparm, TestCaseInTempDir -from samba.tests.provision import create_dummy_secretsdb -import ldb - - -def dummymessage(a=None, b=None): - pass - -smb_conf_path = "%s/%s/%s" % (os.environ["SELFTEST_PREFIX"], "dc", "etc/smb.conf") - -class UpgradeProvisionBasicLdbHelpersTestCase(TestCaseInTempDir): - """Some simple tests for individual functions in the provisioning code. - """ - - def test_get_ldbs(self): - paths = get_paths(param, None, smb_conf_path) - creds = Credentials() - lp = env_loadparm() - creds.guess(lp) - get_ldbs(paths, creds, system_session(), lp) - - def test_find_key_param(self): - paths = get_paths(param, None, smb_conf_path) - creds = Credentials() - lp = env_loadparm() - creds.guess(lp) - rootdn = "dc=samba,dc=example,dc=com" - ldbs = get_ldbs(paths, creds, system_session(), lp) - names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap, - paths, smb_conf_path, lp) - self.assertEquals(names.realm, "SAMBA.EXAMPLE.COM") - self.assertEquals(str(names.rootdn).lower(), rootdn.lower()) - self.assertNotEquals(names.policyid_dc, None) - self.assertNotEquals(names.ntdsguid, "") - - -class UpgradeProvisionWithLdbTestCase(TestCaseInTempDir): - - def _getEmptyDbName(self): - return os.path.join(self.tempdir, "sam.ldb") - - def setUp(self): - super(UpgradeProvisionWithLdbTestCase, self).setUp() - paths = get_paths(param, None, smb_conf_path) - self.creds = Credentials() - self.lp = env_loadparm() - self.creds.guess(self.lp) - self.paths = paths - self.ldbs = get_ldbs(paths, self.creds, system_session(), self.lp) - self.names = find_provision_key_parameters(self.ldbs.sam, - self.ldbs.secrets, self.ldbs.idmap, paths, smb_conf_path, - self.lp) - self.referencedb = create_dummy_secretsdb( - os.path.join(self.tempdir, "ref.ldb")) - - def test_search_constructed_attrs_stored(self): - hashAtt = search_constructed_attrs_stored(self.ldbs.sam, - self.names.rootdn, - ["msds-KeyVersionNumber"]) - self.assertFalse(hashAtt.has_key("msds-KeyVersionNumber")) - - def test_increment_calculated_keyversion_number(self): - dn = "CN=Administrator,CN=Users,%s" % self.names.rootdn - # We conctruct a simple hash for the user administrator - hash = {} - # And we want the version to be 140 - hash[dn.lower()] = 140 - - increment_calculated_keyversion_number(self.ldbs.sam, - self.names.rootdn, - hash) - self.assertEqual(self.ldbs.sam.get_attribute_replmetadata_version(dn, - "unicodePwd"), - 140) - # This function should not decrement the version - hash[dn.lower()] = 130 - - increment_calculated_keyversion_number(self.ldbs.sam, - self.names.rootdn, - hash) - self.assertEqual(self.ldbs.sam.get_attribute_replmetadata_version(dn, - "unicodePwd"), - 140) - - def test_identic_rename(self): - rootdn = "DC=samba,DC=example,DC=com" - - guestDN = ldb.Dn(self.ldbs.sam, "CN=Guest,CN=Users,%s" % rootdn) - identic_rename(self.ldbs.sam, guestDN) - res = self.ldbs.sam.search(expression="(name=Guest)", base=rootdn, - scope=ldb.SCOPE_SUBTREE, attrs=["dn"]) - self.assertEquals(len(res), 1) - self.assertEquals(str(res[0]["dn"]), "CN=Guest,CN=Users,%s" % rootdn) - - def test_delta_update_basesamdb(self): - dummysampath = self._getEmptyDbName() - delta_update_basesamdb(self.paths.samdb, dummysampath, - self.creds, system_session(), self.lp, - dummymessage) - - def test_update_gpo_simple(self): - dir = getpolicypath(self.paths.sysvol, self.names.dnsdomain, - self.names.policyid) - shutil.rmtree(dir) - self.assertFalse(os.path.isdir(dir)) - update_gpo(self.paths, self.ldbs.sam, self.names, self.lp, dummymessage) - self.assertTrue(os.path.isdir(dir)) - - def test_update_gpo_acl(self): - path = os.path.join(self.tempdir, "testupdategpo") - save = self.paths.sysvol - self.paths.sysvol = path - os.mkdir(path) - os.mkdir(os.path.join(path, self.names.dnsdomain)) - os.mkdir(os.path.join(os.path.join(path, self.names.dnsdomain), - "Policies")) - update_gpo(self.paths, self.ldbs.sam, self.names, self.lp, dummymessage) - shutil.rmtree(path) - self.paths.sysvol = save - - def test_getOEMInfo(self): - realm = self.lp.get("realm") - basedn = "DC=%s" % realm.replace(".", ", DC=") - oem = getOEMInfo(self.ldbs.sam, basedn) - self.assertNotEquals(oem, "") - - def test_update_dns_account(self): - update_dns_account_password(self.ldbs.sam, self.ldbs.secrets, self.names) - - def test_updateOEMInfo(self): - realm = self.lp.get("realm") - basedn = "DC=%s" % realm.replace(".", ", DC=") - oem = getOEMInfo(self.ldbs.sam, basedn) - updateOEMInfo(self.ldbs.sam, basedn) - oem2 = getOEMInfo(self.ldbs.sam, basedn) - self.assertNotEquals(str(oem), str(oem2)) - self.assertTrue(re.match(".*upgrade to.*", str(oem2))) - - def tearDown(self): - for name in ["ref.ldb", "secrets.ldb", "sam.ldb"]: - path = os.path.join(self.tempdir, name) - if os.path.exists(path): - os.unlink(path) - super(UpgradeProvisionWithLdbTestCase, self).tearDown() diff --git a/source4/scripting/python/samba/tests/xattr.py b/source4/scripting/python/samba/tests/xattr.py deleted file mode 100644 index f978ee5b2a..0000000000 --- a/source4/scripting/python/samba/tests/xattr.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python - -# Unix SMB/CIFS implementation. Tests for xattr manipulation -# Copyright (C) Matthieu Patou <mat@matws.net> 2009 -# -# 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/>. -# - -"""Tests for samba.xattr_native and samba.xattr_tdb.""" - -import samba.xattr_native, samba.xattr_tdb -from samba.dcerpc import xattr -from samba.ndr import ndr_pack -from samba.tests import TestCase, TestSkipped -import random -import os - -class XattrTests(TestCase): - - def _tmpfilename(self): - random.seed() - path = os.environ['SELFTEST_PREFIX'] - return os.path.join(path, "pytests"+str(int(100000*random.random()))) - - def _eadbpath(self): - return os.path.join(os.environ['SELFTEST_PREFIX'], "eadb.tdb") - - def test_set_xattr_native(self): - if not samba.xattr_native.is_xattr_supported(): - raise TestSkipped() - ntacl = xattr.NTACL() - ntacl.version = 1 - tempf = self._tmpfilename() - open(tempf, 'w').write("empty") - try: - samba.xattr_native.wrap_setxattr(tempf, "user.unittests", - ndr_pack(ntacl)) - except IOError: - raise TestSkipped("the filesystem where the tests are runned do not support XATTR") - os.unlink(tempf) - - def test_set_and_get_native(self): - if not samba.xattr_native.is_xattr_supported(): - raise TestSkipped() - tempf = self._tmpfilename() - reftxt = "this is a test" - open(tempf, 'w').write("empty") - try: - samba.xattr_native.wrap_setxattr(tempf, "user.unittests", reftxt) - text = samba.xattr_native.wrap_getxattr(tempf, "user.unittests") - self.assertEquals(text, reftxt) - except IOError: - raise TestSkipped("the filesystem where the tests are runned do not support XATTR") - os.unlink(tempf) - - def test_set_xattr_tdb(self): - tempf = self._tmpfilename() - eadb_path = self._eadbpath() - ntacl = xattr.NTACL() - ntacl.version = 1 - open(tempf, 'w').write("empty") - try: - samba.xattr_tdb.wrap_setxattr(eadb_path, - tempf, "user.unittests", ndr_pack(ntacl)) - finally: - os.unlink(tempf) - os.unlink(eadb_path) - - def test_set_tdb_not_open(self): - tempf = self._tmpfilename() - ntacl = xattr.NTACL() - ntacl.version = 1 - open(tempf, 'w').write("empty") - try: - self.assertRaises(IOError, samba.xattr_tdb.wrap_setxattr, - os.path.join("nonexistent", "eadb.tdb"), tempf, - "user.unittests", ndr_pack(ntacl)) - finally: - os.unlink(tempf) - - def test_set_and_get_tdb(self): - tempf = self._tmpfilename() - eadb_path = self._eadbpath() - reftxt = "this is a test" - open(tempf, 'w').write("empty") - try: - samba.xattr_tdb.wrap_setxattr(eadb_path, tempf, "user.unittests", - reftxt) - text = samba.xattr_tdb.wrap_getxattr(eadb_path, tempf, - "user.unittests") - self.assertEquals(text, reftxt) - finally: - os.unlink(tempf) - os.unlink(eadb_path) diff --git a/source4/scripting/python/samba/torture/pytorture b/source4/scripting/python/samba/torture/pytorture new file mode 100755 index 0000000000..e0123447e8 --- /dev/null +++ b/source4/scripting/python/samba/torture/pytorture @@ -0,0 +1,51 @@ +#!/usr/bin/python + +import sys +from optparse import OptionParser + +# Parse command line + +parser = OptionParser() + +parser.add_option("-b", "--binding", action="store", type="string", + dest="binding") + +parser.add_option("-d", "--domain", action="store", type="string", + dest="domain") + +parser.add_option("-u", "--username", action="store", type="string", + dest="username") + +parser.add_option("-p", "--password", action="store", type="string", + dest="password") + +(options, args) = parser.parse_args() + +if not options.binding: + parser.error('You must supply a binding string') + +if not options.username or not options.password or not options.domain: + parser.error('You must supply a domain, username and password') + +binding = options.binding +domain = options.domain +username = options.username +password = options.password + +if len(args) == 0: + parser.error('You must supply the name of a module to test') + +# Import and test + +for test in args: + + try: + module = __import__('torture_%s' % test) + except ImportError: + print 'No such module "%s"' % test + sys.exit(1) + + if not hasattr(module, 'runtests'): + print 'Module "%s" does not have a runtests function' % test + + module.runtests(binding, (domain, username, password)) diff --git a/source4/scripting/python/samba/torture/spoolss.py b/source4/scripting/python/samba/torture/spoolss.py new file mode 100644 index 0000000000..a75385e079 --- /dev/null +++ b/source4/scripting/python/samba/torture/spoolss.py @@ -0,0 +1,437 @@ +import sys, string +import dcerpc + + +def ResizeBufferCall(fn, pipe, r): + + r['buffer'] = None + r['buf_size'] = 0 + + result = fn(pipe, r) + + if result['result'] == dcerpc.WERR_INSUFFICIENT_BUFFER or \ + result['result'] == dcerpc.WERR_MORE_DATA: + r['buffer'] = result['buf_size'] * '\x00' + r['buf_size'] = result['buf_size'] + + result = fn(pipe, r) + + return result + + +def test_OpenPrinterEx(pipe, printer): + + print 'spoolss_OpenPrinterEx(%s)' % printer + + printername = '\\\\%s' % dcerpc.dcerpc_server_name(pipe) + + if printer is not None: + printername = printername + '\\%s' % printer + + r = {} + r['printername'] = printername + r['datatype'] = None + r['devmode_ctr'] = {} + r['devmode_ctr']['size'] = 0 + r['devmode_ctr']['devmode'] = None + r['access_mask'] = 0x02000000 + r['level'] = 1 + r['userlevel'] = {} + r['userlevel']['level1'] = {} + r['userlevel']['level1']['size'] = 0 + r['userlevel']['level1']['client'] = None + r['userlevel']['level1']['user'] = None + r['userlevel']['level1']['build'] = 1381 + r['userlevel']['level1']['major'] = 2 + r['userlevel']['level1']['minor'] = 0 + r['userlevel']['level1']['processor'] = 0 + + result = dcerpc.spoolss_OpenPrinterEx(pipe, r) + + return result['handle'] + + +def test_ClosePrinter(pipe, handle): + + r = {} + r['handle'] = handle + + dcerpc.spoolss_ClosePrinter(pipe, r) + + +def test_GetPrinter(pipe, handle): + + r = {} + r['handle'] = handle + + for level in [0, 1, 2, 3, 4, 5, 6, 7]: + + print 'spoolss_GetPrinter(level = %d)' % level + + r['level'] = level + r['buffer'] = None + r['buf_size'] = 0 + + result = ResizeBufferCall(dcerpc.spoolss_GetPrinter, pipe, r) + + +def test_EnumForms(pipe, handle): + + print 'spoolss_EnumForms()' + + r = {} + r['handle'] = handle + r['level'] = 1 + r['buffer'] = None + r['buf_size'] = 0 + + result = ResizeBufferCall(dcerpc.spoolss_EnumForms, pipe, r) + + forms = dcerpc.unmarshall_spoolss_FormInfo_array( + result['buffer'], r['level'], result['count']) + + for form in forms: + + r = {} + r['handle'] = handle + r['formname'] = form['info1']['formname'] + r['level'] = 1 + + result = ResizeBufferCall(dcerpc.spoolss_GetForm, pipe, r) + + +def test_EnumPorts(pipe, handle): + + print 'spoolss_EnumPorts()' + + for level in [1, 2]: + + r = {} + r['handle'] = handle + r['servername'] = None + r['level'] = level + + result = ResizeBufferCall(dcerpc.spoolss_EnumPorts, pipe, r) + + ports = dcerpc.unmarshall_spoolss_PortInfo_array( + result['buffer'], r['level'], result['count']) + + if level == 1: + port_names = map(lambda x: x['info1']['port_name'], ports) + + +def test_DeleteForm(pipe, handle, formname): + + r = {} + r['handle'] = handle + r['formname'] = formname + + dcerpc.spoolss_DeleteForm(pipe, r) + + +def test_GetForm(pipe, handle, formname): + + r = {} + r['handle'] = handle + r['formname'] = formname + r['level'] = 1 + + result = ResizeBufferCall(dcerpc.spoolss_GetForm, pipe, r) + + return result['info']['info1'] + + +def test_SetForm(pipe, handle, form): + + print 'spoolss_SetForm()' + + r = {} + r['handle'] = handle + r['level'] = 1 + r['formname'] = form['info1']['formname'] + r['info'] = form + + dcerpc.spoolss_SetForm(pipe, r) + + newform = test_GetForm(pipe, handle, r['formname']) + + if form['info1'] != newform: + print 'SetForm: mismatch: %s != %s' % \ + (r['info']['info1'], f) + sys.exit(1) + + +def test_AddForm(pipe, handle): + + print 'spoolss_AddForm()' + + formname = '__testform__' + + r = {} + r['handle'] = handle + r['level'] = 1 + r['info'] = {} + r['info']['info1'] = {} + r['info']['info1']['formname'] = formname + r['info']['info1']['flags'] = 0x0002 + r['info']['info1']['width'] = 100 + r['info']['info1']['length'] = 100 + r['info']['info1']['left'] = 0 + r['info']['info1']['top'] = 1000 + r['info']['info1']['right'] = 2000 + r['info']['info1']['bottom'] = 3000 + + try: + result = dcerpc.spoolss_AddForm(pipe, r) + except dcerpc.WERROR, arg: + if arg[0] == dcerpc.WERR_ALREADY_EXISTS: + test_DeleteForm(pipe, handle, formname) + result = dcerpc.spoolss_AddForm(pipe, r) + + f = test_GetForm(pipe, handle, formname) + + if r['info']['info1'] != f: + print 'AddForm: mismatch: %s != %s' % \ + (r['info']['info1'], f) + sys.exit(1) + + r['formname'] = formname + + test_SetForm(pipe, handle, r['info']) + + test_DeleteForm(pipe, handle, formname) + + +def test_EnumJobs(pipe, handle): + + print 'spoolss_EnumJobs()' + + r = {} + r['handle'] = handle + r['firstjob'] = 0 + r['numjobs'] = 0xffffffff + r['level'] = 1 + + result = ResizeBufferCall(dcerpc.spoolss_EnumJobs, pipe, r) + + if result['buffer'] is None: + return + + jobs = dcerpc.unmarshall_spoolss_JobInfo_array( + result['buffer'], r['level'], result['count']) + + for job in jobs: + + s = {} + s['handle'] = handle + s['job_id'] = job['info1']['job_id'] + s['level'] = 1 + + result = ResizeBufferCall(dcerpc.spoolss_GetJob, pipe, s) + + if result['info'] != job: + print 'EnumJobs: mismatch: %s != %s' % (result['info'], job) + sys.exit(1) + + + # TODO: AddJob, DeleteJob, ScheduleJob + + +def test_EnumPrinterData(pipe, handle): + + print 'test_EnumPrinterData()' + + enum_index = 0 + + while 1: + + r = {} + r['handle'] = handle + r['enum_index'] = enum_index + + r['value_offered'] = 0 + r['data_size'] = 0 + + result = dcerpc.spoolss_EnumPrinterData(pipe, r) + + r['value_offered'] = result['value_needed'] + r['data_size'] = result['data_size'] + + result = dcerpc.spoolss_EnumPrinterData(pipe, r) + + if result['result'] == dcerpc.WERR_NO_MORE_ITEMS: + break + + s = {} + s['handle'] = handle + s['value_name'] = result['value_name'] + + result2 = ResizeBufferCall(dcerpc.spoolss_GetPrinterData, pipe, s) + + if result['buffer'][:result2['buf_size']] != result2['buffer']: + print 'EnumPrinterData/GetPrinterData mismatch' + sys.exit(1) + + enum_index += 1 + + +def test_SetPrinterDataEx(pipe, handle): + + valuename = '__printerdataextest__' + data = '12345' + + r = {} + r['handle'] = handle + r['key_name'] = 'DsSpooler' + r['value_name'] = valuename + r['type'] = 3 + r['buffer'] = data + r['buf_size'] = len(data) + + result = dcerpc.spoolss_SetPrinterDataEx(pipe, r) + + +def test_EnumPrinterDataEx(pipe, handle): + + r = {} + r['handle'] = handle + r['key_name'] = 'DsSpooler' + r['buf_size'] = 0 + + result = dcerpc.spoolss_EnumPrinterDataEx(pipe, r) + + if result['result'] == dcerpc.WERR_MORE_DATA: + r['buf_size'] = result['buf_size'] + + result = dcerpc.spoolss_EnumPrinterDataEx(pipe, r) + + # TODO: test spoolss_GetPrinterDataEx() + + +def test_SetPrinterData(pipe, handle): + + print 'testing spoolss_SetPrinterData()' + + valuename = '__printerdatatest__' + data = '12345' + + r = {} + r['handle'] = handle + r['value_name'] = valuename + r['type'] = 3 # REG_BINARY + r['buffer'] = data + r['real_len'] = 5 + + dcerpc.spoolss_SetPrinterData(pipe, r) + + s = {} + s['handle'] = handle + s['value_name'] = valuename + + result = ResizeBufferCall(dcerpc.spoolss_GetPrinterData, pipe, r) + + if result['buffer'] != data: + print 'SetPrinterData: mismatch' + sys.exit(1) + + dcerpc.spoolss_DeletePrinterData(pipe, r) + + +def test_EnumPrinters(pipe): + + print 'testing spoolss_EnumPrinters()' + + printer_names = None + + r = {} + r['flags'] = 0x02 + r['server'] = None + + for level in [0, 1, 2, 4, 5]: + + print 'test_EnumPrinters(level = %d)' % level + + r['level'] = level + + result = ResizeBufferCall(dcerpc.spoolss_EnumPrinters, pipe, r) + + printers = dcerpc.unmarshall_spoolss_PrinterInfo_array( + result['buffer'], r['level'], result['count']) + + if level == 2: + for p in printers: + + # A nice check is for the specversion in the + # devicemode. This has always been observed to be + # 1025. + + if p['info2']['devmode']['specversion'] != 1025: + print 'test_EnumPrinters: specversion != 1025' + sys.exit(1) + + r['level'] = 1 + result = ResizeBufferCall(dcerpc.spoolss_EnumPrinters, pipe, r) + + for printer in dcerpc.unmarshall_spoolss_PrinterInfo_array( + result['buffer'], r['level'], result['count']): + + if string.find(printer['info1']['name'], '\\\\') == 0: + print 'Skipping remote printer %s' % printer['info1']['name'] + continue + + printername = string.split(printer['info1']['name'], ',')[0] + + handle = test_OpenPrinterEx(pipe, printername) + + test_GetPrinter(pipe, handle) + test_EnumPorts(pipe, handle) + test_EnumForms(pipe, handle) + test_AddForm(pipe, handle) + test_EnumJobs(pipe, handle) + test_EnumPrinterData(pipe, handle) + test_EnumPrinterDataEx(pipe, handle) + test_SetPrinterData(pipe, handle) +# test_SetPrinterDataEx(pipe, handle) + test_ClosePrinter(pipe, handle) + + +def test_EnumPrinterDrivers(pipe): + + print 'test spoolss_EnumPrinterDrivers()' + + for level in [1, 2, 3]: + + r = {} + r['server'] = None + r['environment'] = None + r['level'] = level + + result = ResizeBufferCall(dcerpc.spoolss_EnumPrinterDrivers, pipe, r) + + drivers = dcerpc.unmarshall_spoolss_DriverInfo_array( + result['buffer'], r['level'], result['count']) + + if level == 1: + driver_names = map(lambda x: x['info1']['driver_name'], drivers) + + +def test_PrintServer(pipe): + + handle = test_OpenPrinterEx(pipe, None) + + # EnumForms and AddForm tests return WERR_BADFID here (??) + + test_ClosePrinter(pipe, handle) + + +def runtests(binding, domain, username, password): + + print 'Testing SPOOLSS pipe' + + pipe = dcerpc.pipe_connect(binding, + dcerpc.DCERPC_SPOOLSS_UUID, dcerpc.DCERPC_SPOOLSS_VERSION, + domain, username, password) + + test_EnumPrinters(pipe) + test_EnumPrinterDrivers(pipe) + test_PrintServer(pipe) diff --git a/source4/scripting/python/samba/torture/torture_samr.py b/source4/scripting/python/samba/torture/torture_samr.py new file mode 100755 index 0000000000..15c6dc1a76 --- /dev/null +++ b/source4/scripting/python/samba/torture/torture_samr.py @@ -0,0 +1,221 @@ +#!/usr/bin/python + +import sys +import dcerpc, samr + +def test_Connect(pipe): + + handle = samr.Connect(pipe) + handle = samr.Connect2(pipe) + handle = samr.Connect3(pipe) + handle = samr.Connect4(pipe) + + # WIN2K3 only? + + try: + handle = samr.Connect5(pipe) + except dcerpc.NTSTATUS, arg: + if arg[0] != 0xc00000d2L: # NT_STATUS_NET_WRITE_FAULT + raise + + return handle + +def test_UserHandle(user_handle): + + # QuerySecurity()/SetSecurity() + + user_handle.SetSecurity(user_handle.QuerySecurity()) + + # GetUserPwInfo() + + user_handle.GetUserPwInfo() + + # GetUserInfo() + + for level in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 20, + 21, 23, 24, 25, 26]: + + try: + user_handle.QueryUserInfo(level) + user_handle.QueryUserInfo2(level) + except dcerpc.NTSTATUS, arg: + if arg[0] != 0xc0000003L: # NT_STATUS_INVALID_INFO_CLASS + raise + + # GetGroupsForUser() + + user_handle.GetGroupsForUser() + + # TestPrivateFunctionsUser() + + try: + user_handle.TestPrivateFunctionsUser() + except dcerpc.NTSTATUS, arg: + if arg[0] != 0xC0000002L: + raise + +def test_GroupHandle(group_handle): + + # QuerySecurity()/SetSecurity() + + group_handle.SetSecurity(group_handle.QuerySecurity()) + + # QueryGroupInfo() + + for level in [1, 2, 3, 4, 5]: + info = group_handle.QueryGroupInfo(level) + + # TODO: SetGroupinfo() + + # QueryGroupMember() + + group_handle.QueryGroupMember() + +def test_AliasHandle(alias_handle): + + # QuerySecurity()/SetSecurity() + + alias_handle.SetSecurity(alias_handle.QuerySecurity()) + + print alias_handle.GetMembersInAlias() + +def test_DomainHandle(name, sid, domain_handle): + + print 'testing %s (%s)' % (name, sid) + + # QuerySecurity()/SetSecurity() + + domain_handle.SetSecurity(domain_handle.QuerySecurity()) + + # LookupNames(), none mapped + + try: + domain_handle.LookupNames(['xxNONAMExx']) + except dcerpc.NTSTATUS, arg: + if arg[0] != 0xc0000073L: + raise dcerpc.NTSTATUS(arg) + + # LookupNames(), some mapped + + if name != 'Builtin': + domain_handle.LookupNames(['Administrator', 'xxNONAMExx']) + + # QueryDomainInfo()/SetDomainInfo() + + levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13] + set_ok = [1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0] + + for i in range(len(levels)): + + info = domain_handle.QueryDomainInfo(level = levels[i]) + + try: + domain_handle.SetDomainInfo(levels[i], info) + except dcerpc.NTSTATUS, arg: + if not (arg[0] == 0xc0000003L and not set_ok[i]): + raise + + # QueryDomainInfo2() + + levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13] + + for i in range(len(levels)): + domain_handle.QueryDomainInfo2(level = levels[i]) + + # EnumDomainUsers + + print 'testing users' + + users = domain_handle.EnumDomainUsers() + rids = domain_handle.LookupNames(users) + + for i in range(len(users)): + test_UserHandle(domain_handle.OpenUser(rids[0][i])) + + # QueryDisplayInfo + + for i in [1, 2, 3, 4, 5]: + domain_handle.QueryDisplayInfo(level = i) + domain_handle.QueryDisplayInfo2(level = i) + domain_handle.QueryDisplayInfo3(level = i) + + # EnumDomainGroups + + print 'testing groups' + + groups = domain_handle.EnumDomainGroups() + rids = domain_handle.LookupNames(groups) + + for i in range(len(groups)): + test_GroupHandle(domain_handle.OpenGroup(rids[0][i])) + + # EnumDomainAliases + + print 'testing aliases' + + aliases = domain_handle.EnumDomainAliases() + rids = domain_handle.LookupNames(aliases) + + for i in range(len(aliases)): + test_AliasHandle(domain_handle.OpenAlias(rids[0][i])) + + # CreateUser + # CreateUser2 + # CreateDomAlias + # RidToSid + # RemoveMemberFromForeignDomain + # CreateDomainGroup + # GetAliasMembership + + # GetBootKeyInformation() + + try: + domain_handle.GetBootKeyInformation() + except dcerpc.NTSTATUS, arg: + pass + + # TestPrivateFunctionsDomain() + + try: + domain_handle.TestPrivateFunctionsDomain() + except dcerpc.NTSTATUS, arg: + if arg[0] != 0xC0000002L: + raise + +def test_ConnectHandle(connect_handle): + + print 'testing connect handle' + + # QuerySecurity/SetSecurity + + connect_handle.SetSecurity(connect_handle.QuerySecurity()) + + # Lookup bogus domain + + try: + connect_handle.LookupDomain('xxNODOMAINxx') + except dcerpc.NTSTATUS, arg: + if arg[0] != 0xC00000DFL: # NT_STATUS_NO_SUCH_DOMAIN + raise + + # Test all domains + + for domain_name in connect_handle.EnumDomains(): + + connect_handle.GetDomPwInfo(domain_name) + sid = connect_handle.LookupDomain(domain_name) + domain_handle = connect_handle.OpenDomain(sid) + + test_DomainHandle(domain_name, sid, domain_handle) + + # TODO: Test Shutdown() function + +def runtests(binding, creds): + + print 'Testing SAMR pipe' + + pipe = dcerpc.pipe_connect(binding, + dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION), creds) + + handle = test_Connect(pipe) + test_ConnectHandle(handle) diff --git a/source4/scripting/python/samba/torture/torture_tdb.py b/source4/scripting/python/samba/torture/torture_tdb.py new file mode 100755 index 0000000000..7f97caf6cb --- /dev/null +++ b/source4/scripting/python/samba/torture/torture_tdb.py @@ -0,0 +1,90 @@ +#!/usr/bin/python + +import sys, os +import Tdb + +def fail(msg): + print 'FAILED:', msg + sys.exit(1) + +tdb_file = '/tmp/torture_tdb.tdb' + +# Create temporary tdb file + +t = Tdb.Tdb(tdb_file, flags = Tdb.CLEAR_IF_FIRST) + +# Check non-existent key throws KeyError exception + +try: + t['__none__'] +except KeyError: + pass +else: + fail('non-existent key did not throw KeyError') + +# Check storing key + +t['bar'] = '1234' +if t['bar'] != '1234': + fail('store key failed') + +# Check key exists + +if not t.has_key('bar'): + fail('has_key() failed for existing key') + +if t.has_key('__none__'): + fail('has_key() succeeded for non-existent key') + +# Delete key + +try: + del(t['__none__']) +except KeyError: + pass +else: + fail('delete of non-existent key did not throw KeyError') + +del t['bar'] +if t.has_key('bar'): + fail('delete of existing key did not delete key') + +# Clear all keys + +t.clear() +if len(t) != 0: + fail('clear failed to remove all keys') + +# Other dict functions + +t['a'] = '1' +t['ab'] = '12' +t['abc'] = '123' + +if len(t) != 3: + fail('len method produced wrong value') + +keys = t.keys() +values = t.values() +items = t.items() + +if set(keys) != set(['a', 'ab', 'abc']): + fail('keys method produced wrong values') + +if set(values) != set(['1', '12', '123']): + fail('values method produced wrong values') + +if set(items) != set([('a', '1'), ('ab', '12'), ('abc', '123')]): + fail('values method produced wrong values') + +t.close() + +# Re-open read-only + +t = Tdb.Tdb(tdb_file, open_flags = os.O_RDONLY) +t.keys() +t.close() + +# Clean up + +os.unlink(tdb_file) diff --git a/source4/scripting/python/samba/torture/winreg.py b/source4/scripting/python/samba/torture/winreg.py new file mode 100755 index 0000000000..eb60b9847e --- /dev/null +++ b/source4/scripting/python/samba/torture/winreg.py @@ -0,0 +1,165 @@ +#!/usr/bin/python + +import sys, dcerpc + +def test_OpenHKLM(pipe): + + r = {} + r['unknown'] = {} + r['unknown']['unknown0'] = 0x9038 + r['unknown']['unknown1'] = 0x0000 + r['access_required'] = 0x02000000 + + result = dcerpc.winreg_OpenHKLM(pipe, r) + + return result['handle'] + +def test_QueryInfoKey(pipe, handle): + + r = {} + r['handle'] = handle + r['class'] = {} + r['class']['name'] = None + + return dcerpc.winreg_QueryInfoKey(pipe, r) + +def test_CloseKey(pipe, handle): + + r = {} + r['handle'] = handle + + dcerpc.winreg_CloseKey(pipe, r) + +def test_FlushKey(pipe, handle): + + r = {} + r['handle'] = handle + + dcerpc.winreg_FlushKey(pipe, r) + +def test_GetVersion(pipe, handle): + + r = {} + r['handle'] = handle + + dcerpc.winreg_GetVersion(pipe, r) + +def test_GetKeySecurity(pipe, handle): + + r = {} + r['handle'] = handle + r['unknown'] = 4 + r['size'] = None + r['data'] = {} + r['data']['max_len'] = 0 + r['data']['data'] = '' + + result = dcerpc.winreg_GetKeySecurity(pipe, r) + + print result + + if result['result'] == dcerpc.WERR_INSUFFICIENT_BUFFER: + r['size'] = {} + r['size']['max_len'] = result['data']['max_len'] + r['size']['offset'] = 0 + r['size']['len'] = result['data']['max_len'] + + result = dcerpc.winreg_GetKeySecurity(pipe, r) + + print result + + sys.exit(1) + +def test_Key(pipe, handle, name, depth = 0): + + # Don't descend too far. Registries can be very deep. + + if depth > 2: + return + + try: + keyinfo = test_QueryInfoKey(pipe, handle) + except dcerpc.WERROR, arg: + if arg[0] == dcerpc.WERR_ACCESS_DENIED: + return + + test_GetVersion(pipe, handle) + + test_FlushKey(pipe, handle) + + test_GetKeySecurity(pipe, handle) + + # Enumerate values in this key + + r = {} + r['handle'] = handle + r['name_in'] = {} + r['name_in']['len'] = 0 + r['name_in']['max_len'] = (keyinfo['max_valnamelen'] + 1) * 2 + r['name_in']['buffer'] = {} + r['name_in']['buffer']['max_len'] = keyinfo['max_valnamelen'] + 1 + r['name_in']['buffer']['offset'] = 0 + r['name_in']['buffer']['len'] = 0 + r['type'] = 0 + r['value_in'] = {} + r['value_in']['max_len'] = keyinfo['max_valbufsize'] + r['value_in']['offset'] = 0 + r['value_in']['len'] = 0 + r['value_len1'] = keyinfo['max_valbufsize'] + r['value_len2'] = 0 + + for i in range(0, keyinfo['num_values']): + + r['enum_index'] = i + + dcerpc.winreg_EnumValue(pipe, r) + + # Recursively test subkeys of this key + + r = {} + r['handle'] = handle + r['key_name_len'] = 0 + r['unknown'] = 0x0414 + r['in_name'] = {} + r['in_name']['unknown'] = 0x20a + r['in_name']['key_name'] = {} + r['in_name']['key_name']['name'] = None + r['class'] = {} + r['class']['name'] = None + r['last_changed_time'] = {} + r['last_changed_time']['low'] = 0 + r['last_changed_time']['high'] = 0 + + for i in range(0, keyinfo['num_subkeys']): + + r['enum_index'] = i + + subkey = dcerpc.winreg_EnumKey(pipe, r) + + s = {} + s['handle'] = handle + s['keyname'] = {} + s['keyname']['name'] = subkey['out_name']['name'] + s['unknown'] = 0 + s['access_mask'] = 0x02000000 + + result = dcerpc.winreg_OpenKey(pipe, s) + + test_Key(pipe, result['handle'], name + '/' + s['keyname']['name'], + depth + 1) + + test_CloseKey(pipe, result['handle']) + + # Enumerate values + +def runtests(binding, domain, username, password): + + print 'Testing WINREG pipe' + + pipe = dcerpc.pipe_connect(binding, + dcerpc.DCERPC_WINREG_UUID, dcerpc.DCERPC_WINREG_VERSION, + domain, username, password) + + handle = test_OpenHKLM(pipe) + + test_Key(pipe, handle, 'HKLM') diff --git a/source4/scripting/python/samba/upgrade.py b/source4/scripting/python/samba/upgrade.py index 5bcc4294ba..81945525e6 100644 --- a/source4/scripting/python/samba/upgrade.py +++ b/source4/scripting/python/samba/upgrade.py @@ -1,32 +1,22 @@ -# backend code for upgrading from Samba3 -# Copyright Jelmer Vernooij 2005-2007 +#!/usr/bin/python # -# 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/>. +# backend code for upgrading from Samba3 +# Copyright Jelmer Vernooij 2005-2007 +# Released under the GNU GPL v3 or later # """Support code for upgrading from Samba 3 to Samba 4.""" __docformat__ = "restructuredText" +from provision import provision, FILL_DRS import grp import ldb import time import pwd - -from samba import Ldb, registry +import registry +from samba import Ldb from samba.param import LoadParm -from samba.provision import provision def import_sam_policy(samldb, policy, dn): """Import a Samba 3 policy database.""" @@ -44,7 +34,7 @@ samba3UserMustLogonToChangePassword: %d samba3BadLockoutMinutes: %d samba3DisconnectTime: %d -""" % (dn, policy.min_password_length, +""" % (dn, policy.min_password_length, policy.password_history, policy.minimum_password_age, policy.maximum_password_age, policy.lockout_duration, policy.reset_count_minutes, policy.user_must_logon_to_change_password, @@ -53,7 +43,7 @@ samba3DisconnectTime: %d def import_sam_account(samldb,acc,domaindn,domainsid): """Import a Samba 3 SAM account. - + :param samldb: Samba 4 SAM Database handle :param acc: Samba 3 account :param domaindn: Domain DN @@ -69,7 +59,7 @@ def import_sam_account(samldb,acc,domaindn,domainsid): if acc.fullname is None: acc.fullname = acc.username - + assert acc.fullname is not None assert acc.nt_username is not None @@ -88,8 +78,8 @@ def import_sam_account(samldb,acc,domaindn,domainsid): "samba3Domain": acc.domain, "samba3DirDrive": acc.dir_drive, "samba3MungedDial": acc.munged_dial, - "samba3Homedir": acc.homedir, - "samba3LogonScript": acc.logon_script, + "samba3Homedir": acc.homedir, + "samba3LogonScript": acc.logon_script, "samba3ProfilePath": acc.profile_path, "samba3Workstations": acc.workstations, "samba3KickOffTime": str(acc.kickoff_time), @@ -105,7 +95,7 @@ def import_sam_account(samldb,acc,domaindn,domainsid): def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn): """Upgrade a SAM group. - + :param samldb: SAM database. :param gid: Group GID :param sid_name_use: SID name use @@ -119,7 +109,7 @@ def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn) if nt_name in ("Domain Guests", "Domain Users", "Domain Admins"): return None - + if gid == -1: gr = grp.getgrnam(nt_name) else: @@ -131,12 +121,12 @@ def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn) unixname = gr.gr_name assert unixname is not None - + samldb.add({ "dn": "cn=%s,%s" % (nt_name, domaindn), "objectClass": ["top", "group"], "description": comment, - "cn": nt_name, + "cn": nt_name, "objectSid": sid, "unixName": unixname, "samba3SidNameUse": str(sid_name_use) @@ -170,7 +160,7 @@ def import_idmap(samdb,samba3_idmap,domaindn): def import_wins(samba4_winsdb, samba3_winsdb): """Import settings from a Samba3 WINS database. - + :param samba4_winsdb: WINS database to import to :param samba3_winsdb: WINS database to import from """ @@ -218,6 +208,73 @@ def import_wins(samba4_winsdb, samba3_winsdb): "objectClass": "winsMaxVersion", "maxVersion": str(version_id)}) +def upgrade_provision(samba3, setup_dir, message, credentials, session_info, smbconf, targetdir): + oldconf = samba3.get_conf() + + if oldconf.get("domain logons") == "True": + serverrole = "domain controller" + else: + if oldconf.get("security") == "user": + serverrole = "standalone" + else: + serverrole = "member server" + + domainname = oldconf.get("workgroup") + if domainname: + domainname = str(domainname) + realm = oldconf.get("realm") + netbiosname = oldconf.get("netbios name") + + secrets_db = samba3.get_secrets_db() + + if domainname is None: + domainname = secrets_db.domains()[0] + message("No domain specified in smb.conf file, assuming '%s'" % domainname) + + if realm is None: + realm = domainname.lower() + message("No realm specified in smb.conf file, assuming '%s'\n" % realm) + + domainguid = secrets_db.get_domain_guid(domainname) + domainsid = secrets_db.get_sid(domainname) + if domainsid is None: + message("Can't find domain secrets for '%s'; using random SID\n" % domainname) + + if netbiosname is not None: + machinepass = secrets_db.get_machine_password(netbiosname) + else: + machinepass = None + + result = provision(setup_dir=setup_dir, message=message, + samdb_fill=FILL_DRS, smbconf=smbconf, session_info=session_info, + credentials=credentials, realm=realm, + domain=domainname, domainsid=domainsid, domainguid=domainguid, + machinepass=machinepass, serverrole=serverrole, targetdir=targetdir) + + import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db()) + + # FIXME: import_registry(registry.Registry(), samba3.get_registry()) + + # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn) + + groupdb = samba3.get_groupmapping_db() + for sid in groupdb.groupsids(): + (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid) + # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn) + + # FIXME: Aliases + + passdb = samba3.get_sam_db() + for name in passdb: + user = passdb[name] + #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid) + + if hasattr(passdb, 'ldap_url'): + message("Enabling Samba3 LDAP mappings for SAM database") + + enable_samba3sam(result.samdb, passdb.ldap_url) + + def enable_samba3sam(samdb, ldapurl): """Enable Samba 3 LDAP URL database. @@ -235,7 +292,7 @@ replace: @LIST smbconf_keep = [ - "dos charset", + "dos charset", "unix charset", "display charset", "comment", @@ -332,7 +389,7 @@ def upgrade_smbconf(oldconf,mark): """Remove configuration variables not present in Samba4 :param oldconf: Old configuration structure - :param mark: Whether removed configuration variables should be + :param mark: Whether removed configuration variables should be kept in the new configuration as "samba3:<name>" """ data = oldconf.data() @@ -377,76 +434,3 @@ def import_registry(samba4_registry, samba3_regdb): key_handle.set_value(value_name, value_type, value_data) -def upgrade_provision(samba3, logger, credentials, session_info, - smbconf, targetdir): - oldconf = samba3.get_conf() - - if oldconf.get("domain logons") == "True": - serverrole = "domain controller" - else: - if oldconf.get("security") == "user": - serverrole = "standalone" - else: - serverrole = "member server" - - domainname = oldconf.get("workgroup") - realm = oldconf.get("realm") - netbiosname = oldconf.get("netbios name") - - secrets_db = samba3.get_secrets_db() - - if domainname is None: - domainname = secrets_db.domains()[0] - logger.warning("No domain specified in smb.conf file, assuming '%s'", - domainname) - - if realm is None: - if oldconf.get("domain logons") == "True": - logger.warning("No realm specified in smb.conf file and being a DC. That upgrade path doesn't work! Please add a 'realm' directive to your old smb.conf to let us know which one you want to use (generally it's the upcased DNS domainname).") - return - else: - realm = domainname.upper() - logger.warning("No realm specified in smb.conf file, assuming '%s'", - realm) - - domainguid = secrets_db.get_domain_guid(domainname) - domainsid = secrets_db.get_sid(domainname) - if domainsid is None: - logger.warning("Can't find domain secrets for '%s'; using random SID", - domainname) - - if netbiosname is not None: - machinepass = secrets_db.get_machine_password(netbiosname) - else: - machinepass = None - - result = provision(logger=logger, - session_info=session_info, credentials=credentials, - targetdir=targetdir, realm=realm, domain=domainname, - domainguid=domainguid, domainsid=domainsid, - hostname=netbiosname, machinepass=machinepass, - serverrole=serverrole) - - import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db()) - - # FIXME: import_registry(registry.Registry(), samba3.get_registry()) - - # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn) - - groupdb = samba3.get_groupmapping_db() - for sid in groupdb.groupsids(): - (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid) - # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn) - - # FIXME: Aliases - - passdb = samba3.get_sam_db() - for name in passdb: - user = passdb[name] - #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid) - - if hasattr(passdb, 'ldap_url'): - logger.info("Enabling Samba3 LDAP mappings for SAM database") - - enable_samba3sam(result.samdb, passdb.ldap_url) - diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py deleted file mode 100755 index 3a7dfb3997..0000000000 --- a/source4/scripting/python/samba/upgradehelpers.py +++ /dev/null @@ -1,953 +0,0 @@ -#!/usr/bin/env python -# -# Helpers for provision stuff -# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010 -# -# Based on provision a Samba4 server by -# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008 -# Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 -# 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/>. - -"""Helers used for upgrading between different database formats.""" - -import os -import string -import re -import shutil -import samba - -from samba import Ldb, version, ntacls -from samba.dsdb import DS_DOMAIN_FUNCTION_2000 -from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE -import ldb -from samba.provision import (ProvisionNames, provision_paths_from_lp, - getpolicypath, set_gpos_acl, create_gpo_struct, - FILL_FULL, provision, ProvisioningError, - setsysvolacl, secretsdb_self_join) -from samba.dcerpc import misc, security, xattr -from samba.dcerpc.misc import SEC_CHAN_BDC -from samba.ndr import ndr_unpack -from samba.samdb import SamDB - -# All the ldb related to registry are commented because the path for them is -# relative in the provisionPath object -# And so opening them create a file in the current directory which is not what -# we want -# I still keep them commented because I plan soon to make more cleaner -ERROR = -1 -SIMPLE = 0x00 -CHANGE = 0x01 -CHANGESD = 0x02 -GUESS = 0x04 -PROVISION = 0x08 -CHANGEALL = 0xff - -hashAttrNotCopied = set(["dn", "whenCreated", "whenChanged", "objectGUID", - "uSNCreated", "replPropertyMetaData", "uSNChanged", "parentGUID", - "objectCategory", "distinguishedName", "nTMixedDomain", - "showInAdvancedViewOnly", "instanceType", "msDS-Behavior-Version", - "nextRid", "cn", "versionNumber", "lmPwdHistory", "pwdLastSet", - "ntPwdHistory", "unicodePwd","dBCSPwd", "supplementalCredentials", - "gPCUserExtensionNames", "gPCMachineExtensionNames","maxPwdAge", "secret", - "possibleInferiors", "privilege", "sAMAccountType"]) - - -class ProvisionLDB(object): - - def __init__(self): - self.sam = None - self.secrets = None - self.idmap = None - self.privilege = None - self.hkcr = None - self.hkcu = None - self.hku = None - self.hklm = None - - def startTransactions(self): - self.sam.transaction_start() - self.secrets.transaction_start() - self.idmap.transaction_start() - self.privilege.transaction_start() -# TO BE DONE -# self.hkcr.transaction_start() -# self.hkcu.transaction_start() -# self.hku.transaction_start() -# self.hklm.transaction_start() - - def groupedRollback(self): - ok = True - try: - self.sam.transaction_cancel() - except Exception: - ok = False - - try: - self.secrets.transaction_cancel() - except Exception: - ok = False - - try: - self.idmap.transaction_cancel() - except Exception: - ok = False - - try: - self.privilege.transaction_cancel() - except Exception: - ok = False - - return ok -# TO BE DONE -# self.hkcr.transaction_cancel() -# self.hkcu.transaction_cancel() -# self.hku.transaction_cancel() -# self.hklm.transaction_cancel() - - def groupedCommit(self): - try: - self.sam.transaction_prepare_commit() - self.secrets.transaction_prepare_commit() - self.idmap.transaction_prepare_commit() - self.privilege.transaction_prepare_commit() - except Exception: - return self.groupedRollback() -# TO BE DONE -# self.hkcr.transaction_prepare_commit() -# self.hkcu.transaction_prepare_commit() -# self.hku.transaction_prepare_commit() -# self.hklm.transaction_prepare_commit() - try: - self.sam.transaction_commit() - self.secrets.transaction_commit() - self.idmap.transaction_commit() - self.privilege.transaction_commit() - except Exception: - return self.groupedRollback() - -# TO BE DONE -# self.hkcr.transaction_commit() -# self.hkcu.transaction_commit() -# self.hku.transaction_commit() -# self.hklm.transaction_commit() - return True - -def get_ldbs(paths, creds, session, lp): - """Return LDB object mapped on most important databases - - :param paths: An object holding the different importants paths for provision object - :param creds: Credential used for openning LDB files - :param session: Session to use for openning LDB files - :param lp: A loadparam object - :return: A ProvisionLDB object that contains LDB object for the different LDB files of the provision""" - - ldbs = ProvisionLDB() - - ldbs.sam = SamDB(paths.samdb, session_info=session, credentials=creds, lp=lp, options=["modules:samba_dsdb"]) - ldbs.secrets = Ldb(paths.secrets, session_info=session, credentials=creds, lp=lp) - ldbs.idmap = Ldb(paths.idmapdb, session_info=session, credentials=creds, lp=lp) - ldbs.privilege = Ldb(paths.privilege, session_info=session, credentials=creds, lp=lp) -# ldbs.hkcr = Ldb(paths.hkcr, session_info=session, credentials=creds, lp=lp) -# ldbs.hkcu = Ldb(paths.hkcu, session_info=session, credentials=creds, lp=lp) -# ldbs.hku = Ldb(paths.hku, session_info=session, credentials=creds, lp=lp) -# ldbs.hklm = Ldb(paths.hklm, session_info=session, credentials=creds, lp=lp) - - return ldbs - - -def usn_in_range(usn, range): - """Check if the usn is in one of the range provided. - To do so, the value is checked to be between the lower bound and - higher bound of a range - - :param usn: A integer value corresponding to the usn that we want to update - :param range: A list of integer representing ranges, lower bounds are in - the even indices, higher in odd indices - :return: True if the usn is in one of the range, False otherwise - """ - - idx = 0 - cont = True - ok = False - while cont: - if idx == len(range): - cont = False - continue - if usn < int(range[idx]): - if idx %2 == 1: - ok = True - cont = False - if usn == int(range[idx]): - cont = False - ok = True - idx = idx + 1 - return ok - - -def get_paths(param, targetdir=None, smbconf=None): - """Get paths to important provision objects (smb.conf, ldb files, ...) - - :param param: Param object - :param targetdir: Directory where the provision is (or will be) stored - :param smbconf: Path to the smb.conf file - :return: A list with the path of important provision objects""" - if targetdir is not None: - etcdir = os.path.join(targetdir, "etc") - if not os.path.exists(etcdir): - os.makedirs(etcdir) - smbconf = os.path.join(etcdir, "smb.conf") - if smbconf is None: - smbconf = param.default_path() - - if not os.path.exists(smbconf): - raise ProvisioningError("Unable to find smb.conf") - - lp = param.LoadParm() - lp.load(smbconf) - paths = provision_paths_from_lp(lp, lp.get("realm")) - return paths - -def update_policyids(names, samdb): - """Update policy ids that could have changed after sam update - - :param names: List of key provision parameters - :param samdb: An Ldb object conntected with the sam DB - """ - # policy guid - res = samdb.search(expression="(displayName=Default Domain Policy)", - base="CN=Policies,CN=System," + str(names.rootdn), - scope=SCOPE_ONELEVEL, attrs=["cn","displayName"]) - names.policyid = str(res[0]["cn"]).replace("{","").replace("}","") - # dc policy guid - res2 = samdb.search(expression="(displayName=Default Domain Controllers" - " Policy)", - base="CN=Policies,CN=System," + str(names.rootdn), - scope=SCOPE_ONELEVEL, attrs=["cn","displayName"]) - if len(res2) == 1: - names.policyid_dc = str(res2[0]["cn"]).replace("{","").replace("}","") - else: - names.policyid_dc = None - - -def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp): - """Get key provision parameters (realm, domain, ...) from a given provision - - :param samdb: An LDB object connected to the sam.ldb file - :param secretsdb: An LDB object connected to the secrets.ldb file - :param idmapdb: An LDB object connected to the idmap.ldb file - :param paths: A list of path to provision object - :param smbconf: Path to the smb.conf file - :param lp: A LoadParm object - :return: A list of key provision parameters - """ - names = ProvisionNames() - names.adminpass = None - - # NT domain, kerberos realm, root dn, domain dn, domain dns name - names.domain = string.upper(lp.get("workgroup")) - names.realm = lp.get("realm") - basedn = "DC=" + names.realm.replace(".",",DC=") - names.dnsdomain = names.realm.lower() - names.realm = string.upper(names.realm) - # netbiosname - # Get the netbiosname first (could be obtained from smb.conf in theory) - res = secretsdb.search(expression="(flatname=%s)" % - names.domain,base="CN=Primary Domains", - scope=SCOPE_SUBTREE, attrs=["sAMAccountName"]) - names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","") - - names.smbconf = smbconf - - # That's a bit simplistic but it's ok as long as we have only 3 - # partitions - current = samdb.search(expression="(objectClass=*)", - base="", scope=SCOPE_BASE, - attrs=["defaultNamingContext", "schemaNamingContext", - "configurationNamingContext","rootDomainNamingContext"]) - - names.configdn = current[0]["configurationNamingContext"] - configdn = str(names.configdn) - names.schemadn = current[0]["schemaNamingContext"] - if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb, - current[0]["defaultNamingContext"][0]))): - raise ProvisioningError(("basedn in %s (%s) and from %s (%s)" - "is not the same ..." % (paths.samdb, - str(current[0]["defaultNamingContext"][0]), - paths.smbconf, basedn))) - - names.domaindn=current[0]["defaultNamingContext"] - names.rootdn=current[0]["rootDomainNamingContext"] - # default site name - res3 = samdb.search(expression="(objectClass=*)", - base="CN=Sites," + configdn, scope=SCOPE_ONELEVEL, attrs=["cn"]) - names.sitename = str(res3[0]["cn"]) - - # dns hostname and server dn - res4 = samdb.search(expression="(CN=%s)" % names.netbiosname, - base="OU=Domain Controllers,%s" % basedn, - scope=SCOPE_ONELEVEL, attrs=["dNSHostName"]) - names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"") - - server_res = samdb.search(expression="serverReference=%s" % res4[0].dn, - attrs=[], base=configdn) - names.serverdn = server_res[0].dn - - # invocation id/objectguid - res5 = samdb.search(expression="(objectClass=*)", - base="CN=NTDS Settings,%s" % str(names.serverdn), scope=SCOPE_BASE, - attrs=["invocationID", "objectGUID"]) - names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0])) - names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0])) - - # domain guid/sid - res6 = samdb.search(expression="(objectClass=*)", base=basedn, - scope=SCOPE_BASE, attrs=["objectGUID", - "objectSid","msDS-Behavior-Version" ]) - names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0])) - names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0]) - if res6[0].get("msDS-Behavior-Version") is None or \ - int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000: - names.domainlevel = DS_DOMAIN_FUNCTION_2000 - else: - names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0]) - - # policy guid - res7 = samdb.search(expression="(displayName=Default Domain Policy)", - base="CN=Policies,CN=System," + basedn, - scope=SCOPE_ONELEVEL, attrs=["cn","displayName"]) - names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","") - # dc policy guid - res8 = samdb.search(expression="(displayName=Default Domain Controllers" - " Policy)", - base="CN=Policies,CN=System," + basedn, - scope=SCOPE_ONELEVEL, attrs=["cn","displayName"]) - if len(res8) == 1: - names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","") - else: - names.policyid_dc = None - res9 = idmapdb.search(expression="(cn=%s)" % - (security.SID_BUILTIN_ADMINISTRATORS), - attrs=["xidNumber"]) - if len(res9) == 1: - names.wheel_gid = res9[0]["xidNumber"] - else: - raise ProvisioningError("Unable to find uid/gid for Domain Admins rid") - return names - - -def newprovision(names, creds, session, smbconf, provdir, logger): - """Create a new provision. - - This provision will be the reference for knowing what has changed in the - since the latest upgrade in the current provision - - :param names: List of provision parameters - :param creds: Credentials for the authentification - :param session: Session object - :param smbconf: Path to the smb.conf file - :param provdir: Directory where the provision will be stored - :param logger: A Logger - """ - if os.path.isdir(provdir): - shutil.rmtree(provdir) - os.mkdir(provdir) - logger.info("Provision stored in %s", provdir) - provision(logger, session, creds, smbconf=smbconf, - targetdir=provdir, samdb_fill=FILL_FULL, realm=names.realm, - domain=names.domain, domainguid=names.domainguid, - domainsid=str(names.domainsid), ntdsguid=names.ntdsguid, - policyguid=names.policyid, policyguid_dc=names.policyid_dc, - hostname=names.netbiosname.lower(), hostip=None, hostip6=None, - invocationid=names.invocation, adminpass=names.adminpass, - krbtgtpass=None, machinepass=None, dnspass=None, root=None, - nobody=None, wheel=None, users=None, - serverrole="domain controller", ldap_backend_extra_port=None, - backend_type=None, ldapadminpass=None, ol_mmr_urls=None, - slapd_path=None, setup_ds_path=None, nosync=None, - dom_for_fun_level=names.domainlevel, - ldap_dryrun_mode=None, useeadb=True) - - -def dn_sort(x, y): - """Sorts two DNs in the lexicographical order it and put higher level DN - before. - - So given the dns cn=bar,cn=foo and cn=foo the later will be return as - smaller - - :param x: First object to compare - :param y: Second object to compare - """ - p = re.compile(r'(?<!\\), ?') - tab1 = p.split(str(x)) - tab2 = p.split(str(y)) - minimum = min(len(tab1), len(tab2)) - len1 = len(tab1)-1 - len2 = len(tab2)-1 - # Note: python range go up to upper limit but do not include it - for i in range(0, minimum): - ret = cmp(tab1[len1-i], tab2[len2-i]) - if ret != 0: - return ret - else: - if i == minimum-1: - assert len1!=len2,"PB PB PB" + " ".join(tab1)+" / " + " ".join(tab2) - if len1 > len2: - return 1 - else: - return -1 - return ret - - -def identic_rename(ldbobj, dn): - """Perform a back and forth rename to trigger renaming on attribute that - can't be directly modified. - - :param lbdobj: An Ldb Object - :param dn: DN of the object to manipulate - """ - (before, after) = str(dn).split('=', 1) - # we need to use relax to avoid the subtree_rename constraints - ldbobj.rename(dn, ldb.Dn(ldbobj, "%s=foo%s" % (before, after)), ["relax:0"]) - ldbobj.rename(ldb.Dn(ldbobj, "%s=foo%s" % (before, after)), dn, ["relax:0"]) - - -def chunck_acl(acl): - """Return separate ACE of an ACL - - :param acl: A string representing the ACL - :return: A hash with different parts - """ - - p = re.compile(r'(\w+)?(\(.*?\))') - tab = p.findall(acl) - - hash = {} - hash["aces"] = [] - for e in tab: - if len(e[0]) > 0: - hash["flags"] = e[0] - hash["aces"].append(e[1]) - - return hash - - -def chunck_sddl(sddl): - """ Return separate parts of the SDDL (owner, group, ...) - - :param sddl: An string containing the SDDL to chunk - :return: A hash with the different chunk - """ - - p = re.compile(r'([OGDS]:)(.*?)(?=(?:[GDS]:|$))') - tab = p.findall(sddl) - - hash = {} - for e in tab: - if e[0] == "O:": - hash["owner"] = e[1] - if e[0] == "G:": - hash["group"] = e[1] - if e[0] == "D:": - hash["dacl"] = e[1] - if e[0] == "S:": - hash["sacl"] = e[1] - - return hash - - -def get_diff_sddls(refsddl, cursddl): - """Get the difference between 2 sddl - - This function split the textual representation of ACL into smaller - chunck in order to not to report a simple permutation as a difference - - :param refsddl: First sddl to compare - :param cursddl: Second sddl to compare - :return: A string that explain difference between sddls - """ - - txt = "" - hash_new = chunck_sddl(cursddl) - hash_ref = chunck_sddl(refsddl) - - if hash_new["owner"] != hash_ref["owner"]: - txt = "\tOwner mismatch: %s (in ref) %s" \ - "(in current)\n" % (hash_ref["owner"], hash_new["owner"]) - - if hash_new["group"] != hash_ref["group"]: - txt = "%s\tGroup mismatch: %s (in ref) %s" \ - "(in current)\n" % (txt, hash_ref["group"], hash_new["group"]) - - for part in ["dacl", "sacl"]: - if hash_new.has_key(part) and hash_ref.has_key(part): - - # both are present, check if they contain the same ACE - h_new = set() - h_ref = set() - c_new = chunck_acl(hash_new[part]) - c_ref = chunck_acl(hash_ref[part]) - - for elem in c_new["aces"]: - h_new.add(elem) - - for elem in c_ref["aces"]: - h_ref.add(elem) - - for k in set(h_ref): - if k in h_new: - h_new.remove(k) - h_ref.remove(k) - - if len(h_new) + len(h_ref) > 0: - txt = "%s\tPart %s is different between reference" \ - " and current here is the detail:\n" % (txt, part) - - for item in h_new: - txt = "%s\t\t%s ACE is not present in the" \ - " reference\n" % (txt, item) - - for item in h_ref: - txt = "%s\t\t%s ACE is not present in the" \ - " current\n" % (txt, item) - - elif hash_new.has_key(part) and not hash_ref.has_key(part): - txt = "%s\tReference ACL hasn't a %s part\n" % (txt, part) - elif not hash_new.has_key(part) and hash_ref.has_key(part): - txt = "%s\tCurrent ACL hasn't a %s part\n" % (txt, part) - - return txt - - -def update_secrets(newsecrets_ldb, secrets_ldb, messagefunc): - """Update secrets.ldb - - :param newsecrets_ldb: An LDB object that is connected to the secrets.ldb - of the reference provision - :param secrets_ldb: An LDB object that is connected to the secrets.ldb - of the updated provision - """ - - messagefunc(SIMPLE, "update secrets.ldb") - reference = newsecrets_ldb.search(expression="dn=@MODULES", base="", - scope=SCOPE_SUBTREE) - current = secrets_ldb.search(expression="dn=@MODULES", base="", - scope=SCOPE_SUBTREE) - assert reference, "Reference modules list can not be empty" - if len(current) == 0: - # No modules present - delta = secrets_ldb.msg_diff(ldb.Message(), reference[0]) - delta.dn = reference[0].dn - secrets_ldb.add(reference[0]) - else: - delta = secrets_ldb.msg_diff(current[0], reference[0]) - delta.dn = current[0].dn - secrets_ldb.modify(delta) - - reference = newsecrets_ldb.search(expression="objectClass=top", base="", - scope=SCOPE_SUBTREE, attrs=["dn"]) - current = secrets_ldb.search(expression="objectClass=top", base="", - scope=SCOPE_SUBTREE, attrs=["dn"]) - hash_new = {} - hash = {} - listMissing = [] - listPresent = [] - - empty = ldb.Message() - for i in range(0, len(reference)): - hash_new[str(reference[i]["dn"]).lower()] = reference[i]["dn"] - - # Create a hash for speeding the search of existing object in the - # current provision - for i in range(0, len(current)): - hash[str(current[i]["dn"]).lower()] = current[i]["dn"] - - for k in hash_new.keys(): - if not hash.has_key(k): - listMissing.append(hash_new[k]) - else: - listPresent.append(hash_new[k]) - - for entry in listMissing: - reference = newsecrets_ldb.search(expression="dn=%s" % entry, - base="", scope=SCOPE_SUBTREE) - current = secrets_ldb.search(expression="dn=%s" % entry, - base="", scope=SCOPE_SUBTREE) - delta = secrets_ldb.msg_diff(empty, reference[0]) - for att in hashAttrNotCopied: - delta.remove(att) - messagefunc(CHANGE, "Entry %s is missing from secrets.ldb" % - reference[0].dn) - for att in delta: - messagefunc(CHANGE, " Adding attribute %s" % att) - delta.dn = reference[0].dn - secrets_ldb.add(delta) - - for entry in listPresent: - reference = newsecrets_ldb.search(expression="dn=%s" % entry, - base="", scope=SCOPE_SUBTREE) - current = secrets_ldb.search(expression="dn=%s" % entry, base="", - scope=SCOPE_SUBTREE) - delta = secrets_ldb.msg_diff(current[0], reference[0]) - for att in hashAttrNotCopied: - delta.remove(att) - for att in delta: - if att == "name": - messagefunc(CHANGE, "Found attribute name on %s," - " must rename the DN" % (current[0].dn)) - identic_rename(secrets_ldb, reference[0].dn) - else: - delta.remove(att) - - for entry in listPresent: - reference = newsecrets_ldb.search(expression="dn=%s" % entry, base="", - scope=SCOPE_SUBTREE) - current = secrets_ldb.search(expression="dn=%s" % entry, base="", - scope=SCOPE_SUBTREE) - delta = secrets_ldb.msg_diff(current[0], reference[0]) - for att in hashAttrNotCopied: - delta.remove(att) - for att in delta: - if att == "msDS-KeyVersionNumber": - delta.remove(att) - if att != "dn": - messagefunc(CHANGE, - "Adding/Changing attribute %s to %s" % - (att, current[0].dn)) - - delta.dn = current[0].dn - secrets_ldb.modify(delta) - - res2 = secrets_ldb.search(expression="(samaccountname=dns)", - scope=SCOPE_SUBTREE, attrs=["dn"]) - - if (len(res2) == 1): - messagefunc(SIMPLE, "Remove old dns account") - secrets_ldb.delete(res2[0]["dn"]) - - -def getOEMInfo(samdb, rootdn): - """Return OEM Information on the top level Samba4 use to store version - info in this field - - :param samdb: An LDB object connect to sam.ldb - :param rootdn: Root DN of the domain - :return: The content of the field oEMInformation (if any) - """ - res = samdb.search(expression="(objectClass=*)", base=str(rootdn), - scope=SCOPE_BASE, attrs=["dn", "oEMInformation"]) - if len(res) > 0: - info = res[0]["oEMInformation"] - return info - else: - return "" - - -def updateOEMInfo(samdb, rootdn): - """Update the OEMinfo field to add information about upgrade - - :param samdb: an LDB object connected to the sam DB - :param rootdn: The string representation of the root DN of - the provision (ie. DC=...,DC=...) - """ - res = samdb.search(expression="(objectClass=*)", base=rootdn, - scope=SCOPE_BASE, attrs=["dn", "oEMInformation"]) - if len(res) > 0: - info = res[0]["oEMInformation"] - info = "%s, upgrade to %s" % (info, version) - delta = ldb.Message() - delta.dn = ldb.Dn(samdb, str(res[0]["dn"])) - delta["oEMInformation"] = ldb.MessageElement(info, ldb.FLAG_MOD_REPLACE, - "oEMInformation" ) - samdb.modify(delta) - -def update_gpo(paths, samdb, names, lp, message, force=0): - """Create missing GPO file object if needed - - Set ACL correctly also. - Check ACLs for sysvol/netlogon dirs also - """ - resetacls = False - try: - ntacls.checkset_backend(lp, None, None) - eadbname = lp.get("posix:eadb") - if eadbname is not None and eadbname != "": - try: - attribute = samba.xattr_tdb.wrap_getxattr(eadbname, - paths.sysvol, xattr.XATTR_NTACL_NAME) - except Exception: - attribute = samba.xattr_native.wrap_getxattr(paths.sysvol, - xattr.XATTR_NTACL_NAME) - else: - attribute = samba.xattr_native.wrap_getxattr(paths.sysvol, - xattr.XATTR_NTACL_NAME) - except Exception: - resetacls = True - - if force: - resetacls = True - - dir = getpolicypath(paths.sysvol, names.dnsdomain, names.policyid) - if not os.path.isdir(dir): - create_gpo_struct(dir) - - if names.policyid_dc is None: - raise ProvisioningError("Policy ID for Domain controller is missing") - dir = getpolicypath(paths.sysvol, names.dnsdomain, names.policyid_dc) - if not os.path.isdir(dir): - create_gpo_struct(dir) - # We always reinforce acls on GPO folder because they have to be in sync - # with the one in DS - try: - set_gpos_acl(paths.sysvol, names.dnsdomain, names.domainsid, - names.domaindn, samdb, lp) - except TypeError, e: - message(ERROR, "Unable to set ACLs on policies related objects," - " if not using posix:eadb, you must be root to do it") - - if resetacls: - try: - setsysvolacl(samdb, paths.netlogon, paths.sysvol, names.wheel_gid, - names.domainsid, names.dnsdomain, names.domaindn, lp) - except TypeError, e: - message(ERROR, "Unable to set ACLs on sysvol share, if not using" - "posix:eadb, you must be root to do it") - -def increment_calculated_keyversion_number(samdb, rootdn, hashDns): - """For a given hash associating dn and a number, this function will - update the replPropertyMetaData of each dn in the hash, so that the - calculated value of the msDs-KeyVersionNumber is equal or superior to the - one associated to the given dn. - - :param samdb: An SamDB object pointing to the sam - :param rootdn: The base DN where we want to start - :param hashDns: A hash with dn as key and number representing the - minimum value of msDs-KeyVersionNumber that we want to - have - """ - entry = samdb.search(expression='(objectClass=user)', - base=ldb.Dn(samdb,str(rootdn)), - scope=SCOPE_SUBTREE, attrs=["msDs-KeyVersionNumber"], - controls=["search_options:1:2"]) - done = 0 - hashDone = {} - if len(entry) == 0: - raise ProvisioningError("Unable to find msDs-KeyVersionNumber") - else: - for e in entry: - if hashDns.has_key(str(e.dn).lower()): - val = e.get("msDs-KeyVersionNumber") - if not val: - val = "0" - version = int(str(hashDns[str(e.dn).lower()])) - if int(str(val)) < version: - done = done + 1 - samdb.set_attribute_replmetadata_version(str(e.dn), - "unicodePwd", - version, True) -def delta_update_basesamdb(refsampath, sampath, creds, session, lp, message): - """Update the provision container db: sam.ldb - This function is aimed for alpha9 and newer; - - :param refsampath: Path to the samdb in the reference provision - :param sampath: Path to the samdb in the upgraded provision - :param creds: Credential used for openning LDB files - :param session: Session to use for openning LDB files - :param lp: A loadparam object - :return: A msg_diff object with the difference between the @ATTRIBUTES - of the current provision and the reference provision - """ - - message(SIMPLE, - "Update base samdb by searching difference with reference one") - refsam = Ldb(refsampath, session_info=session, credentials=creds, - lp=lp, options=["modules:"]) - sam = Ldb(sampath, session_info=session, credentials=creds, lp=lp, - options=["modules:"]) - - empty = ldb.Message() - deltaattr = None - reference = refsam.search(expression="") - - for refentry in reference: - entry = sam.search(expression="dn=%s" % refentry["dn"], - scope=SCOPE_SUBTREE) - if not len(entry): - delta = sam.msg_diff(empty, refentry) - message(CHANGE, "Adding %s to sam db" % str(refentry.dn)) - if str(refentry.dn) == "@PROVISION" and\ - delta.get(samba.provision.LAST_PROVISION_USN_ATTRIBUTE): - delta.remove(samba.provision.LAST_PROVISION_USN_ATTRIBUTE) - delta.dn = refentry.dn - sam.add(delta) - else: - delta = sam.msg_diff(entry[0], refentry) - if str(refentry.dn) == "@ATTRIBUTES": - deltaattr = sam.msg_diff(refentry, entry[0]) - if str(refentry.dn) == "@PROVISION" and\ - delta.get(samba.provision.LAST_PROVISION_USN_ATTRIBUTE): - delta.remove(samba.provision.LAST_PROVISION_USN_ATTRIBUTE) - if len(delta.items()) > 1: - delta.dn = refentry.dn - sam.modify(delta) - - return deltaattr - - -def construct_existor_expr(attrs): - """Construct a exists or LDAP search expression. - - :param attrs: List of attribute on which we want to create the search - expression. - :return: A string representing the expression, if attrs is empty an - empty string is returned - """ - expr = "" - if len(attrs) > 0: - expr = "(|" - for att in attrs: - expr = "%s(%s=*)"%(expr,att) - expr = "%s)"%expr - return expr - -def update_machine_account_password(samdb, secrets_ldb, names): - """Update (change) the password of the current DC both in the SAM db and in - secret one - - :param samdb: An LDB object related to the sam.ldb file of a given provision - :param secrets_ldb: An LDB object related to the secrets.ldb file of a given - provision - :param names: List of key provision parameters""" - - expression = "samAccountName=%s$" % names.netbiosname - secrets_msg = secrets_ldb.search(expression=expression, - attrs=["secureChannelType"]) - if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC: - res = samdb.search(expression=expression, attrs=[]) - assert(len(res) == 1) - - msg = ldb.Message(res[0].dn) - machinepass = samba.generate_random_password(128, 255) - mputf16 = machinepass.encode('utf-16-le') - msg["clearTextPassword"] = ldb.MessageElement(mputf16, - ldb.FLAG_MOD_REPLACE, - "clearTextPassword") - samdb.modify(msg) - - res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname), - attrs=["msDs-keyVersionNumber"]) - assert(len(res) == 1) - kvno = int(str(res[0]["msDs-keyVersionNumber"])) - secChanType = int(secrets_msg[0]["secureChannelType"][0]) - - secretsdb_self_join(secrets_ldb, domain=names.domain, - realm=names.realm, - domainsid=names.domainsid, - dnsdomain=names.dnsdomain, - netbiosname=names.netbiosname, - machinepass=machinepass, - key_version_number=kvno, - secure_channel_type=secChanType) - else: - raise ProvisioningError("Unable to find a Secure Channel" - "of type SEC_CHAN_BDC") - -def update_dns_account_password(samdb, secrets_ldb, names): - """Update (change) the password of the dns both in the SAM db and in - secret one - - :param samdb: An LDB object related to the sam.ldb file of a given provision - :param secrets_ldb: An LDB object related to the secrets.ldb file of a given - provision - :param names: List of key provision parameters""" - - expression = "samAccountName=dns-%s" % names.netbiosname - secrets_msg = secrets_ldb.search(expression=expression) - if len(secrets_msg) == 1: - res = samdb.search(expression=expression, attrs=[]) - assert(len(res) == 1) - - msg = ldb.Message(res[0].dn) - machinepass = samba.generate_random_password(128, 255) - mputf16 = machinepass.encode('utf-16-le') - msg["clearTextPassword"] = ldb.MessageElement(mputf16, - ldb.FLAG_MOD_REPLACE, - "clearTextPassword") - - samdb.modify(msg) - - res = samdb.search(expression=expression, - attrs=["msDs-keyVersionNumber"]) - assert(len(res) == 1) - kvno = str(res[0]["msDs-keyVersionNumber"]) - - msg = ldb.Message(secrets_msg[0].dn) - msg["secret"] = ldb.MessageElement(machinepass, - ldb.FLAG_MOD_REPLACE, - "secret") - msg["msDS-KeyVersionNumber"] = ldb.MessageElement(kvno, - ldb.FLAG_MOD_REPLACE, - "msDS-KeyVersionNumber") - - secrets_ldb.modify(msg) - else: - raise ProvisioningError("Unable to find an object" - " with %s" % expression ) - -def search_constructed_attrs_stored(samdb, rootdn, attrs): - """Search a given sam DB for calculated attributes that are - still stored in the db. - - :param samdb: An LDB object pointing to the sam - :param rootdn: The base DN where the search should start - :param attrs: A list of attributes to be searched - :return: A hash with attributes as key and an array of - array. Each array contains the dn and the associated - values for this attribute as they are stored in the - sam.""" - - hashAtt = {} - expr = construct_existor_expr(attrs) - if expr == "": - return hashAtt - entry = samdb.search(expression=expr, base=ldb.Dn(samdb, str(rootdn)), - scope=SCOPE_SUBTREE, attrs=attrs, - controls=["search_options:1:2","bypassoperational:0"]) - if len(entry) == 0: - # Nothing anymore - return hashAtt - - for ent in entry: - for att in attrs: - if ent.get(att): - if hashAtt.has_key(att): - hashAtt[att][str(ent.dn).lower()] = str(ent[att]) - else: - hashAtt[att] = {} - hashAtt[att][str(ent.dn).lower()] = str(ent[att]) - - return hashAtt - -def int64range2str(value): - """Display the int64 range stored in value as xxx-yyy - - :param value: The int64 range - :return: A string of the representation of the range - """ - - lvalue = long(value) - str = "%d-%d" % (lvalue&0xFFFFFFFF, lvalue>>32) - return str diff --git a/source4/scripting/python/samba/web_server/__init__.py b/source4/scripting/python/samba/web_server/__init__.py deleted file mode 100644 index da528f42d1..0000000000 --- a/source4/scripting/python/samba/web_server/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Unix SMB/CIFS implementation. -# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008 -# -# Implementation of SWAT that uses WSGI -# -# 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/>. -# - - - -def render_placeholder(environ, start_response): - status = '200 OK' - response_headers = [('Content-type','text/html')] - start_response(status, response_headers) - - yield "<!doctype html>\n" - yield "<html>\n" - yield " <title>The Samba web service</title>\n" - yield "</html>\n" - - yield "<body>\n" - yield "<p>Welcome to this Samba web server.</p>\n" - yield "<p>This page is a simple placeholder. You probably want to install " - yield "SWAT. More information can be found " - yield "<a href='http://wiki.samba.org/index.php/SWAT'>on the wiki</a>.</p>" - yield "</p>\n" - yield "</body>\n" - yield "</html>\n" - - -__call__ = render_placeholder - - -if __name__ == '__main__': - from wsgiref import simple_server - httpd = simple_server.make_server('localhost', 8090, __call__) - print "Serving HTTP on port 8090..." - httpd.serve_forever() diff --git a/source4/scripting/python/samba_external/README b/source4/scripting/python/samba_external/README deleted file mode 100644 index d6a4dec7b1..0000000000 --- a/source4/scripting/python/samba_external/README +++ /dev/null @@ -1,4 +0,0 @@ -This directory is for external python libraries that may not be -installed on the local system. We always should try to use the -system version of the library if possible, then use the Samba -supplied copy if the system copy is unavailable diff --git a/source4/scripting/python/uuidmodule.c b/source4/scripting/python/uuidmodule.c index 3bfe0162ca..98ef9adaa9 100644 --- a/source4/scripting/python/uuidmodule.c +++ b/source4/scripting/python/uuidmodule.c @@ -17,8 +17,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <Python.h> #include "includes.h" +#include <Python.h> #include "librpc/ndr/libndr.h" static PyObject *uuid_random(PyObject *self, PyObject *args) diff --git a/source4/scripting/python/wscript_build b/source4/scripting/python/wscript_build deleted file mode 100644 index 540f3b7bb7..0000000000 --- a/source4/scripting/python/wscript_build +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python - -bld.SAMBA_LIBRARY('samba_python', - source=[], - deps='LIBPYTHON pytalloc-util pyrpc_util', - grouping_library=True, - private_library=True, - pyext=True) - -bld.SAMBA_SUBSYSTEM('LIBPYTHON', - source='modules.c', - public_deps='', - init_function_sentinal='{NULL,NULL}', - deps='talloc', - pyext=True, - ) - - -bld.SAMBA_PYTHON('python_uuid', - source='uuidmodule.c', - deps='ndr', - realname='uuid.so', - enabled = float(bld.env.PYTHON_VERSION) <= 2.4 - ) - - -bld.SAMBA_PYTHON('python_glue', - source='pyglue.c', - deps='pyparam_util samba-util netif pytalloc-util', - realname='samba/_glue.so' - ) - - -# install out various python scripts for use by make test -bld.SAMBA_SCRIPT('samba_python', - pattern='samba/**/*.py', - installdir='python') - -bld.INSTALL_WILDCARD('${PYTHONARCHDIR}', 'samba/**/*.py', flat=False) |