Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

asn1.py 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. """
  2. """
  3. # Created on 2015.08.19
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2015 - 2018 Giovanni Cannata
  8. #
  9. # This file is part of ldap3.
  10. #
  11. # ldap3 is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU Lesser General Public License as published
  13. # by the Free Software Foundation, either version 3 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # ldap3 is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU Lesser General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Lesser General Public License
  22. # along with ldap3 in the COPYING and COPYING.LESSER files.
  23. # If not, see <http://www.gnu.org/licenses/>.
  24. from pyasn1 import __version__ as pyasn1_version
  25. from pyasn1.codec.ber import decoder # for usage in other modules
  26. from pyasn1.codec.ber.encoder import Encoder # for monkeypatching of boolean value
  27. from ..core.results import RESULT_CODES
  28. from ..utils.conv import to_unicode
  29. from ..protocol.convert import referrals_to_list
  30. CLASSES = {(False, False): 0, # Universal
  31. (False, True): 1, # Application
  32. (True, False): 2, # Context
  33. (True, True): 3} # Private
  34. # Monkeypatching of pyasn1 for encoding Boolean with the value 0xFF for TRUE
  35. # THIS IS NOT PART OF THE FAST BER DECODER
  36. if pyasn1_version == 'xxx0.2.3':
  37. from pyasn1.codec.ber.encoder import tagMap, BooleanEncoder, encode
  38. from pyasn1.type.univ import Boolean
  39. from pyasn1.compat.octets import ints2octs
  40. class BooleanCEREncoder(BooleanEncoder):
  41. _true = ints2octs((255,))
  42. tagMap[Boolean.tagSet] = BooleanCEREncoder()
  43. else:
  44. from pyasn1.codec.ber.encoder import tagMap, typeMap, AbstractItemEncoder
  45. from pyasn1.type.univ import Boolean
  46. from copy import deepcopy
  47. class LDAPBooleanEncoder(AbstractItemEncoder):
  48. supportIndefLenMode = False
  49. if pyasn1_version <= '0.2.3':
  50. from pyasn1.compat.octets import ints2octs
  51. _true = ints2octs((255,))
  52. _false = ints2octs((0,))
  53. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  54. return value and self._true or self._false, 0
  55. elif pyasn1_version <= '0.3.1':
  56. def encodeValue(self, encodeFun, value, defMode, maxChunkSize):
  57. return value and (255,) or (0,), False, False
  58. elif pyasn1_version <= '0.3.4':
  59. def encodeValue(self, encodeFun, value, defMode, maxChunkSize, ifNotEmpty=False):
  60. return value and (255,) or (0,), False, False
  61. elif pyasn1_version <= '0.3.7':
  62. def encodeValue(self, value, encodeFun, **options):
  63. return value and (255,) or (0,), False, False
  64. else:
  65. def encodeValue(self, value, asn1Spec, encodeFun, **options):
  66. return value and (255,) or (0,), False, False
  67. customTagMap = deepcopy(tagMap)
  68. customTypeMap = deepcopy(typeMap)
  69. customTagMap[Boolean.tagSet] = LDAPBooleanEncoder()
  70. customTypeMap[Boolean.typeId] = LDAPBooleanEncoder()
  71. encode = Encoder(customTagMap, customTypeMap)
  72. # end of monkey patching
  73. # a fast BER decoder for LDAP responses only
  74. def compute_ber_size(data):
  75. """
  76. Compute size according to BER definite length rules
  77. Returns size of value and value offset
  78. """
  79. if data[1] <= 127: # BER definite length - short form. Highest bit of byte 1 is 0, message length is in the last 7 bits - Value can be up to 127 bytes long
  80. return data[1], 2
  81. else: # BER definite length - long form. Highest bit of byte 1 is 1, last 7 bits counts the number of following octets containing the value length
  82. bytes_length = data[1] - 128
  83. value_length = 0
  84. cont = bytes_length
  85. for byte in data[2: 2 + bytes_length]:
  86. cont -= 1
  87. value_length += byte * (256 ** cont)
  88. return value_length, bytes_length + 2
  89. def decode_message_fast(message):
  90. ber_len, ber_value_offset = compute_ber_size(get_bytes(message[:10])) # get start of sequence, at maximum 3 bytes for length
  91. decoded = decode_sequence(message, ber_value_offset, ber_len + ber_value_offset, LDAP_MESSAGE_CONTEXT)
  92. return {
  93. 'messageID': decoded[0][3],
  94. 'protocolOp': decoded[1][2],
  95. 'payload': decoded[1][3],
  96. 'controls': decoded[2][3] if len(decoded) == 3 else None
  97. }
  98. def decode_sequence(message, start, stop, context_decoders=None):
  99. decoded = []
  100. while start < stop:
  101. octet = get_byte(message[start])
  102. ber_class = CLASSES[(bool(octet & 0b10000000), bool(octet & 0b01000000))]
  103. ber_constructed = bool(octet & 0b00100000)
  104. ber_type = octet & 0b00011111
  105. ber_decoder = DECODERS[(ber_class, octet & 0b00011111)] if ber_class < 2 else None
  106. ber_len, ber_value_offset = compute_ber_size(get_bytes(message[start: start + 10]))
  107. start += ber_value_offset
  108. if ber_decoder:
  109. value = ber_decoder(message, start, start + ber_len, context_decoders) # call value decode function
  110. else:
  111. # try:
  112. value = context_decoders[ber_type](message, start, start + ber_len) # call value decode function for context class
  113. # except KeyError:
  114. # if ber_type == 3: # Referral in result
  115. # value = decode_sequence(message, start, start + ber_len)
  116. # else:
  117. # raise # re-raise, should never happen
  118. decoded.append((ber_class, ber_constructed, ber_type, value))
  119. start += ber_len
  120. return decoded
  121. def decode_integer(message, start, stop, context_decoders=None):
  122. first = message[start]
  123. value = -1 if get_byte(first) & 0x80 else 0
  124. for octet in message[start: stop]:
  125. value = value << 8 | get_byte(octet)
  126. return value
  127. def decode_octet_string(message, start, stop, context_decoders=None):
  128. return message[start: stop]
  129. def decode_boolean(message, start, stop, context_decoders=None):
  130. return False if message[start: stop] == 0 else True
  131. def decode_bind_response(message, start, stop, context_decoders=None):
  132. return decode_sequence(message, start, stop, BIND_RESPONSE_CONTEXT)
  133. def decode_extended_response(message, start, stop, context_decoders=None):
  134. return decode_sequence(message, start, stop, EXTENDED_RESPONSE_CONTEXT)
  135. def decode_intermediate_response(message, start, stop, context_decoders=None):
  136. return decode_sequence(message, start, stop, INTERMEDIATE_RESPONSE_CONTEXT)
  137. def decode_controls(message, start, stop, context_decoders=None):
  138. return decode_sequence(message, start, stop, CONTROLS_CONTEXT)
  139. def ldap_result_to_dict_fast(response):
  140. response_dict = dict()
  141. response_dict['result'] = int(response[0][3]) # resultCode
  142. response_dict['description'] = RESULT_CODES[response_dict['result']]
  143. response_dict['dn'] = to_unicode(response[1][3], from_server=True) # matchedDN
  144. response_dict['message'] = to_unicode(response[2][3], from_server=True) # diagnosticMessage
  145. if len(response) == 4:
  146. response_dict['referrals'] = referrals_to_list([to_unicode(referral[3], from_server=True) for referral in response[3][3]]) # referrals
  147. else:
  148. response_dict['referrals'] = None
  149. return response_dict
  150. ######
  151. if str is not bytes: # Python 3
  152. def get_byte(x):
  153. return x
  154. def get_bytes(x):
  155. return x
  156. else: # Python 2
  157. def get_byte(x):
  158. return ord(x)
  159. def get_bytes(x):
  160. return bytearray(x)
  161. DECODERS = {
  162. # Universal
  163. (0, 1): decode_boolean, # Boolean
  164. (0, 2): decode_integer, # Integer
  165. (0, 4): decode_octet_string, # Octet String
  166. (0, 10): decode_integer, # Enumerated
  167. (0, 16): decode_sequence, # Sequence
  168. (0, 17): decode_sequence, # Set
  169. # Application
  170. (1, 1): decode_bind_response, # Bind response
  171. (1, 4): decode_sequence, # Search result entry
  172. (1, 5): decode_sequence, # Search result done
  173. (1, 7): decode_sequence, # Modify response
  174. (1, 9): decode_sequence, # Add response
  175. (1, 11): decode_sequence, # Delete response
  176. (1, 13): decode_sequence, # ModifyDN response
  177. (1, 15): decode_sequence, # Compare response
  178. (1, 19): decode_sequence, # Search result reference
  179. (1, 24): decode_extended_response, # Extended response
  180. (1, 25): decode_intermediate_response, # intermediate response
  181. (2, 3): decode_octet_string #
  182. }
  183. BIND_RESPONSE_CONTEXT = {
  184. 7: decode_octet_string # SaslCredentials
  185. }
  186. EXTENDED_RESPONSE_CONTEXT = {
  187. 10: decode_octet_string, # ResponseName
  188. 11: decode_octet_string # Response Value
  189. }
  190. INTERMEDIATE_RESPONSE_CONTEXT = {
  191. 0: decode_octet_string, # IntermediateResponseName
  192. 1: decode_octet_string # IntermediateResponseValue
  193. }
  194. LDAP_MESSAGE_CONTEXT = {
  195. 0: decode_controls, # Controls
  196. 3: decode_sequence # Referral
  197. }
  198. CONTROLS_CONTEXT = {
  199. 0: decode_sequence # Control
  200. }