| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- package spnego
- import (
- "context"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "fmt"
- "github.com/jcmturner/gofork/encoding/asn1"
- "gopkg.in/jcmturner/gokrb5.v7/asn1tools"
- "gopkg.in/jcmturner/gokrb5.v7/client"
- "gopkg.in/jcmturner/gokrb5.v7/credentials"
- "gopkg.in/jcmturner/gokrb5.v7/gssapi"
- "gopkg.in/jcmturner/gokrb5.v7/iana/chksumtype"
- "gopkg.in/jcmturner/gokrb5.v7/iana/msgtype"
- "gopkg.in/jcmturner/gokrb5.v7/krberror"
- "gopkg.in/jcmturner/gokrb5.v7/messages"
- "gopkg.in/jcmturner/gokrb5.v7/service"
- "gopkg.in/jcmturner/gokrb5.v7/types"
- )
- // GSSAPI KRB5 MechToken IDs.
- const (
- TOK_ID_KRB_AP_REQ = "0100"
- TOK_ID_KRB_AP_REP = "0200"
- TOK_ID_KRB_ERROR = "0300"
- )
- // KRB5Token context token implementation for GSSAPI.
- type KRB5Token struct {
- OID asn1.ObjectIdentifier
- tokID []byte
- APReq messages.APReq
- APRep messages.APRep
- KRBError messages.KRBError
- settings *service.Settings
- context context.Context
- }
- // Marshal a KRB5Token into a slice of bytes.
- func (m *KRB5Token) Marshal() ([]byte, error) {
- // Create the header
- b, _ := asn1.Marshal(m.OID)
- b = append(b, m.tokID...)
- var tb []byte
- var err error
- switch hex.EncodeToString(m.tokID) {
- case TOK_ID_KRB_AP_REQ:
- tb, err = m.APReq.Marshal()
- if err != nil {
- return []byte{}, fmt.Errorf("error marshalling AP_REQ for MechToken: %v", err)
- }
- case TOK_ID_KRB_AP_REP:
- return []byte{}, errors.New("marshal of AP_REP GSSAPI MechToken not supported by gokrb5")
- case TOK_ID_KRB_ERROR:
- return []byte{}, errors.New("marshal of KRB_ERROR GSSAPI MechToken not supported by gokrb5")
- }
- if err != nil {
- return []byte{}, fmt.Errorf("error mashalling kerberos message within mech token: %v", err)
- }
- b = append(b, tb...)
- return asn1tools.AddASNAppTag(b, 0), nil
- }
- // Unmarshal a KRB5Token.
- func (m *KRB5Token) Unmarshal(b []byte) error {
- var oid asn1.ObjectIdentifier
- r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0))
- if err != nil {
- return fmt.Errorf("error unmarshalling KRB5Token OID: %v", err)
- }
- if !oid.Equal(gssapi.OID(gssapi.OIDKRB5)) {
- return fmt.Errorf("error unmarshalling KRB5Token, OID is %s not %s", oid.String(), gssapi.OID(gssapi.OIDKRB5).String())
- }
- m.OID = oid
- if len(r) < 2 {
- return fmt.Errorf("krb5token too short")
- }
- m.tokID = r[0:2]
- switch hex.EncodeToString(m.tokID) {
- case TOK_ID_KRB_AP_REQ:
- var a messages.APReq
- err = a.Unmarshal(r[2:])
- if err != nil {
- return fmt.Errorf("error unmarshalling KRB5Token AP_REQ: %v", err)
- }
- m.APReq = a
- case TOK_ID_KRB_AP_REP:
- var a messages.APRep
- err = a.Unmarshal(r[2:])
- if err != nil {
- return fmt.Errorf("error unmarshalling KRB5Token AP_REP: %v", err)
- }
- m.APRep = a
- case TOK_ID_KRB_ERROR:
- var a messages.KRBError
- err = a.Unmarshal(r[2:])
- if err != nil {
- return fmt.Errorf("error unmarshalling KRB5Token KRBError: %v", err)
- }
- m.KRBError = a
- }
- return nil
- }
- // Verify a KRB5Token.
- func (m *KRB5Token) Verify() (bool, gssapi.Status) {
- switch hex.EncodeToString(m.tokID) {
- case TOK_ID_KRB_AP_REQ:
- ok, creds, err := service.VerifyAPREQ(m.APReq, m.settings)
- if err != nil {
- return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: err.Error()}
- }
- if !ok {
- return false, gssapi.Status{Code: gssapi.StatusDefectiveCredential, Message: "KRB5_AP_REQ token not valid"}
- }
- m.context = context.Background()
- m.context = context.WithValue(m.context, CTXKeyCredentials, creds)
- m.context = context.WithValue(m.context, CTXKeyAuthenticated, ok)
- return true, gssapi.Status{Code: gssapi.StatusComplete}
- case TOK_ID_KRB_AP_REP:
- // Client side
- // TODO how to verify the AP_REP - not yet implemented
- return false, gssapi.Status{Code: gssapi.StatusFailure, Message: "verifying an AP_REP is not currently supported by gokrb5"}
- case TOK_ID_KRB_ERROR:
- if m.KRBError.MsgType != msgtype.KRB_ERROR {
- return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "KRB5_Error token not valid"}
- }
- return true, gssapi.Status{Code: gssapi.StatusUnavailable}
- }
- return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "unknown TOK_ID in KRB5 token"}
- }
- // IsAPReq tests if the MechToken contains an AP_REQ.
- func (m *KRB5Token) IsAPReq() bool {
- if hex.EncodeToString(m.tokID) == TOK_ID_KRB_AP_REQ {
- return true
- }
- return false
- }
- // IsAPRep tests if the MechToken contains an AP_REP.
- func (m *KRB5Token) IsAPRep() bool {
- if hex.EncodeToString(m.tokID) == TOK_ID_KRB_AP_REP {
- return true
- }
- return false
- }
- // IsKRBError tests if the MechToken contains an KRB_ERROR.
- func (m *KRB5Token) IsKRBError() bool {
- if hex.EncodeToString(m.tokID) == TOK_ID_KRB_ERROR {
- return true
- }
- return false
- }
- // Context returns the KRB5 token's context which will contain any verify user identity information.
- func (m *KRB5Token) Context() context.Context {
- return m.context
- }
- // NewKRB5TokenAPREQ creates a new KRB5 token with AP_REQ
- func NewKRB5TokenAPREQ(cl *client.Client, tkt messages.Ticket, sessionKey types.EncryptionKey, GSSAPIFlags []int, APOptions []int) (KRB5Token, error) {
- // TODO consider providing the SPN rather than the specific tkt and key and get these from the krb client.
- var m KRB5Token
- m.OID = gssapi.OID(gssapi.OIDKRB5)
- tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ)
- m.tokID = tb
- auth, err := krb5TokenAuthenticator(cl.Credentials, GSSAPIFlags)
- if err != nil {
- return m, err
- }
- APReq, err := messages.NewAPReq(
- tkt,
- sessionKey,
- auth,
- )
- if err != nil {
- return m, err
- }
- for _, o := range APOptions {
- types.SetFlag(&APReq.APOptions, o)
- }
- m.APReq = APReq
- return m, nil
- }
- // krb5TokenAuthenticator creates a new kerberos authenticator for kerberos MechToken
- func krb5TokenAuthenticator(creds *credentials.Credentials, flags []int) (types.Authenticator, error) {
- //RFC 4121 Section 4.1.1
- auth, err := types.NewAuthenticator(creds.Domain(), creds.CName())
- if err != nil {
- return auth, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
- }
- auth.Cksum = types.Checksum{
- CksumType: chksumtype.GSSAPI,
- Checksum: newAuthenticatorChksum(flags),
- }
- return auth, nil
- }
- // Create new authenticator checksum for kerberos MechToken
- func newAuthenticatorChksum(flags []int) []byte {
- a := make([]byte, 24)
- binary.LittleEndian.PutUint32(a[:4], 16)
- for _, i := range flags {
- if i == gssapi.ContextFlagDeleg {
- x := make([]byte, 28-len(a))
- a = append(a, x...)
- }
- f := binary.LittleEndian.Uint32(a[20:24])
- f |= uint32(i)
- binary.LittleEndian.PutUint32(a[20:24], f)
- }
- return a
- }
|