Class: Radius::Packet
- Inherits:
-
Object
- Object
- Radius::Packet
- Defined in:
- lib/radius/packet.rb
Constant Summary collapse
- VSA_TYPE =
type given to vendor-specific attributes
26
Instance Attribute Summary collapse
-
#attributes ⇒ Object
readonly
Returns the value of attribute attributes.
-
#authenticator ⇒ Object
The 16-byte Authenticator field can be read as a character string with this attribute reader.
-
#code ⇒ Object
The code field is returned as a string.
-
#identifier ⇒ Object
The one-byte Identifier used to match requests and responses is obtained as a character.
Class Method Summary collapse
-
.auth_resp(packed_packet, secret) ⇒ Object
Given a (packed) RADIUS packet and a shared secret, returns a new packet with the authenticator field changed in accordance with RADIUS protocol requirements.
Instance Method Summary collapse
-
#attr(name) ⇒ Object
The value of the named attribute in the object’s internal state can be obtained.
- #check_password(given, secret) ⇒ Object
-
#each ⇒ Object
This method is provided a block which will pass every attribute-value pair currently available.
-
#each_vsa ⇒ Object
This method will pass each vendor-specific attribute available to a passed block.
-
#each_vsaval(vendorid) ⇒ Object
This method is an iterator that passes each vendor-specific attribute associated with a vendor ID.
-
#initialize(dict) ⇒ Packet
constructor
To initialize the object, pass a Radius::Dictionary object to it.
-
#pack ⇒ Object
The Radius::Packet object contains attributes that can be set and altered with the object’s accessor methods, or obtained from the unpack method.
-
#password(secret) ⇒ Object
The RADIUS User-Password attribute is encoded with a shared secret.
-
#set_attr(name, value) ⇒ Object
Changes the value of the named attribute.
-
#set_password(pwdin, secret) ⇒ Object
The RADIUS User-Password attribute is encoded with a shared secret.
-
#set_vsattr(vendorid, name, value) ⇒ Object
Changes the value of the named vendor-specific attribute.
-
#to_s ⇒ Object
This method will convert a RADIUS packet into a printable string.
-
#unpack(data) ⇒ Object
Given a raw RADIUS packet
data, unpacks its contents so it can be analyzed with other methods, (e.g.code,attr, etc.). -
#unset_all ⇒ Object
Undefines all regular and vendor-specific attributes.
-
#unset_all_attr ⇒ Object
Undefines all attributes.
-
#unset_all_vsattr ⇒ Object
Undefines all vendor-specific attributes.
-
#unset_attr(name) ⇒ Object
Undefines the current value of the named attribute.
-
#unset_vsattr(vendorid, name) ⇒ Object
Undefines the current value of the named vendor-specific attribute.
-
#vsattr(vendorid, name) ⇒ Object
This method obtains the value of a vendor-specific attribute, given the vendor ID and the name of the vendor-specific attribute.
Constructor Details
#initialize(dict) ⇒ Packet
To initialize the object, pass a Radius::Dictionary object to it.
75 76 77 78 79 |
# File 'lib/radius/packet.rb', line 75 def initialize(dict) @dict = dict @attributes = Hash.new(nil) @vsattributes = Array.new end |
Instance Attribute Details
#attributes ⇒ Object (readonly)
Returns the value of attribute attributes.
72 73 74 |
# File 'lib/radius/packet.rb', line 72 def attributes @attributes end |
#authenticator ⇒ Object
The 16-byte Authenticator field can be read as a character string with this attribute reader.
67 68 69 |
# File 'lib/radius/packet.rb', line 67 def authenticator @authenticator end |
#code ⇒ Object
The code field is returned as a string. As of this writing, the following codes are recognized:
Access-Request Access-Accept
Access-Reject Accounting-Request
Accounting-Response Access-Challenge
Status-Server Status-Client
51 52 53 |
# File 'lib/radius/packet.rb', line 51 def code @code end |
#identifier ⇒ Object
The one-byte Identifier used to match requests and responses is obtained as a character.
59 60 61 |
# File 'lib/radius/packet.rb', line 59 def identifier @identifier end |
Class Method Details
.auth_resp(packed_packet, secret) ⇒ Object
Given a (packed) RADIUS packet and a shared secret, returns a new packet with the authenticator field changed in accordance with RADIUS protocol requirements.
Parameters
packed_packet-
The packed packet to compute a new Authenticator field for.
secret-
The shared secret of the RADIUS system.
Return value
a new packed packet with the authenticator field recomputed.
498 499 500 501 502 |
# File 'lib/radius/packet.rb', line 498 def Packet.auth_resp(packed_packet, secret) new = String.new(packed_packet) new[4, 16] = Digest::MD5.digest(packed_packet + secret) return(new) end |
Instance Method Details
#attr(name) ⇒ Object
The value of the named attribute in the object’s internal state can be obtained.
Parameters
name-
the name of the attribute to obtain
Return value:
The value of the attribute is returned.
275 276 277 |
# File 'lib/radius/packet.rb', line 275 def attr(name) return(@attributes[name]) end |
#check_password(given, secret) ⇒ Object
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/radius/packet.rb', line 420 def check_password(given, secret) given += "\000" * (15 - (15 + given.length) % 16) pwdout = "".force_encoding("ASCII-8BIT") lastround = @authenticator 0.step(given.length() -1, 16) do |i| lastround = xor_str(given[i, 16], Digest::MD5.digest(secret + lastround)) pwdout += lastround.force_encoding("ASCII-8BIT") end pwdout.sub(/\000+$/, "") if pwdout actual_password = @attributes["User-Password"].force_encoding("ASCII-8BIT") pwdout == actual_password end |
#each ⇒ Object
This method is provided a block which will pass every attribute-value pair currently available.
261 262 263 264 265 |
# File 'lib/radius/packet.rb', line 261 def each @attributes.each_pair do |key, value| yield(key, value) end end |
#each_vsa ⇒ Object
This method will pass each vendor-specific attribute available to a passed block. The parameters to the block are the vendor ID, the attribute name, and the attribute value.
307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/radius/packet.rb', line 307 def each_vsa @vsattributes.each_index do |vendorid| if @vsattributes[vendorid] != nil @vsattributes[vendorid].each_pair do |key, value| value.each do |val| yield(vendorid, key, val) end end end end end |
#each_vsaval(vendorid) ⇒ Object
This method is an iterator that passes each vendor-specific attribute associated with a vendor ID.
321 322 323 324 325 |
# File 'lib/radius/packet.rb', line 321 def each_vsaval(vendorid) @vsattributes[vendorid].each_pair do |key, value| yield(key, value) end end |
#pack ⇒ Object
The Radius::Packet object contains attributes that can be set and altered with the object’s accessor methods, or obtained from the unpack method. This method will return a raw RADIUS packet that should be suitable for sending to a RADIUS client or server over UDP as per RFC 2138.
Return Value
The RADIUS packet corresponding to the object’s current internal state.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/radius/packet.rb', line 194 def pack hdrlen = 1 + 1 + 2 + 16 # size of packet header p_hdr = "CCna16a*" # pack template for header p_attr = "CCa*" # pack template for attribute p_vsa = "CCNCCa*" # pack template for VSA's p_vsa_3com = "CCNNa*" # used by 3COM devices codes = { 'Access-Request' => 1, 'Access-Accept' => 2, 'Access-Reject' => 3, 'Accounting-Request' => 4, 'Accounting-Response' => 5, 'Access-Challenge' => 11, 'Status-Server' => 12, 'Status-Client' => 13 } attstr = "" each do |attr, value| anum = @dict.attr_num(attr) val = case @dict.attr_type(attr) when "string" then value when "integer" [@dict.attr_has_val(anum) ? @dict.val_num(anum, value) ? @dict.val_num(anum, value) : value : value].pack("N") when "ipaddr" then [inet_aton(value)].pack("N") when "date" then [value].pack("N") when "time" then [value].pack("N") when "octets" then value else next end attstr += [@dict.attr_num(attr), val.length + 2, val].pack(p_attr) end # Pack vendor-specific attributes each_vsa do |vendor, attr, datum| code = @dict.vsattr_num(vendor, attr) vval = case @dict.vsattr_type(vendor, attr) when "string" then datum when "integer" @dict.vsattr_has_val(vendor.to_i, code) ? [@dict.vsaval_num(vendor, code, datum)].pack("N") : [datum].pack("N") when "ipaddr" then inet_aton(datum) when "time" then [datum].pack("N") when "date" then [datum].pack("N") when "octets" then value else next end if vendor == 429 # For 3COM devices attstr += [VSA_TYPE, vval.length + 10, vendor, @dict.vsattr_num(vendor, attr), vval].pack(p_vsa_3com) else attstr += [VSA_TYPE, vval.length + 8, vendor, @dict.vsattr_num(vendor, attr), vval.length + 2, vval].pack(p_vsa) end end return([codes[@code], @identifier, attstr.length + hdrlen, @authenticator, attstr].pack(p_hdr)) end |
#password(secret) ⇒ Object
The RADIUS User-Password attribute is encoded with a shared secret. This method will return the decoded version given the shared secret. This also works when the attribute name is ‘Password’ for compatibility reasons.
Parameters
secret-
The shared secret of the RADIUS system
Return
The cleartext version of the User-Password.
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
# File 'lib/radius/packet.rb', line 404 def password(secret) pwdin = attr("User-Password") || attr("Password") pwdout = "" lastround = @authenticator 0.step(pwdin.length-1, 16) do |i| pwdout = xor_str(pwdin[i, 16], Digest::MD5.digest(secret + lastround)) lastround = pwdin[i, 16] end pwdout.sub(/\000+$/, "") if pwdout pwdout[length.pwdin, -1] = "" unless (pwdout.length <= pwdin.length) return(pwdout) end |
#set_attr(name, value) ⇒ Object
Changes the value of the named attribute.
Parameters
name-
The name of the attribute to set
value-
The value of the attribute
284 285 286 |
# File 'lib/radius/packet.rb', line 284 def set_attr(name, value) @attributes[name] = value end |
#set_password(pwdin, secret) ⇒ Object
The RADIUS User-Password attribute is encoded with a shared secret. This method will prepare the encoded version of the password. Note that this method always stores the encrypted password in the ‘User-Password’ attribute. Some (non-RFC 2138-compliant) servers have been reported that insist on using the ‘Password’ attribute instead.
Parameters
pwdin+-
The password to encrypt
secret-
The shared secret of the RADIUS system
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/radius/packet.rb', line 448 def set_password(pwdin, secret) lastround = @authenticator pwdout = "" # pad to 16n bytes pwdin += "\000" * (15 - (15 + pwdin.length) % 16) 0.step(pwdin.length-1, 16) do |i| lastround = xor_str(pwdin[i, 16], Digest::MD5.digest(secret + lastround)) pwdout += lastround end set_attr("User-Password", pwdout) return(pwdout) end |
#set_vsattr(vendorid, name, value) ⇒ Object
Changes the value of the named vendor-specific attribute.
Parameters
vendorid-
The vendor ID for the VSA to set
name-
The name of the attribute to set
value-
The value of the attribute
333 334 335 336 337 338 339 340 341 |
# File 'lib/radius/packet.rb', line 333 def set_vsattr(vendorid, name, value) if @vsattributes[vendorid] == nil @vsattributes[vendorid] = Hash.new(nil) end if @vsattributes[vendorid][name] == nil @vsattributes[vendorid][name] = Array.new end @vsattributes[vendorid][name].push(value) end |
#to_s ⇒ Object
This method will convert a RADIUS packet into a printable string. Any fields in the packet that might possibly contain non-printable characters are turned into Base64 strings.
Parameters
secret-
The shared secret of the RADIUS system. Pass nil if
you don’t want to see User-Password attributes decoded.
Return
The string representation of the RADIUS packet.
475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/radius/packet.rb', line 475 def to_s str = "RAD-Code = #{@code}\n" str += "RAD-Identifier = #{@identifier}\n" str += "RAD-Authenticator = #{[@authenticator].pack('m')}" each do |attr, val| str += "#{attr} = #{val}\n" end each_vsa do |vendorid, vsaname, val| str += "Vendor-Id: #{vendorid} -- #{vsaname} = #{val}\n" end return(str) end |
#unpack(data) ⇒ Object
Given a raw RADIUS packet data, unpacks its contents so it can be analyzed with other methods, (e.g. code, attr, etc.). It also clears all present attributes.
Parameters
data-
The raw RADIUS packet to decode
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/radius/packet.rb', line 108 def unpack(data) p_hdr = "CCna16a*" rcodes = { 1 => 'Access-Request', 2 => 'Access-Accept', 3 => 'Access-Reject', 4 => 'Accounting-Request', 5 => 'Accounting-Response', 11 => 'Access-Challenge', 12 => 'Status-Server', 13 => 'Status-Client' } @code, @identifier, len, @authenticator, attrdat = data.unpack(p_hdr) @code = rcodes[@code] unset_all while attrdat.length > 0 length = attrdat.unpack("xC")[0].to_i tval, value = attrdat.unpack("Cxa#{length-2}") tval = tval.to_i if tval == VSA_TYPE # handle vendor-specific attributes vid, vtype, vlength = value.unpack("NCC") # XXX - How do we calculate the length of the VSA? It's not # defined! # XXX - 3COM seems to do things a bit differently. The 'if' # below takes care of that. This is based on the # Net::Radius code. if vid == 429 # 3COM packet vid, vtype = value.unpack("NN") vvalue = value.unpack("xxxxxxxxa#{length - 10}")[0] else vvalue = value.unpack("xxxxxxa#{vlength - 2}")[0] end type = @dict.vsattr_numtype(vid, vtype) if type != nil val = case type when 'string' then vvalue when 'integer' (@dict.vsaval_has_name(vid, vtype)) ? @dict.vsaval_name(vid, vtype, vvalue.unpack("N")[0]) : vvalue.unpack("N")[0] when 'ipaddr' then inet_ntoa(vvalue) when 'time' then vvalue.unpack("N")[0] when 'date' then vvalue.unpack("N")[0] else raise "Unknown VSattribute type found: #{vtype}" end set_vsattr(vid, @dict.vsattr_name(vid, vtype), val) end else type = @dict.attr_numtype(tval) unless type.nil? val = case type when 'string' then value when 'integer' @dict.val_has_name(tval) ? @dict.val_name(tval, value.unpack("N")[0]) : value.unpack("N")[0] when 'ipaddr' then inet_ntoa(value.unpack("N")[0]) when 'time' then value.unpack("N")[0] when 'date' then value.unpack("N")[0] when 'octets' then value else raise "Unknown attribute type found: #{type}" end set_attr(@dict.attr_name(tval), val) end end attrdat[0, length] = "" end end |
#unset_all ⇒ Object
Undefines all regular and vendor-specific attributes
363 364 365 366 |
# File 'lib/radius/packet.rb', line 363 def unset_all unset_all_attr unset_all_vsattr end |
#unset_all_attr ⇒ Object
Undefines all attributes.
298 299 300 301 302 |
# File 'lib/radius/packet.rb', line 298 def unset_all_attr each do |key, value| unset_attr(key) end end |
#unset_all_vsattr ⇒ Object
Undefines all vendor-specific attributes.
356 357 358 359 360 |
# File 'lib/radius/packet.rb', line 356 def unset_all_vsattr each_vsa do |vendor, attr, datum| unset_vsattr(vendor, attr) end end |
#unset_attr(name) ⇒ Object
Undefines the current value of the named attribute.
Parameters
name-
The name of the attribute to unset
292 293 294 |
# File 'lib/radius/packet.rb', line 292 def unset_attr(name) @attributes[name] = nil end |
#unset_vsattr(vendorid, name) ⇒ Object
Undefines the current value of the named vendor-specific attribute.
Parameters
vendorid-
The vendor ID for the VSA to set
name-
The name of the attribute to unset
349 350 351 352 |
# File 'lib/radius/packet.rb', line 349 def unset_vsattr(vendorid, name) return if @vsattributes[vendorid] == nil @vsattributes[vendorid][name] = nil end |
#vsattr(vendorid, name) ⇒ Object
This method obtains the value of a vendor-specific attribute, given the vendor ID and the name of the vendor-specific attribute.
Parameters
vendorid-
the vendor ID
name-
the name of the attribute to obtain
Return value:
The value of the vendor-specific attribute is returned.
377 378 379 380 |
# File 'lib/radius/packet.rb', line 377 def vsattr(vendorid, name) return(nil) if @vsattributes[vendorid] == nil return(@vsattributes[vendorid][name]) end |