summaryrefslogtreecommitdiff
path: root/source4/scripting
diff options
context:
space:
mode:
Diffstat (limited to 'source4/scripting')
-rwxr-xr-xsource4/scripting/bin/upgradeprovision583
-rwxr-xr-xsource4/scripting/devel/chgtdcpass63
-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
7 files changed, 752 insertions, 259 deletions
diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision
index 48c4ce63b8..deb50e36fb 100755
--- a/source4/scripting/bin/upgradeprovision
+++ b/source4/scripting/bin/upgradeprovision
@@ -43,20 +43,22 @@ from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
MessageElement, Message, Dn)
from samba import param
from samba.provision import (find_setup_dir, get_domain_descriptor,
- get_config_descriptor, secretsdb_self_join,
+ get_config_descriptor,
ProvisioningError, get_last_provision_usn,
get_max_usn, update_provision_usn)
from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
-from samba.dcerpc import security, drsblobs
+from samba.dcerpc import security, drsblobs, xattr
from samba.ndr import ndr_unpack
-from samba.dcerpc.misc import SEC_CHAN_BDC
from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
find_provision_key_parameters, get_ldbs,
usn_in_range, identic_rename, get_diff_sddls,
update_secrets, CHANGE, ERROR, SIMPLE,
CHANGEALL, GUESS, CHANGESD, PROVISION,
updateOEMInfo, getOEMInfo, update_gpo,
- delta_update_basesamdb, update_policyids)
+ delta_update_basesamdb, update_policyids,
+ update_machine_account_password,
+ search_constructed_attrs_stored,
+ increment_calculated_keyversion_number)
replace=2**FLAG_MOD_REPLACE
add=2**FLAG_MOD_ADD
@@ -103,7 +105,9 @@ hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,
"wellKnownObjects":replace, "privilege":never,
"defaultSecurityDescriptor": replace,
"rIDAvailablePool": never,
- "defaultSecurityDescriptor": replace + add }
+ "defaultSecurityDescriptor": replace + add,
+ "isMemberOfPartialAttributeSet": delete,
+ "attributeDisplayNames": replace + add}
backlinked = []
@@ -285,7 +289,7 @@ def print_provision_key_parameters(names):
message(GUESS, "domainlevel :" + str(names.domainlevel))
-def handle_special_case(att, delta, new, old, usn):
+def handle_special_case(att, delta, new, old, usn, basedn, aldb):
"""Define more complicate update rules for some attributes
:param att: The attribute to be updated
@@ -294,6 +298,8 @@ def handle_special_case(att, delta, new, old, usn):
:param new: The reference object
:param old: The Updated object
:param usn: The highest usn modified by a previous (upgrade)provision
+ :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
discarding it"""
@@ -302,6 +308,23 @@ def handle_special_case(att, delta, new, old, usn):
# highest usn as otherwise the replPropertyMetaData will guide us more
# correctly
if usn is None:
+ if (att == "sPNMappings" and flag == FLAG_MOD_REPLACE and
+ ldb.Dn(aldb, "CN=Directory Service,CN=Windows NT,"
+ "CN=Services,CN=Configuration,%s" % basedn)
+ == old[0].dn):
+ return True
+ if (att == "userAccountControl" and flag == FLAG_MOD_REPLACE and
+ ldb.Dn(aldb, "CN=Administrator,CN=Users,%s" % basedn)
+ == old[0].dn):
+ message(SIMPLE, "We suggest that you change the userAccountControl"
+ " for user Administrator from value %d to %d" %
+ (int(str(old[0][att])), int(str(new[0][att]))))
+ return False
+ if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
+ if (long(str(old[0][att])) == 0):
+ delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
+ return True
+
if (att == "member" and flag == FLAG_MOD_REPLACE):
hash = {}
newval = []
@@ -320,7 +343,7 @@ def handle_special_case(att, delta, new, old, usn):
delta.remove(att)
return True
- if (att in ("gPLink", "gPCFileSysPath") and
+ if (att in ("gPLink", "gPCFileSysPath") and
flag == FLAG_MOD_REPLACE and
str(new[0].dn).lower() == str(old[0].dn).lower()):
delta.remove(att)
@@ -516,30 +539,40 @@ def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
empty = Message()
delta = samdb.msg_diff(empty, reference[0])
delta.dn
- if delta.get("objectSid"):
- sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
- m = re.match(r".*-(\d+)$", sid)
- if m and int(m.group(1))>999:
- delta.remove("objectSid")
- for att in hashAttrNotCopied.keys():
- delta.remove(att)
- for att in backlinked:
- delta.remove(att)
- depend_on_yettobecreated = None
- for att in dn_syntax_att:
- depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
- delta.get(str(att)))
- if depend_on_yet_tobecreated is not None:
- message(CHANGE, "Object %s depends on %s in attribute %s,"
- "delaying the creation" % (dn,
- depend_on_yet_tobecreated, att))
- return False
+ skip = False
+ try:
+ if str(reference[0].get("cn")) == "RID Set":
+ for klass in reference[0].get("objectClass"):
+ if str(klass).lower == "ridset":
+ skip = True
+ finally:
+ if delta.get("objectSid"):
+ sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
+ m = re.match(r".*-(\d+)$", sid)
+ if m and int(m.group(1))>999:
+ delta.remove("objectSid")
+ for att in hashAttrNotCopied.keys():
+ delta.remove(att)
+ for att in backlinked:
+ delta.remove(att)
+ depend_on_yettobecreated = None
+ for att in dn_syntax_att:
+ depend_on_yet_tobecreated = check_dn_nottobecreated(hash, index,
+ delta.get(str(att)))
+ if depend_on_yet_tobecreated is not None:
+ message(CHANGE, "Object %s depends on %s in attribute %s,"
+ "delaying the creation" % (dn,
+ depend_on_yet_tobecreated, att))
+ return False
- delta.dn = dn
- message(CHANGE,"Object %s will be added" % dn)
- samdb.add(delta, ["relax:0"])
+ delta.dn = dn
+ if not skip:
+ message(CHANGE,"Object %s will be added" % dn)
+ samdb.add(delta, ["relax:0"])
+ else:
+ message(CHANGE,"Object %s was skipped" % dn)
- return True
+ return True
def gen_dn_index_hash(listMissing):
"""Generate a hash associating the DN to its creation order
@@ -813,7 +846,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
# idea to change it
delta.remove(att)
continue
- if handle_special_case(att, delta, reference, current, usns):
+ 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
@@ -827,16 +860,16 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
if attrUSN == -1:
# This attribute was last modified by another DC forget
# about it
- message(CHANGE, "%sAttribute: %s has been"
+ message(CHANGE, "%sAttribute: %s has been"
"created/modified/deleted by another DC,"
" do nothing" % (txt, att ))
txt = ""
delta.remove(att)
continue
elif not usn_in_range(int(attrUSN), usns):
- message(CHANGE, "%sAttribute: %s has been"
- "created/modified/deleted not during a"
- " provision or upgradeprovision: current"
+ message(CHANGE, "%sAttribute: %s has been"
+ "created/modified/deleted not during a"
+ " provision or upgradeprovision: current"
" usn %d , do nothing" % (txt, att, attrUSN))
txt = ""
delta.remove(att)
@@ -845,13 +878,13 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
if att == "defaultSecurityDescriptor":
defSDmodified = True
if attrUSN:
- message(CHANGE, "%sAttribute: %s will be modified"
- "/deleted it was last modified"
- "during a provision, current usn:"
+ 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"
+ message(CHANGE, "%sAttribute: %s will be added because"
" it hasn't existed before " % (txt, att))
txt = ""
continue
@@ -871,7 +904,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
if not hashOverwrittenAtt.has_key(att):
if msgElt.flags() != FLAG_MOD_ADD:
if not handle_special_case(att, delta, reference, current,
- usns):
+ usns, basedn, samdb):
if opts.debugchange or opts.debugall:
try:
dump_denied_change(dn, att,
@@ -893,7 +926,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns, invocationid):
delta.dn = dn
if len(delta.items()) >1:
attributes=", ".join(delta.keys())
- message(CHANGE, "%s is different from the reference one, changed"
+ message(CHANGE, "%s is different from the reference one, changed"
" attributes: %s\n" % (dn, attributes))
changed += 1
samdb.modify(delta)
@@ -1002,6 +1035,7 @@ def check_updated_sd(ref_sam, cur_sam, names):
str(reference[i]["nTSecurityDescriptor"]))
hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
+
for i in range(0, len(current)):
key = str(current[i]["dn"]).lower()
if hash.has_key(key):
@@ -1073,7 +1107,7 @@ def rebuild_sd(samdb, names):
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.configdn) or
str(obj["dn"]) == str(names.schemadn)):
hash[str(obj["dn"])] = obj["whenCreated"]
@@ -1113,6 +1147,18 @@ def removeProvisionUSN(samdb):
delta.dn = entry[0].dn
samdb.modify(delta)
+def remove_stored_generated_attrs(paths, creds, session, lp):
+ """Remove previously stored constructed attributes
+
+ :param paths: List of paths for different provision objects
+ from the upgraded provision
+ :param creds: A credential object
+ :param session: A session object
+ :param lp: A line parser object
+ :return: An associative array whose key are the different constructed
+ attributes and the value the dn where this attributes were found.
+ """
+
def simple_update_basesamdb(newpaths, paths, names):
"""Update the provision container db: sam.ldb
@@ -1185,49 +1231,114 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema):
return 0
-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
+def copyxattrs(dir, refdir):
+ """ Copy owner, groups, extended ACL and NT acls from
+ a reference dir to a destination dir
+
+ Both dir are supposed to hold the same files
+ :param dir: Destination dir
+ :param refdir: Reference directory"""
+
+ noxattr = 0
+ for root, dirs, files in os.walk(dir, topdown=True):
+ for name in files:
+ subdir=root[len(dir):]
+ ref = os.path.join("%s%s" % (refdir, subdir), name)
+ statsinfo = os.stat(ref)
+ tgt = os.path.join(root, name)
+ try:
+
+ os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid)
+ # Get the xattr attributes if any
+ try:
+ attribute = samba.xattr_native.wrap_getxattr(ref,
+ xattr.XATTR_NTACL_NAME)
+ samba.xattr_native.wrap_setxattr(tgt,
+ xattr.XATTR_NTACL_NAME,
+ attribute)
+ except:
+ noxattr = 1
+ attribute = samba.xattr_native.wrap_getxattr(ref,
+ "system.posix_acl_access")
+ samba.xattr_native.wrap_setxattr(tgt,
+ "system.posix_acl_access",
+ attribute)
+ except:
+ continue
+ for name in dirs:
+ subdir=root[len(dir):]
+ ref = os.path.join("%s%s" % (refdir, subdir), name)
+ statsinfo = os.stat(ref)
+ tgt = os.path.join(root, name)
+ try:
+ os.chown(os.path.join(root, name), statsinfo.st_uid,
+ statsinfo.st_gid)
+ try:
+ attribute = samba.xattr_native.wrap_getxattr(ref,
+ xattr.XATTR_NTACL_NAME)
+ samba.xattr_native.wrap_setxattr(tgt,
+ xattr.XATTR_NTACL_NAME,
+ attribute)
+ except:
+ noxattr = 1
+ attribute = samba.xattr_native.wrap_getxattr(ref,
+ "system.posix_acl_access")
+ samba.xattr_native.wrap_setxattr(tgt,
+ "system.posix_acl_access",
+ attribute)
+
+ except:
+ continue
+
+
+def backup_provision(paths, dir):
+ """This function backup the provision files so that a rollback
+ is possible
+
+ :param paths: Paths to different objects
+ :param dir: Directory where to store the backup
+ """
- :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"""
+ shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol"))
+ copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol)
+ shutil.copy2(paths.samdb, dir)
+ shutil.copy2(paths.secrets, dir)
+ shutil.copy2(paths.idmapdb, dir)
+ shutil.copy2(paths.privilege, dir)
+ if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
+ shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
+ shutil.copy2(paths.smbconf, dir)
+ shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
- message(SIMPLE, "Update machine account")
- 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 = Message(res[0].dn)
- machinepass = samba.generate_random_password(128, 255)
- msg["userPassword"] = MessageElement(machinepass, 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 or sambaopts._lp.get('realm'),
- domainsid=names.domainsid,
- dnsdomain=names.dnsdomain,
- netbiosname=names.netbiosname,
- machinepass=machinepass,
- key_version_number=kvno,
- secure_channel_type=secChanType)
+ samldbdir = os.path.join(paths.private_dir, "sam.ldb.d")
+ if not os.path.isdir(samldbdir):
+ samldbdir = paths.private_dir
+ schemaldb = os.path.join(paths.private_dir, "schema.ldb")
+ configldb = os.path.join(paths.private_dir, "configuration.ldb")
+ usersldb = os.path.join(paths.private_dir, "users.ldb")
+ shutil.copy2(schemaldb, dir)
+ shutil.copy2(usersldb, dir)
+ shutil.copy2(configldb, dir)
else:
- raise ProvisioningError("Unable to find a Secure Channel"
- "of type SEC_CHAN_BDC")
+ shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
+
+def sync_calculated_attributes(samdb, names):
+ """Synchronize attributes used for constructed ones, with the
+ old constructed that were stored in the database.
+
+ This apply for instance to msds-keyversionnumber that was
+ stored and that is now constructed from replpropertymetadata.
+
+ :param samdb: An LDB object attached to the currently upgraded samdb
+ :param names: Various key parameter about current provision.
+ """
+ listAttrs = ["msDs-KeyVersionAttribute"]
+ hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
+ increment_calculated_keyversion_number(samdb, names.rootdn, hash)
+
def setup_path(file):
return os.path.join(setup_dir, file)
@@ -1252,12 +1363,13 @@ def setup_path(file):
# A) When alpha9 or alphaxx is present
# The base sam.ldb file is updated by looking at the difference between
# referrence one and the current one. Everything is copied with the
-# exception of lastProvisionUSN attributes. The highest used USN
-# is fetched so that changed by upgradeprovision usn can be tracked
+# exception of lastProvisionUSN attributes.
# B) Other case (it reflect that that provision was done before alpha9)
# The base sam.ldb of the reference provision is copied over
# the current one, if necessary ldb related to partitions are moved
# and renamed
+# The highest used USN is fetched so that changed by upgradeprovision
+# usn can be tracked
# 12)A Schema object is created, it will be used to provide a complete
# schema to current provision during update (as the schema of the
# current provision might not be complete and so won't allow some
@@ -1380,157 +1492,172 @@ if __name__ == '__main__':
minUSN = 0
# 2)
ldbs = get_ldbs(paths, creds, session, lp)
- ldbs.startTransactions()
-
- # 3) Guess all the needed names (variables in fact) from the current
- # provision.
- names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
- paths, smbconf, lp)
- # 4)
- lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
- if lastProvisionUSNs is not None:
- message(CHANGE,
- "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
-
- # 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 fails, checks messages"
- " and correct them 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, setup_dir, 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()
- # 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
+ backupdir = tempfile.mkdtemp(dir=paths.private_dir,
+ prefix="backupprovision")
+ backup_provision(paths, backupdir)
+ try:
+ ldbs.startTransactions()
+
+ # 3) Guess all the needed names (variables in fact) from the current
+ # provision.
+ names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
+ paths, smbconf, lp)
+ # 4)
+ lastProvisionUSNs = get_last_provision_usn(ldbs.sam)
+ if lastProvisionUSNs is not None:
+ message(CHANGE,
+ "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs))
+
+ # 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 fails, checks messages"
+ " and correct them 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, setup_dir, 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()
- delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message)
+
+ # 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
+ 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()
- else:
- # 11) B
- simple_update_basesamdb(newpaths, paths, names)
- ldbs = get_ldbs(paths, creds, session, lp)
- removeProvisionUSN(ldbs.sam)
- ldbs.startTransactions()
- # 12)
- schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
- serverdn=str(names.serverdn))
- # 13)
- if opts.full:
- if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
- schema):
- message(SIMPLE, "Rollbacking every changes. Check the reason"
- " of the problem")
- message(SIMPLE, "In any case your system as it was before"
- " the upgrade")
- ldbs.groupedRollback()
- new_ldbs.groupedRollback()
- shutil.rmtree(provisiondir)
- sys.exit(1)
- # 14)
- update_secrets(new_ldbs.secrets, ldbs.secrets, message)
- # 15)
- 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)):
+ # 12)
+ schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn),
+ serverdn=str(names.serverdn))
+
+ # 13)
+ if opts.full:
+ if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
+ schema):
+ message(SIMPLE, "Rollbacking every changes. Check the reason"
+ " of the problem")
+ message(SIMPLE, "In any case your system as it was before"
+ " the upgrade")
+ ldbs.groupedRollback()
+ new_ldbs.groupedRollback()
+ shutil.rmtree(provisiondir)
+ sys.exit(1)
+ else:
+ sync_calculated_attributes(ldbs.sam, names)
+ # 14)
+ update_secrets(new_ldbs.secrets, ldbs.secrets, message)
+ # 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)
-
- # 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:
- 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")
- else:
- try:
- 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 !
- shutil.rmtree(provisiondir)
+ 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:
+ 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")
+ else:
+ try:
+ 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 !
+ shutil.rmtree(provisiondir)
+ except StandardError, err:
+ message(ERROR,"A problem has occured when trying to upgrade your provision,"
+ " a full backup is located at %s" % backupdir)
+ if opts.changeall:
+ (typ, val, tb) = sys.exc_info()
+ traceback.print_exception(typ, val, tb)
diff --git a/source4/scripting/devel/chgtdcpass b/source4/scripting/devel/chgtdcpass
new file mode 100755
index 0000000000..1030531363
--- /dev/null
+++ b/source4/scripting/devel/chgtdcpass
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+#
+# Copyright (C) Matthieu Patou <mat@matws.net> 2010
+#
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import optparse
+import sys
+# Allow to run from s4 source directory (without installing samba)
+sys.path.insert(0, "bin/python")
+
+import samba.getopt as options
+from samba.credentials import DONT_USE_KERBEROS
+from samba.auth import system_session
+from samba import param
+from samba.upgradehelpers import (get_paths,
+ find_provision_key_parameters, get_ldbs,
+ update_machine_account_password)
+
+__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)
+
+opts = parser.parse_args()[0]
+
+
+lp = sambaopts.get_loadparm()
+smbconf = lp.configfile
+creds = credopts.get_credentials(lp)
+creds.set_kerberos_state(DONT_USE_KERBEROS)
+
+
+if __name__ == '__main__':
+ paths = get_paths(param, smbconf=smbconf)
+ session = system_session()
+
+ ldbs = get_ldbs(paths, creds, session, lp)
+ ldbs.startTransactions()
+
+ names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap,
+ paths, smbconf, lp)
+
+ update_machine_account_password(ldbs.sam, ldbs.secrets, names)
+ ldbs.groupedCommit()
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: