github.com/jcmturner/gokrb5/v8@v8.4.4/messages/APReq.go (about) 1 package messages 2 3 import ( 4 "fmt" 5 "time" 6 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/asnAppTag" 12 "github.com/jcmturner/gokrb5/v8/iana/errorcode" 13 "github.com/jcmturner/gokrb5/v8/iana/keyusage" 14 "github.com/jcmturner/gokrb5/v8/iana/msgtype" 15 "github.com/jcmturner/gokrb5/v8/keytab" 16 "github.com/jcmturner/gokrb5/v8/krberror" 17 "github.com/jcmturner/gokrb5/v8/types" 18 ) 19 20 type marshalAPReq struct { 21 PVNO int `asn1:"explicit,tag:0"` 22 MsgType int `asn1:"explicit,tag:1"` 23 APOptions asn1.BitString `asn1:"explicit,tag:2"` 24 // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag 25 Ticket asn1.RawValue `asn1:"explicit,tag:3"` 26 EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"` 27 } 28 29 // APReq implements RFC 4120 KRB_AP_REQ: https://tools.ietf.org/html/rfc4120#section-5.5.1. 30 type APReq struct { 31 PVNO int `asn1:"explicit,tag:0"` 32 MsgType int `asn1:"explicit,tag:1"` 33 APOptions asn1.BitString `asn1:"explicit,tag:2"` 34 Ticket Ticket `asn1:"explicit,tag:3"` 35 EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"` 36 Authenticator types.Authenticator `asn1:"optional"` 37 } 38 39 // NewAPReq generates a new KRB_AP_REQ struct. 40 func NewAPReq(tkt Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) { 41 var a APReq 42 ed, err := encryptAuthenticator(auth, sessionKey, tkt) 43 if err != nil { 44 return a, krberror.Errorf(err, krberror.KRBMsgError, "error creating Authenticator for AP_REQ") 45 } 46 a = APReq{ 47 PVNO: iana.PVNO, 48 MsgType: msgtype.KRB_AP_REQ, 49 APOptions: types.NewKrbFlags(), 50 Ticket: tkt, 51 EncryptedAuthenticator: ed, 52 } 53 return a, nil 54 } 55 56 // Encrypt Authenticator 57 func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey, tkt Ticket) (types.EncryptedData, error) { 58 var ed types.EncryptedData 59 m, err := a.Marshal() 60 if err != nil { 61 return ed, krberror.Errorf(err, krberror.EncodingError, "marshaling error of EncryptedData form of Authenticator") 62 } 63 usage := authenticatorKeyUsage(tkt.SName) 64 ed, err = crypto.GetEncryptedData(m, sessionKey, uint32(usage), tkt.EncPart.KVNO) 65 if err != nil { 66 return ed, krberror.Errorf(err, krberror.EncryptingError, "error encrypting Authenticator") 67 } 68 return ed, nil 69 } 70 71 // DecryptAuthenticator decrypts the Authenticator within the AP_REQ. 72 // sessionKey may simply be the key within the decrypted EncPart of the ticket within the AP_REQ. 73 func (a *APReq) DecryptAuthenticator(sessionKey types.EncryptionKey) error { 74 usage := authenticatorKeyUsage(a.Ticket.SName) 75 ab, e := crypto.DecryptEncPart(a.EncryptedAuthenticator, sessionKey, uint32(usage)) 76 if e != nil { 77 return fmt.Errorf("error decrypting authenticator: %v", e) 78 } 79 err := a.Authenticator.Unmarshal(ab) 80 if err != nil { 81 return fmt.Errorf("error unmarshaling authenticator: %v", err) 82 } 83 return nil 84 } 85 86 func authenticatorKeyUsage(pn types.PrincipalName) int { 87 if pn.NameString[0] == "krbtgt" { 88 return keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR 89 } 90 return keyusage.AP_REQ_AUTHENTICATOR 91 } 92 93 // Unmarshal bytes b into the APReq struct. 94 func (a *APReq) Unmarshal(b []byte) error { 95 var m marshalAPReq 96 _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREQ)) 97 if err != nil { 98 return krberror.Errorf(err, krberror.EncodingError, "unmarshal error of AP_REQ") 99 } 100 if m.MsgType != msgtype.KRB_AP_REQ { 101 return NewKRBError(types.PrincipalName{}, "", errorcode.KRB_AP_ERR_MSG_TYPE, errorcode.Lookup(errorcode.KRB_AP_ERR_MSG_TYPE)) 102 } 103 a.PVNO = m.PVNO 104 a.MsgType = m.MsgType 105 a.APOptions = m.APOptions 106 a.EncryptedAuthenticator = m.EncryptedAuthenticator 107 a.Ticket, err = unmarshalTicket(m.Ticket.Bytes) 108 if err != nil { 109 return krberror.Errorf(err, krberror.EncodingError, "unmarshaling error of Ticket within AP_REQ") 110 } 111 return nil 112 } 113 114 // Marshal APReq struct. 115 func (a *APReq) Marshal() ([]byte, error) { 116 m := marshalAPReq{ 117 PVNO: a.PVNO, 118 MsgType: a.MsgType, 119 APOptions: a.APOptions, 120 EncryptedAuthenticator: a.EncryptedAuthenticator, 121 } 122 var b []byte 123 b, err := a.Ticket.Marshal() 124 if err != nil { 125 return b, err 126 } 127 m.Ticket = asn1.RawValue{ 128 Class: asn1.ClassContextSpecific, 129 IsCompound: true, 130 Tag: 3, 131 Bytes: b, 132 } 133 mk, err := asn1.Marshal(m) 134 if err != nil { 135 return mk, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AP_REQ") 136 } 137 mk = asn1tools.AddASNAppTag(mk, asnAppTag.APREQ) 138 return mk, nil 139 } 140 141 // Verify an AP_REQ using service's keytab, spn and max acceptable clock skew duration. 142 // The service ticket encrypted part and authenticator will be decrypted as part of this operation. 143 func (a *APReq) Verify(kt *keytab.Keytab, d time.Duration, cAddr types.HostAddress, snameOverride *types.PrincipalName) (bool, error) { 144 // Decrypt ticket's encrypted part with service key 145 //TODO decrypt with service's session key from its TGT is use-to-user. Need to figure out how to get TGT. 146 //if types.IsFlagSet(&a.APOptions, flags.APOptionUseSessionKey) { 147 // err := a.Ticket.Decrypt(tgt.DecryptedEncPart.Key) 148 // if err != nil { 149 // return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of ticket provided using session key") 150 // } 151 //} else { 152 // err := a.Ticket.DecryptEncPart(*kt, &a.Ticket.SName) 153 // if err != nil { 154 // return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided") 155 // } 156 //} 157 sname := &a.Ticket.SName 158 if snameOverride != nil { 159 sname = snameOverride 160 } 161 err := a.Ticket.DecryptEncPart(kt, sname) 162 if err != nil { 163 return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided") 164 } 165 166 // Check time validity of ticket 167 ok, err := a.Ticket.Valid(d) 168 if err != nil || !ok { 169 return ok, err 170 } 171 172 // Check client's address is listed in the client addresses in the ticket 173 if len(a.Ticket.DecryptedEncPart.CAddr) > 0 { 174 //If client addresses are present check if any of them match the source IP that sent the APReq 175 //If there is no match return KRB_AP_ERR_BADADDR error. 176 if !types.HostAddressesContains(a.Ticket.DecryptedEncPart.CAddr, cAddr) { 177 return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "client address not within the list contained in the service ticket") 178 } 179 } 180 181 // Decrypt authenticator with session key from ticket's encrypted part 182 err = a.DecryptAuthenticator(a.Ticket.DecryptedEncPart.Key) 183 if err != nil { 184 return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BAD_INTEGRITY, "could not decrypt authenticator") 185 } 186 187 // Check CName in authenticator is the same as that in the ticket 188 if !a.Authenticator.CName.Equal(a.Ticket.DecryptedEncPart.CName) { 189 return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BADMATCH, "CName in Authenticator does not match that in service ticket") 190 } 191 192 // Check the clock skew between the client and the service server 193 ct := a.Authenticator.CTime.Add(time.Duration(a.Authenticator.Cusec) * time.Microsecond) 194 t := time.Now().UTC() 195 if t.Sub(ct) > d || ct.Sub(t) > d { 196 return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_SKEW, fmt.Sprintf("clock skew with client too large. greater than %v seconds", d)) 197 } 198 return true, nil 199 }