#!/usr/bin/env python
#
# 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 .
#
# 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]
# $
#
from __future__ import print_function
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
import binascii
import hashlib
import Crypto.Cipher.ARC4
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("= 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 = Crypto.Cipher.ARC4.new(enc_key)
plain_buffer = rc4.decrypt(enc_buffer)
(crc32_v) = struct.unpack("