summaryrefslogtreecommitdiff
path: root/source4/scripting/python
diff options
context:
space:
mode:
authorSimo Sorce <idra@samba.org>2010-07-15 20:50:06 -0400
committerSimo Sorce <idra@samba.org>2010-07-15 20:50:06 -0400
commit2f249538ac8f2a54d9c8f8dbf0107db2f33bfe16 (patch)
tree00eb61080648488e0d4d5e370f4d30827f4e9228 /source4/scripting/python
parent0ab8e8be62bcbb1f6441f745736fcee7cbd559eb (diff)
parent5f8678f34be57ccbbf9d9c93ee34b1d8f09c75c4 (diff)
downloadsamba-2f249538ac8f2a54d9c8f8dbf0107db2f33bfe16.tar.gz
samba-2f249538ac8f2a54d9c8f8dbf0107db2f33bfe16.tar.bz2
samba-2f249538ac8f2a54d9c8f8dbf0107db2f33bfe16.zip
Merge branch 'master' of ssh://git.samba.org/data/git/samba
Diffstat (limited to 'source4/scripting/python')
-rw-r--r--source4/scripting/python/samba/provision.py38
-rw-r--r--source4/scripting/python/samba/samdb.py103
-rw-r--r--source4/scripting/python/samba/tests/dsdb.py113
-rw-r--r--source4/scripting/python/samba/tests/upgradeprovisionneeddc.py26
-rwxr-xr-xsource4/scripting/python/samba/upgradehelpers.py85
5 files changed, 334 insertions, 31 deletions
diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py
index 5ede869015..8be3f655d2 100644
--- a/source4/scripting/python/samba/provision.py
+++ b/source4/scripting/python/samba/provision.py
@@ -680,17 +680,16 @@ def secretsdb_self_join(secretsdb, domain,
"krb5Keytab",
"privateKeytab"]
-
+ #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
- msg["secureChannelType"] = str(secure_channel_type)
- msg["flatname"] = [domain]
+ msg["secureChannelType"] = [str(secure_channel_type)]
msg["objectClass"] = ["top", "primaryDomain"]
if realm is not None:
if dnsdomain is None:
dnsdomain = realm.lower()
msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
- msg["realm"] = realm
- msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
+ msg["realm"] = [realm]
+ msg["saltPrincipal"] = ["host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())]
msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
msg["privateKeytab"] = ["secrets.keytab"]
@@ -701,30 +700,39 @@ def secretsdb_self_join(secretsdb, domain,
if domainsid is not None:
msg["objectSid"] = [ndr_pack(domainsid)]
+ # This complex expression tries to ensure that we don't have more
+ # than one record for this SID, realm or netbios domain at a time,
+ # but we don't delete the old record that we are about to modify,
+ # because that would delete the keytab and previous password.
res = secretsdb.search(base="cn=Primary Domains",
attrs=attrs,
- expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
+ expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
scope=ldb.SCOPE_ONELEVEL)
for del_msg in res:
- if del_msg.dn is not msg.dn:
secretsdb.delete(del_msg.dn)
res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
if len(res) == 1:
- msg["priorSecret"] = res[0]["secret"]
- msg["priorWhenChanged"] = res[0]["whenChanged"]
+ msg["priorSecret"] = [res[0]["secret"][0]]
+ msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
- if res["privateKeytab"] is not None:
- msg["privateKeytab"] = res[0]["privateKeytab"]
+ try:
+ msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
+ except KeyError:
+ pass
- if res["krb5Keytab"] is not None:
- msg["krb5Keytab"] = res[0]["krb5Keytab"]
+ try:
+ msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
+ except KeyError:
+ pass
for el in msg:
- el.set_flags(ldb.FLAG_MOD_REPLACE)
- secretsdb.modify(msg)
+ if el != 'dn':
+ msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
+ secretsdb.modify(msg)
+ secretsdb.rename(res[0].dn, msg.dn)
else:
secretsdb.add(msg)
diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py
index f810926710..f358747b51 100644
--- a/source4/scripting/python/samba/samdb.py
+++ b/source4/scripting/python/samba/samdb.py
@@ -28,12 +28,16 @@ import samba
import ldb
import time
import base64
+from samba.ndr import ndr_unpack, ndr_pack
+from samba.dcerpc import drsblobs, misc
__docformat__ = "restructuredText"
class SamDB(samba.Ldb):
"""The SAM database."""
+ hash_oid_name = {}
+
def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
credentials=None, flags=0, options=None, global_schema=True,
auto_connect=True, am_rodc=False):
@@ -470,5 +474,104 @@ accountExpires: %u
def set_schema_from_ldb(self, ldb):
dsdb._dsdb_set_schema_from_ldb(self, ldb)
+ def get_attribute_from_attid(self, attid):
+ """ Get from an attid the associated attribute
+
+ :param attid: The attribute id for searched attribute
+ :return: The name of the attribute associated with this id
+ """
+ if len(self.hash_oid_name.keys()) == 0:
+ self._populate_oid_attid()
+ if self.hash_oid_name.has_key(self.get_oid_from_attid(attid)):
+ return self.hash_oid_name[self.get_oid_from_attid(attid)]
+ else:
+ return None
+
+
+ def _populate_oid_attid(self):
+ """Populate the hash hash_oid_name
+
+ This hash contains the oid of the attribute as a key and
+ its display name as a value
+ """
+ self.hash_oid_name = {}
+ res = self.search(expression="objectClass=attributeSchema",
+ controls=["search_options:1:2"],
+ attrs=["attributeID",
+ "lDAPDisplayName"])
+ if len(res) > 0:
+ for e in res:
+ strDisplay = str(e.get("lDAPDisplayName"))
+ self.hash_oid_name[str(e.get("attributeID"))] = strDisplay
+
+
+ def get_attribute_replmetadata_version(self, dn, att):
+ """ Get the version field trom the replPropertyMetaData for
+ the given field
+
+ :param dn: The on which we want to get the version
+ :param att: The name of the attribute
+ :return: The value of the version field in the replPropertyMetaData
+ for the given attribute. None if the attribute is not replicated
+ """
+
+ res = self.search(expression="dn=%s" % dn,
+ scope=ldb.SCOPE_SUBTREE,
+ controls=["search_options:1:2"],
+ attrs=["replPropertyMetaData"])
+ if len(res) == 0:
+ return None
+
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ ctr = repl.ctr
+ if len(self.hash_oid_name.keys()) == 0:
+ self._populate_oid_attid()
+ for o in ctr.array:
+ # Search for Description
+ att_oid = self.get_oid_from_attid(o.attid)
+ if self.hash_oid_name.has_key(att_oid) and\
+ att.lower() == self.hash_oid_name[att_oid].lower():
+ return o.version
+ return None
+
+
+ def set_attribute_replmetadata_version(self, dn, att, value):
+ res = self.search(expression="dn=%s" % dn,
+ scope=ldb.SCOPE_SUBTREE,
+ controls=["search_options:1:2"],
+ attrs=["replPropertyMetaData"])
+ if len(res) == 0:
+ return None
+
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ ctr = repl.ctr
+ now = samba.unix2nttime(int(time.time()))
+ found = False
+ if len(self.hash_oid_name.keys()) == 0:
+ self._populate_oid_attid()
+ for o in ctr.array:
+ # Search for Description
+ att_oid = self.get_oid_from_attid(o.attid)
+ if self.hash_oid_name.has_key(att_oid) and\
+ att.lower() == self.hash_oid_name[att_oid].lower():
+ found = True
+ seq = self.sequence_number(ldb.SEQ_NEXT)
+ o.version = value
+ o.originating_change_time = now
+ o.originating_invocation_id = misc.GUID(self.get_invocation_id())
+ o.originating_usn = seq
+ o.local_usn = seq
+ if found :
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob,
+ ldb.FLAG_MOD_REPLACE,
+ "replPropertyMetaData")
+ self.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+
+
def write_prefixes_from_schema(self):
dsdb._dsdb_write_prefixes_from_schema_to_ldb(self)
diff --git a/source4/scripting/python/samba/tests/dsdb.py b/source4/scripting/python/samba/tests/dsdb.py
index c19dbaab47..4a50c96e25 100644
--- a/source4/scripting/python/samba/tests/dsdb.py
+++ b/source4/scripting/python/samba/tests/dsdb.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Unix SMB/CIFS implementation. Tests for dsdb
+# Unix SMB/CIFS implementation. Tests for dsdb
# Copyright (C) Matthieu Patou <mat@matws.net> 2010
#
# This program is free software; you can redistribute it and/or modify
@@ -17,26 +17,117 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-import samba.dsdb
from samba.credentials import Credentials
from samba.samdb import SamDB
from samba.auth import system_session
from testtools.testcase import TestCase
+from samba.ndr import ndr_unpack, ndr_pack
+from samba.dcerpc import drsblobs
+import ldb
import os
+import samba
class DsdbTests(TestCase):
- def _baseprovpath(self):
+
+ def setUp(self):
+ super(DsdbTests, self).setUp()
+ self.lp = samba.param.LoadParm()
+ self.lp.load(os.path.join(os.path.join(self.baseprovpath(), "etc"), "smb.conf"))
+ self.creds = Credentials()
+ self.creds.guess(self.lp)
+ self.session = system_session()
+ self.samdb = SamDB(os.path.join(self.baseprovpath(), "private", "sam.ldb"),
+ session_info=self.session, credentials=self.creds,lp=self.lp)
+
+
+ def baseprovpath(self):
return os.path.join(os.environ['SELFTEST_PREFIX'], "dc")
+
def test_get_oid_from_attrid(self):
- lp = samba.param.LoadParm()
- lp.load(os.path.join(os.path.join(self._baseprovpath(), "etc"), "smb.conf"))
- creds = Credentials()
- creds.guess(lp)
- session = system_session()
- test_ldb = SamDB(os.path.join(self._baseprovpath(), "private", "sam.ldb"),
- session_info=session, credentials=creds,lp=lp)
- oid = test_ldb.get_oid_from_attid(591614)
+ oid = self.samdb.get_oid_from_attid(591614)
self.assertEquals(oid, "1.2.840.113556.1.4.1790")
+
+ def test_error_replpropertymetadata(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData"])
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ ctr = repl.ctr
+ for o in ctr.array:
+ # Search for Description
+ if o.attid == 13:
+ old_version = o.version
+ o.version = o.version + 1
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
+ self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+
+ def test_twoatt_replpropertymetadata(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData", "uSNChanged"])
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ ctr = repl.ctr
+ for o in ctr.array:
+ # Search for Description
+ if o.attid == 13:
+ old_version = o.version
+ o.version = o.version + 1
+ o.local_usn = long(str(res[0]["uSNChanged"])) + 1
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
+ msg["description"] = ldb.MessageElement("new val", ldb.FLAG_MOD_REPLACE, "description")
+ self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+
+ def test_set_replpropertymetadata(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["replPropertyMetaData", "uSNChanged"])
+ repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
+ str(res[0]["replPropertyMetaData"]))
+ ctr = repl.ctr
+ for o in ctr.array:
+ # Search for Description
+ if o.attid == 13:
+ old_version = o.version
+ o.version = o.version + 1
+ o.local_usn = long(str(res[0]["uSNChanged"])) + 1
+ o.originating_usn = long(str(res[0]["uSNChanged"])) + 1
+ replBlob = ndr_pack(repl)
+ msg = ldb.Message()
+ msg.dn = res[0].dn
+ msg["replPropertyMetaData"] = ldb.MessageElement(replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
+ self.samdb.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
+
+ def test_ok_get_attribute_from_attid(self):
+ self.assertEquals(self.samdb.get_attribute_from_attid(13), "description")
+
+ def test_ko_get_attribute_from_attid(self):
+ self.assertEquals(self.samdb.get_attribute_from_attid(11979), None)
+
+ def test_get_attribute_replmetadata_version(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["dn"])
+ self.assertEquals(len(res), 1)
+ dn = str(res[0].dn)
+ self.assertEqual(self.samdb.get_attribute_replmetadata_version(dn, "unicodePwd"), 1)
+
+ def test_set_attribute_replmetadata_version(self):
+ res = self.samdb.search(expression="cn=Administrator",
+ scope=ldb.SCOPE_SUBTREE,
+ attrs=["dn"])
+ self.assertEquals(len(res), 1)
+ dn = str(res[0].dn)
+ version = self.samdb.get_attribute_replmetadata_version(dn, "description")
+ self.samdb.set_attribute_replmetadata_version(dn, "description", version + 2)
+ self.assertEqual(self.samdb.get_attribute_replmetadata_version(dn, "description"), version + 2)
diff --git a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py b/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py
index b32ffc6155..e30906fc6b 100644
--- a/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py
+++ b/source4/scripting/python/samba/tests/upgradeprovisionneeddc.py
@@ -29,7 +29,8 @@ from samba.upgradehelpers import (get_paths, get_ldbs,
find_provision_key_parameters, identic_rename,
updateOEMInfo, getOEMInfo, update_gpo,
delta_update_basesamdb,
- search_constructed_attrs_stored)
+ search_constructed_attrs_stored,
+ increment_calculated_keyversion_number)
from samba.tests import env_loadparm, TestCaseInTempDir
from samba.tests.provision import create_dummy_secretsdb
import ldb
@@ -91,6 +92,29 @@ class UpgradeProvisionWithLdbTestCase(TestCaseInTempDir):
["msds-KeyVersionNumber"])
self.assertFalse(hashAtt.has_key("msds-KeyVersionNumber"))
+ def test_increment_calculated_keyversion_number(self):
+ dn = "CN=Administrator,CN=Users,%s" % self.names.rootdn
+ # We conctruct a simple hash for the user administrator
+ hash = {}
+ # And we want the version to be 140
+ hash[dn.lower()] = 140
+
+ increment_calculated_keyversion_number(self.ldbs.sam,
+ self.names.rootdn,
+ hash)
+ self.assertEqual(self.ldbs.sam.get_attribute_replmetadata_version(dn,
+ "unicodePwd"),
+ 140)
+ # This function should not decrement the version
+ hash[dn.lower()] = 130
+
+ increment_calculated_keyversion_number(self.ldbs.sam,
+ self.names.rootdn,
+ hash)
+ self.assertEqual(self.ldbs.sam.get_attribute_replmetadata_version(dn,
+ "unicodePwd"),
+ 140)
+
def test_identic_rename(self):
rootdn = "DC=samba,DC=example,DC=com"
diff --git a/source4/scripting/python/samba/upgradehelpers.py b/source4/scripting/python/samba/upgradehelpers.py
index 74a157d041..58106e0a70 100755
--- a/source4/scripting/python/samba/upgradehelpers.py
+++ b/source4/scripting/python/samba/upgradehelpers.py
@@ -35,8 +35,9 @@ import ldb
from samba.provision import (ProvisionNames, provision_paths_from_lp,
getpolicypath, set_gpo_acl, create_gpo_struct,
FILL_FULL, provision, ProvisioningError,
- setsysvolacl)
+ setsysvolacl, secretsdb_self_join)
from samba.dcerpc import misc, security, xattr
+from samba.dcerpc.misc import SEC_CHAN_BDC
from samba.ndr import ndr_unpack
from samba.samdb import SamDB
@@ -703,14 +704,48 @@ def update_gpo(paths, samdb, names, lp, message, force=0):
set_gpo_acl(paths.sysvol, names.dnsdomain, names.domainsid,
names.domaindn, samdb, lp)
except TypeError, e:
- message(ERROR, "Unable to set ACLs on policies related objects, if not using posix:eadb, you must be root to do it")
+ message(ERROR, "Unable to set ACLs on policies related objects,"
+ " if not using posix:eadb, you must be root to do it")
if resetacls:
try:
setsysvolacl(samdb, paths.netlogon, paths.sysvol, names.wheel_gid,
names.domainsid, names.dnsdomain, names.domaindn, lp)
except TypeError, e:
- message(ERROR, "Unable to set ACLs on sysvol share, if not using posix:eadb, you must be root to do it")
+ message(ERROR, "Unable to set ACLs on sysvol share, if not using"
+ "posix:eadb, you must be root to do it")
+
+def increment_calculated_keyversion_number(samdb, rootdn, hashDns):
+ """For a given hash associating dn and a number, this function will
+ update the replPropertyMetaData of each dn in the hash, so that the
+ calculated value of the msDs-KeyVersionNumber is equal or superior to the
+ one associated to the given dn.
+
+ :param samdb: An SamDB object pointing to the sam
+ :param rootdn: The base DN where we want to start
+ :param hashDns: A hash with dn as key and number representing the
+ minimum value of msDs-KeyVersionNumber that we want to
+ have
+ """
+ entry = samdb.search(expression='(objectClass=user)',
+ base=ldb.Dn(samdb,str(rootdn)),
+ scope=SCOPE_SUBTREE, attrs=["msDs-KeyVersionNumber"],
+ controls=["search_options:1:2"])
+ done = 0
+ if len(entry) == 0:
+ raise ProvisioningError("Unable to find msDs-KeyVersionNumber")
+ else:
+ for e in entry:
+ if hashDns.has_key(str(e.dn).lower()):
+ done = done + 1
+ val = e.get("msDs-KeyVersionNumber")
+ if not val:
+ continue
+ version = int(str(hashDns[str(e.dn).lower()]))
+ if int(str(val)) < version:
+ samdb.set_attribute_replmetadata_version(str(e.dn),
+ "unicodePwd",
+ version)
def delta_update_basesamdb(refsam, sam, creds, session, lp, message):
"""Update the provision container db: sam.ldb
@@ -770,6 +805,48 @@ def construct_existor_expr(attrs):
expr = "%s)"%expr
return expr
+def update_machine_account_password(samdb, secrets_ldb, names):
+ """Update (change) the password of the current DC both in the SAM db and in
+ secret one
+
+ :param samdb: An LDB object related to the sam.ldb file of a given provision
+ :param secrets_ldb: An LDB object related to the secrets.ldb file of a given
+ provision
+ :param names: List of key provision parameters"""
+
+ expression = "samAccountName=%s$" % names.netbiosname
+ secrets_msg = secrets_ldb.search(expression=expression,
+ attrs=["secureChannelType"])
+ if int(secrets_msg[0]["secureChannelType"][0]) == SEC_CHAN_BDC:
+ res = samdb.search(expression=expression, attrs=[])
+ assert(len(res) == 1)
+
+ msg = ldb.Message(res[0].dn)
+ machinepass = samba.generate_random_password(128, 255)
+ msg["userPassword"] = ldb.MessageElement(machinepass,
+ ldb.FLAG_MOD_REPLACE,
+ "userPassword")
+ samdb.modify(msg)
+
+ res = samdb.search(expression=("samAccountName=%s$" % names.netbiosname),
+ attrs=["msDs-keyVersionNumber"])
+ assert(len(res) == 1)
+ kvno = int(str(res[0]["msDs-keyVersionNumber"]))
+ secChanType = int(secrets_msg[0]["secureChannelType"][0])
+
+ secretsdb_self_join(secrets_ldb, domain=names.domain,
+ realm=names.realm,
+ domainsid=names.domainsid,
+ dnsdomain=names.dnsdomain,
+ netbiosname=names.netbiosname,
+ machinepass=machinepass,
+ key_version_number=kvno,
+ secure_channel_type=secChanType)
+ else:
+ raise ProvisioningError("Unable to find a Secure Channel"
+ "of type SEC_CHAN_BDC")
+
+
def search_constructed_attrs_stored(samdb, rootdn, attrs):
"""Search a given sam DB for calculated attributes that are
still stored in the db.
@@ -786,7 +863,7 @@ def search_constructed_attrs_stored(samdb, rootdn, attrs):
expr = construct_existor_expr(attrs)
if expr == "":
return hashAtt
- entry = samdb.search(expression=expr, base=ldb.Dn(samdb,str(rootdn)),
+ entry = samdb.search(expression=expr, base=ldb.Dn(samdb, str(rootdn)),
scope=SCOPE_SUBTREE, attrs=attrs,
controls=["search_options:1:2","bypassoperational:0"])
if len(entry) == 0: