123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- """
- Fetches a URL from a web-server supporting NTLM authentication
- eg, IIS.
- If no arguments are specified, a default of http://localhost/localstart.asp
- is used. This script does follow simple 302 redirections, so pointing at the
- root of an IIS server is should work.
- """
- import sys
- import urllib
- import httplib
- import urlparse
- from base64 import encodestring, decodestring
- from sspi import ClientAuth
- import optparse # sorry, this demo needs 2.3+
- options = None # set to optparse options object
- def open_url(host, url):
- h = httplib.HTTPConnection(host)
- # h.set_debuglevel(9)
- h.putrequest('GET', url)
- h.endheaders()
- resp = h.getresponse()
- print "Initial response is", resp.status, resp.reason
- body = resp.read()
- if resp.status == 302: # object moved
- url = "/" + resp.msg["location"]
- resp.close()
- h.putrequest('GET', url)
- h.endheaders()
- resp = h.getresponse()
- print "After redirect response is", resp.status, resp.reason
- if options.show_headers:
- print "Initial response headers:"
- for name, val in resp.msg.items():
- print " %s: %s" % (name, val)
- if options.show_body:
- print body
- if resp.status == 401:
- # 401: Unauthorized - here is where the real work starts
- auth_info = None
- if options.user or options.domain or options.password:
- auth_info = options.user, options.domain, options.password
- ca = ClientAuth("NTLM", auth_info=auth_info)
- auth_scheme = ca.pkg_info['Name']
- data = None
- while 1:
- err, out_buf = ca.authorize(data)
- data = out_buf[0].Buffer
- # Encode it as base64 as required by HTTP
- auth = encodestring(data).replace("\012", "")
- h.putrequest('GET', url)
- h.putheader('Authorization', auth_scheme + ' ' + auth)
- h.putheader('Content-Length', '0')
- h.endheaders()
- resp = h.getresponse()
- if options.show_headers:
- print "Token dance headers:"
- for name, val in resp.msg.items():
- print " %s: %s" % (name, val)
- if err==0:
- break
- else:
- if resp.status != 401:
- print "Eeek - got response", resp.status
- cl = resp.msg.get("content-length")
- if cl:
- print repr(resp.read(int(cl)))
- else:
- print "no content!"
- assert resp.status == 401, resp.status
- assert not resp.will_close, "NTLM is per-connection - must not close"
- schemes = [s.strip() for s in resp.msg.get("WWW-Authenticate", "").split(",")]
- for scheme in schemes:
- if scheme.startswith(auth_scheme):
- data = decodestring(scheme[len(auth_scheme)+1:])
- break
- else:
- print "Could not find scheme '%s' in schemes %r" % (auth_scheme, schemes)
- break
-
- resp.read()
- print "Final response status is", resp.status, resp.reason
- if resp.status == 200:
- # Worked!
- # Check we can read it again without re-authenticating.
- if resp.will_close:
- print "EEEK - response will close, but NTLM is per connection - it must stay open"
- body = resp.read()
- if options.show_body:
- print "Final response body:"
- print body
- h.putrequest('GET', url)
- h.endheaders()
- resp = h.getresponse()
- print "Second fetch response is", resp.status, resp.reason
- if options.show_headers:
- print "Second response headers:"
- for name, val in resp.msg.items():
- print " %s: %s" % (name, val)
-
- resp.read(int(resp.msg.get("content-length", 0)))
- elif resp.status == 500:
- print "Error text"
- print resp.read()
- else:
- if options.show_body:
- cl = resp.msg.get("content-length")
- print resp.read(int(cl))
- if __name__=='__main__':
- parser = optparse.OptionParser(description=__doc__)
-
- parser.add_option("", "--show-body", action="store_true",
- help="print the body of each response as it is received")
- parser.add_option("", "--show-headers", action="store_true",
- help="print the headers of each response as it is received")
- parser.add_option("", "--user", action="store",
- help="The username to login with")
- parser.add_option("", "--password", action="store",
- help="The password to login with")
- parser.add_option("", "--domain", action="store",
- help="The domain to login to")
- options, args = parser.parse_args()
- if not args:
- print "Run with --help for usage details"
- args = ["http://localhost/localstart.asp"]
- for url in args:
- scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
- if (scheme != "http") or params or query or fragment:
- parser.error("Scheme must be http, URL must be simple")
-
- print "Opening '%s' from '%s'" % (path, netloc)
- r = open_url(netloc, path)
|