Skip to content

Add support for version 1 (0x501) keytab files (#5)#6

Open
p0dalirius wants to merge 1 commit into
mainfrom
feature-version-1-support
Open

Add support for version 1 (0x501) keytab files (#5)#6
p0dalirius wants to merge 1 commit into
mainfrom
feature-version-1-support

Conversation

@p0dalirius

Copy link
Copy Markdown
Collaborator

Linked Issue

Closes #5

Root Cause

The keytab (de)serialization was written for version 2 only. Every integer read and write used binary.BigEndian, the name_type 32-bit field was always present, and the component count was used verbatim. Version 1 of the format differs in exactly three ways — it uses native (little-endian) byte order, its component count includes the realm, and it has no name_type field — and none of these were derived from FileFormatVersion. As a result version-1 files were parsed with the wrong byte order and a phantom name_type, desynchronizing every field, and the writer could only emit version-2 layout.

Fix Description

  • Added src/keytab/FileFormatVersion.go with the version constants, a byteOrderForVersion helper (version 1 → little-endian, otherwise big-endian), and small resolvers for the optional parameters.
  • CountedOctetString and KeyBlock FromBytes/ToBytes take an optional binary.ByteOrder and use it for all integer fields.
  • KeytabEntry.FromBytes/ToBytes/UpdateSize take an optional version; they derive the byte order, subtract 1 from the on-disk component count for version 1 (and add it back when writing), and skip the name_type field for version 1, defaulting it in memory to KRB5_NT_PRINCIPAL.
  • Keytab.FromBytes/ToBytes/UpdateEntriesSizes pass FileFormatVersion down to the entries. The 2-byte version header itself is unchanged (always 0x05 followed by the version byte).

All new parameters are variadic and default to version 2 / big-endian, so the change is backward compatible: existing call sites (and all existing tests) behave exactly as before.

Note on byte order: the spec says version 1 uses "native" byte order. Since a reader cannot know the producing host's endianness, this implementation assumes little-endian, which is the conventional interpretation (version 1 predates the big-endian convention that version 2 introduced to remove the ambiguity).

How Verified

  • go test ./... and go vet ./keytab/ pass.
  • keytab describe -f example.kt (version 2) is unchanged.
  • New tests parse a hand-built little-endian version-1 record and confirm the byte order, component-count adjustment, and absent name_type are all handled, then confirm byte-exact round-trips.

Test Coverage

  • Added: src/keytab/Keytab_version1_test.go:
    • Test_KeytabEntry_Version1_Parse — a hand-built little-endian record decodes correctly (realm subtracted from the count, name_type defaulted, all fields little-endian).
    • Test_KeytabEntry_Version1_RoundTrip — that record serializes back to identical bytes.
    • Test_Keytab_Version1_FileRoundTrip — a full version-1 keytab round-trips and emits the 0x05 0x01 header with a little-endian, realm-inclusive component count.
    • Test_KeytabEntry_Version1_OmitsNameType — a version-1 entry is exactly 4 bytes shorter than the version-2 encoding of the same entry.

Scope of Change

  • Files changed: src/keytab/FileFormatVersion.go (new), src/keytab/CountedOctetString.go, src/keytab/KeyBlock.go, src/keytab/KeytabEntry.go, src/keytab/Keytab.go, src/keytab/Keytab_version1_test.go (new)
  • Submodule pointer updated: no
  • Behavioral changes outside the bug fix: none — version-2 behavior is byte-for-byte identical.

Risk and Rollout

The optional-parameter defaults preserve all version-2 behavior, so the blast radius is limited to the new version-1 path. Safe to merge directly.

Notes

This branch is based on main and touches the same (de)serialization methods as the open PRs for #1 (parser robustness) and #2 (kvno round-trip); whichever merges first, the others will need a straightforward rebase.

The (de)serialization code only implemented version 2: it hardcoded
big-endian byte order, always read/wrote a 32-bit name_type, and never
adjusted the component count. Version-1 files, which use native
(little-endian) byte order, count the realm in num_components, and omit
name_type, were therefore mis-parsed and could not be produced.

Derive the byte order and the version-specific layout from the file format
version and thread it through the entry, key block, and counted-octet-string
(de)serialization. Version-1 reads now decode little-endian integers,
subtract 1 from the component count, and default name_type to
KRB5_NT_PRINCIPAL; writes emit the matching layout. The version/byte-order
parameters are optional and default to version 2, so existing callers are
unaffected.

Fixes #5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant