ccache.go 8.6 KB

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