ASExchange.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package client
  2. import (
  3. "gopkg.in/jcmturner/gokrb5.v7/crypto"
  4. "gopkg.in/jcmturner/gokrb5.v7/crypto/etype"
  5. "gopkg.in/jcmturner/gokrb5.v7/iana/errorcode"
  6. "gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
  7. "gopkg.in/jcmturner/gokrb5.v7/iana/patype"
  8. "gopkg.in/jcmturner/gokrb5.v7/krberror"
  9. "gopkg.in/jcmturner/gokrb5.v7/messages"
  10. "gopkg.in/jcmturner/gokrb5.v7/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 performed")
  16. }
  17. // Set PAData if required
  18. err := setPAData(cl, nil, &ASReq)
  19. if err != nil {
  20. return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: issue with setting PAData on AS_REQ")
  21. }
  22. b, err := ASReq.Marshal()
  23. if err != nil {
  24. return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ")
  25. }
  26. var ASRep messages.ASRep
  27. rb, err := cl.sendToKDC(b, realm)
  28. if err != nil {
  29. if e, ok := err.(messages.KRBError); ok {
  30. switch e.ErrorCode {
  31. case errorcode.KDC_ERR_PREAUTH_REQUIRED, errorcode.KDC_ERR_PREAUTH_FAILED:
  32. // From now on assume this client will need to do this pre-auth and set the PAData
  33. cl.settings.assumePreAuthentication = true
  34. err = setPAData(cl, &e, &ASReq)
  35. if err != nil {
  36. return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required")
  37. }
  38. b, err := ASReq.Marshal()
  39. if err != nil {
  40. return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ with PAData")
  41. }
  42. rb, err = cl.sendToKDC(b, realm)
  43. if err != nil {
  44. if _, ok := err.(messages.KRBError); ok {
  45. return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC")
  46. }
  47. return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
  48. }
  49. case errorcode.KDC_ERR_WRONG_REALM:
  50. // Client referral https://tools.ietf.org/html/rfc6806.html#section-7
  51. if referral > 5 {
  52. return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "maximum number of client referrals exceeded")
  53. }
  54. referral++
  55. return cl.ASExchange(e.CRealm, ASReq, referral)
  56. default:
  57. return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC")
  58. }
  59. } else {
  60. return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
  61. }
  62. }
  63. err = ASRep.Unmarshal(rb)
  64. if err != nil {
  65. return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed to process the AS_REP")
  66. }
  67. if ok, err := ASRep.Verify(cl.Config, cl.Credentials, ASReq); !ok {
  68. return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: AS_REP is not valid or client password/keytab incorrect")
  69. }
  70. return ASRep, nil
  71. }
  72. // setPAData adds pre-authentication data to the AS_REQ.
  73. func setPAData(cl *Client, krberr *messages.KRBError, ASReq *messages.ASReq) error {
  74. if !cl.settings.DisablePAFXFAST() {
  75. pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP}
  76. ASReq.PAData = append(ASReq.PAData, pa)
  77. }
  78. if cl.settings.AssumePreAuthentication() {
  79. // Identify the etype to use to encrypt the PA Data
  80. var et etype.EType
  81. var err error
  82. var key types.EncryptionKey
  83. if krberr == nil {
  84. // This is not in response to an error from the KDC. It is preemptive or renewal
  85. // There is no KRB Error that tells us the etype to use
  86. etn := cl.settings.preAuthEType // Use the etype that may have previously been negotiated
  87. if etn == 0 {
  88. etn = int32(cl.Config.LibDefaults.PreferredPreauthTypes[0]) // Resort to config
  89. }
  90. et, err = crypto.GetEtype(etn)
  91. if err != nil {
  92. return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
  93. }
  94. key, err = cl.Key(et, nil)
  95. if err != nil {
  96. return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials")
  97. }
  98. } else {
  99. // Get the etype to use from the PA data in the KRBError e-data
  100. et, err = preAuthEType(krberr)
  101. if err != nil {
  102. return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
  103. }
  104. cl.settings.preAuthEType = et.GetETypeID() // Set the etype that has been defined for potential future use
  105. key, err = cl.Key(et, krberr)
  106. if err != nil {
  107. return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials")
  108. }
  109. }
  110. // Generate the PA data
  111. paTSb, err := types.GetPAEncTSEncAsnMarshalled()
  112. if err != nil {
  113. return krberror.Errorf(err, krberror.KRBMsgError, "error creating PAEncTSEnc for Pre-Authentication")
  114. }
  115. //TODO (theme: KVNO from keytab) the kvno should not be hard coded to 1 as this hampers troubleshooting.
  116. paEncTS, err := crypto.GetEncryptedData(paTSb, key, keyusage.AS_REQ_PA_ENC_TIMESTAMP, 1)
  117. if err != nil {
  118. return krberror.Errorf(err, krberror.EncryptingError, "error encrypting pre-authentication timestamp")
  119. }
  120. pb, err := paEncTS.Marshal()
  121. if err != nil {
  122. return krberror.Errorf(err, krberror.EncodingError, "error marshaling the PAEncTSEnc encrypted data")
  123. }
  124. pa := types.PAData{
  125. PADataType: patype.PA_ENC_TIMESTAMP,
  126. PADataValue: pb,
  127. }
  128. // Look for and delete any exiting patype.PA_ENC_TIMESTAMP
  129. for i, pa := range ASReq.PAData {
  130. if pa.PADataType == patype.PA_ENC_TIMESTAMP {
  131. ASReq.PAData[i] = ASReq.PAData[len(ASReq.PAData)-1]
  132. ASReq.PAData = ASReq.PAData[:len(ASReq.PAData)-1]
  133. }
  134. }
  135. ASReq.PAData = append(ASReq.PAData, pa)
  136. }
  137. return nil
  138. }
  139. // preAuthEType establishes what encryption type to use for pre-authentication from the KRBError returned from the KDC.
  140. func preAuthEType(krberr *messages.KRBError) (etype etype.EType, err error) {
  141. //The preferred ordering of the "hint" pre-authentication data that
  142. //affect client key selection is: ETYPE-INFO2, followed by ETYPE-INFO,
  143. //followed by PW-SALT.
  144. //A KDC SHOULD NOT send PA-PW-SALT when issuing a KRB-ERROR message
  145. //that requests additional pre-authentication. Implementation note:
  146. //Some KDC implementations issue an erroneous PA-PW-SALT when issuing a
  147. //KRB-ERROR message that requests additional pre-authentication.
  148. //Therefore, clients SHOULD ignore a PA-PW-SALT accompanying a
  149. //KRB-ERROR message that requests additional pre-authentication.
  150. var etypeID int32
  151. var pas types.PADataSequence
  152. e := pas.Unmarshal(krberr.EData)
  153. if e != nil {
  154. err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling KRBError data")
  155. return
  156. }
  157. for _, pa := range pas {
  158. switch pa.PADataType {
  159. case patype.PA_ETYPE_INFO2:
  160. info, e := pa.GetETypeInfo2()
  161. if e != nil {
  162. err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO2 data")
  163. return
  164. }
  165. etypeID = info[0].EType
  166. break
  167. case patype.PA_ETYPE_INFO:
  168. info, e := pa.GetETypeInfo()
  169. if e != nil {
  170. err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO data")
  171. return
  172. }
  173. etypeID = info[0].EType
  174. }
  175. }
  176. etype, e = crypto.GetEtype(etypeID)
  177. if e != nil {
  178. err = krberror.Errorf(e, krberror.EncryptingError, "error creating etype")
  179. return
  180. }
  181. return etype, nil
  182. }