Skip to content

Commit

Permalink
Add support of Message-Authenticator AVP
Browse files Browse the repository at this point in the history
* Packet class now extend OrderedDict to maintain
  correct AVP order and HMAC/MD5 check fo
  Message-Authenticator

* Fix client_async for Accounting, Coa
  • Loading branch information
geaaru committed Sep 4, 2018
1 parent 0ada747 commit b3f114b
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 49 deletions.
101 changes: 101 additions & 0 deletions example/acct_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/usr/bin/python

import asyncio

import logging
import traceback
from pyrad.dictionary import Dictionary
from pyrad.client_async import ClientAsync
from pyrad.packet import AccountingResponse

logging.basicConfig(level="DEBUG",
format="%(asctime)s [%(levelname)-8s] %(message)s")
client = ClientAsync(server="127.0.0.1",
secret=b"Kah3choteereethiejeimaeziecumi",
timeout=3, debug=True,
dict=Dictionary("dictionary"))

loop = asyncio.get_event_loop()


def create_request(client, user):
req = client.CreateAcctPacket(User_Name=user)

req["NAS-IP-Address"] = "192.168.1.10"
req["NAS-Port"] = 0
req["Service-Type"] = "Login-User"
req["NAS-Identifier"] = "trillian"
req["Called-Station-Id"] = "00-04-5F-00-0F-D1"
req["Calling-Station-Id"] = "00-01-24-80-B3-9C"
req["Framed-IP-Address"] = "10.0.0.100"

return req


def print_reply(reply):
print("Received Accounting-Response")

print("Attributes returned by server:")
for i in reply.keys():
print("%s: %s" % (i, reply[i]))


def test_acct1(enable_message_authenticator=False):

global client

try:
# Initialize transports
loop.run_until_complete(
asyncio.ensure_future(
client.initialize_transports(enable_auth=True,
# local_addr='127.0.0.1',
# local_auth_port=8000,
enable_acct=True,
enable_coa=True)))

req = create_request(client, "wichert")
if enable_message_authenticator:
req.add_message_authenticator()

future = client.SendPacket(req)

# loop.run_until_complete(future)
loop.run_until_complete(asyncio.ensure_future(
asyncio.gather(
future,
return_exceptions=True
)

))

if future.exception():
print('EXCEPTION ', future.exception())
else:
reply = future.result()

if reply.code == AccountingResponse:
print("Accounting accepted")

print("Attributes returned by server:")
for i in reply.keys():
print("%s: %s" % (i, reply[i]))

# Close transports
loop.run_until_complete(asyncio.ensure_future(
client.deinitialize_transports()))
print('END')

del client
except Exception as exc:
print('Error: ', exc)
print('\n'.join(traceback.format_exc().splitlines()))
# Close transports
loop.run_until_complete(asyncio.ensure_future(
client.deinitialize_transports()))

loop.close()


#test_acct1()
test_acct1(enable_message_authenticator=True)
84 changes: 71 additions & 13 deletions example/auth_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

logging.basicConfig(level="DEBUG",
format="%(asctime)s [%(levelname)-8s] %(message)s")
client = ClientAsync(server="localhost",
client = ClientAsync(server="127.0.0.1",
secret=b"Kah3choteereethiejeimaeziecumi",
timeout=3, debug=True,
dict=Dictionary("dictionary"))
Expand All @@ -31,6 +31,7 @@ def create_request(client, user):

return req


def print_reply(reply):
if reply.code == AccessAccept:
print("Access accepted")
Expand All @@ -41,6 +42,7 @@ def print_reply(reply):
for i in reply.keys():
print("%s: %s" % (i, reply[i]))


def test_auth1():

global client
Expand All @@ -50,13 +52,11 @@ def test_auth1():
loop.run_until_complete(
asyncio.ensure_future(
client.initialize_transports(enable_auth=True,
#local_addr='127.0.0.1',
#local_auth_port=8000,
# local_addr='127.0.0.1',
# local_auth_port=8000,
enable_acct=True,
enable_coa=True)))



req = client.CreateAuthPacket(User_Name="wichert")

req["NAS-IP-Address"] = "192.168.1.10"
Expand Down Expand Up @@ -107,6 +107,7 @@ def test_auth1():

loop.close()


def test_multi_auth():

global client
Expand All @@ -117,12 +118,10 @@ def test_multi_auth():
asyncio.ensure_future(
client.initialize_transports(enable_auth=True,
local_addr='127.0.0.1',
#local_auth_port=8000,
# local_auth_port=8000,
enable_acct=True,
enable_coa=True)))



reqs = []
for i in range(150):
req = create_request(client, "user%s" % i)
Expand Down Expand Up @@ -162,6 +161,7 @@ def test_multi_auth():

loop.close()


def test_multi_client():

clients = []
Expand Down Expand Up @@ -189,8 +189,8 @@ def test_multi_client():
enable_coa=False)))

# Send
for i in range(n_req4client):
req = create_request(client, "user%s" % i)
for j in range(n_req4client):
req = create_request(client, "user%s" % j)
print('CREATE REQUEST with id %d' % req.id)
future = client.SendPacket(req)
reqs.append(future)
Expand Down Expand Up @@ -240,6 +240,64 @@ def test_multi_client():
loop.close()


#test_multi_auth()
#test_auth1()
test_multi_client()
def test_auth1_msg_authenticator():
global client

try:
# Initialize transports
loop.run_until_complete(
asyncio.ensure_future(
client.initialize_transports(enable_auth=True,
# local_addr='127.0.0.1',
# local_auth_port=8000,
enable_acct=True,
enable_coa=True)))

req = create_request(client, "wichert")
req.add_message_authenticator()

future = client.SendPacket(req)

# loop.run_until_complete(future)
loop.run_until_complete(asyncio.ensure_future(
asyncio.gather(
future,
return_exceptions=True
)

))

if future.exception():
print('EXCEPTION ', future.exception())
else:
reply = future.result()

if reply.code == AccessAccept:
print("Access accepted")
else:
print("Access denied")

print("Attributes returned by server:")
for i in reply.keys():
print("%s: %s" % (i, reply[i]))

# Close transports
loop.run_until_complete(asyncio.ensure_future(
client.deinitialize_transports()))
print('END')

del client
except Exception as exc:
print('Error: ', exc)
print('\n'.join(traceback.format_exc().splitlines()))
# Close transports
loop.run_until_complete(asyncio.ensure_future(
client.deinitialize_transports()))

loop.close()


# test_multi_auth()
# test_auth1()
# test_multi_client()
test_auth1_msg_authenticator()
Empty file removed example/pyrad.log
Empty file.
18 changes: 16 additions & 2 deletions example/server_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@

class FakeServer(ServerAsync):

def __init__(self, loop, dictionary):
def __init__(self, loop, dictionary, enable_message_authenticator=False):

ServerAsync.__init__(self, loop=loop, dictionary=dictionary,
enable_pkt_verify=True, debug=True)
self.enable_message_authenticator = enable_message_authenticator


def handle_auth_packet(self, protocol, pkt, addr):
Expand All @@ -43,6 +44,10 @@ def handle_auth_packet(self, protocol, pkt, addr):
})

reply.code = AccessAccept

if self.enable_message_authenticator and pkt.message_authenticator:
reply.add_message_authenticator()

protocol.send_response(reply, addr)

def handle_acct_packet(self, protocol, pkt, addr):
Expand All @@ -53,6 +58,9 @@ def handle_acct_packet(self, protocol, pkt, addr):
print("%s: %s" % (attr, pkt[attr]))

reply = self.CreateReplyPacket(pkt)

if self.enable_message_authenticator and pkt.message_authenticator:
reply.add_message_authenticator()
protocol.send_response(reply, addr)

def handle_coa_packet(self, protocol, pkt, addr):
Expand All @@ -63,6 +71,8 @@ def handle_coa_packet(self, protocol, pkt, addr):
print("%s: %s" % (attr, pkt[attr]))

reply = self.CreateReplyPacket(pkt)
if self.enable_message_authenticator and pkt.message_authenticator:
reply.add_message_authenticator()
protocol.send_response(reply, addr)

def handle_disconnect_packet(self, protocol, pkt, addr):
Expand All @@ -75,14 +85,18 @@ def handle_disconnect_packet(self, protocol, pkt, addr):
reply = self.CreateReplyPacket(pkt)
# COA NAK
reply.code = 45

if self.enable_message_authenticator and pkt.message_authenticator:
reply.add_message_authenticator()
protocol.send_response(reply, addr)


if __name__ == '__main__':

# create server and read dictionary
loop = asyncio.get_event_loop()
server = FakeServer(loop=loop, dictionary=Dictionary('dictionary'))
server = FakeServer(loop=loop, dictionary=Dictionary('dictionary'),
enable_message_authenticator=True)

# add clients (address, secret, name)
server.hosts["127.0.0.1"] = RemoteHost("127.0.0.1",
Expand Down
26 changes: 22 additions & 4 deletions pyrad/client_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,29 @@ def datagram_received(self, data, addr):

reply = Packet(packet=data, dict=self.client.dict)

if reply and reply.id in self.pending_requests:
if reply is not None and reply.id in self.pending_requests:
req = self.pending_requests[reply.id]
packet = req['packet']

reply.secret = packet.secret

if packet.VerifyReply(reply, data):
req['future'].set_result(reply)
# Remove request for map
del self.pending_requests[reply.id]

if reply.message_authenticator and not \
reply.verify_message_authenticator(
original_authenticator=packet.authenticator):
self.logger.warn(
'[%s:%d] Received invalid reply for id %d. %s' % (
self.server, self.port, reply.id,
'Invalid Message-Authenticator. Ignoring it.'
)
)
self.errors += 1
else:

req['future'].set_result(reply)
# Remove request for map
del self.pending_requests[reply.id]
else:
self.logger.warn(
'[%s:%d] Received invalid reply for id %d. %s' % (
Expand Down Expand Up @@ -430,9 +443,14 @@ def SendPacket(self, pkt):
if not self.protocol_acct:
raise Exception('Transport not initialized')

self.protocol_acct.send_packet(pkt, ans)

elif isinstance(pkt, CoAPacket):
if not self.protocol_coa:
raise Exception('Transport not initialized')

self.protocol_coa.send_packet(pkt, ans)

else:
raise Exception('Unsupported packet')

Expand Down
Loading

0 comments on commit b3f114b

Please sign in to comment.