Ticket.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. package messages
  2. import (
  3. "crypto/rand"
  4. "fmt"
  5. "log"
  6. "time"
  7. "github.com/jcmturner/gofork/encoding/asn1"
  8. "github.com/jcmturner/gokrb5/v8/asn1tools"
  9. "github.com/jcmturner/gokrb5/v8/crypto"
  10. "github.com/jcmturner/gokrb5/v8/iana"
  11. "github.com/jcmturner/gokrb5/v8/iana/adtype"
  12. "github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
  13. "github.com/jcmturner/gokrb5/v8/iana/errorcode"
  14. "github.com/jcmturner/gokrb5/v8/iana/flags"
  15. "github.com/jcmturner/gokrb5/v8/iana/keyusage"
  16. "github.com/jcmturner/gokrb5/v8/keytab"
  17. "github.com/jcmturner/gokrb5/v8/krberror"
  18. "github.com/jcmturner/gokrb5/v8/pac"
  19. "github.com/jcmturner/gokrb5/v8/types"
  20. )
  21. // Reference: https://www.ietf.org/rfc/rfc4120.txt
  22. // Section: 5.3
  23. // Ticket implements the Kerberos ticket.
  24. type Ticket struct {
  25. TktVNO int `asn1:"explicit,tag:0"`
  26. Realm string `asn1:"generalstring,explicit,tag:1"`
  27. SName types.PrincipalName `asn1:"explicit,tag:2"`
  28. EncPart types.EncryptedData `asn1:"explicit,tag:3"`
  29. DecryptedEncPart EncTicketPart `asn1:"optional"` // Not part of ASN1 bytes so marked as optional so unmarshalling works
  30. }
  31. // EncTicketPart is the encrypted part of the Ticket.
  32. type EncTicketPart struct {
  33. Flags asn1.BitString `asn1:"explicit,tag:0"`
  34. Key types.EncryptionKey `asn1:"explicit,tag:1"`
  35. CRealm string `asn1:"generalstring,explicit,tag:2"`
  36. CName types.PrincipalName `asn1:"explicit,tag:3"`
  37. Transited TransitedEncoding `asn1:"explicit,tag:4"`
  38. AuthTime time.Time `asn1:"generalized,explicit,tag:5"`
  39. StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
  40. EndTime time.Time `asn1:"generalized,explicit,tag:7"`
  41. RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"`
  42. CAddr types.HostAddresses `asn1:"explicit,optional,tag:9"`
  43. AuthorizationData types.AuthorizationData `asn1:"explicit,optional,tag:10"`
  44. }
  45. // TransitedEncoding part of the ticket's encrypted part.
  46. type TransitedEncoding struct {
  47. TRType int32 `asn1:"explicit,tag:0"`
  48. Contents []byte `asn1:"explicit,tag:1"`
  49. }
  50. // NewTicket creates a new Ticket instance.
  51. func NewTicket(cname types.PrincipalName, crealm string, sname types.PrincipalName, srealm string, flags asn1.BitString, sktab *keytab.Keytab, eTypeID int32, kvno int, authTime, startTime, endTime, renewTill time.Time) (Ticket, types.EncryptionKey, error) {
  52. etype, err := crypto.GetEtype(eTypeID)
  53. if err != nil {
  54. return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting etype for new ticket")
  55. }
  56. ks := etype.GetKeyByteSize()
  57. kv := make([]byte, ks, ks)
  58. rand.Read(kv)
  59. sessionKey := types.EncryptionKey{
  60. KeyType: eTypeID,
  61. KeyValue: kv,
  62. }
  63. etp := EncTicketPart{
  64. Flags: flags,
  65. Key: sessionKey,
  66. CRealm: crealm,
  67. CName: cname,
  68. Transited: TransitedEncoding{},
  69. AuthTime: authTime,
  70. StartTime: startTime,
  71. EndTime: endTime,
  72. RenewTill: renewTill,
  73. }
  74. b, err := asn1.Marshal(etp)
  75. if err != nil {
  76. return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncodingError, "error marshalling ticket encpart")
  77. }
  78. b = asn1tools.AddASNAppTag(b, asnAppTag.EncTicketPart)
  79. skey, _, err := sktab.GetEncryptionKey(sname, srealm, kvno, eTypeID)
  80. if err != nil {
  81. return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting encryption key for new ticket")
  82. }
  83. ed, err := crypto.GetEncryptedData(b, skey, keyusage.KDC_REP_TICKET, kvno)
  84. if err != nil {
  85. return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error encrypting ticket encpart")
  86. }
  87. tkt := Ticket{
  88. TktVNO: iana.PVNO,
  89. Realm: srealm,
  90. SName: sname,
  91. EncPart: ed,
  92. }
  93. return tkt, sessionKey, nil
  94. }
  95. // Unmarshal bytes b into a Ticket struct.
  96. func (t *Ticket) Unmarshal(b []byte) error {
  97. _, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.Ticket))
  98. return err
  99. }
  100. // Marshal the Ticket.
  101. func (t *Ticket) Marshal() ([]byte, error) {
  102. b, err := asn1.Marshal(*t)
  103. if err != nil {
  104. return nil, err
  105. }
  106. b = asn1tools.AddASNAppTag(b, asnAppTag.Ticket)
  107. return b, nil
  108. }
  109. // Unmarshal bytes b into the EncTicketPart struct.
  110. func (t *EncTicketPart) Unmarshal(b []byte) error {
  111. _, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.EncTicketPart))
  112. return err
  113. }
  114. // unmarshalTicket returns a ticket from the bytes provided.
  115. func unmarshalTicket(b []byte) (t Ticket, err error) {
  116. err = t.Unmarshal(b)
  117. return
  118. }
  119. // UnmarshalTicketsSequence returns a slice of Tickets from a raw ASN1 value.
  120. func unmarshalTicketsSequence(in asn1.RawValue) ([]Ticket, error) {
  121. //This is a workaround to a asn1 decoding issue in golang - https://github.com/golang/go/issues/17321. It's not pretty I'm afraid
  122. //We pull out raw values from the larger raw value (that is actually the data of the sequence of raw values) and track our position moving along the data.
  123. b := in.Bytes
  124. // Ignore the head of the asn1 stream (1 byte for tag and those for the length) as this is what tells us its a sequence but we're handling it ourselves
  125. p := 1 + asn1tools.GetNumberBytesInLengthHeader(in.Bytes)
  126. var tkts []Ticket
  127. var raw asn1.RawValue
  128. for p < (len(b)) {
  129. _, err := asn1.UnmarshalWithParams(b[p:], &raw, fmt.Sprintf("application,tag:%d", asnAppTag.Ticket))
  130. if err != nil {
  131. return nil, fmt.Errorf("unmarshaling sequence of tickets failed getting length of ticket: %v", err)
  132. }
  133. t, err := unmarshalTicket(b[p:])
  134. if err != nil {
  135. return nil, fmt.Errorf("unmarshaling sequence of tickets failed: %v", err)
  136. }
  137. p += len(raw.FullBytes)
  138. tkts = append(tkts, t)
  139. }
  140. MarshalTicketSequence(tkts)
  141. return tkts, nil
  142. }
  143. // MarshalTicketSequence marshals a slice of Tickets returning an ASN1 raw value containing the ticket sequence.
  144. func MarshalTicketSequence(tkts []Ticket) (asn1.RawValue, error) {
  145. raw := asn1.RawValue{
  146. Class: 2,
  147. IsCompound: true,
  148. }
  149. if len(tkts) < 1 {
  150. // There are no tickets to marshal
  151. return raw, nil
  152. }
  153. var btkts []byte
  154. for i, t := range tkts {
  155. b, err := t.Marshal()
  156. if err != nil {
  157. return raw, fmt.Errorf("error marshaling ticket number %d in sequence of tickets", i+1)
  158. }
  159. btkts = append(btkts, b...)
  160. }
  161. // The ASN1 wrapping consists of 2 bytes:
  162. // 1st byte -> Identifier Octet - In this case an OCTET STRING (ASN TAG
  163. // 2nd byte -> The length (this will be the size indicated in the input bytes + 2 for the additional bytes we add here.
  164. // Application Tag:
  165. //| Byte: | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
  166. //| Value: | 0 | 1 | 1 | From the RFC spec 4120 |
  167. //| Explanation | Defined by the ASN1 encoding rules for an application tag | A value of 1 indicates a constructed type | The ASN Application tag value |
  168. btkts = append(asn1tools.MarshalLengthBytes(len(btkts)), btkts...)
  169. btkts = append([]byte{byte(32 + asn1.TagSequence)}, btkts...)
  170. raw.Bytes = btkts
  171. // If we need to create the full bytes then identifier octet is "context-specific" = 128 + "constructed" + 32 + the wrapping explicit tag (11)
  172. //fmt.Fprintf(os.Stderr, "mRaw fb: %v\n", raw.FullBytes)
  173. return raw, nil
  174. }
  175. // DecryptEncPart decrypts the encrypted part of the ticket.
  176. // The sname argument can be used to specify which service principal's key should be used to decrypt the ticket.
  177. // If nil is passed as the sname then the service principal specified within the ticket it used.
  178. func (t *Ticket) DecryptEncPart(keytab *keytab.Keytab, sname *types.PrincipalName) error {
  179. if sname == nil {
  180. sname = &t.SName
  181. }
  182. key, _, err := keytab.GetEncryptionKey(*sname, t.Realm, t.EncPart.KVNO, t.EncPart.EType)
  183. if err != nil {
  184. return NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err))
  185. }
  186. return t.Decrypt(key)
  187. }
  188. // Decrypt decrypts the encrypted part of the ticket using the key provided.
  189. func (t *Ticket) Decrypt(key types.EncryptionKey) error {
  190. b, err := crypto.DecryptEncPart(t.EncPart, key, keyusage.KDC_REP_TICKET)
  191. if err != nil {
  192. return fmt.Errorf("error decrypting Ticket EncPart: %v", err)
  193. }
  194. var denc EncTicketPart
  195. err = denc.Unmarshal(b)
  196. if err != nil {
  197. return fmt.Errorf("error unmarshaling encrypted part: %v", err)
  198. }
  199. t.DecryptedEncPart = denc
  200. return nil
  201. }
  202. // GetPACType returns a Microsoft PAC that has been extracted from the ticket and processed.
  203. func (t *Ticket) GetPACType(keytab *keytab.Keytab, sname *types.PrincipalName, l *log.Logger) (bool, pac.PACType, error) {
  204. var isPAC bool
  205. for _, ad := range t.DecryptedEncPart.AuthorizationData {
  206. if ad.ADType == adtype.ADIfRelevant {
  207. var ad2 types.AuthorizationData
  208. err := ad2.Unmarshal(ad.ADData)
  209. if err != nil {
  210. l.Printf("PAC authorization data could not be unmarshaled: %v", err)
  211. continue
  212. }
  213. if ad2[0].ADType == adtype.ADWin2KPAC {
  214. isPAC = true
  215. var p pac.PACType
  216. err = p.Unmarshal(ad2[0].ADData)
  217. if err != nil {
  218. return isPAC, p, fmt.Errorf("error unmarshaling PAC: %v", err)
  219. }
  220. if sname == nil {
  221. sname = &t.SName
  222. }
  223. key, _, err := keytab.GetEncryptionKey(*sname, t.Realm, t.EncPart.KVNO, t.EncPart.EType)
  224. if err != nil {
  225. return isPAC, p, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err))
  226. }
  227. err = p.ProcessPACInfoBuffers(key, l)
  228. return isPAC, p, err
  229. }
  230. }
  231. }
  232. return isPAC, pac.PACType{}, nil
  233. }
  234. // Valid checks it the ticket is currently valid. Max duration passed endtime passed in as argument.
  235. func (t *Ticket) Valid(d time.Duration) (bool, error) {
  236. // Check for future tickets or invalid tickets
  237. time := time.Now().UTC()
  238. if t.DecryptedEncPart.StartTime.Sub(time) > d || types.IsFlagSet(&t.DecryptedEncPart.Flags, flags.Invalid) {
  239. return false, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_TKT_NYV, "service ticket provided is not yet valid")
  240. }
  241. // Check for expired ticket
  242. if time.Sub(t.DecryptedEncPart.EndTime) > d {
  243. return false, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "service ticket provided has expired")
  244. }
  245. return true, nil
  246. }