ASExchange.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package client
  2. import (
  3. "gopkg.in/jcmturner/gokrb5.v6/crypto"
  4. "gopkg.in/jcmturner/gokrb5.v6/crypto/etype"
  5. "gopkg.in/jcmturner/gokrb5.v6/iana/errorcode"
  6. "gopkg.in/jcmturner/gokrb5.v6/iana/keyusage"
  7. "gopkg.in/jcmturner/gokrb5.v6/iana/patype"
  8. "gopkg.in/jcmturner/gokrb5.v6/krberror"
  9. "gopkg.in/jcmturner/gokrb5.v6/messages"
  10. "gopkg.in/jcmturner/gokrb5.v6/types"
  11. )
  12. // ASExchange performs an AS exchange for the client to retrieve a TGT.
  13. func (cl *Client) ASExchange(realm string, ASReq messages.ASReq, referral int) (messages.ASRep, error) {
  14. if ok, err := cl.IsConfigured(); !ok {
  15. return messages.ASRep{}, krberror.Errorf(err, krberror.ConfigError, "AS Exchange cannot be preformed")
  16. }
  17. b, err := ASReq.Marshal()
  18. if err != nil {
  19. return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ")
  20. }
  21. var ASRep messages.ASRep
  22. rb, err := cl.sendToKDC(b, realm)
  23. if err != nil {
  24. if e, ok := err.(messages.KRBError); ok {
  25. switch e.ErrorCode {
  26. case errorcode.KDC_ERR_PREAUTH_REQUIRED, errorcode.KDC_ERR_PREAUTH_FAILED:
  27. // From now on assume this client will need to do this pre-auth and set the PAData
  28. cl.GoKrb5Conf.AssumePAEncTimestampRequired = true
  29. err = setPAData(cl, e, &ASReq)
  30. if err != nil {
  31. return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required")
  32. }
  33. b, err := ASReq.Marshal()
  34. if err != nil {
  35. return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ with PAData")
  36. }
  37. rb, err = cl.sendToKDC(b, realm)
  38. if err != nil {
  39. if _, ok := err.(messages.KRBError); ok {
  40. return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC")
  41. }
  42. return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
  43. }
  44. case errorcode.KDC_ERR_WRONG_REALM:
  45. // Client referral https://tools.ietf.org/html/rfc6806.html#section-7
  46. if referral > 5 {
  47. return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "maximum number of client referrals exceeded")
  48. }
  49. referral++
  50. return cl.ASExchange(e.CRealm, ASReq, referral)
  51. default:
  52. return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC")
  53. }
  54. } else {
  55. return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
  56. }
  57. }
  58. err = ASRep.Unmarshal(rb)
  59. if err != nil {
  60. return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed to process the AS_REP")
  61. }
  62. if ok, err := ASRep.IsValid(cl.Config, cl.Credentials, ASReq); !ok {
  63. return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: AS_REP is not valid or client password/keytab incorrect")
  64. }
  65. return ASRep, nil
  66. }
  67. func setPAData(cl *Client, krberr messages.KRBError, ASReq *messages.ASReq) error {
  68. if !cl.GoKrb5Conf.DisablePAFXFast {
  69. pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP}
  70. ASReq.PAData = append(ASReq.PAData, pa)
  71. }
  72. if cl.GoKrb5Conf.AssumePAEncTimestampRequired {
  73. paTSb, err := types.GetPAEncTSEncAsnMarshalled()
  74. if err != nil {
  75. return krberror.Errorf(err, krberror.KRBMsgError, "error creating PAEncTSEnc for Pre-Authentication")
  76. }
  77. var et etype.EType
  78. if krberr.ErrorCode == 0 {
  79. etn := cl.GoKrb5Conf.preAuthEType
  80. if etn == 0 {
  81. etn = int32(cl.Config.LibDefaults.PreferredPreauthTypes[0])
  82. }
  83. // This is not in response to an error from the KDC. It is preemptive or renewal
  84. et, err = crypto.GetEtype(etn) // Take the first as preference
  85. if err != nil {
  86. return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
  87. }
  88. } else {
  89. // Get the etype to use from the PA data in the KRBError e-data
  90. et, err = preAuthEType(krberr)
  91. if err != nil {
  92. return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
  93. }
  94. cl.GoKrb5Conf.preAuthEType = et.GetETypeID()
  95. }
  96. key, err := cl.Key(et, krberr)
  97. if err != nil {
  98. return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials")
  99. }
  100. paEncTS, err := crypto.GetEncryptedData(paTSb, key, keyusage.AS_REQ_PA_ENC_TIMESTAMP, 1)
  101. if err != nil {
  102. return krberror.Errorf(err, krberror.EncryptingError, "error encrypting pre-authentication timestamp")
  103. }
  104. pb, err := paEncTS.Marshal()
  105. if err != nil {
  106. return krberror.Errorf(err, krberror.EncodingError, "error marshaling the PAEncTSEnc encrypted data")
  107. }
  108. pa := types.PAData{
  109. PADataType: patype.PA_ENC_TIMESTAMP,
  110. PADataValue: pb,
  111. }
  112. ASReq.PAData = append(ASReq.PAData, pa)
  113. }
  114. return nil
  115. }
  116. func preAuthEType(krberr messages.KRBError) (etype etype.EType, err error) {
  117. //The preferred ordering of the "hint" pre-authentication data that
  118. //affect client key selection is: ETYPE-INFO2, followed by ETYPE-INFO,
  119. //followed by PW-SALT.
  120. //A KDC SHOULD NOT send PA-PW-SALT when issuing a KRB-ERROR message
  121. //that requests additional pre-authentication. Implementation note:
  122. //Some KDC implementations issue an erroneous PA-PW-SALT when issuing a
  123. //KRB-ERROR message that requests additional pre-authentication.
  124. //Therefore, clients SHOULD ignore a PA-PW-SALT accompanying a
  125. //KRB-ERROR message that requests additional pre-authentication.
  126. var etypeID int32
  127. var pas types.PADataSequence
  128. e := pas.Unmarshal(krberr.EData)
  129. if e != nil {
  130. err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling KRBError data")
  131. return
  132. }
  133. for _, pa := range pas {
  134. switch pa.PADataType {
  135. case patype.PA_ETYPE_INFO2:
  136. info, e := pa.GetETypeInfo2()
  137. if e != nil {
  138. err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO2 data")
  139. return
  140. }
  141. etypeID = info[0].EType
  142. break
  143. case patype.PA_ETYPE_INFO:
  144. info, e := pa.GetETypeInfo()
  145. if e != nil {
  146. err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO data")
  147. return
  148. }
  149. etypeID = info[0].EType
  150. }
  151. }
  152. etype, e = crypto.GetEtype(etypeID)
  153. if e != nil {
  154. err = krberror.Errorf(e, krberror.EncryptingError, "error creating etype")
  155. return
  156. }
  157. return etype, nil
  158. }