3 # Copyright Stefan Metzmacher 2011-2012
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # This is useful to sync passwords from an AD domain.
21 # $ source4/scripting/devel/repl_cleartext_pwd.py \
22 # -Uadministrator%A1b2C3d4 \
23 # 172.31.9.219 DC=bla,DC=base /tmp/cookie cleartext_utf8 131085 displayName
24 # # starting at usn[0]
25 # dn: CN=Test User1,CN=Users,DC=bla,DC=base
26 # cleartext_utf8: A1b2C3d4
27 # displayName:: VABlAHMAdAAgAFUAcwBlAHIAMQA=
31 # $ source4/scripting/devel/repl_cleartext_pwd.py \
32 # -Uadministrator%A1b2C3d4
33 # 172.31.9.219 DC=bla,DC=base cookie_file cleartext_utf8 131085 displayName
34 # # starting at usn[16449]
41 # Find right direction when running from source tree
42 sys
.path
.insert(0, "bin/python")
44 import samba
.getopt
as options
45 from optparse
import OptionParser
47 from samba
.dcerpc
import drsuapi
, drsblobs
, misc
48 from samba
.ndr
import ndr_pack
, ndr_unpack
, ndr_print
52 import Crypto
.Cipher
.ARC4
56 from ldif
import LDIFWriter
61 self
.ldif
= LDIFWriter(sys
.stdout
)
63 def add_attr(self
, dn
, attname
, vals
):
64 if dn
not in self
.global_objs
:
65 self
.global_objs
[dn
] = {}
66 self
.global_objs
[dn
][attname
] = vals
69 for dn
, obj
in self
.global_objs
.items():
70 self
.ldif
.unparse(dn
, obj
)
74 def attid_equal(a1
,a2
):
75 return (a1
& 0xffffffff) == (a2
& 0xffffffff)
77 ########### main code ###########
78 if __name__
== "__main__":
79 parser
= OptionParser("repl_cleartext_pwd.py [options] server dn cookie_file clear_utf8_name [attid attname attmode] [clear_utf16_name")
80 sambaopts
= options
.SambaOptions(parser
)
81 credopts
= options
.CredentialsOptions(parser
)
82 parser
.add_option_group(credopts
)
84 (opts
, args
) = parser
.parse_args()
93 parser
.error("more arguments required - given=%d" % (len(args
)))
98 if len(cookie_file
) == 0:
100 clear_utf8_name
= args
[3]
103 attid
= int(args
[4], 16)
108 if attmode
not in ["raw", "utf8"]:
109 parser
.error("attmode should be 'raw' or 'utf8'")
115 clear_utf16_name
= args
[7]
117 clear_utf16_name
= None
119 lp
= sambaopts
.get_loadparm()
120 creds
= credopts
.get_credentials(lp
)
122 if not creds
.authentication_requested():
123 parser
.error("You must supply credentials")
127 f
= open(cookie_file
, 'r')
128 store_blob
= f
.read()
131 store_hdr
= store_blob
[0:28]
133 store_dn_len
, store_dn_ofs
, \
134 store_hwm_len
, store_hwm_ofs
, \
135 store_utdv_len
, store_utdv_ofs
) = \
136 struct
.unpack("<LLLLLLL", store_hdr
)
138 store_dn
= store_blob
[store_dn_ofs
:store_dn_ofs
+store_dn_len
]
139 store_hwm_blob
= store_blob
[store_hwm_ofs
:store_hwm_ofs
+store_hwm_len
]
140 store_utdv_blob
= store_blob
[store_utdv_ofs
:store_utdv_ofs
+store_utdv_len
]
142 store_hwm
= ndr_unpack(drsuapi
.DsReplicaHighWaterMark
, store_hwm_blob
)
143 store_utdv
= ndr_unpack(drsblobs
.replUpToDateVectorBlob
, store_utdv_blob
)
145 assert store_dn
== dn
146 #print "%s" % ndr_print(store_hwm)
147 #print "%s" % ndr_print(store_utdv)
150 store_hwm
= drsuapi
.DsReplicaHighWaterMark()
151 store_hwm
.tmp_highest_usn
= 0
152 store_hwm
.reserved_usn
= 0
153 store_hwm
.highest_usn
= 0
156 binding_str
= "ncacn_ip_tcp:%s[spnego,seal]" % server
158 drs_conn
= drsuapi
.drsuapi(binding_str
, lp
, creds
)
160 bind_info
= drsuapi
.DsBindInfoCtr()
161 bind_info
.length
= 28
162 bind_info
.info
= drsuapi
.DsBindInfo28()
163 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_BASE
164 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
165 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
166 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
167 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
168 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
169 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
170 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
171 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
172 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
173 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
174 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
175 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
176 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
177 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
178 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
179 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
180 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
181 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
182 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
183 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
184 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
185 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
186 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
187 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
188 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
189 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
190 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
191 (info
, drs_handle
) = drs_conn
.DsBind(misc
.GUID(drsuapi
.DRSUAPI_DS_BIND_GUID
), bind_info
)
193 null_guid
= misc
.GUID()
195 naming_context
= drsuapi
.DsReplicaObjectIdentifier()
196 naming_context
.dn
= dn
197 highwatermark
= store_hwm
198 uptodateness_vector
= None
199 if store_utdv
is not None:
200 uptodateness_vector
= drsuapi
.DsReplicaCursorCtrEx()
201 if store_utdv
.version
== 1:
202 uptodateness_vector
.cursors
= store_utdv
.cursors
203 elif store_utdv
.version
== 2:
205 for i
in range(0, store_utdv
.ctr
.count
):
206 cursor
= drsuapi
.DsReplicaCursor()
207 cursor
.source_dsa_invocation_id
= store_utdv
.ctr
.cursors
[i
].source_dsa_invocation_id
208 cursor
.highest_usn
= store_utdv
.ctr
.cursors
[i
].highest_usn
209 cursors
.append(cursor
)
210 uptodateness_vector
.cursors
= cursors
212 req8
= drsuapi
.DsGetNCChangesRequest8()
214 req8
.destination_dsa_guid
= null_guid
215 req8
.source_dsa_invocation_id
= null_guid
216 req8
.naming_context
= naming_context
217 req8
.highwatermark
= highwatermark
218 req8
.uptodateness_vector
= uptodateness_vector
219 req8
.replica_flags
= (drsuapi
.DRSUAPI_DRS_INIT_SYNC |
220 drsuapi
.DRSUAPI_DRS_PER_SYNC |
221 drsuapi
.DRSUAPI_DRS_GET_ANC |
222 drsuapi
.DRSUAPI_DRS_NEVER_SYNCED |
223 drsuapi
.DRSUAPI_DRS_WRIT_REP
)
224 req8
.max_object_count
= 402
225 req8
.max_ndr_size
= 402116
228 req8
.partial_attribute_set
= None
229 req8
.partial_attribute_set_ex
= None
230 req8
.mapping_ctr
.num_mappings
= 0
231 req8
.mapping_ctr
.mappings
= None
233 user_session_key
= drs_conn
.user_session_key
235 print "# starting at usn[%d]" % (highwatermark
.highest_usn
)
238 (level
, ctr
) = drs_conn
.DsGetNCChanges(drs_handle
, 8, req8
)
239 if ctr
.first_object
== None and ctr
.object_count
!= 0:
240 raise RuntimeError("DsGetNCChanges: NULL first_object with object_count=%u" % (ctr
.object_count
))
242 obj_item
= ctr
.first_object
243 while obj_item
is not None:
244 obj
= obj_item
.object
246 if obj
.identifier
is None:
247 obj_item
= obj_item
.next_object
250 #print '%s' % obj.identifier.dn
253 for i
in range(0, obj
.attribute_ctr
.num_attributes
):
254 attr
= obj
.attribute_ctr
.attributes
[i
]
255 if attid_equal(attr
.attid
, drsuapi
.DRSUAPI_ATTID_isDeleted
):
258 obj_item
= obj_item
.next_object
263 for i
in range(0, obj
.attribute_ctr
.num_attributes
):
264 attr
= obj
.attribute_ctr
.attributes
[i
]
265 if attid_equal(attr
.attid
, attid
):
267 for j
in range(0, attr
.value_ctr
.num_values
):
268 assert attr
.value_ctr
.values
[j
].blob
is not None
269 val_raw
= attr
.value_ctr
.values
[j
].blob
271 if attmode
== "utf8":
272 val_unicode
= unicode(val_raw
, 'utf-16-le')
273 val
= val_unicode
.encode('utf-8')
274 elif attmode
== "raw":
277 assert False, "attmode[%s]" % attmode
279 if not attid_equal(attr
.attid
, drsuapi
.DRSUAPI_ATTID_supplementalCredentials
):
281 assert attr
.value_ctr
.num_values
<= 1
282 if attr
.value_ctr
.num_values
== 0:
284 assert attr
.value_ctr
.values
[0].blob
is not None
285 spl_crypt
= attr
.value_ctr
.values
[0].blob
287 if spl_crypt
is None:
288 obj_item
= obj_item
.next_object
291 assert len(spl_crypt
) >= 20
292 confounder
= spl_crypt
[0:16]
293 enc_buffer
= spl_crypt
[16:]
296 m5
.update(user_session_key
)
297 m5
.update(confounder
)
298 enc_key
= m5
.digest()
300 rc4
= Crypto
.Cipher
.ARC4
.new(enc_key
)
301 plain_buffer
= rc4
.decrypt(enc_buffer
)
303 (crc32_v
) = struct
.unpack("<L", plain_buffer
[0:4])
304 attr_val
= plain_buffer
[4:]
305 crc32_c
= binascii
.crc32(attr_val
) & 0xffffffff
306 assert int(crc32_v
[0]) == int(crc32_c
), "CRC32 0x%08X != 0x%08X" % (crc32_v
[0], crc32_c
)
308 spl
= ndr_unpack(drsblobs
.supplementalCredentialsBlob
, attr_val
)
310 #print '%s' % ndr_print(spl)
314 for i
in range(0, spl
.sub
.num_packages
):
315 pkg
= spl
.sub
.packages
[i
]
316 if pkg
.name
!= "Primary:CLEARTEXT":
318 cleartext_hex
= pkg
.data
320 if cleartext_hex
is not None:
321 cleartext_utf16
= binascii
.a2b_hex(cleartext_hex
)
322 if clear_utf16_name
is not None:
323 gls
.add_attr(obj
.identifier
.dn
, clear_utf16_name
, [cleartext_utf16
])
325 cleartext_unicode
= unicode(cleartext_utf16
, 'utf-16-le')
326 cleartext_utf8
= cleartext_unicode
.encode('utf-8')
327 gls
.add_attr(obj
.identifier
.dn
, clear_utf8_name
, [cleartext_utf8
])
331 if attvals
is not None:
332 gls
.add_attr(obj
.identifier
.dn
, attname
, attvals
)
336 for i
in range(0, spl
.sub
.num_packages
):
337 pkg
= spl
.sub
.packages
[i
]
338 if pkg
.name
!= "Primary:Kerberos":
340 krb5_old_hex
= pkg
.data
342 if krb5_old_hex
is not None:
343 krb5_old_raw
= binascii
.a2b_hex(krb5_old_hex
)
344 krb5_old
= ndr_unpack(drsblobs
.package_PrimaryKerberosBlob
, krb5_old_raw
, allow_remaining
=True)
346 #print '%s' % ndr_print(krb5_old)
350 for i
in range(0, spl
.sub
.num_packages
):
351 pkg
= spl
.sub
.packages
[i
]
352 if pkg
.name
!= "Primary:Kerberos-Newer-Keys":
354 krb5_new_hex
= pkg
.data
356 if krb5_new_hex
is not None:
357 krb5_new_raw
= binascii
.a2b_hex(krb5_new_hex
)
358 krb5_new
= ndr_unpack(drsblobs
.package_PrimaryKerberosBlob
, krb5_new_raw
, allow_remaining
=True)
360 #print '%s' % ndr_print(krb5_new)
362 obj_item
= obj_item
.next_object
366 if ctr
.more_data
== 0:
367 store_hwm
= ctr
.new_highwatermark
369 store_utdv
= drsblobs
.replUpToDateVectorBlob()
370 store_utdv
.version
= ctr
.uptodateness_vector
.version
371 store_utdv_ctr
= store_utdv
.ctr
372 store_utdv_ctr
.count
= ctr
.uptodateness_vector
.count
373 store_utdv_ctr
.cursors
= ctr
.uptodateness_vector
.cursors
374 store_utdv
.ctr
= store_utdv_ctr
376 #print "%s" % ndr_print(store_hwm)
377 #print "%s" % ndr_print(store_utdv)
379 store_hwm_blob
= ndr_pack(store_hwm
)
380 store_utdv_blob
= ndr_pack(store_utdv
)
383 # uint32_t version '1'
384 # uint32_t dn_str_len
385 # uint32_t dn_str_ofs
386 # uint32_t hwm_blob_len
387 # uint32_t hwm_blob_ofs
388 # uint32_t utdv_blob_len
389 # uint32_t utdv_blob_ofs
390 store_hdr_len
= 7 * 4
391 dn_ofs
= store_hdr_len
392 hwm_ofs
= dn_ofs
+ len(dn
)
393 utdv_ofs
= hwm_ofs
+ len(store_hwm_blob
)
394 store_blob
= struct
.pack("<LLLLLLL", 1, \
396 len(store_hwm_blob
), hwm_ofs
, \
397 len(store_utdv_blob
), utdv_ofs
) + \
398 dn
+ store_hwm_blob
+ store_utdv_blob
400 tmp_file
= "%s.tmp" % cookie_file
401 f
= open(tmp_file
, 'wb')
404 os
.rename(tmp_file
, cookie_file
)
406 print "# up to usn[%d]" % (ctr
.new_highwatermark
.highest_usn
)
408 print "# up to tmp_usn[%d]" % (ctr
.new_highwatermark
.highest_usn
)
409 req8
.highwatermark
= ctr
.new_highwatermark