github.com/jcmturner/gokrb5/v8@v8.4.4/keytab/keytab.go (about) 1 // Package keytab implements Kerberos keytabs: https://web.mit.edu/kerberos/krb5-devel/doc/formats/keytab_file_format.html. 2 package keytab 3 4 import ( 5 "bytes" 6 "encoding/binary" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io" 11 "os" 12 "strings" 13 "time" 14 "unsafe" 15 16 "github.com/jcmturner/gokrb5/v8/crypto" 17 "github.com/jcmturner/gokrb5/v8/types" 18 ) 19 20 const ( 21 keytabFirstByte byte = 05 22 ) 23 24 // Keytab struct. 25 type Keytab struct { 26 version uint8 27 Entries []entry 28 } 29 30 // Keytab entry struct. 31 type entry struct { 32 Principal principal 33 Timestamp time.Time 34 KVNO8 uint8 35 Key types.EncryptionKey 36 KVNO uint32 37 } 38 39 func (e entry) String() string { 40 return fmt.Sprintf("% 4d %s %-56s %2d %-64x", 41 e.KVNO8, 42 e.Timestamp.Format("02/01/06 15:04:05"), 43 e.Principal.String(), 44 e.Key.KeyType, 45 e.Key.KeyValue, 46 ) 47 } 48 49 // Keytab entry principal struct. 50 type principal struct { 51 NumComponents int16 `json:"-"` 52 Realm string 53 Components []string 54 NameType int32 55 } 56 57 func (p principal) String() string { 58 return fmt.Sprintf("%s@%s", strings.Join(p.Components, "/"), p.Realm) 59 } 60 61 // New creates new, empty Keytab type. 62 func New() *Keytab { 63 var e []entry 64 return &Keytab{ 65 version: 2, 66 Entries: e, 67 } 68 } 69 70 // GetEncryptionKey returns the EncryptionKey from the Keytab for the newest entry with the required kvno, etype and matching principal. 71 // If the kvno is zero then the latest kvno will be returned. The kvno is also returned for 72 func (kt *Keytab) GetEncryptionKey(princName types.PrincipalName, realm string, kvno int, etype int32) (types.EncryptionKey, int, error) { 73 var key types.EncryptionKey 74 var t time.Time 75 var kv int 76 for _, k := range kt.Entries { 77 if k.Principal.Realm == realm && len(k.Principal.Components) == len(princName.NameString) && 78 k.Key.KeyType == etype && 79 (k.KVNO == uint32(kvno) || kvno == 0) && 80 k.Timestamp.After(t) { 81 p := true 82 for i, n := range k.Principal.Components { 83 if princName.NameString[i] != n { 84 p = false 85 break 86 } 87 } 88 if p { 89 key = k.Key 90 kv = int(k.KVNO) 91 t = k.Timestamp 92 } 93 } 94 } 95 if len(key.KeyValue) < 1 { 96 return key, 0, fmt.Errorf("matching key not found in keytab. Looking for %q realm: %v kvno: %v etype: %v", princName.PrincipalNameString(), realm, kvno, etype) 97 } 98 return key, kv, nil 99 } 100 101 // Create a new Keytab entry. 102 func newEntry() entry { 103 var b []byte 104 return entry{ 105 Principal: newPrincipal(), 106 Timestamp: time.Time{}, 107 KVNO8: 0, 108 Key: types.EncryptionKey{ 109 KeyType: 0, 110 KeyValue: b, 111 }, 112 KVNO: 0, 113 } 114 } 115 116 func (kt Keytab) String() string { 117 var s string 118 s = `KVNO Timestamp Principal ET Key 119 ---- ----------------- -------------------------------------------------------- -- ---------------------------------------------------------------- 120 ` 121 for _, entry := range kt.Entries { 122 s += entry.String() + "\n" 123 } 124 return s 125 } 126 127 // AddEntry adds an entry to the keytab. The password should be provided in plain text and it will be converted using the defined enctype to be stored. 128 func (kt *Keytab) AddEntry(principalName, realm, password string, ts time.Time, KVNO uint8, encType int32) error { 129 // Generate a key from the password 130 princ, _ := types.ParseSPNString(principalName) 131 key, _, err := crypto.GetKeyFromPassword(password, princ, realm, encType, types.PADataSequence{}) 132 if err != nil { 133 return err 134 } 135 136 // Populate the keytab entry principal 137 ktep := newPrincipal() 138 ktep.NumComponents = int16(len(princ.NameString)) 139 if kt.version == 1 { 140 ktep.NumComponents += 1 141 } 142 143 ktep.Realm = realm 144 ktep.Components = princ.NameString 145 ktep.NameType = princ.NameType 146 147 // Populate the keytab entry 148 e := newEntry() 149 e.Principal = ktep 150 e.Timestamp = ts 151 e.KVNO8 = KVNO 152 e.KVNO = uint32(KVNO) 153 e.Key = key 154 155 kt.Entries = append(kt.Entries, e) 156 return nil 157 } 158 159 // Create a new principal. 160 func newPrincipal() principal { 161 var c []string 162 return principal{ 163 NumComponents: 0, 164 Realm: "", 165 Components: c, 166 NameType: 0, 167 } 168 } 169 170 // Load a Keytab file into a Keytab type. 171 func Load(ktPath string) (*Keytab, error) { 172 kt := new(Keytab) 173 b, err := os.ReadFile(ktPath) 174 if err != nil { 175 return kt, err 176 } 177 err = kt.Unmarshal(b) 178 return kt, err 179 } 180 181 // Marshal keytab into byte slice 182 func (kt *Keytab) Marshal() ([]byte, error) { 183 b := []byte{keytabFirstByte, kt.version} 184 for _, e := range kt.Entries { 185 eb, err := e.marshal(int(kt.version)) 186 if err != nil { 187 return b, err 188 } 189 b = append(b, eb...) 190 } 191 return b, nil 192 } 193 194 // Write the keytab bytes to io.Writer. 195 // Returns the number of bytes written 196 func (kt *Keytab) Write(w io.Writer) (int, error) { 197 b, err := kt.Marshal() 198 if err != nil { 199 return 0, fmt.Errorf("error marshaling keytab: %v", err) 200 } 201 return w.Write(b) 202 } 203 204 // Unmarshal byte slice of Keytab data into Keytab type. 205 func (kt *Keytab) Unmarshal(b []byte) error { 206 if len(b) < 2 { 207 return fmt.Errorf("byte array is less than 2 bytes: %d", len(b)) 208 } 209 210 //The first byte of the file always has the value 5 211 if b[0] != keytabFirstByte { 212 return errors.New("invalid keytab data. First byte does not equal 5") 213 } 214 //Get keytab version 215 //The 2nd byte contains the version number (1 or 2) 216 kt.version = b[1] 217 if kt.version != 1 && kt.version != 2 { 218 return errors.New("invalid keytab data. Keytab version is neither 1 nor 2") 219 } 220 //Version 1 of the file format uses native byte order for integer representations. Version 2 always uses big-endian byte order 221 var endian binary.ByteOrder 222 endian = binary.BigEndian 223 if kt.version == 1 && isNativeEndianLittle() { 224 endian = binary.LittleEndian 225 } 226 // n tracks position in the byte array 227 n := 2 228 l, err := readInt32(b, &n, &endian) 229 if err != nil { 230 return err 231 } 232 for l != 0 { 233 if l < 0 { 234 //Zero padded so skip over 235 l = l * -1 236 n = n + int(l) 237 } else { 238 if n < 0 { 239 return fmt.Errorf("%d can't be less than zero", n) 240 } 241 if n+int(l) > len(b) { 242 return fmt.Errorf("%s's length is less than %d", b, n+int(l)) 243 } 244 eb := b[n : n+int(l)] 245 n = n + int(l) 246 ke := newEntry() 247 // p keeps track as to where we are in the byte stream 248 var p int 249 var err error 250 parsePrincipal(eb, &p, kt, &ke, &endian) 251 ke.Timestamp, err = readTimestamp(eb, &p, &endian) 252 if err != nil { 253 return err 254 } 255 rei8, err := readInt8(eb, &p, &endian) 256 if err != nil { 257 return err 258 } 259 ke.KVNO8 = uint8(rei8) 260 rei16, err := readInt16(eb, &p, &endian) 261 if err != nil { 262 return err 263 } 264 ke.Key.KeyType = int32(rei16) 265 rei16, err = readInt16(eb, &p, &endian) 266 if err != nil { 267 return err 268 } 269 kl := int(rei16) 270 ke.Key.KeyValue, err = readBytes(eb, &p, kl, &endian) 271 if err != nil { 272 return err 273 } 274 // The 32-bit key version overrides the 8-bit key version. 275 // If at least 4 bytes are left after the other fields are read and they are non-zero 276 // this indicates the 32-bit version is present. 277 if len(eb)-p >= 4 { 278 // The 32-bit key may be present 279 ri32, err := readInt32(eb, &p, &endian) 280 if err != nil { 281 return err 282 } 283 ke.KVNO = uint32(ri32) 284 } 285 if ke.KVNO == 0 { 286 // Handles if the value from the last 4 bytes was zero and also if there are not the 4 bytes present. Makes sense to put the same value here as KVNO8 287 ke.KVNO = uint32(ke.KVNO8) 288 } 289 // Add the entry to the keytab 290 kt.Entries = append(kt.Entries, ke) 291 } 292 // Check if there are still 4 bytes left to read 293 // Also check that n is greater than zero 294 if n < 0 || n > len(b) || len(b[n:]) < 4 { 295 break 296 } 297 // Read the size of the next entry 298 l, err = readInt32(b, &n, &endian) 299 if err != nil { 300 return err 301 } 302 } 303 return nil 304 } 305 306 func (e entry) marshal(v int) ([]byte, error) { 307 var b []byte 308 pb, err := e.Principal.marshal(v) 309 if err != nil { 310 return b, err 311 } 312 b = append(b, pb...) 313 314 var endian binary.ByteOrder 315 endian = binary.BigEndian 316 if v == 1 && isNativeEndianLittle() { 317 endian = binary.LittleEndian 318 } 319 320 t := make([]byte, 9) 321 endian.PutUint32(t[0:4], uint32(e.Timestamp.Unix())) 322 t[4] = e.KVNO8 323 endian.PutUint16(t[5:7], uint16(e.Key.KeyType)) 324 endian.PutUint16(t[7:9], uint16(len(e.Key.KeyValue))) 325 b = append(b, t...) 326 327 buf := new(bytes.Buffer) 328 err = binary.Write(buf, endian, e.Key.KeyValue) 329 if err != nil { 330 return b, err 331 } 332 b = append(b, buf.Bytes()...) 333 334 t = make([]byte, 4) 335 endian.PutUint32(t, e.KVNO) 336 b = append(b, t...) 337 338 // Add the length header 339 t = make([]byte, 4) 340 endian.PutUint32(t, uint32(len(b))) 341 b = append(t, b...) 342 return b, nil 343 } 344 345 // Parse the Keytab bytes of a principal into a Keytab entry's principal. 346 func parsePrincipal(b []byte, p *int, kt *Keytab, ke *entry, e *binary.ByteOrder) error { 347 var err error 348 ke.Principal.NumComponents, err = readInt16(b, p, e) 349 if err != nil { 350 return err 351 } 352 if kt.version == 1 { 353 //In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2 354 ke.Principal.NumComponents-- 355 } 356 lenRealm, err := readInt16(b, p, e) 357 if err != nil { 358 return err 359 } 360 realmB, err := readBytes(b, p, int(lenRealm), e) 361 if err != nil { 362 return err 363 } 364 ke.Principal.Realm = string(realmB) 365 for i := 0; i < int(ke.Principal.NumComponents); i++ { 366 l, err := readInt16(b, p, e) 367 if err != nil { 368 return err 369 } 370 compB, err := readBytes(b, p, int(l), e) 371 if err != nil { 372 return err 373 } 374 ke.Principal.Components = append(ke.Principal.Components, string(compB)) 375 } 376 if kt.version != 1 { 377 //Name Type is omitted in version 1 378 ke.Principal.NameType, err = readInt32(b, p, e) 379 if err != nil { 380 return err 381 } 382 } 383 return nil 384 } 385 386 func (p principal) marshal(v int) ([]byte, error) { 387 //var b []byte 388 b := make([]byte, 2) 389 var endian binary.ByteOrder 390 endian = binary.BigEndian 391 if v == 1 && isNativeEndianLittle() { 392 endian = binary.LittleEndian 393 } 394 endian.PutUint16(b[0:], uint16(p.NumComponents)) 395 realm, err := marshalString(p.Realm, v) 396 if err != nil { 397 return b, err 398 } 399 b = append(b, realm...) 400 for _, c := range p.Components { 401 cb, err := marshalString(c, v) 402 if err != nil { 403 return b, err 404 } 405 b = append(b, cb...) 406 } 407 if v != 1 { 408 t := make([]byte, 4) 409 endian.PutUint32(t, uint32(p.NameType)) 410 b = append(b, t...) 411 } 412 return b, nil 413 } 414 415 func marshalString(s string, v int) ([]byte, error) { 416 sb := []byte(s) 417 b := make([]byte, 2) 418 var endian binary.ByteOrder 419 endian = binary.BigEndian 420 if v == 1 && isNativeEndianLittle() { 421 endian = binary.LittleEndian 422 } 423 endian.PutUint16(b[0:], uint16(len(sb))) 424 buf := new(bytes.Buffer) 425 err := binary.Write(buf, endian, sb) 426 if err != nil { 427 return b, err 428 } 429 b = append(b, buf.Bytes()...) 430 return b, err 431 } 432 433 // Read bytes representing a timestamp. 434 func readTimestamp(b []byte, p *int, e *binary.ByteOrder) (time.Time, error) { 435 i32, err := readInt32(b, p, e) 436 if err != nil { 437 return time.Time{}, err 438 } 439 return time.Unix(int64(i32), 0), nil 440 } 441 442 // Read bytes representing an eight bit integer. 443 func readInt8(b []byte, p *int, e *binary.ByteOrder) (i int8, err error) { 444 if *p < 0 { 445 return 0, fmt.Errorf("%d cannot be less than zero", *p) 446 } 447 448 if (*p + 1) > len(b) { 449 return 0, fmt.Errorf("%s's length is less than %d", b, *p+1) 450 } 451 buf := bytes.NewBuffer(b[*p : *p+1]) 452 binary.Read(buf, *e, &i) 453 *p++ 454 return 455 } 456 457 // Read bytes representing a sixteen bit integer. 458 func readInt16(b []byte, p *int, e *binary.ByteOrder) (i int16, err error) { 459 if *p < 0 { 460 return 0, fmt.Errorf("%d cannot be less than zero", *p) 461 } 462 463 if (*p + 2) > len(b) { 464 return 0, fmt.Errorf("%s's length is less than %d", b, *p+2) 465 } 466 467 buf := bytes.NewBuffer(b[*p : *p+2]) 468 binary.Read(buf, *e, &i) 469 *p += 2 470 return 471 } 472 473 // Read bytes representing a thirty two bit integer. 474 func readInt32(b []byte, p *int, e *binary.ByteOrder) (i int32, err error) { 475 if *p < 0 { 476 return 0, fmt.Errorf("%d cannot be less than zero", *p) 477 } 478 479 if (*p + 4) > len(b) { 480 return 0, fmt.Errorf("%s's length is less than %d", b, *p+4) 481 } 482 483 buf := bytes.NewBuffer(b[*p : *p+4]) 484 binary.Read(buf, *e, &i) 485 *p += 4 486 return 487 } 488 489 func readBytes(b []byte, p *int, s int, e *binary.ByteOrder) ([]byte, error) { 490 if s < 0 { 491 return nil, fmt.Errorf("%d cannot be less than zero", s) 492 } 493 i := *p + s 494 if i > len(b) { 495 return nil, fmt.Errorf("%s's length is greater than %d", b, i) 496 } 497 buf := bytes.NewBuffer(b[*p:i]) 498 r := make([]byte, s) 499 if err := binary.Read(buf, *e, &r); err != nil { 500 return nil, err 501 } 502 *p += s 503 return r, nil 504 } 505 506 func isNativeEndianLittle() bool { 507 var x = 0x012345678 508 var p = unsafe.Pointer(&x) 509 var bp = (*[4]byte)(p) 510 511 var endian bool 512 if 0x01 == bp[0] { 513 endian = false 514 } else if (0x78 & 0xff) == (bp[0] & 0xff) { 515 endian = true 516 } else { 517 // Default to big endian 518 endian = false 519 } 520 return endian 521 } 522 523 // JSON return information about the keys held in the keytab in a JSON format. 524 func (kt *Keytab) JSON() (string, error) { 525 b, err := json.MarshalIndent(kt, "", " ") 526 if err != nil { 527 return "", err 528 } 529 return string(b), nil 530 }