+++ /dev/null
-#!/usr/bin/env python3
-#
-# Copyright Stefan Metzmacher 2011-2012
-#
-# 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/>.
-#
-# This is useful to sync passwords from an AD domain.
-#
-# $
-# $ source4/scripting/devel/repl_cleartext_pwd.py \
-# -Uadministrator%A1b2C3d4 \
-# 172.31.9.219 DC=bla,DC=base /tmp/cookie cleartext_utf8 131085 displayName
-# # starting at usn[0]
-# dn: CN=Test User1,CN=Users,DC=bla,DC=base
-# cleartext_utf8: A1b2C3d4
-# displayName:: VABlAHMAdAAgAFUAcwBlAHIAMQA=
-#
-# # up to usn[16449]
-# $
-# $ source4/scripting/devel/repl_cleartext_pwd.py \
-# -Uadministrator%A1b2C3d4
-# 172.31.9.219 DC=bla,DC=base cookie_file cleartext_utf8 131085 displayName
-# # starting at usn[16449]
-# # up to usn[16449]
-# $
-#
-
-import sys
-
-# Find right direction when running from source tree
-sys.path.insert(0, "bin/python")
-
-import samba.getopt as options
-from optparse import OptionParser
-
-from samba.dcerpc import drsuapi, drsblobs, misc
-from samba.ndr import ndr_pack, ndr_unpack, ndr_print
-from samba import arcfour_encrypt
-
-import binascii
-import hashlib
-import struct
-import os
-
-from ldif import LDIFWriter
-
-
-class globals:
- def __init__(self):
- self.global_objs = {}
- self.ldif = LDIFWriter(sys.stdout)
-
- def add_attr(self, dn, attname, vals):
- if dn not in self.global_objs:
- self.global_objs[dn] = {}
- self.global_objs[dn][attname] = vals
-
- def print_all(self):
- for dn, obj in self.global_objs.items():
- self.ldif.unparse(dn, obj)
- continue
- self.global_objs = {}
-
-
-def attid_equal(a1, a2):
- return (a1 & 0xffffffff) == (a2 & 0xffffffff)
-
-
-########### main code ###########
-if __name__ == "__main__":
- parser = OptionParser("repl_cleartext_pwd.py [options] server dn cookie_file clear_utf8_name [attid attname attmode] [clear_utf16_name")
- sambaopts = options.SambaOptions(parser)
- credopts = options.CredentialsOptions(parser)
- parser.add_option_group(credopts)
-
- (opts, args) = parser.parse_args()
-
- if len(args) == 4:
- pass
- elif len(args) == 7:
- pass
- elif len(args) >= 8:
- pass
- else:
- parser.error("more arguments required - given=%d" % (len(args)))
-
- server = args[0]
- dn = args[1]
- cookie_file = args[2]
- if len(cookie_file) == 0:
- cookie_file = None
- clear_utf8_name = args[3]
- if len(args) >= 7:
- try:
- attid = int(args[4], 16)
- except Exception:
- attid = int(args[4])
- attname = args[5]
- attmode = args[6]
- if attmode not in ["raw", "utf8"]:
- parser.error("attmode should be 'raw' or 'utf8'")
- else:
- attid = -1
- attname = None
- attmode = "raw"
- if len(args) >= 8:
- clear_utf16_name = args[7]
- else:
- clear_utf16_name = None
-
- lp = sambaopts.get_loadparm()
- creds = credopts.get_credentials(lp)
-
- if not creds.authentication_requested():
- parser.error("You must supply credentials")
-
- gls = globals()
- try:
- f = open(cookie_file, 'r')
- store_blob = f.read()
- f.close()
-
- store_hdr = store_blob[0:28]
- (store_version,
- store_dn_len, store_dn_ofs,
- store_hwm_len, store_hwm_ofs,
- store_utdv_len, store_utdv_ofs) = \
- struct.unpack("<LLLLLLL", store_hdr)
-
- store_dn = store_blob[store_dn_ofs:store_dn_ofs + store_dn_len]
- store_hwm_blob = store_blob[store_hwm_ofs:store_hwm_ofs + store_hwm_len]
- store_utdv_blob = store_blob[store_utdv_ofs:store_utdv_ofs + store_utdv_len]
-
- store_hwm = ndr_unpack(drsuapi.DsReplicaHighWaterMark, store_hwm_blob)
- store_utdv = ndr_unpack(drsblobs.replUpToDateVectorBlob, store_utdv_blob)
-
- assert store_dn == dn
- # print "%s" % ndr_print(store_hwm)
- # print "%s" % ndr_print(store_utdv)
- except Exception:
- store_dn = dn
- store_hwm = drsuapi.DsReplicaHighWaterMark()
- store_hwm.tmp_highest_usn = 0
- store_hwm.reserved_usn = 0
- store_hwm.highest_usn = 0
- store_utdv = None
-
- binding_str = "ncacn_ip_tcp:%s[spnego,seal]" % server
-
- drs_conn = drsuapi.drsuapi(binding_str, lp, creds)
-
- bind_info = drsuapi.DsBindInfoCtr()
- bind_info.length = 28
- bind_info.info = drsuapi.DsBindInfo28()
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_BASE
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
- bind_info.info.supported_extensions |= drsuapi.DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
- (info, drs_handle) = drs_conn.DsBind(misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID), bind_info)
-
- null_guid = misc.GUID()
-
- naming_context = drsuapi.DsReplicaObjectIdentifier()
- naming_context.dn = dn
- highwatermark = store_hwm
- uptodateness_vector = None
- if store_utdv is not None:
- uptodateness_vector = drsuapi.DsReplicaCursorCtrEx()
- if store_utdv.version == 1:
- uptodateness_vector.cursors = store_utdv.cursors
- elif store_utdv.version == 2:
- cursors = []
- for i in range(0, store_utdv.ctr.count):
- cursor = drsuapi.DsReplicaCursor()
- cursor.source_dsa_invocation_id = store_utdv.ctr.cursors[i].source_dsa_invocation_id
- cursor.highest_usn = store_utdv.ctr.cursors[i].highest_usn
- cursors.append(cursor)
- uptodateness_vector.cursors = cursors
-
- req8 = drsuapi.DsGetNCChangesRequest8()
-
- req8.destination_dsa_guid = null_guid
- req8.source_dsa_invocation_id = null_guid
- req8.naming_context = naming_context
- req8.highwatermark = highwatermark
- req8.uptodateness_vector = uptodateness_vector
- req8.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC |
- drsuapi.DRSUAPI_DRS_PER_SYNC |
- drsuapi.DRSUAPI_DRS_GET_ANC |
- drsuapi.DRSUAPI_DRS_NEVER_SYNCED |
- drsuapi.DRSUAPI_DRS_WRIT_REP)
- req8.max_object_count = 402
- req8.max_ndr_size = 402116
- req8.extended_op = 0
- req8.fsmo_info = 0
- req8.partial_attribute_set = None
- req8.partial_attribute_set_ex = None
- req8.mapping_ctr.num_mappings = 0
- req8.mapping_ctr.mappings = None
-
- user_session_key = drs_conn.user_session_key
-
- print("# starting at usn[%d]" % (highwatermark.highest_usn))
-
- while True:
- (level, ctr) = drs_conn.DsGetNCChanges(drs_handle, 8, req8)
- if ctr.first_object is None and ctr.object_count != 0:
- raise RuntimeError("DsGetNCChanges: NULL first_object with object_count=%u" % (ctr.object_count))
-
- obj_item = ctr.first_object
- while obj_item is not None:
- obj = obj_item.object
-
- if obj.identifier is None:
- obj_item = obj_item.next_object
- continue
-
- # print '%s' % obj.identifier.dn
-
- is_deleted = False
- for i in range(0, obj.attribute_ctr.num_attributes):
- attr = obj.attribute_ctr.attributes[i]
- if attid_equal(attr.attid, drsuapi.DRSUAPI_ATTID_isDeleted):
- is_deleted = True
- if is_deleted:
- obj_item = obj_item.next_object
- continue
-
- spl_crypt = None
- attvals = None
- for i in range(0, obj.attribute_ctr.num_attributes):
- attr = obj.attribute_ctr.attributes[i]
- if attid_equal(attr.attid, attid):
- attvals = []
- for j in range(0, attr.value_ctr.num_values):
- assert attr.value_ctr.values[j].blob is not None
- val_raw = attr.value_ctr.values[j].blob
- val = None
- if attmode == "utf8":
- val_unicode = unicode(val_raw, 'utf-16-le')
- val = val_unicode.encode('utf-8')
- elif attmode == "raw":
- val = val_raw
- else:
- assert False, "attmode[%s]" % attmode
- attvals.append(val)
- if not attid_equal(attr.attid, drsuapi.DRSUAPI_ATTID_supplementalCredentials):
- continue
- assert attr.value_ctr.num_values <= 1
- if attr.value_ctr.num_values == 0:
- break
- assert attr.value_ctr.values[0].blob is not None
- spl_crypt = attr.value_ctr.values[0].blob
-
- if spl_crypt is None:
- obj_item = obj_item.next_object
- continue
-
- assert len(spl_crypt) >= 20
- confounder = spl_crypt[0:16]
- enc_buffer = spl_crypt[16:]
-
- m5 = hashlib.md5()
- m5.update(user_session_key)
- m5.update(confounder)
- enc_key = m5.digest()
-
- # RC4 encryption is the same as decryption
- plain_buffer = arcfour_encrypt(enc_key, enc_buffer)
-
- (crc32_v) = struct.unpack("<L", plain_buffer[0:4])
- attr_val = plain_buffer[4:]
- crc32_c = binascii.crc32(attr_val) & 0xffffffff
- assert int(crc32_v[0]) == int(crc32_c), "CRC32 0x%08X != 0x%08X" % (crc32_v[0], crc32_c)
-
- spl = ndr_unpack(drsblobs.supplementalCredentialsBlob, attr_val)
-
- # print '%s' % ndr_print(spl)
-
- cleartext_hex = None
-
- for i in range(0, spl.sub.num_packages):
- pkg = spl.sub.packages[i]
- if pkg.name != "Primary:CLEARTEXT":
- continue
- cleartext_hex = pkg.data
-
- if cleartext_hex is not None:
- cleartext_utf16 = binascii.a2b_hex(cleartext_hex)
- if clear_utf16_name is not None:
- gls.add_attr(obj.identifier.dn, clear_utf16_name, [cleartext_utf16])
- try:
- cleartext_unicode = unicode(cleartext_utf16, 'utf-16-le')
- cleartext_utf8 = cleartext_unicode.encode('utf-8')
- gls.add_attr(obj.identifier.dn, clear_utf8_name, [cleartext_utf8])
- except Exception:
- pass
-
- if attvals is not None:
- gls.add_attr(obj.identifier.dn, attname, attvals)
-
- krb5_old_hex = None
-
- for i in range(0, spl.sub.num_packages):
- pkg = spl.sub.packages[i]
- if pkg.name != "Primary:Kerberos":
- continue
- krb5_old_hex = pkg.data
-
- if krb5_old_hex is not None:
- krb5_old_raw = binascii.a2b_hex(krb5_old_hex)
- krb5_old = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb5_old_raw, allow_remaining=True)
-
- # print '%s' % ndr_print(krb5_old)
-
- krb5_new_hex = None
-
- for i in range(0, spl.sub.num_packages):
- pkg = spl.sub.packages[i]
- if pkg.name != "Primary:Kerberos-Newer-Keys":
- continue
- krb5_new_hex = pkg.data
-
- if krb5_new_hex is not None:
- krb5_new_raw = binascii.a2b_hex(krb5_new_hex)
- krb5_new = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb5_new_raw, allow_remaining=True)
-
- # print '%s' % ndr_print(krb5_new)
-
- obj_item = obj_item.next_object
-
- gls.print_all()
-
- if ctr.more_data == 0:
- store_hwm = ctr.new_highwatermark
-
- store_utdv = drsblobs.replUpToDateVectorBlob()
- store_utdv.version = ctr.uptodateness_vector.version
- store_utdv_ctr = store_utdv.ctr
- store_utdv_ctr.count = ctr.uptodateness_vector.count
- store_utdv_ctr.cursors = ctr.uptodateness_vector.cursors
- store_utdv.ctr = store_utdv_ctr
-
- # print "%s" % ndr_print(store_hwm)
- # print "%s" % ndr_print(store_utdv)
-
- store_hwm_blob = ndr_pack(store_hwm)
- store_utdv_blob = ndr_pack(store_utdv)
-
- #
- # uint32_t version '1'
- # uint32_t dn_str_len
- # uint32_t dn_str_ofs
- # uint32_t hwm_blob_len
- # uint32_t hwm_blob_ofs
- # uint32_t utdv_blob_len
- # uint32_t utdv_blob_ofs
- store_hdr_len = 7 * 4
- dn_ofs = store_hdr_len
- dn_bytes = dn.encode('utf8')
- hwm_ofs = dn_ofs + len(dn_bytes)
- utdv_ofs = hwm_ofs + len(store_hwm_blob)
- store_blob = struct.pack("<LLLLLLL", 1,
- len(dn_bytes), dn_ofs,
- len(store_hwm_blob), hwm_ofs,
- len(store_utdv_blob), utdv_ofs) + \
- dn_bytes + store_hwm_blob + store_utdv_blob
-
- tmp_file = "%s.tmp" % cookie_file
- f = open(tmp_file, 'wb')
- f.write(store_blob)
- f.close()
- os.rename(tmp_file, cookie_file)
-
- print("# up to usn[%d]" % (ctr.new_highwatermark.highest_usn))
- break
- print("# up to tmp_usn[%d]" % (ctr.new_highwatermark.highest_usn))
- req8.highwatermark = ctr.new_highwatermark