pyrad 2.5.2__tar.gz → 2.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyrad-2.5.2 → pyrad-2.5.4}/CHANGES.rst +6 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/LICENSE.txt +2 -2
- {pyrad-2.5.2/pyrad.egg-info → pyrad-2.5.4}/PKG-INFO +15 -12
- {pyrad-2.5.2 → pyrad-2.5.4}/README.rst +13 -8
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/conf.py +3 -3
- {pyrad-2.5.2 → pyrad-2.5.4}/pyproject.toml +2 -4
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/__init__.py +1 -1
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/bidict.py +1 -1
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/client.py +1 -2
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/dictfile.py +2 -3
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/dictionary.py +1 -1
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/host.py +1 -1
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/packet.py +25 -42
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/test_packet.py +13 -13
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tools.py +3 -0
- {pyrad-2.5.2 → pyrad-2.5.4/pyrad.egg-info}/PKG-INFO +15 -12
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad.egg-info/requires.txt +0 -4
- {pyrad-2.5.2 → pyrad-2.5.4}/MANIFEST.in +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/TODO.rst +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/Makefile +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/make.bat +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/_static/logo.png +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/api/client.rst +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/api/dictionary.rst +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/api/host.rst +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/api/packet.rst +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/api/proxy.rst +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/api/server.rst +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/docs/source/index.rst +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/acct.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/auth.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/auth_async.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/client-coa.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/coa.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/dictionary +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/dictionary.freeradius +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/pyrad.log +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/server.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/server_async.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/example/status.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/client_async.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/curved.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/proxy.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/server.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/server_async.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/__init__.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/mock.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/test_bidict.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/test_client.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/test_dictionary.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/test_host.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/test_proxy.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/test_server.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad/tests/test_tools.py +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad.egg-info/SOURCES.txt +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad.egg-info/dependency_links.txt +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad.egg-info/top_level.txt +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/pyrad.egg-info/zip-safe +0 -0
- {pyrad-2.5.2 → pyrad-2.5.4}/setup.cfg +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
Copyright
|
|
2
|
-
Copyright
|
|
1
|
+
Copyright 2026 Christian Giese, Istvan Ruzman and Stefan Lieberth. All rights reserved.
|
|
2
|
+
Copyright 2002-2025 Christian Giese and Istvan Ruzman. All rights reserved.
|
|
3
3
|
Copyright 2007-2008 Simplon. All rights reserved.
|
|
4
4
|
Copyright 2002-2008 Wichert Akkerman. All rights reserved.
|
|
5
5
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyrad
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.4
|
|
4
4
|
Summary: RADIUS tools
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Christian Giese <gic@gicnet.de>, Istvan Ruzman <istvan@ruzman.eu>, Stefan Lieberth <stefan@lieberth.net>
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
Project-URL: Repository, https://github.com/pyradius/pyrad
|
|
8
8
|
Project-URL: Documentation, https://pyradius-pyrad.readthedocs.io
|
|
@@ -23,10 +23,8 @@ Requires-Python: >=3.8
|
|
|
23
23
|
Description-Content-Type: text/x-rst
|
|
24
24
|
License-File: LICENSE.txt
|
|
25
25
|
Requires-Dist: netaddr>=0.8.0
|
|
26
|
-
Requires-Dist: six>=1.16.0
|
|
27
26
|
Provides-Extra: test
|
|
28
27
|
Requires-Dist: pytest>=8; extra == "test"
|
|
29
|
-
Requires-Dist: mock; python_version < "3.10" and extra == "test"
|
|
30
28
|
Provides-Extra: docs
|
|
31
29
|
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
32
30
|
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
@@ -59,18 +57,24 @@ pyrad is an implementation of a RADIUS client/server as described in RFC2865.
|
|
|
59
57
|
It takes care of all the details like building RADIUS packets, sending
|
|
60
58
|
them and decoding responses.
|
|
61
59
|
|
|
62
|
-
Here is an example of doing a authentication request
|
|
60
|
+
Here is an example of doing a authentication request:
|
|
61
|
+
|
|
62
|
+
.. code-block:: python
|
|
63
63
|
|
|
64
64
|
from pyrad.client import Client
|
|
65
65
|
from pyrad.dictionary import Dictionary
|
|
66
66
|
import pyrad.packet
|
|
67
67
|
|
|
68
|
-
srv = Client(
|
|
69
|
-
|
|
68
|
+
srv = Client(
|
|
69
|
+
server="localhost",
|
|
70
|
+
secret=b"Kah3choteereethiejeimaeziecumi",
|
|
71
|
+
dict=Dictionary("dictionary"),
|
|
72
|
+
)
|
|
70
73
|
|
|
71
74
|
# create request
|
|
72
|
-
req = srv.CreateAuthPacket(
|
|
73
|
-
|
|
75
|
+
req = srv.CreateAuthPacket(
|
|
76
|
+
code=pyrad.packet.AccessRequest, User_Name="wichert", NAS_Identifier="localhost"
|
|
77
|
+
)
|
|
74
78
|
req["User-Password"] = req.PwCrypt("password")
|
|
75
79
|
|
|
76
80
|
# send request
|
|
@@ -82,9 +86,8 @@ Here is an example of doing a authentication request::
|
|
|
82
86
|
print("access denied")
|
|
83
87
|
|
|
84
88
|
print("Attributes returned by server:")
|
|
85
|
-
for
|
|
86
|
-
print("
|
|
87
|
-
|
|
89
|
+
for key, val in reply.items():
|
|
90
|
+
print(f"{key}: {val}")
|
|
88
91
|
|
|
89
92
|
|
|
90
93
|
Requirements & Installation
|
|
@@ -24,18 +24,24 @@ pyrad is an implementation of a RADIUS client/server as described in RFC2865.
|
|
|
24
24
|
It takes care of all the details like building RADIUS packets, sending
|
|
25
25
|
them and decoding responses.
|
|
26
26
|
|
|
27
|
-
Here is an example of doing a authentication request
|
|
27
|
+
Here is an example of doing a authentication request:
|
|
28
|
+
|
|
29
|
+
.. code-block:: python
|
|
28
30
|
|
|
29
31
|
from pyrad.client import Client
|
|
30
32
|
from pyrad.dictionary import Dictionary
|
|
31
33
|
import pyrad.packet
|
|
32
34
|
|
|
33
|
-
srv = Client(
|
|
34
|
-
|
|
35
|
+
srv = Client(
|
|
36
|
+
server="localhost",
|
|
37
|
+
secret=b"Kah3choteereethiejeimaeziecumi",
|
|
38
|
+
dict=Dictionary("dictionary"),
|
|
39
|
+
)
|
|
35
40
|
|
|
36
41
|
# create request
|
|
37
|
-
req = srv.CreateAuthPacket(
|
|
38
|
-
|
|
42
|
+
req = srv.CreateAuthPacket(
|
|
43
|
+
code=pyrad.packet.AccessRequest, User_Name="wichert", NAS_Identifier="localhost"
|
|
44
|
+
)
|
|
39
45
|
req["User-Password"] = req.PwCrypt("password")
|
|
40
46
|
|
|
41
47
|
# send request
|
|
@@ -47,9 +53,8 @@ Here is an example of doing a authentication request::
|
|
|
47
53
|
print("access denied")
|
|
48
54
|
|
|
49
55
|
print("Attributes returned by server:")
|
|
50
|
-
for
|
|
51
|
-
print("
|
|
52
|
-
|
|
56
|
+
for key, val in reply.items():
|
|
57
|
+
print(f"{key}: {val}")
|
|
53
58
|
|
|
54
59
|
|
|
55
60
|
Requirements & Installation
|
|
@@ -57,9 +57,9 @@ author = u'Christian Giese <gic@gicnet.de>, Istvan Ruzman <istvan@ruzman.eu> and
|
|
|
57
57
|
# built documents.
|
|
58
58
|
#
|
|
59
59
|
# The short X.Y version.
|
|
60
|
-
version = u'2.5.
|
|
60
|
+
version = u'2.5.4'
|
|
61
61
|
# The full version, including alpha/beta/rc tags.
|
|
62
|
-
release = u'2.5.
|
|
62
|
+
release = u'2.5.4'
|
|
63
63
|
|
|
64
64
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
65
65
|
# for a list of supported languages.
|
|
@@ -134,7 +134,7 @@ latex_elements = {
|
|
|
134
134
|
# author, documentclass [howto, manual, or own class]).
|
|
135
135
|
latex_documents = [
|
|
136
136
|
(master_doc, 'pyrad.tex', u'pyrad Documentation',
|
|
137
|
-
u'Christian Giese and
|
|
137
|
+
u'Christian Giese, Istvan Ruzman and Stefan Lieberth', 'manual'),
|
|
138
138
|
]
|
|
139
139
|
|
|
140
140
|
|
|
@@ -10,8 +10,8 @@ readme = "README.rst"
|
|
|
10
10
|
license = { text = "BSD-3-Clause" }
|
|
11
11
|
requires-python = ">=3.8"
|
|
12
12
|
authors = [
|
|
13
|
-
{ name = "Istvan Ruzman", email = "istvan@ruzman.eu" },
|
|
14
13
|
{ name = "Christian Giese", email = "gic@gicnet.de" },
|
|
14
|
+
{ name = "Istvan Ruzman", email = "istvan@ruzman.eu" },
|
|
15
15
|
{ name = "Stefan Lieberth", email = "stefan@lieberth.net" },
|
|
16
16
|
]
|
|
17
17
|
keywords = ["radius", "authentication", "AAA", "accounting", "authorization", "RADIUS"]
|
|
@@ -32,13 +32,11 @@ classifiers = [
|
|
|
32
32
|
|
|
33
33
|
dependencies = [
|
|
34
34
|
"netaddr>=0.8.0",
|
|
35
|
-
"six>=1.16.0",
|
|
36
35
|
]
|
|
37
36
|
|
|
38
37
|
[project.optional-dependencies]
|
|
39
38
|
test = [
|
|
40
39
|
"pytest>=8",
|
|
41
|
-
"mock; python_version<'3.10'",
|
|
42
40
|
]
|
|
43
41
|
docs = [
|
|
44
42
|
"sphinx>=7.0",
|
|
@@ -56,7 +54,7 @@ include-package-data = true
|
|
|
56
54
|
zip-safe = true
|
|
57
55
|
|
|
58
56
|
[tool.pytest.ini_options]
|
|
59
|
-
testpaths = ["tests"]
|
|
57
|
+
testpaths = ["pyrad/tests"]
|
|
60
58
|
addopts = "-ra"
|
|
61
59
|
|
|
62
60
|
[tool.setuptools.dynamic]
|
|
@@ -41,6 +41,6 @@ __docformat__ = 'epytext en'
|
|
|
41
41
|
__author__ = 'Christian Giese <gic@gicnet.de>, Istvan Ruzman <istvan@ruzman.eu> and Stefan Lieberth <stefan@lieberth.net>'
|
|
42
42
|
__url__ = 'http://pyrad.readthedocs.io/en/latest/?badge=latest'
|
|
43
43
|
__copyright__ = 'Copyright 2002-2026 Wichert Akkerman, Christian Giese, Istvan Ruzman and Stefan Lieberth. All rights reserved.'
|
|
44
|
-
__version__ = '2.5.
|
|
44
|
+
__version__ = '2.5.4'
|
|
45
45
|
|
|
46
46
|
__all__ = ['client', 'dictionary', 'packet', 'server', 'tools', 'dictfile']
|
|
@@ -9,7 +9,6 @@ import select
|
|
|
9
9
|
import socket
|
|
10
10
|
import time
|
|
11
11
|
import struct
|
|
12
|
-
import six
|
|
13
12
|
from pyrad import host
|
|
14
13
|
from pyrad import packet
|
|
15
14
|
|
|
@@ -35,7 +34,7 @@ class Client(host.Host):
|
|
|
35
34
|
:type timeout: float
|
|
36
35
|
"""
|
|
37
36
|
def __init__(self, server, authport=1812, acctport=1813,
|
|
38
|
-
coaport=3799, secret=
|
|
37
|
+
coaport=3799, secret=b'', dict=None, retries=3, timeout=5, enforce_ma=False):
|
|
39
38
|
"""Constructor.
|
|
40
39
|
|
|
41
40
|
:param server: hostname or IP address of RADIUS server
|
|
@@ -11,7 +11,7 @@ RADIUS $INCLUDE directives behind the scene.
|
|
|
11
11
|
import os
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class _Node
|
|
14
|
+
class _Node:
|
|
15
15
|
"""Dictionary file node
|
|
16
16
|
|
|
17
17
|
A single dictionary file.
|
|
@@ -36,7 +36,7 @@ class _Node(object):
|
|
|
36
36
|
return self.lines[self.current - 1]
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
class DictFile
|
|
39
|
+
class DictFile:
|
|
40
40
|
"""Dictionary file class
|
|
41
41
|
|
|
42
42
|
An iterable file type that handles $INCLUDE
|
|
@@ -111,4 +111,3 @@ class DictFile(object):
|
|
|
111
111
|
else:
|
|
112
112
|
return line
|
|
113
113
|
raise StopIteration
|
|
114
|
-
next = __next__ # BBB for python <3
|
|
@@ -133,7 +133,7 @@ class Attribute(object):
|
|
|
133
133
|
self.values.Add(key, value)
|
|
134
134
|
|
|
135
135
|
|
|
136
|
-
class Dictionary
|
|
136
|
+
class Dictionary:
|
|
137
137
|
"""RADIUS dictionary class.
|
|
138
138
|
This class stores all information about vendors, attributes and their
|
|
139
139
|
values as defined in RADIUS dictionary files.
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
from collections import OrderedDict
|
|
8
8
|
from pyrad import tools
|
|
9
|
+
import hashlib
|
|
9
10
|
import hmac
|
|
10
11
|
import struct
|
|
11
|
-
import sys
|
|
12
12
|
|
|
13
13
|
try:
|
|
14
14
|
import secrets
|
|
@@ -18,24 +18,6 @@ except ImportError:
|
|
|
18
18
|
random_generator = random.SystemRandom()
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def _hmac_md5(*args, **kwargs):
|
|
22
|
-
"""Py3 hmac.new() wrapper with explicit MD5 digestmod."""
|
|
23
|
-
return hmac.new(*args, digestmod='MD5', **kwargs)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if sys.version_info >= (3, 0):
|
|
27
|
-
hmac_new = _hmac_md5
|
|
28
|
-
else:
|
|
29
|
-
hmac_new = hmac.new
|
|
30
|
-
|
|
31
|
-
try:
|
|
32
|
-
import hashlib
|
|
33
|
-
md5_constructor = hashlib.md5
|
|
34
|
-
except ImportError:
|
|
35
|
-
# BBB for python 2.4
|
|
36
|
-
import md5
|
|
37
|
-
md5_constructor = md5.new
|
|
38
|
-
|
|
39
21
|
# Packet codes
|
|
40
22
|
AccessRequest = 1
|
|
41
23
|
AccessAccept = 2
|
|
@@ -145,7 +127,7 @@ class Packet(OrderedDict):
|
|
|
145
127
|
return self.message_authenticator
|
|
146
128
|
|
|
147
129
|
def _refresh_message_authenticator(self):
|
|
148
|
-
hmac_constructor =
|
|
130
|
+
hmac_constructor = hmac.new(self.secret, digestmod='MD5')
|
|
149
131
|
|
|
150
132
|
# Maintain a zero octets content for md5 and hmac calculation.
|
|
151
133
|
self['Message-Authenticator'] = 16 * b'\00'
|
|
@@ -212,7 +194,7 @@ class Packet(OrderedDict):
|
|
|
212
194
|
header = struct.pack('!BBH', self.code, self.id,
|
|
213
195
|
(20 + len(attr)))
|
|
214
196
|
|
|
215
|
-
hmac_constructor =
|
|
197
|
+
hmac_constructor = hmac.new(key, digestmod='MD5')
|
|
216
198
|
hmac_constructor.update(header)
|
|
217
199
|
if self.code in (AccountingRequest, DisconnectRequest,
|
|
218
200
|
CoARequest, AccountingResponse):
|
|
@@ -419,8 +401,8 @@ class Packet(OrderedDict):
|
|
|
419
401
|
attr = self._PktEncodeAttributes()
|
|
420
402
|
header = struct.pack('!BBH', self.code, self.id, (20 + len(attr)))
|
|
421
403
|
|
|
422
|
-
authenticator =
|
|
423
|
-
|
|
404
|
+
authenticator = hashlib.md5(header[0:4] + self.authenticator
|
|
405
|
+
+ attr + self.secret).digest()
|
|
424
406
|
|
|
425
407
|
return header + authenticator + attr
|
|
426
408
|
|
|
@@ -440,8 +422,8 @@ class Packet(OrderedDict):
|
|
|
440
422
|
# response attributes if any, followed by the shared secret. The
|
|
441
423
|
# resulting 16 octet MD5 hash value is stored in the Authenticator
|
|
442
424
|
# field of the Accounting-Response packet.
|
|
443
|
-
hash =
|
|
444
|
-
|
|
425
|
+
hash = hashlib.md5(rawreply[0:4] + self.authenticator +
|
|
426
|
+
rawreply[20:] + self.secret).digest()
|
|
445
427
|
|
|
446
428
|
if hash != rawreply[4:20]:
|
|
447
429
|
return False
|
|
@@ -595,7 +577,7 @@ class Packet(OrderedDict):
|
|
|
595
577
|
else:
|
|
596
578
|
last = self.authenticator + salt
|
|
597
579
|
while data:
|
|
598
|
-
hash =
|
|
580
|
+
hash = hashlib.md5(self.secret + last).digest()
|
|
599
581
|
for i in range(16):
|
|
600
582
|
result += bytes((hash[i] ^ data[i],))
|
|
601
583
|
|
|
@@ -707,11 +689,12 @@ class AuthPacket(Packet):
|
|
|
707
689
|
header = struct.pack(
|
|
708
690
|
'!BBH16s', self.code, self.id, (20 + 18 + len(attr)), self.authenticator
|
|
709
691
|
)
|
|
710
|
-
digest =
|
|
692
|
+
digest = hmac.new(
|
|
711
693
|
self.secret,
|
|
712
694
|
header
|
|
713
695
|
+ attr
|
|
714
696
|
+ struct.pack('!BB16s', 80, struct.calcsize('!BB16s'), b''),
|
|
697
|
+
digestmod='MD5'
|
|
715
698
|
).digest()
|
|
716
699
|
return (
|
|
717
700
|
header
|
|
@@ -743,7 +726,7 @@ class AuthPacket(Packet):
|
|
|
743
726
|
|
|
744
727
|
last = self.authenticator
|
|
745
728
|
while buf:
|
|
746
|
-
hash =
|
|
729
|
+
hash = hashlib.md5(self.secret + last).digest()
|
|
747
730
|
for i in range(16):
|
|
748
731
|
pw += bytes((hash[i] ^ buf[i],))
|
|
749
732
|
(last, buf) = (buf[:16], buf[16:])
|
|
@@ -787,7 +770,7 @@ class AuthPacket(Packet):
|
|
|
787
770
|
|
|
788
771
|
last = self.authenticator
|
|
789
772
|
while buf:
|
|
790
|
-
hash =
|
|
773
|
+
hash = hashlib.md5(self.secret + last).digest()
|
|
791
774
|
for i in range(16):
|
|
792
775
|
result += bytes((hash[i] ^ buf[i],))
|
|
793
776
|
last = result[-16:]
|
|
@@ -820,7 +803,7 @@ class AuthPacket(Packet):
|
|
|
820
803
|
challenge = self.authenticator
|
|
821
804
|
if 'CHAP-Challenge' in self:
|
|
822
805
|
challenge = self['CHAP-Challenge'][0]
|
|
823
|
-
return password ==
|
|
806
|
+
return password == hashlib.md5(chapid + userpwd + challenge).digest()
|
|
824
807
|
|
|
825
808
|
def VerifyAuthRequest(self):
|
|
826
809
|
"""Verify request authenticator.
|
|
@@ -829,8 +812,8 @@ class AuthPacket(Packet):
|
|
|
829
812
|
:rtype: boolean
|
|
830
813
|
"""
|
|
831
814
|
assert (self.raw_packet)
|
|
832
|
-
hash =
|
|
833
|
-
|
|
815
|
+
hash = hashlib.md5(self.raw_packet[0:4] + 16 * b'\x00' +
|
|
816
|
+
self.raw_packet[20:] + self.secret).digest()
|
|
834
817
|
return hash == self.authenticator
|
|
835
818
|
|
|
836
819
|
|
|
@@ -873,8 +856,8 @@ class AcctPacket(Packet):
|
|
|
873
856
|
"""
|
|
874
857
|
assert (self.raw_packet)
|
|
875
858
|
|
|
876
|
-
hash =
|
|
877
|
-
|
|
859
|
+
hash = hashlib.md5(self.raw_packet[0:4] + 16 * b'\x00' +
|
|
860
|
+
self.raw_packet[20:] + self.secret).digest()
|
|
878
861
|
|
|
879
862
|
return hash == self.authenticator
|
|
880
863
|
|
|
@@ -895,8 +878,8 @@ class AcctPacket(Packet):
|
|
|
895
878
|
|
|
896
879
|
attr = self._PktEncodeAttributes()
|
|
897
880
|
header = struct.pack('!BBH', self.code, self.id, (20 + len(attr)))
|
|
898
|
-
self.authenticator =
|
|
899
|
-
|
|
881
|
+
self.authenticator = hashlib.md5(header[0:4] + 16 * b'\x00' +
|
|
882
|
+
attr + self.secret).digest()
|
|
900
883
|
|
|
901
884
|
ans = header + self.authenticator + attr
|
|
902
885
|
|
|
@@ -941,8 +924,8 @@ class CoAPacket(Packet):
|
|
|
941
924
|
:rtype: boolean
|
|
942
925
|
"""
|
|
943
926
|
assert (self.raw_packet)
|
|
944
|
-
hash =
|
|
945
|
-
|
|
927
|
+
hash = hashlib.md5(self.raw_packet[0:4] + 16 * b'\x00' +
|
|
928
|
+
self.raw_packet[20:] + self.secret).digest()
|
|
946
929
|
return hash == self.authenticator
|
|
947
930
|
|
|
948
931
|
def RequestPacket(self):
|
|
@@ -960,14 +943,14 @@ class CoAPacket(Packet):
|
|
|
960
943
|
self.id = self.CreateID()
|
|
961
944
|
|
|
962
945
|
header = struct.pack('!BBH', self.code, self.id, (20 + len(attr)))
|
|
963
|
-
self.authenticator =
|
|
964
|
-
|
|
946
|
+
self.authenticator = hashlib.md5(header[0:4] + 16 * b'\x00' +
|
|
947
|
+
attr + self.secret).digest()
|
|
965
948
|
|
|
966
949
|
if self.message_authenticator:
|
|
967
950
|
self._refresh_message_authenticator()
|
|
968
951
|
attr = self._PktEncodeAttributes()
|
|
969
|
-
self.authenticator =
|
|
970
|
-
|
|
952
|
+
self.authenticator = hashlib.md5(header[0:4] + 16 * b'\x00' +
|
|
953
|
+
attr + self.secret).digest()
|
|
971
954
|
|
|
972
955
|
return header + self.authenticator + attr
|
|
973
956
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import hashlib
|
|
1
2
|
import hmac
|
|
2
3
|
import os
|
|
3
4
|
import struct
|
|
@@ -9,13 +10,6 @@ from collections import OrderedDict
|
|
|
9
10
|
from pyrad import packet
|
|
10
11
|
from pyrad.client import Client
|
|
11
12
|
from pyrad.dictionary import Dictionary
|
|
12
|
-
try:
|
|
13
|
-
import hashlib
|
|
14
|
-
md5_constructor = hashlib.md5
|
|
15
|
-
except ImportError:
|
|
16
|
-
# BBB for python 2.4
|
|
17
|
-
import md5
|
|
18
|
-
md5_constructor = md5.new
|
|
19
13
|
|
|
20
14
|
|
|
21
15
|
class UtilityTests(unittest.TestCase):
|
|
@@ -106,17 +100,17 @@ class PacketTests(unittest.TestCase):
|
|
|
106
100
|
request.id, (20 + len(attributes)))
|
|
107
101
|
|
|
108
102
|
# Calculate the Message-Authenticator and update the attribute
|
|
109
|
-
hmac_constructor = hmac.new(request.secret, None,
|
|
103
|
+
hmac_constructor = hmac.new(request.secret, None, hashlib.md5)
|
|
110
104
|
hmac_constructor.update(header + request.authenticator + attributes)
|
|
111
105
|
updated_message_authenticator = hmac_constructor.digest()
|
|
112
106
|
attributes = attributes.replace(b'\x00' * 16,
|
|
113
107
|
updated_message_authenticator)
|
|
114
108
|
|
|
115
109
|
# Calculate the response authenticator
|
|
116
|
-
authenticator =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
authenticator = hashlib.md5(header
|
|
111
|
+
+ request.authenticator
|
|
112
|
+
+ attributes
|
|
113
|
+
+ request.secret).digest()
|
|
120
114
|
|
|
121
115
|
reply_bytes = header + authenticator + attributes
|
|
122
116
|
return packet.AuthPacket(packet=reply_bytes, dict=self.dict)
|
|
@@ -554,7 +548,7 @@ class AuthPacketChapTests(unittest.TestCase):
|
|
|
554
548
|
def testVerifyChapPasswd(self):
|
|
555
549
|
chap_id = b'9'
|
|
556
550
|
chap_challenge = b'987654321'
|
|
557
|
-
chap_password = chap_id +
|
|
551
|
+
chap_password = chap_id + hashlib.md5(
|
|
558
552
|
chap_id + b'test_password' + chap_challenge).digest()
|
|
559
553
|
pkt = self.client.CreateAuthPacket(
|
|
560
554
|
code=packet.AccessChallenge,
|
|
@@ -677,3 +671,9 @@ class AcctPacketTests(unittest.TestCase):
|
|
|
677
671
|
|
|
678
672
|
# Vendor unknown preserved
|
|
679
673
|
self.assertEqual(pkt[(594, 1)], [b'UNKNOWN_PRODUCT'])
|
|
674
|
+
|
|
675
|
+
raw_no_authenticator = raw[:4] + b"\x00" * 16 + raw[20:]
|
|
676
|
+
rebuilt = pkt.RequestPacket()
|
|
677
|
+
rebuilt_no_authenticator = rebuilt[:4] + b"\x00" * 16 + rebuilt[20:]
|
|
678
|
+
|
|
679
|
+
self.assertEqual(raw_no_authenticator, rebuilt_no_authenticator)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyrad
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.4
|
|
4
4
|
Summary: RADIUS tools
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Christian Giese <gic@gicnet.de>, Istvan Ruzman <istvan@ruzman.eu>, Stefan Lieberth <stefan@lieberth.net>
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
Project-URL: Repository, https://github.com/pyradius/pyrad
|
|
8
8
|
Project-URL: Documentation, https://pyradius-pyrad.readthedocs.io
|
|
@@ -23,10 +23,8 @@ Requires-Python: >=3.8
|
|
|
23
23
|
Description-Content-Type: text/x-rst
|
|
24
24
|
License-File: LICENSE.txt
|
|
25
25
|
Requires-Dist: netaddr>=0.8.0
|
|
26
|
-
Requires-Dist: six>=1.16.0
|
|
27
26
|
Provides-Extra: test
|
|
28
27
|
Requires-Dist: pytest>=8; extra == "test"
|
|
29
|
-
Requires-Dist: mock; python_version < "3.10" and extra == "test"
|
|
30
28
|
Provides-Extra: docs
|
|
31
29
|
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
32
30
|
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
@@ -59,18 +57,24 @@ pyrad is an implementation of a RADIUS client/server as described in RFC2865.
|
|
|
59
57
|
It takes care of all the details like building RADIUS packets, sending
|
|
60
58
|
them and decoding responses.
|
|
61
59
|
|
|
62
|
-
Here is an example of doing a authentication request
|
|
60
|
+
Here is an example of doing a authentication request:
|
|
61
|
+
|
|
62
|
+
.. code-block:: python
|
|
63
63
|
|
|
64
64
|
from pyrad.client import Client
|
|
65
65
|
from pyrad.dictionary import Dictionary
|
|
66
66
|
import pyrad.packet
|
|
67
67
|
|
|
68
|
-
srv = Client(
|
|
69
|
-
|
|
68
|
+
srv = Client(
|
|
69
|
+
server="localhost",
|
|
70
|
+
secret=b"Kah3choteereethiejeimaeziecumi",
|
|
71
|
+
dict=Dictionary("dictionary"),
|
|
72
|
+
)
|
|
70
73
|
|
|
71
74
|
# create request
|
|
72
|
-
req = srv.CreateAuthPacket(
|
|
73
|
-
|
|
75
|
+
req = srv.CreateAuthPacket(
|
|
76
|
+
code=pyrad.packet.AccessRequest, User_Name="wichert", NAS_Identifier="localhost"
|
|
77
|
+
)
|
|
74
78
|
req["User-Password"] = req.PwCrypt("password")
|
|
75
79
|
|
|
76
80
|
# send request
|
|
@@ -82,9 +86,8 @@ Here is an example of doing a authentication request::
|
|
|
82
86
|
print("access denied")
|
|
83
87
|
|
|
84
88
|
print("Attributes returned by server:")
|
|
85
|
-
for
|
|
86
|
-
print("
|
|
87
|
-
|
|
89
|
+
for key, val in reply.items():
|
|
90
|
+
print(f"{key}: {val}")
|
|
88
91
|
|
|
89
92
|
|
|
90
93
|
Requirements & Installation
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|