Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions PyAuthenNTLM2/ntlm_ad_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand All @@ -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)

Expand All @@ -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
Expand All @@ -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
6 changes: 4 additions & 2 deletions PyAuthenNTLM2/ntlm_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down Expand Up @@ -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'])
Expand Down
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``).
Expand Down
11 changes: 7 additions & 4 deletions pyntlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:]
Expand All @@ -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)
Expand All @@ -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))
Expand Down Expand Up @@ -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 ]
Expand Down