btimby / py-radius

RADIUS authentication module
Other
62 stars 38 forks source link

Attributes? #4

Closed ronsmith closed 7 years ago

ronsmith commented 11 years ago

There doesn't seem to be any way to set attributes on the authenticate request. Am I just missing it?

btimby commented 11 years ago

Ron, I am the maintainer for this project, not the original author. I moved it to github, and fixed some bugs. I am not as well-versed in RADIUS as the original author surely is. However, I am happy to help if you can provide some more detail about what you are looking to do.

ronsmith commented 11 years ago

The spec (see section 5) allows for a number of attributes to be passed in the authentication packet that is sent to the RADIUS server. These attributes can be used by the RADIUS server admin to create additional limitations on authentication. For example, at my company, they are using the attribute NAS-Port-Type to check if the authentication request is coming from Wireless or Ethernet. It looks like the current code does not do anything with attributes at all.

They have given me a temporary workaround by allowing my app to authenticate without passing any attributes so, if I can figure out how all that struct.pack stuff is working in the current code (just isn't something I have experience with yet) I might be able to add support for attributes myself (and I would have happy to contribute it to the project if I do). I was just kinda hoping that it was already in there somewhere and I was missing it.

On Mon, Jan 7, 2013 at 2:10 PM, Ben Timby notifications@github.com wrote:

Ron, I am the maintainer for this project, not the original author. I moved it to github, and fixed some bugs. I am not as well-versed in RADIUS as the original author surely is. However, I am happy to help if you can provide some more detail about what you are looking to do.

— Reply to this email directly or view it on GitHubhttps://github.com/btimby/py-radius/issues/4#issuecomment-11969064.

btimby commented 11 years ago

Ron, Great information. I will look at the spec.

For the purposes of struct.pack(), it is useful to think in terms of C structs. Think of a memory buffer, or binary file structure. Let's say you need a header, the header consists of a magic byte, a version number byte, and a record count. Let's assume the records are fixed-length. The struct module provides a way to use a familiar format-string type interface for reading or writing data in such cases. The receiver of such data (be it in a file or packet) could use the struct module to extract these fields then read all the records. For the most part, network protocols and file structures are text-based. This makes them easier to develop, troubleshoot and maintain. Older protocols tend toward binary formats, which save bytes.

Struct also provides control over the byte order etc. This way you can easily convert network packets, binary file contents to Python native types, and also write Python native types to files or network packets. The module uses a format string to define the native data type, and tuples for the packed values. So the format string 'hhi' would be appropriate for our hypothethetical header (byte, byte, integer). To write that header, file.write(struct.pack('hhi', magic, version, record_count)). To read it: magic, version, record_count = struct.unpack('hhi', file.read(struct.calcsize('hhi'))). The call to calcsize returns the size of the format string in bytes, so you know how many to read before unpacking.

For the purposes of this library, struct is used to construct the RADIUS packets so that they conform to spec. It is also used to unpack the replies.

btimby commented 11 years ago

Here is a link to the documentation for RADIUS attributes.

http://tools.ietf.org/html/rfc2865#section-5

btimby commented 7 years ago

I have rewritten this library and it now fully supports Attributes. You can reference them by name or by constants defined in the lib. Here is an example of authenticating using an Attribute.

import radius

radius.authenticate('secret', 'username', 'password', attributes={
    'Service-Type': 'foo'
})
# -- or --
radius.authenticate('secret', 'username', 'password', attributes={
    radius.ATTR_SERVICE_TYPE: 'foo'
})

Some caveats are:

Example low-level usage:

import radius

r = radius.Radius('secret', 'host', 1812)

# access_request() is a shortcut, tou could optionally build your own radius.Message().
m = radius.access_request('secret', 'username', 'password')

with r.connect() as c:
    # Serialize and send the data.
    c.send(m.pack())

    # This blocks!
    recv = c.recv(radius.MAX_PACKET)

    # This validates that the reply matches the request.
    m = m.verify(recv)

print(m.code)
print(m.attributes.items())

Of course you must poll or wait for the reply using select() or similar if you don't want to block forever. See radius.py for more details on the low-level API.