keytab.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Implementation of Kerberos keytabs: https://web.mit.edu/kerberos/krb5-devel/doc/formats/keytab_file_format.html.
  2. package keytab
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. "errors"
  7. "github.com/jcmturner/gokrb5/types"
  8. "io/ioutil"
  9. "time"
  10. "unsafe"
  11. )
  12. // Keytab struct.
  13. type Keytab struct {
  14. Version uint16
  15. Entries []KeytabEntry
  16. }
  17. // Keytab entry struct.
  18. type KeytabEntry struct {
  19. Principal Principal
  20. Timestamp time.Time
  21. KVNO8 uint8
  22. Key types.EncryptionKey
  23. KVNO uint32
  24. }
  25. // Keytab entry principal struct.
  26. type Principal struct {
  27. NumComponents int16
  28. Realm string
  29. Components []string
  30. NameType int32
  31. }
  32. //Create new, empty Keytab type.
  33. func NewKeytab() Keytab {
  34. var e []KeytabEntry
  35. return Keytab{
  36. Version: 0,
  37. Entries: e,
  38. }
  39. }
  40. // Get the EncryptionKey from the Keytab for the newest entry with the required kvno, etype and matching principal.
  41. func (kt *Keytab) GetEncryptionKey(username, realm string, kvno, etype int) (types.EncryptionKey, error) {
  42. var key types.EncryptionKey
  43. var t time.Time
  44. for _, k := range kt.Entries {
  45. if k.Principal.Realm == realm && int(k.Key.KeyType) == etype && (int(k.KVNO) == kvno || kvno == 0) && k.Timestamp.After(t) {
  46. for _, n := range k.Principal.Components {
  47. if n == username {
  48. key = k.Key
  49. }
  50. }
  51. }
  52. }
  53. if len(key.KeyValue) < 1 {
  54. return key, errors.New("Matching key not found in keytab")
  55. }
  56. return key, nil
  57. }
  58. // Create a new Keytab entry.
  59. func newKeytabEntry() KeytabEntry {
  60. var b []byte
  61. return KeytabEntry{
  62. Principal: newPrincipal(),
  63. Timestamp: time.Time{},
  64. KVNO8: 0,
  65. Key: types.EncryptionKey{
  66. KeyType: 0,
  67. KeyValue: b,
  68. },
  69. KVNO: 0,
  70. }
  71. }
  72. // Create a new principal.
  73. func newPrincipal() Principal {
  74. var c []string
  75. return Principal{
  76. NumComponents: 0,
  77. Realm: "",
  78. Components: c,
  79. NameType: 0,
  80. }
  81. }
  82. // Load a Keytab file into a Keytab type.
  83. func Load(ktPath string) (kt Keytab, err error) {
  84. k, err := ioutil.ReadFile(ktPath)
  85. if err != nil {
  86. return
  87. }
  88. return Parse(k)
  89. }
  90. // Parse byte slice of Keytab data into Keytab type.
  91. func Parse(b []byte) (kt Keytab, err error) {
  92. //The first byte of the file always has the value 5
  93. if int8(b[0]) != 5 {
  94. err = errors.New("Invalid keytab data. First byte does not equal 5")
  95. return
  96. }
  97. //Get keytab version
  98. //The second byte contains the version number (1 or 2)
  99. kt.Version = uint16(b[1])
  100. if kt.Version != 1 && kt.Version != 2 {
  101. err = errors.New("Invalid keytab data. Keytab version is neither 1 nor 2")
  102. return
  103. }
  104. //Version 1 of the file format uses native byte order for integer representations. Version 2 always uses big-endian byte order
  105. var endian binary.ByteOrder
  106. endian = binary.BigEndian
  107. if kt.Version == 1 && isNativeEndianLittle() {
  108. endian = binary.LittleEndian
  109. }
  110. /*
  111. After the two-byte version indicator, the file contains a sequence of signed 32-bit record lengths followed by key records or holes.
  112. A positive record length indicates a valid key entry whose size is equal to or less than the record length.
  113. A negative length indicates a zero-filled hole whose size is the inverse of the length.
  114. A length of 0 indicates the end of the file.
  115. */
  116. // n tracks position in the byte array
  117. n := 2
  118. l := read_int32(b, &n, &endian)
  119. for l != 0 {
  120. if l < 0 {
  121. //Zero padded so skip over
  122. l = l * -1
  123. n = n + int(l)
  124. }
  125. if l > 0 {
  126. //fmt.Printf("Bytes for entry: %v\n", b[p:p+int(l)])
  127. eb := b[n : n+int(l)]
  128. n = n + int(l)
  129. ke := newKeytabEntry()
  130. // p keeps track as to where we are in the byte stream
  131. var p int
  132. parse_principal(eb, &p, &kt, &ke, &endian)
  133. ke.Timestamp = read_timestamp(eb, &p, &endian)
  134. ke.KVNO8 = uint8(read_int8(eb, &p, &endian))
  135. ke.Key.KeyType = int(read_int16(eb, &p, &endian))
  136. key_len := int(read_int16(eb, &p, &endian))
  137. ke.Key.KeyValue = read_Bytes(eb, &p, key_len, &endian)
  138. //The 32-bit key version overrides the 8-bit key version.
  139. // To determine if it is present, the implementation must check that at least 4 bytes remain in the record after the other fields are read,
  140. // and that the value of the 32-bit integer contained in those bytes is non-zero.
  141. if len(eb)-p >= 4 {
  142. // The 32-bit key may be present
  143. ke.KVNO = uint32(read_int32(eb, &p, &endian))
  144. }
  145. if ke.KVNO == 0 {
  146. // 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
  147. ke.KVNO = uint32(ke.KVNO8)
  148. }
  149. // Add the entry to the keytab
  150. kt.Entries = append(kt.Entries, ke)
  151. }
  152. // Check if there are still 4 bytes left to read
  153. if len(b[n:]) < 4 {
  154. break
  155. }
  156. // Read the size of the next entry
  157. l = read_int32(b, &n, &endian)
  158. }
  159. return
  160. }
  161. // Parse the Keytab bytes of a principal into a Keytab entry's principal.
  162. func parse_principal(b []byte, p *int, kt *Keytab, ke *KeytabEntry, e *binary.ByteOrder) (err error) {
  163. ke.Principal.NumComponents = read_int16(b, p, e)
  164. if kt.Version == 1 {
  165. //In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2
  166. ke.Principal.NumComponents -= 1
  167. }
  168. len_realm := read_int16(b, p, e)
  169. ke.Principal.Realm = string(read_Bytes(b, p, int(len_realm), e))
  170. for i := 0; i < int(ke.Principal.NumComponents); i++ {
  171. l := read_int16(b, p, e)
  172. ke.Principal.Components = append(ke.Principal.Components, string(read_Bytes(b, p, int(l), e)))
  173. }
  174. if kt.Version != 1 {
  175. //Name Type is omitted in version 1
  176. ke.Principal.NameType = read_int32(b, p, e)
  177. }
  178. return
  179. }
  180. // Read bytes representing a timestamp.
  181. func read_timestamp(b []byte, p *int, e *binary.ByteOrder) time.Time {
  182. return time.Unix(int64(read_int32(b, p, e)), 0)
  183. }
  184. // Read bytes representing an eight bit integer.
  185. func read_int8(b []byte, p *int, e *binary.ByteOrder) (i int8) {
  186. buf := bytes.NewBuffer(b[*p : *p+1])
  187. binary.Read(buf, *e, &i)
  188. *p += 1
  189. return
  190. }
  191. // Read bytes representing a sixteen bit integer.
  192. func read_int16(b []byte, p *int, e *binary.ByteOrder) (i int16) {
  193. buf := bytes.NewBuffer(b[*p : *p+2])
  194. binary.Read(buf, *e, &i)
  195. *p += 2
  196. return
  197. }
  198. // Read bytes representing a thirty two bit integer.
  199. func read_int32(b []byte, p *int, e *binary.ByteOrder) (i int32) {
  200. buf := bytes.NewBuffer(b[*p : *p+4])
  201. binary.Read(buf, *e, &i)
  202. *p += 4
  203. return
  204. }
  205. func read_Bytes(b []byte, p *int, s int, e *binary.ByteOrder) []byte {
  206. buf := bytes.NewBuffer(b[*p : *p+s])
  207. r := make([]byte, s)
  208. binary.Read(buf, *e, &r)
  209. *p += s
  210. return r
  211. }
  212. func isNativeEndianLittle() bool {
  213. var x int = 0x012345678
  214. var p unsafe.Pointer = unsafe.Pointer(&x)
  215. var bp *[4]byte = (*[4]byte)(p)
  216. var endian bool
  217. if 0x01 == bp[0] {
  218. endian = false
  219. } else if (0x78 & 0xff) == (bp[0] & 0xff) {
  220. endian = true
  221. } else {
  222. // Default to big endian
  223. endian = false
  224. }
  225. return endian
  226. }