def PwDecrypt(self, password): """Unobfuscate a RADIUS password. RADIUS hides passwords in packets by using an algorithm based on the MD5 hash of the packet authenticator and RADIUS secret. This function reverses the obfuscation process. :param password: obfuscated form of password :type password: binary string :return: plaintext password :rtype: unicode string """ buf = password pw = six.b('') last = self.authenticator while buf: hash = md5_constructor(self.secret + last).digest() if six.PY3: for i in range(16): pw += bytes((hash[i] ^ buf[i], )) else: for i in range(16): pw += chr(ord(hash[i]) ^ ord(buf[i])) (last, buf) = (buf[:16], buf[16:]) while pw.endswith(six.b('\x00')): pw = pw[:-1] return pw.decode('utf-8')
def __init__(self, code=AccountingRequest, id=None, secret=six.b(''), authenticator=None, **attributes): AcctPacket.__init__(self, code, id, six.b(secret), authenticator, **attributes) ExtAttrMixin.__init__(self)
def get_dm_packet(vendor_id, nas_secret, nas_addr, coa_port=3799, **kwargs): coa_request = message.CoAMessage(code=packet.DisconnectRequest, dict=RADIUS_DICT, secret=six.b(str(nas_secret)), **kwargs) username = coa_request["User-Name"][0] if int(vendor_id) == ikuai.VENDOR_ID: pkg = ikuai.create_dm_pkg(six.b(str(nas_secret)), username) return (pkg, nas_addr, coa_port) else: return (coa_request.RequestPacket(), nas_addr, coa_port)
def _PktEncodeAttributes(self): result = six.b('') for (code, datalst) in self.items(): for data in datalst: result += self._PktEncodeAttribute(code, data) return result
def PwDecrypt(self, password): buf = password pw = six.b('') last = self.authenticator while buf: hash = md5_constructor(self.secret + last).digest() for i in range(16): pw += chr(ord(hash[i]) ^ ord(buf[i])) (last, buf) = (buf[:16], buf[16:]) while pw.endswith(six.b('\x00')): pw = pw[:-1] return pw.decode('utf-8')
def send_auth(server, port=1812, secret=six.b("testing123"), debug=False, dictfile=None, **kwargs): """ send auth request :param server: radius server ipaddr :param port: auth port, default 1812 :param secret: nas share secret :param debug: logging level :param dictfile: dictionary file path :param kwargs: request params :return: auth response """ try: radius_dictionary = get_dictionary(dictfile=dictfile) timeout_sec = kwargs.pop('timeout', 5) result = kwargs.pop('result', True) User_Password = kwargs.pop("User-Password", None) CHAP_Password = kwargs.pop("CHAP-Password", None) CHAP_Password_Plaintext = kwargs.pop("CHAP-Password-Plaintext", None) CHAP_Challenge = kwargs.get("CHAP-Challenge") request = message.AuthMessage(dict=radius_dictionary, secret=secret, **kwargs) if User_Password: request['User-Password'] = request.PwCrypt(User_Password) if CHAP_Password: if CHAP_Challenge: request['CHAP-Challenge'] = CHAP_Challenge request['CHAP-Password'] = CHAP_Password if CHAP_Password_Plaintext: request['CHAP-Password'] = request.ChapEcrypt( CHAP_Password_Plaintext) if debug: logger.debug("Send radius auth request to (%s:%s): %s" % (server, port, request.format_str())) sock = socket.socket(type=socket.SOCK_DGRAM) sock.settimeout(timeout_sec) sock.connect((server, port)) sock.send(request.RequestPacket()) if result: data, address = sock.recvfrom(8192) reply = request.CreateReply(packet=data) if debug: logger.debug("Recv radius auth response from (%s:%s): %s" % (server, port, reply.format_str())) return reply except Exception as e: logger.error("authenticator error {}".format(e.message), exc_info=True)
def VerifyCoARequest(self): """Verify request authenticator. :return: True if verification failed else False :rtype: boolean """ # assert(self.raw_packet) hash = md5_constructor(self.raw_packet[0:4] + 16 * six.b('\x00') + self.raw_packet[20:] + self.secret).digest() return hash == self.authenticator
def PwCrypt(self, password): """Obfuscate password. RADIUS hides passwords in packets by using an algorithm based on the MD5 hash of the packet authenticator and RADIUS secret. If no authenticator has been set before calling PwCrypt one is created automatically. Changing the authenticator after setting a password that has been encrypted using this function will not work. :param password: plaintext password :type password: unicode stringn :return: obfuscated version of the password :rtype: binary string """ if self.authenticator is None: self.authenticator = self.CreateAuthenticator() if isinstance(password, six.text_type): password = password.encode('utf-8') buf = password if len(password) % 16 != 0: buf += six.b('\x00') * (16 - (len(password) % 16)) hash = md5_constructor(self.secret + self.authenticator).digest() result = six.b('') last = self.authenticator while buf: hash = md5_constructor(self.secret + last).digest() if six.PY3: for i in range(16): result += bytes((hash[i] ^ buf[i], )) else: for i in range(16): result += chr(ord(hash[i]) ^ ord(buf[i])) last = result[-16:] buf = buf[16:] return result
def __init__(self, vendor_id, dictionary, nas_secret, nas_addr, coa_port=3799, debug=False): self.dictionary = dictionary self.secret = six.b(str(nas_secret)) self.addr = nas_addr self.port = int(coa_port) self.vendor_id = int(vendor_id) self.debug = debug self.uport = reactor.listenUDP(0, self)
def __init__(self, secret, dictionary, server, authport=1812, acctport=1813, debug=False): self.dict = dictionary self.secret = six.b(secret) self.server = server self.authport = authport self.acctport = acctport self.debug = debug reactor.listenUDP(0, self)
def PwCrypt(self, password): if self.authenticator is None: self.authenticator = self.CreateAuthenticator() if isinstance(password, six.text_type): password = password.encode('utf-8') buf = password if len(password) % 16 != 0: buf += six.b('\x00') * (16 - (len(password) % 16)) hash = md5_constructor(self.secret + self.authenticator).digest() result = six.b('') last = self.authenticator while buf: hash = md5_constructor(self.secret + last).digest() for i in range(16): result += chr(ord(hash[i]) ^ ord(buf[i])) last = result[-16:] buf = buf[16:] return result
def RequestPacket(self): """Create a ready-to-transmit CoA request packet. Return a RADIUS packet which can be directly transmitted to a RADIUS server. :return: raw packet :rtype: string """ attr = self._PktEncodeAttributes() if self.id is None: self.id = self.CreateID() header = struct.pack('!BBH', self.code, self.id, (20 + len(attr))) self.authenticator = md5_constructor(header[0:4] + 16 * six.b('\x00') + attr + self.secret).digest() return header + self.authenticator + attr
def __init__(self, code=CoARequest, id=None, secret=six.b(''), authenticator=None, **attributes): """Constructor :param dict: RADIUS dictionary :type dict: pyrad.dictionary.Dictionary class :param secret: secret needed to communicate with a RADIUS server :type secret: string :param id: packet identifaction number :type id: integer (8 bits) :param code: packet type code :type code: integer (8bits) :param packet: raw packet to decode :type packet: string """ Packet.__init__(self, code, id, secret, authenticator, **attributes) if 'packet' in attributes: self.raw_packet = attributes['packet']
def __init__(self, code=0, id=None, secret=six.b(''), authenticator=None, **attributes): """Constructor :param dict: RADIUS dictionary :type dict: pyrad.dictionary.Dictionary class :param secret: secret needed to communicate with a RADIUS server :type secret: string :param id: packet identifaction number :type id: integer (8 bits) :param code: packet type code :type code: integer (8bits) :param packet: raw packet to decode :type packet: string """ dict.__init__(self) self.code = code if id is not None: self.id = id else: self.id = CreateID() if not isinstance(secret, six.binary_type): raise TypeError('secret must be a binary string') self.secret = secret if authenticator is not None and \ not isinstance(authenticator, six.binary_type): raise TypeError('authenticator must be a binary string') self.authenticator = authenticator if 'dict' in attributes: self.dict = attributes['dict'] if 'packet' in attributes: self.DecodePacket(attributes['packet']) for (key, value) in attributes.items(): if key in ['dict', 'fd', 'packet']: continue key = key.replace('_', '-') self.AddAttribute(key, value)
def send_acct(server, port=1813, secret=six.b("testing123"), debug=False, dictfile=None, **kwargs): """ send accounting request :param server: radius server ipaddr :param port: acct port, default 1813 :param secret: nas share secret :param debug: logging level :param dictfile: dictionary file path :param kwargs: request params :return: acct response """ try: radius_dictionary = get_dictionary(dictfile=dictfile) timeout_sec = kwargs.pop('timeout', 5) result = kwargs.pop('result', True) request = message.AcctMessage(dict=radius_dictionary, secret=secret, **kwargs) if debug: logger.debug("Send radius acct request to (%s:%s): %s" % (server, port, request.format_str())) sock = socket.socket(type=socket.SOCK_DGRAM) sock.settimeout(timeout_sec) sock.connect((server, port)) sock.send(request.RequestPacket()) if result: data, address = sock.recvfrom(8192) reply = request.CreateReply(packet=data) if debug: logger.debug("Recv radius auth response from (%s:%s): %s" % (server, port, reply.format_str())) return reply except Exception as e: logger.error("accounting error {}".format(e.message), exc_info=True)
Acct_Status_Off = 8 def parse_auth_packet(datagram,(host,port),client_config,dictionary=None): """ parse radius auth request :param datagram: :param client_config: :param dictionary: :return: """ if host in client_config.defaults: client = client_config.defaults[host] authreq = message.AuthMessage(packet=datagram, dict=dictionary,secret=str(client['secret'])) authreq.vendor_id=client_config.vendors.get(client['vendor']) else: authreq = message.AuthMessage(packet=datagram,dict=dictionary, secret=six.b('')) nas_id = authreq.get_nas_id() _client = [c for c in client_config.defaults.itervalues() if c['nasid'] == nas_id] if _client: client = _client[0] authreq.vendor_id = client_config.vendors.get(client['vendor']) authreq.secret = six.b(client['secret']) else: raise packet.PacketError("Unauthorized Radius Access Device [%s] (%s:%s)"%(nas_id,host,port)) authreq.source = (host,port) authreq = request_logger.handle_radius(authreq) authreq = request_mac_parse.handle_radius(authreq) authreq = request_vlan_parse.handle_radius(authreq) return authreq
class BasicAdapter(object): def __init__(self, settings): self.settings = settings self.pool = Pool(self.settings.RADIUSD['pool_size']) self.logger = logging.getLogger(__name__) self.dictionary = dictionary.Dictionary( self.settings.RADIUSD['dictionary']) self.auth_pre = [ importlib.import_module(m) for m in self.settings.MODULES["auth_pre"] ] self.acct_pre = [ importlib.import_module(m) for m in self.settings.MODULES["acct_pre"] ] self.auth_post = [ importlib.import_module(m) for m in self.settings.MODULES["auth_post"] ] self.acct_post = [ importlib.import_module(m) for m in self.settings.MODULES["acct_post"] ] def handleAuth(self, socket, data, address): """ auth request handle :param socket: :param data: :param address: :return: """ try: req = self.parseAuthPacket(data, address) prereply = self.processAuth(req) reply = self.authReply(req, prereply) self.pool.spawn(socket.sendto, reply.ReplyPacket(), address) except Exception as e: self.logger.error("Handle Radius Auth error {}".format(e.message), exc_info=True) def handleAcct(self, socket, data, address): """ acct request handle :param socket: :param data: :param address: :return: """ try: req = self.parseAcctPacket(data, address) prereply = self.processAcct(req) reply = self.acctReply(req, prereply) self.pool.spawn(socket.sendto, reply.ReplyPacket(), address) except Exception as e: self.logger.error("Handle Radius Acct error {}".format(e.message), exc_info=True) def getClients(self): """ fetch nas clients Usage example:: def getClients(self): nas = dict( status=1, nasid='toughac', name='toughac', vendor=0, ipaddr='127.0.0.1', secret='testing123', coaport=3799 ) return { 'toughac' : nas, '127.0.0.1' : nas} :return: nas dict """ raise NotImplementedError('Attempted to use a pure base class') @staticmethod def verifyAcctRequest(req): """ verify radius accounting request :param req: """ if req.code != packet.AccountingRequest: errstr = u'Invalid accounting request code=%s' % req.code raise packet.PacketError(errstr) if not req.VerifyAcctRequest(): errstr = u'The accounting response check failed. Check that the shared key is consistent' raise packet.PacketError(errstr) @staticmethod def freeReply(req, **params): """ gen free auth response :param req: :param params: :return: """ reply = req.CreateReply() reply.vendor_id = req.vendor_id reply[ 'Reply-Message'] = u'User:%s (Free)Authenticate Success' % req.get_user_name( ) reply.code = packet.AccessAccept reply_attrs = dict(attrs={}) reply_attrs['input_rate'] = params.pop("free_auth_input_limit", 1048576) reply_attrs['output_rate'] = params.pop("free_auth_output_limit", 4194304) reply_attrs['rate_code'] = params.pop("free_auth_rate_code", "") reply_attrs['domain'] = params.pop("free_auth_domain", "") reply_attrs['attrs']['Session-Timeout'] = params.pop( "max_session_timeout", 86400) reply.resp_attrs = reply_attrs return reply @staticmethod def rejectReply(req, errmsg=''): """ gen reject radius auth response :param req: :param errmsg: :return: """ reply = req.CreateReply() reply.vendor_id = req.vendor_id reply['Reply-Message'] = errmsg reply.code = packet.AccessReject return reply def parseAuthPacket(self, datagram, (host, port)): """ parse radius auth request :param datagram: :return: pyrad.message """ clients = self.getClients() vendors = self.settings.VENDORS if host in clients: client = clients[host] request = message.AuthMessage(packet=datagram, dict=self.dictionary, secret=str(client['secret'])) request.vendor_id = vendors.get(client['vendor']) else: request = message.AuthMessage(packet=datagram, dict=self.dictionary, secret=six.b('')) nas_id = request.get_nas_id() if nas_id in clients: client = clients[nas_id] request.vendor_id = vendors.get(client['vendor']) request.secret = six.b(client['secret']) else: raise packet.PacketError( "Unauthorized Radius Access Device [%s] (%s:%s)" % (nas_id, host, port)) if request.code != packet.AccessRequest: errstr = u'Invalid authenticator request code=%s' % request.code raise packet.PacketError(errstr) request.source = (host, port) for _module in self.auth_pre: request = _module.handle_radius(request) return request
def send_coadm(server, port=3799, secret=six.b("testing123"), debug=False, dictfile=None, **kwargs): """ send coa disconnect request to nas :param server: nas server ipaddr :param port: coa port, default 3799 :param secret: nas share secret :param debug: logging level :param dictfile: dictionary file path :param kwargs: request params :return: coa response """ try: radius_dictionary = get_dictionary(dictfile=dictfile) timeout_sec = kwargs.pop('timeout', 5) result = kwargs.pop('result', True) vendor_id = kwargs.pop('vendor_id', 0) request = message.CoAMessage(code=packet.DisconnectRequest, dict=radius_dictionary, secret=secret, **kwargs) username = request["User-Name"][0] if vendor_id == ikuai.VENDOR_ID: pkg = ikuai.create_dm_pkg(secret, username) if debug: logger.debug( "Send ikuai radius CoaDmRequest to (%s:%s) [username:%s]: %s" % (server, port, username, repr(pkg))) else: pkg = request.RequestPacket() if debug: logger.debug( "Send radius CoaDmRequest to (%s:%s) [username:%s]: %s" % (server, port, username, request.format_str())) sock = socket.socket(type=socket.SOCK_DGRAM) sock.settimeout(timeout_sec) sock.connect((server, port)) sock.send(pkg) if result: data, address = sock.recvfrom(8192) if vendor_id != ikuai.VENDOR_ID: reply = request.CreateReply(packet=data) if debug: logger.debug( "Recv radius coa dm response from (%s:%s): %s" % (server, port, reply.format_str())) return reply else: if debug: logger.debug( "Recv radius ik coa dm response from (%s:%s): %s" % (server, port, repr(data))) return data except Exception as e: logger.error("accounting error {}".format(e.message), exc_info=True)
:param datagram: :return: pyrad.message """ clients = self.getClients() vendors = self.settings.VENDORS if host in clients: client = clients[host] request = message.AcctMessage(packet=datagram, dict=self.dictionary, secret=str(client['secret'])) request.vendor_id = vendors.get(client['vendor']) else: request = message.AcctMessage(packet=datagram, dict=self.dictionary, secret=six.b('')) nas_id = request.get_nas_id() if nas_id in clients: client = clients[nas_id] request.vendor_id = vendors.get(client['vendor']) request.secret = six.b(client['secret']) else: raise packet.PacketError( "Unauthorized Radius Access Device [%s] (%s:%s)" % (nas_id, host, port)) self.verifyAcctRequest(request) request.source = (host, port) for _module in self.acct_pre: request = _module.handle_radius(request) return request