ccache.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package credentials
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "github.com/jcmturner/asn1"
  7. "gopkg.in/jcmturner/gokrb5.v2/types"
  8. "io/ioutil"
  9. "strings"
  10. "time"
  11. "unsafe"
  12. )
  13. const (
  14. headerFieldTagKDCOffset = 1
  15. )
  16. // The first byte of the file always has the value 5.
  17. // The value of the second byte contains the version number (1 through 4)
  18. // Versions 1 and 2 of the file format use native byte order for integer representations.
  19. // Versions 3 and 4 always use big-endian byte order
  20. // After the two-byte version indicator, the file has three parts:
  21. // 1) the header (in version 4 only)
  22. // 2) the default principal name
  23. // 3) a sequence of credentials
  24. // CCache is the file credentials cache as define here: https://web.mit.edu/kerberos/krb5-latest/doc/formats/ccache_file_format.html
  25. type CCache struct {
  26. Version uint8
  27. Header header
  28. DefaultPrincipal principal
  29. Credentials []credential
  30. Path string
  31. }
  32. type header struct {
  33. length uint16
  34. fields []headerField
  35. }
  36. type headerField struct {
  37. tag uint16
  38. length uint16
  39. value []byte
  40. }
  41. // Credential cache entry principal struct.
  42. type principal struct {
  43. Realm string
  44. PrincipalName types.PrincipalName
  45. }
  46. type credential struct {
  47. Client principal
  48. Server principal
  49. Key types.EncryptionKey
  50. AuthTime time.Time
  51. StartTime time.Time
  52. EndTime time.Time
  53. RenewTill time.Time
  54. IsSKey bool
  55. TicketFlags asn1.BitString
  56. Addresses []types.HostAddress
  57. AuthData []types.AuthorizationDataEntry
  58. Ticket []byte
  59. SecondTicket []byte
  60. }
  61. // LoadCCache loads a credential cache file into a CCache type.
  62. func LoadCCache(cpath string) (CCache, error) {
  63. k, err := ioutil.ReadFile(cpath)
  64. if err != nil {
  65. return CCache{}, err
  66. }
  67. c, err := ParseCCache(k)
  68. c.Path = cpath
  69. return c, err
  70. }
  71. // ParseCCache byte slice of credential cache data into CCache type.
  72. func ParseCCache(b []byte) (c CCache, err error) {
  73. p := 0
  74. //The first byte of the file always has the value 5
  75. if int8(b[p]) != 5 {
  76. err = errors.New("Invalid credential cache data. First byte does not equal 5")
  77. return
  78. }
  79. p++
  80. //Get credential cache version
  81. //The second byte contains the version number (1 to 4)
  82. c.Version = uint8(b[p])
  83. if c.Version < 1 || c.Version > 4 {
  84. err = errors.New("Invalid credential cache data. Keytab version is not within 1 to 4")
  85. if err != nil {
  86. return
  87. }
  88. }
  89. p++
  90. //Version 1 or 2 of the file format uses native byte order for integer representations. Versions 3 & 4 always uses big-endian byte order
  91. var endian binary.ByteOrder
  92. endian = binary.BigEndian
  93. if (c.Version == 1 || c.Version == 2) && isNativeEndianLittle() {
  94. endian = binary.LittleEndian
  95. }
  96. if c.Version == 4 {
  97. err = parseHeader(b, &p, &c, &endian)
  98. if err != nil {
  99. return
  100. }
  101. }
  102. c.DefaultPrincipal = parsePrincipal(b, &p, &c, &endian)
  103. for p < len(b) {
  104. cred, e := parseCredential(b, &p, &c, &endian)
  105. if e != nil {
  106. err = e
  107. return
  108. }
  109. c.Credentials = append(c.Credentials, cred)
  110. }
  111. return
  112. }
  113. func parseHeader(b []byte, p *int, c *CCache, e *binary.ByteOrder) error {
  114. if c.Version != 4 {
  115. return errors.New("Credentials cache version is not 4 so there is no header to parse.")
  116. }
  117. h := header{}
  118. h.length = uint16(readInt16(b, p, e))
  119. for *p <= int(h.length) {
  120. f := headerField{}
  121. f.tag = uint16(readInt16(b, p, e))
  122. f.length = uint16(readInt16(b, p, e))
  123. f.value = b[*p : *p+int(f.length)]
  124. *p += int(f.length)
  125. if !f.valid() {
  126. return errors.New("Invalid credential cache header found")
  127. }
  128. h.fields = append(h.fields, f)
  129. }
  130. c.Header = h
  131. return nil
  132. }
  133. // Parse the Keytab bytes of a principal into a Keytab entry's principal.
  134. func parsePrincipal(b []byte, p *int, c *CCache, e *binary.ByteOrder) (princ principal) {
  135. if c.Version != 1 {
  136. //Name Type is omitted in version 1
  137. princ.PrincipalName.NameType = int(readInt32(b, p, e))
  138. }
  139. nc := int(readInt32(b, p, e))
  140. if c.Version == 1 {
  141. //In version 1 the number of components includes the realm. Minus 1 to make consistent with version 2
  142. nc--
  143. }
  144. lenRealm := readInt32(b, p, e)
  145. princ.Realm = string(readBytes(b, p, int(lenRealm), e))
  146. for i := 0; i < int(nc); i++ {
  147. l := readInt32(b, p, e)
  148. princ.PrincipalName.NameString = append(princ.PrincipalName.NameString, string(readBytes(b, p, int(l), e)))
  149. }
  150. return princ
  151. }
  152. func parseCredential(b []byte, p *int, c *CCache, e *binary.ByteOrder) (cred credential, err error) {
  153. cred.Client = parsePrincipal(b, p, c, e)
  154. cred.Server = parsePrincipal(b, p, c, e)
  155. key := types.EncryptionKey{}
  156. key.KeyType = int(readInt16(b, p, e))
  157. if c.Version == 3 {
  158. //repeated twice in version 3
  159. key.KeyType = int(readInt16(b, p, e))
  160. }
  161. key.KeyValue = readData(b, p, e)
  162. cred.Key = key
  163. cred.AuthTime = readTimestamp(b, p, e)
  164. cred.StartTime = readTimestamp(b, p, e)
  165. cred.EndTime = readTimestamp(b, p, e)
  166. cred.RenewTill = readTimestamp(b, p, e)
  167. if ik := readInt8(b, p, e); ik == 0 {
  168. cred.IsSKey = false
  169. } else {
  170. cred.IsSKey = true
  171. }
  172. cred.TicketFlags = types.NewKrbFlags()
  173. cred.TicketFlags.Bytes = readBytes(b, p, 4, e)
  174. l := int(readInt32(b, p, e))
  175. cred.Addresses = make([]types.HostAddress, l, l)
  176. for i := range cred.Addresses {
  177. cred.Addresses[i] = readAddress(b, p, e)
  178. }
  179. l = int(readInt32(b, p, e))
  180. cred.AuthData = make([]types.AuthorizationDataEntry, l, l)
  181. for i := range cred.AuthData {
  182. cred.AuthData[i] = readAuthDataEntry(b, p, e)
  183. }
  184. cred.Ticket = readData(b, p, e)
  185. cred.SecondTicket = readData(b, p, e)
  186. return
  187. }
  188. // GetClientPrincipalName returns a PrincipalName type for the client the credentials cache is for.
  189. func (c *CCache) GetClientPrincipalName() types.PrincipalName {
  190. return c.DefaultPrincipal.PrincipalName
  191. }
  192. // GetClientRealm returns the reals of the client the credentials cache is for.
  193. func (c *CCache) GetClientRealm() string {
  194. return c.DefaultPrincipal.Realm
  195. }
  196. // GetClientCredentials returns a Credentials object representing the client of the credentials cache.
  197. func (c *CCache) GetClientCredentials() *Credentials {
  198. return &Credentials{
  199. Username: c.DefaultPrincipal.PrincipalName.GetPrincipalNameString(),
  200. Realm: c.GetClientRealm(),
  201. CName: c.DefaultPrincipal.PrincipalName,
  202. }
  203. }
  204. // Contains tests if the cache contains a credential for the provided server PrincipalName
  205. func (c *CCache) Contains(p types.PrincipalName) bool {
  206. for _, cred := range c.Credentials {
  207. if cred.Server.PrincipalName.Equal(p) {
  208. return true
  209. }
  210. }
  211. return false
  212. }
  213. // GetEntry returns a specific credential for the PrincipalName provided.
  214. func (c *CCache) GetEntry(p types.PrincipalName) (credential, bool) {
  215. var cred credential
  216. var found bool
  217. for i := range c.Credentials {
  218. if c.Credentials[i].Server.PrincipalName.Equal(p) {
  219. cred = c.Credentials[i]
  220. found = true
  221. break
  222. }
  223. }
  224. if !found {
  225. return cred, false
  226. }
  227. return cred, true
  228. }
  229. // GetEntries filters out configuration entries an returns a slice of credentials.
  230. func (c *CCache) GetEntries() []credential {
  231. var creds []credential
  232. for _, cred := range c.Credentials {
  233. // Filter out configuration entries
  234. if strings.HasPrefix(cred.Server.Realm, "X-CACHECONF") {
  235. continue
  236. }
  237. creds = append(creds, cred)
  238. }
  239. return creds
  240. }
  241. func (h *headerField) valid() bool {
  242. // At this time there is only one defined header field.
  243. // Its tag value is 1, its length is always 8.
  244. // Its contents are two 32-bit integers giving the seconds and microseconds
  245. // of the time offset of the KDC relative to the client.
  246. // Adding this offset to the current time on the client should give the current time on the KDC, if that offset has not changed since the initial authentication.
  247. // Done as a switch in case other tag values are added in the future.
  248. switch h.tag {
  249. case headerFieldTagKDCOffset:
  250. if h.length != 8 || len(h.value) != 8 {
  251. return false
  252. }
  253. return true
  254. }
  255. return false
  256. }
  257. func readData(b []byte, p *int, e *binary.ByteOrder) []byte {
  258. l := readInt32(b, p, e)
  259. return readBytes(b, p, int(l), e)
  260. }
  261. func readAddress(b []byte, p *int, e *binary.ByteOrder) types.HostAddress {
  262. a := types.HostAddress{}
  263. a.AddrType = int(readInt16(b, p, e))
  264. a.Address = readData(b, p, e)
  265. return a
  266. }
  267. func readAuthDataEntry(b []byte, p *int, e *binary.ByteOrder) types.AuthorizationDataEntry {
  268. a := types.AuthorizationDataEntry{}
  269. a.ADType = int(readInt16(b, p, e))
  270. a.ADData = readData(b, p, e)
  271. return a
  272. }
  273. // Read bytes representing a timestamp.
  274. func readTimestamp(b []byte, p *int, e *binary.ByteOrder) time.Time {
  275. return time.Unix(int64(readInt32(b, p, e)), 0)
  276. }
  277. // Read bytes representing an eight bit integer.
  278. func readInt8(b []byte, p *int, e *binary.ByteOrder) (i int8) {
  279. buf := bytes.NewBuffer(b[*p : *p+1])
  280. binary.Read(buf, *e, &i)
  281. *p++
  282. return
  283. }
  284. // Read bytes representing a sixteen bit integer.
  285. func readInt16(b []byte, p *int, e *binary.ByteOrder) (i int16) {
  286. buf := bytes.NewBuffer(b[*p : *p+2])
  287. binary.Read(buf, *e, &i)
  288. *p += 2
  289. return
  290. }
  291. // Read bytes representing a thirty two bit integer.
  292. func readInt32(b []byte, p *int, e *binary.ByteOrder) (i int32) {
  293. buf := bytes.NewBuffer(b[*p : *p+4])
  294. binary.Read(buf, *e, &i)
  295. *p += 4
  296. return
  297. }
  298. func readBytes(b []byte, p *int, s int, e *binary.ByteOrder) []byte {
  299. buf := bytes.NewBuffer(b[*p : *p+s])
  300. r := make([]byte, s)
  301. binary.Read(buf, *e, &r)
  302. *p += s
  303. return r
  304. }
  305. func isNativeEndianLittle() bool {
  306. var x = 0x012345678
  307. var p = unsafe.Pointer(&x)
  308. var bp = (*[4]byte)(p)
  309. var endian bool
  310. if 0x01 == bp[0] {
  311. endian = false
  312. } else if (0x78 & 0xff) == (bp[0] & 0xff) {
  313. endian = true
  314. } else {
  315. // Default to big endian
  316. endian = false
  317. }
  318. return endian
  319. }