keytab.go 6.8 KB

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