Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/keytab/CountedOctetString.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ type CountedOctetString struct {
//
// Returns:
// - error: An error if the parsing fails.
func (c *CountedOctetString) FromBytes(data []byte) error {
func (c *CountedOctetString) FromBytes(data []byte, order ...binary.ByteOrder) error {
bo := resolveByteOrder(order)

c.RawBytes = data

c.Length = binary.BigEndian.Uint16(data[0:2])
c.Length = bo.Uint16(data[0:2])
data = data[2:]

c.Data = data[:c.Length]
Expand All @@ -49,15 +51,17 @@ func (c *CountedOctetString) FromBytes(data []byte) error {
// Returns:
// - []byte: The byte array representation of the CountedOctetString.
// - error: An error if the conversion fails.
func (c *CountedOctetString) ToBytes() ([]byte, error) {
func (c *CountedOctetString) ToBytes(order ...binary.ByteOrder) ([]byte, error) {
bo := resolveByteOrder(order)

if c.Length != uint16(len(c.Data)) {
return nil, fmt.Errorf("length of data is not equal to the length of the counted octet string")
}

data := make([]byte, 0)

buffer := make([]byte, 2)
binary.BigEndian.PutUint16(buffer, c.Length)
bo.PutUint16(buffer, c.Length)
data = append(data, buffer...)

data = append(data, c.Data...)
Expand Down
56 changes: 56 additions & 0 deletions src/keytab/FileFormatVersion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package keytab

import "encoding/binary"

// Keytab file format versions. The first header byte is always 0x05; the second
// byte is the version number, so the 16-bit header reads as 0x0501 or 0x0502.
const (
FileFormatVersion1 uint16 = 0x0501
FileFormatVersion2 uint16 = 0x0502
)

// nameTypePrincipal is the KRB5_NT_PRINCIPAL name type. It is used as the
// in-memory default for version-1 entries, which do not store a name_type on
// disk.
const nameTypePrincipal uint32 = 1

// isVersion1 reports whether the given file format version is version 1, which
// differs from version 2 in byte order, component count, and name_type
// presence. Only the low byte (the version number) is significant.
func isVersion1(version uint16) bool {
return version&0x00ff == 0x01
}

// byteOrderForVersion returns the integer byte order used by the given keytab
// file format version. Version 1 uses native byte order, which in practice is
// little-endian (version 1 predates the big-endian convention introduced in
// version 2 specifically to remove this ambiguity); every other version uses
// big-endian.
func byteOrderForVersion(version uint16) binary.ByteOrder {
if isVersion1(version) {
return binary.LittleEndian
}
return binary.BigEndian
}

// resolveVersion returns the version from an optional variadic argument,
// defaulting to version 2 when none is supplied or the supplied value is zero.
// This lets the entry (de)serialization methods stay backward compatible: an
// existing call with no version behaves exactly as before (version 2).
func resolveVersion(version []uint16) uint16 {
if len(version) > 0 && version[0] != 0 {
return version[0]
}
return FileFormatVersion2
}

// resolveByteOrder returns the byte order from an optional variadic argument,
// defaulting to big-endian (version 2) when none is supplied. This keeps the
// counted-octet-string and key-block (de)serialization methods backward
// compatible with callers that do not pass a byte order.
func resolveByteOrder(order []binary.ByteOrder) binary.ByteOrder {
if len(order) > 0 && order[0] != nil {
return order[0]
}
return binary.BigEndian
}
16 changes: 10 additions & 6 deletions src/keytab/KeyBlock.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ type KeyBlock struct {
//
// Returns:
// - error: An error if the parsing failed.
func (k *KeyBlock) FromBytes(data []byte) error {
k.Type = EncryptionType(binary.BigEndian.Uint16(data[0:2]))
func (k *KeyBlock) FromBytes(data []byte, order ...binary.ByteOrder) error {
bo := resolveByteOrder(order)

k.Type = EncryptionType(bo.Uint16(data[0:2]))
k.RawBytesSize = 2

k.Key.FromBytes(data[2:])
k.Key.FromBytes(data[2:], bo)
k.RawBytesSize += k.Key.RawBytesSize

return nil
Expand All @@ -42,14 +44,16 @@ func (k *KeyBlock) FromBytes(data []byte) error {
//
// Returns:
// - ([]byte, error): The byte array and an error if the conversion failed.
func (k *KeyBlock) ToBytes() ([]byte, error) {
func (k *KeyBlock) ToBytes(order ...binary.ByteOrder) ([]byte, error) {
bo := resolveByteOrder(order)

data := make([]byte, 0)

buffer := make([]byte, 2)
binary.BigEndian.PutUint16(buffer, uint16(k.Type))
bo.PutUint16(buffer, uint16(k.Type))
data = append(data, buffer...)

keyBytes, err := k.Key.ToBytes()
keyBytes, err := k.Key.ToBytes(bo)
if err != nil {
return nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions src/keytab/Keytab.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (k *Keytab) FromBytes(data []byte) error {

for len(data) != 0 {
entry := KeytabEntry{}
entry.FromBytes(data)
entry.FromBytes(data, k.FileFormatVersion)
data = data[entry.RawBytesSize:]
k.Entries = append(k.Entries, entry)
k.RawBytesSize += entry.RawBytesSize
Expand All @@ -65,7 +65,7 @@ func (k *Keytab) ToBytes() ([]byte, error) {
data = append(data, buffer2...)

for _, entry := range k.Entries {
entryBytes, err := entry.ToBytes()
entryBytes, err := entry.ToBytes(k.FileFormatVersion)
if err != nil {
return nil, err
}
Expand All @@ -81,7 +81,7 @@ func (k *Keytab) ToBytes() ([]byte, error) {
// - error: An error if the update fails.
func (k *Keytab) UpdateEntriesSizes() error {
for i := range k.Entries {
err := k.Entries[i].UpdateSize()
err := k.Entries[i].UpdateSize(k.FileFormatVersion)
if err != nil {
return err
}
Expand Down
77 changes: 50 additions & 27 deletions src/keytab/KeytabEntry.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package keytab

import (
"encoding/binary"
"fmt"
"strings"
"time"
Expand Down Expand Up @@ -30,43 +29,57 @@ type KeytabEntry struct {
//
// Returns:
// - error: An error if the parsing fails.
func (k *KeytabEntry) FromBytes(data []byte) error {
func (k *KeytabEntry) FromBytes(data []byte, version ...uint16) error {
ver := resolveVersion(version)
bo := byteOrderForVersion(ver)
v1 := isVersion1(ver)

k.RawBytesSize = 0
k.RawBytes = data

// Size
k.Size = binary.BigEndian.Uint32(data[0:4])
k.Size = bo.Uint32(data[0:4])
data = data[4:]
data = data[:k.Size]
k.RawBytesSize += 4

// NumComponents
k.NumComponents = binary.BigEndian.Uint16(data[0:2])
numComponents := bo.Uint16(data[0:2])
data = data[2:]
k.RawBytesSize += 2
// In version 1 the on-disk count includes the realm, so subtract 1 to get
// the number of name components.
if v1 && numComponents > 0 {
numComponents--
}
k.NumComponents = numComponents

// Realm
k.Realm = CountedOctetString{}
k.Realm.FromBytes(data)
k.Realm.FromBytes(data, bo)
data = data[k.Realm.RawBytesSize:]
k.RawBytesSize += k.Realm.RawBytesSize

// Components
for i := uint16(0); i < k.NumComponents; i++ {
component := CountedOctetString{}
component.FromBytes(data)
component.FromBytes(data, bo)
k.Components = append(k.Components, component)
data = data[component.RawBytesSize:]
k.RawBytesSize += component.RawBytesSize
}

// NameType
k.NameType = binary.BigEndian.Uint32(data[0:4])
data = data[4:]
k.RawBytesSize += 4
// NameType (omitted in version 1, which defaults to the principal name type)
if v1 {
k.NameType = nameTypePrincipal
} else {
k.NameType = bo.Uint32(data[0:4])
data = data[4:]
k.RawBytesSize += 4
}

// Timestamp
k.Timestamp = binary.BigEndian.Uint32(data[0:4])
k.Timestamp = bo.Uint32(data[0:4])
data = data[4:]
k.RawBytesSize += 4

Expand All @@ -76,13 +89,13 @@ func (k *KeytabEntry) FromBytes(data []byte) error {
k.RawBytesSize += 1

// Key
k.Key.FromBytes(data)
k.Key.FromBytes(data, bo)
data = data[k.Key.RawBytesSize:]
k.RawBytesSize += k.Key.RawBytesSize

// Vno
if len(data) >= 4 {
k.Vno = binary.BigEndian.Uint32(data[0:4])
k.Vno = bo.Uint32(data[0:4])
k.RawBytesSize += 4
// data = data[4:]
} else {
Expand All @@ -99,56 +112,66 @@ func (k *KeytabEntry) FromBytes(data []byte) error {
// Returns:
// - []byte: The byte array representation of the KeytabEntry.
// - error: An error if the conversion fails.
func (k *KeytabEntry) ToBytes() ([]byte, error) {
func (k *KeytabEntry) ToBytes(version ...uint16) ([]byte, error) {
ver := resolveVersion(version)
bo := byteOrderForVersion(ver)
v1 := isVersion1(ver)

data := make([]byte, 0)

buffer4 := make([]byte, 4)
buffer2 := make([]byte, 2)

// Add the number of components
binary.BigEndian.PutUint16(buffer2, k.NumComponents)
// Add the number of components. Version 1 includes the realm in the count.
numComponents := k.NumComponents
if v1 {
numComponents++
}
bo.PutUint16(buffer2, numComponents)
data = append(data, buffer2...)

// Add the realm
realmBytes, err := k.Realm.ToBytes()
realmBytes, err := k.Realm.ToBytes(bo)
if err != nil {
return nil, err
}
data = append(data, realmBytes...)

// Add the components
for _, component := range k.Components {
componentBytes, err := component.ToBytes()
componentBytes, err := component.ToBytes(bo)
if err != nil {
return nil, err
}
data = append(data, componentBytes...)
}

// Add the name type
binary.BigEndian.PutUint32(buffer4, k.NameType)
data = append(data, buffer4...)
// Add the name type (omitted in version 1)
if !v1 {
bo.PutUint32(buffer4, k.NameType)
data = append(data, buffer4...)
}

// Add the timestamp
binary.BigEndian.PutUint32(buffer4, k.Timestamp)
bo.PutUint32(buffer4, k.Timestamp)
data = append(data, buffer4...)

// Add the vno8
data = append(data, k.Vno8)

// Add the key
keyBytes, err := k.Key.ToBytes()
keyBytes, err := k.Key.ToBytes(bo)
if err != nil {
return nil, err
}
data = append(data, keyBytes...)

// Add the vno
binary.BigEndian.PutUint32(buffer4, k.Vno)
bo.PutUint32(buffer4, k.Vno)
data = append(data, buffer4...)

// At the start of the data, add the size of the entry
binary.BigEndian.PutUint32(buffer4, uint32(len(data)))
bo.PutUint32(buffer4, uint32(len(data)))
data = append(buffer4, data...)

return data, nil
Expand All @@ -158,8 +181,8 @@ func (k *KeytabEntry) ToBytes() ([]byte, error) {
//
// Returns:
// - error: An error if the update fails.
func (k *KeytabEntry) UpdateSize() error {
bytes, err := k.ToBytes()
func (k *KeytabEntry) UpdateSize(version ...uint16) error {
bytes, err := k.ToBytes(version...)
if err != nil {
return err
}
Expand Down
Loading
Loading