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.

ntlmpool.py 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. """
  2. NTLM authenticating pool, contributed by erikcederstran
  3. Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10
  4. """
  5. from __future__ import absolute_import
  6. from logging import getLogger
  7. from ntlm import ntlm
  8. from .. import HTTPSConnectionPool
  9. from ..packages.six.moves.http_client import HTTPSConnection
  10. log = getLogger(__name__)
  11. class NTLMConnectionPool(HTTPSConnectionPool):
  12. """
  13. Implements an NTLM authentication version of an urllib3 connection pool
  14. """
  15. scheme = 'https'
  16. def __init__(self, user, pw, authurl, *args, **kwargs):
  17. """
  18. authurl is a random URL on the server that is protected by NTLM.
  19. user is the Windows user, probably in the DOMAIN\\username format.
  20. pw is the password for the user.
  21. """
  22. super(NTLMConnectionPool, self).__init__(*args, **kwargs)
  23. self.authurl = authurl
  24. self.rawuser = user
  25. user_parts = user.split('\\', 1)
  26. self.domain = user_parts[0].upper()
  27. self.user = user_parts[1]
  28. self.pw = pw
  29. def _new_conn(self):
  30. # Performs the NTLM handshake that secures the connection. The socket
  31. # must be kept open while requests are performed.
  32. self.num_connections += 1
  33. log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s',
  34. self.num_connections, self.host, self.authurl)
  35. headers = {}
  36. headers['Connection'] = 'Keep-Alive'
  37. req_header = 'Authorization'
  38. resp_header = 'www-authenticate'
  39. conn = HTTPSConnection(host=self.host, port=self.port)
  40. # Send negotiation message
  41. headers[req_header] = (
  42. 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser))
  43. log.debug('Request headers: %s', headers)
  44. conn.request('GET', self.authurl, None, headers)
  45. res = conn.getresponse()
  46. reshdr = dict(res.getheaders())
  47. log.debug('Response status: %s %s', res.status, res.reason)
  48. log.debug('Response headers: %s', reshdr)
  49. log.debug('Response data: %s [...]', res.read(100))
  50. # Remove the reference to the socket, so that it can not be closed by
  51. # the response object (we want to keep the socket open)
  52. res.fp = None
  53. # Server should respond with a challenge message
  54. auth_header_values = reshdr[resp_header].split(', ')
  55. auth_header_value = None
  56. for s in auth_header_values:
  57. if s[:5] == 'NTLM ':
  58. auth_header_value = s[5:]
  59. if auth_header_value is None:
  60. raise Exception('Unexpected %s response header: %s' %
  61. (resp_header, reshdr[resp_header]))
  62. # Send authentication message
  63. ServerChallenge, NegotiateFlags = \
  64. ntlm.parse_NTLM_CHALLENGE_MESSAGE(auth_header_value)
  65. auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge,
  66. self.user,
  67. self.domain,
  68. self.pw,
  69. NegotiateFlags)
  70. headers[req_header] = 'NTLM %s' % auth_msg
  71. log.debug('Request headers: %s', headers)
  72. conn.request('GET', self.authurl, None, headers)
  73. res = conn.getresponse()
  74. log.debug('Response status: %s %s', res.status, res.reason)
  75. log.debug('Response headers: %s', dict(res.getheaders()))
  76. log.debug('Response data: %s [...]', res.read()[:100])
  77. if res.status != 200:
  78. if res.status == 401:
  79. raise Exception('Server rejected request: wrong '
  80. 'username or password')
  81. raise Exception('Wrong server response: %s %s' %
  82. (res.status, res.reason))
  83. res.fp = None
  84. log.debug('Connection established')
  85. return conn
  86. def urlopen(self, method, url, body=None, headers=None, retries=3,
  87. redirect=True, assert_same_host=True):
  88. if headers is None:
  89. headers = {}
  90. headers['Connection'] = 'Keep-Alive'
  91. return super(NTLMConnectionPool, self).urlopen(method, url, body,
  92. headers, retries,
  93. redirect,
  94. assert_same_host)