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  }