RADIUS Server iRule

Problem this snippet solves:

An iRule that implements a basic RADIUS service in an iRule. Users are configured statically via a data group. I wrote this iRule as an exercise to further my knowledge of both iRules and the RADIUS protocol. It is not intended for production usage, and as such has not been volume tested.

Note that the functions are all implemented using binary scans, and not the iRule RADIUS:: functions, that were not available at the time when I wrote it.

Code :

class /Common/radiusd_users {
   {
      "test@f5.com" { "LLev3ZY2n0oq6Bcst6/dljafSiroF8cg/X9iQkNVen1WohflM8QForUaayxDaA==" } # pw = password
   }
}

rule /Common/radiusd_rule {

when RULE_INIT {
  set radius_secret "mysecret"
  set static::aes_key "AES 128 63544a5e7178677b45366b41405f2dab"
  binary scan $radius_secret H* static::hexsecret
  set static::hexsecretlen [string length $static::hexsecret]
}

when CLIENT_ACCEPTED {

  # request
  set q_payload [UDP::payload]
  binary scan $q_payload cH2SH32a* q_code q_id q_len q_auth q_avp
  set q_avplen [expr $q_len - 20]
  set start_index 0
  set q_var2 0   
  while {[expr $q_avplen - $start_index] > 4} {
     binary scan $q_avp @${start_index}xc q_var2
     set q_var4 [expr $q_var2 -2]  
     binary scan $q_avp @${start_index}cxa${q_var4} q_var1 q_var3
     if {$q_var1 eq "1"} {
       set q_username $q_var3
     }
     if {$q_var1 eq "2"} {
       set q_userpass $q_var3
     }
     set start_index [expr $start_index + $q_var2]
     #log local0. "attr_type $q_var1 attr_len $q_var2 val_len $q_var4 val $q_var3"
     #log local0. "q_avplen $q_avplen new_offset $start_index rem [expr $q_avplen - $start_index]"
  }  
  log local0. "q_code $q_code id $q_id len $q_len auth $q_auth q_avp $q_avp q_username $q_username q_userpass $q_userpass "
  UDP::drop

  # answer
  set a_code 3
  if { [class match $q_username contains radiusd_users] } {
    binary scan $q_userpass H* a_userpass
    binary scan [md5 [binary format H*H* $static::hexsecret $q_auth]] c* vl1
    binary scan [binary format H* $a_userpass] c* vl2
    set res {}
    foreach v1 $vl1 v2 [lrange $vl2 0 15] {
      append res [binary format c [expr {$v1 ^ $v2}]]
    }
    set res [string trimright $res \u0000]
    if { [AES::decrypt $static::aes_key [b64decode [class match -value $q_username equals radiusd_users]]] eq $res } {
      set a_code 2
    }
  }
  set a_id $q_id
  set a_auth $q_auth
  set a_avp ""
  set a_len 20
  set a_var1 [binary format cH2SH32H$static::hexsecretlen $a_code $a_id $a_len $a_auth $static::hexsecret ]  
  set a_var2 [md5 $a_var1]
  binary scan $a_var2 H* a_auth
  set a_payload [binary format cH2SH32 $a_code $a_id $a_len $a_auth ]
  binary scan $a_payload cH2SH32 a_code a_id a_len a_auth a_avp
  #log local0. "a_code $a_code id $a_id len $a_len auth $a_auth "
  UDP::respond $a_payload

}


}
Published Mar 18, 2015
Version 1.0

Was this article helpful?

No CommentsBe the first to comment