Coverage for /root/GitHubProjects/impacket/impacket/dcerpc/v5/nspi.py : 46%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SECUREAUTH LABS. Copyright 2020 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Description:
8# [MS-NSPI]: Name Service Provider Interface (NSPI) Protocol
9# [MS-OXNSPI]: Exchange Server Name Service Provider Interface (NSPI) Protocol
10#
11# Authors:
12# Arseniy Sharoglazov <mohemiv@gmail.com> / Positive Technologies (https://www.ptsecurity.com/)
13#
14# Tested for MS-OXNSPI, some operation may not work for MS-NSPI
15#
16# ToDo:
17# [ ] Test commented NDRCALLs and write helpers for them
18# [ ] Test restriction structures
20from __future__ import division
21from __future__ import print_function
22from struct import unpack
23from datetime import datetime
24from six import PY2
25import binascii
27from impacket import hresult_errors, mapi_constants, uuid
28from impacket.uuid import EMPTY_UUID
29from impacket.structure import Structure
30from impacket.dcerpc.v5.dtypes import NULL, STR, DWORD, LPDWORD, UUID, PUUID, LONG, ULONG, \
31 FILETIME, PFILETIME, BYTE, SHORT, LPSTR, LPWSTR, USHORT, LPLONG, DWORD_ARRAY
32from impacket.ldap.ldaptypes import LDAP_SID
33from impacket.dcerpc.v5.ndr import NDR, NDRCALL, NDRPOINTER, NDRSTRUCT, NDRUNION, \
34 NDRUniConformantVaryingArray, NDRUniConformantArray, NDRUniVaryingArray
35from impacket.dcerpc.v5.rpcrt import DCERPCException
36from impacket.uuid import string_to_bin, uuidtup_to_bin, EMPTY_UUID
38MSRPC_UUID_NSPI = uuidtup_to_bin(('F5CC5A18-4264-101A-8C59-08002B2F8426', '56.0'))
40class DCERPCSessionError(DCERPCException):
41 def __str__( self ):
42 key = self.error_code
43 if key in mapi_constants.ERROR_MESSAGES:
44 error_msg_short = mapi_constants.ERROR_MESSAGES[key]
45 return 'NSPI SessionError: code: 0x%x - %s' % (self.error_code, error_msg_short)
46 elif key in hresult_errors.ERROR_MESSAGES:
47 error_msg_short = hresult_errors.ERROR_MESSAGES[key][0]
48 error_msg_verbose = hresult_errors.ERROR_MESSAGES[key][1]
49 return 'NSPI SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
50 else:
51 return 'NSPI SessionError: unknown error code: 0x%x' % self.error_code
53################################################################################
54# STRUCTURES
55################################################################################
56class handle_t(NDRSTRUCT):
57 structure = (
58 ('context_handle_attributes',ULONG),
59 ('context_handle_uuid',UUID),
60 )
62 def __init__(self, data=None, isNDR64=False):
63 NDRSTRUCT.__init__(self, data, isNDR64)
64 self['context_handle_uuid'] = b'\x00'*16
66 def isNull(self):
67 return self['context_handle_uuid'] == b'\x00'*16
69# 2.2.1 Permitted Property Type Values
70PtypEmbeddedTable = 0x0000000D
71PtypNull = 0x00000001
72PtypUnspecified = 0x00000000
74# 2.2.3 Display Type Values
75DT_MAILUSER = 0x00000000
76DT_DISTLIST = 0x00000001
77DT_FORUM = 0x00000002
78DT_AGENT = 0x00000003
79DT_ORGANIZATION = 0x00000004
80DT_PRIVATE_DISTLIST = 0x00000005
81DT_REMOTE_MAILUSER = 0x00000006
82DT_CONTAINER = 0x00000100
83DT_TEMPLATE = 0x00000101
84DT_ADDRESS_TEMPLATE = 0x00000102
85DT_SEARCH = 0x00000200
87# 2.2.4 Default Language Code Identifier
88NSPI_DEFAULT_LOCALE = 0x00000409
90# 2.2.5 Required Codepages
91CP_TELETEX = 0x00004F25
92CP_WINUNICODE = 0x000004B0
94# 2.2.6.1 Comparison Flags
95NORM_IGNORECASE = 1 << 0
96NORM_IGNORENONSPACE = 1 << 1
97NORM_IGNORESYMBOLS = 1 << 2
98SORT_STRINGSORT = 1 << 12
99NORM_IGNOREKANATYPE = 1 << 16
100NORM_IGNOREWIDTH = 1 << 17
102# 2.2.7 Permanent Entry ID GUID
103GUID_NSPI = string_to_bin("C840A7DC-42C0-1A10-B4B9-08002B2FE182")
105# 2.2.8 Positioning Minimal Entry IDs
106MID_BEGINNING_OF_TABLE = 0x00000000
107MID_END_OF_TABLE = 0x00000002
108MID_CURRENT = 0x00000001
110# 2.2.9 Ambiguous Name Resolution Minimal Entry IDs
111MID_UNRESOLVED = 0x00000000
112MID_AMBIGUOUS = 0x00000001
113MID_RESOLVED = 0x00000002
115# 2.2.10 Table Sort Orders
116SortTypeDisplayName = 0
117SortTypePhoneticDisplayName = 0x00000003
118SortTypeDisplayName_RO = 0x000003E8
119SortTypeDisplayName_W = 0x000003E9
121# 2.2.11 NspiBind Flags
122fAnonymousLogin = 0x00000020
124# 2.2.12 Retrieve Property Flags
125fSkipObjects = 0x00000001
126fEphID = 0x00000002
128# 2.2.13 NspiGetSpecialTable Flags
129NspiAddressCreationTemplates = 0x00000002
130NspiUnicodeStrings = 0x00000004
132# 2.2.14 NspiQueryColumns Flags
133NspiUnicodeProptypes = 0x80000000
135# 2.2.15 NspiGetIDsFromNames Flags
136NspiVerifyNames = 0x00000002
138# 2.2.16 NspiGetTemplateInfo Flags
139TI_TEMPLATE = 0x00000001
140TI_SCRIPT = 0x00000004
141TI_EMT = 0x00000010
142TI_HELPFILE_NAME = 0x00000020
143TI_HELPFILE_CONTENTS = 0x00000040
145# 2.2.17 NspiModLinkAtt Flags
146fDelete = 0x00000001
148# 2.3.1.1 FlatUID_r
149FlatUID_r = UUID
150PFlatUID_r = PUUID
152# 2.3.1.2 PropertyTagArray_r
153class PropertyTagArray(NDRUniConformantVaryingArray):
154 item = DWORD
156class PropertyTagArray_r(NDRSTRUCT):
157 structure = (
158 ('cValues', ULONG),
159 ('aulPropTag', PropertyTagArray)
160 )
162class PPropertyTagArray_r(NDRPOINTER):
163 referent = (
164 ('Data', PropertyTagArray_r),
165 )
167# 2.3.1.3 Binary_r
168class Binary(NDRUniConformantArray):
169 item = 'c'
171class PBinary(NDRPOINTER):
172 referent = (
173 ('Data', Binary),
174 )
176class Binary_r(NDRSTRUCT):
177 structure = (
178 ('cValues', DWORD),
179 ('lpb', PBinary),
180 )
182# 2.3.1.4 ShortArray_r
183class ShortArray(NDRUniConformantArray):
184 item = SHORT
186class PShortArray(NDRPOINTER):
187 referent = (
188 ('Data', ShortArray),
189 )
191class ShortArray_r(NDRSTRUCT):
192 structure = (
193 ('cValues', DWORD),
194 ('lpi', PShortArray),
195 )
197# 2.3.1.5 LongArray_r
198class LongArray(NDRUniConformantArray):
199 item = LONG
201class PLongArray(NDRPOINTER):
202 referent = (
203 ('Data', LongArray),
204 )
206class LongArray_r(NDRSTRUCT):
207 structure = (
208 ('cValues', DWORD),
209 ('lpl', PLongArray)
210 )
212# 2.3.1.6 StringArray_r
213class StringArray(NDRUniConformantArray):
214 item = LPSTR
216class PStringArray(NDRPOINTER):
217 referent = (
218 ('Data', StringArray),
219 )
221class StringArray_r(NDRSTRUCT):
222 structure = (
223 ('cValues', DWORD),
224 ('lppszA', PStringArray)
225 )
227# 2.3.1.7 BinaryArray_r
228class BinaryArray(NDRUniConformantArray):
229 item = Binary_r
231class PBinaryArray(NDRPOINTER):
232 referent = (
233 ('Data', BinaryArray),
234 )
236class BinaryArray_r(NDRSTRUCT):
237 structure = (
238 ('cValues', DWORD),
239 ('lpbin', PBinaryArray)
240 )
242# 2.3.1.8 FlatUIDArray_r
243class FlatUIDArray(NDRUniConformantArray):
244 item = PFlatUID_r
246class PFlatUIDArray(NDRPOINTER):
247 referent = (
248 ('Data', FlatUIDArray),
249 )
251class FlatUIDArray_r(NDRSTRUCT):
252 structure = (
253 ('cValues', DWORD),
254 ('lpguid', PFlatUIDArray)
255 )
257# 2.3.1.9 WStringArray_r
258class WStringArray(NDRUniConformantArray):
259 item = LPWSTR
261class PWStringArray(NDRPOINTER):
262 referent = (
263 ('Data', WStringArray),
264 )
266class WStringArray_r(NDRSTRUCT):
267 structure = (
268 ('cValues', DWORD),
269 ('lppszW', PWStringArray)
270 )
272# 2.3.1.10 DateTimeArray_r
273class DateTimeArray(NDRUniConformantArray):
274 item = PFILETIME
276class PDateTimeArray(NDRPOINTER):
277 referent = (
278 ('Data', DateTimeArray),
279 )
281class DateTimeArray_r(NDRSTRUCT):
282 structure = (
283 ('cValues', DWORD),
284 ('lpft', PDateTimeArray)
285 )
287# 2.3.1.11 PROP_VAL_UNION
288class PROP_VAL_UNION(NDRUNION):
289 commonHdr = (
290 ('tag', DWORD),
291 )
293 union = {
294 0x0002: ('i', SHORT), # PtypInteger16
295 0x0003: ('l', LONG), # PtypInteger32
296 0x000B: ('b', USHORT), # PtypBoolean
297 0x001E: ('lpszA', LPSTR), # PtypString8
298 0x0102: ('bin', Binary_r), # PtypBinary
299 0x001F: ('lpszW', LPWSTR), # PtypString
300 0x0048: ('lpguid', PFlatUID_r), # PtypGuid
301 0x0040: ('ft', FILETIME), # PtypTime
302 0x000A: ('err', ULONG), # PtypErrorCode
303 0x1002: ('MVi', ShortArray_r), # PtypMultipleInteger16
304 0x1003: ('MVl', LongArray_r), # PtypMultipleInteger32
305 0x101E: ('MVszA', StringArray_r), # PtypMultipleString8
306 0x1102: ('MVbin', BinaryArray_r), # PtypMultipleBinary
307 0x1048: ('MVguid', FlatUIDArray_r), # PtypMultipleGuid
308 0x101F: ('MVszW', WStringArray_r), # PtypMultipleString
309 0x1040: ('MVft', DateTimeArray_r), # PtypMultipleTime
310 0x0001: ('lReserved', LONG), # PtypNull
311 0x000D: ('lReserved', LONG), # PtypEmbeddedTable
312 0x0000: ('lReserved', LONG), # PtypUnspecified
313 }
315# 2.3.1.12 PropertyValue_r
316class PropertyValue_r(NDRSTRUCT):
317 structure = (
318 ('ulPropTag', DWORD),
319 ('ulReserved', DWORD), # dwAlignPad
320 ('Value', PROP_VAL_UNION),
321 )
323class PPropertyValue_r(NDRPOINTER):
324 referent = (
325 ('Data', PropertyValue_r),
326 )
328# 2.3.2 PropertyRow_r
329class PropertyValue(NDRUniConformantArray):
330 item = PropertyValue_r
332class PPropertyValue(NDRPOINTER):
333 referent = (
334 ('Data', PropertyValue),
335 )
337class PropertyRow_r(NDRSTRUCT):
338 structure = (
339 ('Reserved', DWORD), # ulAdrEntryPad
340 ('cValues', DWORD),
341 ('lpProps', PPropertyValue)
342 )
344class PPropertyRow_r(NDRPOINTER):
345 referent = (
346 ('Data', PropertyRow_r),
347 )
349# 2.3.3 PropertyRowSet_r
350class PropertyRowSet(NDRUniConformantArray):
351 item = PropertyRow_r
353class PropertyRowSet_r(NDRSTRUCT):
354 structure = (
355 ('cRows', DWORD),
356 ('aRow', PropertyRowSet),
357 )
359class PPropertyRowSet_r(NDRPOINTER):
360 referent = (
361 ('Data', PropertyRowSet_r),
362 )
364# 2.3.4 Restrictions
365class Restriction_r(NDRSTRUCT):
366 pass
368class PRestriction_r(NDRPOINTER):
369 referent = (
370 ('Data', Restriction_r),
371 )
373# 2.3.4.1 AndRestriction_r, OrRestriction_r
374class AndRestriction(NDRUniConformantArray):
375 item = Restriction_r
377class PAndRestriction(NDRPOINTER):
378 referent = (
379 ('Data', AndRestriction),
380 )
382class AndRestriction_r(NDRSTRUCT):
383 structure = (
384 ('cRes', DWORD),
385 ('lpRes', PAndRestriction),
386 )
388OrRestriction_r = AndRestriction_r
390# 2.3.4.2 NotRestriction_r
391class NotRestriction_r(NDRSTRUCT):
392 structure = (
393 ('lpRes', PRestriction_r),
394 )
396# 2.3.4.3 ContentRestriction_r
397class ContentRestriction_r(NDRSTRUCT):
398 structure = (
399 ('ulFuzzyLevel', DWORD),
400 ('ulPropTag', DWORD),
401 ('lpProp', PPropertyValue_r),
402 )
404# 2.3.4.4 BitMaskRestriction_r
405class BitMaskRestriction_r(NDRSTRUCT):
406 structure = (
407 ('relBMR', DWORD),
408 ('ulPropTag', DWORD),
409 ('ulMask', DWORD),
410 )
412# 2.3.4.5 PropertyRestriction_r
413class PropertyRestriction_r(NDRSTRUCT):
414 structure = (
415 ('relop', DWORD),
416 ('ulPropTag', DWORD),
417 ('lpProp', PPropertyValue_r),
418 )
420# 2.3.4.6 ComparePropsRestriction_r
421class ComparePropsRestriction_r(NDRSTRUCT):
422 structure = (
423 ('relop', DWORD),
424 ('ulPropTag1', DWORD),
425 ('ulPropTag2', DWORD),
426 )
428# 2.3.4.7 SubRestriction_r
429class SubRestriction_r(NDRSTRUCT):
430 structure = (
431 ('ulSubObject', DWORD),
432 ('lpRes', PRestriction_r),
433 )
435# 2.3.4.8 SizeRestriction_r
436class SizeRestriction_r(NDRSTRUCT):
437 structure = (
438 ('relop', DWORD),
439 ('ulPropTag', DWORD),
440 ('cb', DWORD),
441 )
443# 2.3.4.9 ExistRestriction_r
444class ExistRestriction_r(NDRSTRUCT):
445 structure = (
446 ('ulReserved1', DWORD),
447 ('ulPropTag', DWORD),
448 ('ulReserved2', DWORD),
449 )
451# 2.3.4.10 RestrictionUnion_r
452class RestrictionUnion_r(NDRUNION):
453 commonHdr = (
454 ('tag', DWORD),
455 )
457 union = {
458 0x00000000: ('resAnd', AndRestriction_r),
459 0x00000001: ('resOr', OrRestriction_r),
460 0x00000002: ('resNot', NotRestriction_r),
461 0x00000003: ('resContent', ContentRestriction_r),
462 0x00000004: ('resProperty', PropertyRestriction_r),
463 0x00000005: ('resCompareProps', ComparePropsRestriction_r),
464 0x00000006: ('resBitMask', BitMaskRestriction_r),
465 0x00000007: ('resSize', SizeRestriction_r),
466 0x00000008: ('resExist', ExistRestriction_r),
467 0x00000009: ('resSubRestriction', SubRestriction_r),
468 }
470# 2.3.4.11 Restriction_r
471Restriction_r.structure = (
472 ('rt', DWORD),
473 ('res', RestrictionUnion_r),
474)
476# 2.3.5.1 PropertyName_r
477class PropertyName_r(NDRSTRUCT):
478 structure = (
479 ('lpguid', PFlatUID_r),
480 ('ulReserved', DWORD),
481 ('lID', LONG),
482 )
484class PPropertyName_r(NDRPOINTER):
485 referent = (
486 ('Data', PropertyName_r),
487 )
489# 2.3.5.2 PropertyNameSet_r
490class PropertyNameSet(NDRUniConformantArray):
491 item = PropertyName_r
493class PropertyNameSet_r(NDRSTRUCT):
494 structure = (
495 ('cNames', DWORD),
496 ('aulPropTag', PropertyNameSet)
497 )
499class PPropertyNameSet_r(NDRPOINTER):
500 referent = (
501 ('Data', PropertyNameSet_r),
502 )
504# 2.3.6.1 StringsArray_r
505class StringsArray(NDRUniConformantArray):
506 item = LPSTR
508class StringsArray_r(NDRSTRUCT):
509 structure = (
510 ('Count', DWORD),
511 ('Strings', StringsArray)
512 )
514# 2.3.6.1 StringsArray_r
515class WStringsArray(NDRUniConformantArray):
516 item = LPWSTR
518class WStringsArray_r(NDRSTRUCT):
519 structure = (
520 ('Count', DWORD),
521 ('Strings', WStringsArray)
522 )
524# 2.3.7 STAT
525class STAT(NDRSTRUCT):
526 structure = (
527 ('SortType', DWORD),
528 ('ContainerID', DWORD),
529 ('CurrentRec', DWORD),
530 ('Delta', LONG),
531 ('NumPos', DWORD),
532 ('TotalRecs', DWORD),
533 ('CodePage', DWORD),
534 ('TemplateLocale', DWORD),
535 ('SortLocale', DWORD),
536 )
538class PSTAT(NDRPOINTER):
539 referent = (
540 ('Data', STAT),
541 )
543# 2.3.8.1 MinimalEntryID
544MinEntryID = '<L=0'
546# 2.3.8.2 EphemeralEntryID
547class EphemeralEntryID(Structure):
548 structure = (
549 ('IDType','<B=0x87'),
550 ('R1','<B=0'),
551 ('R2','<B=0'),
552 ('R3','<B=0'),
553 ('ProviderUID','16s=b"\\x00"*16'),
554 ('R4','<L=0x0000001'),
555 ('DisplayType','<L'),
556 ('MId',MinEntryID),
557 )
559# 2.3.8.3 PermanentEntryID
560class PermanentEntryID(Structure):
561 default_guid = GUID_NSPI
562 structure = (
563 ('IDType','<B=0'),
564 ('R1','<B=0'),
565 ('R2','<B=0'),
566 ('R3','<B=0'),
567 ('ProviderUID','16s=self["default_guid"]'),
568 ('R4','<L=0x0000001'),
569 ('DisplayType','<L'),
570 ('DistinguishedName','z'),
571 )
573 def __str__(self):
574 return self["DistinguishedName"]
576################################################################################
577# RPC CALLS
578################################################################################
580# 3.1.4.1 RfrGetNewDSA (opnum 0)
581class NspiBind(NDRCALL):
582 opnum = 0
583 structure = (
584 ('dwFlags', DWORD),
585 ('pStat', STAT),
586 ('pServerGuid', PFlatUID_r),
587 )
589class NspiBindResponse(NDRCALL):
590 structure = (
591 ('pServerGuid', PFlatUID_r),
592 ('contextHandle', handle_t),
593 ('ErrorCode', ULONG),
594 )
596# 3.1.4.2 NspiUnbind (Opnum 1)
597class NspiUnbind(NDRCALL):
598 opnum = 1
599 structure = (
600 ('contextHandle', handle_t),
601 ('Reserved', DWORD), # flags
602 )
604class NspiUnbindResponse(NDRCALL):
605 structure = (
606 ('contextHandle', handle_t),
607 ('ErrorCode', ULONG),
608 )
610# 3.1.4.4 NspiUpdateStat (Opnum 2)
611class NspiUpdateStat(NDRCALL):
612 opnum = 2
613 structure = (
614 ('hRpc', handle_t),
615 ('Reserved', DWORD), # flags
616 ('pStat', STAT),
617 ('plDelta', LPLONG),
618 )
620class NspiUpdateStatResponse(NDRCALL):
621 structure = (
622 ('pStat', STAT),
623 ('plDelta', LPLONG),
624 ('ErrorCode', ULONG),
625 )
627# 3.1.4.8 NspiQueryRows (Opnum 3)
628class DWORD_ARRAY(NDRUniConformantArray):
629 item = DWORD
631class PDWORD_ARRAY(NDRPOINTER):
632 referent = (
633 ('Data', DWORD_ARRAY),
634 )
636class NspiQueryRows(NDRCALL):
637 opnum = 3
638 structure = (
639 ('hRpc', handle_t),
640 ('dwFlags', DWORD),
641 ('pStat', STAT),
642 ('dwETableCount', DWORD),
643 ('lpETable', PDWORD_ARRAY),
644 ('Count', DWORD),
645 ('pPropTags', PPropertyTagArray_r),
646 )
648class NspiQueryRowsResponse(NDRCALL):
649 structure = (
650 ('pStat', STAT),
651 ('ppRows', PPropertyRowSet_r),
652 ('ErrorCode', ULONG),
653 )
655# 3.1.4.9 NspiSeekEntries (Opnum 4)
656class NspiSeekEntries(NDRCALL):
657 opnum = 4
658 structure = (
659 ('hRpc', handle_t),
660 ('Reserved', DWORD), # flags
661 ('pStat', STAT),
662 ('pTarget', PropertyValue_r),
663 ('lpETable', PropertyTagArray_r),
664 ('pPropTags', PropertyTagArray_r),
665 )
667class NspiSeekEntriesResponse(NDRCALL):
668 structure = (
669 ('pStat', STAT),
670 ('ppRows', PPropertyRowSet_r),
671 ('ErrorCode', ULONG),
672 )
674# 3.1.4.10 NspiGetMatches (Opnum 5)
675#class NspiGetMatches(NDRCALL):
676# opnum = 5
677# structure = (
678# ('hRpc', handle_t),
679# ('Reserved1', DWORD), # flags
680# ('pStat', STAT),
681# ('pReserved', PropertyTagArray_r), # mids
682# ('Reserved2', DWORD), # interfaceOptions
683# ('Filter', Restriction_r),
684# ('lpPropName', PropertyName_r),
685# ('ulRequested', DWORD),
686# ('pPropTags', PropertyTagArray_r),
687# )
689#class NspiGetMatchesResponse(NDRCALL):
690# structure = (
691# ('pStat', PSTAT),
692# ('ppOutMIds', PPropertyTagArray_r),
693# ('ppRows', PPropertyRowSet_r),
694# ('ErrorCode', ULONG),
695# )
697# 3.1.4.11 NspiResortRestriction (Opnum 6)
698#class NspiResortRestriction(NDRCALL):
699# opnum = 6
700# structure = (
701# ('hRpc', handle_t),
702# ('Reserved', DWORD),
703# ('pStat', STAT),
704# ('pInMIds', PropertyTagArray_r),
705# ('ppOutMIds', PPropertyTagArray_r),
706# )
708#class NspiResortRestrictionResponse(NDRCALL):
709# structure = (
710# ('pStat', PSTAT),
711# ('ppOutMIds', PPropertyTagArray_r),
712# ('ErrorCode', ULONG),
713# )
715# 3.1.4.13 NspiDNToMId (Opnum 7)
716class NspiDNToMId(NDRCALL):
717 opnum = 7
718 structure = (
719 ('hRpc', handle_t),
720 ('Reserved', DWORD), # flags
721 ('pNames', StringsArray_r),
722 )
724class NspiDNToMIdResponse(NDRCALL):
725 structure = (
726 ('ppOutMIds', PPropertyTagArray_r),
727 ('ErrorCode', ULONG),
728 )
730# 3.1.4.6 NspiGetPropList (Opnum 8)
731class NspiGetPropList(NDRCALL):
732 opnum = 8
733 structure = (
734 ('hRpc', handle_t),
735 ('dwFlags', DWORD),
736 ('dwMId', DWORD),
737 ('CodePage', DWORD),
738 )
740class NspiGetPropListResponse(NDRCALL):
741 structure = (
742 ('ppOutMIds', PPropertyTagArray_r),
743 ('ErrorCode', ULONG),
744 )
746# 3.1.4.7 NspiGetProps (Opnum 9)
747class NspiGetProps(NDRCALL):
748 opnum = 9
749 structure = (
750 ('hRpc', handle_t),
751 ('dwFlags', DWORD),
752 ('pStat', PSTAT),
753 ('pPropTags', PPropertyTagArray_r),
754 )
756class NspiGetPropsResponse(NDRCALL):
757 structure = (
758 ('ppRows', PPropertyRow_r),
759 ('ErrorCode', ULONG),
760 )
762# 3.1.4.12 NspiCompareMIds (Opnum 10)
763class NspiCompareMIds(NDRCALL):
764 opnum = 10
765 structure = (
766 ('hRpc', handle_t),
767 ('Reserved', DWORD), # flags
768 ('pStat', STAT),
769 ('MId1', DWORD),
770 ('MId2', DWORD),
771 )
773class NspiCompareMIdsResponse(NDRCALL):
774 structure = (
775 ('plResult', LONG),
776 ('ErrorCode', ULONG),
777 )
779# 3.1.4.14 NspiModProps (Opnum 11)
780#class NspiModProps(NDRCALL):
781# opnum = 11
782# structure = (
783# ('hRpc', handle_t),
784# ('Reserved', DWORD), # flags
785# ('pStat', STAT),
786# ('pPropTags', PropertyTagArray_r),
787# ('pRow', PropertyRow_r),
788# )
790#class NspiModPropsResponse(NDRCALL):
791# structure = (
792# ('plResult', LPLONG),
793# ('ErrorCode', ULONG),
794# )
796# 3.1.4.3 NspiGetSpecialTable (Opnum 12)
797class NspiGetSpecialTable(NDRCALL):
798 opnum = 12
799 structure = (
800 ('hRpc', handle_t),
801 ('dwFlags', DWORD),
802 ('pStat', PSTAT),
803 ('lpVersion', LPDWORD),
804 )
806class NspiGetSpecialTableResponse(NDRCALL):
807 structure = (
808 # In Exchange 2013 / 2016 / 2019 lpVersion is
809 # a RuntimeHelpers.GetHashCode value, and it will be
810 # different each call
811 ('lpVersion', DWORD),
812 ('ppRows', PPropertyRowSet_r),
813 ('ErrorCode', DWORD),
814 )
816# 3.1.4.20 NspiGetTemplateInfo (Opnum 13)
817class NspiGetTemplateInfo(NDRCALL):
818 opnum = 13
819 structure = (
820 ('hRpc', handle_t),
821 ('dwFlags', DWORD),
822 ('ulType', DWORD),
823 ('pDN', LPSTR),
824 ('dwCodePage', DWORD),
825 ('dwLocaleID', DWORD),
826 )
828class NspiGetTemplateInfoResponse(NDRCALL):
829 structure = (
830 ('ppData', PPropertyRow_r),
831 ('ErrorCode', ULONG),
832 )
834# 3.1.4.15 NspiModLinkAtt (Opnum 14)
835class NspiModLinkAtt(NDRCALL):
836 opnum = 14
837 structure = (
838 ('hRpc', handle_t),
839 ('dwFlags', DWORD),
840 ('ulPropTag', DWORD),
841 ('dwMId', DWORD),
842 ('lpEntryIds', BinaryArray_r),
843 )
845class NspiModLinkAttResponse(NDRCALL):
846 structure = (
847 ('ErrorCode', ULONG),
848 )
850# Undocumented opnum 15
851#class NspiDeleteEntries(NDRCALL):
852# opnum = 15
853# structure = (
854# ('hRpc', handle_t),
855# ('dwFlags', DWORD),
856# ('dwMId', DWORD),
857# ('entryIds', BinaryArray_r),
858# )
860#class NspiDeleteEntriesResponse(NDRCALL):
861# structure = (
862# ('ErrorCode', ULONG),
863# )
865# 3.1.4.5 NspiQueryColumns (Opnum 16)
866class NspiQueryColumns(NDRCALL):
867 opnum = 16
868 structure = (
869 ('hRpc', handle_t),
870 ('Reserved', DWORD), # flags
871 ('dwFlags', DWORD), # mapiFlags
872 )
874class NspiQueryColumnsResponse(NDRCALL):
875 structure = (
876 ('ppColumns', PPropertyTagArray_r),
877 ('ErrorCode', ULONG),
878 )
880# 3.1.4.16 NspiGetNamesFromIDs (Opnum 17)
881class NspiGetNamesFromIDs(NDRCALL):
882 opnum = 17
883 structure = (
884 ('hRpc', handle_t),
885 ('Reserved', DWORD), # flags
886 ('lpguid', PFlatUID_r),
887 ('pPropTags', PPropertyTagArray_r),
888 )
890class NspiGetNamesFromIDsResponse(NDRCALL):
891 structure = (
892 ('ppReturnedPropTags', PPropertyTagArray_r),
893 ('ppNames', PPropertyNameSet_r),
894 ('ErrorCode', ULONG),
895 )
897# 3.1.4.17 NspiGetIDsFromNames (Opnum 18)
898class PropertyName_r_ARRAY(NDRUniConformantVaryingArray):
899 item = PropertyName_r
901class NspiGetIDsFromNames(NDRCALL):
902 opnum = 18
903 structure = (
904 ('hRpc', handle_t),
905 ('Reserved', DWORD), # flags
906 ('dwFlags', DWORD), # mapiFlags
907 ('cPropNames', DWORD),
908 ('pNames', PropertyName_r_ARRAY),
909 )
911class NspiGetIDsFromNamesResponse(NDRCALL):
912 structure = (
913 ('ppPropTags', PPropertyTagArray_r),
914 ('ErrorCode', ULONG),
915 )
917# 3.1.4.18 NspiResolveNames (Opnum 19)
918class NspiResolveNames(NDRCALL):
919 opnum = 19
920 structure = (
921 ('hRpc', handle_t),
922 ('Reserved', DWORD), # flags
923 ('pStat', STAT),
924 ('pPropTags', PPropertyTagArray_r),
925 ('paStr', StringsArray_r),
926 )
928class NspiResolveNamesResponse(NDRCALL):
929 structure = (
930 ('ppMIds', PPropertyTagArray_r),
931 ('ppRows', PPropertyRowSet_r),
932 ('ErrorCode', ULONG),
933 )
935# 3.1.4.19 NspiResolveNamesW (Opnum 20)
936class NspiResolveNamesW(NDRCALL):
937 opnum = 20
938 structure = (
939 ('hRpc', handle_t),
940 ('Reserved', DWORD), # flags
941 ('pStat', STAT),
942 ('pPropTags', PPropertyTagArray_r),
943 ('paStr', WStringsArray_r),
944 )
946class NspiResolveNamesWResponse(NDRCALL):
947 structure = (
948 ('ppMIds', PPropertyTagArray_r),
949 ('ppRows', PPropertyRowSet_r),
950 ('ErrorCode', ULONG),
951 )
953################################################################################
954# OPNUMs and their corresponding structures
955################################################################################
956OPNUMS = {
957 0 : (NspiBind, NspiBindResponse),
958 1 : (NspiUnbind, NspiUnbindResponse),
959 2 : (NspiUpdateStat, NspiUpdateStatResponse),
960 3 : (NspiQueryRows, NspiQueryRowsResponse),
961 4 : (NspiSeekEntries, NspiSeekEntriesResponse),
962# 5 : (NspiGetMatches, NspiGetMatchesResponse),
963# 6 : (NspiResortRestriction, NspiResortRestrictionResponse),
964 7 : (NspiDNToMId, NspiDNToMIdResponse),
965 8 : (NspiGetPropList, NspiGetPropListResponse),
966 9 : (NspiGetProps, NspiGetPropsResponse),
967 10 : (NspiCompareMIds, NspiCompareMIdsResponse),
968# 11 : (NspiModProps, NspiModPropsResponse),
969 12 : (NspiGetSpecialTable, NspiGetSpecialTableResponse),
970 13 : (NspiGetTemplateInfo, NspiGetTemplateInfoResponse),
971 14 : (NspiModLinkAtt, NspiModLinkAttResponse),
972# 15 : (NspiDeleteEntries, NspiDeleteEntriesResponse),
973 16 : (NspiQueryColumns, NspiQueryColumnsResponse),
974 17 : (NspiGetNamesFromIDs, NspiGetNamesFromIDsResponse),
975 18 : (NspiGetIDsFromNames, NspiGetIDsFromNamesResponse),
976 19 : (NspiResolveNames, NspiResolveNamesResponse),
977 20 : (NspiResolveNamesW, NspiResolveNamesWResponse),
978}
980################################################################################
981# HELPER FUNCTIONS
982################################################################################
983def checkNullString(string):
984 if string == NULL:
985 return string
987 if string[-1:] != '\x00':
988 return string + '\x00'
989 else:
990 return string
992def get_guid_from_dn(legacyDN):
993 legacyDN = str(legacyDN)
994 guid = legacyDN[legacyDN.rfind("=")+1:]
996 return uuid.string_to_bin(guid)
998def get_dn_from_guid(guid, minimize=False):
999 if minimize:
1000 # MS-OXNSPI
1001 dn_template = "/guid="
1002 else:
1003 # MS-NSPI and MS-OXNSPI
1004 dn_template = "/o=NT5/ou=00000000000000000000000000000000/cn="
1006 guid_bin = string_to_bin(guid)
1008 if PY2:
1009 return "%s%s" % (dn_template, binascii.hexlify(guid_bin))
1010 else:
1011 return "%s%s" % (dn_template, str(binascii.hexlify(guid_bin), 'ascii'))
1013class EXCH_SID(LDAP_SID):
1014 def __str__(self):
1015 return self.formatCanonical()
1017class ExchBinaryObject(bytes):
1018 pass
1020def getUnixTime(t):
1021 t -= 116444736000000000
1022 t //= 10000000
1023 return t
1025def simplifyPropertyRow(rowSetElem):
1026 row = {}
1028 for prop in rowSetElem['lpProps']:
1029 prop_name_in_union = prop['Value'].structure[0][0]
1030 prop_value = prop['Value'].fields[prop_name_in_union]
1032 PropTag = prop['ulPropTag']
1034 if isinstance(prop_value, SHORT) or \
1035 isinstance(prop_value, USHORT) or \
1036 isinstance(prop_value, LONG) or \
1037 isinstance(prop_value, ULONG):
1038 row[PropTag] = int(prop_value['Data'])
1039 elif isinstance(prop_value, LPWSTR):
1040 if PropTag in [0x8c38001f]:
1041 # What is this field for?
1042 row[PropTag] = ExchBinaryObject(prop_value['Data'].encode("utf-16le")[:-2])
1043 else:
1044 row[PropTag] = prop_value['Data'][:-1]
1045 elif isinstance(prop_value, LPSTR):
1046 row[PropTag] = prop_value['Data'][:-1]
1047 elif isinstance(prop_value, Binary_r):
1048 value = b''.join(prop_value['lpb'])
1050 if PropTag in [0x80270102, 0x8c750102]:
1051 value = EXCH_SID(value)
1052 elif PropTag == 0x300b0102:
1053 value = value[:-1].decode("utf-8")
1054 elif value[4:20] == GUID_NSPI and value[20:24] == b'\x01\x00\x00\x00' and value[:4] == b'\x00\x00\x00\x00':
1055 value = PermanentEntryID(value)
1056 elif value[:4] == b'\x87\x00\x00\x00' and value[20:24] == b'\x01\x00\x00\x00' and len(value) == 32:
1057 value = EphemeralEntryID(value)
1058 elif PropTag in [0x8c6d0102, 0x68c40102, 0x8c730102, 0x0ff80102]:
1059 value = uuid.bin_to_string(value).lower()
1060 elif PropTag == 0x0ff60102:
1061 value = unpack('<l', value)[0]
1062 else:
1063 value = ExchBinaryObject(value)
1065 row[PropTag] = value
1066 elif isinstance(prop_value, BinaryArray_r):
1067 array = []
1068 for value in prop_value['lpbin']:
1069 array.append(b''.join(value['lpb']))
1070 row[PropTag] = array
1071 elif isinstance(prop_value, StringArray_r):
1072 array = []
1073 for value in prop_value['lppszA']:
1074 array.append(value['Data'][:-1])
1075 row[PropTag] = array
1076 elif isinstance(prop_value, WStringArray_r):
1077 array = []
1078 for value in prop_value['lppszW']:
1079 array.append(value['Data'][:-1])
1080 row[PropTag] = array
1081 elif isinstance(prop_value, FILETIME):
1082 row[PropTag] = datetime.fromtimestamp( \
1083 getUnixTime(unpack('<Q', prop_value.getData())[0]))
1084 else:
1085 row[PropTag] = prop_value
1087 return row
1089def simplifyPropertyRowSet(propertyRowSet):
1090 ret = []
1092 for rowSet in propertyRowSet['aRow']:
1093 ret.append(simplifyPropertyRow(rowSet))
1095 return ret
1097def hNspiBind(dce, pStat=None):
1098 request = NspiBind()
1100 if pStat == None:
1101 request['pStat']['CodePage'] = CP_TELETEX
1102 else:
1103 request['pStat'] = pStat
1105 resp = dce.request(request)
1106 return resp
1108def hNspiUnbind(dce, handler):
1109 request = NspiUnbind()
1110 request['contextHandle'] = handler
1112 resp = dce.request(request, checkError=False)
1113 return resp
1115def hNspiUpdateStat(dce, handler, pStat, plDelta=NULL):
1116 request = NspiUpdateStat()
1117 request['hRpc'] = handler
1118 request['pStat'] = pStat
1119 request['plDelta'] = plDelta
1121 resp = dce.request(request, checkError=False)
1122 return resp
1124def hNspiQueryRows(dce, handler, dwFlags=fSkipObjects, pStat=None, ContainerID=0,
1125 Count=50, pPropTags=[], pPropTagsRaw=NULL, lpETable=[]):
1126 request = NspiQueryRows()
1127 request['hRpc'] = handler
1128 request['dwFlags'] = dwFlags
1129 request['Count'] = Count
1131 if pStat == None:
1132 request['pStat']['ContainerID'] = ContainerID
1133 else:
1134 request['pStat'] = pStat
1136 if len(pPropTags) > 0:
1137 for aulPropTag in pPropTags:
1138 prop = DWORD()
1139 prop['Data'] = aulPropTag
1140 request['pPropTags']['aulPropTag'].append(prop)
1141 request['pPropTags']['cValues'] = len(pPropTags)
1142 request.fields['pPropTags'].fields['Data'].fields['aulPropTag'].fields['MaximumCount'] = len(pPropTags) + 1
1143 else:
1144 request['pPropTags'] = pPropTagsRaw
1146 if len(lpETable) > 0:
1147 for mID in lpETable:
1148 elem = DWORD()
1149 elem['Data'] = mID
1150 request['lpETable'].append(elem)
1151 request['dwETableCount'] = len(lpETable)
1152 else:
1153 request['lpETable'] = NULL
1154 request['dwETableCount'] = 0
1156 resp = dce.request(request)
1157 return resp
1159def hNspiSeekEntries(dce, handler, displayName, ContainerID=0, SortType=0, \
1160 lpETable=[], lpETableRaw=NULL, pPropTags=[], pPropTagsRaw=NULL):
1161 request = NspiSeekEntries()
1162 request['hRpc'] = handler
1163 request['pStat']['ContainerID'] = ContainerID
1165 # MS-OXNSPI 3.1.4.1.9.9
1166 # If the SortType field in the input parameter pStat has any value other than
1167 # SortTypeDisplayName, the server MUST return the value GeneralFailure.
1168 request['pStat']['SortType'] = SortTypeDisplayName
1170 # MS-OXNSPI 3.1.4.1.9.10
1171 # If the SortType field in the input parameter pStat is SortTypeDisplayName and the property
1172 # specified in the input parameter pTarget is anything other than PidTagDisplayName (with either
1173 # the Property Type PtypString8 or PtypString), the server MUST return the value
1174 # GeneralFailure.
1175 request['pTarget']['ulPropTag'] = 0x3001001F
1176 request['pTarget']['Value']['tag'] = 0x0000001F
1177 request['pTarget']['Value']['lpszW'] = checkNullString(displayName)
1179 if len(lpETable) > 0:
1180 for mID in lpETable:
1181 elem = DWORD()
1182 elem['Data'] = mID
1183 request['lpETable'].append(elem)
1184 else:
1185 request['lpETable'] = lpETableRaw
1187 if len(pPropTags) > 0:
1188 for aulPropTag in pPropTags:
1189 prop = DWORD()
1190 prop['Data'] = aulPropTag
1191 request['pPropTags']['aulPropTag'].append(prop)
1192 request.fields['pPropTags'].fields['aulPropTag'].fields['MaximumCount'] = len(pPropTags) + 1
1193 else:
1194 request['pPropTags'] = pPropTagsRaw
1196 resp = dce.request(request)
1197 return resp
1199def hNspiDNToMId(dce, handler, pNames=[]):
1200 request = NspiDNToMId()
1201 request['hRpc'] = handler
1202 request['pNames']['Count'] = len(pNames)
1204 for name in pNames:
1205 lpstr = LPSTR()
1206 lpstr['Data'] = checkNullString(name)
1207 request['pNames']['Strings'].append(lpstr)
1209 resp = dce.request(request)
1210 return resp
1212def hNspiGetPropList(dce, handler, dwMId=0, dwFlags=fSkipObjects, CodePage=CP_TELETEX):
1213 request = NspiGetPropList()
1214 request['hRpc'] = handler
1215 request['dwMId'] = dwMId
1216 request['dwFlags'] = dwFlags
1217 request['CodePage'] = CodePage
1218 resp = dce.request(request)
1220 return resp
1222def hNspiGetProps(dce, handler, ContainerID=0, CurrentRec=0, dwFlags=fSkipObjects, CodePage=CP_TELETEX, pPropTags=[]):
1223 request = NspiGetProps()
1224 request['hRpc'] = handler
1225 request['dwFlags'] = dwFlags
1227 request['pStat']['CurrentRec'] = CurrentRec
1228 request['pStat']['ContainerID'] = ContainerID
1229 request['pStat']['CodePage'] = CodePage
1231 for aulPropTag in pPropTags:
1232 prop = DWORD()
1233 prop['Data'] = aulPropTag
1234 request['pPropTags']['aulPropTag'].append(prop)
1235 request['pPropTags']['cValues'] = len(pPropTags) + 1
1236 request.fields['pPropTags'].fields['Data'].fields['aulPropTag'].fields['MaximumCount'] = len(pPropTags) + 1
1238 resp = dce.request(request)
1239 return resp
1241def hNspiGetSpecialTable(dce, handler, dwFlags=NspiUnicodeStrings, pStat=STAT(), lpVersion=NULL):
1242 request = NspiGetSpecialTable()
1243 request['hRpc'] = handler
1244 request['dwFlags'] = dwFlags
1245 request['pStat'] = pStat
1246 request['lpVersion'] = lpVersion
1248 resp = dce.request(request)
1249 return resp
1251# Lookups specified LegacyDN or CN={ulType},CN={dwLocaleID},CN=Display-Templates,CN=Addressing in Configuration Naming Context
1252def hNspiGetTemplateInfo(dce, handler, pDN=NULL, dwLocaleID=0, ulType=0, dwCodePage=0, dwFlags=0xFFFFFFFF):
1253 request = NspiGetTemplateInfo()
1254 request['hRpc'] = handler
1255 request['dwFlags'] = dwFlags
1256 request['ulType'] = ulType
1257 request['pDN'] = checkNullString(pDN)
1258 request['dwCodePage'] = dwCodePage
1259 request['dwLocaleID'] = dwLocaleID
1261 resp = dce.request(request)
1262 return resp
1264def hNspiModLinkAtt(dce, handler, dwFlags, ulPropTag, dwMId, lpEntryIds):
1265 request = NspiModLinkAtt()
1266 request['hRpc'] = handler
1267 request['dwFlags'] = dwFlags
1268 request['ulPropTag'] = ulPropTag
1269 request['dwMId'] = dwMId
1271 for lpEntryId in lpEntryIds:
1272 prop = Binary_r()
1273 prop['lpb'] = lpEntryId.getData()
1274 prop['cValues'] = len(prop['lpb'])
1275 request['lpEntryIds']['lpbin'].append(prop)
1276 request['lpEntryIds']['cValues'] = len(lpEntryIds)
1278 resp = dce.request(request)
1279 return resp
1281def hNspiQueryColumns(dce, handler, dwFlags=NspiUnicodeProptypes):
1282 request = NspiQueryColumns()
1283 request['hRpc'] = handler
1284 request['dwFlags'] = dwFlags
1286 resp = dce.request(request)
1287 return resp
1289def hNspiGetNamesFromIDs(dce, handler, lpguid=EMPTY_UUID, pPropTags=[], pPropTagsRaw=NULL):
1290 request = NspiGetNamesFromIDs()
1291 request['hRpc'] = handler
1292 request['lpguid'] = lpguid
1294 if len(pPropTags) > 0:
1295 for aulPropTag in pPropTags:
1296 prop = DWORD()
1297 prop['Data'] = aulPropTag
1298 request['pPropTags']['aulPropTag'].append(prop)
1299 request['pPropTags']['cValues'] = len(pPropTags)
1300 request.fields['pPropTags'].fields['Data'].fields['aulPropTag'].fields['MaximumCount'] = len(pPropTags) + 1
1301 elif pPropTagsRaw == NULL:
1302 request.fields['pPropTags'] = NULL
1303 else:
1304 request['pPropTags'] = pPropTagsRaw
1306 resp = dce.request(request)
1307 return resp
1309def hNspiResolveNames(dce, handler, ContainerID=0, pPropTags=[], pPropTagsRaw=NULL, paStr=[]):
1310 request = NspiResolveNames()
1311 request['hRpc'] = handler
1312 request['pStat']['ContainerID'] = ContainerID
1314 if len(pPropTags) > 0:
1315 for aulPropTag in pPropTags:
1316 prop = DWORD()
1317 prop['Data'] = aulPropTag
1318 request['pPropTags']['aulPropTag'].append(prop)
1319 request['pPropTags']['cValues'] = len(pPropTags)
1320 request.fields['pPropTags'].fields['Data'].fields['aulPropTag'].fields['MaximumCount'] = len(pPropTags) + 1
1321 elif pPropTagsRaw == NULL:
1322 request.fields['pPropTags'] = NULL
1323 else:
1324 request['pPropTags'] = pPropTagsRaw
1326 if len(paStr) > 0:
1327 for paStrElem in paStr:
1328 value = LPSTR()
1329 value['Data'] = checkNullString(paStrElem)
1330 request['paStr']['Strings'].append(value)
1331 request['paStr']['Count'] = len(paStr)
1333 resp = dce.request(request)
1334 return resp
1336def hNspiResolveNamesW(dce, handler, ContainerID=0, pPropTags=[], pPropTagsRaw=NULL, paStr=[]):
1337 request = NspiResolveNamesW()
1338 request['hRpc'] = handler
1339 request['pStat']['ContainerID'] = ContainerID
1341 if len(pPropTags) > 0:
1342 for aulPropTag in pPropTags:
1343 prop = DWORD()
1344 prop['Data'] = aulPropTag
1345 request['pPropTags']['aulPropTag'].append(prop)
1346 request['pPropTags']['cValues'] = len(pPropTags)
1347 request.fields['pPropTags'].fields['Data'].fields['aulPropTag'].fields['MaximumCount'] = len(pPropTags) + 1
1348 elif pPropTagsRaw == NULL:
1349 request.fields['pPropTags'] = NULL
1350 else:
1351 request['pPropTags'] = pPropTagsRaw
1353 if len(paStr) > 0:
1354 for paStrElem in paStr:
1355 value = LPWSTR()
1356 value['Data'] = checkNullString(paStrElem)
1357 request['paStr']['Strings'].append(value)
1358 request['paStr']['Count'] = len(paStr)
1360 resp = dce.request(request)
1361 return resp