Forum Discussion

John_Allen's avatar
John_Allen
Icon for Altostratus rankAltostratus
Dec 18, 2011

Creating a proper RADIUS Accounting-Response packet in iRules

Creating a proper RADIUS Accounting-Response packet in iRules

 

 

If you do a lot of work with RADIUS messages being sent to your BIGIP so that you can get some information from another network node in the system, you are going to need to respond back to that node in a correct and proper way, so that you don’t mess it up, or otherwise fill up its error log with ‘improper response’ messages.

 

I was working on just that kind of iRule, and got it working. Here’s the code:

 


 1: 
 2:  RADIUS Acct-Resp packet
 3: 
 4:  input variables:
 5:   $rId     -> RADIUS Request ID field
 6:   $rAuth   -> RADIUS Request Authorization field
 7:   $static::SHARED_SECRET -> RADIUS Shared secret of the 2 nodes.
 8: 
 9: UDP::drop           ; kill the incoming packet, don’t need it.                                      
10: set RADcode 5       ; RADIUS Accounting-Response Type code.
11: set md5hash [ format "05%02x00%02x" $rId 20 ]           
12: set md5hash $md5hash$rAuth$static::SHARED_SECRET        
13: set hash [ binary format H* $md5hash ]                  
14: set respAuth [md5 $hash]                                
15: set payldhdr [ binary format ccS $RADcode $rId 20 ]     
16: set payload $payldhdr$respAuth                          
17: UDP::respond $payload 

 

So the RADIUS Accounting-Response packet has, at a minimum, four fields: RADIUS Type code(1 byte), ID number(1 byte), Packet Length(2 byte Integer), and the Authenticator(16 bytes). Getting the response authenticator correct is really the only tricky part.

 

Line 5: The RADIUS Request ID field comes from the incoming packet. You can retrieve this value previously in the iRule using a line of code like this: set rId [RADIUS::id]. Of course, this value can also be pulled out at the same time some other important fields are decoded using the sample code for line 6 below.

 

 

Line 6: The RADIUS Request Authorization field in the incoming packet is an MD5 hash of most if the incoming packet. To get this value, you will need to do a binary scan of the UDP packet:

 

set payload [UDP::payload]

 

binary scan $payload ccSH32a* rType rId rLen rAuth rPkt

 

In this example, we also pull out the Type Code (rType) and the packet ID (rId), the length of the overall packet, the Authorization field that we are looking for, and the rest of the packet, which should be all the AVP fields.

 

 

Line 9: We don’t need (and usually don’t want) the RADIUS Request packet from going on from the BIGIP, so we just drop the packet to prevent this.

 

 

Line 10: 0x05 is the RADIUS Type Code for an Account-Response packet.

 

 

Line 11: Here’s where we start working on our response authenticator. We are going to setup a hexstring (which is hex characters displayed as a string; IE> “05140016” is four bytes 0x05, 0x14, 0x00, 0x16), pack all of our values into it, convert it to real binary hex, do the MD5 hash on the result, and use the MD5 checksum result in the response packet. This line sets Type Code, id, and Length (hard coded to 20 bytes).

 

 

Line 12: Using the hexstring from the last line, it adds the Original request Authenticator, and the shared secret (which should already be in hexstring format) to it. We now have our complete hexstring that needs to be run through the MD5 checksum function.

 

 

Line 13: This line simply converts the hexstring to a binary hex value.

 

 

Line 14: We get our valid Authenticator for the response.

 

 

Line 15: Now we create the actual response packet. This line sets the first three fields: Type Code, id, and Length.

 

 

Line 16: We add the header and the response Authenticator and we have our RADIUS Accounting-Response packet.

 

 

Line 17: Send the response back.

 

 

The RADIUS Accounting-Response protocol is documented in RFC-2866, Section 4.2.

 

 

7 Replies

  • this irule shows how to craft a rfc compliant Radius Accouting Accept message

     

    upon an incoming Radius Accouting Request

     

    christian@f5.com

     

     

     

     

    when RULE_INIT {

     

    set static::secret "mysecret"

     

    binary scan $static::secret H* static::secrethex

     

    }

     

     

     

     

    when CLIENT_DATA {

     

     

    getting base information, see also RFC 2865

     

    0 1 2 3

     

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

     

    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

     

    | Code | Identifier | Length |

     

    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

     

    | Authenticator |

     

    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

     

    | Attributes ...

     

    +-+-+-+-+-+-+-+-+-+-+-+-+-

     

    binary scan [UDP::payload] cH2SH32 code ident len auth

     

    see also http://tmml.sourceforge.net/doc/tcl/binary.html

     

     

     

    Checking if Radius Code is 4

     

    4 Accounting-Request

     

     

    if { $code == 4 } {

     

     

    setting response code

     

    5 Accounting-Response

     

    set code 5

     

     

    Accounting Response will be 20 bytes long

     

    set len 20

     

     

    creating ResponseAuth =

     

    MD5(Code+ID+Length+RequestAuth+Attributes+Secret)

     

     

    set md5me [binary format cH2SH32H8 $code $ident $len $auth $static::secrethex]

     

     

    running the MD5

     

    set ResponseAuthRaw [ md5 $md5me]

     

    doing some string to binary conversion, i am sure i can avoid this, need to optimize it

     

    binary scan $ResponseAuthRaw H* ResponseAuth

     

    crafting the response packet

     

    set packetdata [binary format cH2SH32 $code $ident $len $ResponseAuth]

     

    UDP::drop

     

    clientside { UDP::respond ${packetdata} }

     

    }

     

    else {

     

    log local0. "Dropping Message"

     

    UDP::drop

     

    }

     

    }
  • Alex_D's avatar
    Alex_D
    Icon for Nimbostratus rankNimbostratus
    Very useful!

     

     

    I had a problem with it only using the first 4 characters of the shared secret. Made the adjustments as per below. Someone might have a more elegant way.

     

     

    Added to RULE_INIT after set static::secret:

     

    set static::secretlen [expr 2 * [string length $static::secret]]

     

     

    Replaced existing set md5me line with:

     

    set md5me [binary format cH2SH32H$static::secretlen $code $ident $len $auth $static::secrethex]
  • Hi all,

     

    first of all, great job on this one, works flawlessly 🙂 i'm trying to understand couple of things here, and i would appreciate your help ,I could not understand what are the following steps are needed :

     

    set md5me [binary format cH2SH32H$static::secretlen $code $ident $len $auth $static::secrethex]
     running the MD5 
    set ResponseAuthRaw [ md5 $md5me] 
     doing some string to binary conversion, i am sure i can avoid this, need to optimize it 
    binary scan $ResponseAuthRaw H* ResponseAuth 
     crafting the response packet 
    set packetdata [binary format cH2SH32 $code $ident $len $ResponseAuth] 
  • he is creating response authenticator.

     

    Response Authenticator
    
             The value of the Authenticator field in Access-Accept, Access-
             Reject, and Access-Challenge packets is called the Response
             Authenticator, and contains a one-way MD5 hash calculated over
             a stream of octets consisting of: the RADIUS packet, beginning
             with the Code field, including the Identifier, the Length, the
             Request Authenticator field from the Access-Request packet, and
             the response Attributes, followed by the shared secret.  That
             is, ResponseAuth =
             MD5(Code+ID+Length+RequestAuth+Attributes+Secret) where +
             denotes concatenation.
    

    Remote Authentication Dial In User Service (RADIUS)

     

    http://tools.ietf.org/html/rfc2865

     

  • why do we need the binary scan?

     

    it converts binary (ResponseAuthRaw) to hex (ResponseAuth) which then is used to craft response (packetdata).

     

  • Good day all,

    Trying to extend the functionality of these iRules. We've got an F5 sitting in front of a ClearPass RADIUS farm for 802.1x. We'd like to send a RADIUS access-accept message if the backend RADIUS are offline (fail open). The Cisco switch we're testing with kicks the following error message in the debug:

    Hashes are not matching it appears. I suspect the issue has to do with the radius attribute being sent to the client. Tried creating the response without it but the result is a malformed packet. Any pointers are greatly appreciated.

     

    when RULE_INIT {
        set static::secret "testing" binary scan $static::secret H* static::secrethex
        } 
    
    when CLIENT_DATA { 
        binary scan [UDP::payload] cH2SH32 code ident len auth 
    
        if { $code == 1 } { 
    
            set code 2 
    
            set len 134
    
            set rad_attr_svr 25
            set rad_attr_len_svr 58
            set rad_attr_string 6bf92bf8920b46e49ca55f519c9194f0bc0b0000000000005230303030303162342d30312d35623030303734340000000000000000000000
    
            set md5me [binary format cH2SH32H8 $code $ident $len $auth $static::secrethex] 
    
            set ResponseAuthRaw [ md5 $md5me] 
    
            binary scan $ResponseAuthRaw H* ResponseAuth 
    
            set packetdata [binary format cH2SH32cca* $code $ident $len $ResponseAuth $rad_attr_svr $rad_attr_len_svr $rad_attr_string] 
    
            UDP::drop 
            clientside { UDP::respond ${packetdata} } 
    
        } else { 
            log local0. "Dropping Message" 
            UDP::drop 
        } 
    }