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