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.

tls_backport.py 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. """
  2. """
  3. # Created on 2014.10.05
  4. #
  5. # Author: Giovanni Cannata
  6. #
  7. # Copyright 2014 - 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 re
  25. from ..utils.log import log, log_enabled, NETWORK
  26. try:
  27. from backports.ssl_match_hostname import match_hostname, CertificateError
  28. except ImportError:
  29. class CertificateError(ValueError): # fix for Python 2, code from Python 3.5 standard library
  30. pass
  31. def _dnsname_match(dn, hostname, max_wildcards=1):
  32. """Backported from Python 3.4.3 standard library
  33. Matching according to RFC 6125, section 6.4.3
  34. http://tools.ietf.org/html/rfc6125#section-6.4.3
  35. """
  36. if log_enabled(NETWORK):
  37. log(NETWORK, "matching dn %s with hostname %s", dn, hostname)
  38. pats = []
  39. if not dn:
  40. return False
  41. pieces = dn.split(r'.')
  42. leftmost = pieces[0]
  43. remainder = pieces[1:]
  44. wildcards = leftmost.count('*')
  45. if wildcards > max_wildcards:
  46. # Issue #17980: avoid denials of service by refusing more
  47. # than one wildcard per fragment. A survey of established
  48. # policy among SSL implementations showed it to be a
  49. # reasonable choice.
  50. raise CertificateError(
  51. "too many wildcards in certificate DNS name: " + repr(dn))
  52. # speed up common case w/o wildcards
  53. if not wildcards:
  54. return dn.lower() == hostname.lower()
  55. # RFC 6125, section 6.4.3, subitem 1.
  56. # The client SHOULD NOT attempt to match a presented identifier in which
  57. # the wildcard character comprises a label other than the left-most label.
  58. if leftmost == '*':
  59. # When '*' is a fragment by itself, it matches a non-empty dotless
  60. # fragment.
  61. pats.append('[^.]+')
  62. elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
  63. # RFC 6125, section 6.4.3, subitem 3.
  64. # The client SHOULD NOT attempt to match a presented identifier
  65. # where the wildcard character is embedded within an A-label or
  66. # U-label of an internationalized domain name.
  67. pats.append(re.escape(leftmost))
  68. else:
  69. # Otherwise, '*' matches any dotless string, e.g. www*
  70. pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
  71. # add the remaining fragments, ignore any wildcards
  72. for frag in remainder:
  73. pats.append(re.escape(frag))
  74. pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
  75. return pat.match(hostname)
  76. def match_hostname(cert, hostname):
  77. """Backported from Python 3.4.3 standard library.
  78. Verify that *cert* (in decoded format as returned by
  79. SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
  80. rules are followed, but IP addresses are not accepted for *hostname*.
  81. CertificateError is raised on failure. On success, the function
  82. returns nothing.
  83. """
  84. if not cert:
  85. raise ValueError("empty or no certificate, match_hostname needs a "
  86. "SSL socket or SSL context with either "
  87. "CERT_OPTIONAL or CERT_REQUIRED")
  88. dnsnames = []
  89. san = cert.get('subjectAltName', ())
  90. for key, value in san:
  91. if key == 'DNS':
  92. if _dnsname_match(value, hostname):
  93. return
  94. dnsnames.append(value)
  95. if not dnsnames:
  96. # The subject is only checked when there is no dNSName entry
  97. # in subjectAltName
  98. for sub in cert.get('subject', ()):
  99. for key, value in sub:
  100. # XXX according to RFC 2818, the most specific Common Name
  101. # must be used.
  102. if key == 'commonName':
  103. if _dnsname_match(value, hostname):
  104. return
  105. dnsnames.append(value)
  106. if len(dnsnames) > 1:
  107. raise CertificateError("hostname %r "
  108. "doesn't match either of %s"
  109. % (hostname, ', '.join(map(repr, dnsnames))))
  110. elif len(dnsnames) == 1:
  111. raise CertificateError("hostname %r "
  112. "doesn't match %r"
  113. % (hostname, dnsnames[0]))
  114. else:
  115. raise CertificateError("no appropriate commonName or "
  116. "subjectAltName fields were found")