Class: KRB5::KeytabParser

Inherits:
Object
  • Object
show all
Includes:
Mixin::Unpacker
Defined in:
lib/krb5/keytab_parser.rb

Constant Summary collapse

SUPPORTED_VERSIONS =
[1, 2]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixin::Unpacker

#unpack_bytes, #unpack_int16, #unpack_int32, #unpack_int8

Constructor Details

#initializeKeytabParser

Returns a new instance of KeytabParser.



22
23
24
25
# File 'lib/krb5/keytab_parser.rb', line 22

def initialize
  @index = 0
  @keytab = KRB5::Keytab.new
end

Instance Attribute Details

#bytesString

raw bytes of keytab file

Returns:

  • (String)


16
17
18
# File 'lib/krb5/keytab_parser.rb', line 16

def bytes
  @bytes
end

#keytabKRB5::Keytab

Instance of KRB5::Keytab to unpack bytes into Ruby class

Returns:



20
21
22
# File 'lib/krb5/keytab_parser.rb', line 20

def keytab
  @keytab
end

Class Method Details

.parse(bytes) ⇒ KRB5::Keytab

Parses byte array and creates relevant Ruby objects

Returns:



38
39
40
41
42
43
44
# 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

Instance Method Details

#indexInteger

Keep track of where we are in byte array

Returns:

  • (Integer)


29
30
31
# File 'lib/krb5/keytab_parser.rb', line 29

def index
  @index
end

#unpack_byte_arrayObject



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 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_dataString

Generic method to unpack Kerberos data

data ::=

length (16 bits)
value (length bytes)

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

Returns:

  • (String)


163
164
165
166
# File 'lib/krb5/keytab_parser.rb', line 163

def unpack_data
  length = unpack_int16
  data = unpack_bytes(length)
end

#unpack_entryKRB5::Entry

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

Returns:



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# 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_headernil

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

Returns:

  • (nil)

Raises:

  • (RuntimeError)

    if the file does not have a valid header



74
75
76
77
78
79
# 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_keyObject



148
149
150
151
# File 'lib/krb5/keytab_parser.rb', line 148

def unpack_key
  key_length = unpack_int16
  unpack_bytes(key_length)
end

#unpack_principalKRB5::Principal

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

Returns:



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# 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_timestampObject



142
143
144
145
146
# File 'lib/krb5/keytab_parser.rb', line 142

def unpack_timestamp
  require 'date'
  epoch = unpack_int32
  DateTime.strptime(epoch.to_s, '%s')
end