<s> import sys <EOL> import base64 <EOL> import time <EOL> import urllib <EOL> from struct import unpack <EOL> from threading import Lock <EOL> from binascii import hexlify <EOL> from urlparse import urlparse <EOL> from mod_python import apache <EOL> from PyAuthenNTLM2 . ntlm_dc_proxy import NTLM_DC_Proxy <EOL> from PyAuthenNTLM2 . ntlm_ad_proxy import NTLM_AD_Proxy <EOL> use_basic_auth = True <EOL> try : <EOL> from PyAuthenNTLM2 . ntlm_client import NTLM_Client <EOL> except ImportError : <EOL> use_basic_auth = False <EOL> class CacheConnections : <EOL> def __init__ ( self ) : <EOL> self . _mutex = Lock ( ) <EOL> self . _cache = { } <EOL> def __len__ ( self ) : <EOL> return len ( self . _cache ) <EOL> def remove ( self , id ) : <EOL> self . _mutex . acquire ( ) <EOL> ( proxy , ts ) = self . _cache . get ( id , ( None , None ) ) <EOL> if proxy : <EOL> proxy . close ( ) <EOL> del self . _cache [ id ] <EOL> self . _mutex . release ( ) <EOL> def add ( self , id , proxy ) : <EOL> self . _mutex . acquire ( ) <EOL> self . _cache [ id ] = ( proxy , int ( time . time ( ) ) ) <EOL> self . _mutex . release ( ) <EOL> def clean ( self ) : <EOL> now = int ( time . time ( ) ) <EOL> self . _mutex . acquire ( ) <EOL> for id , conn in self . _cache . items ( ) : <EOL> if conn [ <NUM_LIT:1> ] + <NUM_LIT> < now : <EOL> conn [ <NUM_LIT:0> ] . close ( ) <EOL> del self . _cache [ id ] <EOL> self . _mutex . release ( ) <EOL> def has_key ( self , id ) : <EOL> return self . _cache . has_key ( id ) <EOL> def get_proxy ( self , id ) : <EOL> self . _mutex . acquire ( ) <EOL> proxy = self . _cache [ id ] [ <NUM_LIT:0> ] <EOL> self . _mutex . release ( ) <EOL> return proxy <EOL> class CacheGroups : <EOL> def __init__ ( self ) : <EOL> self . _mutex = Lock ( ) <EOL> self . _cache = { } <EOL> def __len__ ( self ) : <EOL> return len ( self . _cache ) <EOL> def add ( self , group , user ) : <EOL> self . _mutex . acquire ( ) <EOL> if not self . _cache . has_key ( group ) : <EOL> self . _cache [ group ] = { } <EOL> self . _cache [ group ] [ user ] = int ( time . time ( ) ) <EOL> self . _mutex . release ( ) <EOL> def clean ( self ) : <EOL> now = int ( time . time ( ) ) <EOL> self . _mutex . acquire ( ) <EOL> old = [ ] <EOL> for group , members in self . _cache . items ( ) : <EOL> for user in members : <EOL> if members [ user ] + <NUM_LIT:3> * <NUM_LIT> * <NUM_LIT> < now : <EOL> old . append ( ( group , user ) ) <EOL> for group , user in old : <EOL> del self . _cache [ group ] [ user ] <EOL> self . _mutex . release ( ) <EOL> def has ( self , group , user ) : <EOL> if not self . _cache . has_key ( group ) : <EOL> return False <EOL> return self . _cache [ group ] . has_key ( user ) <EOL> cache = CacheConnections ( ) <EOL> cacheGroups = CacheGroups ( ) <EOL> def ntlm_message_type ( msg ) : <EOL> if not msg . startswith ( '<STR_LIT>' ) or len ( msg ) < <NUM_LIT:12> : <EOL> raise RuntimeError ( "<STR_LIT>" % hexlify ( msg ) ) <EOL> msg_type = unpack ( '<STR_LIT>' , msg [ <NUM_LIT:8> : <NUM_LIT:8> + <NUM_LIT:4> ] ) [ <NUM_LIT:0> ] <EOL> if msg_type not in ( <NUM_LIT:1> , <NUM_LIT:2> , <NUM_LIT:3> ) : <EOL> raise RuntimeError ( "<STR_LIT>" % msg_type ) <EOL> return msg_type <EOL> def parse_ntlm_authenticate ( msg ) : <EOL> '''<STR_LIT>''' <EOL> NTLMSSP_NEGOTIATE_UNICODE = <NUM_LIT> <EOL> idx = <NUM_LIT> <EOL> length , offset = unpack ( '<STR_LIT>' , msg [ idx : idx + <NUM_LIT:8> ] ) <EOL> domain = msg [ offset : offset + length ] <EOL> idx += <NUM_LIT:8> <EOL> length , offset = unpack ( '<STR_LIT>' , msg [ idx : idx + <NUM_LIT:8> ] ) <EOL> username = msg [ offset : offset + length ] <EOL> idx += <NUM_LIT> <EOL> flags = unpack ( '<STR_LIT>' , msg [ idx : idx + <NUM_LIT:4> ] ) [ <NUM_LIT:0> ] <EOL> if flags & NTLMSSP_NEGOTIATE_UNICODE : <EOL> domain = str ( domain . decode ( '<STR_LIT>' ) ) <EOL> username = str ( username . decode ( '<STR_LIT>' ) ) <EOL> return username , domain <EOL> def set_remote_user ( req , username , domain ) : <EOL> format = req . get_options ( ) . get ( '<STR_LIT>' , '<STR_LIT>' ) . lower ( ) <EOL> if format == '<STR_LIT>' : <EOL> req . user = domain + '<STR_LIT:\\>' + username <EOL> else : <EOL> req . user = username <EOL> def decode_http_authorization_header ( auth ) : <EOL> '''<STR_LIT>''' <EOL> ah = auth . split ( '<STR_LIT:U+0020>' ) <EOL> if len ( ah ) == <NUM_LIT:2> : <EOL> b64 = base64 . b64decode ( ah [ <NUM_LIT:1> ] ) <EOL> if ah [ <NUM_LIT:0> ] == '<STR_LIT>' : <EOL> return ( '<STR_LIT>' , b64 ) <EOL> elif ah [ <NUM_LIT:0> ] == '<STR_LIT>' and use_basic_auth : <EOL> ( user , password ) = b64 . split ( '<STR_LIT::>' ) <EOL> return ( '<STR_LIT>' , user , password ) <EOL> return False <EOL> def handle_unauthorized ( req ) : <EOL> '''<STR_LIT>''' <EOL> req . err_headers_out . add ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> if use_basic_auth : <EOL> req . err_headers_out . add ( '<STR_LIT>' , '<STR_LIT>' % req . auth_name ( ) ) <EOL> req . err_headers_out . add ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> return apache . HTTP_UNAUTHORIZED <EOL> def connect_to_proxy ( req , type1 ) : <EOL> '''<STR_LIT>''' <EOL> try : <EOL> domain = req . get_options ( ) [ '<STR_LIT>' ] <EOL> pdc = req . get_options ( ) [ '<STR_LIT>' ] <EOL> bdc = req . get_options ( ) . get ( '<STR_LIT>' , False ) <EOL> except KeyError , e : <EOL> req . log_error ( '<STR_LIT>' % str ( e ) , apache . APLOG_CRIT ) <EOL> raise <EOL> ntlm_challenge = None <EOL> for server in ( pdc , bdc ) : <EOL> if not server : continue <EOL> try : <EOL> if server . startswith ( '<STR_LIT>' ) : <EOL> url = urlparse ( server ) <EOL> decoded_path = urllib . unquote ( url . path ) [ <NUM_LIT:1> : ] <EOL> req . log_error ( '<STR_LIT>' % <EOL> ( url . netloc , domain , decoded_path ) , apache . APLOG_INFO ) <EOL> proxy = NTLM_AD_Proxy ( url . netloc , domain , base = decoded_path ) <EOL> else : <EOL> req . log_error ( '<STR_LIT>' % <EOL> ( server , domain ) , apache . APLOG_INFO ) <EOL> proxy = NTLM_DC_Proxy ( server , domain ) <EOL> ntlm_challenge = proxy . negotiate ( type1 ) <EOL> except Exception , e : <EOL> req . log_error ( '<STR_LIT>' % ( server , str ( e ) ) , apache . APLOG_CRIT ) <EOL> if ntlm_challenge : break <EOL> proxy . close ( ) <EOL> else : <EOL> raise RuntimeError ( "<STR_LIT>" ) <EOL> return ( proxy , ntlm_challenge ) <EOL> def handle_type1 ( req , ntlm_message ) : <EOL> '''<STR_LIT>''' <EOL> cache . remove ( req . connection . id ) <EOL> cache . clean ( ) <EOL> try : <EOL> ( proxy , ntlm_challenge ) = connect_to_proxy ( req , ntlm_message ) <EOL> except Exception , e : <EOL> return apache . HTTP_INTERNAL_SERVER_ERROR <EOL> cache . add ( req . connection . id , proxy ) <EOL> req . err_headers_out . add ( '<STR_LIT>' , "<STR_LIT>" + base64 . b64encode ( ntlm_challenge ) ) <EOL> return apache . HTTP_UNAUTHORIZED <EOL> def check_authorization ( req , username , proxy ) : <EOL> '''<STR_LIT>''' <EOL> rules = '<STR_LIT>' . join ( req . requires ( ) ) . strip ( ) <EOL> if rules == '<STR_LIT>' or cacheGroups . has ( rules , username ) : <EOL> return True <EOL> groups = [ ] <EOL> for r in req . requires ( ) : <EOL> if r . lower ( ) . startswith ( "<STR_LIT>" ) : <EOL> users = [ u . strip ( ) for u in r [ <NUM_LIT:5> : ] . split ( "<STR_LIT:U+002C>" ) ] <EOL> if username in users : <EOL> req . log_error ( '<STR_LIT>' % <EOL> ( username , req . unparsed_uri ) , apache . APLOG_INFO ) <EOL> return True <EOL> if r . lower ( ) . startswith ( "<STR_LIT>" ) : <EOL> groups += [ g . strip ( ) for g in r [ <NUM_LIT:6> : ] . split ( "<STR_LIT:U+002C>" ) ] <EOL> if groups : <EOL> try : <EOL> res = proxy . check_membership ( username , groups ) <EOL> except Exception , e : <EOL> req . log_error ( '<STR_LIT>' % ( username , str ( groups ) , req . unparsed_uri , str ( e ) ) ) <EOL> if res : <EOL> cacheGroups . add ( rules , username ) <EOL> req . log_error ( '<STR_LIT>' % <EOL> ( username , str ( groups ) , req . unparsed_uri ) , apache . APLOG_INFO ) <EOL> return True <EOL> req . log_error ( '<STR_LIT>' % <EOL> ( username , str ( groups ) , req . unparsed_uri ) ) <EOL> else : <EOL> req . log_error ( '<STR_LIT>' % <EOL> ( username , req . unparsed_uri ) ) <EOL> return False <EOL> def handle_type3 ( req , ntlm_message ) : <EOL> '''<STR_LIT>''' <EOL> proxy = cache . get_proxy ( req . connection . id ) <EOL> try : <EOL> user , domain = parse_ntlm_authenticate ( ntlm_message ) <EOL> if not domain : <EOL> domain = req . get_options ( ) . get ( '<STR_LIT>' , req . auth_name ( ) ) <EOL> result = proxy . authenticate ( ntlm_message ) <EOL> except Exception , e : <EOL> req . log_error ( '<STR_LIT>' % str ( e ) , apache . APLOG_CRIT ) <EOL> user , domain = '<STR_LIT>' , '<STR_LIT>' <EOL> result = False <EOL> if not result : <EOL> cache . remove ( req . connection . id ) <EOL> req . log_error ( '<STR_LIT>' % ( <EOL> domain , user , req . unparsed_uri ) ) <EOL> return handle_unauthorized ( req ) <EOL> req . log_error ( '<STR_LIT>' % ( user , domain , req . unparsed_uri ) , apache . APLOG_NOTICE ) <EOL> set_remote_user ( req , user , domain ) <EOL> result = check_authorization ( req , user , proxy ) <EOL> cache . remove ( req . connection . id ) <EOL> if not result : <EOL> return apache . HTTP_FORBIDDEN <EOL> req . connection . notes . add ( '<STR_LIT>' , req . user ) <EOL> return apache . OK <EOL> def handle_basic ( req , user , password ) : <EOL> '''<STR_LIT>''' <EOL> req . log_error ( '<STR_LIT>' % ( req . unparsed_uri ) ) <EOL> domain = req . get_options ( ) . get ( '<STR_LIT>' , req . auth_name ( ) ) <EOL> client = NTLM_Client ( user , domain , password ) <EOL> type1 = client . make_ntlm_negotiate ( ) <EOL> try : <EOL> ( proxy , type2 ) = connect_to_proxy ( req , type1 ) <EOL> except Exception , e : <EOL> return apache . HTTP_INTERNAL_SERVER_ERROR <EOL> client . parse_ntlm_challenge ( type2 ) <EOL> type3 = client . make_ntlm_authenticate ( ) <EOL> if not proxy . authenticate ( type3 ) : <EOL> proxy . close ( ) <EOL> req . log_error ( '<STR_LIT>' % ( <EOL> user , domain , req . unparsed_uri ) ) <EOL> return handle_unauthorized ( req ) <EOL> req . log_error ( '<STR_LIT>' % ( user , domain , req . unparsed_uri ) , apache . APLOG_NOTICE ) <EOL> set_remote_user ( req , user , domain ) <EOL> result = check_authorization ( req , user , proxy ) <EOL> proxy . close ( ) <EOL> if not result : <EOL> return apache . HTTP_FORBIDDEN <EOL> req . connection . notes . add ( '<STR_LIT>' , user + password ) <EOL> return apache . OK <EOL> def authenhandler ( req ) : <EOL> '''<STR_LIT>''' <EOL> req . log_error ( "<STR_LIT>" % ( <EOL> req . connection . id , req . method , req . unparsed_uri , len ( cache ) ) , apache . APLOG_INFO ) <EOL> auth_headers = req . headers_in . get ( '<STR_LIT>' , [ ] ) <EOL> if not isinstance ( auth_headers , list ) : <EOL> auth_headers = [ auth_headers ] <EOL> user = req . connection . notes . get ( '<STR_LIT>' , None ) <EOL> if user : <EOL> req . user = user <EOL> if auth_headers : <EOL> req . log_error ( '<STR_LIT>' % ( <EOL> req . connection . id , req . method , req . clength , auth_headers ) , apache . APLOG_INFO ) <EOL> if req . method != '<STR_LIT:POST>' or req . clength > <NUM_LIT:0> : <EOL> return apache . OK <EOL> else : <EOL> return apache . OK <EOL> if not auth_headers : <EOL> return handle_unauthorized ( req ) <EOL> try : <EOL> for ah in auth_headers : <EOL> ah_data = decode_http_authorization_header ( ah ) <EOL> if ah_data : <EOL> break <EOL> except : <EOL> ah_data = False <EOL> if not ah_data : <EOL> req . log_error ( '<STR_LIT>' % req . unparsed_uri , apache . APLOG_ERR ) <EOL> return apache . HTTP_BAD_REQUEST <EOL> if ah_data [ <NUM_LIT:0> ] == '<STR_LIT>' : <EOL> userpwd = req . connection . notes . get ( '<STR_LIT>' , None ) <EOL> if userpwd : <EOL> if userpwd != ah_data [ <NUM_LIT:1> ] + ah_data [ <NUM_LIT:2> ] : <EOL> return handle_unauthorized ( req ) <EOL> domain = req . get_options ( ) . get ( '<STR_LIT>' , req . auth_name ( ) ) <EOL> set_remote_user ( req , ah_data [ <NUM_LIT:1> ] , domain ) <EOL> return apache . OK <EOL> return handle_basic ( req , ah_data [ <NUM_LIT:1> ] , ah_data [ <NUM_LIT:2> ] ) <EOL> try : <EOL> ntlm_version = ntlm_message_type ( ah_data [ <NUM_LIT:1> ] ) <EOL> if ntlm_version == <NUM_LIT:1> : <EOL> return handle_type1 ( req , ah_data [ <NUM_LIT:1> ] ) <EOL> if ntlm_version == <NUM_LIT:3> : <EOL> if cache . has_key ( req . connection . id ) : <EOL> return handle_type3 ( req , ah_data [ <NUM_LIT:1> ] ) <EOL> req . log_error ( '<STR_LIT>' % <EOL> ( req . unparsed_uri ) , apache . APLOG_INFO ) <EOL> return handle_unauthorized ( req ) <EOL> error = '<STR_LIT>' <EOL> except Exception , e : <EOL> error = str ( e ) <EOL> req . log_error ( '<STR_LIT>' % <EOL> ( req . unparsed_uri , error ) , apache . APLOG_ERR ) <EOL> return apache . HTTP_BAD_REQUEST </s>
