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