[PATCH] support SSPI for Windows
Chuck.Kirschman at bentley.com
Chuck.Kirschman at bentley.com
Thu Dec 1 22:38:53 UTC 2011
# HG changeset patch
# User Chuck.Kirschman
# Date 1322778699 18000
# Branch stable
# Node ID 6f36523ce8fc00d836770a1a0f481df0ed27983f
# Parent 351a9292e430e35766c552066ed3e87c557b803b
enable sspi for windows
This patch will add SSPI capabilities for both normal http and ui.usehttp2=true
configurations of Mercurial on Windows. The check for the authorization failure
has to happen at the point of opening the connection and resolved at that time.
diff --git a/mercurial/httpconnection.py b/mercurial/httpconnection.py
--- a/mercurial/httpconnection.py
+++ b/mercurial/httpconnection.py
@@ -16,6 +16,7 @@
from mercurial import httpclient
from mercurial import sslutil
from mercurial import util
+from mercurial import ntlm
from mercurial.i18n import _
# moved here from url.py to avoid a cycle
@@ -211,6 +212,13 @@
path = '/' + path
h.request(req.get_method(), path, req.data, headers)
r = h.getresponse()
+ # authorization schemes that require persistent connections
+ # can be attempted here before urllib2 sees them.
+ if r.status == 401 and 'www-authenticate' in r.headers:
+ auth_methods = [s.strip() for s in
+ r.headers['www-authenticate'].split(",")]
+ if "NTLM" in auth_methods:
+ r = ntlm.ntlm_auth_httplib2 (h, req, r)
except socket.error, err: # XXX what error?
raise urllib2.URLError(err)
diff --git a/mercurial/keepalive.py b/mercurial/keepalive.py
--- a/mercurial/keepalive.py
+++ b/mercurial/keepalive.py
@@ -116,6 +116,7 @@
import socket
import thread
import urllib2
+import ntlm
DEBUG = None
@@ -255,6 +256,13 @@
self._cm.add(host, h, 0)
self._start_transaction(h, req)
r = h.getresponse()
+ # authorization schemes that require persistent connections
+ # can be attempted here before urllib2 sees them.
+ if r.status == 401:
+ auth_methods = [s.strip() for s in
+ r.msg.get('WWW-Authenticate').split(",")]
+ if "NTLM" in auth_methods:
+ r = ntlm.ntlm_auth (h, req, r)
except (socket.error, httplib.HTTPException), err:
raise urllib2.URLError(err)
diff --git a/mercurial/ntlm.py b/mercurial/ntlm.py
new file mode 100644
--- /dev/null
+++ b/mercurial/ntlm.py
@@ -0,0 +1,127 @@
+import base64, string, os
+
+class WindowsNtlmMessageGenerator:
+ def __init__(self, user=None):
+ import win32api, sspi
+ if not user:
+ user = win32api.GetUserName()
+ self.sspi_client = sspi.ClientAuth("NTLM", user)
+
+ def create_auth_req(self):
+ import pywintypes
+ output_buffer = None
+ error_msg = None
+
+ try:
+ error_msg, output_buffer = self.sspi_client.authorize(None)
+ except pywintypes.error:
+ return None
+
+ auth_req = output_buffer[0].Buffer
+ auth_req = base64.encodestring(auth_req)
+ auth_req = string.replace(auth_req, '\012', '')
+ return auth_req
+
+ def create_challenge_response(self, challenge):
+ import pywintypes
+ input_buffer = challenge
+ outout_buffer = None
+ error_msg = None
+
+ try:
+ error_msg, output_buffer = self.sspi_client.authorize(input_buffer)
+ except pywintypes.error:
+ return None
+
+ response_msg = output_buffer[0].Buffer
+ response_msg = base64.encodestring(response_msg)
+ response_msg = string.replace(response_msg, '\012', '')
+ return response_msg
+
+def ntlm_auth (host, req, orig_resp):
+ if os.name == 'nt':
+ ntlm_gen = WindowsNtlmMessageGenerator()
+ else:
+ return orig_resp
+
+ # we have to read the original response before we can send a new request
+ orig_resp.read()
+
+ auth_req_msg = ntlm_gen.create_auth_req()
+ if not auth_req_msg: return orig_resp
+ host.putrequest(req.get_method(), req.get_selector())
+ for origHeader in req.headers:
+ host.putheader (origHeader, req.headers[origHeader])
+ host.putheader('Authorization', 'NTLM' + ' ' + auth_req_msg)
+ host.endheaders()
+
+ if req.has_data():
+ host.send (req.get_data())
+
+ resp = host.getresponse()
+ resp.read()
+
+ challenge = resp.msg.get('WWW-Authenticate')
+ if not challenge: return orig_resp
+ challenge = base64.decodestring(challenge.split()[1])
+
+ chal_response_msg = ntlm_gen.create_challenge_response (challenge)
+ if not chal_response_msg: return orig_resp
+ host.putrequest(req.get_method(), req.get_selector())
+ host.putheader('Authorization', 'NTLM' + ' ' + chal_response_msg)
+ for origHeader in req.headers:
+ host.putheader (origHeader, req.headers[origHeader])
+ host.endheaders()
+
+ if req.has_data():
+ host.send (req.get_data())
+
+ resp = host.getresponse()
+
+ # If our NTLM attempt wasn't successful, return the original response
+ if resp.status != 200 and resp.status != 404:
+ return orig_resp
+
+ return resp
+
+
+def ntlm_auth_httplib2 (host, req, orig_resp):
+ # This is different than ntlm_auth because host and response are different classes
+ if os.name == 'nt':
+ ntlm_gen = WindowsNtlmMessageGenerator()
+ else:
+ return orig_resp
+
+ # we have to read the original response before we can send a new request
+ orig_resp.read()
+
+ auth_req_msg = ntlm_gen.create_auth_req()
+ if not auth_req_msg: return orig_resp
+
+ headers = {}
+ for origHeader in req.headers:
+ headers [origHeader] = req.headers[origHeader]
+ headers['Authorization'] = 'NTLM' + ' ' + auth_req_msg
+ host.request(req.get_method(), req.get_selector(), headers=headers)
+ resp = host.getresponse()
+ resp.read()
+
+ challenge = resp.headers['www-authenticate'] if 'www-authenticate' in resp.headers else None
+ if not challenge: return orig_resp
+ challenge = base64.decodestring(challenge.split()[1])
+
+ chal_response_msg = ntlm_gen.create_challenge_response (challenge)
+ if not chal_response_msg: return orig_resp
+
+ headers = {}
+ for origHeader in req.headers:
+ headers [origHeader] = req.headers[origHeader]
+ headers['Authorization'] = 'NTLM' + ' ' + chal_response_msg
+ host.request(req.get_method(), req.get_selector(), headers=headers)
+ resp = host.getresponse()
+
+ # If our NTLM attempt wasn't successful, return the original response
+ if resp.status != 200 and resp.status != 404:
+ return orig_resp
+
+ return resp
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurial-scm.org/pipermail/mercurial-devel/attachments/20111201/5d004313/attachment-0002.html>
More information about the Mercurial-devel
mailing list