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