github.com/jcmturner/gokrb5/v8@v8.4.4/client/ASExchange.go (about) 1 package client 2 3 import ( 4 "github.com/jcmturner/gokrb5/v8/crypto" 5 "github.com/jcmturner/gokrb5/v8/crypto/etype" 6 "github.com/jcmturner/gokrb5/v8/iana/errorcode" 7 "github.com/jcmturner/gokrb5/v8/iana/keyusage" 8 "github.com/jcmturner/gokrb5/v8/iana/patype" 9 "github.com/jcmturner/gokrb5/v8/krberror" 10 "github.com/jcmturner/gokrb5/v8/messages" 11 "github.com/jcmturner/gokrb5/v8/types" 12 ) 13 14 // ASExchange performs an AS exchange for the client to retrieve a TGT. 15 func (cl *Client) ASExchange(realm string, ASReq messages.ASReq, referral int) (messages.ASRep, error) { 16 if ok, err := cl.IsConfigured(); !ok { 17 return messages.ASRep{}, krberror.Errorf(err, krberror.ConfigError, "AS Exchange cannot be performed") 18 } 19 20 // Set PAData if required 21 err := setPAData(cl, nil, &ASReq) 22 if err != nil { 23 return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: issue with setting PAData on AS_REQ") 24 } 25 26 b, err := ASReq.Marshal() 27 if err != nil { 28 return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ") 29 } 30 var ASRep messages.ASRep 31 32 rb, err := cl.sendToKDC(b, realm) 33 if err != nil { 34 if e, ok := err.(messages.KRBError); ok { 35 switch e.ErrorCode { 36 case errorcode.KDC_ERR_PREAUTH_REQUIRED, errorcode.KDC_ERR_PREAUTH_FAILED: 37 // From now on assume this client will need to do this pre-auth and set the PAData 38 cl.settings.assumePreAuthentication = true 39 err = setPAData(cl, &e, &ASReq) 40 if err != nil { 41 return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required") 42 } 43 b, err := ASReq.Marshal() 44 if err != nil { 45 return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ with PAData") 46 } 47 rb, err = cl.sendToKDC(b, realm) 48 if err != nil { 49 if _, ok := err.(messages.KRBError); ok { 50 return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC") 51 } 52 return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC") 53 } 54 case errorcode.KDC_ERR_WRONG_REALM: 55 // Client referral https://tools.ietf.org/html/rfc6806.html#section-7 56 if referral > 5 { 57 return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "maximum number of client referrals exceeded") 58 } 59 referral++ 60 return cl.ASExchange(e.CRealm, ASReq, referral) 61 default: 62 return messages.ASRep{}, krberror.Errorf(err, krberror.KDCError, "AS Exchange Error: kerberos error response from KDC") 63 } 64 } else { 65 return messages.ASRep{}, krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC") 66 } 67 } 68 err = ASRep.Unmarshal(rb) 69 if err != nil { 70 return messages.ASRep{}, krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed to process the AS_REP") 71 } 72 if ok, err := ASRep.Verify(cl.Config, cl.Credentials, ASReq); !ok { 73 return messages.ASRep{}, krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: AS_REP is not valid or client password/keytab incorrect") 74 } 75 return ASRep, nil 76 } 77 78 // setPAData adds pre-authentication data to the AS_REQ. 79 func setPAData(cl *Client, krberr *messages.KRBError, ASReq *messages.ASReq) error { 80 if !cl.settings.DisablePAFXFAST() { 81 pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP} 82 ASReq.PAData = append(ASReq.PAData, pa) 83 } 84 if cl.settings.AssumePreAuthentication() { 85 // Identify the etype to use to encrypt the PA Data 86 var et etype.EType 87 var err error 88 var key types.EncryptionKey 89 var kvno int 90 if krberr == nil { 91 // This is not in response to an error from the KDC. It is preemptive or renewal 92 // There is no KRB Error that tells us the etype to use 93 etn := cl.settings.preAuthEType // Use the etype that may have previously been negotiated 94 if etn == 0 { 95 etn = int32(cl.Config.LibDefaults.PreferredPreauthTypes[0]) // Resort to config 96 } 97 et, err = crypto.GetEtype(etn) 98 if err != nil { 99 return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption") 100 } 101 key, kvno, err = cl.Key(et, 0, nil) 102 if err != nil { 103 return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials") 104 } 105 } else { 106 // Get the etype to use from the PA data in the KRBError e-data 107 et, err = preAuthEType(krberr) 108 if err != nil { 109 return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption") 110 } 111 cl.settings.preAuthEType = et.GetETypeID() // Set the etype that has been defined for potential future use 112 key, kvno, err = cl.Key(et, 0, krberr) 113 if err != nil { 114 return krberror.Errorf(err, krberror.EncryptingError, "error getting key from credentials") 115 } 116 } 117 // Generate the PA data 118 paTSb, err := types.GetPAEncTSEncAsnMarshalled() 119 if err != nil { 120 return krberror.Errorf(err, krberror.KRBMsgError, "error creating PAEncTSEnc for Pre-Authentication") 121 } 122 paEncTS, err := crypto.GetEncryptedData(paTSb, key, keyusage.AS_REQ_PA_ENC_TIMESTAMP, kvno) 123 if err != nil { 124 return krberror.Errorf(err, krberror.EncryptingError, "error encrypting pre-authentication timestamp") 125 } 126 pb, err := paEncTS.Marshal() 127 if err != nil { 128 return krberror.Errorf(err, krberror.EncodingError, "error marshaling the PAEncTSEnc encrypted data") 129 } 130 pa := types.PAData{ 131 PADataType: patype.PA_ENC_TIMESTAMP, 132 PADataValue: pb, 133 } 134 // Look for and delete any exiting patype.PA_ENC_TIMESTAMP 135 for i, pa := range ASReq.PAData { 136 if pa.PADataType == patype.PA_ENC_TIMESTAMP { 137 ASReq.PAData[i] = ASReq.PAData[len(ASReq.PAData)-1] 138 ASReq.PAData = ASReq.PAData[:len(ASReq.PAData)-1] 139 } 140 } 141 ASReq.PAData = append(ASReq.PAData, pa) 142 } 143 return nil 144 } 145 146 // preAuthEType establishes what encryption type to use for pre-authentication from the KRBError returned from the KDC. 147 func preAuthEType(krberr *messages.KRBError) (etype etype.EType, err error) { 148 //RFC 4120 5.2.7.5 covers the preference order of ETYPE-INFO2 and ETYPE-INFO. 149 var etypeID int32 150 var pas types.PADataSequence 151 e := pas.Unmarshal(krberr.EData) 152 if e != nil { 153 err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling KRBError data") 154 return 155 } 156 Loop: 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 Loop 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 }