class RadiusRB::Packet

Constants

CODES
HDRLEN
P_ATTR
P_HDR

Attributes

attributes[R]
authenticator[R]
code[RW]
id[R]

Public Class Methods

new(dictionary, id, data = nil) click to toggle source
# File lib/radiusrb/packet.rb, line 38
def initialize(dictionary, id, data = nil)
  @dict = dictionary
  @id = id
  unset_all_attributes
  if data
    @packed = data
    self.unpack
  end
  self
end

Public Instance Methods

attribute(name) click to toggle source
# File lib/radiusrb/packet.rb, line 128
def attribute(name)
  if @attributes[name]
    @attributes[name].value
  end
end
decode_attribute(name, secret) click to toggle source
# File lib/radiusrb/packet.rb, line 142
def decode_attribute(name, secret)
  if @attributes[name]
    decode(@attributes[name].value.to_s, secret)
  end
end
gen_acct_authenticator(secret) click to toggle source
# File lib/radiusrb/packet.rb, line 73
def gen_acct_authenticator(secret)
  # From RFC2866
  # Request Authenticator
  #
  #       In Accounting-Request Packets, the Authenticator value is a 16
  #       octet MD5 [5] checksum, called the Request Authenticator.
  #
  #       The NAS and RADIUS accounting server share a secret.  The Request
  #       Authenticator field in Accounting-Request packets contains a one-
  #       way MD5 hash calculated over a stream of octets consisting of the
  #       Code + Identifier + Length + 16 zero octets + request attributes +
  #       shared secret (where + indicates concatenation).  The 16 octet MD5
  #       hash value is stored in the Authenticator field of the
  #       Accounting-Request packet.
  #
  #       Note that the Request Authenticator of an Accounting-Request can
  #       not be done the same way as the Request Authenticator of a RADIUS
  #       Access-Request, because there is no User-Password attribute in an
  #       Accounting-Request.
  #
  @authenticator = "\000"*16
  @authenticator = Digest::MD5.digest(pack + secret)
  @packed = nil
  @authenticator
end
gen_auth_authenticator() click to toggle source

Generate an authenticator. It will try to use /dev/urandom if possible, or the system rand call if that’s not available.

# File lib/radiusrb/packet.rb, line 59
def gen_auth_authenticator
  if (File.exist?("/dev/urandom"))
    File.open("/dev/urandom") do |urandom|
      @authenticator = urandom.read(16)
    end
  else
    @authenticator = []
    8.times do
      @authenticator << rand(65536)
    end
    @authenticator = @authenticator.pack("n8")
  end
end
gen_response_authenticator(secret, request_authenticator) click to toggle source
# File lib/radiusrb/packet.rb, line 99
def gen_response_authenticator(secret, request_authenticator)
  @authenticator = request_authenticator
  @authenticator = Digest::MD5.digest(pack + secret)
  @packed = nil
  @authenticator
end
increment_id() click to toggle source
# File lib/radiusrb/packet.rb, line 49
def increment_id
  @id = (@id + 1) & 0xff
end
pack() click to toggle source
# File lib/radiusrb/packet.rb, line 148
def pack
  attstr = ""
  @attributes.values.each do |attribute|
    attstr += attribute.pack
  end
  @packed = [CODES[@code], @id, attstr.length + HDRLEN, @authenticator, attstr].pack(P_HDR)
end
set_attribute(name, value) click to toggle source
# File lib/radiusrb/packet.rb, line 120
def set_attribute(name, value)
  @attributes[name] = Attribute.new(@dict, name, value)
end
set_encoded_attribute(name, value, secret) click to toggle source
# File lib/radiusrb/packet.rb, line 138
def set_encoded_attribute(name, value, secret)
  @attributes[name] = Attribute.new(@dict, name, encode(value, secret))
end
to_a() click to toggle source
# File lib/radiusrb/packet.rb, line 53
def to_a
  @attributes.to_a
end
unset_all_attributes() click to toggle source
# File lib/radiusrb/packet.rb, line 134
def unset_all_attributes
  @attributes = {}
end
unset_attribute(name) click to toggle source
# File lib/radiusrb/packet.rb, line 124
def unset_attribute(name)
  @attributes.delete(name)
end
validate_acct_authenticator(secret) click to toggle source
# File lib/radiusrb/packet.rb, line 106
def validate_acct_authenticator(secret)
  if @authenticator
    original_authenticator = @authenticator
    if gen_acct_authenticator(secret) == original_authenticator
      true
    else
      @authenticator = original_authenticator
      false
    end
  else
    false
  end
end

Protected Instance Methods

decode(value, secret) click to toggle source
# File lib/radiusrb/packet.rb, line 211
def decode(value, secret)
  decoded_value = ""
  lastround = @authenticator
  0.step(value.length-1, 16) do |i|
          decoded_value = xor_str(value[i, 16], Digest::MD5.digest(secret + lastround))
          lastround = value[i, 16]
  end

  decoded_value.gsub!(/\000+/, "") if decoded_value
  decoded_value[value.length, -1] = "" unless (decoded_value.length <= value.length)
  return decoded_value
end
encode(value, secret) click to toggle source
# File lib/radiusrb/packet.rb, line 199
def encode(value, secret)
  lastround = @authenticator
  encoded_value = ""
  # pad to 16n bytes
  value += "\000" * (15-(15 + value.length) % 16)
  0.step(value.length-1, 16) do |i|
    lastround = xor_str(value[i, 16], Digest::MD5.digest(secret + lastround) )
    encoded_value += lastround
  end
  encoded_value
end
unpack() click to toggle source
# File lib/radiusrb/packet.rb, line 158
def unpack
  @code, @id, len, @authenticator, attribute_data = @packed.unpack(P_HDR)
  @code = CODES.key(@code)

  unset_all_attributes

  while attribute_data.length > 0 do
    length = attribute_data.unpack("xC").first.to_i
    attribute_type, attribute_value = attribute_data.unpack("Cxa#{length-2}")
    attribute_type = attribute_type.to_i

    attribute = @dict.find_attribute_by_id(attribute_type)
    attribute_value = case attribute.type
    when 'string'
      attribute_value
    when 'integer'
      attribute.has_values? ? attribute.find_values_by_id(attribute_value.unpack("N")[0]).name : attribute_value.unpack("N")[0]
    when 'ipaddr'
      attribute_value.unpack("N")[0].to_ip.to_s
    when 'time'
      attribute_value.unpack("N")[0]
    when 'date'
      attribute_value.unpack("N")[0]
    end

    set_attribute(attribute.name, attribute_value) if attribute
    attribute_data[0, length] = ""
  end
end
xor_str(str1, str2) click to toggle source
# File lib/radiusrb/packet.rb, line 188
def xor_str(str1, str2)
  i = 0
  newstr = ""
  str1.each_byte do |c1|
    c2 = str2.bytes.to_a[i]
    newstr = newstr << (c1 ^ c2)
    i = i+1
  end
  newstr
end