class KRB5::KeytabParser

Constants

SUPPORTED_VERSIONS

Attributes

bytes[RW]

raw bytes of keytab file @return [String]

keytab[RW]

Instance of KRB5::Keytab to unpack bytes into Ruby class @return [KRB5::Keytab]

Public Class Methods

new() click to toggle source
# File lib/krb5/keytab_parser.rb, line 22
def initialize
  @index = 0
  @keytab = KRB5::Keytab.new
end
parse(bytes) click to toggle source

Parses byte array and creates relevant Ruby objects

@return [KRB5::Keytab]

# File lib/krb5/keytab_parser.rb, line 38
def self.parse(bytes)
  parser = new
  parser.bytes = bytes
  parser.keytab.source_bytes = bytes
  parser.unpack_byte_array
  parser.keytab
end

Public Instance Methods

index() click to toggle source

Keep track of where we are in byte array @return [Integer]

# File lib/krb5/keytab_parser.rb, line 29
def index
  @index
end
unpack_byte_array() click to toggle source
# File lib/krb5/keytab_parser.rb, line 46
def unpack_byte_array
  unpack_header

  record_length = unpack_int32
  while record_length != 0 do
    if record_length < 0
      @index += record_length * -1
    else
      @curr_entry_first_byte = @index
      @curr_entry_last_byte = @index + record_length - 1
      keytab.entries << unpack_entry
      @index = @curr_entry_first_byte + record_length
    end

    if @index > bytes.length || bytes[@index..-1].length < 4
      break
    end

    record_length = unpack_int32
  end
end
unpack_entry() click to toggle source

entry ::=

principal
timestamp (32 bits)
key version (8 bits)
enctype (16 bits)
key length (16 bits)
key contents
key version (32 bits) [in release 1.14 and later]

See: web.mit.edu/kerberos/krb5-1.16/doc/formats/keytab_file_format.html

@return [KRB5::Entry]

# File lib/krb5/keytab_parser.rb, line 94
def unpack_entry
  entry = KRB5::Entry.new(keytab)
  entry.principal = unpack_principal
  entry.timestamp = unpack_timestamp
  entry.kvno8 = unpack_int8
  entry.enctype = unpack_int16
  entry.key = unpack_key

  if @curr_entry_last_byte - @index >= 3
    entry.kvno32 = unpack_int32
  end

  entry
end
unpack_header() click to toggle source

Unpacks the Keytab header which is two bytes and contains the version number of the keytab

@return [nil] @raise [RuntimeError] if the file does not have a valid header

# File lib/krb5/keytab_parser.rb, line 74
def unpack_header
  raise "Invalid keytab data. First byte does not equal 5" if unpack_int8 != 5
  keytab.version = unpack_int8
  raise "Invalid keytab data. Keytab version is neither 1 nor 2" unless SUPPORTED_VERSIONS.include?(keytab.version)
  nil
end
unpack_key() click to toggle source
# File lib/krb5/keytab_parser.rb, line 148
def unpack_key
  key_length = unpack_int16
  unpack_bytes(key_length)
end
unpack_principal() click to toggle source

principal ::=

count of components (16 bits) [includes realm in version 1]
realm (data)
component1 (data)
component2 (data)
...
name type (32 bits) [omitted in version 1]

See: web.mit.edu/kerberos/krb5-1.16/doc/formats/keytab_file_format.html

@return [KRB5::Principal]

# File lib/krb5/keytab_parser.rb, line 121
def unpack_principal
  principal = KRB5::Principal.new(keytab)

  count_of_components = unpack_int16
  # Keytab v1 used to include the realm in the component count. This makes
  # v1 parsing just like v2
  if keytab.version == 1
    count_of_components -= 1
  end

  principal.realm = unpack_data

  count_of_components.times do
    principal.components << unpack_data
  end

  principal.name_type = unpack_int32 unless keytab.version == 1

  principal
end
unpack_timestamp() click to toggle source
# File lib/krb5/keytab_parser.rb, line 142
def unpack_timestamp
  require 'date'
  epoch = unpack_int32
  DateTime.strptime(epoch.to_s, '%s')
end