summaryrefslogtreecommitdiffstats
path: root/source4/scripting
diff options
context:
space:
mode:
Diffstat (limited to 'source4/scripting')
-rwxr-xr-xsource4/scripting/bin/findprovisionusnranges174
-rwxr-xr-xsource4/scripting/bin/renamedc200
-rwxr-xr-xsource4/scripting/bin/samba_dnsupdate44
-rwxr-xr-xsource4/scripting/bin/samba_spnupdate6
-rwxr-xr-xsource4/scripting/bin/setup_dns.sh2
-rwxr-xr-xsource4/scripting/bin/testparm219
-rwxr-xr-xsource4/scripting/bin/upgradeprovision977
-rw-r--r--source4/scripting/bin/wscript_build5
-rwxr-xr-xsource4/scripting/devel/chgtdcpass3
-rwxr-xr-xsource4/scripting/devel/demodirsync.py156
-rw-r--r--source4/scripting/devel/selftest-vars.sh24
-rw-r--r--source4/scripting/python/pyglue.c44
-rw-r--r--source4/scripting/python/samba/__init__.py7
-rw-r--r--source4/scripting/python/samba/common.py33
-rw-r--r--source4/scripting/python/samba/dbchecker.py317
-rw-r--r--source4/scripting/python/samba/hostconfig.py2
-rw-r--r--source4/scripting/python/samba/idmap.py2
-rw-r--r--source4/scripting/python/samba/join.py11
-rw-r--r--source4/scripting/python/samba/netcmd/__init__.py7
-rw-r--r--source4/scripting/python/samba/netcmd/dbcheck.py104
-rw-r--r--source4/scripting/python/samba/netcmd/drs.py40
-rw-r--r--source4/scripting/python/samba/netcmd/enableaccount.py60
-rw-r--r--source4/scripting/python/samba/netcmd/gpo.py2
-rw-r--r--source4/scripting/python/samba/netcmd/group.py4
-rw-r--r--source4/scripting/python/samba/netcmd/join.py24
-rw-r--r--source4/scripting/python/samba/netcmd/setexpiry.py67
-rw-r--r--source4/scripting/python/samba/netcmd/user.py89
-rw-r--r--source4/scripting/python/samba/provision/__init__.py251
-rw-r--r--source4/scripting/python/samba/samba3.py7
-rw-r--r--source4/scripting/python/samba/samdb.py26
-rw-r--r--source4/scripting/python/samba/tests/samba3sam.py312
-rw-r--r--source4/scripting/python/samba/tests/strings.py104
-rw-r--r--source4/scripting/python/samba/tests/unicodenames.py31
-rw-r--r--source4/scripting/python/samba/tests/upgradeprovisionneeddc.py4
-rwxr-xr-xsource4/scripting/python/samba/upgradehelpers.py166
-rw-r--r--source4/scripting/wscript_build4
36 files changed, 2374 insertions, 1154 deletions
diff --git a/source4/scripting/bin/findprovisionusnranges b/source4/scripting/bin/findprovisionusnranges
new file mode 100755
index 00000000000..c91e42e936e
--- /dev/null
+++ b/source4/scripting/bin/findprovisionusnranges
@@ -0,0 +1,174 @@
+#!/usr/bin/python
+#
+# Helper for determining USN ranges created of modified by provision and
+# upgradeprovision.
+# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2011
+#
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import sys
+import optparse
+import tempfile
+sys.path.insert(0, "bin/python")
+
+from samba.credentials import DONT_USE_KERBEROS
+from samba.auth import system_session
+from samba import Ldb
+import ldb
+
+import samba.getopt as options
+from samba import param
+from samba import _glue
+from samba.upgradehelpers import get_paths
+from samba.ndr import ndr_unpack
+from samba.dcerpc import drsblobs, misc
+
+parser = optparse.OptionParser("provision [options]")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+parser.add_option("--storedir", type="string", help="Directory where to store result files")
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+opts = parser.parse_args()[0]
+lp = sambaopts.get_loadparm()
+smbconf = lp.configfile
+
+creds = credopts.get_credentials(lp)
+creds.set_kerberos_state(DONT_USE_KERBEROS)
+session = system_session()
+paths = get_paths(param, smbconf=smbconf)
+basedn="DC=" + lp.get("realm").replace(".",",DC=")
+samdb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp)
+
+hash_id = {}
+ldif = ""
+nb_obj = 0
+
+res = samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
+
+invocation = None
+if res and len(res) == 1 and res[0]["dsServiceName"] != None:
+ dn = ldb.Dn(samdb, str(res[0]["dsServiceName"]))
+ res = samdb.search(base=str(dn), scope=ldb.SCOPE_BASE, attrs=["invocationId"],
+ controls=["search_options:1:2"])
+
+ if res and len(res) == 1 and res[0]["invocationId"]:
+ invocation = str(ndr_unpack(misc.GUID, res[0]["invocationId"][0]))
+ else:
+ print "Unable to find invocation ID"
+ sys.exit(1)
+else:
+ print "Unable to find attribute dsServiceName in rootDSE"
+ sys.exit(1)
+
+res = samdb.search(base=basedn, expression="objectClass=*",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData"],
+ controls=["search_options:1:2"])
+
+for e in res:
+ nb_obj = nb_obj + 1
+ obj = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(e["replPropertyMetaData"])).ctr
+
+ for o in obj.array:
+ # like a timestamp but with the resolution of 1 minute
+ minutestamp =_glue.nttime2unix(o.originating_change_time)/60
+ hash_ts = hash_id.get(str(o.originating_invocation_id))
+ if hash_ts == None:
+ ob = {}
+ ob["min"] = o.originating_usn
+ ob["max"] = o.originating_usn
+ ob["num"] = 1
+ ob["list"] = [str(e.dn)]
+ hash_ts = {}
+ else:
+ ob = hash_ts.get(minutestamp)
+ if ob == None:
+ ob = {}
+ ob["min"] = o.originating_usn
+ ob["max"] = o.originating_usn
+ ob["num"] = 1
+ ob["list"] = [str(e.dn)]
+ else:
+ if ob["min"] > o.originating_usn:
+ ob["min"] = o.originating_usn
+ if ob["max"] < o.originating_usn:
+ ob["max"] = o.originating_usn
+ if not (str(e.dn) in ob["list"]):
+ ob["num"] = ob["num"] + 1
+ ob["list"].append(str(e.dn))
+ hash_ts[minutestamp] = ob
+ hash_id[str(o.originating_invocation_id)] = hash_ts
+
+minobj = 5
+print "Here is a list of changes that modified more than %d objects in 1 minute." % minobj
+print "Usually changes made by provision and upgradeprovision are those who affect a couple"\
+ " of hundred of objects or more"
+print "Total number of objects: %d" % nb_obj
+print
+
+for id in hash_id:
+ hash_ts = hash_id[id]
+ sorted_keys = []
+ sorted_keys.extend(hash_ts.keys())
+ sorted_keys.sort()
+
+ kept_record = []
+ for k in sorted_keys:
+ obj = hash_ts[k]
+ if obj["num"] > minobj:
+ dt = _glue.nttime2string(_glue.unix2nttime(k*60))
+ print "%s # of modification: %d \tmin: %d max: %d" % (dt , obj["num"],
+ obj["min"],
+ obj["max"])
+ if hash_ts[k]["num"] > 600:
+ kept_record.append(k)
+
+ # Let's try to concatenate consecutive block if they are in the almost same minutestamp
+ for i in range(0, len(kept_record)):
+ if i != 0:
+ key1 = kept_record[i]
+ key2 = kept_record[i-1]
+ if key1 - key2 == 1:
+ # previous record is just 1 minute away from current
+ if int(hash_ts[key1]["min"]) == int(hash_ts[key2]["max"]) + 1:
+ # Copy the highest USN in the previous record
+ # and mark the current as skipped
+ hash_ts[key2]["max"] = hash_ts[key1]["max"]
+ hash_ts[key1]["skipped"] = True
+
+ for k in kept_record:
+ obj = hash_ts[k]
+ if obj.get("skipped") == None:
+ ldif = "%slastProvisionUSN: %d-%d;%s\n" % (ldif, obj["min"],
+ obj["max"], id)
+
+if ldif != "":
+ dest = opts.storedir
+ if dest == None:
+ dest = "/tmp"
+
+ file = tempfile.mktemp(dir=dest, prefix="usnprov", suffix=".ldif")
+ print
+ print "To track the USNs modified/created by provision and upgrade proivsion,"
+ print " the following ranges are proposed to be added to your provision sam.ldb: \n%s" % ldif
+ print "We recommend to review them, and if it's correct to integrate the following ldif: %s in your sam.ldb" % file
+ print "You can load this file like this: ldbadd -H %s %s\n"%(str(paths.samdb),file)
+ ldif = "dn: @PROVISION\nprovisionnerID: %s\n%s" % (invocation, ldif)
+ open(file,'w').write(ldif)
+
diff --git a/source4/scripting/bin/renamedc b/source4/scripting/bin/renamedc
new file mode 100755
index 00000000000..0915b157832
--- /dev/null
+++ b/source4/scripting/bin/renamedc
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# vim: expandtab
+#
+# Copyright (C) Matthieu Patou <mat@matws.net> 2011
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import optparse
+import sys
+# Allow to run from s4 source directory (without installing samba)
+sys.path.insert(0, "bin/python")
+
+import ldb
+import samba
+import samba.getopt as options
+import os
+
+from samba.credentials import DONT_USE_KERBEROS
+from samba.auth import system_session
+from samba import param
+from samba.provision import find_provision_key_parameters, secretsdb_self_join
+from samba.upgradehelpers import get_ldbs, get_paths
+
+
+__docformat__ = "restructuredText"
+
+parser = optparse.OptionParser("provision [options]")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option("--oldname",
+ help="Old DC name")
+parser.add_option("--newname",
+ help="New DC name")
+
+opts = parser.parse_args()[0]
+
+if len(sys.argv) == 1:
+ opts.interactive = True
+lp = sambaopts.get_loadparm()
+smbconf = lp.configfile
+
+creds = credopts.get_credentials(lp)
+creds.set_kerberos_state(DONT_USE_KERBEROS)
+
+
+if __name__ == '__main__':
+ global defSDmodified
+ defSDmodified = False
+ # 1) First get files paths
+ paths = get_paths(param, smbconf=smbconf)
+ # Get ldbs with the system session, it is needed for searching
+ # provision parameters
+ session = system_session()
+
+ ldbs = get_ldbs(paths, creds, session, lp)
+ ldbs.sam.transaction_start()
+ ldbs.secrets.transaction_start()
+
+ if opts.oldname is None or opts.newname is None:
+ raise Exception("Option oldname or newname is missing")
+ res = ldbs.sam.search(expression="(&(name=%s)(serverReferenceBL=*))" % opts.oldname)
+ if res is None or len(res) != 1:
+ raise Exception("Wrong number of result returned, are you sure of the old name %s" %
+ opts.oldname)
+
+ # Ok got it then check that the new name is not used as well
+ res2 = ldbs.sam.search(expression="(&(name=%s)(objectclass=computer))" % opts.newname)
+ if len(res2) != 0:
+ raise Exception("Seems that %s is a name that already exists, pick another one" %
+ opts.newname)
+
+ names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
+ paths, smbconf, lp)
+
+ #First rename the entry
+ # provision put the name in upper case so let's do it too !
+ newdn = str(res[0].dn).replace("CN=%s" % opts.oldname, "CN=%s" % opts.newname.upper())
+ dnobj = ldb.Dn(ldbs.sam, newdn)
+ ldbs.sam.rename(res[0].dn, dnobj)
+
+ # Then change password and samaccountname and dnshostname
+ msg = ldb.Message(dnobj)
+ machinepass = samba.generate_random_password(128, 255)
+ mputf16 = machinepass.encode('utf-16-le')
+
+ account = "%s$" % opts.newname.upper()
+ msg["clearTextPassword"] = ldb.MessageElement(mputf16,
+ ldb.FLAG_MOD_REPLACE,
+ "clearTextPassword")
+
+ msg["sAMAccountName"] = ldb.MessageElement(account,
+ ldb.FLAG_MOD_REPLACE,
+ "sAMAccountName")
+
+ msg["dNSHostName"] = ldb.MessageElement("%s.%s" % (opts.newname,
+ names.dnsdomain),
+ ldb.FLAG_MOD_REPLACE,
+ "dNSHostName")
+ ldbs.sam.modify(msg)
+
+ # Do a self join one more time to resync the secrets file
+ res = ldbs.sam.search(expression=("dn=%s" % newdn),
+ attrs=["msDs-keyVersionNumber", "serverReferenceBL"])
+ assert(len(res) == 1)
+ kvno = int(str(res[0]["msDs-keyVersionNumber"]))
+ serverbldn = ldb.Dn(ldbs.sam, str(res[0]["serverReferenceBL"]))
+
+ secrets_msg = ldbs.secrets.search(expression="sAMAccountName=%s$" %
+ opts.oldname.upper(),
+ attrs=["secureChannelType"])
+
+ secChanType = int(secrets_msg[0]["secureChannelType"][0])
+
+ secretsdb_self_join(ldbs.secrets, domain=names.domain,
+ realm=names.realm,
+ domainsid=names.domainsid,
+ dnsdomain=names.dnsdomain,
+ netbiosname=opts.newname.upper(),
+ machinepass=machinepass,
+ key_version_number=kvno,
+ secure_channel_type=secChanType)
+
+ # Update RID set reference as there is no back link for the moment.
+
+ res = ldbs.sam.search(expression="(objectClass=rIDSet)", base=newdn, attrs=[])
+ assert(len(res) == 1)
+ newridset = str(res[0].dn)
+ msg = ldb.Message(dnobj)
+
+ msg["rIDSetReferences"] = ldb.MessageElement(newridset,
+ ldb.FLAG_MOD_REPLACE,
+ "rIDSetReferences")
+ ldbs.sam.modify(msg)
+
+ # Update the server's sites configuration
+ if False:
+ # Desactivated for the moment we have a couple of issues with site
+ # renaming first one is that it's currently forbidden
+ # second one is that a lot of links are not backlinked
+ # and so won't be updated when the DN change (ie. fmsowner ...)
+ serverbl = str(serverbldn)
+ dnparts = serverbl.split(",")
+ dnparts[0] = "CN=%s" % opts.newname.upper()
+ newserverref = ",".join(dnparts)
+
+ newserverrefdn = ldb.Dn(ldbs.sam, newserverref)
+
+ ldbs.sam.rename(serverbldn, newserverrefdn)
+
+ msg = ldb.Message(newserverrefdn)
+ msg["dNSHostName"] = ldb.MessageElement("%s.%s" % (opts.newname,
+ names.dnsdomain),
+ ldb.FLAG_MOD_REPLACE,
+ "dNSHostName")
+ ldbs.sam.modify(msg)
+
+ try:
+ ldbs.sam.transaction_prepare_commit()
+ ldbs.secrets.transaction_prepare_commit()
+ except Exception:
+ ldbs.sam.rollback()
+ ldbs.secrets.rollback()
+ sys.exit(1)
+
+ try:
+ ldbs.sam.transaction_commit()
+ ldbs.secrets.transaction_commit()
+ except Exception:
+ ldbs.sam.rollback()
+ ldbs.secrets.rollback()
+
+ # All good so far
+ #print lp.get("private dir")
+ cf = open(lp.configfile)
+ ncfname = "%s.new" % lp.configfile
+ newconf = open(ncfname, 'w')
+ for l in cf.readlines():
+ if l.find("netbios name") > 0:
+ newconf.write("\tnetbios name = %s\n" % opts.newname.upper())
+ else:
+ newconf.write(l)
+ newconf.close()
+ cf.close()
+ os.rename(ncfname, lp.configfile)
+
diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate
index e86fba29833..78d7dc1712c 100755
--- a/source4/scripting/bin/samba_dnsupdate
+++ b/source4/scripting/bin/samba_dnsupdate
@@ -89,6 +89,17 @@ if len(IPs) == 0:
print "No IP interfaces - skipping DNS updates"
sys.exit(0)
+IP6s = []
+IP4s = []
+for i in IPs:
+ if i.find(':') != -1:
+ if i.find('%') == -1:
+ # we don't want link local addresses for DNS updates
+ IP6s.append(i)
+ else:
+ IP4s.append(i)
+
+
if opts.verbose:
print "IPs: %s" % IPs
@@ -122,7 +133,7 @@ class dnsobj(object):
if self.type == 'SRV':
self.dest = list[2].lower()
self.port = list[3]
- elif self.type == 'A':
+ elif self.type in ['A', 'AAAA']:
self.ip = list[2] # usually $IP, which gets replaced
elif self.type == 'CNAME':
self.dest = list[2].lower()
@@ -132,6 +143,7 @@ class dnsobj(object):
def __str__(self):
if d.type == "A": return "%s %s %s" % (self.type, self.name, self.ip)
+ if d.type == "AAAA": return "%s %s %s" % (self.type, self.name, self.ip)
if d.type == "SRV": return "%s %s %s %s" % (self.type, self.name, self.dest, self.port)
if d.type == "CNAME": return "%s %s %s" % (self.type, self.name, self.dest)
@@ -178,7 +190,7 @@ def check_dns_name(d):
if opts.verbose:
print "Failed to find DNS entry %s" % d
return False
- if d.type == 'A':
+ if d.type in ['A', 'AAAA']:
# we need to be sure that our IP is there
for rdata in ans:
if str(rdata) == str(d.ip):
@@ -210,7 +222,7 @@ def get_subst_vars():
global lp, am_rodc
vars = {}
- samdb = SamDB(url=lp.get("sam database"), session_info=system_session(),
+ samdb = SamDB(url=lp.samdb_url(), session_info=system_session(),
lp=lp)
vars['DNSDOMAIN'] = lp.get('realm').lower()
@@ -247,6 +259,8 @@ def call_nsupdate(d):
f = os.fdopen(tmp_fd, 'w')
if d.type == "A":
f.write("update add %s %u A %s\n" % (normalised_name, default_ttl, d.ip))
+ if d.type == "AAAA":
+ f.write("update add %s %u AAAA %s\n" % (normalised_name, default_ttl, d.ip))
if d.type == "SRV":
if d.existing_port is not None:
f.write("update delete %s SRV 0 %s %s %s\n" % (normalised_name, d.existing_weight,
@@ -264,7 +278,7 @@ def call_nsupdate(d):
try:
cmd = nsupdate_cmd[:]
cmd.append(tmpfile)
- ret = subprocess.call(cmd, shell=False)
+ ret = subprocess.call(cmd, shell=False, env={"KRB5CCNAME": ccachename})
if ret != 0:
if opts.fail_immediately:
sys.exit(1)
@@ -382,16 +396,28 @@ for line in file:
if line == '' or line[0] == "#":
continue
d = parse_dns_line(line, sub_vars)
+ if d.type == 'A' and len(IP4s) == 0:
+ continue
+ if d.type == 'AAAA' and len(IP6s) == 0:
+ continue
dns_list.append(d)
# now expand the entries, if any are A record with ip set to $IP
# then replace with multiple entries, one for each interface IP
for d in dns_list:
- if d.type == 'A' and d.ip == "$IP":
- d.ip = IPs[0]
- for i in range(len(IPs)-1):
+ if d.ip != "$IP":
+ continue
+ if d.type == 'A':
+ d.ip = IP4s[0]
+ for i in range(len(IP4s)-1):
+ d2 = dnsobj(str(d))
+ d2.ip = IP4s[i+1]
+ dns_list.append(d2)
+ if d.type == 'AAAA':
+ d.ip = IP6s[0]
+ for i in range(len(IP6s)-1):
d2 = dnsobj(str(d))
- d2.ip = IPs[i+1]
+ d2.ip = IP6s[i+1]
dns_list.append(d2)
# now check if the entries already exist on the DNS server
@@ -412,7 +438,7 @@ for d in update_list:
if am_rodc:
if d.name.lower() == domain.lower():
continue
- if d.type != 'A':
+ if not d.type in [ 'A', 'AAAA' ]:
call_rodc_update(d)
else:
call_nsupdate(d)
diff --git a/source4/scripting/bin/samba_spnupdate b/source4/scripting/bin/samba_spnupdate
index 1794f2bd267..9f8f4073d31 100755
--- a/source4/scripting/bin/samba_spnupdate
+++ b/source4/scripting/bin/samba_spnupdate
@@ -82,7 +82,7 @@ def get_subst_vars(samdb):
try:
private_dir = lp.get("private dir")
- secrets_path = os.path.join(private_dir, lp.get("secrets database"))
+ secrets_path = os.path.join(private_dir, "secrets.ldb")
secrets_db = Ldb(url=secrets_path, session_info=system_session(),
credentials=creds, lp=lp)
@@ -103,9 +103,9 @@ try:
else:
credentials = None
- samdb = SamDB(url=lp.get("sam database"), session_info=system_session(), credentials=credentials, lp=lp)
+ samdb = SamDB(url=lp.samdb_url(), session_info=system_session(), credentials=credentials, lp=lp)
except ldb.LdbError, (num, msg):
- print("Unable to open sam database %s : %s" % (lp.get("sam database"), msg))
+ print("Unable to open sam database %s : %s" % (lp.samdb_url(), msg))
sys.exit(1)
diff --git a/source4/scripting/bin/setup_dns.sh b/source4/scripting/bin/setup_dns.sh
index de4485fc07a..bc2ae96b845 100755
--- a/source4/scripting/bin/setup_dns.sh
+++ b/source4/scripting/bin/setup_dns.sh
@@ -13,7 +13,7 @@ IP="$3"
RSUFFIX=$(echo $DOMAIN | sed s/[\.]/,DC=/g)
[ -z "$PRIVATEDIR" ] && {
- PRIVATEDIR=$(bin/testparm --section-name=global --parameter-name='private dir' --suppress-prompt 2> /dev/null)
+ PRIVATEDIR=$(bin/samba-tool testparm --section-name=global --parameter-name='private dir' --suppress-prompt 2> /dev/null)
}
OBJECTGUID=$(bin/ldbsearch -s base -H "$PRIVATEDIR/sam.ldb" -b "CN=NTDS Settings,CN=$HOSTNAME,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=$RSUFFIX" objectguid|grep ^objectGUID| cut -d: -f2)
diff --git a/source4/scripting/bin/testparm b/source4/scripting/bin/testparm
deleted file mode 100755
index c30e46148cc..00000000000
--- a/source4/scripting/bin/testparm
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/env python
-# vim: expandtab ft=python
-#
-# Unix SMB/CIFS implementation.
-# Test validity of smb.conf
-# Copyright (C) Karl Auer 1993, 1994-1998
-#
-# Extensively modified by Andrew Tridgell, 1995
-# Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
-# Updated for Samba4 by Andrew Bartlett <abartlet@samba.org> 2006
-# Converted to Python by Jelmer Vernooij <jelmer@samba.org> 2010
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# Testbed for loadparm.c/params.c
-#
-# This module simply loads a specified configuration file and
-# if successful, dumps it's contents to stdout. Note that the
-# operation is performed with DEBUGLEVEL at 3.
-#
-# Useful for a quick 'syntax check' of a configuration file.
-#
-
-import logging
-import optparse
-import os
-import sys
-
-# Find right directory when running from source tree
-sys.path.insert(0, "bin/python")
-
-import samba
-from samba import getopt as options
-
-# Here we do a set of 'hard coded' checks for bad
-# configuration settings.
-
-def do_global_checks(lp, logger):
- valid = True
-
- netbios_name = lp.get("netbios name")
- if not samba.valid_netbios_name(netbios_name):
- logger.error("netbios name %s is not a valid netbios name",
- netbios_name)
- valid = False
-
- workgroup = lp.get("workgroup")
- if not samba.valid_netbios_name(workgroup):
- logger.error("workgroup name %s is not a valid netbios name",
- workgroup)
- valid = False
-
- lockdir = lp.get("lockdir")
-
- if not os.path.isdir(lockdir):
- logger.error("lock directory %s does not exist", lockdir)
- valid = False
-
- piddir = lp.get("pid directory")
-
- if not os.path.isdir(piddir):
- logger.error("pid directory %s does not exist", piddir)
- valid = False
-
- winbind_separator = lp.get("winbind separator")
-
- if len(winbind_separator) != 1:
- logger.error("the 'winbind separator' parameter must be a single "
- "character.")
- valid = False
-
- if winbind_separator == '+':
- logger.error("'winbind separator = +' might cause problems with group "
- "membership.")
- valid = False
-
- return valid
-
-
-def allow_access(deny_list, allow_list, cname, caddr):
- raise NotImplementedError(allow_access)
-
-
-def do_share_checks(lp, logger):
- valid = True
- for s in lp.services():
- if len(s) > 12:
- logger.warning("You have some share names that are longer than 12 "
- "characters. These may not be accessible to some older "
- "clients. (Eg. Windows9x, WindowsMe, and not listed in "
- "smbclient in Samba 3.0.)")
- break
-
- for s in lp.services():
- deny_list = lp.get("hosts deny", s)
- allow_list = lp.get("hosts allow", s)
- if deny_list:
- for entry in deny_list:
- if "*" in entry or "?" in entry:
- logger.error("Invalid character (* or ?) in hosts deny "
- "list (%s) for service %s.", entry, s)
- valid = False
-
- if allow_list:
- for entry in allow_list:
- if "*" in entry or "?" in entry:
- logger.error("Invalid character (* or ?) in hosts allow "
- "list (%s) for service %s.", entry, s)
- valid = False
- return valid
-
-def check_client_access(lp, cname, caddr):
- # this is totally ugly, a real `quick' hack
- for s in lp.services():
- if (allow_access(lp.get("hosts deny"), lp.get("hosts allow"), cname,
- caddr) and
- allow_access(lp.get("hosts deny", s), lp.get("hosts allow", s),
- cname, caddr)):
- logger.info("Allow connection from %s (%s) to %s", cname, caddr, s)
- else:
- logger.info("Deny connection from %s (%s) to %s", cname, caddr, s)
-
-
-if __name__ == '__main__':
- parser = optparse.OptionParser("testparm [OPTION...] [host-name] [host-ip]")
- parser.add_option("--section-name", type="string", metavar="SECTION",
- help="Limit testparm to a named section")
- parser.add_option("--parameter-name", type="string", metavar="PARAMETER",
- help="Limit testparm to a named parameter")
- parser.add_option("--client-name", type="string", metavar="HOSTNAME",
- help="Client DNS name for 'hosts allow' checking "
- "(should match reverse lookup)")
- parser.add_option("--client-ip", type="string", metavar="IP",
- help="Client IP address for 'hosts allow' checking")
- parser.add_option("--suppress-prompt", action="store_true", default=False,
- help="Suppress prompt for enter")
- parser.add_option("-v", "--verbose", action="store_true",
- default=False, help="Show default options too")
- parser.add_option_group(options.VersionOptions(parser))
- # We need support for smb.conf macros before this will work again
- parser.add_option("--server", type="string",
- help="Set %%L macro to servername")
- # These are harder to do with the new code structure
- parser.add_option("--show-all-parameters", action="store_true",
- default=False, help="Show the parameters, type, possible values")
-
- sambaopts = options.SambaOptions(parser)
- parser.add_option_group(sambaopts)
-
- opts, args = parser.parse_args()
-
-#
-# if (show_all_parameters) {
-# show_parameter_list()
-# exit(0)
-# }
-
- if len(args) > 0:
- cname = args[0]
- else:
- cname = None
- if len(args) > 1:
- caddr = args[1]
- else:
- caddr = None
-
- if cname is not None and caddr is None:
- print "Both a DNS name and an IP address are required for the host " \
- "access check."
- sys.exit(1)
-
-# FIXME: We need support for smb.conf macros before this will work again
-#
-# if (new_local_machine) {
-# set_local_machine_name(new_local_machine, True)
-# }
-
- lp = sambaopts.get_loadparm()
-
- # We need this to force the output
- samba.set_debug_level(2)
-
- logger = logging.getLogger("testparm")
- logger.addHandler(logging.StreamHandler(sys.stdout))
-
- logger.info("Loaded smb config files from %s", lp.configfile)
- logger.info("Loaded services file OK.")
-
- valid = do_global_checks(lp, logger)
- valid = valid and do_share_checks(lp, logger)
- if cname is not None:
- check_client_access(lp, cname, caddr)
- else:
- if opts.section_name is not None or opts.parameter_name is not None:
- if opts.parameter_name is None:
- lp[opts.section_name].dump(sys.stdout, lp.default_service,
- opts.verbose)
- else:
- print lp.get(opts.parameter_name, opts.section_name)
- else:
- if not opts.suppress_prompt:
- print "Press enter to see a dump of your service definitions\n"
- sys.stdin.readline()
- lp.dump(sys.stdout, opts.verbose)
- if valid:
- sys.exit(0)
- else:
- sys.exit(1)
diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision
index 8c79917d5e2..e98b6427762 100755
--- a/source4/scripting/bin/upgradeprovision
+++ b/source4/scripting/bin/upgradeprovision
@@ -42,9 +42,10 @@ from samba.credentials import DONT_USE_KERBEROS
from samba.auth import system_session, admin_session
from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,
- MessageElement, Message, Dn)
+ MessageElement, Message, Dn, LdbError)
from samba import param, dsdb, Ldb
-from samba.provision import (get_domain_descriptor,
+from samba.common import confirm
+from samba.provision import (get_domain_descriptor, find_provision_key_parameters,
get_config_descriptor,
ProvisioningError, get_last_provision_usn,
get_max_usn, update_provision_usn, setup_path)
@@ -52,7 +53,7 @@ from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
from samba.dcerpc import security, drsblobs, xattr
from samba.ndr import ndr_unpack
from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
- find_provision_key_parameters, get_ldbs,
+ get_ldbs,
usn_in_range, identic_rename, get_diff_sddls,
update_secrets, CHANGE, ERROR, SIMPLE,
CHANGEALL, GUESS, CHANGESD, PROVISION,
@@ -81,20 +82,25 @@ __docformat__ = "restructuredText"
# This is most probably because they are populated automatcally when object is
# created
# This also apply to imported object from reference provision
-hashAttrNotCopied = { "dn": 1, "whenCreated": 1, "whenChanged": 1,
- "objectGUID": 1, "uSNCreated": 1,
- "replPropertyMetaData": 1, "uSNChanged": 1,
- "parentGUID": 1, "objectCategory": 1,
- "distinguishedName": 1, "nTMixedDomain": 1,
- "showInAdvancedViewOnly": 1, "instanceType": 1,
- "msDS-Behavior-Version":1, "nextRid":1, "cn": 1,
- "lmPwdHistory":1, "pwdLastSet": 1,
- "ntPwdHistory":1, "unicodePwd":1,"dBCSPwd":1,
- "supplementalCredentials":1, "gPCUserExtensionNames":1,
- "gPCMachineExtensionNames":1,"maxPwdAge":1, "secret":1,
- "possibleInferiors":1, "privilege":1,
- "sAMAccountType":1 }
+replAttrNotCopied = [ "dn", "whenCreated", "whenChanged", "objectGUID",
+ "parentGUID", "objectCategory", "distinguishedName",
+ "nTMixedDomain", "showInAdvancedViewOnly",
+ "instanceType", "msDS-Behavior-Version", "cn",
+ "lmPwdHistory", "pwdLastSet", "ntPwdHistory",
+ "unicodePwd", "dBCSPwd", "supplementalCredentials",
+ "gPCUserExtensionNames", "gPCMachineExtensionNames",
+ "maxPwdAge", "secret", "possibleInferiors", "privilege",
+ "sAMAccountType", "oEMInformation", "creationTime" ]
+nonreplAttrNotCopied = ["uSNCreated", "replPropertyMetaData", "uSNChanged",
+ "nextRid" ,"rIDNextRID", "rIDPreviousAllocationPool"]
+
+nonDSDBAttrNotCopied = ["msDS-KeyVersionNumber", "priorSecret", "priorWhenChanged"]
+
+
+attrNotCopied = replAttrNotCopied
+attrNotCopied.extend(nonreplAttrNotCopied)
+attrNotCopied.extend(nonDSDBAttrNotCopied)
# Usually for an object that already exists we do not overwrite attributes as
# they might have been changed for good reasons. Anyway for a few of them it's
# mandatory to replace them otherwise the provision will be broken somehow.
@@ -108,15 +114,19 @@ hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
"wellKnownObjects":replace, "privilege":never,
"defaultSecurityDescriptor": replace,
"rIDAvailablePool": never,
+ "versionNumber" : add,
"rIDNextRID": add, "rIDUsedPool": never,
"defaultSecurityDescriptor": replace + add,
"isMemberOfPartialAttributeSet": delete,
"attributeDisplayNames": replace + add,
"versionNumber": add}
+dnNotToRecalculate = []
+dnToRecalculate = []
backlinked = []
forwardlinked = set()
dn_syntax_att = []
+not_replicated = []
def define_what_to_log(opts):
what = 0
if opts.debugchange:
@@ -151,6 +161,8 @@ parser.add_option("--debugall", action="store_true",
help="Print all available information (very verbose)")
parser.add_option("--resetfileacl", action="store_true",
help="Force a reset on filesystem acls in sysvol / netlogon share")
+parser.add_option("--fixntacl", action="store_true",
+ help="Only fix NT ACLs in sysvol / netlogon share")
parser.add_option("--full", action="store_true",
help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
@@ -236,6 +248,26 @@ def populate_links(samdb, schemadn):
for t in linkedAttHash.keys():
forwardlinked.add(t)
+def isReplicated(att):
+ """ Indicate if the attribute is replicated or not
+
+ :param att: Name of the attribute to be tested
+ :return: True is the attribute is replicated, False otherwise
+ """
+
+ return (att not in not_replicated)
+
+def populateNotReplicated(samdb, schemadn):
+ """Populate an array with all the attributes that are not replicated
+
+ :param samdb: A LDB object for sam.ldb file
+ :param schemadn: DN of the schema for the partition"""
+ res = samdb.search(expression="(&(objectclass=attributeSchema)(systemflags:1.2.840.113556.1.4.803:=1))", base=Dn(samdb,
+ str(schemadn)), scope=SCOPE_SUBTREE,
+ attrs=["lDAPDisplayName"])
+ for elem in res:
+ not_replicated.append(str(elem["lDAPDisplayName"]))
+
def populate_dnsyntax(samdb, schemadn):
"""Populate an array with all the attributes that have DN synthax
@@ -295,7 +327,7 @@ def print_provision_key_parameters(names):
message(GUESS, "domainlevel :" + str(names.domainlevel))
-def handle_special_case(att, delta, new, old, usn, basedn, aldb):
+def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):
"""Define more complicate update rules for some attributes
:param att: The attribute to be updated
@@ -303,7 +335,8 @@ def handle_special_case(att, delta, new, old, usn, basedn, aldb):
between the updated object and the reference one
:param new: The reference object
:param old: The Updated object
- :param usn: The highest usn modified by a previous (upgrade)provision
+ :param useReplMetadata: A boolean that indicate if the update process
+ use replPropertyMetaData to decide what has to be updated.
:param basedn: The base DN of the provision
:param aldb: An ldb object used to build DN
:return: True to indicate that the attribute should be kept, False for
@@ -313,7 +346,7 @@ def handle_special_case(att, delta, new, old, usn, basedn, aldb):
# We do most of the special case handle if we do not have the
# highest usn as otherwise the replPropertyMetaData will guide us more
# correctly
- if usn is None:
+ if not useReplMetadata:
if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and
ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"
"CN=Services,CN=Configuration,%s" % basedn)
@@ -389,14 +422,14 @@ def handle_special_case(att, delta, new, old, usn, basedn, aldb):
if (att == "servicePrincipalName" and flag == FLAG_MOD_REPLACE):
hash = {}
newval = []
- changeDelta=0
+ changeDelta = 0
for elem in old[0][att]:
hash[str(elem)]=1
newval.append(str(elem))
for elem in new[0][att]:
if not hash.has_key(str(elem)):
- changeDelta=1
+ changeDelta = 1
newval.append(str(elem))
if changeDelta == 1:
delta[att] = MessageElement(newval, FLAG_MOD_REPLACE, att)
@@ -583,7 +616,7 @@ def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
m = re.match(r".*-(\d+)$", sid)
if m and int(m.group(1))>999:
delta.remove("objectSid")
- for att in hashAttrNotCopied.keys():
+ for att in attrNotCopied:
delta.remove(att)
for att in backlinked:
delta.remove(att)
@@ -650,7 +683,7 @@ def add_deletedobj_containers(ref_samdb, samdb, names):
delta = samdb.msg_diff(empty, reference[0])
delta.dn = Dn(samdb, str(reference[0]["dn"]))
- for att in hashAttrNotCopied.keys():
+ for att in attrNotCopied:
delta.remove(att)
modcontrols = ["relax:0", "provision:0"]
@@ -771,8 +804,192 @@ msg_elt_flag_strs = {
ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
+def checkKeepAttributeOldMtd(delta, att, reference, current,
+ basedn, samdb):
+ """ Check if we should keep the attribute modification or not.
+ This function didn't use replicationMetadata to take a decision.
+
+ :param delta: A message diff object
+ :param att: An attribute
+ :param reference: A message object for the current entry comming from
+ the reference provision.
+ :param current: A message object for the current entry commin from
+ the current provision.
+ :param basedn: The DN of the partition
+ :param samdb: A ldb connection to the sam database of the current provision.
+
+ :return: The modified message diff.
+ """
+ # Old school way of handling things for pre alpha12 upgrade
+ global defSDmodified
+ isFirst = False
+ txt = ""
+ dn = current[0].dn
+
+ for att in list(delta):
+ msgElt = delta.get(att)
+
+ if att == "nTSecurityDescriptor":
+ defSDmodified = True
+ delta.remove(att)
+ continue
+
+ if att == "dn":
+ continue
+
+ if not hashOverwrittenAtt.has_key(att):
+ if msgElt.flags() != FLAG_MOD_ADD:
+ if not handle_special_case(att, delta, reference, current,
+ False, basedn, samdb):
+ if opts.debugchange or opts.debugall:
+ try:
+ dump_denied_change(dn, att,
+ msg_elt_flag_strs[msgElt.flags()],
+ current[0][att], reference[0][att])
+ except KeyError:
+ dump_denied_change(dn, att,
+ msg_elt_flag_strs[msgElt.flags()],
+ current[0][att], None)
+ delta.remove(att)
+ continue
+ else:
+ if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
+ continue
+ elif hashOverwrittenAtt.get(att)==never:
+ delta.remove(att)
+ continue
+
+ return delta
+
+def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
+ hash_attr_usn, basedn, usns, samdb):
+ """ Check if we should keep the attribute modification or not
+
+ :param delta: A message diff object
+ :param att: An attribute
+ :param message: A function to print messages
+ :param reference: A message object for the current entry comming from
+ the reference provision.
+ :param current: A message object for the current entry commin from
+ the current provision.
+ :param hash_attr_usn: A dictionnary with attribute name as keys,
+ USN and invocation id as values.
+ :param basedn: The DN of the partition
+ :param usns: A dictionnary with invocation ID as keys and USN ranges
+ as values.
+ :param samdb: A ldb object pointing to the sam DB
+
+ :return: The modified message diff.
+ """
+ global defSDmodified
+ isFirst = True
+ txt = ""
+ dn = current[0].dn
+
+ for att in list(delta):
+ if att in ["dn", "objectSid"]:
+ delta.remove(att)
+ continue
+
+ # We have updated by provision usn information so let's exploit
+ # replMetadataProperties
+ if att in forwardlinked:
+ curval = current[0].get(att, ())
+ refval = reference[0].get(att, ())
+ handle_links(samdb, att, basedn, current[0]["dn"],
+ curval, refval, delta)
+ continue
+
+ if isFirst and len(delta.items())>1:
+ isFirst = False
+ txt = "%s\n" % (str(dn))
+
+ if handle_special_case(att, delta, reference, current, True, None, None):
+ # This attribute is "complicated" to handle and handling
+ # was done in handle_special_case
+ continue
+
+ attrUSN = None
+ if hash_attr_usn.get(att):
+ [attrUSN, attInvId] = hash_attr_usn.get(att)
+
+ if attrUSN is None:
+ # If it's a replicated attribute and we don't have any USN
+ # information about it. It means that we never saw it before
+ # so let's add it !
+ # If it is a replicated attribute but we are not master on it
+ # (ie. not initially added in the provision we masterize).
+ # attrUSN will be -1
+ if isReplicated(att):
+ continue
+ else:
+ message(CHANGE, "Non replicated attribute %s changed" % att)
+ continue
-def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
+ if att == "nTSecurityDescriptor":
+ cursd = ndr_unpack(security.descriptor,
+ str(current[0]["nTSecurityDescriptor"]))
+ cursddl = cursd.as_sddl(names.domainsid)
+ refsd = ndr_unpack(security.descriptor,
+ str(reference[0]["nTSecurityDescriptor"]))
+ refsddl = refsd.as_sddl(names.domainsid)
+
+ diff = get_diff_sddls(refsddl, cursddl)
+ if diff == "":
+ # FIXME find a way to have it only with huge huge verbose mode
+ # message(CHANGE, "%ssd are identical" % txt)
+ # txt = ""
+ delta.remove(att)
+ continue
+ else:
+ delta.remove(att)
+ message(CHANGESD, "%ssd are not identical:\n%s" % (txt, diff))
+ txt = ""
+ if attrUSN == -1:
+ message(CHANGESD, "But the SD has been changed by someonelse "\
+ "so it's impossible to know if the difference"\
+ " cames from the modification or from a previous bug")
+ dnNotToRecalculate.append(str(dn))
+ else:
+ dnToRecalculate.append(str(dn))
+ continue
+
+ if attrUSN == -1:
+ # This attribute was last modified by another DC forget
+ # about it
+ message(CHANGE, "%sAttribute: %s has been "
+ "created/modified/deleted by another DC. "
+ "Doing nothing" % (txt, att))
+ txt = ""
+ delta.remove(att)
+ continue
+ elif not usn_in_range(int(attrUSN), usns.get(attInvId)):
+ message(CHANGE, "%sAttribute: %s was not "
+ "created/modified/deleted during a "
+ "provision or upgradeprovision. Current "
+ "usn: %d. Doing nothing" % (txt, att,
+ attrUSN))
+ txt = ""
+ delta.remove(att)
+ continue
+ else:
+ if att == "defaultSecurityDescriptor":
+ defSDmodified = True
+ if attrUSN:
+ message(CHANGE, "%sAttribute: %s will be modified"
+ "/deleted it was last modified "
+ "during a provision. Current usn: "
+ "%d" % (txt, att, attrUSN))
+ txt = ""
+ else:
+ message(CHANGE, "%sAttribute: %s will be added because "
+ "it did not exist before" % (txt, att))
+ txt = ""
+ continue
+
+ return delta
+
+def update_present(ref_samdb, samdb, basedn, listPresent, usns):
""" This function updates the object that are already present in the
provision
@@ -782,10 +999,9 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
(ie. DC=foo, DC=bar)
:param listPresent: A list of object that is present in the provision
:param usns: A list of USN range modified by previous provision and
- upgradeprovision
- :param invocationid: The value of the invocationid for the current DC"""
+ upgradeprovision grouped by invocation ID
+ """
- global defSDmodified
# This hash is meant to speedup lookup of attribute name from an oid,
# it's for the replPropertyMetaData handling
hash_oid_name = {}
@@ -801,7 +1017,9 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
raise ProvisioningError(msg)
changed = 0
- controls = ["search_options:1:2", "sd_flags:1:2"]
+ controls = ["search_options:1:2", "sd_flags:1:0"]
+ if usns is not None:
+ message(CHANGE, "Using replPropertyMetadata for change selection")
for dn in listPresent:
reference = ref_samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
scope=SCOPE_SUBTREE,
@@ -823,14 +1041,17 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
delta = samdb.msg_diff(current[0], reference[0])
- for att in hashAttrNotCopied.keys():
+ for att in backlinked:
delta.remove(att)
- for att in backlinked:
+ for att in attrNotCopied:
delta.remove(att)
delta.remove("name")
+ if len(delta.items()) == 1:
+ continue
+
if len(delta.items()) > 1 and usns is not None:
# Fetch the replPropertyMetaData
res = samdb.search(expression="dn=%s" % (str(dn)), base=basedn,
@@ -844,145 +1065,25 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
# We put in this hash only modification
# made on the current host
att = hash_oid_name[samdb.get_oid_from_attid(o.attid)]
- if str(o.originating_invocation_id) == str(invocationid):
- # Note we could just use 1 here
- hash_attr_usn[att] = o.originating_usn
+ if str(o.originating_invocation_id) in usns.keys():
+ hash_attr_usn[att] = [o.originating_usn, str(o.originating_invocation_id)]
else:
- hash_attr_usn[att] = -1
-
- isFirst = 0
- txt = ""
-
- for att in list(delta):
- if usns is not None:
- # We have updated by provision usn information so let's exploit
- # replMetadataProperties
- if att in forwardlinked:
- curval = current[0].get(att, ())
- refval = reference[0].get(att, ())
- handle_links(samdb, att, basedn, current[0]["dn"],
- curval, refval, delta)
- continue
-
- if isFirst == 0 and len(delta.items())>1:
- isFirst = 1
- txt = "%s\n" % (str(dn))
- if att == "dn":
- # There is always a dn attribute after a msg_diff
- continue
- if att == "rIDAvailablePool":
- delta.remove(att)
- continue
- if att == "objectSid":
- delta.remove(att)
- continue
- if att == "creationTime":
- delta.remove(att)
- continue
- if att == "oEMInformation":
- delta.remove(att)
- continue
- if att == "msDs-KeyVersionNumber":
- # This is the kvno of the computer/user it's a very bad
- # idea to change it
- delta.remove(att)
- continue
- if handle_special_case(att, delta, reference, current, usns, basedn, samdb):
- # This attribute is "complicated" to handle and handling
- # was done in handle_special_case
- continue
- attrUSN = hash_attr_usn.get(att)
- if att == "forceLogoff" and attrUSN is None:
- continue
- if attrUSN is None:
- delta.remove(att)
- continue
- if att == "nTSecurityDescriptor":
- cursd = ndr_unpack(security.descriptor,
- str(current[0]["nTSecurityDescriptor"]))
- cursddl = cursd.as_sddl(names.domainsid)
- refsd = ndr_unpack(security.descriptor,
- str(reference[0]["nTSecurityDescriptor"]))
- refsddl = cursd.as_sddl(names.domainsid)
-
- if get_diff_sddls(refsddl, cursddl) == "":
- message(CHANGE, "sd are identical")
- else:
- message(CHANGE, "sd are not identical")
- if attrUSN == -1:
- # This attribute was last modified by another DC forget
- # about it
- message(CHANGE, "%sAttribute: %s has been "
- "created/modified/deleted by another DC. "
- "Doing nothing" % (txt, att))
- txt = ""
- delta.remove(att)
- continue
- elif not usn_in_range(int(attrUSN), usns):
- message(CHANGE, "%sAttribute: %s was not "
- "created/modified/deleted during a "
- "provision or upgradeprovision. Current "
- "usn: %d. Doing nothing" % (txt, att,
- attrUSN))
- txt = ""
- delta.remove(att)
- continue
- else:
- if att == "defaultSecurityDescriptor":
- defSDmodified = True
- if attrUSN:
- message(CHANGE, "%sAttribute: %s will be modified"
- "/deleted it was last modified "
- "during a provision. Current usn: "
- "%d" % (txt, att, attrUSN))
- txt = ""
- else:
- message(CHANGE, "%sAttribute: %s will be added because "
- "it did not exist before" % (txt, att))
- txt = ""
- continue
+ hash_attr_usn[att] = [-1, None]
- else:
- # Old school way of handling things for pre alpha12 upgrade
- defSDmodified = True
- msgElt = delta.get(att)
-
- if att == "nTSecurityDescriptor":
- delta.remove(att)
- continue
-
- if att == "dn":
- continue
-
- if not hashOverwrittenAtt.has_key(att):
- if msgElt.flags() != FLAG_MOD_ADD:
- if not handle_special_case(att, delta, reference, current,
- usns, basedn, samdb):
- if opts.debugchange or opts.debugall:
- try:
- dump_denied_change(dn, att,
- msg_elt_flag_strs[msgElt.flags()],
- current[0][att], reference[0][att])
- except KeyError:
- dump_denied_change(dn, att,
- msg_elt_flag_strs[msgElt.flags()],
- current[0][att], None)
- delta.remove(att)
- continue
- else:
- if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
- continue
- elif hashOverwrittenAtt.get(att)==never:
- delta.remove(att)
- continue
+ if usns is not None:
+ delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
+ current, hash_attr_usn,
+ basedn, usns, samdb)
+ else:
+ delta = checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)
delta.dn = dn
if len(delta.items()) >1:
- attributes=", ".join(delta.keys())
+ # Skip dn as the value is not really changed ...
+ attributes=", ".join(delta.keys()[1:])
modcontrols = []
relaxedatt = ['iscriticalsystemobject', 'grouptype']
# Let's try to reduce as much as possible the use of relax control
- #for checkedatt in relaxedatt:
for attr in delta.keys():
if attr.lower() in relaxedatt:
modcontrols = ["relax:0", "provision:0"]
@@ -1033,8 +1134,8 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre
:param basedn: String value of the DN of the partition
:param names: List of key provision parameters
:param schema: A Schema object
- :param provisionUSNs: The USNs modified by provision/upgradeprovision
- last time
+ :param provisionUSNs: A dictionnary with range of USN modified during provision
+ or upgradeprovision. Ranges are grouped by invocationID.
:param prereloadfunc: A function that must be executed just before the reload
of the schema
"""
@@ -1096,7 +1197,7 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre
message(SIMPLE, "Schema reloaded!")
changed = update_present(ref_samdb, samdb, basedn, listPresent,
- provisionUSNs, names.invocation)
+ provisionUSNs)
message(SIMPLE, "There are %d changed objects" % (changed))
return 1
@@ -1138,7 +1239,7 @@ def check_updated_sd(ref_sam, cur_sam, names):
str(current[i]["nTSecurityDescriptor"]))
sddl = cursd.as_sddl(names.domainsid)
if sddl != hash[key]:
- txt = get_diff_sddls(hash[key], sddl)
+ txt = get_diff_sddls(hash[key], sddl, False)
if txt != "":
message(CHANGESD, "On object %s ACL is different"
" \n%s" % (current[i]["dn"], txt))
@@ -1152,37 +1253,38 @@ def fix_partition_sd(samdb, names):
:param samdb: An LDB object pointing to the sam of the current provision
:param names: A list of key provision parameters
"""
+ alwaysRecalculate = False
+ if len(dnToRecalculate) == 0 and len(dnNotToRecalculate) == 0:
+ alwaysRecalculate = True
+
+
+ # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate
# First update the SD for the rootdn
- res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
- scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
- controls=["search_options:1:2"])
- delta = Message()
- delta.dn = Dn(samdb, str(res[0]["dn"]))
- descr = get_domain_descriptor(names.domainsid)
- delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
- "nTSecurityDescriptor")
- samdb.modify(delta)
+ if alwaysRecalculate or str(names.rootdn) in dnToRecalculate:
+ delta = Message()
+ delta.dn = Dn(samdb, str(names.rootdn))
+ descr = get_domain_descriptor(names.domainsid)
+ delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
+ "nTSecurityDescriptor")
+ samdb.modify(delta)
+
# Then the config dn
- res = samdb.search(expression="objectClass=*", base=str(names.configdn),
- scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
- controls=["search_options:1:2"])
- delta = Message()
- delta.dn = Dn(samdb, str(res[0]["dn"]))
- descr = get_config_descriptor(names.domainsid)
- delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
- "nTSecurityDescriptor" )
- samdb.modify(delta)
- # Then the schema dn
- res = samdb.search(expression="objectClass=*", base=str(names.schemadn),
- scope=SCOPE_BASE, attrs=["dn", "whenCreated"],
- controls=["search_options:1:2"])
+ if alwaysRecalculate or str(names.configdn) in dnToRecalculate:
+ delta = Message()
+ delta.dn = Dn(samdb, str(names.configdn))
+ descr = get_config_descriptor(names.domainsid)
+ delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
+ "nTSecurityDescriptor" )
+ samdb.modify(delta)
- delta = Message()
- delta.dn = Dn(samdb, str(res[0]["dn"]))
- descr = get_schema_descriptor(names.domainsid)
- delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
- "nTSecurityDescriptor" )
- samdb.modify(delta)
+ # Then the schema dn
+ if alwaysRecalculate or str(names.schemadn) in dnToRecalculate:
+ delta = Message()
+ delta.dn = Dn(samdb, str(names.schemadn))
+ descr = get_schema_descriptor(names.domainsid)
+ delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
+ "nTSecurityDescriptor" )
+ samdb.modify(delta)
def rebuild_sd(samdb, names):
"""Rebuild security descriptor of the current provision from scratch
@@ -1195,30 +1297,46 @@ def rebuild_sd(samdb, names):
:param names: List of key provision parameters"""
+ fix_partition_sd(samdb, names)
+ # List of namming contexts
+ listNC = [str(names.rootdn), str(names.configdn), str(names.schemadn)]
hash = {}
- res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
+ if len(dnToRecalculate) == 0:
+ res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
controls=["search_options:1:2"])
- for obj in res:
- if not (str(obj["dn"]) == str(names.rootdn) or
- str(obj["dn"]) == str(names.configdn) or
- str(obj["dn"]) == str(names.schemadn)):
- hash[str(obj["dn"])] = obj["whenCreated"]
-
- listkeys = hash.keys()
- listkeys.sort(dn_sort)
-
- for key in listkeys:
+ for obj in res:
+ hash[str(obj["dn"])] = obj["whenCreated"]
+ else:
+ for dn in dnToRecalculate:
+ if hash.has_key(dn):
+ continue
+ # fetch each dn to recalculate and their child within the same partition
+ res = samdb.search(expression="objectClass=*", base=dn,
+ scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"])
+ for obj in res:
+ hash[str(obj["dn"])] = obj["whenCreated"]
+
+ listKeys = list(set(hash.keys()))
+ listKeys.sort(dn_sort)
+
+ if len(dnToRecalculate) != 0:
+ message(CHANGESD, "%d DNs have been marked as needed to be recalculated"\
+ ", recalculating %d due to inheritance"
+ % (len(dnToRecalculate), len(listKeys)))
+
+ for key in listKeys:
+ if (key in listNC or
+ key in dnNotToRecalculate):
+ continue
+ delta = Message()
+ delta.dn = Dn(samdb, key)
try:
- delta = Message()
- delta.dn = Dn(samdb, key)
delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
"whenCreated" )
- samdb.modify(delta, ["recalculate_sd:0"])
- except:
- # XXX: We should always catch an explicit exception.
- # What could go wrong here?
+ samdb.modify(delta, ["recalculate_sd:0","relax:0"])
+ except LdbError, e:
samdb.transaction_cancel()
res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
scope=SCOPE_SUBTREE,
@@ -1226,14 +1344,13 @@ def rebuild_sd(samdb, names):
controls=["search_options:1:2"])
badsd = ndr_unpack(security.descriptor,
str(res[0]["nTSecurityDescriptor"]))
- print "bad stuff %s" % badsd.as_sddl(names.domainsid)
+ message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid)))
return
def removeProvisionUSN(samdb):
attrs = [samba.provision.LAST_PROVISION_USN_ATTRIBUTE, "dn"]
entry = samdb.search(expression="dn=@PROVISION", base = "",
scope=SCOPE_SUBTREE,
- controls=["search_options:1:2"],
attrs=attrs)
empty = Message()
empty.dn = entry[0].dn
@@ -1303,7 +1420,7 @@ def update_privilege(ref_private_path, cur_private_path):
os.path.join(cur_private_path, "privilege.ldb"))
-def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc):
+def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
"""Upgrade the SAM DB contents for all the provision partitions
:param ref_sambdb: An LDB object conntected to the sam.ldb of the reference
@@ -1311,8 +1428,8 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc):
:param samdb: An LDB object connected to the sam.ldb of the update
provision
:param names: List of key provision parameters
- :param highestUSN: The highest USN modified by provision/upgradeprovision
- last time
+ :param provisionUSNs: A dictionnary with range of USN modified during provision
+ or upgradeprovision. Ranges are grouped by invocationID.
:param schema: A Schema object that represent the schema of the provision
:param prereloadfunc: A function that must be executed just before the reload
of the schema
@@ -1320,7 +1437,7 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema, prereloadfunc):
message(SIMPLE, "Starting update of samdb")
ret = update_partition(ref_samdb, samdb, str(names.rootdn), names,
- schema, highestUSN, prereloadfunc)
+ schema, provisionUSNs, prereloadfunc)
if ret:
message(SIMPLE, "Update of samdb finished")
return 1
@@ -1537,7 +1654,7 @@ def sync_calculated_attributes(samdb, names):
# This resulting object is filtered to remove all the back link attribute
# (ie. memberOf) as they will be created by the other linked object (ie.
# the one with the member attribute)
-# All attributes specified in the hashAttrNotCopied associative array are
+# All attributes specified in the attrNotCopied array are
# also removed it's most of the time generated attributes
# After missing entries have been added the update_partition function will
@@ -1601,202 +1718,226 @@ if __name__ == '__main__':
# 4)
lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
if lastProvisionUSNs is not None:
+ v = 0
+ for k in lastProvisionUSNs.keys():
+ for r in lastProvisionUSNs[k]:
+ v = v + 1
+
message(CHANGE,
- "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
+ "Find last provision USN, %d invocation(s) for a total of %d ranges" % \
+ (len(lastProvisionUSNs.keys()), v /2 ))
+
+ if lastProvisionUSNs.get("default") != None:
+ message(CHANGE, "Old style for usn ranges used")
+ lastProvisionUSNs[str(names.invocation)] = lastProvisionUSNs["default"]
+ del lastProvisionUSNs["default"]
+ else:
+ message(SIMPLE, "Your provision lacks provision range information")
+ if confirm("Do you want to run findprovisionusnranges to try to find them ?", False):
+ ldbs.groupedRollback()
+ os.system("%s %s %s %s %s" % (os.path.join(os.path.dirname(sys.argv[0]),
+ "findprovisionusnranges"),
+ "--storedir",
+ paths.private_dir,
+ "-s",
+ smbconf))
+ message(SIMPLE, "Once you applied/adapted the change(s) please restart the upgradeprovision script")
+ sys.exit(0)
# Objects will be created with the admin session
# (not anymore system session)
adm_session = admin_session(lp, str(names.domainsid))
# So we reget handle on objects
# ldbs = get_ldbs(paths, creds, adm_session, lp)
-
- if not sanitychecks(ldbs.sam, names):
- message(SIMPLE, "Sanity checks for the upgrade have failed. "
- "Check the messages and correct the errors "
- "before rerunning upgradeprovision")
- sys.exit(1)
-
- # Let's see provision parameters
- print_provision_key_parameters(names)
-
- # 5) With all this information let's create a fresh new provision used as
- # reference
- message(SIMPLE, "Creating a reference provision")
- provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
- prefix="referenceprovision")
- newprovision(names, creds, session, smbconf, provisiondir,
- provision_logger)
-
- # TODO
- # 6) and 7)
- # We need to get a list of object which SD is directly computed from
- # defaultSecurityDescriptor.
- # This will allow us to know which object we can rebuild the SD in case
- # of change of the parent's SD or of the defaultSD.
- # Get file paths of this new provision
- newpaths = get_paths(param, targetdir=provisiondir)
- new_ldbs = get_ldbs(newpaths, creds, session, lp)
- new_ldbs.startTransactions()
-
- # 8) Populate some associative array to ease the update process
- # List of attribute which are link and backlink
- populate_links(new_ldbs.sam, names.schemadn)
- # List of attribute with ASN DN synthax)
- populate_dnsyntax(new_ldbs.sam, names.schemadn)
- # 9)
- update_privilege(newpaths.private_dir, paths.private_dir)
- # 10)
- oem = getOEMInfo(ldbs.sam, str(names.rootdn))
- # Do some modification on sam.ldb
- ldbs.groupedCommit()
- new_ldbs.groupedCommit()
- deltaattr = None
-# 11)
- if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
- # 11) A
- # Starting from alpha9 we can consider that the structure is quite ok
- # and that we should do only dela
- deltaattr = delta_update_basesamdb(newpaths.samdb,
- paths.samdb,
- creds,
- session,
- lp,
- message)
- else:
- # 11) B
- simple_update_basesamdb(newpaths, paths, names)
- ldbs = get_ldbs(paths, creds, session, lp)
- removeProvisionUSN(ldbs.sam)
-
- ldbs.startTransactions()
- minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
- new_ldbs.startTransactions()
-
- # 12)
- schema = Schema(names.domainsid, schemadn=str(names.schemadn))
- # We create a closure that will be invoked just before schema reload
- def schemareloadclosure():
- basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
- options=["modules:"])
- doit = False
- if deltaattr is not None and len(deltaattr) > 1:
- doit = True
- if doit:
- deltaattr.remove("dn")
- for att in deltaattr:
- if att.lower() == "dn":
- continue
- if (deltaattr.get(att) is not None
- and deltaattr.get(att).flags() != FLAG_MOD_ADD):
- doit = False
- elif deltaattr.get(att) is None:
- doit = False
- if doit:
- message(CHANGE, "Applying delta to @ATTRIBUTES")
- deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
- basesam.modify(deltaattr)
- else:
- message(CHANGE, "Not applying delta to @ATTRIBUTES because "
- "there is not only add")
- # 13)
- if opts.full:
- if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
- schema, schemareloadclosure):
- message(SIMPLE, "Rolling back all changes. Check the cause"
- " of the problem")
- message(SIMPLE, "Your system is as it was before the upgrade")
+ if not opts.fixntacl:
+ if not sanitychecks(ldbs.sam, names):
+ message(SIMPLE, "Sanity checks for the upgrade have failed. "
+ "Check the messages and correct the errors "
+ "before rerunning upgradeprovision")
ldbs.groupedRollback()
- new_ldbs.groupedRollback()
- shutil.rmtree(provisiondir)
sys.exit(1)
- else:
- # Try to reapply the change also when we do not change the sam
- # as the delta_upgrade
- schemareloadclosure()
- sync_calculated_attributes(ldbs.sam, names)
+
+ # Let's see provision parameters
+ print_provision_key_parameters(names)
+
+ # 5) With all this information let's create a fresh new provision used as
+ # reference
+ message(SIMPLE, "Creating a reference provision")
+ provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
+ prefix="referenceprovision")
+ newprovision(names, creds, session, smbconf, provisiondir,
+ provision_logger)
+
+ # TODO
+ # 6) and 7)
+ # We need to get a list of object which SD is directly computed from
+ # defaultSecurityDescriptor.
+ # This will allow us to know which object we can rebuild the SD in case
+ # of change of the parent's SD or of the defaultSD.
+ # Get file paths of this new provision
+ newpaths = get_paths(param, targetdir=provisiondir)
+ new_ldbs = get_ldbs(newpaths, creds, session, lp)
+ new_ldbs.startTransactions()
+
+ populateNotReplicated(new_ldbs.sam, names.schemadn)
+ # 8) Populate some associative array to ease the update process
+ # List of attribute which are link and backlink
+ populate_links(new_ldbs.sam, names.schemadn)
+ # List of attribute with ASN DN synthax)
+ populate_dnsyntax(new_ldbs.sam, names.schemadn)
+ # 9)
+ update_privilege(newpaths.private_dir, paths.private_dir)
+ # 10)
+ oem = getOEMInfo(ldbs.sam, str(names.rootdn))
+ # Do some modification on sam.ldb
+ ldbs.groupedCommit()
+ new_ldbs.groupedCommit()
+ deltaattr = None
+ # 11)
+ if re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
+ # 11) A
+ # Starting from alpha9 we can consider that the structure is quite ok
+ # and that we should do only dela
+ deltaattr = delta_update_basesamdb(newpaths.samdb,
+ paths.samdb,
+ creds,
+ session,
+ lp,
+ message)
+ else:
+ # 11) B
+ simple_update_basesamdb(newpaths, paths, names)
+ ldbs = get_ldbs(paths, creds, session, lp)
+ removeProvisionUSN(ldbs.sam)
+
+ ldbs.startTransactions()
+ minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
+ new_ldbs.startTransactions()
+
+ # 12)
+ schema = Schema(names.domainsid, schemadn=str(names.schemadn))
+ # We create a closure that will be invoked just before schema reload
+ def schemareloadclosure():
+ basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
+ options=["modules:"])
+ doit = False
+ if deltaattr is not None and len(deltaattr) > 1:
+ doit = True
+ if doit:
+ deltaattr.remove("dn")
+ for att in deltaattr:
+ if att.lower() == "dn":
+ continue
+ if (deltaattr.get(att) is not None
+ and deltaattr.get(att).flags() != FLAG_MOD_ADD):
+ doit = False
+ elif deltaattr.get(att) is None:
+ doit = False
+ if doit:
+ message(CHANGE, "Applying delta to @ATTRIBUTES")
+ deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
+ basesam.modify(deltaattr)
+ else:
+ message(CHANGE, "Not applying delta to @ATTRIBUTES because "
+ "there is not only add")
+ # 13)
+ if opts.full:
+ if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
+ schema, schemareloadclosure):
+ message(SIMPLE, "Rolling back all changes. Check the cause"
+ " of the problem")
+ message(SIMPLE, "Your system is as it was before the upgrade")
+ ldbs.groupedRollback()
+ new_ldbs.groupedRollback()
+ shutil.rmtree(provisiondir)
+ sys.exit(1)
+ else:
+ # Try to reapply the change also when we do not change the sam
+ # as the delta_upgrade
+ schemareloadclosure()
+ sync_calculated_attributes(ldbs.sam, names)
+ res = ldbs.sam.search(expression="(samaccountname=dns)",
+ scope=SCOPE_SUBTREE, attrs=["dn"],
+ controls=["search_options:1:2"])
+ if len(res) > 0:
+ message(SIMPLE, "You still have the old DNS object for managing "
+ "dynamic DNS, but you didn't supply --full so "
+ "a correct update can't be done")
+ ldbs.groupedRollback()
+ new_ldbs.groupedRollback()
+ shutil.rmtree(provisiondir)
+ sys.exit(1)
+ # 14)
+ update_secrets(new_ldbs.secrets, ldbs.secrets, message)
+ # 14bis)
res = ldbs.sam.search(expression="(samaccountname=dns)",
- scope=SCOPE_SUBTREE, attrs=["dn"],
- controls=["search_options:1:2"])
- if len(res) > 0:
- message(SIMPLE, "You still have the old DNS object for managing "
- "dynamic DNS, but you didn't supply --full so "
- "a correct update can't be done")
- ldbs.groupedRollback()
- new_ldbs.groupedRollback()
- shutil.rmtree(provisiondir)
- sys.exit(1)
- # 14)
- update_secrets(new_ldbs.secrets, ldbs.secrets, message)
- # 14bis)
- res = ldbs.sam.search(expression="(samaccountname=dns)",
- scope=SCOPE_SUBTREE, attrs=["dn"],
- controls=["search_options:1:2"])
-
- if (len(res) == 1):
- ldbs.sam.delete(res[0]["dn"])
- res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
- scope=SCOPE_SUBTREE, attrs=["dn"])
- update_dns_account_password(ldbs.sam, ldbs.secrets, names)
- message(SIMPLE, "IMPORTANT!!! "
- "If you were using Dynamic DNS before you need "
- "to update your configuration, so that the "
- "tkey-gssapi-credential has the following value: "
- "DNS/%s.%s" % (names.netbiosname.lower(),
- names.realm.lower()))
- # 15)
- message(SIMPLE, "Update machine account")
- update_machine_account_password(ldbs.sam, ldbs.secrets, names)
-
- # 16) SD should be created with admin but as some previous acl were so wrong
- # that admin can't modify them we have first to recreate them with the good
- # form but with system account and then give the ownership to admin ...
- if not re.match(r'.*alpha(9|\d\d+)', str(oem)):
- message(SIMPLE, "Fixing old povision SD")
- fix_partition_sd(ldbs.sam, names)
- rebuild_sd(ldbs.sam, names)
-
- # We calculate the max USN before recalculating the SD because we might
- # touch object that have been modified after a provision and we do not
- # want that the next upgradeprovision thinks that it has a green light
- # to modify them
-
- # 17)
- maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
-
- # 18) We rebuild SD only if defaultSecurityDescriptor is modified
- # But in fact we should do it also if one object has its SD modified as
- # child might need rebuild
- if defSDmodified:
- message(SIMPLE, "Updating SD")
- ldbs.sam.set_session_info(adm_session)
- # Alpha10 was a bit broken still
- if re.match(r'.*alpha(\d|10)', str(oem)):
- fix_partition_sd(ldbs.sam, names)
- rebuild_sd(ldbs.sam, names)
-
- # 19)
- # Now we are quite confident in the recalculate process of the SD, we make
- # it optional.
- # Also the check must be done in a clever way as for the moment we just
- # compare SDDL
- if opts.debugchangesd:
- check_updated_sd(new_ldbs.sam, ldbs.sam, names)
-
- # 20)
- updateOEMInfo(ldbs.sam, str(names.rootdn))
- # 21)
- check_for_DNS(newpaths.private_dir, paths.private_dir)
- # 22)
- if lastProvisionUSNs is not None:
- update_provision_usn(ldbs.sam, minUSN, maxUSN)
- if opts.full and (names.policyid is None or names.policyid_dc is None):
- update_policyids(names, ldbs.sam)
- if opts.full or opts.resetfileacl:
+ scope=SCOPE_SUBTREE, attrs=["dn"],
+ controls=["search_options:1:2"])
+
+ if (len(res) == 1):
+ ldbs.sam.delete(res[0]["dn"])
+ res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
+ scope=SCOPE_SUBTREE, attrs=["dn"])
+ update_dns_account_password(ldbs.sam, ldbs.secrets, names)
+ message(SIMPLE, "IMPORTANT!!! "
+ "If you were using Dynamic DNS before you need "
+ "to update your configuration, so that the "
+ "tkey-gssapi-credential has the following value: "
+ "DNS/%s.%s" % (names.netbiosname.lower(),
+ names.realm.lower()))
+ # 15)
+ message(SIMPLE, "Update machine account")
+ update_machine_account_password(ldbs.sam, ldbs.secrets, names)
+
+ dnToRecalculate.sort(dn_sort)
+ # 16) SD should be created with admin but as some previous acl were so wrong
+ # that admin can't modify them we have first to recreate them with the good
+ # form but with system account and then give the ownership to admin ...
+ if str(oem) != "" and not re.match(r'.*alpha(9|\d\d+)', str(oem)):
+ message(SIMPLE, "Fixing very old provision SD")
+ rebuild_sd(ldbs.sam, names)
+
+ # We calculate the max USN before recalculating the SD because we might
+ # touch object that have been modified after a provision and we do not
+ # want that the next upgradeprovision thinks that it has a green light
+ # to modify them
+
+ # 17)
+ maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
+
+ # 18) We rebuild SD if a we have a list of DN to recalculate or if the
+ # defSDmodified is set.
+ if defSDmodified or len(dnToRecalculate) >0:
+ message(SIMPLE, "Some defaultSecurityDescriptors and/or"
+ "securityDescriptor have changed, recalculating SD ")
+ ldbs.sam.set_session_info(adm_session)
+ rebuild_sd(ldbs.sam, names)
+
+ # 19)
+ # Now we are quite confident in the recalculate process of the SD, we make
+ # it optional. And we don't do it if there is DN that we must touch
+ # as we are assured that on this DNs we will have differences !
+ # Also the check must be done in a clever way as for the moment we just
+ # compare SDDL
+ if len(dnNotToRecalculate) == 0 and (opts.debugchangesd or opts.debugall):
+ message(CHANGESD, "Checking recalculated SDs")
+ check_updated_sd(new_ldbs.sam, ldbs.sam, names)
+
+ # 20)
+ updateOEMInfo(ldbs.sam, str(names.rootdn))
+ # 21)
+ check_for_DNS(newpaths.private_dir, paths.private_dir)
+ # 22)
+ if lastProvisionUSNs is not None:
+ update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
+ if opts.full and (names.policyid is None or names.policyid_dc is None):
+ update_policyids(names, ldbs.sam)
+ if opts.full or opts.resetfileacl or opts.fixntacl:
try:
update_gpo(paths, ldbs.sam, names, lp, message, 1)
except ProvisioningError, e:
message(ERROR, "The policy for domain controller is missing. "
- "You should restart upgradeprovision with --full")
+ "You should restart upgradeprovision with --full")
except IOError, e:
message(ERROR, "Setting ACL not supported on your filesystem")
else:
@@ -1804,25 +1945,29 @@ if __name__ == '__main__':
update_gpo(paths, ldbs.sam, names, lp, message, 0)
except ProvisioningError, e:
message(ERROR, "The policy for domain controller is missing. "
- "You should restart upgradeprovision with --full")
- ldbs.groupedCommit()
- new_ldbs.groupedCommit()
- message(SIMPLE, "Upgrade finished!")
- # remove reference provision now that everything is done !
- # So we have reindexed first if need when the merged schema was reloaded
- # (as new attributes could have quick in)
- # But the second part of the update (when we update existing objects
- # can also have an influence on indexing as some attribute might have their
- # searchflag modificated
- message(SIMPLE, "Reopenning samdb to trigger reindexing if needed "
- "after modification")
- samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
- message(SIMPLE, "Reindexing finished")
-
- shutil.rmtree(provisiondir)
+ "You should restart upgradeprovision with --full")
+ if not opts.fixntacl:
+ ldbs.groupedCommit()
+ new_ldbs.groupedCommit()
+ message(SIMPLE, "Upgrade finished!")
+ # remove reference provision now that everything is done !
+ # So we have reindexed first if need when the merged schema was reloaded
+ # (as new attributes could have quick in)
+ # But the second part of the update (when we update existing objects
+ # can also have an influence on indexing as some attribute might have their
+ # searchflag modificated
+ message(SIMPLE, "Reopenning samdb to trigger reindexing if needed "
+ "after modification")
+ samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
+ message(SIMPLE, "Reindexing finished")
+
+ shutil.rmtree(provisiondir)
+ else:
+ ldbs.groupedRollback()
+ message(SIMPLE, "ACLs fixed !")
except StandardError, err:
message(ERROR, "A problem occurred while trying to upgrade your "
- "provision. A full backup is located at %s" % backupdir)
+ "provision. A full backup is located at %s" % backupdir)
if opts.debugall or opts.debugchange:
(typ, val, tb) = sys.exc_info()
traceback.print_exception(typ, val, tb)
diff --git a/source4/scripting/bin/wscript_build b/source4/scripting/bin/wscript_build
new file mode 100644
index 00000000000..e52b32bc02a
--- /dev/null
+++ b/source4/scripting/bin/wscript_build
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SCRIPT('samba_dnsupdate', pattern='samba_dnsupdate', installdir='.')
+bld.SAMBA_SCRIPT('samba_spnupdate', pattern='samba_spnupdate', installdir='.')
+bld.SAMBA_SCRIPT('upgradeprovision', pattern='upgradeprovision', installdir='.')
diff --git a/source4/scripting/devel/chgtdcpass b/source4/scripting/devel/chgtdcpass
index dc249834e00..4f5ea15a80c 100755
--- a/source4/scripting/devel/chgtdcpass
+++ b/source4/scripting/devel/chgtdcpass
@@ -29,8 +29,9 @@ import samba.getopt as options
from samba.credentials import DONT_USE_KERBEROS
from samba.auth import system_session
from samba import param
+from samba.provision import find_provision_key_parameters
from samba.upgradehelpers import (get_paths,
- find_provision_key_parameters, get_ldbs,
+ get_ldbs,
update_machine_account_password)
parser = optparse.OptionParser("chgtdcpass [options]")
diff --git a/source4/scripting/devel/demodirsync.py b/source4/scripting/devel/demodirsync.py
new file mode 100755
index 00000000000..41dac6ff517
--- /dev/null
+++ b/source4/scripting/devel/demodirsync.py
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+
+
+import optparse
+import sys
+import base64
+
+sys.path.insert(0, "bin/python")
+
+import samba.getopt as options
+from samba.dcerpc import drsblobs, misc
+from samba.ndr import ndr_pack, ndr_unpack
+from samba import Ldb
+
+parser = optparse.OptionParser("get-descriptor [options]")
+sambaopts = options.SambaOptions(parser)
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+
+parser.add_option("-b", type="string", metavar="BASE",
+ help="set base DN for the search")
+parser.add_option("--host", type="string", metavar="HOST",
+ help="Ip of the host")
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+opts = parser.parse_args()[0]
+
+def printdirsync(ctl):
+ arr = ctl.split(':')
+ if arr[0] == 'dirsync':
+ print "Need to continue: %s" % arr[1]
+ cookie = ndr_unpack(drsblobs.ldapControlDirSyncCookie, base64.b64decode(arr[3]))
+ print "DC's NTDS guid: %s " % cookie.blob.guid1
+ print "highest usn %s" % cookie.blob.highwatermark.highest_usn
+ print "tmp higest usn %s" % cookie.blob.highwatermark.tmp_highest_usn
+ print "reserved usn %s" % cookie.blob.highwatermark.reserved_usn
+ if cookie.blob.extra_length >0:
+ print "highest usn in extra %s" % cookie.blob.extra.ctr.cursors[0].highest_usn
+ return cookie
+
+remote_ldb= Ldb("ldap://" + opts.host + ":389", credentials=creds, lp=lp)
+tab = []
+if opts.b:
+ base = opts.b
+else:
+ base = None
+
+guid = None
+(msgs, ctrls) = remote_ldb.search(expression="(samaccountname=administrator)", base=base, attrs=["objectClass"], controls=["dirsync:1:1:50"])
+if (len(ctrls)):
+ for ctl in ctrls:
+ arr = ctl.split(':')
+ if arr[0] == 'dirsync':
+ cookie = ndr_unpack(drsblobs.ldapControlDirSyncCookie, base64.b64decode(arr[3]))
+ guid = cookie.blob.guid1
+ pass
+if not guid:
+ print "No dirsync control ... strange"
+ sys.exit(1)
+
+print ""
+print "Getting first guest without any cookie"
+(msgs, ctrls) = remote_ldb.searchex(expression="(samaccountname=guest)", base=base, attrs=["objectClass"], controls=["dirsync:1:1:50"])
+cookie = None
+if (len(ctrls)):
+ for ctl in ctrls:
+ cookie = printdirsync(ctl)
+ print "Returned %d entries" % len(msgs)
+
+savedcookie = cookie
+
+print ""
+print "Getting allusers with cookie"
+controls=["dirsync:1:1:50:%s" % base64.b64encode(ndr_pack(cookie))]
+(msgs, ctrls) = remote_ldb.searchex(expression="(samaccountname=*)", base=base, attrs=["objectClass"], controls=controls)
+if (len(ctrls)):
+ for ctl in ctrls:
+ cookie = printdirsync(ctl)
+ print "Returned %d entries" % len(msgs)
+
+cookie = savedcookie
+cookie.blob.guid1 = misc.GUID("128a99bf-e2df-4832-ac0a-1fb625e530db")
+if cookie.blob.extra_length > 0:
+ cookie.blob.extra.ctr.cursors[0].source_dsa_invocation_id = misc.GUID("128a99bf-e2df-4832-ac0a-1fb625e530db")
+
+print ""
+print "Getting all the entries"
+controls=["dirsync:1:1:50:%s" % base64.b64encode(ndr_pack(cookie))]
+(msgs, ctrls) = remote_ldb.searchex(expression="(objectclass=*)", base=base, controls=controls)
+cont = 0
+if (len(ctrls)):
+ for ctl in ctrls:
+ cookie = printdirsync(ctl)
+ if cookie != None:
+ cont = (ctl.split(':'))[1]
+ print "Returned %d entries" % len(msgs)
+
+usn = cookie.blob.highwatermark.tmp_highest_usn
+if cookie.blob.extra_length > 0:
+ bigusn = cookie.blob.extra.ctr.cursors[0].highest_usn
+else:
+ bigusn = usn + 1000
+while (cont == "1"):
+ print ""
+ controls=["dirsync:1:1:50:%s" % base64.b64encode(ndr_pack(cookie))]
+ (msgs, ctrls) = remote_ldb.searchex(expression="(objectclass=*)", base=base, controls=controls)
+ if (len(ctrls)):
+ for ctl in ctrls:
+ cookie = printdirsync(ctl)
+ if cookie != None:
+ cont = (ctl.split(':'))[1]
+ print "Returned %d entries" % len(msgs)
+
+print ""
+print "Getting with cookie but usn changed to %d we should use the one in extra" % (bigusn - 1)
+cookie.blob.highwatermark.highest_usn = 0
+cookie.blob.highwatermark.tmp_highest_usn = usn - 2
+if cookie.blob.extra_length > 0:
+ print "here"
+ cookie.blob.extra.ctr.cursors[0].highest_usn = bigusn - 1
+controls=["dirsync:1:1:50:%s" % base64.b64encode(ndr_pack(cookie))]
+(msgs, ctrls) = remote_ldb.searchex(expression="(objectclass=*)", base=base, controls=controls)
+if (len(ctrls)):
+ for ctl in ctrls:
+ cookie = printdirsync(ctl)
+ print "Returned %d entries" % len(msgs)
+
+print ""
+print "Getting with cookie but usn %d changed and extra/cursor GUID too" % (usn - 2)
+print " so that it's (tmp)highest_usn that drives the limit"
+cookie.blob.highwatermark.highest_usn = 0
+cookie.blob.highwatermark.tmp_highest_usn = usn - 2
+if cookie.blob.extra_length > 0:
+ cookie.blob.extra.ctr.cursors[0].source_dsa_invocation_id = misc.GUID("128a99bf-e2df-4832-ac0a-1fb625e530db")
+ cookie.blob.extra.ctr.cursors[0].highest_usn = bigusn - 1
+controls=["dirsync:1:1:50:%s" % base64.b64encode(ndr_pack(cookie))]
+(msgs, ctrls) = remote_ldb.searchex(expression="(objectclass=*)", base=base, controls=controls)
+if (len(ctrls)):
+ for ctl in ctrls:
+ cookie = printdirsync(ctl)
+ print "Returned %d entries" % len(msgs)
+
+print ""
+print "Getting with cookie but usn changed to %d" % (usn - 2)
+cookie.blob.highwatermark.highest_usn = 0
+cookie.blob.highwatermark.tmp_highest_usn = (usn - 2)
+if cookie.blob.extra_length > 0:
+ cookie.blob.extra.ctr.cursors[0].highest_usn = (usn - 2)
+controls=["dirsync:1:1:50:%s" % base64.b64encode(ndr_pack(cookie))]
+(msgs, ctrls) = remote_ldb.searchex(expression="(objectclass=*)", base=base, controls=controls)
+if (len(ctrls)):
+ for ctl in ctrls:
+ cookie = printdirsync(ctl)
+ print "Returned %d entries" % len(msgs)
diff --git a/source4/scripting/devel/selftest-vars.sh b/source4/scripting/devel/selftest-vars.sh
index bc73c054078..a8f323dbda8 100644
--- a/source4/scripting/devel/selftest-vars.sh
+++ b/source4/scripting/devel/selftest-vars.sh
@@ -2,38 +2,42 @@
# outside the test environment
export UID_WRAPPER=1
-export NSS_WRAPPER_PASSWD=st/dc/passwd
-export NSS_WRAPPER_GROUP=st/dc/group
+export NSS_WRAPPER_PASSWD=$PWD/st/dc/passwd
+export NSS_WRAPPER_GROUP=$PWD/st/dc/group
export CONFIGURATION="--configfile=$PWD/st/dc/etc/smb.conf"
+export VAMPIRE_DC_SERVER=localvampiredc
+export VAMPIRE_DC_SERVER_IP=127.0.0.22
+export VAMPIRE_DC_NETBIOSNAME=localvampiredc1
+export VAMPIRE_DC_NETBIOSALIAS=localvampiredc
export MEMBER_SERVER=localmember3
-export MEMBER_SERVER_IP=127.0.0.3
+export MEMBER_SERVER_IP=127.0.0.23
export MEMBER_NETBIOSNAME=localmember3
export MEMBER_NETBIOSALIAS=localmember
export RPC_PROXY_SERVER=localrpcproxy4
-export RPC_PROXY_SERVER_IP=127.0.0.4
+export RPC_PROXY_SERVER_IP=127.0.0.24
export RPC_PROXY_NETBIOSNAME=localrpcproxy4
export RPC_PROXY_NETBIOSALIAS=localrpcproxy
export SELFTEST_MAXTIME=1200
export NETBIOSNAME=localdc1
export REALM=SAMBA.EXAMPLE.COM
-export SOCKET_WRAPPER_DEFAULT_IFACE=1
+export SOCKET_WRAPPER_DEFAULT_IFACE=21
export SERVER=localdc1
export WINBINDD_SOCKET_DIR=$PWD/st/dc/winbindd_socket
export SELFTEST_PREFIX=$PWD/st
export DOMAIN=SAMBADOMAIN
export BINDIR=./bin
-export DC_SERVER_IP=127.0.0.1
+export DC_SERVER_IP=127.0.0.21
export SELFTEST_INTERFACES=127.0.0.6/8,127.0.0.7/8,127.0.0.8/8,127.0.0.9/8,127.0.0.10/8,127.0.0.11/8
export SOCKET_WRAPPER_DIR=$PWD/st/w
export DC_USERNAME=Administrator
export USERNAME=Administrator
-export SERVER_IP=127.0.0.1
+export SERVER_IP=127.0.0.21
export KRB5_CONFIG=$PWD/st/dc/etc/krb5.conf
export PREFIX_ABS=$PWD/st
export SRCDIR_ABS=$PWD
-export PREFIX=./st
-export KRB5CCNAME=./st/krb5ticket
-export SRCDIR=.
+export PREFIX=$PWD/st
+export KRB5CCNAME=$PWD/st/krb5ticket
+export SRCDIR=$PWD/
export TLS_ENABLED=yes
export DC_NETBIOSALIAS=localdc
export DC_NETBIOSNAME=localdc1
diff --git a/source4/scripting/python/pyglue.c b/source4/scripting/python/pyglue.c
index f89785f971f..8a82f3502a5 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;
@@ -149,22 +153,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, 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<count; i++) {
- const char *ip = iface_n_ip(ifaces, i);
- if (!(!all_interfaces && iface_same_net(ip, "127.0.0.1", "255.0.0.0"))) {
+ const char *ip = iface_list_n_ip(ifaces, i);
+ if (!(!all_interfaces && iface_list_same_net(ip, "127.0.0.1", "255.0.0.0"))) {
ifcount++;
}
}
pylist = PyList_New(ifcount);
for (ifcount = i = 0; i<count; i++) {
- const char *ip = iface_n_ip(ifaces, i);
- if (!(!all_interfaces && iface_same_net(ip, "127.0.0.1", "255.0.0.0"))) {
+ const char *ip = iface_list_n_ip(ifaces, i);
+ if (!(!all_interfaces && iface_list_same_net(ip, "127.0.0.1", "255.0.0.0"))) {
PyList_SetItem(pylist, ifcount, PyString_FromString(ip));
ifcount++;
}
@@ -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 2a54f47d2bb..76eb44ce928 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)
@@ -348,3 +349,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/common.py b/source4/scripting/python/samba/common.py
new file mode 100644
index 00000000000..a2a49627972
--- /dev/null
+++ b/source4/scripting/python/samba/common.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# Samba common functions
+#
+# Copyright (C) Matthieu Patou <mat@matws.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+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/dbchecker.py b/source4/scripting/python/samba/dbchecker.py
new file mode 100644
index 00000000000..88fd0edf003
--- /dev/null
+++ b/source4/scripting/python/samba/dbchecker.py
@@ -0,0 +1,317 @@
+#!/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 <http://www.gnu.org/licenses/>.
+#
+
+import ldb
+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=[], attrs=['*']):
+ '''perform a database check, returning the number of errors found'''
+
+ 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, 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))
+
+ 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
+ if self.quiet:
+ return self.yes
+ 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)
+
+ 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=controls)
+ 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, errstr):
+ 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="<GUID=%s>" % 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 ((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
+ # 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, "incorrect string version of DN")
+ continue
+
+ return error_count
+
+
+
+ ################################################################
+ # check one object - calls to individual error handlers above
+ 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=attrs)
+ 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/hostconfig.py b/source4/scripting/python/samba/hostconfig.py
index 3e6dc6b1ddd..c50b944c987 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/idmap.py b/source4/scripting/python/samba/idmap.py
index 93fca46edd3..9d957341de8 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/join.py b/source4/scripting/python/samba/join.py
index c0aee714070..b586e2cd5b0 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())
diff --git a/source4/scripting/python/samba/netcmd/__init__.py b/source4/scripting/python/samba/netcmd/__init__.py
index cf514d5c49d..1373cb289b6 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 <jelmer@samba.org> 2009
+# Copyright (C) Theresa Halloran <theresahalloran@gmail.com> 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
@@ -179,10 +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.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
@@ -215,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 00000000000..3cc50eb814a
--- /dev/null
+++ b/source4/scripting/python/samba/netcmd/dbcheck.py
@@ -0,0 +1,104 @@
+#!/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 <http://www.gnu.org/licenses/>.
+#
+
+import ldb, sys
+import samba.getopt as options
+from samba.auth import system_session
+from samba.samdb import SamDB
+from samba.netcmd import (
+ Command,
+ CommandError,
+ Option
+ )
+from samba.dbchecker import dbcheck
+
+
+class cmd_dbcheck(Command):
+ """check local AD database for errors"""
+ synopsis = "dbcheck <DN> [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 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,
+ 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, 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()
+ creds = credopts.get_credentials(lp, fallback_machine=True)
+
+ samdb = SamDB(session_info=system_session(), url=H,
+ credentials=creds, lp=lp)
+ if H is None:
+ samdb_schema = samdb
+ else:
+ 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)
+ 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")
+
+ 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, attrs=attrs)
+
+ if yes and fix:
+ samdb.transaction_commit()
+
+ if error_count != 0:
+ sys.exit(1)
+
diff --git a/source4/scripting/python/samba/netcmd/drs.py b/source4/scripting/python/samba/netcmd/drs.py
index 56c0e39a591..61717a70e98 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)
diff --git a/source4/scripting/python/samba/netcmd/enableaccount.py b/source4/scripting/python/samba/netcmd/enableaccount.py
deleted file mode 100644
index 3ceddb3fd97..00000000000
--- a/source4/scripting/python/samba/netcmd/enableaccount.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env python
-#
-# Enables an user account on a Samba4 server
-# Copyright Jelmer Vernooij 2008
-#
-# Based on the original in EJS:
-# Copyright Andrew Tridgell 2005
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-
-import samba.getopt as options
-
-from samba.auth import system_session
-from samba.netcmd import Command, CommandError, Option
-from samba.samdb import SamDB
-
-class cmd_enableaccount(Command):
- """Enables a user"""
-
- synopsis = "enableaccount [username] [options]"
-
- takes_optiongroups = {
- "sambaopts": options.SambaOptions,
- "versionopts": options.VersionOptions,
- "credopts": options.CredentialsOptions,
- }
-
- takes_options = [
- Option("-H", help="LDB URL for database or target server", type=str),
- Option("--filter", help="LDAP Filter to set password on", type=str),
- ]
-
- takes_args = ["username?"]
-
- def run(self, username=None, sambaopts=None, credopts=None,
- versionopts=None, filter=None, H=None):
- if username is None and filter is None:
- raise CommandError("Either the username or '--filter' must be specified!")
-
- if filter is None:
- filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username)
-
- lp = sambaopts.get_loadparm()
- creds = credopts.get_credentials(lp, fallback_machine=True)
-
- samdb = SamDB(url=H, session_info=system_session(),
- credentials=creds, lp=lp)
- samdb.enable_account(filter)
diff --git a/source4/scripting/python/samba/netcmd/gpo.py b/source4/scripting/python/samba/netcmd/gpo.py
index 19007b361cb..fac91670766 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("")
diff --git a/source4/scripting/python/samba/netcmd/group.py b/source4/scripting/python/samba/netcmd/group.py
index 620a7be8664..95db21adfcf 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):
diff --git a/source4/scripting/python/samba/netcmd/join.py b/source4/scripting/python/samba/netcmd/join.py
index 507253ab817..820709c9e37 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)
@@ -58,21 +59,20 @@ 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)
+ 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)
-
- (join_password, sid, domain_name) = net.join(domain,
- netbios_name,
- secure_channel_type,
- LIBNET_JOIN_AUTOMATIC)
-
- self.outf.write("Joined domain %s (%s)\n" % (domain_name, sid))
diff --git a/source4/scripting/python/samba/netcmd/setexpiry.py b/source4/scripting/python/samba/netcmd/setexpiry.py
deleted file mode 100644
index bd8ea166fac..00000000000
--- a/source4/scripting/python/samba/netcmd/setexpiry.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env python
-#
-# Sets the user password expiry on a Samba4 server
-# Copyright Jelmer Vernooij 2008
-#
-# Based on the original in EJS:
-# Copyright Andrew Tridgell 2005
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-
-from samba.netcmd import Command, CommandError, Option
-
-import samba.getopt as options
-
-from samba.auth import system_session
-from samba.samdb import SamDB
-
-class cmd_setexpiry(Command):
- """Sets the expiration of a user account"""
-
- synopsis = "setexpiry [username] [options]"
-
- takes_optiongroups = {
- "sambaopts": options.SambaOptions,
- "versionopts": options.VersionOptions,
- "credopts": options.CredentialsOptions,
- }
-
- takes_options = [
- Option("-H", help="LDB URL for database or target server", type=str),
- Option("--filter", help="LDAP Filter to set password on", type=str),
- Option("--days", help="Days to expiry", type=int),
- Option("--noexpiry", help="Password does never expire", action="store_true"),
- ]
-
- takes_args = ["username?"]
-
- def run(self, username=None, sambaopts=None, credopts=None,
- versionopts=None, H=None, filter=None, days=None, noexpiry=None):
- if username is None and filter is None:
- raise CommandError("Either the username or '--filter' must be specified!")
-
- if filter is None:
- filter = "(&(objectClass=user)(sAMAccountName=%s))" % (username)
-
- lp = sambaopts.get_loadparm()
- creds = credopts.get_credentials(lp)
-
- if days is None:
- days = 0
-
- samdb = SamDB(url=H, session_info=system_session(),
- credentials=creds, lp=lp)
-
- samdb.setexpiry(filter, days*24*3600, no_expiry_req=noexpiry)
diff --git a/source4/scripting/python/samba/netcmd/user.py b/source4/scripting/python/samba/netcmd/user.py
index a5750b50107..6acf52d790f 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 <jelmer@samba.org>
+# Copyright Theresa Halloran 2011 <theresahalloran@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -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,86 @@ 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 <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)
+ 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"""
+
+ synopsis = "%prog user 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)
+
+ 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]"""
@@ -77,4 +163,5 @@ class cmd_user(SuperCommand):
subcommands = {}
subcommands["add"] = cmd_user_add()
subcommands["delete"] = cmd_user_delete()
-
+ subcommands["enable"] = cmd_user_enable()
+ subcommands["setexpiry"] = cmd_user_setexpiry()
diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py
index ff9b00122d6..5aabd36c1a8 100644
--- a/source4/scripting/python/samba/provision/__init__.py
+++ b/source4/scripting/python/samba/provision/__init__.py
@@ -38,23 +38,23 @@ 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,
- 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,
@@ -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)" \
@@ -217,8 +204,112 @@ 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=site)",
+ 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=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]))
+
+ # 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):
+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
@@ -229,6 +320,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)
"""
@@ -240,17 +332,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.
@@ -259,9 +358,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,
@@ -286,25 +388,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
@@ -328,7 +441,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")
@@ -413,12 +526,9 @@ 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.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.samdb = os.path.join(paths.private_dir, "sam.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")
paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
@@ -608,11 +718,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")
@@ -624,7 +729,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
})
@@ -1166,6 +1270,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"])
@@ -1196,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,
@@ -1208,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")
@@ -1365,6 +1472,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,
@@ -1465,15 +1591,26 @@ def provision(logger, session_info, credentials, smbconf=None,
if hostip is None:
logger.info("Looking up IPv4 addresses")
- hostips = samba.interface_ips(lp, False)
- if len(hostips) == 0:
- logger.warning("No external IPv4 address has been found. Using loopback.")
- hostip = '127.0.0.1'
- else:
+ hostips = interface_ips_v4(lp)
+ if len(hostips) > 0:
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 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")
+ 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 hostip6 is None:
+ logger.warning("No IPv6 address will be assigned")
if serverrole is None:
serverrole = lp.get("server role")
@@ -1640,6 +1777,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)
@@ -1649,9 +1787,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,
@@ -1740,7 +1878,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))
@@ -1876,7 +2014,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).
@@ -1889,6 +2027,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),
diff --git a/source4/scripting/python/samba/samba3.py b/source4/scripting/python/samba/samba3.py
index 2c323bd0b42..ae5b20edd27 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):
diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py
index 99f141e6642..72ee472764b 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,
@@ -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
@@ -106,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
@@ -138,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
@@ -409,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
@@ -470,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.
@@ -501,8 +512,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
@@ -711,3 +727,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)
diff --git a/source4/scripting/python/samba/tests/samba3sam.py b/source4/scripting/python/samba/tests/samba3sam.py
index a34f0f620c5..7353391519f 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):
@@ -64,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()
@@ -86,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}
@@ -135,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"""
@@ -302,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."""
@@ -439,34 +444,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 +483,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 +528,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 +542,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 +562,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 +607,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 +654,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
diff --git a/source4/scripting/python/samba/tests/strings.py b/source4/scripting/python/samba/tests/strings.py
new file mode 100644
index 00000000000..5f3e5c5bb77
--- /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 <mbp@samba.org>
+# 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 <http://www.gnu.org/licenses/>.
+
+# 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 00000000000..fa5d0efc8ca
--- /dev/null
+++ b/source4/scripting/python/samba/tests/unicodenames.py
@@ -0,0 +1,31 @@
+#! /usr/bin/python
+
+# Copyright (C) 2003 by Martin Pool <mbp@samba.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+
+"""
+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'
diff --git a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py b/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py
index 3a9c78e0dcd..596cff6d3aa 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 48f492a7dc3..e15523033fb 100755
--- a/source4/scripting/python/samba/upgradehelpers.py
+++ b/source4/scripting/python/samba/upgradehelpers.py
@@ -24,22 +24,19 @@
"""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,
+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
@@ -242,112 +239,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 = 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.
@@ -469,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
@@ -477,46 +368,54 @@ 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
"""
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):
+ 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
- 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)
@@ -524,9 +423,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
@@ -541,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="",
@@ -649,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:
@@ -666,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"]))
diff --git a/source4/scripting/wscript_build b/source4/scripting/wscript_build
index 76ff739c9e7..d94fc4fe9cc 100644
--- a/source4/scripting/wscript_build
+++ b/source4/scripting/wscript_build
@@ -4,5 +4,5 @@ from samba_utils import MODE_755
bld.INSTALL_FILES('${SBINDIR}','bin/upgradeprovision bin/samba_dnsupdate bin/samba_spnupdate',
chmod=MODE_755, python_fixup=True, flat=True)
-bld.INSTALL_FILES('${BINDIR}','bin/testparm',
- chmod=MODE_755, python_fixup=True, flat=True)
+
+bld.RECURSE('bin')