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.

sasl.py 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. """
  2. """
  3. # Created on 2013.09.11
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2013 - 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. import stringprep
  25. from unicodedata import ucd_3_2_0 as unicode32
  26. from os import urandom
  27. from binascii import hexlify
  28. from ... import SASL
  29. from ...core.results import RESULT_AUTH_METHOD_NOT_SUPPORTED
  30. from ...core.exceptions import LDAPSASLPrepError, LDAPPasswordIsMandatoryError
  31. def sasl_prep(data):
  32. """
  33. implement SASLPrep profile as per RFC4013:
  34. it defines the "SASLprep" profile of the "stringprep" algorithm [StringPrep].
  35. The profile is designed for use in Simple Authentication and Security
  36. Layer ([SASL]) mechanisms, such as [PLAIN], [CRAM-MD5], and
  37. [DIGEST-MD5]. It may be applicable where simple user names and
  38. passwords are used. This profile is not intended for use in
  39. preparing identity strings that are not simple user names (e.g.,
  40. email addresses, domain names, distinguished names), or where
  41. identity or password strings that are not character data, or require
  42. different handling (e.g., case folding).
  43. """
  44. # mapping
  45. prepared_data = ''
  46. for c in data:
  47. if stringprep.in_table_c12(c):
  48. # non-ASCII space characters [StringPrep, C.1.2] that can be mapped to SPACE (U+0020)
  49. prepared_data += ' '
  50. elif stringprep.in_table_b1(c):
  51. # the "commonly mapped to nothing" characters [StringPrep, B.1] that can be mapped to nothing.
  52. pass
  53. else:
  54. prepared_data += c
  55. # normalizing
  56. # This profile specifies using Unicode normalization form KC
  57. # The repertoire is Unicode 3.2 as per RFC 4013 (2)
  58. prepared_data = unicode32.normalize('NFKC', prepared_data)
  59. if not prepared_data:
  60. raise LDAPSASLPrepError('SASLprep error: unable to normalize string')
  61. # prohibit
  62. for c in prepared_data:
  63. if stringprep.in_table_c12(c):
  64. # Non-ASCII space characters [StringPrep, C.1.2]
  65. raise LDAPSASLPrepError('SASLprep error: non-ASCII space character present')
  66. elif stringprep.in_table_c21(c):
  67. # ASCII control characters [StringPrep, C.2.1]
  68. raise LDAPSASLPrepError('SASLprep error: ASCII control character present')
  69. elif stringprep.in_table_c22(c):
  70. # Non-ASCII control characters [StringPrep, C.2.2]
  71. raise LDAPSASLPrepError('SASLprep error: non-ASCII control character present')
  72. elif stringprep.in_table_c3(c):
  73. # Private Use characters [StringPrep, C.3]
  74. raise LDAPSASLPrepError('SASLprep error: private character present')
  75. elif stringprep.in_table_c4(c):
  76. # Non-character code points [StringPrep, C.4]
  77. raise LDAPSASLPrepError('SASLprep error: non-character code point present')
  78. elif stringprep.in_table_c5(c):
  79. # Surrogate code points [StringPrep, C.5]
  80. raise LDAPSASLPrepError('SASLprep error: surrogate code point present')
  81. elif stringprep.in_table_c6(c):
  82. # Inappropriate for plain text characters [StringPrep, C.6]
  83. raise LDAPSASLPrepError('SASLprep error: inappropriate for plain text character present')
  84. elif stringprep.in_table_c7(c):
  85. # Inappropriate for canonical representation characters [StringPrep, C.7]
  86. raise LDAPSASLPrepError('SASLprep error: inappropriate for canonical representation character present')
  87. elif stringprep.in_table_c8(c):
  88. # Change display properties or deprecated characters [StringPrep, C.8]
  89. raise LDAPSASLPrepError('SASLprep error: change display property or deprecated character present')
  90. elif stringprep.in_table_c9(c):
  91. # Tagging characters [StringPrep, C.9]
  92. raise LDAPSASLPrepError('SASLprep error: tagging character present')
  93. # check bidi
  94. # if a string contains any r_and_al_cat character, the string MUST NOT contain any l_cat character.
  95. flag_r_and_al_cat = False
  96. flag_l_cat = False
  97. for c in prepared_data:
  98. if stringprep.in_table_d1(c):
  99. flag_r_and_al_cat = True
  100. elif stringprep.in_table_d2(c):
  101. flag_l_cat = True
  102. if flag_r_and_al_cat and flag_l_cat:
  103. raise LDAPSASLPrepError('SASLprep error: string cannot contain (R or AL) and L bidirectional chars')
  104. # If a string contains any r_and_al_cat character, a r_and_al_cat character MUST be the first character of the string
  105. # and a r_and_al_cat character MUST be the last character of the string.
  106. if flag_r_and_al_cat and not stringprep.in_table_d1(prepared_data[0]) and not stringprep.in_table_d2(prepared_data[-1]):
  107. raise LDAPSASLPrepError('r_and_al_cat character present, must be first and last character of the string')
  108. return prepared_data
  109. def validate_simple_password(password, accept_empty=False):
  110. """
  111. validate simple password as per RFC4013 using sasl_prep:
  112. """
  113. if accept_empty and not password:
  114. return password
  115. elif not password:
  116. raise LDAPPasswordIsMandatoryError("simple password can't be empty")
  117. if not isinstance(password, bytes): # bytes are returned raw, as per RFC (4.2)
  118. password = sasl_prep(password)
  119. if not isinstance(password, bytes):
  120. password = password.encode('utf-8')
  121. return password
  122. def abort_sasl_negotiation(connection, controls):
  123. from ...operation.bind import bind_operation
  124. request = bind_operation(connection.version, SASL, None, None, '', None)
  125. response = connection.post_send_single_response(connection.send('bindRequest', request, controls))
  126. if connection.strategy.sync:
  127. result = connection.result
  128. else:
  129. result = connection.get_response(response)[0][0]
  130. return True if result['result'] == RESULT_AUTH_METHOD_NOT_SUPPORTED else False
  131. def send_sasl_negotiation(connection, controls, payload):
  132. from ...operation.bind import bind_operation
  133. request = bind_operation(connection.version, SASL, None, None, connection.sasl_mechanism, payload)
  134. response = connection.post_send_single_response(connection.send('bindRequest', request, controls))
  135. if connection.strategy.sync:
  136. result = connection.result
  137. else:
  138. _, result = connection.get_response(response)
  139. return result
  140. def random_hex_string(size):
  141. return str(hexlify(urandom(size)).decode('ascii')) # str fix for Python 2