def run(self): ''' Main authenticator controller loop. ''' while True: try: ifaces = getInterfaces() if __DEBUG__: logDebug( 'Checking available wireless interfaces: {0}'.format( str(ifaces))) # We don't need to do anything if there is no wireless interface available. if ifaces == []: raise _WaitForNextEvent() # Do we already have internet access? if __DEBUG__: logDebug( 'About to ping {0} in order to check for internet access.' .format(quote(self.__configDaemon.pingSite))) redirectURL = hlm_http.detectRedirect( self.__configDaemon.pingSite) if redirectURL == None: # Update status self.status['connected'] = True if not self.status['wasOurJob']: self.status['message'] = _( 'Connected to the internet (not thanks to HLM though).' ) if __DEBUG__: logDebug( 'Ping URL {0} was not redirected. We have internet access.' .format(quote(self.__configDaemon.pingSite))) raise _WaitForNextEvent() # We don't know yet if we actually need to authenticate (it could be a standard website redirection) self.status['connected'] = None self.status['wasOurJob'] = False self.status['message'] = _( 'A redirection was detected, you may be behind a captive portal. Trying to authenticate...' ) if __DEBUG__: logDebug( 'Ping URL {0} was redirected to {1}. Trying to find a plugin that accepts the redirected URL...' .format(quote(self.__configDaemon.pingSite), quote(redirectURL))) # Get currently configured SSIDs so the plugins can rely on it too connectedSSIDs = [getSSID(iface) for iface in ifaces] if __DEBUG__: logDebug('Available SSIDs: {0}'.format(connectedSSIDs)) # Try each relevant authentication plugin in turn for plugin in self.__relevantPlugins: # Verify the redirected URL isSupported = False supportedRedirects = plugin.getSupportedRedirectPrefixes() for redirectPrefix in supportedRedirects: if redirectURL.startswith(redirectPrefix): isSupported = True break if not isSupported: continue # Verify the connected SSIDs isSupported = False supportedSSIDs = plugin.supportedSSIDs for ssid in connectedSSIDs: if ssid in supportedSSIDs: isSupported = True break if not isSupported: continue # The plugin matches both the redirectURL and the connected SSIDs, let's try to authenticate! if __DEBUG__: logDebug( 'AuthPlugin {0} could match, trying to authenticate...' .format(quote(plugin.pluginName))) try: plugin.authenticate(redirectURL, connectedSSIDs) except SystemExit: raise except hlm_auth_plugins.Status_Success as exc: self.status['connected'] = True self.status['wasOurJob'] = True self.status['message'] = exc.message self.dispatcher.notify('[ok] ' + exc.message) if __INFO__: logInfo(exc.message) raise _WaitForNextEvent() except hlm_auth_plugins.Status_WrongCredentials as exc: self.status['message'] = exc.message self.dispatcher.notify('[warning] ' + exc.message) if __WARNING__: logWarning(exc.message) except hlm_auth_plugins.Status_Error as exc: if __DEBUG__: logDebug(exc.message) except hlm_http.CertificateError as exc: exc_message = _( 'The hotspot\'s SSL certificate is invalid!\nNo credentials were sent, it may be a phishing hotspot.' ) self.dispatcher.notify('[error] ' + exc_message) logError(exc_message + ' ' + str(exc)) except BaseException as exc: raise Exception( 'AuthPlugin {0}: [UNEXPECTED FAILURE] {1}'.format( quote(plugin.pluginName), exc)) self.status['connected'] = None self.status['wasOurJob'] = False self.status['message'] = _( 'Unknown connection status (a redirect was detected, but no authentication plugin managed to log you in).' ) except _WaitForNextEvent: pass except SystemExit: pass except BaseException as exc: if __DEBUG__: logDebug('Authenticator.run(): {0}'.format(exc)) # Wait for the next event if __DEBUG__: logDebug('Going to sleep for {0} seconds.'.format( hlm_config.antiDosPingInterval)) time.sleep(hlm_config.antiDosPingInterval) if __DEBUG__: logDebug('Waiting for the next event.') self.__antiDosWaiting = False self.wakeUp.wait(self.__sleepInterval) self.wakeUp.clear() self.__antiDosWaiting = True if __DEBUG__: logDebug('Authenticator thread woke up.')
def authenticate(redirectURL, connectedSSIDs): debugMessageHeader = 'AuthPlugin {0}'.format(quote(pluginName)) def debugMessage(message, *args): if args != tuple(): message = message.format(*args) message = '{0}: {1}'.format(debugMessageHeader, message) return message def reportFailure(message, *args): message = debugMessage('[FAILURE] ' + message, *args) raise hlm_auth_plugins.Status_Error(pluginName, message) match = _regexDomainRedirect.search(redirectURL) if match == None: reportFailure('hotspot domain name is missing.') domainRedirect = match.group(1) if __DEBUG__: logDebug(debugMessage('domainRedirect is {0}'.format(domainRedirect))) regexChilliURL = re.compile('SFRLoginURL_JIL=(https://{0}/indexEncryptingChilli.php?[^>]+)-->'.format(re.escape(domainRedirect))) # Extract the URL arguments try: urlArgs = hlm_http.splitUrlArguments(redirectURL, ['challenge', 'mode', 'uamip', 'uamport', 'channel'], 'redirect URL') if __DEBUG__: logDebug(debugMessage('got all required arguments from the redirect URL.')) except BaseException as exc: reportFailure(exc) # Get the login page try: result = hlm_http.urlOpener().open(redirectURL, timeout = 10) pageData = hlm_http.readAll(result) if not (type(pageData) is str): pageData = str(pageData, 'utf-8') result.close() if __DEBUG__: logDebug(debugMessage('grabbed the login webpage.')) except hlm_http.CertificateError as exc: raise except BaseException as exc: reportFailure('error while grabbing the login webpage: {0}'.format(exc)) # Basic check to see if we actually are on a SFR "NeufBox" if _regexCheckNB4.search(pageData) == None: reportFailure('this is not a "NeufBox4".') if __DEBUG__: logDebug(debugMessage('seems we have a "NeufBox".')) # Is the hotspot FON-enabled? hasFONsupport = (_regexCheckChoiceFON.search(pageData) != None) if __DEBUG__: if hasFONsupport: logDebug(debugMessage('we have FON support.')) else: logDebug(debugMessage('we don\'t have FON support.')) # Double-check the Chillispot URL match = regexChilliURL.search(pageData) if match == None: reportFailure('in-page data is missing.') if match.group(1) != redirectURL: reportFailure('in-page data conflicts with the redirected URL.') if __DEBUG__: logDebug(debugMessage('in-page data confirms the redirect URL.')) # Prepare data that is dependent on the kind of hotspot / configured credentials if 'sfr.fr' in pluginCredentials: hotspotCredentials = 'sfr.fr' hotspotAccessType = 'neuf' if hasFONsupport: hotspotChoice = 'choix=neuf&' else: hotspotChoice = '' elif hasFONsupport and ('fon' in pluginCredentials): hotspotCredentials = 'fon' hotspotAccessType = 'fon' hotspotChoice = 'choix=fon&' else: reportFailure('this plugin only supports «sfr.fr» and «fon» credentials.') if __DEBUG__: logDebug(debugMessage('using {0} credentials'.format(quote(hotspotCredentials)))) debugMessageHeader = 'AuthPlugin {0} (credentials {1})'.format(quote(pluginName), quote(hotspotCredentials)) (user, password) = pluginCredentials[hotspotCredentials] postData = hotspotChoice + 'username={0}&password={1}&conditions=on&challenge={2}&accessType={7}&lang=fr&mode={3}&userurl=http%253a%252f%252fwww.google.com%252f&uamip={4}&uamport={5}&channel={6}&connexion=Connexion'.format(urllib.parse.quote(user), urllib.parse.quote(password), urlArgs['challenge'], urlArgs['mode'], urlArgs['uamip'], urlArgs['uamport'], urlArgs['channel'], hotspotAccessType) # Ask the hotspot gateway to give us the Chillispot URL try: # FIXME Python 3.2 (postData) result = hlm_http.urlOpener().open('https://{0}/nb4_crypt.php'.format(domainRedirect), data = postData, timeout = 10) pageData = hlm_http.readAll(result) if not (type(pageData) is str): pageData = str(pageData, 'utf-8') result.close() if __DEBUG__: logDebug(debugMessage('grabbed the encryption gateway (JS redirect) result webpage.')) except hlm_http.CertificateError as exc: raise except BaseException as exc: reportFailure('error while grabbing the encryption gateway (JS redirect) result webpage: {0}'.format(exc)) # OK, now we have to put up with a Javascript redirect. I mean, WTF? match = _regexJSRedirect.search(pageData) if match == None: reportFailure('missing URL in the encryption gateway (JS redirect) result webpage.') redirectURL = match.group(1) # Let's see what Chillispot will answer us... redirectURL = hlm_http.detectRedirect(redirectURL) if redirectURL == None: reportFailure('something went wrong during the Chillispot query (redirect expected, but none obtained).') # Check the final URL arguments try: urlArgs = hlm_http.splitUrlArguments(redirectURL, ['res'], 'redirect URL') urlArgs = urlArgs['res'].lower() except BaseException as exc: reportFailure(exc) if urlArgs == 'failed': raise hlm_auth_plugins.Status_WrongCredentials(pluginName, hotspotCredentials) if (urlArgs != 'success') and (urlArgs != 'already'): reportFailure('Chillispot didn\'t let us log in, no idea why. Here\'s the redirected URL: {0}'.format(redirectURL)) raise hlm_auth_plugins.Status_Success(pluginName, hotspotCredentials)
def run(self): ''' Main authenticator controller loop. ''' while True: try: ifaces = getInterfaces() if __DEBUG__: logDebug('Checking available wireless interfaces: {0}'.format(str(ifaces))) # We don't need to do anything if there is no wireless interface available. if ifaces == []: raise _WaitForNextEvent() # Do we already have internet access? if __DEBUG__: logDebug('About to ping {0} in order to check for internet access.'.format(quote(self.__configDaemon.pingSite))) redirectURL = hlm_http.detectRedirect(self.__configDaemon.pingSite) if redirectURL == None: # Update status self.status['connected'] = True if not self.status['wasOurJob']: self.status['message'] = _('Connected to the internet (not thanks to HLM though).') if __DEBUG__: logDebug('Ping URL {0} was not redirected. We have internet access.'.format(quote(self.__configDaemon.pingSite))) raise _WaitForNextEvent() # We don't know yet if we actually need to authenticate (it could be a standard website redirection) self.status['connected'] = None self.status['wasOurJob'] = False self.status['message'] = _('A redirection was detected, you may be behind a captive portal. Trying to authenticate...') if __DEBUG__: logDebug('Ping URL {0} was redirected to {1}. Trying to find a plugin that accepts the redirected URL...'.format(quote(self.__configDaemon.pingSite), quote(redirectURL))) # Get currently configured SSIDs so the plugins can rely on it too connectedSSIDs = [getSSID(iface) for iface in ifaces] if __DEBUG__: logDebug('Available SSIDs: {0}'.format(connectedSSIDs)) # Try each relevant authentication plugin in turn for plugin in self.__relevantPlugins: # Verify the redirected URL isSupported = False supportedRedirects = plugin.getSupportedRedirectPrefixes() for redirectPrefix in supportedRedirects: if redirectURL.startswith(redirectPrefix): isSupported = True break if not isSupported: continue # Verify the connected SSIDs isSupported = False supportedSSIDs = plugin.supportedSSIDs for ssid in connectedSSIDs: if ssid in supportedSSIDs: isSupported = True break if not isSupported: continue # The plugin matches both the redirectURL and the connected SSIDs, let's try to authenticate! if __DEBUG__: logDebug('AuthPlugin {0} could match, trying to authenticate...'.format(quote(plugin.pluginName))) try: plugin.authenticate(redirectURL, connectedSSIDs) except SystemExit: raise except hlm_auth_plugins.Status_Success as exc: self.status['connected'] = True self.status['wasOurJob'] = True self.status['message'] = exc.message self.dispatcher.notify('[ok] ' + exc.message) if __INFO__: logInfo(exc.message) raise _WaitForNextEvent() except hlm_auth_plugins.Status_WrongCredentials as exc: self.status['message'] = exc.message self.dispatcher.notify('[warning] ' + exc.message) if __WARNING__: logWarning(exc.message) except hlm_auth_plugins.Status_Error as exc: if __DEBUG__: logDebug(exc.message) except hlm_http.CertificateError as exc: exc_message = _('The hotspot\'s SSL certificate is invalid!\nNo credentials were sent, it may be a phishing hotspot.') self.dispatcher.notify('[error] ' + exc_message) logError(exc_message + ' ' + str(exc)) except BaseException as exc: raise Exception('AuthPlugin {0}: [UNEXPECTED FAILURE] {1}'.format(quote(plugin.pluginName), exc)) self.status['connected'] = None self.status['wasOurJob'] = False self.status['message'] = _('Unknown connection status (a redirect was detected, but no authentication plugin managed to log you in).') except _WaitForNextEvent: pass except SystemExit: pass except BaseException as exc: if __DEBUG__: logDebug('Authenticator.run(): {0}'.format(exc)) # Wait for the next event if __DEBUG__: logDebug('Going to sleep for {0} seconds.'.format(hlm_config.antiDosPingInterval)) time.sleep(hlm_config.antiDosPingInterval) if __DEBUG__: logDebug('Waiting for the next event.') self.__antiDosWaiting = False self.wakeUp.wait(self.__sleepInterval) self.wakeUp.clear() self.__antiDosWaiting = True if __DEBUG__: logDebug('Authenticator thread woke up.')