github.com/jcmturner/gokrb5/v8@v8.4.4/messages/KDCRep.go (about)

     1  package messages
     2  
     3  // Reference: https://www.ietf.org/rfc/rfc4120.txt
     4  // Section: 5.4.2
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/jcmturner/gofork/encoding/asn1"
    11  	"github.com/jcmturner/gokrb5/v8/asn1tools"
    12  	"github.com/jcmturner/gokrb5/v8/config"
    13  	"github.com/jcmturner/gokrb5/v8/credentials"
    14  	"github.com/jcmturner/gokrb5/v8/crypto"
    15  	"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
    16  	"github.com/jcmturner/gokrb5/v8/iana/flags"
    17  	"github.com/jcmturner/gokrb5/v8/iana/keyusage"
    18  	"github.com/jcmturner/gokrb5/v8/iana/msgtype"
    19  	"github.com/jcmturner/gokrb5/v8/iana/patype"
    20  	"github.com/jcmturner/gokrb5/v8/krberror"
    21  	"github.com/jcmturner/gokrb5/v8/types"
    22  )
    23  
    24  type marshalKDCRep struct {
    25  	PVNO    int                  `asn1:"explicit,tag:0"`
    26  	MsgType int                  `asn1:"explicit,tag:1"`
    27  	PAData  types.PADataSequence `asn1:"explicit,optional,tag:2"`
    28  	CRealm  string               `asn1:"generalstring,explicit,tag:3"`
    29  	CName   types.PrincipalName  `asn1:"explicit,tag:4"`
    30  	// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
    31  	Ticket  asn1.RawValue       `asn1:"explicit,tag:5"`
    32  	EncPart types.EncryptedData `asn1:"explicit,tag:6"`
    33  }
    34  
    35  // KDCRepFields represents the KRB_KDC_REP fields.
    36  type KDCRepFields struct {
    37  	PVNO             int
    38  	MsgType          int
    39  	PAData           []types.PAData
    40  	CRealm           string
    41  	CName            types.PrincipalName
    42  	Ticket           Ticket
    43  	EncPart          types.EncryptedData
    44  	DecryptedEncPart EncKDCRepPart
    45  }
    46  
    47  // ASRep implements RFC 4120 KRB_AS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
    48  type ASRep struct {
    49  	KDCRepFields
    50  }
    51  
    52  // TGSRep implements RFC 4120 KRB_TGS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
    53  type TGSRep struct {
    54  	KDCRepFields
    55  }
    56  
    57  // EncKDCRepPart is the encrypted part of KRB_KDC_REP.
    58  type EncKDCRepPart struct {
    59  	Key           types.EncryptionKey  `asn1:"explicit,tag:0"`
    60  	LastReqs      []LastReq            `asn1:"explicit,tag:1"`
    61  	Nonce         int                  `asn1:"explicit,tag:2"`
    62  	KeyExpiration time.Time            `asn1:"generalized,explicit,optional,tag:3"`
    63  	Flags         asn1.BitString       `asn1:"explicit,tag:4"`
    64  	AuthTime      time.Time            `asn1:"generalized,explicit,tag:5"`
    65  	StartTime     time.Time            `asn1:"generalized,explicit,optional,tag:6"`
    66  	EndTime       time.Time            `asn1:"generalized,explicit,tag:7"`
    67  	RenewTill     time.Time            `asn1:"generalized,explicit,optional,tag:8"`
    68  	SRealm        string               `asn1:"generalstring,explicit,tag:9"`
    69  	SName         types.PrincipalName  `asn1:"explicit,tag:10"`
    70  	CAddr         []types.HostAddress  `asn1:"explicit,optional,tag:11"`
    71  	EncPAData     types.PADataSequence `asn1:"explicit,optional,tag:12"`
    72  }
    73  
    74  // LastReq part of KRB_KDC_REP.
    75  type LastReq struct {
    76  	LRType  int32     `asn1:"explicit,tag:0"`
    77  	LRValue time.Time `asn1:"generalized,explicit,tag:1"`
    78  }
    79  
    80  // Unmarshal bytes b into the ASRep struct.
    81  func (k *ASRep) Unmarshal(b []byte) error {
    82  	var m marshalKDCRep
    83  	_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREP))
    84  	if err != nil {
    85  		return processUnmarshalReplyError(b, err)
    86  	}
    87  	if m.MsgType != msgtype.KRB_AS_REP {
    88  		return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AS_REP. Expected: %v; Actual: %v", msgtype.KRB_AS_REP, m.MsgType)
    89  	}
    90  	//Process the raw ticket within
    91  	tkt, err := unmarshalTicket(m.Ticket.Bytes)
    92  	if err != nil {
    93  		return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within AS_REP")
    94  	}
    95  	k.KDCRepFields = KDCRepFields{
    96  		PVNO:    m.PVNO,
    97  		MsgType: m.MsgType,
    98  		PAData:  m.PAData,
    99  		CRealm:  m.CRealm,
   100  		CName:   m.CName,
   101  		Ticket:  tkt,
   102  		EncPart: m.EncPart,
   103  	}
   104  	return nil
   105  }
   106  
   107  // Marshal ASRep struct.
   108  func (k *ASRep) Marshal() ([]byte, error) {
   109  	m := marshalKDCRep{
   110  		PVNO:    k.PVNO,
   111  		MsgType: k.MsgType,
   112  		PAData:  k.PAData,
   113  		CRealm:  k.CRealm,
   114  		CName:   k.CName,
   115  		EncPart: k.EncPart,
   116  	}
   117  	b, err := k.Ticket.Marshal()
   118  	if err != nil {
   119  		return []byte{}, err
   120  	}
   121  	m.Ticket = asn1.RawValue{
   122  		Class:      asn1.ClassContextSpecific,
   123  		IsCompound: true,
   124  		Tag:        5,
   125  		Bytes:      b,
   126  	}
   127  	mk, err := asn1.Marshal(m)
   128  	if err != nil {
   129  		return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REP")
   130  	}
   131  	mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREP)
   132  	return mk, nil
   133  }
   134  
   135  // Unmarshal bytes b into the TGSRep struct.
   136  func (k *TGSRep) Unmarshal(b []byte) error {
   137  	var m marshalKDCRep
   138  	_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREP))
   139  	if err != nil {
   140  		return processUnmarshalReplyError(b, err)
   141  	}
   142  	if m.MsgType != msgtype.KRB_TGS_REP {
   143  		return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an TGS_REP. Expected: %v; Actual: %v", msgtype.KRB_TGS_REP, m.MsgType)
   144  	}
   145  	//Process the raw ticket within
   146  	tkt, err := unmarshalTicket(m.Ticket.Bytes)
   147  	if err != nil {
   148  		return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within TGS_REP")
   149  	}
   150  	k.KDCRepFields = KDCRepFields{
   151  		PVNO:    m.PVNO,
   152  		MsgType: m.MsgType,
   153  		PAData:  m.PAData,
   154  		CRealm:  m.CRealm,
   155  		CName:   m.CName,
   156  		Ticket:  tkt,
   157  		EncPart: m.EncPart,
   158  	}
   159  	return nil
   160  }
   161  
   162  // Marshal TGSRep struct.
   163  func (k *TGSRep) Marshal() ([]byte, error) {
   164  	m := marshalKDCRep{
   165  		PVNO:    k.PVNO,
   166  		MsgType: k.MsgType,
   167  		PAData:  k.PAData,
   168  		CRealm:  k.CRealm,
   169  		CName:   k.CName,
   170  		EncPart: k.EncPart,
   171  	}
   172  	b, err := k.Ticket.Marshal()
   173  	if err != nil {
   174  		return []byte{}, err
   175  	}
   176  	m.Ticket = asn1.RawValue{
   177  		Class:      asn1.ClassContextSpecific,
   178  		IsCompound: true,
   179  		Tag:        5,
   180  		Bytes:      b,
   181  	}
   182  	mk, err := asn1.Marshal(m)
   183  	if err != nil {
   184  		return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REP")
   185  	}
   186  	mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREP)
   187  	return mk, nil
   188  }
   189  
   190  // Unmarshal bytes b into encrypted part of KRB_KDC_REP.
   191  func (e *EncKDCRepPart) Unmarshal(b []byte) error {
   192  	_, err := asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncASRepPart))
   193  	if err != nil {
   194  		// Try using tag 26
   195  		// Ref: RFC 4120 - mentions that some implementations use application tag number 26 wether or not the reply is
   196  		// a AS-REP or a TGS-REP.
   197  		_, err = asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncTGSRepPart))
   198  		if err != nil {
   199  			return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part within KDC_REP")
   200  		}
   201  	}
   202  	return nil
   203  }
   204  
   205  // Marshal encrypted part of KRB_KDC_REP.
   206  func (e *EncKDCRepPart) Marshal() ([]byte, error) {
   207  	b, err := asn1.Marshal(*e)
   208  	if err != nil {
   209  		return b, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AS_REP encpart")
   210  	}
   211  	b = asn1tools.AddASNAppTag(b, asnAppTag.EncASRepPart)
   212  	return b, nil
   213  }
   214  
   215  // DecryptEncPart decrypts the encrypted part of an AS_REP.
   216  func (k *ASRep) DecryptEncPart(c *credentials.Credentials) (types.EncryptionKey, error) {
   217  	var key types.EncryptionKey
   218  	var err error
   219  	if c.HasKeytab() {
   220  		key, _, err = c.Keytab().GetEncryptionKey(k.CName, k.CRealm, k.EncPart.KVNO, k.EncPart.EType)
   221  		if err != nil {
   222  			return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
   223  		}
   224  	}
   225  	if c.HasPassword() {
   226  		key, _, err = crypto.GetKeyFromPassword(c.Password(), k.CName, k.CRealm, k.EncPart.EType, k.PAData)
   227  		if err != nil {
   228  			return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
   229  		}
   230  	}
   231  	if !c.HasKeytab() && !c.HasPassword() {
   232  		return key, krberror.NewErrorf(krberror.DecryptingError, "no secret available in credentials to perform decryption of AS_REP encrypted part")
   233  	}
   234  	b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.AS_REP_ENCPART)
   235  	if err != nil {
   236  		return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
   237  	}
   238  	var denc EncKDCRepPart
   239  	err = denc.Unmarshal(b)
   240  	if err != nil {
   241  		return key, krberror.Errorf(err, krberror.EncodingError, "error unmarshaling decrypted encpart of AS_REP")
   242  	}
   243  	k.DecryptedEncPart = denc
   244  	return key, nil
   245  }
   246  
   247  // Verify checks the validity of AS_REP message.
   248  func (k *ASRep) Verify(cfg *config.Config, creds *credentials.Credentials, asReq ASReq) (bool, error) {
   249  	//Ref RFC 4120 Section 3.1.5
   250  	if !k.CName.Equal(asReq.ReqBody.CName) {
   251  		return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName)
   252  	}
   253  	if k.CRealm != asReq.ReqBody.Realm {
   254  		return false, krberror.NewErrorf(krberror.KRBMsgError, "CRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.CRealm)
   255  	}
   256  	key, err := k.DecryptEncPart(creds)
   257  	if err != nil {
   258  		return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting EncPart of AS_REP")
   259  	}
   260  	if k.DecryptedEncPart.Nonce != asReq.ReqBody.Nonce {
   261  		return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
   262  	}
   263  	if !k.DecryptedEncPart.SName.Equal(asReq.ReqBody.SName) {
   264  		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", asReq.ReqBody.SName, k.DecryptedEncPart.SName)
   265  	}
   266  	if k.DecryptedEncPart.SRealm != asReq.ReqBody.Realm {
   267  		return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
   268  	}
   269  	if len(asReq.ReqBody.Addresses) > 0 {
   270  		if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, asReq.ReqBody.Addresses) {
   271  			return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the AS_REP does not match those listed in the AS_REQ")
   272  		}
   273  	}
   274  	t := time.Now().UTC()
   275  	if t.Sub(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(t) > cfg.LibDefaults.Clockskew {
   276  		return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds", cfg.LibDefaults.Clockskew.Seconds())
   277  	}
   278  	// RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11
   279  	if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&k.DecryptedEncPart.Flags, flags.EncPARep) {
   280  		if len(k.DecryptedEncPart.EncPAData) < 2 || !k.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) {
   281  			return false, krberror.NewErrorf(krberror.KRBMsgError, "KDC did not respond appropriately to FAST negotiation")
   282  		}
   283  		for _, pa := range k.DecryptedEncPart.EncPAData {
   284  			if pa.PADataType == patype.PA_REQ_ENC_PA_REP {
   285  				var pafast types.PAReqEncPARep
   286  				err := pafast.Unmarshal(pa.PADataValue)
   287  				if err != nil {
   288  					return false, krberror.Errorf(err, krberror.EncodingError, "KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP")
   289  				}
   290  				etype, err := crypto.GetChksumEtype(pafast.ChksumType)
   291  				if err != nil {
   292  					return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response error")
   293  				}
   294  				ab, _ := asReq.Marshal()
   295  				if !etype.VerifyChecksum(key.KeyValue, ab, pafast.Chksum, keyusage.KEY_USAGE_AS_REQ) {
   296  					return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response checksum invalid")
   297  				}
   298  			}
   299  		}
   300  	}
   301  	return true, nil
   302  }
   303  
   304  // DecryptEncPart decrypts the encrypted part of an TGS_REP.
   305  func (k *TGSRep) DecryptEncPart(key types.EncryptionKey) error {
   306  	b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.TGS_REP_ENCPART_SESSION_KEY)
   307  	if err != nil {
   308  		return krberror.Errorf(err, krberror.DecryptingError, "error decrypting TGS_REP EncPart")
   309  	}
   310  	var denc EncKDCRepPart
   311  	err = denc.Unmarshal(b)
   312  	if err != nil {
   313  		return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part")
   314  	}
   315  	k.DecryptedEncPart = denc
   316  	return nil
   317  }
   318  
   319  // Verify checks the validity of the TGS_REP message.
   320  func (k *TGSRep) Verify(cfg *config.Config, tgsReq TGSReq) (bool, error) {
   321  	if !k.CName.Equal(tgsReq.ReqBody.CName) {
   322  		return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
   323  	}
   324  	if k.Ticket.Realm != tgsReq.ReqBody.Realm {
   325  		return false, krberror.NewErrorf(krberror.KRBMsgError, "realm in response ticket does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.Ticket.Realm)
   326  	}
   327  	if k.DecryptedEncPart.Nonce != tgsReq.ReqBody.Nonce {
   328  		return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
   329  	}
   330  	//if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil {
   331  	//	return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.Ticket.SName)
   332  	//}
   333  	//for i := range k.Ticket.SName.NameString {
   334  	//	if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
   335  	//		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response ticket does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.Ticket.SName)
   336  	//	}
   337  	//}
   338  	//if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
   339  	//	return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %v; Reply: %v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
   340  	//}
   341  	//for i := range k.DecryptedEncPart.SName.NameString {
   342  	//	if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
   343  	//		return false, krberror.NewErrorf(krberror.KRBMsgError, "SName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.SName, k.DecryptedEncPart.SName)
   344  	//	}
   345  	//}
   346  	if k.DecryptedEncPart.SRealm != tgsReq.ReqBody.Realm {
   347  		return false, krberror.NewErrorf(krberror.KRBMsgError, "SRealm in response does not match what was requested. Requested: %s; Reply: %s", tgsReq.ReqBody.Realm, k.DecryptedEncPart.SRealm)
   348  	}
   349  	if len(k.DecryptedEncPart.CAddr) > 0 {
   350  		if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, tgsReq.ReqBody.Addresses) {
   351  			return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the TGS_REP does not match those listed in the TGS_REQ")
   352  		}
   353  	}
   354  	if time.Since(k.DecryptedEncPart.StartTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.StartTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
   355  		if time.Since(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
   356  			return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds.", cfg.LibDefaults.Clockskew.Seconds())
   357  		}
   358  	}
   359  	return true, nil
   360  }