github.com/jcmturner/gokrb5/v8@v8.4.4/spnego/krb5Token.go (about) 1 package spnego 2 3 import ( 4 "context" 5 "encoding/binary" 6 "encoding/hex" 7 "errors" 8 "fmt" 9 10 "github.com/jcmturner/gofork/encoding/asn1" 11 "github.com/jcmturner/gokrb5/v8/asn1tools" 12 "github.com/jcmturner/gokrb5/v8/client" 13 "github.com/jcmturner/gokrb5/v8/credentials" 14 "github.com/jcmturner/gokrb5/v8/gssapi" 15 "github.com/jcmturner/gokrb5/v8/iana/chksumtype" 16 "github.com/jcmturner/gokrb5/v8/iana/msgtype" 17 "github.com/jcmturner/gokrb5/v8/krberror" 18 "github.com/jcmturner/gokrb5/v8/messages" 19 "github.com/jcmturner/gokrb5/v8/service" 20 "github.com/jcmturner/gokrb5/v8/types" 21 ) 22 23 // GSSAPI KRB5 MechToken IDs. 24 const ( 25 TOK_ID_KRB_AP_REQ = "0100" 26 TOK_ID_KRB_AP_REP = "0200" 27 TOK_ID_KRB_ERROR = "0300" 28 ) 29 30 // KRB5Token context token implementation for GSSAPI. 31 type KRB5Token struct { 32 OID asn1.ObjectIdentifier 33 tokID []byte 34 APReq messages.APReq 35 APRep messages.APRep 36 KRBError messages.KRBError 37 settings *service.Settings 38 context context.Context 39 } 40 41 // Marshal a KRB5Token into a slice of bytes. 42 func (m *KRB5Token) Marshal() ([]byte, error) { 43 // Create the header 44 b, _ := asn1.Marshal(m.OID) 45 b = append(b, m.tokID...) 46 var tb []byte 47 var err error 48 switch hex.EncodeToString(m.tokID) { 49 case TOK_ID_KRB_AP_REQ: 50 tb, err = m.APReq.Marshal() 51 if err != nil { 52 return []byte{}, fmt.Errorf("error marshalling AP_REQ for MechToken: %v", err) 53 } 54 case TOK_ID_KRB_AP_REP: 55 return []byte{}, errors.New("marshal of AP_REP GSSAPI MechToken not supported by gokrb5") 56 case TOK_ID_KRB_ERROR: 57 return []byte{}, errors.New("marshal of KRB_ERROR GSSAPI MechToken not supported by gokrb5") 58 } 59 if err != nil { 60 return []byte{}, fmt.Errorf("error mashalling kerberos message within mech token: %v", err) 61 } 62 b = append(b, tb...) 63 return asn1tools.AddASNAppTag(b, 0), nil 64 } 65 66 // Unmarshal a KRB5Token. 67 func (m *KRB5Token) Unmarshal(b []byte) error { 68 var oid asn1.ObjectIdentifier 69 r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0)) 70 if err != nil { 71 return fmt.Errorf("error unmarshalling KRB5Token OID: %v", err) 72 } 73 if !oid.Equal(gssapi.OIDKRB5.OID()) { 74 return fmt.Errorf("error unmarshalling KRB5Token, OID is %s not %s", oid.String(), gssapi.OIDKRB5.OID().String()) 75 } 76 m.OID = oid 77 if len(r) < 2 { 78 return fmt.Errorf("krb5token too short") 79 } 80 m.tokID = r[0:2] 81 switch hex.EncodeToString(m.tokID) { 82 case TOK_ID_KRB_AP_REQ: 83 var a messages.APReq 84 err = a.Unmarshal(r[2:]) 85 if err != nil { 86 return fmt.Errorf("error unmarshalling KRB5Token AP_REQ: %v", err) 87 } 88 m.APReq = a 89 case TOK_ID_KRB_AP_REP: 90 var a messages.APRep 91 err = a.Unmarshal(r[2:]) 92 if err != nil { 93 return fmt.Errorf("error unmarshalling KRB5Token AP_REP: %v", err) 94 } 95 m.APRep = a 96 case TOK_ID_KRB_ERROR: 97 var a messages.KRBError 98 err = a.Unmarshal(r[2:]) 99 if err != nil { 100 return fmt.Errorf("error unmarshalling KRB5Token KRBError: %v", err) 101 } 102 m.KRBError = a 103 } 104 return nil 105 } 106 107 // Verify a KRB5Token. 108 func (m *KRB5Token) Verify() (bool, gssapi.Status) { 109 switch hex.EncodeToString(m.tokID) { 110 case TOK_ID_KRB_AP_REQ: 111 ok, creds, err := service.VerifyAPREQ(&m.APReq, m.settings) 112 if err != nil { 113 return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: err.Error()} 114 } 115 if !ok { 116 return false, gssapi.Status{Code: gssapi.StatusDefectiveCredential, Message: "KRB5_AP_REQ token not valid"} 117 } 118 m.context = context.Background() 119 m.context = context.WithValue(m.context, ctxCredentials, creds) 120 return true, gssapi.Status{Code: gssapi.StatusComplete} 121 case TOK_ID_KRB_AP_REP: 122 // Client side 123 // TODO how to verify the AP_REP - not yet implemented 124 return false, gssapi.Status{Code: gssapi.StatusFailure, Message: "verifying an AP_REP is not currently supported by gokrb5"} 125 case TOK_ID_KRB_ERROR: 126 if m.KRBError.MsgType != msgtype.KRB_ERROR { 127 return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "KRB5_Error token not valid"} 128 } 129 return true, gssapi.Status{Code: gssapi.StatusUnavailable} 130 } 131 return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "unknown TOK_ID in KRB5 token"} 132 } 133 134 // IsAPReq tests if the MechToken contains an AP_REQ. 135 func (m *KRB5Token) IsAPReq() bool { 136 if hex.EncodeToString(m.tokID) == TOK_ID_KRB_AP_REQ { 137 return true 138 } 139 return false 140 } 141 142 // IsAPRep tests if the MechToken contains an AP_REP. 143 func (m *KRB5Token) IsAPRep() bool { 144 if hex.EncodeToString(m.tokID) == TOK_ID_KRB_AP_REP { 145 return true 146 } 147 return false 148 } 149 150 // IsKRBError tests if the MechToken contains an KRB_ERROR. 151 func (m *KRB5Token) IsKRBError() bool { 152 if hex.EncodeToString(m.tokID) == TOK_ID_KRB_ERROR { 153 return true 154 } 155 return false 156 } 157 158 // Context returns the KRB5 token's context which will contain any verify user identity information. 159 func (m *KRB5Token) Context() context.Context { 160 return m.context 161 } 162 163 // NewKRB5TokenAPREQ creates a new KRB5 token with AP_REQ 164 func NewKRB5TokenAPREQ(cl *client.Client, tkt messages.Ticket, sessionKey types.EncryptionKey, GSSAPIFlags []int, APOptions []int) (KRB5Token, error) { 165 // TODO consider providing the SPN rather than the specific tkt and key and get these from the krb client. 166 var m KRB5Token 167 m.OID = gssapi.OIDKRB5.OID() 168 tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ) 169 m.tokID = tb 170 171 auth, err := krb5TokenAuthenticator(cl.Credentials, GSSAPIFlags) 172 if err != nil { 173 return m, err 174 } 175 APReq, err := messages.NewAPReq( 176 tkt, 177 sessionKey, 178 auth, 179 ) 180 if err != nil { 181 return m, err 182 } 183 for _, o := range APOptions { 184 types.SetFlag(&APReq.APOptions, o) 185 } 186 m.APReq = APReq 187 return m, nil 188 } 189 190 // krb5TokenAuthenticator creates a new kerberos authenticator for kerberos MechToken 191 func krb5TokenAuthenticator(creds *credentials.Credentials, flags []int) (types.Authenticator, error) { 192 //RFC 4121 Section 4.1.1 193 auth, err := types.NewAuthenticator(creds.Domain(), creds.CName()) 194 if err != nil { 195 return auth, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator") 196 } 197 auth.Cksum = types.Checksum{ 198 CksumType: chksumtype.GSSAPI, 199 Checksum: newAuthenticatorChksum(flags), 200 } 201 return auth, nil 202 } 203 204 // Create new authenticator checksum for kerberos MechToken 205 func newAuthenticatorChksum(flags []int) []byte { 206 a := make([]byte, 24) 207 binary.LittleEndian.PutUint32(a[:4], 16) 208 for _, i := range flags { 209 if i == gssapi.ContextFlagDeleg { 210 x := make([]byte, 28-len(a)) 211 a = append(a, x...) 212 } 213 f := binary.LittleEndian.Uint32(a[20:24]) 214 f |= uint32(i) 215 binary.LittleEndian.PutUint32(a[20:24], f) 216 } 217 return a 218 }