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 cleartext_name [attid attname attmode]")
80 sambaopts
= options
.SambaOptions(parser
)
81 credopts
= options
.CredentialsOptions(parser
)
82 parser
.add_option_group(credopts
)
84 (opts
, args
) = parser
.parse_args()
91 parser
.error("more arguments required - given=%d" % (len(args
)))
96 if len(cookie_file
) == 0:
98 cleartext_name
= args
[3]
101 attid
= int(args
[4], 16)
106 if attmode
not in ["raw", "utf8"]:
107 parser
.error("attmode should be 'raw' or 'utf8'")
113 lp
= sambaopts
.get_loadparm()
114 creds
= credopts
.get_credentials(lp
)
116 if not creds
.authentication_requested():
117 parser
.error("You must supply credentials")
121 f
= open(cookie_file
, 'r')
122 store_blob
= f
.read()
125 store_hdr
= store_blob
[0:28]
127 store_dn_len
, store_dn_ofs
, \
128 store_hwm_len
, store_hwm_ofs
, \
129 store_utdv_len
, store_utdv_ofs
) = \
130 struct
.unpack("<LLLLLLL", store_hdr
)
132 store_dn
= store_blob
[store_dn_ofs
:store_dn_ofs
+store_dn_len
]
133 store_hwm_blob
= store_blob
[store_hwm_ofs
:store_hwm_ofs
+store_hwm_len
]
134 store_utdv_blob
= store_blob
[store_utdv_ofs
:store_utdv_ofs
+store_utdv_len
]
136 store_hwm
= ndr_unpack(drsuapi
.DsReplicaHighWaterMark
, store_hwm_blob
)
137 store_utdv
= ndr_unpack(drsblobs
.replUpToDateVectorBlob
, store_utdv_blob
)
139 assert store_dn
== dn
140 #print "%s" % ndr_print(store_hwm)
141 #print "%s" % ndr_print(store_utdv)
144 store_hwm
= drsuapi
.DsReplicaHighWaterMark()
145 store_hwm
.tmp_highest_usn
= 0
146 store_hwm
.reserved_usn
= 0
147 store_hwm
.highest_usn
= 0
150 binding_str
= "ncacn_ip_tcp:%s[spnego,seal]" % server
152 drs_conn
= drsuapi
.drsuapi(binding_str
, lp
, creds
)
154 bind_info
= drsuapi
.DsBindInfoCtr()
155 bind_info
.length
= 28
156 bind_info
.info
= drsuapi
.DsBindInfo28()
157 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_BASE
158 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION
159 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI
160 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2
161 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS
162 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1
163 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION
164 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE
165 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2
166 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION
167 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2
168 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD
169 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND
170 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO
171 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION
172 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01
173 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP
174 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY
175 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3
176 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2
177 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6
178 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS
179 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8
180 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5
181 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6
182 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3
183 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7
184 bind_info
.info
.supported_extensions |
= drsuapi
.DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT
185 (info
, drs_handle
) = drs_conn
.DsBind(misc
.GUID(drsuapi
.DRSUAPI_DS_BIND_GUID
), bind_info
)
187 null_guid
= misc
.GUID()
189 naming_context
= drsuapi
.DsReplicaObjectIdentifier()
190 naming_context
.dn
= dn
191 highwatermark
= store_hwm
192 uptodateness_vector
= None
193 if store_utdv
is not None:
194 uptodateness_vector
= drsuapi
.DsReplicaCursorCtrEx()
195 if store_utdv
.version
== 1:
196 uptodateness_vector
.cursors
= store_utdv
.cursors
197 elif store_utdv
.version
== 2:
199 for i
in range(0, store_utdv
.ctr
.count
):
200 cursor
= drsuapi
.DsReplicaCursor()
201 cursor
.source_dsa_invocation_id
= store_utdv
.ctr
.cursors
[i
].source_dsa_invocation_id
202 cursor
.highest_usn
= store_utdv
.ctr
.cursors
[i
].highest_usn
203 cursors
.append(cursor
)
204 uptodateness_vector
.cursors
= cursors
206 req8
= drsuapi
.DsGetNCChangesRequest8()
208 req8
.destination_dsa_guid
= null_guid
209 req8
.source_dsa_invocation_id
= null_guid
210 req8
.naming_context
= naming_context
211 req8
.highwatermark
= highwatermark
212 req8
.uptodateness_vector
= uptodateness_vector
213 req8
.replica_flags
= (drsuapi
.DRSUAPI_DRS_INIT_SYNC |
214 drsuapi
.DRSUAPI_DRS_PER_SYNC |
215 drsuapi
.DRSUAPI_DRS_GET_ANC |
216 drsuapi
.DRSUAPI_DRS_NEVER_SYNCED |
217 drsuapi
.DRSUAPI_DRS_WRIT_REP
)
218 req8
.max_object_count
= 402
219 req8
.max_ndr_size
= 402116
222 req8
.partial_attribute_set
= None
223 req8
.partial_attribute_set_ex
= None
224 req8
.mapping_ctr
.num_mappings
= 0
225 req8
.mapping_ctr
.mappings
= None
227 user_session_key
= drs_conn
.user_session_key
229 print "# starting at usn[%d]" % (highwatermark
.highest_usn
)
232 (level
, ctr
) = drs_conn
.DsGetNCChanges(drs_handle
, 8, req8
)
233 if ctr
.first_object
== None and ctr
.object_count
!= 0:
234 raise RuntimeError("DsGetNCChanges: NULL first_object with object_count=%u" % (ctr
.object_count
))
236 obj_item
= ctr
.first_object
237 while obj_item
is not None:
238 obj
= obj_item
.object
240 if obj
.identifier
is None:
241 obj_item
= obj_item
.next_object
244 #print '%s' % obj.identifier.dn
247 for i
in range(0, obj
.attribute_ctr
.num_attributes
):
248 attr
= obj
.attribute_ctr
.attributes
[i
]
249 if attid_equal(attr
.attid
, drsuapi
.DRSUAPI_ATTID_isDeleted
):
252 obj_item
= obj_item
.next_object
257 for i
in range(0, obj
.attribute_ctr
.num_attributes
):
258 attr
= obj
.attribute_ctr
.attributes
[i
]
259 if attid_equal(attr
.attid
, attid
):
261 for j
in range(0, attr
.value_ctr
.num_values
):
262 assert attr
.value_ctr
.values
[j
].blob
is not None
263 val_raw
= attr
.value_ctr
.values
[j
].blob
265 if attmode
== "utf8":
266 val_unicode
= unicode(val_raw
, 'utf-16-le')
267 val
= val_unicode
.encode('utf-8')
268 elif attmode
== "raw":
271 assert False, "attmode[%s]" % attmode
273 if not attid_equal(attr
.attid
, drsuapi
.DRSUAPI_ATTID_supplementalCredentials
):
275 assert attr
.value_ctr
.num_values
<= 1
276 if attr
.value_ctr
.num_values
== 0:
278 assert attr
.value_ctr
.values
[0].blob
is not None
279 spl_crypt
= attr
.value_ctr
.values
[0].blob
281 if spl_crypt
is None:
282 obj_item
= obj_item
.next_object
285 assert len(spl_crypt
) >= 20
286 confounder
= spl_crypt
[0:16]
287 enc_buffer
= spl_crypt
[16:]
290 m5
.update(user_session_key
)
291 m5
.update(confounder
)
292 enc_key
= m5
.digest()
294 rc4
= Crypto
.Cipher
.ARC4
.new(enc_key
)
295 plain_buffer
= rc4
.decrypt(enc_buffer
)
297 (crc32_v
) = struct
.unpack("<L", plain_buffer
[0:4])
298 attr_val
= plain_buffer
[4:]
299 crc32_c
= binascii
.crc32(attr_val
) & 0xffffffff
300 assert int(crc32_v
[0]) == int(crc32_c
), "CRC32 0x%08X != 0x%08X" % (crc32_v
[0], crc32_c
)
302 spl
= ndr_unpack(drsblobs
.supplementalCredentialsBlob
, attr_val
)
304 #print '%s' % ndr_print(spl)
308 for i
in range(0, spl
.sub
.num_packages
):
309 pkg
= spl
.sub
.packages
[i
]
310 if pkg
.name
!= "Primary:CLEARTEXT":
312 cleartext_hex
= pkg
.data
314 if cleartext_hex
is not None:
315 cleartext_utf16
= binascii
.a2b_hex(cleartext_hex
)
316 cleartext_unicode
= unicode(cleartext_utf16
, 'utf-16-le')
317 cleartext_utf8
= cleartext_unicode
.encode('utf-8')
319 gls
.add_attr(obj
.identifier
.dn
, cleartext_name
, [cleartext_utf8
])
321 if attvals
is not None:
322 gls
.add_attr(obj
.identifier
.dn
, attname
, attvals
)
326 for i
in range(0, spl
.sub
.num_packages
):
327 pkg
= spl
.sub
.packages
[i
]
328 if pkg
.name
!= "Primary:Kerberos":
330 krb5_old_hex
= pkg
.data
332 if krb5_old_hex
is not None:
333 krb5_old_raw
= binascii
.a2b_hex(krb5_old_hex
)
334 krb5_old
= ndr_unpack(drsblobs
.package_PrimaryKerberosBlob
, krb5_old_raw
, allow_remaining
=True)
336 #print '%s' % ndr_print(krb5_old)
340 for i
in range(0, spl
.sub
.num_packages
):
341 pkg
= spl
.sub
.packages
[i
]
342 if pkg
.name
!= "Primary:Kerberos-Newer-Keys":
344 krb5_new_hex
= pkg
.data
346 if krb5_new_hex
is not None:
347 krb5_new_raw
= binascii
.a2b_hex(krb5_new_hex
)
348 krb5_new
= ndr_unpack(drsblobs
.package_PrimaryKerberosBlob
, krb5_new_raw
, allow_remaining
=True)
350 #print '%s' % ndr_print(krb5_new)
352 obj_item
= obj_item
.next_object
356 if ctr
.more_data
== 0:
357 store_hwm
= ctr
.new_highwatermark
359 store_utdv
= drsblobs
.replUpToDateVectorBlob()
360 store_utdv
.version
= ctr
.uptodateness_vector
.version
361 store_utdv_ctr
= store_utdv
.ctr
362 store_utdv_ctr
.count
= ctr
.uptodateness_vector
.count
363 store_utdv_ctr
.cursors
= ctr
.uptodateness_vector
.cursors
364 store_utdv
.ctr
= store_utdv_ctr
366 #print "%s" % ndr_print(store_hwm)
367 #print "%s" % ndr_print(store_utdv)
369 store_hwm_blob
= ndr_pack(store_hwm
)
370 store_utdv_blob
= ndr_pack(store_utdv
)
373 # uint32_t version '1'
374 # uint32_t dn_str_len
375 # uint32_t dn_str_ofs
376 # uint32_t hwm_blob_len
377 # uint32_t hwm_blob_ofs
378 # uint32_t utdv_blob_len
379 # uint32_t utdv_blob_ofs
380 store_hdr_len
= 7 * 4
381 dn_ofs
= store_hdr_len
382 hwm_ofs
= dn_ofs
+ len(dn
)
383 utdv_ofs
= hwm_ofs
+ len(store_hwm_blob
)
384 store_blob
= struct
.pack("<LLLLLLL", 1, \
386 len(store_hwm_blob
), hwm_ofs
, \
387 len(store_utdv_blob
), utdv_ofs
) + \
388 dn
+ store_hwm_blob
+ store_utdv_blob
390 tmp_file
= "%s.tmp" % cookie_file
391 f
= open(tmp_file
, 'wb')
394 os
.rename(tmp_file
, cookie_file
)
396 print "# up to usn[%d]" % (ctr
.new_highwatermark
.highest_usn
)
398 print "# up to tmp_usn[%d]" % (ctr
.new_highwatermark
.highest_usn
)
399 req8
.highwatermark
.tmp_highest_usn
= ctr
.new_highwatermark
.tmp_highest_usn