KDCRep.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package messages
  2. // Reference: https://www.ietf.org/rfc/rfc4120.txt
  3. // Section: 5.4.2
  4. import (
  5. "fmt"
  6. "time"
  7. "github.com/jcmturner/gofork/encoding/asn1"
  8. "gopkg.in/jcmturner/gokrb5.v7/config"
  9. "gopkg.in/jcmturner/gokrb5.v7/credentials"
  10. "gopkg.in/jcmturner/gokrb5.v7/crypto"
  11. "gopkg.in/jcmturner/gokrb5.v7/iana/asnAppTag"
  12. "gopkg.in/jcmturner/gokrb5.v7/iana/flags"
  13. "gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
  14. "gopkg.in/jcmturner/gokrb5.v7/iana/msgtype"
  15. "gopkg.in/jcmturner/gokrb5.v7/iana/patype"
  16. "gopkg.in/jcmturner/gokrb5.v7/krberror"
  17. "gopkg.in/jcmturner/gokrb5.v7/types"
  18. )
  19. type marshalKDCRep struct {
  20. PVNO int `asn1:"explicit,tag:0"`
  21. MsgType int `asn1:"explicit,tag:1"`
  22. PAData types.PADataSequence `asn1:"explicit,optional,tag:2"`
  23. CRealm string `asn1:"generalstring,explicit,tag:3"`
  24. CName types.PrincipalName `asn1:"explicit,tag:4"`
  25. // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
  26. Ticket asn1.RawValue `asn1:"explicit,tag:5"`
  27. EncPart types.EncryptedData `asn1:"explicit,tag:6"`
  28. }
  29. // KDCRepFields represents the KRB_KDC_REP fields.
  30. type KDCRepFields struct {
  31. PVNO int
  32. MsgType int
  33. PAData []types.PAData
  34. CRealm string
  35. CName types.PrincipalName
  36. Ticket Ticket
  37. EncPart types.EncryptedData
  38. DecryptedEncPart EncKDCRepPart
  39. }
  40. // ASRep implements RFC 4120 KRB_AS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
  41. type ASRep struct {
  42. KDCRepFields
  43. }
  44. // TGSRep implements RFC 4120 KRB_TGS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
  45. type TGSRep struct {
  46. KDCRepFields
  47. }
  48. // EncKDCRepPart is the encrypted part of KRB_KDC_REP.
  49. type EncKDCRepPart struct {
  50. Key types.EncryptionKey `asn1:"explicit,tag:0"`
  51. LastReqs []LastReq `asn1:"explicit,tag:1"`
  52. Nonce int `asn1:"explicit,tag:2"`
  53. KeyExpiration time.Time `asn1:"generalized,explicit,optional,tag:3"`
  54. Flags asn1.BitString `asn1:"explicit,tag:4"`
  55. AuthTime time.Time `asn1:"generalized,explicit,tag:5"`
  56. StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
  57. EndTime time.Time `asn1:"generalized,explicit,tag:7"`
  58. RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"`
  59. SRealm string `asn1:"generalstring,explicit,tag:9"`
  60. SName types.PrincipalName `asn1:"explicit,tag:10"`
  61. CAddr []types.HostAddress `asn1:"explicit,optional,tag:11"`
  62. EncPAData types.PADataSequence `asn1:"explicit,optional,tag:12"`
  63. }
  64. // LastReq part of KRB_KDC_REP.
  65. type LastReq struct {
  66. LRType int32 `asn1:"explicit,tag:0"`
  67. LRValue time.Time `asn1:"generalized,explicit,tag:1"`
  68. }
  69. // Unmarshal bytes b into the ASRep struct.
  70. func (k *ASRep) Unmarshal(b []byte) error {
  71. var m marshalKDCRep
  72. _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREP))
  73. if err != nil {
  74. return processUnmarshalReplyError(b, err)
  75. }
  76. if m.MsgType != msgtype.KRB_AS_REP {
  77. return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AS_REP. Expected: %v; Actual: %v", msgtype.KRB_AS_REP, m.MsgType)
  78. }
  79. //Process the raw ticket within
  80. tkt, err := unmarshalTicket(m.Ticket.Bytes)
  81. if err != nil {
  82. return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within AS_REP")
  83. }
  84. k.KDCRepFields = KDCRepFields{
  85. PVNO: m.PVNO,
  86. MsgType: m.MsgType,
  87. PAData: m.PAData,
  88. CRealm: m.CRealm,
  89. CName: m.CName,
  90. Ticket: tkt,
  91. EncPart: m.EncPart,
  92. }
  93. return nil
  94. }
  95. // Unmarshal bytes b into the TGSRep struct.
  96. func (k *TGSRep) Unmarshal(b []byte) error {
  97. var m marshalKDCRep
  98. _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREP))
  99. if err != nil {
  100. return processUnmarshalReplyError(b, err)
  101. }
  102. if m.MsgType != msgtype.KRB_TGS_REP {
  103. return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an TGS_REP. Expected: %v; Actual: %v", msgtype.KRB_TGS_REP, m.MsgType)
  104. }
  105. //Process the raw ticket within
  106. tkt, err := unmarshalTicket(m.Ticket.Bytes)
  107. if err != nil {
  108. return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within TGS_REP")
  109. }
  110. k.KDCRepFields = KDCRepFields{
  111. PVNO: m.PVNO,
  112. MsgType: m.MsgType,
  113. PAData: m.PAData,
  114. CRealm: m.CRealm,
  115. CName: m.CName,
  116. Ticket: tkt,
  117. EncPart: m.EncPart,
  118. }
  119. return nil
  120. }
  121. // Unmarshal bytes b into encrypted part of KRB_KDC_REP.
  122. func (e *EncKDCRepPart) Unmarshal(b []byte) error {
  123. _, err := asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncASRepPart))
  124. if err != nil {
  125. // Try using tag 26
  126. /* Ref: RFC 4120
  127. Compatibility note: Some implementations unconditionally send an
  128. encrypted EncTGSRepPart (application tag number 26) in this field
  129. regardless of whether the reply is a AS-REP or a TGS-REP. In the
  130. interest of compatibility, implementors MAY relax the check on the
  131. tag number of the decrypted ENC-PART.*/
  132. _, err = asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncTGSRepPart))
  133. if err != nil {
  134. return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part within KDC_REP")
  135. }
  136. }
  137. return nil
  138. }
  139. // DecryptEncPart decrypts the encrypted part of an AS_REP.
  140. func (k *ASRep) DecryptEncPart(c *credentials.Credentials) (types.EncryptionKey, error) {
  141. var key types.EncryptionKey
  142. var err error
  143. if c.HasKeytab() {
  144. key, err = c.Keytab().GetEncryptionKey(k.CName, k.CRealm, k.EncPart.KVNO, k.EncPart.EType)
  145. if err != nil {
  146. return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
  147. }
  148. }
  149. if c.HasPassword() {
  150. key, _, err = crypto.GetKeyFromPassword(c.Password(), k.CName, k.CRealm, k.EncPart.EType, k.PAData)
  151. if err != nil {
  152. return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
  153. }
  154. }
  155. if !c.HasKeytab() && !c.HasPassword() {
  156. return key, krberror.NewErrorf(krberror.DecryptingError, "no secret available in credentials to perform decryption of AS_REP encrypted part")
  157. }
  158. b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.AS_REP_ENCPART)
  159. if err != nil {
  160. return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
  161. }
  162. var denc EncKDCRepPart
  163. err = denc.Unmarshal(b)
  164. if err != nil {
  165. return key, krberror.Errorf(err, krberror.EncodingError, "error unmarshaling decrypted encpart of AS_REP")
  166. }
  167. k.DecryptedEncPart = denc
  168. return key, nil
  169. }
  170. // Verify checks the validity of AS_REP message.
  171. func (k *ASRep) Verify(cfg *config.Config, creds *credentials.Credentials, asReq ASReq) (bool, error) {
  172. //Ref RFC 4120 Section 3.1.5
  173. if k.CName.NameType != asReq.ReqBody.CName.NameType || k.CName.NameString == nil {
  174. return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName)
  175. }
  176. for i := range k.CName.NameString {
  177. if k.CName.NameString[i] != asReq.ReqBody.CName.NameString[i] {
  178. return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName)
  179. }
  180. }
  181. if k.CRealm != asReq.ReqBody.Realm {
  182. return false, krberror.NewErrorf(krberror.KRBMsgError, "CRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.CRealm)
  183. }
  184. key, err := k.DecryptEncPart(creds)
  185. if err != nil {
  186. return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting EncPart of AS_REP")
  187. }
  188. if k.DecryptedEncPart.Nonce != asReq.ReqBody.Nonce {
  189. return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
  190. }
  191. if k.DecryptedEncPart.SName.NameType != asReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
  192. return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", asReq.ReqBody.SName, k.DecryptedEncPart.SName)
  193. }
  194. for i := range k.CName.NameString {
  195. if k.DecryptedEncPart.SName.NameString[i] != asReq.ReqBody.SName.NameString[i] {
  196. return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.SName, k.DecryptedEncPart.SName)
  197. }
  198. }
  199. if k.DecryptedEncPart.SRealm != asReq.ReqBody.Realm {
  200. return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
  201. }
  202. if len(asReq.ReqBody.Addresses) > 0 {
  203. if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, asReq.ReqBody.Addresses) {
  204. return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the AS_REP does not match those listed in the AS_REQ")
  205. }
  206. }
  207. t := time.Now().UTC()
  208. if t.Sub(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(t) > cfg.LibDefaults.Clockskew {
  209. return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds", cfg.LibDefaults.Clockskew.Seconds())
  210. }
  211. // RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11
  212. if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&k.DecryptedEncPart.Flags, flags.EncPARep) {
  213. if len(k.DecryptedEncPart.EncPAData) < 2 || !k.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) {
  214. return false, krberror.NewErrorf(krberror.KRBMsgError, "KDC did not respond appropriately to FAST negotiation")
  215. }
  216. for _, pa := range k.DecryptedEncPart.EncPAData {
  217. if pa.PADataType == patype.PA_REQ_ENC_PA_REP {
  218. var pafast types.PAReqEncPARep
  219. err := pafast.Unmarshal(pa.PADataValue)
  220. if err != nil {
  221. return false, krberror.Errorf(err, krberror.EncodingError, "KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP")
  222. }
  223. etype, err := crypto.GetChksumEtype(pafast.ChksumType)
  224. if err != nil {
  225. return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response error")
  226. }
  227. ab, _ := asReq.Marshal()
  228. if !etype.VerifyChecksum(key.KeyValue, ab, pafast.Chksum, keyusage.KEY_USAGE_AS_REQ) {
  229. return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response checksum invalid")
  230. }
  231. }
  232. }
  233. }
  234. return true, nil
  235. }
  236. // DecryptEncPart decrypts the encrypted part of an TGS_REP.
  237. func (k *TGSRep) DecryptEncPart(key types.EncryptionKey) error {
  238. b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.TGS_REP_ENCPART_SESSION_KEY)
  239. if err != nil {
  240. return krberror.Errorf(err, krberror.DecryptingError, "error decrypting TGS_REP EncPart")
  241. }
  242. var denc EncKDCRepPart
  243. err = denc.Unmarshal(b)
  244. if err != nil {
  245. return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part")
  246. }
  247. k.DecryptedEncPart = denc
  248. return nil
  249. }
  250. // Verify checks the validity of the TGS_REP message.
  251. func (k *TGSRep) Verify(cfg *config.Config, tgsReq TGSReq) (bool, error) {
  252. if k.CName.NameType != tgsReq.ReqBody.CName.NameType || k.CName.NameString == nil {
  253. return false, krberror.NewErrorf(krberror.KRBMsgError, "CName type in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
  254. }
  255. for i := range k.CName.NameString {
  256. if k.CName.NameString[i] != tgsReq.ReqBody.CName.NameString[i] {
  257. return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
  258. }
  259. }
  260. if k.Ticket.Realm != tgsReq.ReqBody.Realm {
  261. return false, krberror.NewErrorf(krberror.KRBMsgError, "realm in response ticket does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.Ticket.Realm)
  262. }
  263. if k.DecryptedEncPart.Nonce != tgsReq.ReqBody.Nonce {
  264. return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
  265. }
  266. //if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil {
  267. // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.Ticket.SName)
  268. //}
  269. //for i := range k.Ticket.SName.NameString {
  270. // if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
  271. // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.Ticket.SName)
  272. // }
  273. //}
  274. //if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
  275. // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
  276. //}
  277. //for i := range k.DecryptedEncPart.SName.NameString {
  278. // if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
  279. // return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
  280. // }
  281. //}
  282. if k.DecryptedEncPart.SRealm != tgsReq.ReqBody.Realm {
  283. return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
  284. }
  285. if len(k.DecryptedEncPart.CAddr) > 0 {
  286. if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, tgsReq.ReqBody.Addresses) {
  287. return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the TGS_REP does not match those listed in the TGS_REQ")
  288. }
  289. }
  290. if time.Since(k.DecryptedEncPart.StartTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.StartTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
  291. if time.Since(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
  292. return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds.", cfg.LibDefaults.Clockskew.Seconds())
  293. }
  294. }
  295. return true, nil
  296. }