1# Copyright (c) 2016 CORE Security Technologies
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Authors: Alberto Solino (@agsolino)
8#          Kacper Nowak (@kacpern)
9#
10# Description:
11#   RFC 4511 Minimalistic implementation. We don't need much functionality yet
12#   If we need more complex use cases we might opt to use a third party implementation
13#   Keep in mind the APIs are still unstable, might require to re-write your scripts
14#   as we change them.
15#   Adding [MS-ADTS] specific functionality
16#
17
18from pyasn1.codec.ber import encoder, decoder
19from pyasn1.type import univ, namedtype, namedval, tag, constraint
20
21__all__ = [
22    'CONTROL_PAGEDRESULTS', 'CONTROL_SDFLAGS', 'KNOWN_CONTROLS', 'NOTIFICATION_DISCONNECT', 'KNOWN_NOTIFICATIONS',
23    # classes
24    'ResultCode', 'Scope', 'DerefAliases', 'Operation', 'MessageID', 'LDAPString', 'LDAPOID', 'LDAPDN',
25    'RelativeLDAPDN', 'AttributeDescription', 'AttributeValue', 'AssertionValue', 'MatchingRuleID', 'URI',
26    'AttributeValueAssertion', 'PartialAttribute', 'PartialAttributeList', 'Attribute', 'AttributeList',
27    'AttributeSelection', 'Referral', 'LDAPResult', 'SaslCredentials', 'AuthenticationChoice', 'BindRequest',
28    'BindResponse', 'UnbindRequest', 'SubstringFilter', 'MatchingRuleAssertion', 'Filter', 'SearchRequest',
29    'SearchResultEntry', 'SearchResultReference', 'SearchResultDone', 'ModifyRequest', 'ModifyResponse', 'AddRequest',
30    'AddResponse', 'DelRequest', 'DelResponse', 'ModifyDNRequest', 'ModifyDNResponse', 'CompareRequest',
31    'CompareResponse', 'AbandonRequest', 'ExtendedRequest', 'ExtendedResponse', 'IntermediateResponse', 'Control',
32    'Controls', 'SimplePagedResultsControlValue', 'SimplePagedResultsControl', 'LDAPMessage'
33]
34
35# Controls
36CONTROL_PAGEDRESULTS = '1.2.840.113556.1.4.319'
37CONTROL_SDFLAGS = '1.2.840.113556.1.4.801'
38
39KNOWN_CONTROLS = {}
40
41# Unsolicited notifications
42NOTIFICATION_DISCONNECT = '1.3.6.1.4.1.1466.20036'
43
44KNOWN_NOTIFICATIONS = {NOTIFICATION_DISCONNECT: 'Notice of Disconnection'}
45
46maxInt = univ.Integer(2147483647)
47
48
49class DefaultSequenceAndSetBaseMixin:
50    def getComponentByPosition(self, idx, default=univ.noValue, instantiate=True):
51        for cls in self.__class__.__bases__:
52            if cls is not DefaultSequenceAndSetBaseMixin:
53                try:
54                    component = cls.getComponentByPosition(self, idx)#, default, instantiate)
55                except AttributeError:
56                    continue
57                if component is None:
58                    return self.setComponentByPosition(idx).getComponentByPosition(idx)# , default, instantiate)
59                return component
60
61
62class ResultCode(univ.Enumerated):
63    namedValues = namedval.NamedValues(
64        ('success', 0),
65        ('operationsError', 1),
66        ('protocolError', 2),
67        ('timeLimitExceeded', 3),
68        ('sizeLimitExceeded', 4),
69        ('compareFalse', 5),
70        ('compareTrue', 6),
71        ('authMethodNotSupported', 7),
72        ('strongerAuthRequired', 8),
73        ('referral', 10),
74        ('adminLimitExceeded', 11),
75        ('unavailableCriticalExtension', 12),
76        ('confidentialityRequired', 13),
77        ('saslBindInProgress', 14),
78        ('noSuchAttribute', 16),
79        ('undefinedAttributeType', 17),
80        ('inappropriateMatching', 18),
81        ('constraintViolation', 19),
82        ('attributeOrValueExists', 20),
83        ('invalidAttributeSyntax', 21),
84        ('noSuchObject', 32),
85        ('aliasProblem', 33),
86        ('invalidDNSyntax', 34),
87        ('aliasDereferencingProblem', 36),
88        ('inappropriateAuthentication', 48),
89        ('invalidCredentials', 49),
90        ('insufficientAccessRights', 50),
91        ('busy', 51),
92        ('unavailable', 52),
93        ('unwillingToPerform', 53),
94        ('loopDetect', 54),
95        ('namingViolation', 64),
96        ('objectClassViolation', 65),
97        ('notAllowedOnNonLeaf', 66),
98        ('notAllowedOnRDN', 67),
99        ('entryAlreadyExists', 68),
100        ('objectClassModsProhibited', 69),
101        ('affectsMultipleDSAs', 71),
102        ('other', 80),
103    )
104
105
106class Scope(univ.Enumerated):
107    namedValues = namedval.NamedValues(
108        ('baseObject', 0),
109        ('singleLevel', 1),
110        ('wholeSubtree', 2),
111    )
112
113
114class DerefAliases(univ.Enumerated):
115    namedValues = namedval.NamedValues(
116        ('neverDerefAliases', 0),
117        ('derefInSearching', 1),
118        ('derefFindingBaseObj', 2),
119        ('derefAlways', 3),
120    )
121
122
123class Operation(univ.Enumerated):
124    namedValues = namedval.NamedValues(
125        ('add', 0),
126        ('delete', 1),
127        ('replace', 2),
128    )
129
130
131class MessageID(univ.Integer):
132    subtypeSpec = constraint.ValueRangeConstraint(0, maxInt)
133
134
135class LDAPString(univ.OctetString):
136    encoding = 'utf-8'
137
138
139class LDAPOID(univ.OctetString):
140    pass
141
142
143class LDAPDN(LDAPString):
144    pass
145
146
147class RelativeLDAPDN(LDAPString):
148    pass
149
150
151class AttributeDescription(LDAPString):
152    pass
153
154
155class AttributeValue(univ.OctetString):
156    pass
157
158
159class AssertionValue(univ.OctetString):
160    pass
161
162
163class MatchingRuleID(LDAPString):
164    pass
165
166
167class URI(LDAPString):
168    pass
169
170
171class AttributeValueAssertion(univ.Sequence):
172    componentType = namedtype.NamedTypes(
173        namedtype.NamedType('attributeDesc', AttributeDescription()),
174        namedtype.NamedType('assertionValue', AssertionValue())
175    )
176
177
178class PartialAttribute(univ.Sequence):
179    componentType = namedtype.NamedTypes(
180        namedtype.NamedType('type', AttributeDescription()),
181        namedtype.NamedType('vals', univ.SetOf(componentType=AttributeValue()))
182    )
183
184
185class PartialAttributeList(univ.SequenceOf):
186    componentType = PartialAttribute()
187
188
189class Attribute(univ.Sequence):
190    componentType = namedtype.NamedTypes(
191        namedtype.NamedType('type', AttributeDescription()),
192        namedtype.NamedType(
193            'vals',
194            univ.SetOf(componentType=AttributeValue()).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, maxInt))
195        )
196    )
197
198
199class AttributeList(univ.SequenceOf):
200    componentType = Attribute()
201
202
203class AttributeSelection(univ.SequenceOf):
204    componentType = LDAPString()
205
206
207class Referral(univ.SequenceOf):
208    componentType = URI()
209    subtypeSpec = constraint.ValueSizeConstraint(1, maxInt)
210
211
212class LDAPResult(univ.Sequence):
213    componentType = namedtype.NamedTypes(
214        namedtype.NamedType('resultCode', ResultCode()),
215        namedtype.NamedType('matchedDN', LDAPDN()),
216        namedtype.NamedType('diagnosticMessage', LDAPString()),
217        namedtype.OptionalNamedType(
218            'referral', Referral().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
219        )
220    )
221
222
223class SaslCredentials(univ.Sequence):
224    componentType = namedtype.NamedTypes(
225        namedtype.NamedType('mechanism', LDAPString()),
226        namedtype.OptionalNamedType('credentials', univ.OctetString())
227    )
228
229
230class AuthenticationChoice(DefaultSequenceAndSetBaseMixin, univ.Choice):
231    componentType = namedtype.NamedTypes(
232        namedtype.NamedType(
233            'simple',
234            univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
235        ),
236        namedtype.NamedType(
237            'sasl',
238            SaslCredentials().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))
239        ),
240        namedtype.NamedType(
241            'sicilyPackageDiscovery',
242            univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 9))
243        ),
244        namedtype.NamedType(
245            'sicilyNegotiate',
246            univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10))
247        ),
248        namedtype.NamedType(
249            'sicilyResponse',
250            univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 11))
251        )
252    )
253
254
255class BindRequest(DefaultSequenceAndSetBaseMixin, univ.Sequence):
256    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 0))
257    componentType = namedtype.NamedTypes(
258        namedtype.NamedType('version', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, 127))),
259        namedtype.NamedType('name', LDAPDN()),
260        namedtype.NamedType('authentication', AuthenticationChoice())
261    )
262
263
264class BindResponse(univ.Sequence):
265    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 1))
266    componentType = namedtype.NamedTypes(
267        namedtype.NamedType('resultCode', ResultCode()),
268        namedtype.NamedType('matchedDN', LDAPDN()),
269        namedtype.NamedType('diagnosticMessage', LDAPString()),
270        namedtype.OptionalNamedType(
271            'referral',
272            Referral().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
273        ),
274        namedtype.OptionalNamedType(
275            'serverSaslCreds',
276            univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))
277        )
278    )
279
280
281class UnbindRequest(univ.Null):
282    tagSet = univ.Null.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2))
283
284
285class SubstringFilter(DefaultSequenceAndSetBaseMixin, univ.Sequence):
286    componentType = namedtype.NamedTypes(
287        namedtype.NamedType('type', AttributeDescription()),
288        namedtype.NamedType(
289            'substrings',
290            univ.SequenceOf(componentType=univ.Choice(componentType=namedtype.NamedTypes(
291                namedtype.NamedType(
292                    'initial',
293                    AssertionValue().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
294                ),
295                namedtype.NamedType(
296                    'any',
297                    AssertionValue().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
298                ),
299                namedtype.NamedType(
300                    'final',
301                    AssertionValue().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))
302                )
303            )))
304        )
305    )
306
307
308class MatchingRuleAssertion(univ.Sequence):
309    componentType = namedtype.NamedTypes(
310        namedtype.OptionalNamedType(
311            'matchingRule',
312            MatchingRuleID().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
313        ),
314        namedtype.OptionalNamedType(
315            'type',
316            AttributeDescription().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))
317        ),
318        namedtype.NamedType(
319            'matchValue',
320            AssertionValue().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
321        ),
322        namedtype.DefaultedNamedType(
323            'dnAttributes',
324            univ.Boolean().subtype(value=False, implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
325        )
326    )
327
328
329class Filter(DefaultSequenceAndSetBaseMixin, univ.Choice):
330    pass
331
332
333Filter.componentType = namedtype.NamedTypes(
334    namedtype.NamedType(
335        'and',
336        univ.SetOf(componentType=Filter()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
337    ),
338    namedtype.NamedType(
339        'or',
340        univ.SetOf(componentType=Filter()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
341    ),
342    namedtype.NamedType(
343        'not',
344        univ.SetOf(componentType=Filter()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))
345        #Filter().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))
346    ),
347    namedtype.NamedType(
348        'equalityMatch',
349        AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))
350    ),
351    namedtype.NamedType(
352        'substrings',
353        SubstringFilter().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4))
354    ),
355    namedtype.NamedType(
356        'greaterOrEqual',
357        AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 5))
358    ),
359    namedtype.NamedType(
360        'lessOrEqual',
361        AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 6))
362    ),
363    namedtype.NamedType(
364        'present',
365        AttributeDescription().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))
366    ),
367    namedtype.NamedType(
368        'approxMatch',
369        AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 8))
370    ),
371    namedtype.NamedType(
372        'extensibleMatch',
373        MatchingRuleAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 9))
374    )
375)
376
377
378class SearchRequest(DefaultSequenceAndSetBaseMixin, univ.Sequence):
379    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 3))
380    componentType = namedtype.NamedTypes(
381        namedtype.NamedType('baseObject', LDAPDN()),
382        namedtype.NamedType('scope', Scope()),
383        namedtype.NamedType('derefAliases', DerefAliases()),
384        namedtype.NamedType(
385            'sizeLimit', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, maxInt))
386        ),
387        namedtype.NamedType(
388            'timeLimit', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, maxInt))
389        ),
390        namedtype.NamedType('typesOnly', univ.Boolean()),
391        namedtype.NamedType('filter', Filter()),
392        namedtype.NamedType('attributes', AttributeSelection())
393    )
394
395
396class SearchResultEntry(univ.Sequence):
397    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 4))
398    componentType = namedtype.NamedTypes(
399        namedtype.NamedType('objectName', LDAPDN()),
400        namedtype.NamedType('attributes', PartialAttributeList())
401    )
402
403
404class SearchResultReference(univ.SequenceOf):
405    tagSet = univ.SequenceOf.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 19))
406    componentType = URI()
407    subtypeSpec = constraint.ValueSizeConstraint(1, maxInt)
408
409
410class SearchResultDone(LDAPResult):
411    tagSet = LDAPResult.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 5))
412
413
414class ModifyRequest(DefaultSequenceAndSetBaseMixin, univ.Sequence):
415    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 6))
416    componentType = namedtype.NamedTypes(
417        namedtype.NamedType('object', LDAPDN()),
418        namedtype.NamedType(
419            'changes',
420            univ.SequenceOf(componentType=univ.Sequence(componentType=namedtype.NamedTypes(
421                namedtype.NamedType('operation', Operation()),
422                namedtype.NamedType('modification', PartialAttribute())
423            )))
424        )
425    )
426
427
428class ModifyResponse(LDAPResult):
429    tagSet = LDAPResult.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 7))
430
431
432class AddRequest(DefaultSequenceAndSetBaseMixin, univ.Sequence):
433    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 8))
434    componentType = namedtype.NamedTypes(
435        namedtype.NamedType('entry', LDAPDN()),
436        namedtype.NamedType('attributes', AttributeList())
437    )
438
439
440class AddResponse(LDAPResult):
441    tagSet = LDAPResult.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 9))
442
443
444class DelRequest(LDAPDN):
445    tagSet = LDAPDN.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 10))
446
447
448class DelResponse(LDAPResult):
449    tagSet = LDAPResult.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 11))
450
451
452class ModifyDNRequest(univ.Sequence):
453    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 12))
454    componentType = namedtype.NamedTypes(
455        namedtype.NamedType('entry', LDAPDN()),
456        namedtype.NamedType('newrdn', RelativeLDAPDN()),
457        namedtype.NamedType('deleteoldrdn', univ.Boolean()),
458        namedtype.OptionalNamedType(
459            'newSuperior', LDAPDN().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
460        )
461    )
462
463
464class ModifyDNResponse(LDAPResult):
465    tagSet = LDAPResult.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 13))
466
467
468class CompareRequest(DefaultSequenceAndSetBaseMixin, univ.Sequence):
469    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 14))
470    componentType = namedtype.NamedTypes(
471        namedtype.NamedType('entry', LDAPDN()),
472        namedtype.NamedType('ava', AttributeValueAssertion())
473    )
474
475
476class CompareResponse(LDAPResult):
477    tagSet = LDAPResult.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 15))
478
479
480class AbandonRequest(MessageID):
481    tagSet = MessageID.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 16))
482
483
484class ExtendedRequest(univ.Sequence):
485    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 23))
486    componentType = namedtype.NamedTypes(
487        namedtype.NamedType(
488            'requestName', LDAPOID().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
489        ),
490        namedtype.OptionalNamedType(
491            'requestValue', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
492        )
493    )
494
495
496class ExtendedResponse(univ.Sequence):
497    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 24))
498    componentType = namedtype.NamedTypes(
499        namedtype.NamedType('resultCode', ResultCode()),
500        namedtype.NamedType('matchedDN', LDAPDN()),
501        namedtype.NamedType('diagnosticMessage', LDAPString()),
502        namedtype.OptionalNamedType(
503            'referral',
504            Referral().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
505        ),
506        namedtype.OptionalNamedType(
507            'responseName',
508            LDAPOID().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10))
509        ),
510        namedtype.OptionalNamedType(
511            'responseValue',
512            univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 11))
513        )
514    )
515
516
517class IntermediateResponse(univ.Sequence):
518    tagSet = univ.Sequence.tagSet.tagImplicitly(tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 25))
519    componentType = namedtype.NamedTypes(
520        namedtype.OptionalNamedType(
521            'responseName',
522            LDAPOID().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
523        ),
524        namedtype.OptionalNamedType(
525            'responseValue',
526            univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
527        )
528    )
529
530
531class Control(univ.Sequence):
532    componentType = namedtype.NamedTypes(
533        namedtype.NamedType('controlType', LDAPOID()),
534        namedtype.DefaultedNamedType('criticality', univ.Boolean().subtype(value=False)),
535        namedtype.OptionalNamedType('controlValue', univ.OctetString())
536    )
537
538    def setComponentByPosition(self, idx, value=univ.noValue,
539                               verifyConstraints=True,
540                               matchTags=True,
541                               matchConstraints=True):
542        if idx == 0:  # controlType
543            try:
544                cls = KNOWN_CONTROLS[value]
545                if self.__class__ is not cls:
546                    self.__class__ = cls
547            except KeyError:
548                pass
549        return univ.Sequence.setComponentByPosition(self, idx, value=value,
550                                                    verifyConstraints=verifyConstraints,
551                                                    matchTags=matchTags,
552                                                    matchConstraints=matchConstraints)
553
554    def encodeControlValue(self):
555        pass
556
557    def decodeControlValue(self):
558        return
559
560    def prettyPrint(self, scope=0):
561        r = univ.Sequence.prettyPrint(self, scope)
562        decodedControlValue = self.decodeControlValue()
563        if decodedControlValue is not None:
564            r = r[:r.rindex('=') + 1] + '%s\n' % decodedControlValue.prettyPrint(scope + 1)
565        return r
566
567
568class Controls(univ.SequenceOf):
569    componentType = Control()
570
571
572class SDFlagsControlValue(univ.Sequence):
573    componentType = namedtype.NamedTypes(
574        namedtype.NamedType('flags', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, maxInt))),
575    )
576
577class SDFlagsControl(Control):
578    def __init__(self, criticality=None, flags=0x00000007L, **kwargs):
579        Control.__init__(self, **kwargs)
580        self['controlType'] = CONTROL_SDFLAGS
581        if criticality is not None:
582            self['criticality'] = criticality
583        self.flags = flags
584        self.encodeControlValue()
585
586    def encodeControlValue(self):
587        self['controlValue'] = encoder.encode(
588            SDFlagsControlValue().setComponents(self.flags))
589
590    def decodeControlValue(self):
591        decodedControlValue, _ = decoder.decode(self['controlValue'], asn1Spec=SDFlagsControlValue())
592        self._flags =  decodedControlValue[0]
593        return decodedControlValue
594
595    def getCriticality(self):
596        return self['criticality']
597
598    def setCriticality(self, value):
599        self['criticality'] = value
600
601    def getFlags(self):
602        self.decodeControlValue()
603        return self._flags
604
605    def setFlags(self, value):
606        self._flags = value
607        self.encodeControlValue()
608
609class SimplePagedResultsControlValue(univ.Sequence):
610    componentType = namedtype.NamedTypes(
611        namedtype.NamedType('size', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, maxInt))),
612        namedtype.NamedType('cookie', univ.OctetString()),
613    )
614
615
616class SimplePagedResultsControl(Control):
617    def __init__(self, criticality=None, size=1000, cookie='', **kwargs):
618        Control.__init__(self, **kwargs)
619        self['controlType'] = CONTROL_PAGEDRESULTS
620        if criticality is not None:
621            self['criticality'] = criticality
622        self._size = size
623        self._cookie = cookie
624        self.encodeControlValue()
625
626    def encodeControlValue(self):
627        self['controlValue'] = encoder.encode(SimplePagedResultsControlValue().setComponents(self._size, self._cookie))
628
629    def decodeControlValue(self):
630        decodedControlValue, _ = decoder.decode(self['controlValue'], asn1Spec=SimplePagedResultsControlValue())
631        self._size, self._cookie = decodedControlValue[0], decodedControlValue[1]
632        return decodedControlValue
633
634    def getCriticality(self):
635        return self['criticality']
636
637    def setCriticality(self, value):
638        self['criticality'] = value
639
640    def getSize(self):
641        self.decodeControlValue()
642        return self._size
643
644    def setSize(self, value):
645        self._size = value
646        self.encodeControlValue()
647
648    def getCookie(self):
649        self.decodeControlValue()
650        return self._cookie
651
652    def setCookie(self, value):
653        self._cookie = value
654        self.encodeControlValue()
655
656
657KNOWN_CONTROLS[CONTROL_PAGEDRESULTS] = SimplePagedResultsControl
658KNOWN_CONTROLS[CONTROL_SDFLAGS] = SDFlagsControl
659
660class LDAPMessage(DefaultSequenceAndSetBaseMixin, univ.Sequence):
661    componentType = namedtype.NamedTypes(
662        namedtype.NamedType('messageID', MessageID()),
663        namedtype.NamedType('protocolOp', univ.Choice(componentType=namedtype.NamedTypes(
664            namedtype.NamedType('bindRequest', BindRequest()),
665            namedtype.NamedType('bindResponse', BindResponse()),
666            namedtype.NamedType('unbindRequest', UnbindRequest()),
667            namedtype.NamedType('searchRequest', SearchRequest()),
668            namedtype.NamedType('searchResEntry', SearchResultEntry()),
669            namedtype.NamedType('searchResDone', SearchResultDone()),
670            namedtype.NamedType('searchResRef', SearchResultReference()),
671            namedtype.NamedType('modifyRequest', ModifyRequest()),
672            namedtype.NamedType('modifyResponse', ModifyResponse()),
673            namedtype.NamedType('addRequest', AddRequest()),
674            namedtype.NamedType('addResponse', AddResponse()),
675            namedtype.NamedType('delRequest', DelRequest()),
676            namedtype.NamedType('delResponse', DelResponse()),
677            namedtype.NamedType('modDNRequest', ModifyDNRequest()),
678            namedtype.NamedType('modDNResponse', ModifyDNResponse()),
679            namedtype.NamedType('compareRequest', CompareRequest()),
680            namedtype.NamedType('compareResponse', CompareResponse()),
681            namedtype.NamedType('abandonRequest', AbandonRequest()),
682            namedtype.NamedType('extendedReq', ExtendedRequest()),
683            namedtype.NamedType('extendedResp', ExtendedResponse()),
684            namedtype.NamedType('intermediateResponse', IntermediateResponse())
685        ))),
686        namedtype.OptionalNamedType(
687            'controls',
688            Controls().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))
689        ),
690        # fix AD nonconforming to RFC4511
691        namedtype.OptionalNamedType(
692            'responseName',
693            LDAPOID().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10))
694        ),
695        namedtype.OptionalNamedType(
696            'responseValue',
697            univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 11))
698        )
699    )
700