From c40ddd77a87c3fe2e0fe1669c905128f907a181d Mon Sep 17 00:00:00 2001 From: Tobias Oetiker Date: Mon, 12 Oct 2015 14:25:39 +0200 Subject: [PATCH] * document ProxyMode and VerboseMode options * log to STDERR in VerboseMode so that log messages should end up in stderr * support port option in ntlm_client.py --- PyAuthenNTLM2/ntlm_ad_proxy.py | 20 ++++++++++++++------ PyAuthenNTLM2/ntlm_client.py | 6 ++++-- README.rst | 2 ++ pyntlm.py | 11 +++++++---- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/PyAuthenNTLM2/ntlm_ad_proxy.py b/PyAuthenNTLM2/ntlm_ad_proxy.py index 5738489..21c23ab 100644 --- a/PyAuthenNTLM2/ntlm_ad_proxy.py +++ b/PyAuthenNTLM2/ntlm_ad_proxy.py @@ -19,6 +19,9 @@ # limitations under the License. import socket +import datetime +import sys +from __future__ import print_function from gssapi import * from ntlm_proxy import NTLM_Proxy, NTLM_Proxy_Exception @@ -226,6 +229,11 @@ def __init__(self, ipad, domain, socketFactory=socket, ldapFactory=None, base='' self.debug = verbose #self.smbFactory = smbFactory or (lambda: SMB_Context()) + def log(msg) + if self.debug == False: return + st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') + print("%s %s" % (st,msg), file=sys.stderr) + def check_membership(self, user, groups, base=None, tabs=0, checked=[]): """Check if the given user belong to ANY of the given groups. @@ -237,10 +245,10 @@ def check_membership(self, user, groups, base=None, tabs=0, checked=[]): dn = base or self.base if user: - if self.debug: print '\t'*tabs + "Checking if user %s belongs to group %s (base=%s)" % (user,groups,base) + self.log('\t'*tabs + "Checking if user %s belongs to group %s (base=%s)" % (user,groups,base)) msg = self.proto.make_search_req(dn, { 'sAMAccountName':user }, ['memberOf','sAMAccountName']) else: - if self.debug: print '\t'*tabs + "Checking if group %s is a sub-group of %s" % (groups,base) + self.log('\t'*tabs + "Checking if group %s is a sub-group of %s" % (groups,base)) msg = self.proto.make_search_req(dn, {}, ['memberOf','sAMAccountName']) msg = self._transaction(msg) @@ -255,10 +263,10 @@ def check_membership(self, user, groups, base=None, tabs=0, checked=[]): result[resp[1]] = resp[2] msg = self._transaction('') - checked.append(base); + checked.append(base) if result: assert(len(result)==1) - if self.debug: print '\t'*tabs + "Found entry sAMAccountName:", result.values()[0]['sAMAccountName'] + self.log('\t'*tabs + "Found entry sAMAccountName:", result.values()[0]['sAMAccountName']) for g in groups: if g in result.values()[0]['sAMAccountName']: return True @@ -267,8 +275,8 @@ def check_membership(self, user, groups, base=None, tabs=0, checked=[]): for x in topgroups: if (x not in checked): if self.check_membership(None,groups,x, tabs+1, checked): - if self.debug: print '\t'*tabs + "sAMAccountName:", result.values()[0]['sAMAccountName'],"yield a match." + self.log('\t'*tabs + "sAMAccountName:", result.values()[0]['sAMAccountName'],"yield a match.") return True - if self.debug: print '\t'*tabs + "sAMAccountName:", result.values()[0]['sAMAccountName'],"did not yield any match." + self.log('\t'*tabs + "sAMAccountName:", result.values()[0]['sAMAccountName'],"did not yield any match.") return False diff --git a/PyAuthenNTLM2/ntlm_client.py b/PyAuthenNTLM2/ntlm_client.py index 60d4699..26d83c6 100644 --- a/PyAuthenNTLM2/ntlm_client.py +++ b/PyAuthenNTLM2/ntlm_client.py @@ -250,7 +250,7 @@ def print_help(): print "ntlm_client {-u|--user} usr {-p|--password} pwd {-d|--domain} DOMAIN {-a|--address} address [{-g|--group} name[,name]* [{-m/--member member}]]" print print " When '-a/--address' starts with 'ldap://', it is an URI of an Active Directory server." - print " The URI has format ldap://serveraddres/dn" + print " The URI has format ldap://serveraddres[:port]/dn" print " - serveraddress is the IP or the hostname of the AD server." print " - dn is the base Distinguished name to use for the LDAP search." print " Special characters must be escaped (space=%20, comma=%2C, equals=%3D)" @@ -309,7 +309,9 @@ def print_help(): if config['address'].startswith('ldap:'): print "Using Active Directory (LDAP) to verify credentials." url = urlparse(config['address']) - proxy = NTLM_AD_Proxy(url.netloc, config['domain'], base=urllib.unquote(url.path)[1:], verbose=config['verbose']) + port = 389 + if url.port: port = url.port + proxy = NTLM_AD_Proxy(url.netloc, config['domain'], base=urllib.unquote(url.path)[1:], verbose=config['verbose'], portAD=port) else: print "Using Domain Controller to verify credentials." proxy = NTLM_DC_Proxy(config['address'], config['domain'], verbose=config['verbose']) diff --git a/README.rst b/README.rst index 33ce7f8..222837a 100644 --- a/README.rst +++ b/README.rst @@ -156,6 +156,8 @@ PythonOption BDC *bdc* Replace *bdc* with the address of the Bac PythonOption NameFmt SAM|LogOn Set REMOTE_USER to the user name only (SAM) or to the legacy Logon format (domain\username). This entry is optional. SAM is the default. +PythonOption WebProxyMode ON Work in the context of mod_proxy requests (default is OFF) +PythonOption VerboseMode ON Talk more while working (default is OFF) ===================================== ====== Apache needs to be configured to send keep alives (directive ``KeepAlive On``). diff --git a/pyntlm.py b/pyntlm.py index 2949c8d..88962a2 100644 --- a/pyntlm.py +++ b/pyntlm.py @@ -222,6 +222,7 @@ def connect_to_proxy(req, type1): for server in (pdc, bdc): if not server: continue try: + verbose_mode = req.get_options().get('VerboseMode','off').lower() == 'on' if server.startswith('ldap:'): url = urlparse(server) decoded_path =urllib.unquote(url.path)[1:] @@ -230,11 +231,13 @@ def connect_to_proxy(req, type1): port = 389 req.log_error('PYTNLM: Initiating connection to Active Directory server %s:%s (domain %s) using base DN "%s".' % (url.hostname, port, domain, decoded_path), apache.APLOG_INFO) - proxy = NTLM_AD_Proxy(url.hostname, domain, base=decoded_path, portAD=port) + + + proxy = NTLM_AD_Proxy(url.hostname, domain, base=decoded_path, portAD=port, verbose=verbose_mode) else: req.log_error('PYTNLM: Initiating connection to Domain Controller server %s (domain %s).' % (server, domain), apache.APLOG_INFO) - proxy = NTLM_DC_Proxy(server, domain) + proxy = NTLM_DC_Proxy(server, domain,verbose=verbose_mode) ntlm_challenge = proxy.negotiate(type1) except Exception, e: req.log_error('PYNTLM: Error when retrieving Type 2 message from server(%s) = %s' % (server,str(e)), apache.APLOG_CRIT) @@ -259,7 +262,7 @@ def handle_type1(req, ntlm_message): except Exception, e: return apache.HTTP_INTERNAL_SERVER_ERROR - proxy_mode = req.get_options().get('WebProxyMode','OFF').lower() == 'on'; + proxy_mode = req.get_options().get('WebProxyMode','off').lower() == 'on'; cache.add(req.connection.id, proxy) req.err_headers_out.add('Proxy-Authenticate' if proxy_mode else 'WWW-Authenticate', "NTLM " + base64.b64encode(ntlm_challenge)) @@ -395,7 +398,7 @@ def authenhandler(req): req.connection.id, req.method,req.unparsed_uri,len(cache)), apache.APLOG_INFO) # Extract Authorization header, as a list (if present) - proxy_mode = req.get_options().get('WebProxyMode','OFF').lower() == 'on'; + proxy_mode = req.get_options().get('WebProxyMode','off').lower() == 'on'; auth_headers = req.headers_in.get('Proxy-Authorization' if proxy_mode else 'Authorization', []) if not isinstance(auth_headers, list): auth_headers = [ auth_headers ]