def is_valid_ip(ip): """Checks the ip to see if it's valid in any of the 4 formats we allow Args: ip (str): Formats are single, range, cidr, and IP/netmask Returns: bool: True/False """ if ip is None: log.msg("Invalid ip. Ip was None") return False elif "-" in ip: try: ip_range(ip) except Exception as e: log.msg("Invalid ip range: {0}. Exception: {1}".format(ip, e)) return False elif "/" in ip: try: ip_cidr(ip) except Exception as e: log.msg("Invalid cidr ip: {0}. Exception: {1}".format(ip, e)) return False else: if not is_valid_single_ip(ip): log.msg("Invalid single ip: {0}.".format(ip)) return False log.msg("Valid ip check passed for: {0}.".format(ip)) return True
def __init__(self, config): super(Module, self).__init__() log.msg("CloudSSO Connector Module") log.config( config, (lambda k: k in ( "service_account_password", "service_account_password_protected", "encryption_skey", "encryption_skey_protected", "signing_skey", "signing_skey_protected", )), ) self.debug = config.get_bool("debug", False) self.duo_creds = DuoCreds( identity=config[const.DRPC_PROXY_KEY_IDENTIFIER], secret=config[const.DRPC_SIGNING_SKEY_IDENTIFIER].encode(), ) self.host = config[const.DRPC_API_HOST_IDENTIFIER] self.encryption_skey = config[const.DRPC_ENCRYPTION_SKEY_IDENTIFIER] self.duo_client = self.make_duo_client( duo_creds=self.duo_creds, host=self.host, client_type=duo_async.CloudSSODuoClient, ) self.identities[self.duo_creds.get_identity()] = self.duo_creds self.drpc_path = "/drpc/v1/join"
def __init__(self, config, _primary_ator=None): super(Module, self).__init__() log.msg("Cloud Connector Module Configuration:") log.config( config, ( lambda k: k in ( "skey", "skey_protected", "service_account_password", "service_account_password_protected", ) ), ) self.debug = config.get_bool("debug", False) self.duo_creds = DuoCreds( config.get("ikey"), config.get_protected_str("skey_protected", "skey").encode(), ) host = config.get_str("api_host", "api.duosecurity.com") port = config.get_int("api_port", 443) self.duo_client = self.make_duo_client(self.duo_creds, host, port=port) self.identities[self.duo_creds.get_identity()] = self.duo_creds self.drpc_path = "/auth/v2/proxy_join"
def authenticate(self, username, password, client_ip, pass_through_attrs=None): if pass_through_attrs is None: pass_through_attrs = {} id = yield self.id_list.request() request_packet = packet.AuthPacket(code=packet.AccessRequest, id=id, secret=self.secret.encode(), dict=base.radius_dictionary()) request_packet['NAS-IP-Address'] = self.nas_ip add_packet_attributes(packet=request_packet, attrs=pass_through_attrs) request = ClientRequest(request_packet, self.pw_codec) request.username = username if password is not None: request.password = password if client_ip: request.client_ip = client_ip log.msg('Sending request for user %r to %r with id %r' % (username, self.addrs[0], id)) # Send request; wait for response self.requests[id] = request self._send_request(request) response_packet = yield request.deferred defer.returnValue(response_packet)
def __init__(self, config, primary_ator, server_section_name): log.msg( 'RADIUS PEAP/EAP-GTC Automatic Factor Server Module Configuration:' ) log.config( config, lambda x: x.startswith('radius_secret') or x in ('skey', 'skey_protected')) self.protocol = DuoEAPRadiusServer( secrets=parse_radius_secrets(config), primary_ator=primary_ator, duo_client=self.make_duo_client(config), failmode=config.get_enum('failmode', duo_async.FAILMODES, duo_async.FAILMODE_SAFE, transform=str.lower), factors=util.parse_factor_list(config.get_str('factors', 'auto')), delim=config.get_str('delimiter', ','), delimited_password_length=config.get_int( 'delimited_password_length', 0), allow_concat=config.get_bool('allow_concat', False), debug=config.get_bool('debug', False), pass_through_attr_names=config.get_str('pass_through_attr_names', ''), prompt=config.get_str('prompt', 'Enter your password: '******'pkey'), cert_file=config.get_str('certs'), cipher_list=config.get_str('cipher_list', ''), minimum_tls_version=config.get_str('minimum_tls_version', ''), pw_codec=config.get_str('pw_codec', 'utf-8'), client_ip_attr=parse_client_ip_attribute(config), server_section_name=server_section_name, pass_through_all=config.get_bool('pass_through_all', False), )
def do_ldap_authentication( self, servers, username, password, rikey, base_dns=None, ntlm_domain=None, ntlm_workstation=None, auth_type=const.AD_AUTH_TYPE_NTLM_V2, transport_type=const.AD_TRANSPORT_STARTTLS, ssl_verify_depth=const.DEFAULT_SSL_VERIFY_DEPTH, ssl_verify_hostname=True, ssl_ca_certs=None, timeout=60, username_attributes=None, call_id=None, ): creds = self.get_creds_for_ldap_idp(rikey, auth_type) service_account_username = creds.username service_account_password = creds.password self._verify_ldap_config_args( service_account_username, service_account_password, auth_type, transport_type, ) # Decrypt the end user password using our symmetric key try: password = self.decrypt_password(password) except InvalidToken as e: msg = "Unable to decrypt the password for user: {}. Please check that your CloudSSO enrollment code is correct. Failing the authentication".format( username) log.msg(msg) raise drpc.CallError(ERR_LDAP_PW_DECRYPT_FAILED, { "error": str(e), }) res = yield self.perform_authentication( servers, username, password, service_account_username, service_account_password, base_dns=base_dns, ntlm_domain=ntlm_domain, ntlm_workstation=ntlm_workstation, auth_type=auth_type, transport_type=transport_type, ssl_verify_depth=ssl_verify_depth, ssl_verify_hostname=ssl_verify_hostname, ssl_ca_certs=ssl_ca_certs, timeout=timeout, username_attributes=username_attributes, call_id=call_id, ) defer.returnValue(res)
def __init__(self, config, primary_client, server_section_name): log.msg("RADIUS IFrame Server Module Configuration:") log.config( config, lambda x: x.startswith("radius_secret") or x in ("skey", "skey_protected"), ) failmode = config.get_enum( "failmode", duo_async.FAILMODES, duo_async.FAILMODE_SAFE, transform=str.lower, ) secrets = parse_radius_secrets(config) type = config.get_enum("type", JS_TYPES) if type != JS_TYPE_CITRIX: api_timeout = 15 else: # citrix devices don't retransmit correctly, # and can't do timeouts > 10 seconds api_timeout = 8 duo_client = self.make_duo_client(config, default_timeout=api_timeout) # script injection snippet, js file script_file_default = SCRIPT_FILES[type] if duo_client.port == 443: script_uri_default = "https://%s/frame/hosted/%s" % ( duo_client.host, script_file_default, ) else: script_uri_default = "https://%s:%d/frame/hosted/%s" % ( duo_client.host, duo_client.port, script_file_default, ) script_uri = config.get_str("iframe_script_uri", script_uri_default) script_inject_default = SCRIPT_INJECT[type] script_inject = config.get_str("script_inject", script_inject_default) self.protocol = DuoIFrameRadiusServer( type, script_uri, script_inject, failmode, duo_client=duo_client, exempt_usernames=parse_exempt_usernames(config), secrets=secrets, primary_ator=primary_client, pass_through_attr_names=config.get_str("pass_through_attr_names", ""), pass_through_all=config.get_bool("pass_through_all", False), pw_codec=config.get_str("pw_codec", "utf-8"), client_ip_attr=parse_client_ip_attribute(config), server_section_name=server_section_name, server_section_ikey=config.get_str("ikey", ""), )
def restart_looping_call(self, reason): if self._check_connection_lc.running: # restart looping call which should trigger a reconnection log.msg("Connection lost to SSO: {0}".format(reason)) self._check_connection_lc.stop() yield self._check_connection() delay = random.SystemRandom().uniform(1, 60) self.reactor.callLater(delay, self._check_connection_lc.start, self.reconnect_interval, True)
def log_msg(self, summary, **kwargs): try: kwargs = dict(**kwargs) log.msg( "Summary: {summary}. Extra data: {extra_data}", summary=summary, extra_data=kwargs, ) except Exception: pass
def datagramReceived(self, datagram, addr): """addr is a tuple of (host, port). If calling from a test case, you probably want to call handle_datagram_received instead so exceptions aren't lost.""" host, port = addr try: yield self.handle_datagram_received(datagram, host, port) except packet.PacketError as err: log.msg("dropping packet from %s:%s - %s" % (host, port, err)) except Exception: log.err( None, "unhandled error processing request from %s:%s" % (host, port))
def __init__(self, config): log.msg("HTTP Proxy Module Configuration:") log.config(config) self.port = config.get_int("port", const.DEFAULT_HTTP_PORT) host = config.get_str("api_host") self.interface = config.get_str("interface", "") client_ips = get_allowed_ip_networks(config) self.factory = twisted.web.http.HTTPFactory() self.factory.protocol = functools.partial( ConnectProxy, host=host, client_ips=client_ips ) self.listener = None self._bind_if_necessary()
def handle_datagram_received(self, datagram, host, port): if self.debug: log.msg("Packet dump - received from %s:" % host) log.msg(repr(datagram)) server = self.filter(host) log.msg("Sending request from %s to %s" % (host, self.server_names[server])) request = yield server.protocol._handle_request(datagram, (host, port)) if request.response: self.transport.write(request.response, request.source) if self.debug: log.msg("Packet dump - sent to %s:" % (request.source[0])) log.msg(repr(request.response))
def is_writable(path: str) -> bool: """ Does not create a file but checks to see if one exists and if it's writable args: path (str): path to the file to check """ try: if os.path.isfile(path): with open(path, "a"): return True else: return False except Exception as e: log.msg(e) return False
def do_get_proxy_counter(self): log.msg('Reporting proxy counter') try: counter = secret_storage.access_proxy_counter() if self.debug: log.msg( 'Found counter value {counter}'.format(counter=counter)) except Exception as e: log.err(e, ERR_COUNTER_FAILURE) raise drpc.CallError(ERR_COUNTER_FAILURE, { 'during': 'get_proxy_counter', 'error': str(e) }) return { 'counter': counter, }
def do_get_proxy_counter(self): log.msg("Reporting proxy counter") try: counter = secret_storage.access_proxy_counter() if self.debug: log.msg( "Found counter value {counter}".format(counter=counter)) except Exception as e: log.failure(ERR_COUNTER_FAILURE) raise drpc.CallError(ERR_COUNTER_FAILURE, { "during": "get_proxy_counter", "error": str(e) }) return { "counter": counter, }
def _find_usable_path(storage_paths: List[str]) -> str: """ Returns the file path of the first writable storage location. The file is guarenteed to exist and be writable if it is returned from this function. If an exception is thrown there may be no storage location available """ for path in storage_paths: if is_writable(path): return path for path in storage_paths: try: make_storage(path) return path except Exception as e: log.msg(e) raise SecretStorageFileError( "No writable storage locations found. Make sure you have the privilege to write." )
def radius_proxy(self, orig_request): """Send the given RADIUS packet and return the response received. """ # Create a new request packet with a different ID but the same # type and attributes. request_packet = copy.deepcopy(orig_request.packet) request_packet.id = yield self.id_list.request() request_packet.secret = self.secret.encode() request = ClientRequest(request_packet, self.pw_codec) # Send onward; wait for reply. log.msg('Sending proxied request for id %r to %r with id %r' % (orig_request.id, self.addrs[0], request_packet.id)) self.requests[request_packet.id] = request self._send_request(request) # Make response match principal's expectations. response_packet = yield request.deferred response_packet.id = orig_request.id response_packet.secret = orig_request.secret defer.returnValue(response_packet)
def handle_response(self, datagram, source): response_packet = packet.AuthPacket(packet=datagram, dict=base.radius_dictionary()) response_packet.source = source # make sure it's from the correct host (or properly ip-spoofed :) if (source[0], int(source[1])) not in self.addrs: raise packet.PacketError( 'response packet from unknown address: %s:%s' % source, ) # look up id try: request = self.requests[response_packet.id] except KeyError: raise packet.PacketError('unrecognized id in response packet: %s' % response_packet.id) # verify reply authentication if not request.packet.VerifyReply(response_packet, datagram): raise packet.PacketError( 'response packet has invalid authenticator') response_packet.secret = self.secret.encode() # Validate Message-Authenticator, if any if response_packet.message_authenticator: if not response_packet.verify_message_authenticator( original_authenticator=request.packet.authenticator): raise packet.PacketError( 'Invalid Message-Authenticator from {0}'.format(source[0])) response_packet.authenticator = request.packet.authenticator # clear request state self._request_done(request) log.msg( 'Got response for id %r from %r; code %r' % (response_packet.id, response_packet.source, response_packet.code)) # callback request.deferred.callback(response_packet)
def __init__(self, config, _primary_ator=None): super(Module, self).__init__() log.msg('Cloud Connector Module Configuration:') log.config(config, (lambda k: k in ( 'skey', 'skey_protected', 'service_account_password', 'service_account_password_protected', ))) self.debug = config.get_bool('debug', False) self.duo_creds = DuoCreds( config.get('ikey'), config.get_protected_str('skey_protected', 'skey').encode(), ) host = config.get_str('api_host', 'api.duosecurity.com') port = config.get_int('api_port', 443) self.duo_client = self.make_duo_client(self.duo_creds, host, port=port) self.identities[self.duo_creds.get_identity()] = self.duo_creds self.drpc_path = '/auth/v2/proxy_join'
def proxy_join(self, server_module, drpc_path): """ Connects a proxy to the Duo service and establishes a long lived connection. Args: server_module (module): A module that implements the methods from the DRPC plugin interface drpc_path (str): url path to the join endpoint """ try: yield self.ping() # Update self.time_offset. Needed for sigV2+. # Open a request normally (using configured HTTPS proxy, # etc.), but upgrade protocol to DRPC instead of calling API. method = "POST" _status, rpc_protocol, _headers = yield self._request( method=method, # URL used only for scheme, host, and port. url=("https://{0}:{1:d}/".format(self.host, self.port)), body="", # overridden in protocol. headers={}, # overridden in protocol to sign with v2. timeout=self.timeout_default, protocol_class=functools.partial( self.http_client, duo_client=self, server_module=server_module, drpc_path=drpc_path, ), disconnect=False, ) defer.returnValue(rpc_protocol) except ConnectionRefusedError as e: log.msg( "Encountered exception while attempting to join. Error: {}".format(e) ) # Translate exception into DuoAPIError so that the interface is the same # for all callers. raise DuoAPIError(str(e))
def enable_primary_only(minutes): """Enable primary only mode by adding a callback that stops the Twisted reactor after a number of minutes Args: minutes (int): The number of minutes to run in primary only mode before the Authentication Proxy is stopped """ if minutes > 240: minutes = 240 try: def primary_only_callback(): log.msg("Primary only mode has expired. Stopping proxy") reactor.stop() reactor.callLater(minutes * 60, primary_only_callback) log.msg( "Running in PRIMARY ONLY mode. Stopping in {0} minutes".format(minutes) ) PrimaryOnlyManager.__instance._PRIMARY_ONLY_ENABLED = True except Exception as e: log.msg(e) print(e) sys.exit(2)
def datagramReceived(self, datagram, addr): """addr is a tuple of (host, port). If calling from a test case, you probably want to call handle_response instead so exceptions aren't lost.""" host, port = addr if self.debug: log.msg('Packet dump - received from %s:' % (host)) log.msg(repr(datagram)) try: self.handle_response(datagram, (host, port)) except packet.PacketError as err: log.msg("dropping packet from %s:%s - %s" % (host, port, err)) except Exception as err: traceback.print_exc() log.err(err)
def perform_join(self): try: server_protocol = yield self.duo_client.proxy_join( server_module=self, drpc_path=self.drpc_path) defer.returnValue(server_protocol) except duo_async.DuoAPIRotateRequiredError: server_protocol = yield self.rotate_and_rejoin() defer.returnValue(server_protocol) except duo_async.DuoAPIProxyNotFoundError: log.msg("Error connecting to service: Auth Proxy not found") self.stopService() except duo_async.DuoAPIBadSignatureError: log.msg( "There was a problem with the Duo API credentials. If this persists, you may need to reconnect your Authentication Proxy." ) self.stopService() except Exception as e: log.msg("Exception e: {}".format(e)) raise e
def _send_request(self, request, addrs=None, retry=0): if not addrs: # cycle through configured addresses addrs = self.addrs if retry > self.retries: log.msg('Request timeout for (outgoing) id %r to %r' % (request.id, addrs[0])) self._request_done(request) request.deferred.errback( AuthError('RADIUS auth request timed out')) return # send request to first address raw = request.packet.RequestPacket() self.transport.write(raw, addrs[0]) if self.debug: log.msg("Packet dump - sent to %s:" % (addrs[0][0])) log.msg(repr(raw)) # set retry timer to re-send with next address request.retry_forward_dc = reactor.callLater(self.retry_wait, self._send_request, request, addrs[1:], retry + 1)
def _get_client( self, host, port, transport_type, ssl_verify_depth, ssl_verify_hostname, ssl_ca_certs, timeout, debug, is_logging_insecure, ): if ssl_ca_certs: ssl_ca_certs = load_ca_bundle(ssl_ca_certs) if not ssl_ca_certs: # Didn't parse out any PEM certificates. raise drpc_exceptions.CallBadArgError(["ssl_ca_certs"]) else: # Ensure ssl_ca_certs is a list. ssl_ca_certs = [] is_ssl = transport_type != const.AD_TRANSPORT_CLEAR if is_ssl: if ssl_verify_hostname and not ssl_ca_certs: log.msg("Missing required configuration item: " "'SSL verify hostname' requires that " "'SSL CA certs' also be specified " "(and non-empty).") raise drpc_exceptions.CallError(ERR_LDAP_CONFIGURATION_FAILED) try: factory = self.ldap_client_factory( timeout=timeout, transport_type=transport_type, ssl_verify_depth=ssl_verify_depth, ssl_verify_hostname=ssl_verify_hostname, ssl_ca_certs=ssl_ca_certs, debug=debug, is_logging_insecure=is_logging_insecure, ) except Exception as e: log.failure(ERR_LDAP_CONFIGURATION_FAILED) raise drpc_exceptions.CallError(ERR_LDAP_CONFIGURATION_FAILED, { "error": str(e), }) try: factory.connect_ldap(host, port) client = yield factory.deferred except ldap.client.ADClientError as e: if isinstance(e.underlying_exception, DNSLookupError): log.failure(ERR_LDAP_HOSTNAME_RESOLUTION_FAILED) raise drpc_exceptions.CallError( ERR_LDAP_HOSTNAME_RESOLUTION_FAILED, { "error": str(e), }) else: log.failure(ERR_LDAP_CONNECTION_FAILED) raise drpc_exceptions.CallError(ERR_LDAP_CONNECTION_FAILED, { "error": str(e), }) except Exception as e: log.failure(ERR_LDAP_CONNECTION_FAILED) raise drpc_exceptions.CallError(ERR_LDAP_CONNECTION_FAILED, { "error": str(e), }) def timeout_cb(): log.msg("LDAP operation timed out") client.transport.abortConnection() timeout_dc = reactor.callLater(timeout, timeout_cb) defer.returnValue((client, timeout_dc))
def timeout_cb(): log.msg("LDAP operation timed out") client.transport.abortConnection()
def do_ldap_search( self, host: str, port: int, base_dn: str, filter_text: Optional[str] = None, attributes: Optional[Set[str]] = None, ntlm_domain: Optional[str] = None, ntlm_workstation: Optional[str] = None, auth_type: str = const.AD_AUTH_TYPE_NTLM_V2, transport_type: str = const.AD_TRANSPORT_STARTTLS, ssl_verify_depth: int = const.DEFAULT_SSL_VERIFY_DEPTH, ssl_verify_hostname: bool = True, ssl_ca_certs: Optional[str] = None, pagination: Optional[str] = PAGINATION_TYPE_CRITICAL, page_size: int = DEFAULT_PAGE_SIZE, max_result_size: Optional[int] = None, timeout: int = 60, call_id: Optional[int] = None, ): """ * host: LDAP IP address we will perform actions against. * port: LDAP server port. * filter_text: Filter user search. * attributes: Retrieve only the listed attributes. If None, retrieve all attributes. * ntlm_{domain, workstation}: Windows authentication mechanism. Allows for bypassing primary bind if already within the domain. * auth_type: Bind method to use, e.g. NTLM, Plain, SSPI, etc. * transport_type: Method of transportation to use, e.g. Clear, TLS, LDAPS, etc. * timeout: If either establishing the connection or binding and searching take longer than this number of seconds the response will be an error. * page_size: As in RFC 2696. * max_result_size: If paging, stop requesting results after when at least this number of results have been received. """ if attributes is None: attributes = set() try: attributes = set(k.lower() for k in attributes) if "userpassword" in attributes: attributes.remove("userpassword") except Exception: log.failure("Error parsing provided attributes dict") raise drpc.CallBadArgError(["attributes"]) page_size = parse_positive_int(page_size, "page_size") if max_result_size is not None: max_result_size = parse_positive_int(max_result_size, "max_result_size") if not (filter_text is None or isinstance(filter_text, six.string_types)): raise drpc.CallBadArgError(["filter_text"]) bind_dn = self.service_account_username bind_pw = self.service_account_password log.msg( "Performing LDAP search: " "call_id={call_id} host={host} port={port} base_dn={base_dn} " "auth_type={auth_type} transport_type={transport_type} " "ssl_verify_depth={ssl_verify_depth} ssl_verify_hostname={ssl_verify_hostname} " "ssl_ca_certs={ssl_ca_certs} attributes={attributes}", call_id=call_id, host=host, port=port, base_dn=base_dn, auth_type=auth_type, transport_type=transport_type, ssl_verify_depth=ssl_verify_depth, ssl_verify_hostname=ssl_verify_hostname, attributes=attributes, ssl_ca_certs=ssl_ca_certs is not None, ) self._verify_ldap_config_args(bind_dn, bind_pw, auth_type, transport_type) cl, timeout_dc = yield self._get_client( host, port, transport_type, ssl_verify_depth, ssl_verify_hostname, ssl_ca_certs, timeout, self.debug, self.is_logging_insecure, ) try: try: yield cl.perform_bind( auth_type=auth_type, dn=bind_dn, username=bind_dn, password=bind_pw, domain=ntlm_domain, workstation=ntlm_workstation, permit_implicit=True, ) except Exception: if timeout_dc.active(): log.failure(ldap_base.ERR_LDAP_BIND_FAILED) raise drpc.CallError(ldap_base.ERR_LDAP_BIND_FAILED) else: log.failure(ldap_base.ERR_LDAP_TIMEOUT) raise drpc.CallError(ldap_base.ERR_LDAP_TIMEOUT, { "during": "bind", }) try: filter_bytes = None if filter_text is not None: filter_bytes = filter_text.encode("utf-8") result = yield self._paged_search( client=cl, base_dn=base_dn, filter_text=filter_bytes, attributes=[a.encode("utf-8") for a in attributes], pagination=pagination, page_size=page_size, max_result_size=max_result_size, ) # any object that has a 'member' attribute is a potential # group object. if that attribute is an empty list then the # group is either empty or contains more values than the # server's configured maximum attribute length (usually 1500) # results. # try to check for members using range before giving up. try_ranged = [obj for obj in result if obj.get("member") == []] if try_ranged: yield self.try_ranged_search(cl, base_dn, try_ranged) except ldap.client.ConnectionNotProperlyBoundError: log.failure(ldap_base.ERR_LDAP_SEARCH_FAILED_BAD_BIND) raise drpc.CallError(ldap_base.ERR_LDAP_SEARCH_FAILED_BAD_BIND) except Exception: if timeout_dc.active(): log.failure(ldap_base.ERR_LDAP_SEARCH_FAILED) raise drpc.CallError(ldap_base.ERR_LDAP_SEARCH_FAILED) else: log.failure(ldap_base.ERR_LDAP_TIMEOUT) raise drpc.CallError(ldap_base.ERR_LDAP_TIMEOUT, { "during": "search", }) finally: if timeout_dc.active(): timeout_dc.cancel() try: cl.transport.abortConnection() except Exception: pass defer.returnValue({ "results": result, })
def add_message(self, msg: bytes): """Add a message to the EAP session. Returns False on success, returns errback on error, or gtc_received on receiving a token card response. """ code, identifier, length, eap_type = struct.unpack('>BBHB', msg[:5]) if not self.inner and self.id != 0 and identifier != self.id: # Silently discard response if ID does not match log.msg("Response ID %s does not match request ID %s" % (identifier, self.id)) defer.returnValue(False) if len(msg) != length: message = 'Malformed EAP packet: length ({0}) does not match actual length ({1})'.format( length, len(msg)) log.auth_standard( msg=message, username=self.current_request.username, auth_stage=log.AUTH_UNKNOWN, status=log.AUTH_ERROR, client_ip=self.current_request.client_ip, server_section=self.server.server_section_name, server_section_ikey=self.server.server_section_ikey) error = yield self.errback(self, message) defer.returnValue(error) if code == EAP_CODE_REQUEST: # EAP request, we are talking to RADIUS, this should not happen message = "EAP request received." log.auth_standard( msg=message, username=self.current_request.username, auth_stage=log.AUTH_UNKNOWN, status=log.AUTH_ERROR, client_ip=self.current_request.client_ip, server_section=self.server.server_section_name, server_section_ikey=self.server.server_section_ikey) error = yield self.errback(self, message) defer.returnValue(error) elif code == EAP_CODE_RESPONSE: # EAP response, we are talking to NetMotion self.id += 1 if eap_type == EAP_TYPE_IDENTITY: # Identity self.state = EAP_SESSION_REQUEST_GTC elif eap_type == EAP_TYPE_NAK: # Legacy Nak # Data element (element 5) will indicate the desired type desired_type = msg[5] if desired_type == EAP_TYPE_PEAP: # PEAP request self.state = EAP_SESSION_PEAP_REQUEST else: message = "Unknown Nak request %s." % desired_type log.auth_standard( msg=message, username=self.current_request.username, auth_stage=log.AUTH_UNKNOWN, status=log.AUTH_ERROR, client_ip=self.current_request.client_ip, server_section=self.server.server_section_name, server_section_ikey=self.server.server_section_ikey) error = yield self.errback(self, message) defer.returnValue(error) elif eap_type == EAP_TYPE_TLV: if msg[5:7] == MS_RESULT_HEADER and len( msg) == 11: # MS-Result-TLV if msg[-1] == 1 and self.state == EAP_SESSION_ACCEPT: # Success self.state = EAP_SESSION_SUCCESS else: # Failure self.state = EAP_SESSION_FAILURE else: # No support for cryptobinding or SoH TLV # At least exit with some relevant info message = "Unsupported TLV: {0!r}.".format(msg) log.auth_standard( msg=message, username=self.current_request.username, auth_stage=log.AUTH_UNKNOWN, status=log.AUTH_ERROR, client_ip=self.current_request.client_ip, server_section=self.server.server_section_name, server_section_ikey=self.server.server_section_ikey) error = yield self.errback(self, message) defer.returnValue(error) elif eap_type == EAP_TYPE_PEAP: # PEAP tls_flags = msg[5] if tls_flags & 0x80: # Length included self.tls_len = struct.unpack('>I', msg[6:10]) tls_msg: bytes = msg[10:] else: tls_msg = msg[6:] if self.state == EAP_SESSION_PEAP_MORE: self.tls_msg += tls_msg else: self.tls_msg = tls_msg if tls_flags & 0x40: # More fragments self.state = EAP_SESSION_PEAP_MORE else: self.state = EAP_SESSION_PEAP if self.tls_msg: if hasattr(self, 'ssl_con'): add_response = yield self.add_incoming(tls_msg) if add_response: # Errback or gtc callback occured, pass it through defer.returnValue(add_response) else: # We reset the EAP session but the peer is trying to continue self.state = EAP_SESSION_PEAP_REQUEST else: # Empty PEAP message, probably handshake over self.state = EAP_SESSION_PEAP_ID defer.returnValue(False)
def log_msg(self, summary, **kwargs): try: kwargs = dict(**kwargs) log.msg(summary, kwargs) except Exception: pass
def log_request(self, request, msg): log.msg("({}, {}, {}): {}".format(request.source, request.username, request.id, msg))