From da2e34a134545113c19056e4b20b457676ac0215 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sat, 30 Apr 2011 01:20:32 +0400 Subject: provision: reorganize attributes so that we don't attribute with DN syntax that depends on non present object Autobuild-User: Matthieu Patou Autobuild-Date: Sat Apr 30 14:51:16 CEST 2011 on sn-devel-104 --- source4/scripting/python/samba/provision/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index ff9b00122d..bdca992412 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -1166,6 +1166,11 @@ def setup_samdb(path, session_info, provision_backend, lp, names, "DESCRIPTOR": descr, }) + # Now register this container in the root of the forest + msg = ldb.Message(ldb.Dn(samdb, names.domaindn)) + msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD, + "subRefs") + # 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"]) -- cgit From 7f36f3effa200709df021789786f6e7555aef33c Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 21 Mar 2011 18:03:50 +0300 Subject: s4-samba-tool: allow specification of targetdir when joining as (RO)DC Autobuild-User: Matthieu Patou Autobuild-Date: Thu May 5 02:04:13 CEST 2011 on sn-devel-104 --- source4/scripting/python/samba/netcmd/join.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/join.py b/source4/scripting/python/samba/netcmd/join.py index 507253ab81..70b750191a 100644 --- a/source4/scripting/python/samba/netcmd/join.py +++ b/source4/scripting/python/samba/netcmd/join.py @@ -22,7 +22,7 @@ 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.dcerpc.misc import SEC_CHAN_WKSTA from samba.join import join_RODC, join_DC class cmd_join(Command): @@ -39,12 +39,13 @@ class cmd_join(Command): takes_options = [ Option("--server", help="DC to join", type=str), Option("--site", help="site to join", type=str), + Option("--targetdir", help="where to store provision", type=str), ] takes_args = ["domain", "role?"] def run(self, domain, role=None, sambaopts=None, credopts=None, - versionopts=None, server=None, site=None): + versionopts=None, server=None, site=None, targetdir=None): lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp) net = Net(creds, lp, server=credopts.ipaddress) @@ -61,11 +62,11 @@ class cmd_join(Command): 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) + site=site, netbios_name=netbios_name, targetdir=targetdir) return elif role == "RODC": join_RODC(server=server, creds=creds, lp=lp, domain=domain, - site=site, netbios_name=netbios_name) + site=site, netbios_name=netbios_name, targetdir=targetdir) return else: raise CommandError("Invalid role %s (possible values: MEMBER, BDC, RODC)" % role) -- cgit From 22cb631b4fd0647b70fbaaafaffda8712a84a999 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 2 May 2011 15:57:19 +1000 Subject: s4-interfaces Rename interfaces code so not to conflict with source3/ The iface_count, iface_n_bcast, and load_interfaces functions conflicted with functions of the same name in source3, so the source4 functions were renamed. Hopefully we can actually wrap one around the other in future. Andrew Bartlett --- source4/scripting/python/pyglue.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/pyglue.c b/source4/scripting/python/pyglue.c index f89785f971..03460db8ff 100644 --- a/source4/scripting/python/pyglue.c +++ b/source4/scripting/python/pyglue.c @@ -149,22 +149,22 @@ static PyObject *py_interface_ips(PyObject *self, PyObject *args) return NULL; } - load_interfaces(tmp_ctx, lpcfg_interfaces(lp_ctx), &ifaces); + load_interface_list(tmp_ctx, lpcfg_interfaces(lp_ctx), &ifaces); - count = iface_count(ifaces); + count = iface_list_count(ifaces); /* first count how many are not loopback addresses */ for (ifcount = i = 0; i Date: Fri, 13 May 2011 16:04:07 +0400 Subject: s4-python: raise an error if unable to bind remote ldap while joining --- source4/scripting/python/samba/join.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/join.py b/source4/scripting/python/samba/join.py index c0aee71407..b586e2cd5b 100644 --- a/source4/scripting/python/samba/join.py +++ b/source4/scripting/python/samba/join.py @@ -36,6 +36,11 @@ import talloc # this makes debugging easier talloc.enable_null_tracking() +class DCJoinException(Exception): + + def __init__(self, msg): + super(DCJoinException, self).__init__("Can't join, error: %s" % msg) + class dc_join(object): '''perform a DC join''' @@ -62,6 +67,12 @@ class dc_join(object): session_info=system_session(), credentials=ctx.creds, lp=ctx.lp) + try: + ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL, attrs=["dn"]) + except ldb.LdbError, (enum, estr): + raise DCJoinException(estr) + + ctx.myname = netbios_name ctx.samname = "%s$" % ctx.myname ctx.base_dn = str(ctx.samdb.get_default_basedn()) -- cgit From 2a5ff1513fe07894e0f9f8ee3ac6fd5ea87a4a6d Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 15 May 2011 16:02:54 +0400 Subject: s4-python: keep wheel_gid as an integer --- source4/scripting/python/samba/upgradehelpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index 48f492a7dc..729231e763 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -342,7 +342,7 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp) (security.SID_BUILTIN_ADMINISTRATORS), attrs=["xidNumber"]) if len(res9) == 1: - names.wheel_gid = res9[0]["xidNumber"] + names.wheel_gid = int(str(res9[0]["xidNumber"])) else: raise ProvisioningError("Unable to find uid/gid for Domain Admins rid") return names -- cgit From c6cc22adc059aeb6de50305b8a40d513d8f05bbc Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 16 May 2011 22:55:29 +1000 Subject: s4-libnet: Remove libnet_Join and create libnet_Join_member libnet_Join conflicts with a function in the source3 netapi of the same name, and the ability to join as a DC via this particular method is unused. Andrew Bartlett --- source4/scripting/python/samba/netcmd/join.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/join.py b/source4/scripting/python/samba/netcmd/join.py index 70b750191a..820709c9e3 100644 --- a/source4/scripting/python/samba/netcmd/join.py +++ b/source4/scripting/python/samba/netcmd/join.py @@ -59,7 +59,13 @@ class cmd_join(Command): role = role.upper() if role is None or role == "MEMBER": - secure_channel_type = SEC_CHAN_WKSTA + (join_password, sid, domain_name) = net.join_member(domain, + netbios_name, + LIBNET_JOIN_AUTOMATIC) + + self.outf.write("Joined domain %s (%s)\n" % (domain_name, sid)) + return + elif role == "DC": join_DC(server=server, creds=creds, lp=lp, domain=domain, site=site, netbios_name=netbios_name, targetdir=targetdir) @@ -70,10 +76,3 @@ class cmd_join(Command): 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)) -- cgit From fa3e2fc8bb3e935c65b7043382cad1d649cb68a6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 18 May 2011 12:06:25 +1000 Subject: selftest: Re-enable strings.py from source3/stf as a python subunit test --- source4/scripting/python/pyglue.c | 32 +++++++ source4/scripting/python/samba/__init__.py | 2 + source4/scripting/python/samba/tests/strings.py | 104 +++++++++++++++++++++ .../scripting/python/samba/tests/unicodenames.py | 31 ++++++ 4 files changed, 169 insertions(+) create mode 100644 source4/scripting/python/samba/tests/strings.py create mode 100644 source4/scripting/python/samba/tests/unicodenames.py (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/pyglue.c b/source4/scripting/python/pyglue.c index 03460db8ff..0f2048aa89 100644 --- a/source4/scripting/python/pyglue.c +++ b/source4/scripting/python/pyglue.c @@ -25,6 +25,10 @@ void init_glue(void); +#ifndef Py_RETURN_NONE +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + static PyObject *py_generate_random_str(PyObject *self, PyObject *args) { int len; @@ -173,6 +177,30 @@ static PyObject *py_interface_ips(PyObject *self, PyObject *args) return pylist; } +static PyObject *py_strcasecmp_m(PyObject *self, PyObject *args) +{ + char *s1, *s2; + + if (!PyArg_ParseTuple(args, "ss", &s1, &s2)) + return NULL; + + return PyInt_FromLong(strcasecmp_m(s1, s2)); +} + +static PyObject *py_strstr_m(PyObject *self, PyObject *args) +{ + char *s1, *s2, *ret; + + if (!PyArg_ParseTuple(args, "ss", &s1, &s2)) + return NULL; + + ret = strstr_m(s1, s2); + if (!ret) { + Py_RETURN_NONE; + } + return PyString_FromString(ret); +} + static PyMethodDef py_misc_methods[] = { { "generate_random_str", (PyCFunction)py_generate_random_str, METH_VARARGS, "generate_random_str(len) -> string\n" @@ -192,6 +220,10 @@ static PyMethodDef py_misc_methods[] = { "get debug level" }, { "interface_ips", (PyCFunction)py_interface_ips, METH_VARARGS, "get interface IP address list"}, + { "strcasecmp_m", (PyCFunction)py_strcasecmp_m, METH_VARARGS, + "(for testing) compare two strings using Samba's strcasecmp_m()"}, + { "strstr_m", (PyCFunction)py_strstr_m, METH_VARARGS, + "(for testing) find one string in another with Samba's strstr_m()"}, { NULL } }; diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py index 2a54f47d2b..dac69e7f62 100644 --- a/source4/scripting/python/samba/__init__.py +++ b/source4/scripting/python/samba/__init__.py @@ -348,3 +348,5 @@ nttime2string = _glue.nttime2string nttime2unix = _glue.nttime2unix unix2nttime = _glue.unix2nttime generate_random_password = _glue.generate_random_password +strcasecmp_m = _glue.strcasecmp_m +strstr_m = _glue.strstr_m diff --git a/source4/scripting/python/samba/tests/strings.py b/source4/scripting/python/samba/tests/strings.py new file mode 100644 index 0000000000..5f3e5c5bb7 --- /dev/null +++ b/source4/scripting/python/samba/tests/strings.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +# subunit test cases for Samba string functions. + +# Copyright (C) 2003 by Martin Pool +# Copyright (C) 2011 Andrew Bartlett +# +# 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 . + +# XXX: All this code assumes that the Unix character set is UTF-8, +# which is the most common setting. I guess it would be better to +# force it to that value while running the tests. I'm not sure of the +# best way to do that yet. +# +# -- mbp + +import sys, re +from unicodenames import * + +import samba.tests +from samba import strcasecmp_m, strstr_m + +def signum(a): + if a < 0: + return -1 + elif a > 0: + return +1 + else: + return 0 + + +class strcasecmp_m_Tests(samba.tests.TestCase): + """String comparisons in simple ASCII and unicode""" + def test_strcasecmp_m(self): + # A, B, strcasecmp(A, B) + cases = [('hello', 'hello', 0), + ('hello', 'goodbye', +1), + ('goodbye', 'hello', -1), + ('hell', 'hello', -1), + ('', '', 0), + ('a', '', +1), + ('', 'a', -1), + ('a', 'A', 0), + ('aa', 'aA', 0), + ('Aa', 'aa', 0), + ('longstring ' * 100, 'longstring ' * 100, 0), + ('longstring ' * 100, 'longstring ' * 100 + 'a', -1), + ('longstring ' * 100 + 'a', 'longstring ' * 100, +1), + (KATAKANA_LETTER_A, KATAKANA_LETTER_A, 0), + (KATAKANA_LETTER_A, 'a', 1), + ] + for a, b, expect in cases: + self.assertEquals(signum(strcasecmp_m(a.encode('utf-8'), + b.encode('utf-8'))), + expect) + +class strstr_m_Tests(samba.tests.TestCase): + """strstr_m tests in simple ASCII and unicode strings""" + def test_strstr_m(self): + # A, B, strstr_m(A, B) + cases = [('hello', 'hello', 'hello'), + ('hello', 'goodbye', None), + ('goodbye', 'hello', None), + ('hell', 'hello', None), + ('hello', 'hell', 'hello'), + ('', '', ''), + ('a', '', 'a'), + ('', 'a', None), + ('a', 'A', None), + ('aa', 'aA', None), + ('Aa', 'aa', None), + ('%v foo', '%v', '%v foo'), + ('foo %v foo', '%v', '%v foo'), + ('foo %v', '%v', '%v'), + ('longstring ' * 100, 'longstring ' * 99, 'longstring ' * 100), + ('longstring ' * 99, 'longstring ' * 100, None), + ('longstring a' * 99, 'longstring ' * 100 + 'a', None), + ('longstring ' * 100 + 'a', 'longstring ' * 100, 'longstring ' * 100 + 'a'), + (KATAKANA_LETTER_A, KATAKANA_LETTER_A + 'bcd', None), + (KATAKANA_LETTER_A + 'bcde', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcde'), + ('d'+KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcd'), + ('d'+KATAKANA_LETTER_A + 'bd', KATAKANA_LETTER_A + 'bcd', None), + + ('e'+KATAKANA_LETTER_A + 'bcdf', KATAKANA_LETTER_A + 'bcd', KATAKANA_LETTER_A + 'bcdf'), + (KATAKANA_LETTER_A, KATAKANA_LETTER_A + 'bcd', None), + (KATAKANA_LETTER_A*3, 'a', None), + ] + for a, b, expect in cases: + if expect is not None: + expect = expect.encode('utf-8') + self.assertEquals(strstr_m(a.encode('utf-8'), + b.encode('utf-8')), + expect) diff --git a/source4/scripting/python/samba/tests/unicodenames.py b/source4/scripting/python/samba/tests/unicodenames.py new file mode 100644 index 0000000000..fa5d0efc8c --- /dev/null +++ b/source4/scripting/python/samba/tests/unicodenames.py @@ -0,0 +1,31 @@ +#! /usr/bin/python + +# Copyright (C) 2003 by Martin Pool +# +# 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 . + + +""" +Defines symbolic names for a few UNICODE characters, to make test +source code more readable on machines that don't have all the +necessary fonts. + +You can do "import *" on this file safely. +""" + +LATIN_CAPITAL_LETTER_N_WITH_TILDE = u'\u004e' +LATIN_CAPITAL_LETTER_O_WITH_DIARESIS = u'\u00d6' +LATIN_SMALL_LETTER_O_WITH_DIARESIS = u'\u00f6' + +KATAKANA_LETTER_A = u'\u30a2' -- cgit From 6071ed67bf413b0f9245be9038e2e600d8ebb5aa Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Fri, 20 May 2011 19:43:19 +0400 Subject: s4-python: move function find_provision_key_parameters to provision namespace as it can be used not only for upgradeprovision --- .../scripting/python/samba/provision/__init__.py | 106 ++++++++++++++++++++ .../python/samba/tests/upgradeprovisionneeddc.py | 4 +- source4/scripting/python/samba/upgradehelpers.py | 108 --------------------- 3 files changed, 108 insertions(+), 110 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index bdca992412..a12b9b90c1 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -38,11 +38,13 @@ import uuid import socket import urllib import shutil +import string import ldb from samba.auth import system_session, admin_session import samba +from samba.dsdb import DS_DOMAIN_FUNCTION_2000 from samba import ( Ldb, check_all_substituted, @@ -217,6 +219,110 @@ class ProvisionNames(object): self.sitename = None self.smbconf = 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=ldb.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=ldb.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=ldb.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=ldb.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=ldb.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=ldb.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=ldb.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 update_provision_usn(samdb, low, high, replace=False): """Update the field provisionUSN in sam.ldb diff --git a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py b/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py index 3a9c78e0dc..596cff6d3a 100644 --- a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py +++ b/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py @@ -26,9 +26,9 @@ import shutil from samba import param from samba.credentials import Credentials from samba.auth import system_session -from samba.provision import getpolicypath +from samba.provision import getpolicypath,find_provision_key_parameters from samba.upgradehelpers import (get_paths, get_ldbs, - find_provision_key_parameters, identic_rename, + identic_rename, updateOEMInfo, getOEMInfo, update_gpo, delta_update_basesamdb, update_dns_account_password, diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index 729231e763..66782bb31b 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -24,13 +24,11 @@ """Helpers 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, @@ -242,112 +240,6 @@ def update_policyids(names, samdb): 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 = int(str(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. -- cgit From 22a638b6165518eb9e640b1437b4552d685925dc Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sat, 23 Apr 2011 13:47:27 +0400 Subject: s4-python: Remove not used imports --- source4/scripting/python/samba/provision/__init__.py | 6 ++---- source4/scripting/python/samba/upgradehelpers.py | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index a12b9b90c1..8433f23121 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -48,15 +48,13 @@ from samba.dsdb import DS_DOMAIN_FUNCTION_2000 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 import security, misc from samba.dcerpc.misc import ( SEC_CHAN_BDC, SEC_CHAN_WKSTA, @@ -284,7 +282,7 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp) # invocation id/objectguid res5 = samdb.search(expression="(objectClass=*)", - base="CN=NTDS Settings,%s" % str(names.serverdn), scope=SCOPE_BASE, + base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.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])) diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index 66782bb31b..16e4ea006a 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -31,13 +31,12 @@ import samba from samba import Ldb, version, ntacls from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE import ldb -from samba.provision import (ProvisionNames, provision_paths_from_lp, +from samba.provision import (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 import 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 -- cgit From 53b0c44d8c0f21682220a212baa4b8a2e0f3ceae Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 23 May 2011 15:27:50 +1000 Subject: s4-provision Use correct tkey-gssapi-credential We changed to ${DNSNAME} (the fully qualified domain name) a while back, and while it's usually functionally idential to the previous setting, this breaks down if there is more than one DNS server. Andrew Bartlett --- source4/scripting/python/samba/provision/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index 8433f23121..f987fb8ef3 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -1749,6 +1749,7 @@ def provision(logger, session_info, credentials, smbconf=None, create_named_txt(paths.namedtxt, realm=names.realm, dnsdomain=names.dnsdomain, + dnsname = "%s.%s" % (names.hostname, 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) @@ -1985,7 +1986,7 @@ def create_named_conf(paths, realm, dnsdomain, setup_file(setup_path("named.conf.update"), paths.namedconf_update) -def create_named_txt(path, realm, dnsdomain, private_dir, +def create_named_txt(path, realm, dnsdomain, dnsname, private_dir, keytab_name): """Write out a file containing zone statements suitable for inclusion in a named.conf file (including GSS-TSIG configuration). @@ -1998,6 +1999,7 @@ def create_named_txt(path, realm, dnsdomain, private_dir, """ setup_file(setup_path("named.txt"), path, { "DNSDOMAIN": dnsdomain, + "DNSNAME" : dnsname, "REALM": realm, "DNS_KEYTAB": keytab_name, "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name), -- cgit From 726ee12bb450821f929e05ca1c708f3e33f909cf Mon Sep 17 00:00:00 2001 From: Theresa Halloran Date: Thu, 19 May 2011 16:17:07 -0400 Subject: s4/samba-tool: Move samba-tool enableaccount to samba-tool user enable command. Signed-off-by: Andrew Tridgell --- source4/scripting/python/samba/netcmd/__init__.py | 3 +- .../scripting/python/samba/netcmd/enableaccount.py | 60 ---------------------- source4/scripting/python/samba/netcmd/user.py | 42 +++++++++++++++ 3 files changed, 43 insertions(+), 62 deletions(-) delete mode 100644 source4/scripting/python/samba/netcmd/enableaccount.py (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/__init__.py b/source4/scripting/python/samba/netcmd/__init__.py index cf514d5c49..ee2e700a24 100644 --- a/source4/scripting/python/samba/netcmd/__init__.py +++ b/source4/scripting/python/samba/netcmd/__init__.py @@ -2,6 +2,7 @@ # Unix SMB/CIFS implementation. # Copyright (C) Jelmer Vernooij 2009 +# Copyright (C) Theresa Halloran 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 @@ -181,8 +182,6 @@ 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 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 . -# - -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/user.py b/source4/scripting/python/samba/netcmd/user.py index a5750b5010..5f53263523 100644 --- a/source4/scripting/python/samba/netcmd/user.py +++ b/source4/scripting/python/samba/netcmd/user.py @@ -3,6 +3,7 @@ # user management # # Copyright Jelmer Vernooij 2010 +# Copyright Theresa Halloran 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 @@ -19,6 +20,10 @@ # import samba.getopt as options +import sys +from samba.auth import system_session +from samba.samdb import SamDB + from samba.net import Net @@ -26,6 +31,7 @@ from samba.netcmd import ( Command, CommandError, SuperCommand, + Option, ) class cmd_user_add(Command): @@ -70,6 +76,41 @@ class cmd_user_delete(Command): except RuntimeError, msg: raise CommandError("Failed to delete user %s: %s" % (name, msg)) +class cmd_user_enable(Command): + """Enables a user""" + + synopsis = "%prog user enable [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) + + class cmd_user(SuperCommand): """User management [server connection needed]""" @@ -77,4 +118,5 @@ class cmd_user(SuperCommand): subcommands = {} subcommands["add"] = cmd_user_add() subcommands["delete"] = cmd_user_delete() + subcommands["enable"] = cmd_user_enable() -- cgit From 23177b5f44815bc5b46943c70d37dc626ed60288 Mon Sep 17 00:00:00 2001 From: Theresa Halloran Date: Thu, 19 May 2011 16:24:00 -0400 Subject: s4:samba-tool: Move samba-tool setexpiry to samba-tool user setexpiry Signed-off-by: Andrew Tridgell --- source4/scripting/python/samba/netcmd/__init__.py | 2 - source4/scripting/python/samba/netcmd/setexpiry.py | 67 ---------------------- source4/scripting/python/samba/netcmd/user.py | 38 +++++++++++- 3 files changed, 37 insertions(+), 70 deletions(-) delete mode 100644 source4/scripting/python/samba/netcmd/setexpiry.py (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/__init__.py b/source4/scripting/python/samba/netcmd/__init__.py index ee2e700a24..d934cf8af6 100644 --- a/source4/scripting/python/samba/netcmd/__init__.py +++ b/source4/scripting/python/samba/netcmd/__init__.py @@ -180,8 +180,6 @@ 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.newuser import cmd_newuser commands["newuser"] = cmd_newuser() from samba.netcmd.netacl import cmd_acl 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 . -# - -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/user.py b/source4/scripting/python/samba/netcmd/user.py index 5f53263523..696c8892cf 100644 --- a/source4/scripting/python/samba/netcmd/user.py +++ b/source4/scripting/python/samba/netcmd/user.py @@ -110,7 +110,43 @@ class cmd_user_enable(Command): credentials=creds, lp=lp) samdb.enable_account(filter) +class cmd_user_setexpiry(Command): + """Sets the expiration of a user account""" + synopsis = "%prog user setexpiry [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) class cmd_user(SuperCommand): """User management [server connection needed]""" @@ -119,4 +155,4 @@ class cmd_user(SuperCommand): subcommands["add"] = cmd_user_add() subcommands["delete"] = cmd_user_delete() subcommands["enable"] = cmd_user_enable() - + subcommands["setexpiry"] = cmd_user_setexpiry() -- cgit From 7b3d8b6c908a37bb06e413dee406cebd29b99b3e Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 1 Jun 2011 14:41:51 +1000 Subject: samba-tool: improved user enable error handling --- source4/scripting/python/samba/netcmd/user.py | 7 ++++++- source4/scripting/python/samba/samdb.py | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/user.py b/source4/scripting/python/samba/netcmd/user.py index 696c8892cf..15be4bb87f 100644 --- a/source4/scripting/python/samba/netcmd/user.py +++ b/source4/scripting/python/samba/netcmd/user.py @@ -108,7 +108,12 @@ class cmd_user_enable(Command): samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) - samdb.enable_account(filter) + try: + samdb.enable_account(filter) + except Exception, msg: + raise CommandError("Failed to enable user %s: %s" % (username or filter, msg)) + print("Enabled user %s" % (username or filter)) + class cmd_user_setexpiry(Command): """Sets the expiration of a user account""" diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index 99f141e664..2cea198e6b 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -79,6 +79,8 @@ class SamDB(samba.Ldb): """ res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=search_filter, attrs=["userAccountControl"]) + if len(res) == 0: + raise Exception('Unable to find user "%s"' % search_filter) assert(len(res) == 1) user_dn = res[0].dn -- cgit From 1bc1ac0d084976fccf187526f7bd8d9ad818da10 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 1 Jun 2011 14:46:04 +1000 Subject: samba-tool: improved error handling in user setexpiry --- source4/scripting/python/samba/netcmd/user.py | 6 +++++- source4/scripting/python/samba/samdb.py | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/user.py b/source4/scripting/python/samba/netcmd/user.py index 15be4bb87f..6acf52d790 100644 --- a/source4/scripting/python/samba/netcmd/user.py +++ b/source4/scripting/python/samba/netcmd/user.py @@ -151,7 +151,11 @@ class cmd_user_setexpiry(Command): samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) - samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry) + try: + samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry) + except Exception, msg: + raise CommandError("Failed to set expiry for user %s: %s" % (username or filter, msg)) + print("Set expiry for user %s to %u days" % (username or filter, days)) class cmd_user(SuperCommand): """User management [server connection needed]""" diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index 2cea198e6b..a5c627e5b5 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -108,6 +108,8 @@ userAccountControl: %u """ 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"' % search_filter) assert(len(res) == 1) user_dn = res[0].dn @@ -411,6 +413,8 @@ unicodePwd:: %s res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=search_filter, attrs=["userAccountControl", "accountExpires"]) + if len(res) == 0: + raise Exception('Unable to find user "%s"' % search_filter) assert(len(res) == 1) user_dn = res[0].dn -- cgit From 2f67fae18be13f4bfce009e8639bab74383844cb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Jun 2011 10:22:06 +0930 Subject: samba3sam.py: don't assume order of ldb results. Switching to tdb2 breaks this test horribly, because it relied on the order of TDB1 traversal. Fix it to sort te results (by db), then check them. Signed-off-by: Rusty Russell --- source4/scripting/python/samba/tests/samba3sam.py | 306 ++++++++++++---------- 1 file changed, 164 insertions(+), 142 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/tests/samba3sam.py b/source4/scripting/python/samba/tests/samba3sam.py index a34f0f620c..99b9c0655e 100644 --- a/source4/scripting/python/samba/tests/samba3sam.py +++ b/source4/scripting/python/samba/tests/samba3sam.py @@ -30,6 +30,7 @@ from samba.tests import TestCaseInTempDir, env_loadparm import samba.dcerpc.security import samba.ndr from samba.auth import system_session +from operator import attrgetter def read_datafile(filename): @@ -439,34 +440,37 @@ objectSid: S-1-5-21-4231626423-2410014848-2360679739-552 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") + res = sorted(res, key=attrgetter('dn')) + 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=Y")) + self.assertEquals(str(res[1]["dnsHostName"]), "y") + self.assertEquals(str(res[1]["lastLogon"]), "y") # 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") + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) + self.assertTrue(not "dnsHostName" in res[0]) 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].dn), self.samba4.dn("cn=Z")) + self.assertEquals(str(res[1]["dnsHostName"]), "z") 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")) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) 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.assertEquals(str(res[0]["lastLogon"]), "x") + self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) self.assertTrue(not "dnsHostName" in res[1]) - self.assertEquals(str(res[1]["lastLogon"]), "x") + self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by converted attribute # TODO: @@ -475,18 +479,19 @@ objectSid: S-1-5-21-4231626423-2410014848-2360679739-552 #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]) + res = sorted(res, key=attrgetter('dn')) + 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.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552", res[1]["objectSid"]) self.assertTrue("objectSid" in 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.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552", + res[0]["objectSid"]) + self.assertTrue("objectSid" in res[0]) # Search by generated attribute # In most cases, this even works when the mapping is missing @@ -519,12 +524,13 @@ objectSid: S-1-5-21-4231626423-2410014848-2360679739-552 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") + res = sorted(res, key=attrgetter('dn')) + 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]["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].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") @@ -532,18 +538,19 @@ objectSid: S-1-5-21-4231626423-2410014848-2360679739-552 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")) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) 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[0]["lastLogon"]), "x") + self.assertEquals(res[0]["objectClass"][0], "user") + 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(set(res[1]["objectClass"]), set(["top"])) + 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") - self.assertEquals(res[2]["objectClass"][0], "user") + self.assertEquals(str(res[2]["objectClass"][0]), "user") # Testing search by parse tree @@ -551,34 +558,37 @@ objectSid: S-1-5-21-4231626423-2410014848-2360679739-552 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") + res = sorted(res, key=attrgetter('dn')) + 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=Y")) + self.assertEquals(str(res[1]["dnsHostName"]), "y") + self.assertEquals(str(res[1]["lastLogon"]), "y") # 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") + res = sorted(res, key=attrgetter('dn')) + 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=A")) - self.assertTrue(not "dnsHostName" in res[1]) + 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 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") + res = sorted(res, key=attrgetter('dn')) + 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=Y")) + self.assertEquals(str(res[1]["dnsHostName"]), "y") + self.assertEquals(str(res[1]["lastLogon"]), "y") # Search by conjunction of local and remote attribute w/o match attrs = ["dnsHostName", "lastLogon"] @@ -593,40 +603,43 @@ objectSid: S-1-5-21-4231626423-2410014848-2360679739-552 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") + res = sorted(res, key=attrgetter('dn')) + 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=Y")) + self.assertEquals(str(res[1]["dnsHostName"]), "y") + self.assertEquals(str(res[1]["lastLogon"]), "y") # 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")) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) 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[0]["lastLogon"]), "x") + 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 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") + res = sorted(res, key=attrgetter('dn')) + 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=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") + 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=Y")) + self.assertEquals(str(res[2]["dnsHostName"]), "y") + self.assertEquals(str(res[2]["lastLogon"]), "y") # Search by disjunction of local and remote attribute w/o match res = self.ldb.search(expression="(|(codePage=y)(nextRid=z))", @@ -637,142 +650,151 @@ objectSid: S-1-5-21-4231626423-2410014848-2360679739-552 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")) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) 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.assertEquals(str(res[0]["lastLogon"]), "x") + self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) 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[1]["lastLogon"]), "y") + 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") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) + 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") # 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") + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) + self.assertTrue(not "dnsHostName" in res[0]) 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].dn), self.samba4.dn("cn=Z")) + self.assertEquals(str(res[1]["dnsHostName"]), "z") 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")) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) 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.assertEquals(str(res[0]["lastLogon"]), "x") + self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) 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[1]["lastLogon"]), "y") + 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") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) + 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") # 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") + res = sorted(res, key=attrgetter('dn')) + 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=B")) + self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C")) 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[1]["lastLogon"]), "z") + self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y")) + self.assertEquals(str(res[2]["dnsHostName"]), "y") + self.assertEquals(str(res[2]["lastLogon"]), "y") + 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") # 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")) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) 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.assertEquals(str(res[0]["lastLogon"]), "x") + self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) 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[1]["lastLogon"]), "y") + 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") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) + 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") # 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")) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) 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.assertEquals(str(res[0]["lastLogon"]), "x") + self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B")) 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[1]["lastLogon"]), "y") + 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") - self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C")) - self.assertTrue(not "dnsHostName" in res[3]) + 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") # 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]) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C")) + self.assertTrue(not "dnsHostName" in res[0]) + self.assertEquals(str(res[0]["lastLogon"]), "z") + self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y")) + self.assertEquals(str(res[1]["dnsHostName"]), "y") + 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") # 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) + res = sorted(res, key=attrgetter('dn')) 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].dn), self.samba4.dn("cn=C")) + self.assertTrue(not "dnsHostName" in res[1]) 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].dn), self.samba4.dn("cn=Z")) + self.assertEquals(str(res[2]["dnsHostName"]), "z") 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")) + res = sorted(res, key=attrgetter('dn')) + self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A")) 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.assertEquals(str(res[0]["lastLogon"]), "x") + 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=C")) 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[2]["lastLogon"]), "z") + self.assertEquals(str(res[3].dn), self.samba4.dn("cn=X")) + self.assertEquals(str(res[3]["dnsHostName"]), "x") + self.assertEquals(str(res[3]["lastLogon"]), "x") + self.assertEquals(str(res[4].dn), self.samba4.dn("cn=Z")) + self.assertEquals(str(res[4]["dnsHostName"]), "z") self.assertEquals(str(res[4]["lastLogon"]), "z") # Clean up -- cgit From 087ee1f40e4821f719f592ef2b768afad5c9a175 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 12 May 2011 22:53:26 +0200 Subject: s4-gpo: fixed display of GPO version numbers --- source4/scripting/python/samba/netcmd/gpo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/gpo.py b/source4/scripting/python/samba/netcmd/gpo.py index 19007b361c..fac9167076 100644 --- a/source4/scripting/python/samba/netcmd/gpo.py +++ b/source4/scripting/python/samba/netcmd/gpo.py @@ -126,7 +126,7 @@ class cmd_listall(Command): 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("version : %s" % attr_default(m, 'versionNumber', '0')) print("flags : %s" % flags_string(gpo_flags, int(attr_default(m, 'flags', 0)))) print("") -- cgit From 40dc94a53f4f0f5dee285daf486912b0996d5f3e Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 2 Jun 2011 15:40:28 +1000 Subject: s4-ipv6: update callers to load_interface_list() --- source4/scripting/python/pyglue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/pyglue.c b/source4/scripting/python/pyglue.c index 0f2048aa89..8a82f3502a 100644 --- a/source4/scripting/python/pyglue.c +++ b/source4/scripting/python/pyglue.c @@ -153,7 +153,7 @@ static PyObject *py_interface_ips(PyObject *self, PyObject *args) return NULL; } - load_interface_list(tmp_ctx, lpcfg_interfaces(lp_ctx), &ifaces); + load_interface_list(tmp_ctx, lp_ctx, &ifaces); count = iface_list_count(ifaces); -- cgit From c091a92be51e8c14bf0b51ab83319fbcb704c91f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 2 Jun 2011 15:43:40 +1000 Subject: s4-param Remove 'sam database' parameter This now just relies on the private dir parameter, which remains. Andrew Bartlett --- source4/scripting/python/samba/hostconfig.py | 2 +- source4/scripting/python/samba/provision/__init__.py | 5 ++--- source4/scripting/python/samba/samdb.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/hostconfig.py b/source4/scripting/python/samba/hostconfig.py index 3e6dc6b1dd..c50b944c98 100644 --- a/source4/scripting/python/samba/hostconfig.py +++ b/source4/scripting/python/samba/hostconfig.py @@ -37,7 +37,7 @@ class Hostconfig(object): :param session_info: Session info to use :param credentials: Credentials to access the SamDB with """ - return SamDB(url=self.lp.get("sam database"), + return SamDB(url=self.lp.samdb_url(), session_info=session_info, credentials=credentials, lp=self.lp) diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index f987fb8ef3..fffd352071 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -432,7 +432,7 @@ def check_install(lp, session_info, credentials): """ if lp.get("realm") == "": raise Exception("Realm empty") - samdb = Ldb(lp.get("sam database"), session_info=session_info, + samdb = Ldb(lp.samdb_url(), session_info=session_info, credentials=credentials, lp=lp) if len(samdb.search("(cn=Administrator)")) != 1: raise ProvisioningError("No administrator account found") @@ -517,8 +517,7 @@ def provision_paths_from_lp(lp, dnsdomain): 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.samdb = os.path.join(paths.private_dir, "sam.ldb") paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb") paths.secrets = os.path.join(paths.private_dir, diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index a5c627e5b5..55f3536c27 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -46,7 +46,7 @@ class SamDB(samba.Ldb): if not auto_connect: url = None elif url is None and lp is not None: - url = lp.get("sam database") + url = lp.samdb_url() super(SamDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir, session_info=session_info, credentials=credentials, flags=flags, -- cgit From 1565da76947f91add10a54096cdfe2ab67917b32 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 2 Jun 2011 15:47:44 +1000 Subject: s4-param Remove 'secrets database' parameter This is now just secrets.ldb in the private dir, which remains. --- source4/scripting/python/samba/provision/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index fffd352071..b99eec9ab8 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -520,8 +520,7 @@ def provision_paths_from_lp(lp, dnsdomain): paths.samdb = os.path.join(paths.private_dir, "sam.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.secrets = os.path.join(paths.private_dir, "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") -- cgit From 834d590bcff79e2d458b9e193be0c64e849a6362 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 2 Jun 2011 15:49:36 +1000 Subject: s4-param Remove 'idmap database' This is now just idmap.ldb in the private dir, which remains. --- source4/scripting/python/samba/idmap.py | 2 +- source4/scripting/python/samba/provision/__init__.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/idmap.py b/source4/scripting/python/samba/idmap.py index 93fca46edd..9d957341de 100644 --- a/source4/scripting/python/samba/idmap.py +++ b/source4/scripting/python/samba/idmap.py @@ -41,7 +41,7 @@ class IDmapDB(samba.Ldb): self.lp = lp if url is None: - url = lp.get("idmap database") + url = lp.private_path("idmap.ldb") super(IDmapDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir, session_info=session_info, credentials=credentials, flags=flags, diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index b99eec9ab8..bd3ab8a4f4 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -518,8 +518,7 @@ def provision_paths_from_lp(lp, dnsdomain): paths.shareconf = os.path.join(paths.private_dir, "share.ldb") paths.samdb = os.path.join(paths.private_dir, "sam.ldb") - paths.idmapdb = os.path.join(paths.private_dir, - lp.get("idmap database") or "idmap.ldb") + paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb") paths.secrets = os.path.join(paths.private_dir, "secrets.ldb") paths.privilege = os.path.join(paths.private_dir, "privilege.ldb") paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone") -- cgit From a18efb1490cebd92205973c50ce1582b091a7676 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 2 Jun 2011 18:56:10 +1000 Subject: s4-param Remove 'sid generator' This was only used by the Fedora DS backend for Samba4. We agreed to no longer support external LDAP backends. Andrew Bartlett --- source4/scripting/python/samba/provision/__init__.py | 6 ------ source4/scripting/python/samba/tests/samba3sam.py | 6 +++++- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index bd3ab8a4f4..eb6e01f389 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -709,11 +709,6 @@ def make_smbconf(smbconf, hostname, domain, realm, serverrole, 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") @@ -725,7 +720,6 @@ def make_smbconf(smbconf, hostname, domain, realm, serverrole, "SERVERROLE": serverrole, "NETLOGONPATH": netlogon, "SYSVOLPATH": sysvol, - "SIDGENERATOR_LINE": sid_generator_line, "PRIVATEDIR_LINE": privatedir_line, "LOCKDIR_LINE": lockdir_line }) diff --git a/source4/scripting/python/samba/tests/samba3sam.py b/source4/scripting/python/samba/tests/samba3sam.py index 99b9c0655e..7353391519 100644 --- a/source4/scripting/python/samba/tests/samba3sam.py +++ b/source4/scripting/python/samba/tests/samba3sam.py @@ -65,7 +65,6 @@ class MapBaseTestCase(TestCaseInTempDir): 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() @@ -87,6 +86,7 @@ class MapBaseTestCase(TestCaseInTempDir): def __init__(self, basedn, dn, lp): self.db = Ldb(lp=lp, session_info=system_session()) + self.db.set_opaque("skip_allocate_sids", "true"); self.basedn = basedn self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold() self.substvars = {"BASEDN": self.basedn} @@ -136,12 +136,14 @@ class Samba3SamTestCase(MapBaseTestCase): def setUp(self): super(Samba3SamTestCase, self).setUp() ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) + ldb.set_opaque("skip_allocate_sids", "true"); 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()) + self.ldb.set_opaque("skip_allocate_sids", "true"); def test_search_non_mapped(self): """Looking up by non-mapped attribute""" @@ -303,11 +305,13 @@ class MapTestCase(MapBaseTestCase): def setUp(self): super(MapTestCase, self).setUp() ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session()) + ldb.set_opaque("skip_allocate_sids", "true"); 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()) + self.ldb.set_opaque("skip_allocate_sids", "true"); def test_map_search(self): """Running search tests on mapped data.""" -- cgit From f67a14976bd6ccdacd319df872e6add994f0e0f3 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 6 Jun 2011 14:39:19 +1000 Subject: s4-param Remove 'modules dir' The Samba waf build ensures that dyn_MODULESDIR is always correct (even for in-tree binaries), so we don't need to allow the user to configure this at run time. Andrew Bartlett --- source4/scripting/python/samba/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py index dac69e7f62..76eb44ce92 100644 --- a/source4/scripting/python/samba/__init__.py +++ b/source4/scripting/python/samba/__init__.py @@ -26,6 +26,7 @@ __docformat__ = "restructuredText" import os import sys +import samba.param def source_tree_topdir(): '''return the top level directory (the one containing the source4 directory)''' @@ -77,8 +78,8 @@ class Ldb(_Ldb): if modules_dir is not None: self.set_modules_dir(modules_dir) - elif lp is not None: - self.set_modules_dir(os.path.join(lp.get("modules dir"), "ldb")) + else: + self.set_modules_dir(os.path.join(samba.param.modules_dir(), "ldb")) if session_info is not None: self.set_session_info(session_info) -- cgit From 8acbc3a17435ef330910993aa40b5ec1e4c1dab2 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 7 Jun 2011 12:55:31 +1000 Subject: s4-provision: fixed detection of V4/V6 addresses Autobuild-User: Andrew Tridgell Autobuild-Date: Tue Jun 7 06:07:24 CEST 2011 on sn-devel-104 --- .../scripting/python/samba/provision/__init__.py | 31 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index eb6e01f389..324e76f374 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -1465,6 +1465,25 @@ def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn, set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp) +def interface_ips_v4(lp): + '''return only IPv4 IPs''' + ips = samba.interface_ips(lp, False) + ret = [] + for i in ips: + if i.find(':') == -1: + ret.append(i) + return ret + +def interface_ips_v6(lp, linklocal=False): + '''return only IPv6 IPs''' + ips = samba.interface_ips(lp, False) + ret = [] + for i in ips: + if i.find(':') != -1 and (linklocal or i.find('%') == -1): + ret.append(i) + return ret + + 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, @@ -1565,16 +1584,24 @@ def provision(logger, session_info, credentials, smbconf=None, if hostip is None: logger.info("Looking up IPv4 addresses") - hostips = samba.interface_ips(lp, False) + hostips = interface_ips_v4(lp) 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.", + logger.warning("More than one IPv4 address found. Using %s", hostip) + if hostip6 is None: + logger.info("Looking up IPv6 addresses") + hostips = interface_ips_v6(lp, linklocal=False) + if hostips: + hostip6 = hostips[0] + if len(hostips) > 1: + logger.warning("More than one IPv6 address found. Using %s", hostip6) + if serverrole is None: serverrole = lp.get("server role") -- cgit From 1596595b7e644de6432d7c8fb9e10ee2b525440e Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 9 Jun 2011 15:00:03 +1000 Subject: s4-ipv6: don't default to 127.0.0.1 in provision it is better to just leave the IPv4 address out of the zone file Pair-Programmed-With: Andrew Bartlett --- source4/scripting/python/samba/provision/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index 324e76f374..f272872a93 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -1585,14 +1585,15 @@ def provision(logger, session_info, credentials, smbconf=None, if hostip is None: logger.info("Looking up IPv4 addresses") hostips = interface_ips_v4(lp) - if len(hostips) == 0: - logger.warning("No external IPv4 address has been found. Using loopback.") - hostip = '127.0.0.1' - else: + if len(hostips) > 0: hostip = hostips[0] if len(hostips) > 1: logger.warning("More than one IPv4 address found. Using %s", hostip) + if hostip == "127.0.0.1": + hostip = None + if hostip is None: + logger.warning("No IPv4 address will be assigned") if hostip6 is None: logger.info("Looking up IPv6 addresses") @@ -1601,6 +1602,8 @@ def provision(logger, session_info, credentials, smbconf=None, hostip6 = hostips[0] if len(hostips) > 1: logger.warning("More than one IPv6 address found. Using %s", hostip6) + if hostip6 is None: + logger.warning("No IPv6 address will be assigned") if serverrole is None: serverrole = lp.get("server role") @@ -1868,7 +1871,7 @@ def provision_become_dc(smbconf=None, targetdir=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, + hostname=hostname, hostip=None, domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename) res.lp.set("debuglevel", str(debuglevel)) -- cgit From 516dc404fd7f299b68adae356f2416ca8e265031 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 9 Jun 2011 15:01:30 +1000 Subject: samba-tool: added --local option to drs replicate command this allows replication directly to the local SAM, which means it can run without the samba daemon running. It also bypasses all usnChanged checks, which is useful for forcing replication of a set of objects which are not marked as replication being needed Autobuild-User: Andrew Tridgell Autobuild-Date: Thu Jun 9 08:15:10 CEST 2011 on sn-devel-104 --- source4/scripting/python/samba/netcmd/drs.py | 40 +++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/drs.py b/source4/scripting/python/samba/netcmd/drs.py index 56c0e39a59..61717a70e9 100644 --- a/source4/scripting/python/samba/netcmd/drs.py +++ b/source4/scripting/python/samba/netcmd/drs.py @@ -233,6 +233,39 @@ class cmd_drs_kcc(Command): self.message("Consistency check on %s successful." % DC) +def drs_local_replicate(self, SOURCE_DC, NC): + '''replicate from a source DC to the local SAM''' + self.server = SOURCE_DC + drsuapi_connect(self) + + self.local_samdb = SamDB(session_info=system_session(), url=None, + credentials=self.creds, lp=self.lp) + + self.samdb = SamDB(url="ldap://%s" % self.server, + session_info=system_session(), + credentials=self.creds, lp=self.lp) + + # work out the source and destination GUIDs + res = self.local_samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) + self.ntds_dn = res[0]["dsServiceName"][0] + + res = self.local_samdb.search(base=self.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) + self.ntds_guid = misc.GUID(self.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0])) + + + source_dsa_invocation_id = misc.GUID(self.samdb.get_invocation_id()) + destination_dsa_guid = self.ntds_guid + + self.samdb.transaction_start() + repl = drs_utils.drs_Replicate("ncacn_ip_tcp:%s[seal]" % self.server, self.lp, + self.creds, self.local_samdb) + try: + repl.replicate(NC, source_dsa_invocation_id, destination_dsa_guid) + except Exception, e: + raise CommandError("Error replicating DN %s" % NC, e) + self.samdb.transaction_commit() + + class cmd_drs_replicate(Command): """replicate a naming context between two DCs""" @@ -250,9 +283,10 @@ class cmd_drs_replicate(Command): 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"), + Option("--local", help="pull changes directly into the local database (destination DC is ignored)", action="store_true"), ] - def run(self, DEST_DC, SOURCE_DC, NC, add_ref=False, sync_forced=False, + def run(self, DEST_DC, SOURCE_DC, NC, add_ref=False, sync_forced=False, local=False, sambaopts=None, credopts=None, versionopts=None, server=None): @@ -261,6 +295,10 @@ class cmd_drs_replicate(Command): self.creds = credopts.get_credentials(self.lp, fallback_machine=True) + if local: + drs_local_replicate(self, SOURCE_DC, NC) + return + drsuapi_connect(self) samdb_connect(self) -- cgit From 0c89d624e6d5620c9e37649cc2976aba918b1e6a Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 10 Jun 2011 17:17:12 +1000 Subject: s4-samba-tool: added dbcheck commmand this will be used as a consistency checker and repair tool for sam.ldb. This initial checkin just checks for empty attributes and offers to fix them Autobuild-User: Andrew Tridgell Autobuild-Date: Fri Jun 10 10:31:56 CEST 2011 on sn-devel-104 --- source4/scripting/python/samba/netcmd/__init__.py | 2 + source4/scripting/python/samba/netcmd/dbcheck.py | 123 ++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 source4/scripting/python/samba/netcmd/dbcheck.py (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/__init__.py b/source4/scripting/python/samba/netcmd/__init__.py index d934cf8af6..1373cb289b 100644 --- a/source4/scripting/python/samba/netcmd/__init__.py +++ b/source4/scripting/python/samba/netcmd/__init__.py @@ -212,3 +212,5 @@ from samba.netcmd.ldapcmp import cmd_ldapcmp commands["ldapcmp"] = cmd_ldapcmp() from samba.netcmd.testparm import cmd_testparm commands["testparm"] = cmd_testparm() +from samba.netcmd.dbcheck import cmd_dbcheck +commands["dbcheck"] = cmd_dbcheck() diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py new file mode 100644 index 0000000000..7bbd4d21b6 --- /dev/null +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# +# Samba4 AD database checker +# +# Copyright (C) Andrew Tridgell 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 . +# + +import samba, ldb +import samba.getopt as options +from samba.auth import system_session +from samba.samdb import SamDB +from samba.dcerpc import security +from samba.netcmd import ( + Command, + CommandError, + Option + ) + +def confirm(self, msg): + '''confirm an action with the user''' + if self.yes: + print("%s [YES]" % msg) + return True + v = raw_input(msg + ' [y/N] ') + return v.upper() in ['Y', 'YES'] + + +def empty_attribute(self, dn, attrname): + '''fix empty attributes''' + print("ERROR: Empty attribute %s in %s" % (attrname, dn)) + if not self.fix: + return + if not confirm(self, 'Remove empty attribute %s from %s?' % (attrname, dn)): + print("Not fixing empty attribute %s" % attrname) + return + + m = ldb.Message() + m.dn = dn + m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname) + try: + self.samdb.modify(m, ["relax:0"]) + except Exception, msg: + print("Failed to remove empty attribute %s : %s" % (attrname, msg)) + return + print("Removed empty attribute %s" % attrname) + + + +def check_object(self, dn): + '''check one object''' + if self.verbose: + print("Checking object %s" % dn) + res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE) + if len(res) != 1: + print("Object %s disappeared during check" % dn) + return + obj = res[0] + for attrname in obj: + if attrname == 'dn': + continue + for val in obj[attrname]: + if val == '': + empty_attribute(self, dn, attrname) + continue + + +class cmd_dbcheck(Command): + """check local AD database for errors""" + synopsis = "dbcheck [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "versionopts": options.VersionOptions, + "credopts": options.CredentialsOptionsDouble, + } + + takes_args = ["DN?"] + + takes_options = [ + Option("--scope", dest="scope", default="SUB", + help="Pass search scope that builds DN list. Options: SUB, ONE, BASE"), + Option("--fix", dest="fix", default=False, action='store_true', + help='Fix any errors found'), + Option("--yes", dest="yes", default=False, action='store_true', + help="don't confirm changes, just do them all"), + Option("-v", "--verbose", dest="verbose", action="store_true", default=False, + help="Print more details of checking"), + ] + + def run(self, DN=None, verbose=False, fix=False, yes=False, + scope="SUB", credopts=None, sambaopts=None, versionopts=None): + self.lp = sambaopts.get_loadparm() + self.creds = credopts.get_credentials(self.lp, fallback_machine=True) + + self.samdb = SamDB(session_info=system_session(), url=None, + credentials=self.creds, lp=self.lp) + self.verbose = verbose + self.fix = fix + self.yes = yes + + scope_map = { "SUB": ldb.SCOPE_SUBTREE, "BASE":ldb.SCOPE_BASE, "ONE":ldb.SCOPE_ONELEVEL } + scope = scope.upper() + if not scope in scope_map: + raise CommandError("Unknown scope %s" % scope) + self.search_scope = scope_map[scope] + + res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn']) + for object in res: + check_object(self, object.dn) + print('Checked %u objects' % len(res)) -- cgit From d575b2b0aba0aeaf73d82e2ed58150110db4025b Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 14 Jun 2011 16:43:10 +1000 Subject: samba-tool: disable validation on removing an empty attribute in dbcheck Autobuild-User: Andrew Tridgell Autobuild-Date: Tue Jun 14 10:49:34 CEST 2011 on sn-devel-104 --- source4/scripting/python/samba/netcmd/dbcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 7bbd4d21b6..7af210104c 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -51,7 +51,7 @@ def empty_attribute(self, dn, attrname): m.dn = dn m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname) try: - self.samdb.modify(m, ["relax:0"]) + self.samdb.modify(m, controls=["relax:0"], validate=False) except Exception, msg: print("Failed to remove empty attribute %s : %s" % (attrname, msg)) return -- cgit From 665ef94d3c15ba59811143bb3d3e395ffd306a58 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 17 Jun 2011 11:29:44 +1000 Subject: s4-pydsdb: added dsdb_normalise_attributes() call this call converts a set of attributes to DRSUAPI format and back to ldb format. This has the effect of normalising the attributes using the schema syntax rules --- source4/scripting/python/samba/samdb.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index 55f3536c27..b513ac8fff 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -507,8 +507,13 @@ accountExpires: %u dsdb._dsdb_set_schema_from_ldb(self, ldb_conn) def dsdb_DsReplicaAttribute(self, ldb, ldap_display_name, ldif_elements): + '''convert a list of attribute values to a DRSUAPI DsReplicaAttribute''' return dsdb._dsdb_DsReplicaAttribute(ldb, ldap_display_name, ldif_elements) + def dsdb_normalise_attributes(self, ldb, ldap_display_name, ldif_elements): + '''normalise a list of attribute values''' + return dsdb._dsdb_normalise_attributes(ldb, ldap_display_name, ldif_elements) + def get_attribute_from_attid(self, attid): """ Get from an attid the associated attribute -- cgit From 08dc1aa4cc1a697dd72db6a09a32d1929421fc09 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 17 Jun 2011 11:31:25 +1000 Subject: samba-tool: added attribute normalisation checks this checks that all attributes have the right normalisation, and offers to fix the ones that don't --- source4/scripting/python/samba/netcmd/dbcheck.py | 56 ++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 7af210104c..4eebca3503 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -58,12 +58,46 @@ def empty_attribute(self, dn, attrname): print("Removed empty attribute %s" % attrname) +def normalise_mismatch(self, dn, attrname, values): + '''fix attribute normalisation errors''' + print("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) + mod_list = [] + for val in values: + normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) + if len(normalised) != 1: + print("Unable to normalise value '%s'" % val) + mod_list.append((val, '')) + elif (normalised[0] != val): + print("value '%s' should be '%s'" % (val, normalised[0])) + mod_list.append((val, normalised[0])) + if not self.fix: + return + if not confirm(self, 'Fix normalisation for %s from %s?' % (attrname, dn)): + print("Not fixing attribute %s" % attrname) + return + + m = ldb.Message() + m.dn = dn + for i in range(0, len(mod_list)): + (val, nval) = mod_list[i] + m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + if nval != '': + m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname) + + try: + self.samdb.modify(m, controls=["relax:0"], validate=False) + except Exception, msg: + print("Failed to normalise attribute %s : %s" % (attrname, msg)) + return + print("Normalised attribute %s" % attrname) + + def check_object(self, dn): '''check one object''' if self.verbose: print("Checking object %s" % dn) - res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE) + res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) if len(res) != 1: print("Object %s disappeared during check" % dn) return @@ -71,11 +105,20 @@ def check_object(self, dn): for attrname in obj: if attrname == 'dn': continue + + # check for empty attributes for val in obj[attrname]: if val == '': empty_attribute(self, dn, attrname) continue + # check for incorrectly normalised attributes + for val in obj[attrname]: + normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) + if len(normalised) != 1 or normalised[0] != val: + normalise_mismatch(self, dn, attrname, obj[attrname]) + break + class cmd_dbcheck(Command): """check local AD database for errors""" @@ -96,11 +139,13 @@ class cmd_dbcheck(Command): help='Fix any errors found'), Option("--yes", dest="yes", default=False, action='store_true', help="don't confirm changes, just do them all"), + Option("--cross-ncs", dest="cross_ncs", default=False, action='store_true', + help="cross naming context boundaries"), Option("-v", "--verbose", dest="verbose", action="store_true", default=False, help="Print more details of checking"), ] - def run(self, DN=None, verbose=False, fix=False, yes=False, + def run(self, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False, scope="SUB", credopts=None, sambaopts=None, versionopts=None): self.lp = sambaopts.get_loadparm() self.creds = credopts.get_credentials(self.lp, fallback_machine=True) @@ -117,7 +162,12 @@ class cmd_dbcheck(Command): raise CommandError("Unknown scope %s" % scope) self.search_scope = scope_map[scope] - res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn']) + controls = [] + if cross_ncs: + controls.append("search_options:1:2") + + res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn'], controls=controls) + print('Checking %u objects' % len(res)) for object in res: check_object(self, object.dn) print('Checked %u objects' % len(res)) -- cgit From a8269792aa7c75b82b5ccab0e3b819601f7a4ef4 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 17 Jun 2011 11:34:19 +1000 Subject: samba-tool: report total error count and suggest --fix if needed --- source4/scripting/python/samba/netcmd/dbcheck.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 4eebca3503..9f12136de8 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -100,8 +100,9 @@ def check_object(self, dn): res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) if len(res) != 1: print("Object %s disappeared during check" % dn) - return + return 1 obj = res[0] + error_count = 0 for attrname in obj: if attrname == 'dn': continue @@ -110,6 +111,7 @@ def check_object(self, dn): for val in obj[attrname]: if val == '': empty_attribute(self, dn, attrname) + error_count += 1 continue # check for incorrectly normalised attributes @@ -117,7 +119,9 @@ def check_object(self, dn): normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) if len(normalised) != 1 or normalised[0] != val: normalise_mismatch(self, dn, attrname, obj[attrname]) + error_count += 1 break + return error_count class cmd_dbcheck(Command): @@ -168,6 +172,9 @@ class cmd_dbcheck(Command): res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn'], controls=controls) print('Checking %u objects' % len(res)) + error_count = 0 for object in res: - check_object(self, object.dn) - print('Checked %u objects' % len(res)) + error_count += check_object(self, object.dn) + if error_count != 0 and not self.fix: + print("Please use --fix to fix these errors") + print('Checked %u objects (%u errors)' % (len(res), error_count)) -- cgit From 0c3075cb57134f6171c332158c3052e05dace595 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 17 Jun 2011 12:02:23 +1000 Subject: s4-pysamdb: fixed the normalisation of grouptype in group add ldap integers are signed Autobuild-User: Andrew Tridgell Autobuild-Date: Fri Jun 17 05:43:18 CEST 2011 on sn-devel-104 --- source4/scripting/python/samba/samdb.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index b513ac8fff..53346e8670 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -142,7 +142,7 @@ pwdLastSet: 0 "objectClass": "group"} if grouptype is not None: - ldbmessage["groupType"] = "%d" % grouptype + ldbmessage["groupType"] = self.normalise_int32(grouptype) if description is not None: ldbmessage["description"] = description @@ -722,3 +722,9 @@ accountExpires: %u if sd: m["nTSecurityDescriptor"] = ndr_pack(sd) self.add(m) + + def normalise_int32(self, ivalue): + '''normalise a ldap integer to signed 32 bit''' + if int(ivalue) & 0x80000000: + return str(int(ivalue) - 0x100000000) + return str(ivalue) -- cgit From 705ed1c4921a1456ebcf80ac352567679ab7dfa9 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 17 Jun 2011 13:35:52 +1000 Subject: samba-tool: show success message on group operations --- source4/scripting/python/samba/netcmd/group.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/group.py b/source4/scripting/python/samba/netcmd/group.py index 620a7be866..95db21adfc 100644 --- a/source4/scripting/python/samba/netcmd/group.py +++ b/source4/scripting/python/samba/netcmd/group.py @@ -85,6 +85,7 @@ class cmd_group_add(Command): description=description, mailaddress=mail_address, notes=notes) except Exception, e: raise CommandError('Failed to create group "%s"' % groupname, e) + print("Added group %s" % groupname) class cmd_group_delete(Command): @@ -115,6 +116,7 @@ class cmd_group_delete(Command): samdb.deletegroup(groupname) except Exception, e: raise CommandError('Failed to remove group "%s"' % groupname, e) + print("Deleted group %s" % groupname) class cmd_group_add_members(Command): @@ -146,6 +148,7 @@ class cmd_group_add_members(Command): 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) + print("Added members to group %s" % groupname) class cmd_group_remove_members(Command): @@ -177,6 +180,7 @@ class cmd_group_remove_members(Command): 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) + print("Removed members from group %s" % groupname) class cmd_group(SuperCommand): -- cgit From 0b3b7e3797a9aa0dc8f0922c8cd873b0f0b3231e Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 17 Jun 2011 14:40:48 +1000 Subject: samba-tool: exit with non-zero status on dbcheck failure Pair-Programmed-With: Andrew Bartlett --- source4/scripting/python/samba/netcmd/dbcheck.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 9f12136de8..b0d77f2fd9 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -18,7 +18,7 @@ # along with this program. If not, see . # -import samba, ldb +import samba, ldb, sys import samba.getopt as options from samba.auth import system_session from samba.samdb import SamDB @@ -178,3 +178,5 @@ class cmd_dbcheck(Command): if error_count != 0 and not self.fix: print("Please use --fix to fix these errors") print('Checked %u objects (%u errors)' % (len(res), error_count)) + if error_count != 0: + sys.exit(1) -- cgit From 5db07d2f42e6bbc0023a504f30b9dcc8fd31b230 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 5 Jun 2011 17:39:32 +0400 Subject: s4-upgradeprovisision: fix bug 8063, old SD can miss some componenent (group, owner, ...) Don't make the assumption that SD are correct, they can be wrong and misformed. Fix this bug: https://bugzilla.samba.org/show_bug.cgi?id=8063 --- source4/scripting/python/samba/upgradehelpers.py | 36 +++++++++++++----------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index 16e4ea006a..ad5de73b5b 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -372,42 +372,46 @@ def get_diff_sddls(refsddl, cursddl): """ txt = "" - hash_new = chunck_sddl(cursddl) + hash_cur = chunck_sddl(cursddl) hash_ref = chunck_sddl(refsddl) - if hash_new["owner"] != hash_ref["owner"]: + if not hash_cur.has_key("owner"): + txt = "\tNo owner in current SD" + elif hash_cur["owner"] != hash_ref["owner"]: txt = "\tOwner mismatch: %s (in ref) %s" \ - "(in current)\n" % (hash_ref["owner"], hash_new["owner"]) + "(in current)\n" % (hash_ref["owner"], hash_cur["owner"]) - if hash_new["group"] != hash_ref["group"]: + if not hash_cur.has_key("group"): + txt = "%s\tNo group in current SD" % txt + elif hash_cur["group"] != hash_ref["group"]: txt = "%s\tGroup mismatch: %s (in ref) %s" \ - "(in current)\n" % (txt, hash_ref["group"], hash_new["group"]) + "(in current)\n" % (txt, hash_ref["group"], hash_cur["group"]) for part in ["dacl", "sacl"]: - if hash_new.has_key(part) and hash_ref.has_key(part): + if hash_cur.has_key(part) and hash_ref.has_key(part): # both are present, check if they contain the same ACE - h_new = set() + h_cur = set() h_ref = set() - c_new = chunck_acl(hash_new[part]) + c_cur = chunck_acl(hash_cur[part]) c_ref = chunck_acl(hash_ref[part]) - for elem in c_new["aces"]: - h_new.add(elem) + for elem in c_cur["aces"]: + h_cur.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) + if k in h_cur: + h_cur.remove(k) h_ref.remove(k) - if len(h_new) + len(h_ref) > 0: + if len(h_cur) + 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: + for item in h_cur: txt = "%s\t\t%s ACE is not present in the" \ " reference\n" % (txt, item) @@ -415,9 +419,9 @@ def get_diff_sddls(refsddl, cursddl): 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): + elif hash_cur.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): + elif not hash_cur.has_key(part) and hash_ref.has_key(part): txt = "%s\tCurrent ACL hasn't a %s part\n" % (txt, part) return txt -- cgit From 45df4d81ed37f6d470e3f42a310a6e5f0afa06bf Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 16:09:14 +0400 Subject: s4-python: fix wrong discovery of the site name in find_key_provision_parameters --- source4/scripting/python/samba/provision/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index f272872a93..0d39befc26 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -266,7 +266,7 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp) names.domaindn=current[0]["defaultNamingContext"] names.rootdn=current[0]["rootDomainNamingContext"] # default site name - res3 = samdb.search(expression="(objectClass=*)", + res3 = samdb.search(expression="(objectClass=site)", base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"]) names.sitename = str(res3[0]["cn"]) -- cgit From 20233cdf535b55ee3832d4844eb2109cccab5837 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 13 Jun 2011 17:39:06 +0400 Subject: s4-upgradeprovision: introduce invocation id in lastprovisionUSNs --- .../scripting/python/samba/provision/__init__.py | 56 +++++++++++++++------- 1 file changed, 39 insertions(+), 17 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index 0d39befc26..7c9f223d11 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -322,7 +322,7 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp) raise ProvisioningError("Unable to find uid/gid for Domain Admins rid") return names -def update_provision_usn(samdb, low, high, replace=False): +def update_provision_usn(samdb, low, high, id, replace=False): """Update the field provisionUSN in sam.ldb This field is used to track range of USN modified by provision and @@ -333,6 +333,7 @@ def update_provision_usn(samdb, low, high, replace=False): :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 id: The invocation id of the samba's dc :param replace: A boolean indicating if the range should replace any existing one or appended (default) """ @@ -344,17 +345,24 @@ def update_provision_usn(samdb, low, high, replace=False): scope=ldb.SCOPE_SUBTREE, attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"]) for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]: + if not re.search(';', e): + e = "%s;%s" % (e, id) tab.append(str(e)) - tab.append("%s-%s" % (low, high)) + tab.append("%s-%s;%s" % (low, high, id)) 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) + entry = samdb.search(expression="(&(dn=@PROVISION)(provisionnerID=*))", + base="", scope=ldb.SCOPE_SUBTREE, + attrs=["provisionnerID"]) + if len(entry) == 0 or len(entry[0]) == 0: + delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID") samdb.modify(delta) -def set_provision_usn(samdb, low, high): +def set_provision_usn(samdb, low, high, id): """Set the field provisionUSN in sam.ldb This field is used to track range of USN modified by provision and upgradeprovision. @@ -363,9 +371,12 @@ def set_provision_usn(samdb, low, high): :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 high: The highest USN modified by this upgrade + :param id: The invocationId of the provision""" + tab = [] - tab.append("%s-%s" % (low, high)) + tab.append("%s-%s;%s" % (low, high, id)) + delta = ldb.Message() delta.dn = ldb.Dn(samdb, "@PROVISION") delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab, @@ -390,25 +401,36 @@ def get_max_usn(samdb,basedn): def get_last_provision_usn(sam): - """Get the lastest USN modified by a provision or an upgradeprovision + """Get USNs ranges 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 + :return: a dictionnary which keys are invocation id and values are an array + of integer representing the different ranges """ entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % LAST_PROVISION_USN_ATTRIBUTE, base="", scope=ldb.SCOPE_SUBTREE, - attrs=[LAST_PROVISION_USN_ATTRIBUTE]) + attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"]) if len(entry): - range = [] - idx = 0 + myids = [] + range = {} p = re.compile(r'-') + if entry[0].get("provisionnerID"): + for e in entry[0]["provisionnerID"]: + myids.append(str(e)) 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 + tab1 = str(r).split(';') + if len(tab1) == 2: + id = tab1[1] + else: + id = "default" + if (len(myids) > 0 and id not in myids): + continue + tab2 = p.split(tab1[0]) + if range.get(id) == None: + range[id] = [] + range[id].append(tab2[0]) + range[id].append(tab2[1]) return range else: return None @@ -1780,9 +1802,9 @@ def provision(logger, session_info, credentials, smbconf=None, 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) + update_provision_usn(samdb, 0, maxUSN, invocationid, 1) else: - set_provision_usn(samdb, 0, maxUSN) + set_provision_usn(samdb, 0, maxUSN, invocationid) create_krb5_conf(paths.krb5conf, dnsdomain=names.dnsdomain, hostname=names.hostname, -- cgit From 7128345969927461ec281583abec3ea51bf98586 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sat, 18 Jun 2011 23:21:18 +0400 Subject: s4-python: make checks of sacl in get_diff_sddls optionnal --- source4/scripting/python/samba/upgradehelpers.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index ad5de73b5b..baba332c23 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -360,7 +360,7 @@ def chunck_sddl(sddl): return hash -def get_diff_sddls(refsddl, cursddl): +def get_diff_sddls(refsddl, cursddl, checkSacl = True): """Get the difference between 2 sddl This function split the textual representation of ACL into smaller @@ -368,6 +368,7 @@ def get_diff_sddls(refsddl, cursddl): :param refsddl: First sddl to compare :param cursddl: Second sddl to compare + :param checkSacl: If false we skip the sacl checks :return: A string that explain difference between sddls """ @@ -387,7 +388,10 @@ def get_diff_sddls(refsddl, cursddl): txt = "%s\tGroup mismatch: %s (in ref) %s" \ "(in current)\n" % (txt, hash_ref["group"], hash_cur["group"]) - for part in ["dacl", "sacl"]: + parts = [ "dacl" ] + if checkSacl: + parts.append("sacl") + for part in parts: if hash_cur.has_key(part) and hash_ref.has_key(part): # both are present, check if they contain the same ACE -- cgit From 12b379e9831131c251fde3ebebb76b00323f6bf0 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Wed, 15 Jun 2011 15:20:06 +0400 Subject: s4-upgradeprovision: handle the fact that oEMInformation might not be present --- source4/scripting/python/samba/upgradehelpers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index baba332c23..47ea660e43 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -548,7 +548,7 @@ def getOEMInfo(samdb, rootdn): """ res = samdb.search(expression="(objectClass=*)", base=str(rootdn), scope=SCOPE_BASE, attrs=["dn", "oEMInformation"]) - if len(res) > 0: + if len(res) > 0 and res[0].get("oEMInformation"): info = res[0]["oEMInformation"] return info else: @@ -565,7 +565,10 @@ def updateOEMInfo(samdb, rootdn): res = samdb.search(expression="(objectClass=*)", base=rootdn, scope=SCOPE_BASE, attrs=["dn", "oEMInformation"]) if len(res) > 0: - info = res[0]["oEMInformation"] + if res[0].get("oEMInformation"): + info = str(res[0]["oEMInformation"]) + else: + info = "" info = "%s, upgrade to %s" % (info, version) delta = ldb.Message() delta.dn = ldb.Dn(samdb, str(res[0]["dn"])) -- cgit From 15637206b9b51fea31aa267244aeb386c1ba7676 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 19 Jun 2011 00:16:54 +0400 Subject: s4-provision: Remove hard coded SD for CN=Sites container With the fix introduced by Nadya in changeset 622ef6aed82a2f2f7748c2a88535486af77487de we are now able to generate correct SD (at least the same as W2k3R2 with a Forest Level of 2003), so there is no need for this fix anymore as it makes SDs for Forest Level 2003 and lower incorrect. --- source4/scripting/python/samba/provision/__init__.py | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index 7c9f223d11..5aabd36c1a 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -94,19 +94,6 @@ def setup_path(file): # "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)" \ @@ -1318,7 +1305,6 @@ def setup_samdb(path, session_info, provision_backend, lp, names, 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, @@ -1330,7 +1316,6 @@ def setup_samdb(path, session_info, provision_backend, lp, names, "SERVERDN": names.serverdn, "FOREST_FUNCTIONALITY": str(forestFunctionality), "DOMAIN_FUNCTIONALITY": str(domainFunctionality), - "SITES_DESCRIPTOR": descr }) logger.info("Setting up display specifiers") -- cgit From 89684895ee96741cdf2f88dc812496714df70fbb Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 19 Jun 2011 01:17:44 +0400 Subject: s4-upgradeprovision: improve message output --- source4/scripting/python/samba/upgradehelpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py index 47ea660e43..e15523033f 100755 --- a/source4/scripting/python/samba/upgradehelpers.py +++ b/source4/scripting/python/samba/upgradehelpers.py @@ -440,7 +440,7 @@ def update_secrets(newsecrets_ldb, secrets_ldb, messagefunc): of the updated provision """ - messagefunc(SIMPLE, "update secrets.ldb") + messagefunc(SIMPLE, "Update of secrets.ldb") reference = newsecrets_ldb.search(expression="dn=@MODULES", base="", scope=SCOPE_SUBTREE) current = secrets_ldb.search(expression="dn=@MODULES", base="", -- cgit From 8a04863f0d630119c5f02b2708f50dd37dd37955 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 19 Jun 2011 20:43:11 +0400 Subject: s4-sambatool: use correct way to call class methods --- source4/scripting/python/samba/netcmd/dbcheck.py | 107 ++++++++++++----------- 1 file changed, 54 insertions(+), 53 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index b0d77f2fd9..f433be5a95 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -38,58 +38,6 @@ def confirm(self, msg): return v.upper() in ['Y', 'YES'] -def empty_attribute(self, dn, attrname): - '''fix empty attributes''' - print("ERROR: Empty attribute %s in %s" % (attrname, dn)) - if not self.fix: - return - if not confirm(self, 'Remove empty attribute %s from %s?' % (attrname, dn)): - print("Not fixing empty attribute %s" % attrname) - return - - m = ldb.Message() - m.dn = dn - m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname) - try: - self.samdb.modify(m, controls=["relax:0"], validate=False) - except Exception, msg: - print("Failed to remove empty attribute %s : %s" % (attrname, msg)) - return - print("Removed empty attribute %s" % attrname) - - -def normalise_mismatch(self, dn, attrname, values): - '''fix attribute normalisation errors''' - print("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) - mod_list = [] - for val in values: - normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) - if len(normalised) != 1: - print("Unable to normalise value '%s'" % val) - mod_list.append((val, '')) - elif (normalised[0] != val): - print("value '%s' should be '%s'" % (val, normalised[0])) - mod_list.append((val, normalised[0])) - if not self.fix: - return - if not confirm(self, 'Fix normalisation for %s from %s?' % (attrname, dn)): - print("Not fixing attribute %s" % attrname) - return - - m = ldb.Message() - m.dn = dn - for i in range(0, len(mod_list)): - (val, nval) = mod_list[i] - m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) - if nval != '': - m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname) - - try: - self.samdb.modify(m, controls=["relax:0"], validate=False) - except Exception, msg: - print("Failed to normalise attribute %s : %s" % (attrname, msg)) - return - print("Normalised attribute %s" % attrname) @@ -174,9 +122,62 @@ class cmd_dbcheck(Command): print('Checking %u objects' % len(res)) error_count = 0 for object in res: - error_count += check_object(self, object.dn) + error_count += self.check_object(self, object.dn) if error_count != 0 and not self.fix: print("Please use --fix to fix these errors") print('Checked %u objects (%u errors)' % (len(res), error_count)) if error_count != 0: sys.exit(1) + + def empty_attribute(self, dn, attrname): + '''fix empty attributes''' + print("ERROR: Empty attribute %s in %s" % (attrname, dn)) + if not self.fix: + return + if not confirm(self, 'Remove empty attribute %s from %s?' % (attrname, dn)): + print("Not fixing empty attribute %s" % attrname) + return + + m = ldb.Message() + m.dn = dn + m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname) + try: + self.samdb.modify(m, controls=["relax:0"], validate=False) + except Exception, msg: + print("Failed to remove empty attribute %s : %s" % (attrname, msg)) + return + print("Removed empty attribute %s" % attrname) + + + def normalise_mismatch(self, dn, attrname, values): + '''fix attribute normalisation errors''' + print("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) + mod_list = [] + for val in values: + normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) + if len(normalised) != 1: + print("Unable to normalise value '%s'" % val) + mod_list.append((val, '')) + elif (normalised[0] != val): + print("value '%s' should be '%s'" % (val, normalised[0])) + mod_list.append((val, normalised[0])) + if not self.fix: + return + if not confirm(self, 'Fix normalisation for %s from %s?' % (attrname, dn)): + print("Not fixing attribute %s" % attrname) + return + + m = ldb.Message() + m.dn = dn + for i in range(0, len(mod_list)): + (val, nval) = mod_list[i] + m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + if nval != '': + m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname) + + try: + self.samdb.modify(m, controls=["relax:0"], validate=False) + except Exception, msg: + print("Failed to normalise attribute %s : %s" % (attrname, msg)) + return + print("Normalised attribute %s" % attrname) -- cgit From db0309160740afcdfb192cb55d15f906b15e0481 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 19 Jun 2011 21:26:48 +0400 Subject: s4-samba-tool: use correct object notation ie. obj.method rather than method(obj, ...) --- source4/scripting/python/samba/netcmd/dbcheck.py | 68 +++++++++++------------- 1 file changed, 31 insertions(+), 37 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index f433be5a95..c1321d0f46 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -37,41 +37,6 @@ def confirm(self, msg): v = raw_input(msg + ' [y/N] ') return v.upper() in ['Y', 'YES'] - - - - -def check_object(self, dn): - '''check one object''' - if self.verbose: - print("Checking object %s" % dn) - res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) - if len(res) != 1: - print("Object %s disappeared during check" % dn) - return 1 - obj = res[0] - error_count = 0 - for attrname in obj: - if attrname == 'dn': - continue - - # check for empty attributes - for val in obj[attrname]: - if val == '': - empty_attribute(self, dn, attrname) - error_count += 1 - continue - - # check for incorrectly normalised attributes - for val in obj[attrname]: - normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) - if len(normalised) != 1 or normalised[0] != val: - normalise_mismatch(self, dn, attrname, obj[attrname]) - error_count += 1 - break - return error_count - - class cmd_dbcheck(Command): """check local AD database for errors""" synopsis = "dbcheck [options]" @@ -122,7 +87,7 @@ class cmd_dbcheck(Command): print('Checking %u objects' % len(res)) error_count = 0 for object in res: - error_count += self.check_object(self, object.dn) + error_count += self.check_object(object.dn) if error_count != 0 and not self.fix: print("Please use --fix to fix these errors") print('Checked %u objects (%u errors)' % (len(res), error_count)) @@ -148,6 +113,35 @@ class cmd_dbcheck(Command): return print("Removed empty attribute %s" % attrname) + def check_object(self, dn): + '''check one object''' + if self.verbose: + print("Checking object %s" % dn) + res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) + if len(res) != 1: + print("Object %s disappeared during check" % dn) + return 1 + obj = res[0] + error_count = 0 + for attrname in obj: + if attrname == 'dn': + continue + + # check for empty attributes + for val in obj[attrname]: + if val == '': + self.empty_attribute(dn, attrname) + error_count += 1 + continue + + # check for incorrectly normalised attributes + for val in obj[attrname]: + normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) + if len(normalised) != 1 or normalised[0] != val: + self.normalise_mismatch(dn, attrname, obj[attrname]) + error_count += 1 + break + return error_count def normalise_mismatch(self, dn, attrname, values): '''fix attribute normalisation errors''' @@ -163,7 +157,7 @@ class cmd_dbcheck(Command): mod_list.append((val, normalised[0])) if not self.fix: return - if not confirm(self, 'Fix normalisation for %s from %s?' % (attrname, dn)): + if not self.confirm(self, 'Fix normalisation for %s from %s?' % (attrname, dn)): print("Not fixing attribute %s" % attrname) return -- cgit From bc549575dd0f80089dcaefcefeb4211c3771b8c6 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 19 Jun 2011 21:27:32 +0400 Subject: s4-samba-tool: remove unused imports --- source4/scripting/python/samba/netcmd/dbcheck.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index c1321d0f46..5f75c3ee6c 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -18,11 +18,10 @@ # along with this program. If not, see . # -import samba, ldb, sys +import ldb, sys import samba.getopt as options from samba.auth import system_session from samba.samdb import SamDB -from samba.dcerpc import security from samba.netcmd import ( Command, CommandError, -- cgit From 9c94943d26d1076b0ef196698d7d0d2e3b0b80e4 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Sun, 19 Jun 2011 23:09:59 +0400 Subject: s4-sambatool: extract the confirm function in a separte module for reuse --- source4/scripting/python/samba/common.py | 33 ++++++++++++++++++++++++ source4/scripting/python/samba/netcmd/dbcheck.py | 13 +++------- 2 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 source4/scripting/python/samba/common.py (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/common.py b/source4/scripting/python/samba/common.py new file mode 100644 index 0000000000..a2a4962797 --- /dev/null +++ b/source4/scripting/python/samba/common.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# +# Samba common functions +# +# Copyright (C) Matthieu Patou +# +# 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 . +# + +def confirm(msg, forced = False): + """confirm an action with the user + :param msg: A string to print to the user + :param forced: Are the answer forced + """ + if forced: + print("%s [YES]" % msg) + return True + + v = raw_input(msg + ' [y/N] ') + return v.upper() in ['Y', 'YES'] + + diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 5f75c3ee6c..f28f9c6bbf 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -20,6 +20,7 @@ import ldb, sys import samba.getopt as options +from samba.common import confirm from samba.auth import system_session from samba.samdb import SamDB from samba.netcmd import ( @@ -28,14 +29,6 @@ from samba.netcmd import ( Option ) -def confirm(self, msg): - '''confirm an action with the user''' - if self.yes: - print("%s [YES]" % msg) - return True - v = raw_input(msg + ' [y/N] ') - return v.upper() in ['Y', 'YES'] - class cmd_dbcheck(Command): """check local AD database for errors""" synopsis = "dbcheck [options]" @@ -98,7 +91,7 @@ class cmd_dbcheck(Command): print("ERROR: Empty attribute %s in %s" % (attrname, dn)) if not self.fix: return - if not confirm(self, 'Remove empty attribute %s from %s?' % (attrname, dn)): + if not confirm('Remove empty attribute %s from %s?' % (attrname, dn), self.yes): print("Not fixing empty attribute %s" % attrname) return @@ -156,7 +149,7 @@ class cmd_dbcheck(Command): mod_list.append((val, normalised[0])) if not self.fix: return - if not self.confirm(self, 'Fix normalisation for %s from %s?' % (attrname, dn)): + if not confirm('Fix normalisation for %s from %s?' % (attrname, dn), self.yes): print("Not fixing attribute %s" % attrname) return -- cgit From 5eecc854236f0b943aaa89e0c3a46f9fbd208ca9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2011 18:40:33 +0930 Subject: tdb2: create tdb2 versions of various testing TDBs. Soon, TDB2 will handle tdb1 files, but until then, we substitute. Signed-off-by: Rusty Russell --- source4/scripting/python/samba/samba3.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/samba3.py b/source4/scripting/python/samba/samba3.py index 2c323bd0b4..ae5b20edd2 100644 --- a/source4/scripting/python/samba/samba3.py +++ b/source4/scripting/python/samba/samba3.py @@ -50,9 +50,12 @@ class TdbDatabase(object): def __init__(self, file): """Open a file. - :param file: Path of the file to open. + :param file: Path of the file to open (appending "2" if TDB2 enabled). """ - self.tdb = tdb.Tdb(file, flags=os.O_RDONLY) + if tdb.__version__.startswith("2"): + self.tdb = tdb.Tdb(file + "2", flags=os.O_RDONLY) + else: + self.tdb = tdb.Tdb(file, flags=os.O_RDONLY) self._check_version() def _check_version(self): -- cgit From c4a7908f46e7005f323eeca5fd38ec9e88a54aa9 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 12:23:05 +1000 Subject: samba-tool: try to keep dbcheck.py in a logical ordering keep individual error handlers together and separate from driver code --- source4/scripting/python/samba/netcmd/dbcheck.py | 67 ++++++++++++++---------- 1 file changed, 38 insertions(+), 29 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index f28f9c6bbf..4ec1365f14 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -86,6 +86,9 @@ class cmd_dbcheck(Command): if error_count != 0: sys.exit(1) + + ################################################################ + # handle empty attributes def empty_attribute(self, dn, attrname): '''fix empty attributes''' print("ERROR: Empty attribute %s in %s" % (attrname, dn)) @@ -105,36 +108,9 @@ class cmd_dbcheck(Command): return print("Removed empty attribute %s" % attrname) - def check_object(self, dn): - '''check one object''' - if self.verbose: - print("Checking object %s" % dn) - res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) - if len(res) != 1: - print("Object %s disappeared during check" % dn) - return 1 - obj = res[0] - error_count = 0 - for attrname in obj: - if attrname == 'dn': - continue - - # check for empty attributes - for val in obj[attrname]: - if val == '': - self.empty_attribute(dn, attrname) - error_count += 1 - continue - - # check for incorrectly normalised attributes - for val in obj[attrname]: - normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) - if len(normalised) != 1 or normalised[0] != val: - self.normalise_mismatch(dn, attrname, obj[attrname]) - error_count += 1 - break - return error_count + ################################################################ + # handle normalisation mismatches def normalise_mismatch(self, dn, attrname, values): '''fix attribute normalisation errors''' print("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) @@ -167,3 +143,36 @@ class cmd_dbcheck(Command): print("Failed to normalise attribute %s : %s" % (attrname, msg)) return print("Normalised attribute %s" % attrname) + + + ################################################################ + # check one object - calls to individual error handlers above + def check_object(self, dn): + '''check one object''' + if self.verbose: + print("Checking object %s" % dn) + res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) + if len(res) != 1: + print("Object %s disappeared during check" % dn) + return 1 + obj = res[0] + error_count = 0 + for attrname in obj: + if attrname == 'dn': + continue + + # check for empty attributes + for val in obj[attrname]: + if val == '': + self.empty_attribute(dn, attrname) + error_count += 1 + continue + + # check for incorrectly normalised attributes + for val in obj[attrname]: + normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) + if len(normalised) != 1 or normalised[0] != val: + self.normalise_mismatch(dn, attrname, obj[attrname]) + error_count += 1 + break + return error_count -- cgit From 202f0a4b576d78928a403b68f3e057d3a425bddf Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 14:41:50 +1000 Subject: pydsdb: added get_syntax_oid_from_lDAPDisplayName() this gives you access to the syntax oid of an attribute Pair-Programmed-With: Andrew Bartlett --- source4/scripting/python/samba/samdb.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py index 53346e8670..72ee472764 100644 --- a/source4/scripting/python/samba/samdb.py +++ b/source4/scripting/python/samba/samdb.py @@ -476,9 +476,14 @@ accountExpires: %u def get_attid_from_lDAPDisplayName(self, ldap_display_name, is_schema_nc=False): + '''return the attribute ID for a LDAP attribute as an integer as found in DRSUAPI''' return dsdb._dsdb_get_attid_from_lDAPDisplayName(self, ldap_display_name, is_schema_nc) + def get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name): + '''return the syntax OID for a LDAP attribute as a string''' + return dsdb._dsdb_get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name) + def set_ntds_settings_dn(self, ntds_settings_dn): """Set the NTDS Settings DN, as would be returned on the dsServiceName rootDSE attribute. -- cgit From 9e766f019bff74ec9c1d5df326cdea2c7fe05e2a Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 14:44:36 +1000 Subject: samba-tool: added missing GUID component checks to dbcheck Pair-Programmed-With: Andrew Bartlett Autobuild-User: Andrew Tridgell Autobuild-Date: Wed Jun 22 07:59:30 CEST 2011 on sn-devel-104 --- source4/scripting/python/samba/netcmd/dbcheck.py | 97 +++++++++++++++++++++++- 1 file changed, 93 insertions(+), 4 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 4ec1365f14..4c9e0a1af5 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -20,15 +20,42 @@ import ldb, sys import samba.getopt as options +from samba import dsdb from samba.common import confirm from samba.auth import system_session from samba.samdb import SamDB +from samba.dcerpc import misc from samba.netcmd import ( Command, CommandError, Option ) + +class dsdb_DN(object): + '''a class to manipulate DN components''' + + def __init__(self, samdb, dnstring, syntax_oid): + if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_STRING_DN ]: + colons = dnstring.split(':') + if len(colons) < 4: + raise Exception("invalid DN prefix") + prefix_len = 4 + len(colons[1]) + int(colons[1]) + self.prefix = dnstring[0:prefix_len] + self.dnstring = dnstring[prefix_len:] + else: + self.dnstring = dnstring + self.prefix = '' + try: + self.dn = ldb.Dn(samdb, self.dnstring) + except Exception, msg: + print("ERROR: bad DN string '%s'" % self.dnstring) + raise + + def __str__(self): + return self.prefix + str(self.dn.extended_str(mode=1)) + + class cmd_dbcheck(Command): """check local AD database for errors""" synopsis = "dbcheck [options]" @@ -89,7 +116,7 @@ class cmd_dbcheck(Command): ################################################################ # handle empty attributes - def empty_attribute(self, dn, attrname): + def err_empty_attribute(self, dn, attrname): '''fix empty attributes''' print("ERROR: Empty attribute %s in %s" % (attrname, dn)) if not self.fix: @@ -101,6 +128,8 @@ class cmd_dbcheck(Command): m = ldb.Message() m.dn = dn m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname) + if self.verbose: + print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) try: self.samdb.modify(m, controls=["relax:0"], validate=False) except Exception, msg: @@ -111,7 +140,7 @@ class cmd_dbcheck(Command): ################################################################ # handle normalisation mismatches - def normalise_mismatch(self, dn, attrname, values): + def err_normalise_mismatch(self, dn, attrname, values): '''fix attribute normalisation errors''' print("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) mod_list = [] @@ -137,6 +166,8 @@ class cmd_dbcheck(Command): if nval != '': m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname) + if self.verbose: + print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) try: self.samdb.modify(m, controls=["relax:0"], validate=False) except Exception, msg: @@ -145,6 +176,55 @@ class cmd_dbcheck(Command): print("Normalised attribute %s" % attrname) + ################################################################ + # handle a missing GUID extended DN component + def err_missing_dn_GUID(self, dn, attrname, val, dsdb_dn): + print("ERROR: missing GUID component for %s in object %s - %s" % (attrname, dn, val)) + try: + res = self.samdb.search(base=dsdb_dn.dn, scope=ldb.SCOPE_BASE, attrs=['objectGUID']) + except LdbError, (enum, estr): + print("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr)) + return + guid = res[0]['objectGUID'][0] + guidstr = str(misc.GUID(guid)) + dsdb_dn.dn.set_extended_component("GUID", guid) + + if not confirm('Add GUID %s giving DN %s?' % (guidstr, str(dsdb_dn))): + print("Not fixing missing GUID") + return + m = ldb.Message() + m.dn = dn + m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname) + if self.verbose: + print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + try: + self.samdb.modify(m) + except Exception, msg: + print("Failed to fix missing GUID on attribute %s : %s" % (attrname, msg)) + return + print("Fixed missing GUID on attribute %s" % attrname) + + + + ################################################################ + # specialised checking for a dn attribute + def check_dn(self, obj, attrname, syntax_oid): + '''check a DN attribute for correctness''' + error_count = 0 + for val in obj[attrname]: + dsdb_dn = dsdb_DN(self.samdb, val, syntax_oid) + + # all DNs should have a GUID component + guid = dsdb_dn.dn.get_extended_component("GUID") + if guid is None: + error_count += 1 + self.err_missing_dn_GUID(obj.dn, attrname, val, dsdb_dn) + + return 0 + + + ################################################################ # check one object - calls to individual error handlers above def check_object(self, dn): @@ -164,15 +244,24 @@ class cmd_dbcheck(Command): # check for empty attributes for val in obj[attrname]: if val == '': - self.empty_attribute(dn, attrname) + self.err_empty_attribute(dn, attrname) error_count += 1 continue + # get the syntax oid for the attribute, so we can can have + # special handling for some specific attribute types + syntax_oid = self.samdb.get_syntax_oid_from_lDAPDisplayName(attrname) + + if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME, + dsdb.DSDB_SYNTAX_STRING_DN, ldb.LDB_SYNTAX_DN ]: + # it's some form of DN, do specialised checking on those + error_count += self.check_dn(obj, attrname, syntax_oid) + # check for incorrectly normalised attributes for val in obj[attrname]: normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) if len(normalised) != 1 or normalised[0] != val: - self.normalise_mismatch(dn, attrname, obj[attrname]) + self.err_normalise_mismatch(dn, attrname, obj[attrname]) error_count += 1 break return error_count -- cgit From ff8cdeecfc28be396dcbdc4af6b7e60ab9de45f1 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 17:08:28 +1000 Subject: samba-tool: expanded dbcheck DN checking this now checks for bad GUID elements in DN links, and offers to fix them when possible Pair-Programmed-With: Andrew Bartlett --- source4/scripting/python/samba/netcmd/dbcheck.py | 125 +++++++++++++++++++---- 1 file changed, 104 insertions(+), 21 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 4c9e0a1af5..d7a492836e 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -21,7 +21,7 @@ import ldb, sys import samba.getopt as options from samba import dsdb -from samba.common import confirm +from samba import common from samba.auth import system_session from samba.samdb import SamDB from samba.dcerpc import misc @@ -74,7 +74,7 @@ class cmd_dbcheck(Command): Option("--fix", dest="fix", default=False, action='store_true', help='Fix any errors found'), Option("--yes", dest="yes", default=False, action='store_true', - help="don't confirm changes, just do them all"), + help="don't confirm changes, just do them all as a single transaction"), Option("--cross-ncs", dest="cross_ncs", default=False, action='store_true', help="cross naming context boundaries"), Option("-v", "--verbose", dest="verbose", action="store_true", default=False, @@ -102,6 +102,9 @@ class cmd_dbcheck(Command): if cross_ncs: controls.append("search_options:1:2") + if self.yes and self.fix: + self.samdb.transaction_start() + res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn'], controls=controls) print('Checking %u objects' % len(res)) error_count = 0 @@ -110,18 +113,30 @@ class cmd_dbcheck(Command): if error_count != 0 and not self.fix: print("Please use --fix to fix these errors") print('Checked %u objects (%u errors)' % (len(res), error_count)) + + if self.yes and self.fix: + self.samdb.transaction_commit() + if error_count != 0: sys.exit(1) + + ################################################################ + # a local confirm function that obeys the --fix and --yes options + def confirm(self, msg): + '''confirm a change''' + if not self.fix: + return False + return common.confirm(msg, forced=self.yes) + + ################################################################ # handle empty attributes def err_empty_attribute(self, dn, attrname): '''fix empty attributes''' print("ERROR: Empty attribute %s in %s" % (attrname, dn)) - if not self.fix: - return - if not confirm('Remove empty attribute %s from %s?' % (attrname, dn), self.yes): + if not self.confirm('Remove empty attribute %s from %s?' % (attrname, dn)): print("Not fixing empty attribute %s" % attrname) return @@ -152,9 +167,7 @@ class cmd_dbcheck(Command): elif (normalised[0] != val): print("value '%s' should be '%s'" % (val, normalised[0])) mod_list.append((val, normalised[0])) - if not self.fix: - return - if not confirm('Fix normalisation for %s from %s?' % (attrname, dn), self.yes): + if not self.confirm('Fix normalisation for %s from %s?' % (attrname, dn)): print("Not fixing attribute %s" % attrname) return @@ -178,19 +191,18 @@ class cmd_dbcheck(Command): ################################################################ # handle a missing GUID extended DN component - def err_missing_dn_GUID(self, dn, attrname, val, dsdb_dn): - print("ERROR: missing GUID component for %s in object %s - %s" % (attrname, dn, val)) + def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr): + print("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val)) try: - res = self.samdb.search(base=dsdb_dn.dn, scope=ldb.SCOPE_BASE, attrs=['objectGUID']) - except LdbError, (enum, estr): + res = self.samdb.search(base=dsdb_dn.dn, scope=ldb.SCOPE_BASE, + attrs=[], controls=["extended_dn:1:1"]) + except ldb.LdbError, (enum, estr): print("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr)) return - guid = res[0]['objectGUID'][0] - guidstr = str(misc.GUID(guid)) - dsdb_dn.dn.set_extended_component("GUID", guid) + dsdb_dn.dn = res[0].dn - if not confirm('Add GUID %s giving DN %s?' % (guidstr, str(dsdb_dn))): - print("Not fixing missing GUID") + if not self.confirm('Change DN to %s?' % str(dsdb_dn)): + print("Not fixing %s" % errstr) return m = ldb.Message() m.dn = dn @@ -201,10 +213,53 @@ class cmd_dbcheck(Command): try: self.samdb.modify(m) except Exception, msg: - print("Failed to fix missing GUID on attribute %s : %s" % (attrname, msg)) + print("Failed to fix %s on attribute %s : %s" % (errstr, attrname, msg)) return - print("Fixed missing GUID on attribute %s" % attrname) + print("Fixed %s on attribute %s" % (errstr, attrname)) + + ################################################################ + # handle a DN pointing to a deleted object + def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn): + print("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val)) + print("Target GUID points at deleted DN %s" % correct_dn) + if not self.confirm('Remove DN?'): + print("Not removing") + return + m = ldb.Message() + m.dn = dn + m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + if self.verbose: + print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + try: + self.samdb.modify(m) + except Exception, msg: + print("Failed to remove deleted DN attribute %s : %s" % (attrname, msg)) + return + print("Removed deleted DN on attribute %s" % attrname) + + + ################################################################ + # handle a DN string being incorrect + def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn): + print("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val)) + dsdb_dn.dn = correct_dn + + if not self.confirm('Change DN to %s?' % str(dsdb_dn)): + print("Not fixing %s" % errstr) + return + m = ldb.Message() + m.dn = dn + m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname) + if self.verbose: + print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + try: + self.samdb.modify(m) + except Exception, msg: + print("Failed to fix incorrect DN string on attribute %s : %s" % (attrname, msg)) + return + print("Fixed incorrect DN string on attribute %s" % (attrname)) ################################################################ @@ -219,9 +274,37 @@ class cmd_dbcheck(Command): guid = dsdb_dn.dn.get_extended_component("GUID") if guid is None: error_count += 1 - self.err_missing_dn_GUID(obj.dn, attrname, val, dsdb_dn) + self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "missing GUID") + continue + + guidstr = str(misc.GUID(guid)) + + # check its the right GUID + try: + res = self.samdb.search(base="" % guidstr, scope=ldb.SCOPE_BASE, + attrs=['isDeleted'], controls=["extended_dn:1:1", "show_deleted:1"]) + except ldb.LdbError, (enum, estr): + error_count += 1 + self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "incorrect GUID") + continue + + # the target DN might be deleted + if (dsdb_dn.prefix != "B:32:18E2EA80684F11D2B9AA00C04F79F805:" and + 'isDeleted' in res[0] and + res[0]['isDeleted'][0].upper() == "TRUE"): + # note that we don't check this for the special wellKnownObjects prefix + # for Deleted Objects, as we expect that to be deleted + error_count += 1 + self.err_deleted_dn(obj.dn, attrname, val, dsdb_dn, res[0].dn) + continue + + # check the DN matches in string form + if res[0].dn.extended_str() != dsdb_dn.dn.extended_str(): + error_count += 1 + self.err_dn_target_mismatch(obj.dn, attrname, val, dsdb_dn, res[0].dn) + continue - return 0 + return error_count -- cgit From 6d1fe054dd93b8d282fcf515fc62f5d5ab72e6a8 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 17:38:19 +1000 Subject: samba-tool: allow for running dbcheck against a remove ldap server this is useful for running it against a Windows server --- source4/scripting/python/samba/netcmd/dbcheck.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index d7a492836e..93fe3f6c37 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -79,15 +79,22 @@ class cmd_dbcheck(Command): help="cross naming context boundaries"), Option("-v", "--verbose", dest="verbose", action="store_true", default=False, help="Print more details of checking"), + Option("-H", help="LDB URL for database or target server (defaults to local SAM database)", type=str), ] - def run(self, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False, + def run(self, H=None, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False, scope="SUB", credopts=None, sambaopts=None, versionopts=None): self.lp = sambaopts.get_loadparm() self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - self.samdb = SamDB(session_info=system_session(), url=None, + self.samdb = SamDB(session_info=system_session(), url=H, credentials=self.creds, lp=self.lp) + if H is None: + self.local_samdb = self.samdb + else: + self.local_samdb = SamDB(session_info=system_session(), url=None, + credentials=self.creds, lp=self.lp) + self.verbose = verbose self.fix = fix self.yes = yes @@ -99,6 +106,8 @@ class cmd_dbcheck(Command): self.search_scope = scope_map[scope] controls = [] + if H is not None: + controls.append('paged_results:1:1000') if cross_ncs: controls.append("search_options:1:2") @@ -160,7 +169,7 @@ class cmd_dbcheck(Command): print("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) mod_list = [] for val in values: - normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) + normalised = self.samdb.dsdb_normalise_attributes(self.local_samdb, attrname, [val]) if len(normalised) != 1: print("Unable to normalise value '%s'" % val) mod_list.append((val, '')) @@ -333,7 +342,7 @@ class cmd_dbcheck(Command): # get the syntax oid for the attribute, so we can can have # special handling for some specific attribute types - syntax_oid = self.samdb.get_syntax_oid_from_lDAPDisplayName(attrname) + syntax_oid = self.local_samdb.get_syntax_oid_from_lDAPDisplayName(attrname) if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME, dsdb.DSDB_SYNTAX_STRING_DN, ldb.LDB_SYNTAX_DN ]: @@ -342,7 +351,7 @@ class cmd_dbcheck(Command): # check for incorrectly normalised attributes for val in obj[attrname]: - normalised = self.samdb.dsdb_normalise_attributes(self.samdb, attrname, [val]) + normalised = self.samdb.dsdb_normalise_attributes(self.local_samdb, attrname, [val]) if len(normalised) != 1 or normalised[0] != val: self.err_normalise_mismatch(dn, attrname, obj[attrname]) error_count += 1 -- cgit From 9be9f0e43c9312094a42efa236791dfcd95dc9f9 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 19:32:45 +1000 Subject: samba-tool: added --quiet option to dbcheck this will be used to allow for other tools (such as provision) to call into dbcheck without generating a lot of noise --- source4/scripting/python/samba/netcmd/dbcheck.py | 78 +++++++++++++----------- 1 file changed, 43 insertions(+), 35 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 93fe3f6c37..ccc6355193 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -79,10 +79,12 @@ class cmd_dbcheck(Command): help="cross naming context boundaries"), Option("-v", "--verbose", dest="verbose", action="store_true", default=False, help="Print more details of checking"), + Option("--quiet", dest="quiet", action="store_true", default=False, + help="don't print details of checking"), Option("-H", help="LDB URL for database or target server (defaults to local SAM database)", type=str), ] - def run(self, H=None, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False, + def run(self, H=None, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False, quiet=False, scope="SUB", credopts=None, sambaopts=None, versionopts=None): self.lp = sambaopts.get_loadparm() self.creds = credopts.get_credentials(self.lp, fallback_machine=True) @@ -98,6 +100,7 @@ class cmd_dbcheck(Command): self.verbose = verbose self.fix = fix self.yes = yes + self.quiet = quiet scope_map = { "SUB": ldb.SCOPE_SUBTREE, "BASE":ldb.SCOPE_BASE, "ONE":ldb.SCOPE_ONELEVEL } scope = scope.upper() @@ -115,13 +118,13 @@ class cmd_dbcheck(Command): self.samdb.transaction_start() res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn'], controls=controls) - print('Checking %u objects' % len(res)) + self.report('Checking %u objects' % len(res)) error_count = 0 for object in res: error_count += self.check_object(object.dn) if error_count != 0 and not self.fix: - print("Please use --fix to fix these errors") - print('Checked %u objects (%u errors)' % (len(res), error_count)) + self.report("Please use --fix to fix these errors") + self.report('Checked %u objects (%u errors)' % (len(res), error_count)) if self.yes and self.fix: self.samdb.transaction_commit() @@ -130,6 +133,11 @@ class cmd_dbcheck(Command): sys.exit(1) + def report(self, msg): + '''print a message unless quiet is set''' + if not self.quiet: + print(msg) + ################################################################ # a local confirm function that obeys the --fix and --yes options @@ -144,40 +152,40 @@ class cmd_dbcheck(Command): # handle empty attributes def err_empty_attribute(self, dn, attrname): '''fix empty attributes''' - print("ERROR: Empty attribute %s in %s" % (attrname, dn)) + self.report("ERROR: Empty attribute %s in %s" % (attrname, dn)) if not self.confirm('Remove empty attribute %s from %s?' % (attrname, dn)): - print("Not fixing empty attribute %s" % attrname) + self.report("Not fixing empty attribute %s" % attrname) return m = ldb.Message() m.dn = dn m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname) if self.verbose: - print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) try: self.samdb.modify(m, controls=["relax:0"], validate=False) except Exception, msg: - print("Failed to remove empty attribute %s : %s" % (attrname, msg)) + self.report("Failed to remove empty attribute %s : %s" % (attrname, msg)) return - print("Removed empty attribute %s" % attrname) + self.report("Removed empty attribute %s" % attrname) ################################################################ # handle normalisation mismatches def err_normalise_mismatch(self, dn, attrname, values): '''fix attribute normalisation errors''' - print("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) + self.report("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) mod_list = [] for val in values: normalised = self.samdb.dsdb_normalise_attributes(self.local_samdb, attrname, [val]) if len(normalised) != 1: - print("Unable to normalise value '%s'" % val) + self.report("Unable to normalise value '%s'" % val) mod_list.append((val, '')) elif (normalised[0] != val): - print("value '%s' should be '%s'" % (val, normalised[0])) + self.report("value '%s' should be '%s'" % (val, normalised[0])) mod_list.append((val, normalised[0])) if not self.confirm('Fix normalisation for %s from %s?' % (attrname, dn)): - print("Not fixing attribute %s" % attrname) + self.report("Not fixing attribute %s" % attrname) return m = ldb.Message() @@ -189,86 +197,86 @@ class cmd_dbcheck(Command): m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname) if self.verbose: - print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) try: self.samdb.modify(m, controls=["relax:0"], validate=False) except Exception, msg: - print("Failed to normalise attribute %s : %s" % (attrname, msg)) + self.report("Failed to normalise attribute %s : %s" % (attrname, msg)) return - print("Normalised attribute %s" % attrname) + self.report("Normalised attribute %s" % attrname) ################################################################ # handle a missing GUID extended DN component def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr): - print("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val)) + self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val)) try: res = self.samdb.search(base=dsdb_dn.dn, scope=ldb.SCOPE_BASE, attrs=[], controls=["extended_dn:1:1"]) except ldb.LdbError, (enum, estr): - print("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr)) + self.report("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr)) return dsdb_dn.dn = res[0].dn if not self.confirm('Change DN to %s?' % str(dsdb_dn)): - print("Not fixing %s" % errstr) + self.report("Not fixing %s" % errstr) return m = ldb.Message() m.dn = dn m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname) if self.verbose: - print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) try: self.samdb.modify(m) except Exception, msg: - print("Failed to fix %s on attribute %s : %s" % (errstr, attrname, msg)) + self.report("Failed to fix %s on attribute %s : %s" % (errstr, attrname, msg)) return - print("Fixed %s on attribute %s" % (errstr, attrname)) + self.report("Fixed %s on attribute %s" % (errstr, attrname)) ################################################################ # handle a DN pointing to a deleted object def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn): - print("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val)) - print("Target GUID points at deleted DN %s" % correct_dn) + self.report("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val)) + self.report("Target GUID points at deleted DN %s" % correct_dn) if not self.confirm('Remove DN?'): - print("Not removing") + self.report("Not removing") return m = ldb.Message() m.dn = dn m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) if self.verbose: - print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) try: self.samdb.modify(m) except Exception, msg: - print("Failed to remove deleted DN attribute %s : %s" % (attrname, msg)) + self.report("Failed to remove deleted DN attribute %s : %s" % (attrname, msg)) return - print("Removed deleted DN on attribute %s" % attrname) + self.report("Removed deleted DN on attribute %s" % attrname) ################################################################ # handle a DN string being incorrect def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn): - print("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val)) + self.report("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val)) dsdb_dn.dn = correct_dn if not self.confirm('Change DN to %s?' % str(dsdb_dn)): - print("Not fixing %s" % errstr) + self.report("Not fixing %s" % errstr) return m = ldb.Message() m.dn = dn m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname) if self.verbose: - print(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) try: self.samdb.modify(m) except Exception, msg: - print("Failed to fix incorrect DN string on attribute %s : %s" % (attrname, msg)) + self.report("Failed to fix incorrect DN string on attribute %s : %s" % (attrname, msg)) return - print("Fixed incorrect DN string on attribute %s" % (attrname)) + self.report("Fixed incorrect DN string on attribute %s" % (attrname)) ################################################################ @@ -322,10 +330,10 @@ class cmd_dbcheck(Command): def check_object(self, dn): '''check one object''' if self.verbose: - print("Checking object %s" % dn) + self.report("Checking object %s" % dn) res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) if len(res) != 1: - print("Object %s disappeared during check" % dn) + self.report("Object %s disappeared during check" % dn) return 1 obj = res[0] error_count = 0 -- cgit From 7fff636bce2576a63170bf3cc555eb85b8fefd67 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 20:01:58 +1000 Subject: samba-tool: make the dbcheck class available outside of samba-tool this will be used in provision, and probably in upgradeprovision as well --- source4/scripting/python/samba/dbchecker.py | 308 +++++++++++++++++++++++ source4/scripting/python/samba/netcmd/dbcheck.py | 301 ++-------------------- 2 files changed, 324 insertions(+), 285 deletions(-) create mode 100644 source4/scripting/python/samba/dbchecker.py (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/dbchecker.py b/source4/scripting/python/samba/dbchecker.py new file mode 100644 index 0000000000..1687c82d83 --- /dev/null +++ b/source4/scripting/python/samba/dbchecker.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python +# +# Samba4 AD database checker +# +# Copyright (C) Andrew Tridgell 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 . +# + +import ldb, sys +from samba import dsdb +from samba import common +from samba.dcerpc import misc + + +class dsdb_DN(object): + '''a class to manipulate DN components''' + + def __init__(self, samdb, dnstring, syntax_oid): + if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_STRING_DN ]: + colons = dnstring.split(':') + if len(colons) < 4: + raise Exception("invalid DN prefix") + prefix_len = 4 + len(colons[1]) + int(colons[1]) + self.prefix = dnstring[0:prefix_len] + self.dnstring = dnstring[prefix_len:] + else: + self.dnstring = dnstring + self.prefix = '' + try: + self.dn = ldb.Dn(samdb, self.dnstring) + except Exception, msg: + print("ERROR: bad DN string '%s'" % self.dnstring) + raise + + def __str__(self): + return self.prefix + str(self.dn.extended_str(mode=1)) + +class dbcheck(object): + """check a SAM database for errors""" + + def __init__(self, samdb, samdb_schema=None, verbose=False, fix=False, yes=False, quiet=False): + self.samdb = samdb + self.samdb_schema = (samdb_schema or samdb) + self.verbose = verbose + self.fix = fix + self.yes = yes + self.quiet = quiet + + def check_database(self, DN=None, scope=ldb.SCOPE_SUBTREE, controls=[]): + '''perform a database check, returning the number of errors found''' + self.search_scope = scope + + res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn'], controls=controls) + self.report('Checking %u objects' % len(res)) + error_count = 0 + for object in res: + error_count += self.check_object(object.dn) + if error_count != 0 and not self.fix: + self.report("Please use --fix to fix these errors") + self.report('Checked %u objects (%u errors)' % (len(res), error_count)) + + return error_count + + + def report(self, msg): + '''print a message unless quiet is set''' + if not self.quiet: + print(msg) + + + ################################################################ + # a local confirm function that obeys the --fix and --yes options + def confirm(self, msg): + '''confirm a change''' + if not self.fix: + return False + return common.confirm(msg, forced=self.yes) + + + ################################################################ + # handle empty attributes + def err_empty_attribute(self, dn, attrname): + '''fix empty attributes''' + self.report("ERROR: Empty attribute %s in %s" % (attrname, dn)) + if not self.confirm('Remove empty attribute %s from %s?' % (attrname, dn)): + self.report("Not fixing empty attribute %s" % attrname) + return + + m = ldb.Message() + m.dn = dn + m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname) + if self.verbose: + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + try: + self.samdb.modify(m, controls=["relax:0"], validate=False) + except Exception, msg: + self.report("Failed to remove empty attribute %s : %s" % (attrname, msg)) + return + self.report("Removed empty attribute %s" % attrname) + + + ################################################################ + # handle normalisation mismatches + def err_normalise_mismatch(self, dn, attrname, values): + '''fix attribute normalisation errors''' + self.report("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) + mod_list = [] + for val in values: + normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val]) + if len(normalised) != 1: + self.report("Unable to normalise value '%s'" % val) + mod_list.append((val, '')) + elif (normalised[0] != val): + self.report("value '%s' should be '%s'" % (val, normalised[0])) + mod_list.append((val, normalised[0])) + if not self.confirm('Fix normalisation for %s from %s?' % (attrname, dn)): + self.report("Not fixing attribute %s" % attrname) + return + + m = ldb.Message() + m.dn = dn + for i in range(0, len(mod_list)): + (val, nval) = mod_list[i] + m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + if nval != '': + m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname) + + if self.verbose: + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + try: + self.samdb.modify(m, controls=["relax:0"], validate=False) + except Exception, msg: + self.report("Failed to normalise attribute %s : %s" % (attrname, msg)) + return + self.report("Normalised attribute %s" % attrname) + + + ################################################################ + # handle a missing GUID extended DN component + def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr): + self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val)) + try: + res = self.samdb.search(base=dsdb_dn.dn, scope=ldb.SCOPE_BASE, + attrs=[], controls=["extended_dn:1:1"]) + except ldb.LdbError, (enum, estr): + self.report("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr)) + return + dsdb_dn.dn = res[0].dn + + if not self.confirm('Change DN to %s?' % str(dsdb_dn)): + self.report("Not fixing %s" % errstr) + return + m = ldb.Message() + m.dn = dn + m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname) + if self.verbose: + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + try: + self.samdb.modify(m) + except Exception, msg: + self.report("Failed to fix %s on attribute %s : %s" % (errstr, attrname, msg)) + return + self.report("Fixed %s on attribute %s" % (errstr, attrname)) + + + ################################################################ + # handle a DN pointing to a deleted object + def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn): + self.report("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val)) + self.report("Target GUID points at deleted DN %s" % correct_dn) + if not self.confirm('Remove DN?'): + self.report("Not removing") + return + m = ldb.Message() + m.dn = dn + m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + if self.verbose: + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + try: + self.samdb.modify(m) + except Exception, msg: + self.report("Failed to remove deleted DN attribute %s : %s" % (attrname, msg)) + return + self.report("Removed deleted DN on attribute %s" % attrname) + + + ################################################################ + # handle a DN string being incorrect + def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn): + self.report("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val)) + dsdb_dn.dn = correct_dn + + if not self.confirm('Change DN to %s?' % str(dsdb_dn)): + self.report("Not fixing %s" % errstr) + return + m = ldb.Message() + m.dn = dn + m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) + m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname) + if self.verbose: + self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) + try: + self.samdb.modify(m) + except Exception, msg: + self.report("Failed to fix incorrect DN string on attribute %s : %s" % (attrname, msg)) + return + self.report("Fixed incorrect DN string on attribute %s" % (attrname)) + + + ################################################################ + # specialised checking for a dn attribute + def check_dn(self, obj, attrname, syntax_oid): + '''check a DN attribute for correctness''' + error_count = 0 + for val in obj[attrname]: + dsdb_dn = dsdb_DN(self.samdb, val, syntax_oid) + + # all DNs should have a GUID component + guid = dsdb_dn.dn.get_extended_component("GUID") + if guid is None: + error_count += 1 + self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "missing GUID") + continue + + guidstr = str(misc.GUID(guid)) + + # check its the right GUID + try: + res = self.samdb.search(base="" % guidstr, scope=ldb.SCOPE_BASE, + attrs=['isDeleted'], controls=["extended_dn:1:1", "show_deleted:1"]) + except ldb.LdbError, (enum, estr): + error_count += 1 + self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "incorrect GUID") + continue + + # the target DN might be deleted + if (dsdb_dn.prefix != "B:32:18E2EA80684F11D2B9AA00C04F79F805:" and + 'isDeleted' in res[0] and + res[0]['isDeleted'][0].upper() == "TRUE"): + # note that we don't check this for the special wellKnownObjects prefix + # for Deleted Objects, as we expect that to be deleted + error_count += 1 + self.err_deleted_dn(obj.dn, attrname, val, dsdb_dn, res[0].dn) + continue + + # check the DN matches in string form + if res[0].dn.extended_str() != dsdb_dn.dn.extended_str(): + error_count += 1 + self.err_dn_target_mismatch(obj.dn, attrname, val, dsdb_dn, res[0].dn) + continue + + return error_count + + + + ################################################################ + # check one object - calls to individual error handlers above + def check_object(self, dn): + '''check one object''' + if self.verbose: + self.report("Checking object %s" % dn) + res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) + if len(res) != 1: + self.report("Object %s disappeared during check" % dn) + return 1 + obj = res[0] + error_count = 0 + for attrname in obj: + if attrname == 'dn': + continue + + # check for empty attributes + for val in obj[attrname]: + if val == '': + self.err_empty_attribute(dn, attrname) + error_count += 1 + continue + + # get the syntax oid for the attribute, so we can can have + # special handling for some specific attribute types + syntax_oid = self.samdb_schema.get_syntax_oid_from_lDAPDisplayName(attrname) + + if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME, + dsdb.DSDB_SYNTAX_STRING_DN, ldb.LDB_SYNTAX_DN ]: + # it's some form of DN, do specialised checking on those + error_count += self.check_dn(obj, attrname, syntax_oid) + + # check for incorrectly normalised attributes + for val in obj[attrname]: + normalised = self.samdb.dsdb_normalise_attributes(self.samdb_schema, attrname, [val]) + if len(normalised) != 1 or normalised[0] != val: + self.err_normalise_mismatch(dn, attrname, obj[attrname]) + error_count += 1 + break + return error_count diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index ccc6355193..4a4fe9cb84 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -20,40 +20,14 @@ import ldb, sys import samba.getopt as options -from samba import dsdb -from samba import common from samba.auth import system_session from samba.samdb import SamDB -from samba.dcerpc import misc from samba.netcmd import ( Command, CommandError, Option ) - - -class dsdb_DN(object): - '''a class to manipulate DN components''' - - def __init__(self, samdb, dnstring, syntax_oid): - if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_STRING_DN ]: - colons = dnstring.split(':') - if len(colons) < 4: - raise Exception("invalid DN prefix") - prefix_len = 4 + len(colons[1]) + int(colons[1]) - self.prefix = dnstring[0:prefix_len] - self.dnstring = dnstring[prefix_len:] - else: - self.dnstring = dnstring - self.prefix = '' - try: - self.dn = ldb.Dn(samdb, self.dnstring) - except Exception, msg: - print("ERROR: bad DN string '%s'" % self.dnstring) - raise - - def __str__(self): - return self.prefix + str(self.dn.extended_str(mode=1)) +from samba.dbchecker import dbcheck class cmd_dbcheck(Command): @@ -86,27 +60,23 @@ class cmd_dbcheck(Command): def run(self, H=None, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False, quiet=False, scope="SUB", credopts=None, sambaopts=None, versionopts=None): - self.lp = sambaopts.get_loadparm() - self.creds = credopts.get_credentials(self.lp, fallback_machine=True) - self.samdb = SamDB(session_info=system_session(), url=H, - credentials=self.creds, lp=self.lp) + lp = sambaopts.get_loadparm() + creds = credopts.get_credentials(lp, fallback_machine=True) + + samdb = SamDB(session_info=system_session(), url=H, + credentials=creds, lp=lp) if H is None: - self.local_samdb = self.samdb + samdb_schema = samdb else: - self.local_samdb = SamDB(session_info=system_session(), url=None, - credentials=self.creds, lp=self.lp) - - self.verbose = verbose - self.fix = fix - self.yes = yes - self.quiet = quiet + samdb_schema = SamDB(session_info=system_session(), url=None, + credentials=creds, lp=lp) scope_map = { "SUB": ldb.SCOPE_SUBTREE, "BASE":ldb.SCOPE_BASE, "ONE":ldb.SCOPE_ONELEVEL } scope = scope.upper() if not scope in scope_map: raise CommandError("Unknown scope %s" % scope) - self.search_scope = scope_map[scope] + search_scope = scope_map[scope] controls = [] if H is not None: @@ -114,254 +84,15 @@ class cmd_dbcheck(Command): if cross_ncs: controls.append("search_options:1:2") - if self.yes and self.fix: - self.samdb.transaction_start() + if yes and fix: + samdb.transaction_start() - res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn'], controls=controls) - self.report('Checking %u objects' % len(res)) - error_count = 0 - for object in res: - error_count += self.check_object(object.dn) - if error_count != 0 and not self.fix: - self.report("Please use --fix to fix these errors") - self.report('Checked %u objects (%u errors)' % (len(res), error_count)) + chk = dbcheck(samdb, samdb_schema=samdb_schema, verbose=verbose, fix=fix, yes=yes, quiet=quiet) + error_count = chk.check_database(DN=DN, scope=search_scope, controls=controls) - if self.yes and self.fix: - self.samdb.transaction_commit() + if yes and fix: + samdb.transaction_commit() if error_count != 0: sys.exit(1) - - def report(self, msg): - '''print a message unless quiet is set''' - if not self.quiet: - print(msg) - - - ################################################################ - # a local confirm function that obeys the --fix and --yes options - def confirm(self, msg): - '''confirm a change''' - if not self.fix: - return False - return common.confirm(msg, forced=self.yes) - - - ################################################################ - # handle empty attributes - def err_empty_attribute(self, dn, attrname): - '''fix empty attributes''' - self.report("ERROR: Empty attribute %s in %s" % (attrname, dn)) - if not self.confirm('Remove empty attribute %s from %s?' % (attrname, dn)): - self.report("Not fixing empty attribute %s" % attrname) - return - - m = ldb.Message() - m.dn = dn - m[attrname] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, attrname) - if self.verbose: - self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) - try: - self.samdb.modify(m, controls=["relax:0"], validate=False) - except Exception, msg: - self.report("Failed to remove empty attribute %s : %s" % (attrname, msg)) - return - self.report("Removed empty attribute %s" % attrname) - - - ################################################################ - # handle normalisation mismatches - def err_normalise_mismatch(self, dn, attrname, values): - '''fix attribute normalisation errors''' - self.report("ERROR: Normalisation error for attribute %s in %s" % (attrname, dn)) - mod_list = [] - for val in values: - normalised = self.samdb.dsdb_normalise_attributes(self.local_samdb, attrname, [val]) - if len(normalised) != 1: - self.report("Unable to normalise value '%s'" % val) - mod_list.append((val, '')) - elif (normalised[0] != val): - self.report("value '%s' should be '%s'" % (val, normalised[0])) - mod_list.append((val, normalised[0])) - if not self.confirm('Fix normalisation for %s from %s?' % (attrname, dn)): - self.report("Not fixing attribute %s" % attrname) - return - - m = ldb.Message() - m.dn = dn - for i in range(0, len(mod_list)): - (val, nval) = mod_list[i] - m['value_%u' % i] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) - if nval != '': - m['normv_%u' % i] = ldb.MessageElement(nval, ldb.FLAG_MOD_ADD, attrname) - - if self.verbose: - self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) - try: - self.samdb.modify(m, controls=["relax:0"], validate=False) - except Exception, msg: - self.report("Failed to normalise attribute %s : %s" % (attrname, msg)) - return - self.report("Normalised attribute %s" % attrname) - - - ################################################################ - # handle a missing GUID extended DN component - def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr): - self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val)) - try: - res = self.samdb.search(base=dsdb_dn.dn, scope=ldb.SCOPE_BASE, - attrs=[], controls=["extended_dn:1:1"]) - except ldb.LdbError, (enum, estr): - self.report("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr)) - return - dsdb_dn.dn = res[0].dn - - if not self.confirm('Change DN to %s?' % str(dsdb_dn)): - self.report("Not fixing %s" % errstr) - return - m = ldb.Message() - m.dn = dn - m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) - m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname) - if self.verbose: - self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) - try: - self.samdb.modify(m) - except Exception, msg: - self.report("Failed to fix %s on attribute %s : %s" % (errstr, attrname, msg)) - return - self.report("Fixed %s on attribute %s" % (errstr, attrname)) - - - ################################################################ - # handle a DN pointing to a deleted object - def err_deleted_dn(self, dn, attrname, val, dsdb_dn, correct_dn): - self.report("ERROR: target DN is deleted for %s in object %s - %s" % (attrname, dn, val)) - self.report("Target GUID points at deleted DN %s" % correct_dn) - if not self.confirm('Remove DN?'): - self.report("Not removing") - return - m = ldb.Message() - m.dn = dn - m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) - if self.verbose: - self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) - try: - self.samdb.modify(m) - except Exception, msg: - self.report("Failed to remove deleted DN attribute %s : %s" % (attrname, msg)) - return - self.report("Removed deleted DN on attribute %s" % attrname) - - - ################################################################ - # handle a DN string being incorrect - def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn): - self.report("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val)) - dsdb_dn.dn = correct_dn - - if not self.confirm('Change DN to %s?' % str(dsdb_dn)): - self.report("Not fixing %s" % errstr) - return - m = ldb.Message() - m.dn = dn - m['old_value'] = ldb.MessageElement(val, ldb.FLAG_MOD_DELETE, attrname) - m['new_value'] = ldb.MessageElement(str(dsdb_dn), ldb.FLAG_MOD_ADD, attrname) - if self.verbose: - self.report(self.samdb.write_ldif(m, ldb.CHANGETYPE_MODIFY)) - try: - self.samdb.modify(m) - except Exception, msg: - self.report("Failed to fix incorrect DN string on attribute %s : %s" % (attrname, msg)) - return - self.report("Fixed incorrect DN string on attribute %s" % (attrname)) - - - ################################################################ - # specialised checking for a dn attribute - def check_dn(self, obj, attrname, syntax_oid): - '''check a DN attribute for correctness''' - error_count = 0 - for val in obj[attrname]: - dsdb_dn = dsdb_DN(self.samdb, val, syntax_oid) - - # all DNs should have a GUID component - guid = dsdb_dn.dn.get_extended_component("GUID") - if guid is None: - error_count += 1 - self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "missing GUID") - continue - - guidstr = str(misc.GUID(guid)) - - # check its the right GUID - try: - res = self.samdb.search(base="" % guidstr, scope=ldb.SCOPE_BASE, - attrs=['isDeleted'], controls=["extended_dn:1:1", "show_deleted:1"]) - except ldb.LdbError, (enum, estr): - error_count += 1 - self.err_incorrect_dn_GUID(obj.dn, attrname, val, dsdb_dn, "incorrect GUID") - continue - - # the target DN might be deleted - if (dsdb_dn.prefix != "B:32:18E2EA80684F11D2B9AA00C04F79F805:" and - 'isDeleted' in res[0] and - res[0]['isDeleted'][0].upper() == "TRUE"): - # note that we don't check this for the special wellKnownObjects prefix - # for Deleted Objects, as we expect that to be deleted - error_count += 1 - self.err_deleted_dn(obj.dn, attrname, val, dsdb_dn, res[0].dn) - continue - - # check the DN matches in string form - if res[0].dn.extended_str() != dsdb_dn.dn.extended_str(): - error_count += 1 - self.err_dn_target_mismatch(obj.dn, attrname, val, dsdb_dn, res[0].dn) - continue - - return error_count - - - - ################################################################ - # check one object - calls to individual error handlers above - def check_object(self, dn): - '''check one object''' - if self.verbose: - self.report("Checking object %s" % dn) - res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) - if len(res) != 1: - self.report("Object %s disappeared during check" % dn) - return 1 - obj = res[0] - error_count = 0 - for attrname in obj: - if attrname == 'dn': - continue - - # check for empty attributes - for val in obj[attrname]: - if val == '': - self.err_empty_attribute(dn, attrname) - error_count += 1 - continue - - # get the syntax oid for the attribute, so we can can have - # special handling for some specific attribute types - syntax_oid = self.local_samdb.get_syntax_oid_from_lDAPDisplayName(attrname) - - if syntax_oid in [ dsdb.DSDB_SYNTAX_BINARY_DN, dsdb.DSDB_SYNTAX_OR_NAME, - dsdb.DSDB_SYNTAX_STRING_DN, ldb.LDB_SYNTAX_DN ]: - # it's some form of DN, do specialised checking on those - error_count += self.check_dn(obj, attrname, syntax_oid) - - # check for incorrectly normalised attributes - for val in obj[attrname]: - normalised = self.samdb.dsdb_normalise_attributes(self.local_samdb, attrname, [val]) - if len(normalised) != 1 or normalised[0] != val: - self.err_normalise_mismatch(dn, attrname, obj[attrname]) - error_count += 1 - break - return error_count -- cgit From 9676c26fdd7ca53405abd06f58ae40d39d818e4d Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 20:44:35 +1000 Subject: samba-tool: added --attrs option to dbcheck this allows checking of a specific list of attributes --- source4/scripting/python/samba/dbchecker.py | 13 +++++++------ source4/scripting/python/samba/netcmd/dbcheck.py | 10 ++++++++-- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/dbchecker.py b/source4/scripting/python/samba/dbchecker.py index 1687c82d83..38c3549245 100644 --- a/source4/scripting/python/samba/dbchecker.py +++ b/source4/scripting/python/samba/dbchecker.py @@ -58,15 +58,14 @@ class dbcheck(object): self.yes = yes self.quiet = quiet - def check_database(self, DN=None, scope=ldb.SCOPE_SUBTREE, controls=[]): + def check_database(self, DN=None, scope=ldb.SCOPE_SUBTREE, controls=[], attrs=['*']): '''perform a database check, returning the number of errors found''' - self.search_scope = scope - res = self.samdb.search(base=DN, scope=self.search_scope, attrs=['dn'], controls=controls) + res = self.samdb.search(base=DN, scope=scope, attrs=['dn'], controls=controls) self.report('Checking %u objects' % len(res)) error_count = 0 for object in res: - error_count += self.check_object(object.dn) + error_count += self.check_object(object.dn, attrs=attrs) if error_count != 0 and not self.fix: self.report("Please use --fix to fix these errors") self.report('Checked %u objects (%u errors)' % (len(res), error_count)) @@ -86,6 +85,8 @@ class dbcheck(object): '''confirm a change''' if not self.fix: return False + if self.quiet: + return self.yes return common.confirm(msg, forced=self.yes) @@ -268,11 +269,11 @@ class dbcheck(object): ################################################################ # check one object - calls to individual error handlers above - def check_object(self, dn): + def check_object(self, dn, attrs=['*']): '''check one object''' if self.verbose: self.report("Checking object %s" % dn) - res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=['*', 'ntSecurityDescriptor']) + res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"], attrs=attrs) if len(res) != 1: self.report("Object %s disappeared during check" % dn) return 1 diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index 4a4fe9cb84..b8a0055d3a 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -55,11 +55,12 @@ class cmd_dbcheck(Command): help="Print more details of checking"), Option("--quiet", dest="quiet", action="store_true", default=False, help="don't print details of checking"), + Option("--attrs", dest="attrs", default=None, help="list of attributes to check (space separated)"), Option("-H", help="LDB URL for database or target server (defaults to local SAM database)", type=str), ] def run(self, H=None, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False, quiet=False, - scope="SUB", credopts=None, sambaopts=None, versionopts=None): + scope="SUB", credopts=None, sambaopts=None, versionopts=None, attrs=None): lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp, fallback_machine=True) @@ -84,11 +85,16 @@ class cmd_dbcheck(Command): if cross_ncs: controls.append("search_options:1:2") + if not attrs: + attrs = ['*'] + else: + attrs = attrs.split() + if yes and fix: samdb.transaction_start() chk = dbcheck(samdb, samdb_schema=samdb_schema, verbose=verbose, fix=fix, yes=yes, quiet=quiet) - error_count = chk.check_database(DN=DN, scope=search_scope, controls=controls) + error_count = chk.check_database(DN=DN, scope=search_scope, controls=controls, attrs=attrs) if yes and fix: samdb.transaction_commit() -- cgit From 6b939f4a9c19cd868ac1b6d77cc26662e2726e8c Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 20:53:44 +1000 Subject: dbchecker: when fixing a bad GUID in a DN, search by the string DN --- source4/scripting/python/samba/dbchecker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/dbchecker.py b/source4/scripting/python/samba/dbchecker.py index 38c3549245..2594e9a1fe 100644 --- a/source4/scripting/python/samba/dbchecker.py +++ b/source4/scripting/python/samba/dbchecker.py @@ -153,7 +153,7 @@ class dbcheck(object): def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr): self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val)) try: - res = self.samdb.search(base=dsdb_dn.dn, scope=ldb.SCOPE_BASE, + res = self.samdb.search(base=str(dsdb_dn.dn), scope=ldb.SCOPE_BASE, attrs=[], controls=["extended_dn:1:1"]) except ldb.LdbError, (enum, estr): self.report("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr)) -- cgit From 4fe9ebc2e3e09befe8d7a2ce577336eefd9b9694 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 21:22:39 +1000 Subject: dbchecker: fixed argument error for -H and DN --- source4/scripting/python/samba/netcmd/dbcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/netcmd/dbcheck.py b/source4/scripting/python/samba/netcmd/dbcheck.py index b8a0055d3a..3cc50eb814 100644 --- a/source4/scripting/python/samba/netcmd/dbcheck.py +++ b/source4/scripting/python/samba/netcmd/dbcheck.py @@ -59,7 +59,7 @@ class cmd_dbcheck(Command): Option("-H", help="LDB URL for database or target server (defaults to local SAM database)", type=str), ] - def run(self, H=None, DN=None, verbose=False, fix=False, yes=False, cross_ncs=False, quiet=False, + def run(self, DN=None, H=None, verbose=False, fix=False, yes=False, cross_ncs=False, quiet=False, scope="SUB", credopts=None, sambaopts=None, versionopts=None, attrs=None): lp = sambaopts.get_loadparm() -- cgit From 2f4251c389f5fa92bfba10739677a760f0bdf198 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 22 Jun 2011 22:06:18 +1000 Subject: dbchecker: cope with a broken link to Deleted Objects if a DN link to Deleted Objects has a bad GUID, we need to use show_deleted --- source4/scripting/python/samba/dbchecker.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/dbchecker.py b/source4/scripting/python/samba/dbchecker.py index 2594e9a1fe..d7680eda94 100644 --- a/source4/scripting/python/samba/dbchecker.py +++ b/source4/scripting/python/samba/dbchecker.py @@ -147,14 +147,21 @@ class dbcheck(object): return self.report("Normalised attribute %s" % attrname) + def is_deleted_objects_dn(self, dsdb_dn): + '''see if a dsdb_DN is the special Deleted Objects DN''' + return dsdb_dn.prefix == "B:32:18E2EA80684F11D2B9AA00C04F79F805:" + ################################################################ # handle a missing GUID extended DN component def err_incorrect_dn_GUID(self, dn, attrname, val, dsdb_dn, errstr): self.report("ERROR: %s component for %s in object %s - %s" % (errstr, attrname, dn, val)) + controls=["extended_dn:1:1"] + if self.is_deleted_objects_dn(dsdb_dn): + controls.append("show_deleted:1") try: res = self.samdb.search(base=str(dsdb_dn.dn), scope=ldb.SCOPE_BASE, - attrs=[], controls=["extended_dn:1:1"]) + attrs=[], controls=controls) except ldb.LdbError, (enum, estr): self.report("unable to find object for DN %s - cannot fix (%s)" % (dsdb_dn.dn, estr)) return @@ -248,7 +255,7 @@ class dbcheck(object): continue # the target DN might be deleted - if (dsdb_dn.prefix != "B:32:18E2EA80684F11D2B9AA00C04F79F805:" and + if ((not self.is_deleted_objects_dn(dsdb_dn)) and 'isDeleted' in res[0] and res[0]['isDeleted'][0].upper() == "TRUE"): # note that we don't check this for the special wellKnownObjects prefix -- cgit From ef7940f7be7de238a693cfba649faf8b67b7da3a Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Wed, 22 Jun 2011 21:28:00 +0400 Subject: s4-dbcheck: remove unused include --- source4/scripting/python/samba/dbchecker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/dbchecker.py b/source4/scripting/python/samba/dbchecker.py index d7680eda94..dd76cd878d 100644 --- a/source4/scripting/python/samba/dbchecker.py +++ b/source4/scripting/python/samba/dbchecker.py @@ -18,7 +18,7 @@ # along with this program. If not, see . # -import ldb, sys +import ldb from samba import dsdb from samba import common from samba.dcerpc import misc -- cgit From a9e45923369e3171cb7f42284f52ce3c4c8b0a4b Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Wed, 22 Jun 2011 21:28:25 +0400 Subject: s4-dbcheck: fix uninitialized errstr in err_dn_target_mismatch Autobuild-User: Matthieu Patou Autobuild-Date: Wed Jun 22 21:22:27 CEST 2011 on sn-devel-104 --- source4/scripting/python/samba/dbchecker.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'source4/scripting/python') diff --git a/source4/scripting/python/samba/dbchecker.py b/source4/scripting/python/samba/dbchecker.py index dd76cd878d..88fd0edf00 100644 --- a/source4/scripting/python/samba/dbchecker.py +++ b/source4/scripting/python/samba/dbchecker.py @@ -207,7 +207,7 @@ class dbcheck(object): ################################################################ # handle a DN string being incorrect - def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn): + def err_dn_target_mismatch(self, dn, attrname, val, dsdb_dn, correct_dn, errstr): self.report("ERROR: incorrect DN string component for %s in object %s - %s" % (attrname, dn, val)) dsdb_dn.dn = correct_dn @@ -267,7 +267,8 @@ class dbcheck(object): # check the DN matches in string form if res[0].dn.extended_str() != dsdb_dn.dn.extended_str(): error_count += 1 - self.err_dn_target_mismatch(obj.dn, attrname, val, dsdb_dn, res[0].dn) + self.err_dn_target_mismatch(obj.dn, attrname, val, dsdb_dn, + res[0].dn, "incorrect string version of DN") continue return error_count -- cgit