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

     1  package messages
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/jcmturner/gofork/encoding/asn1"
     8  	"github.com/jcmturner/gokrb5/v8/asn1tools"
     9  	"github.com/jcmturner/gokrb5/v8/crypto"
    10  	"github.com/jcmturner/gokrb5/v8/iana"
    11  	"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
    12  	"github.com/jcmturner/gokrb5/v8/iana/errorcode"
    13  	"github.com/jcmturner/gokrb5/v8/iana/keyusage"
    14  	"github.com/jcmturner/gokrb5/v8/iana/msgtype"
    15  	"github.com/jcmturner/gokrb5/v8/keytab"
    16  	"github.com/jcmturner/gokrb5/v8/krberror"
    17  	"github.com/jcmturner/gokrb5/v8/types"
    18  )
    19  
    20  type marshalAPReq struct {
    21  	PVNO      int            `asn1:"explicit,tag:0"`
    22  	MsgType   int            `asn1:"explicit,tag:1"`
    23  	APOptions asn1.BitString `asn1:"explicit,tag:2"`
    24  	// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
    25  	Ticket                 asn1.RawValue       `asn1:"explicit,tag:3"`
    26  	EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"`
    27  }
    28  
    29  // APReq implements RFC 4120 KRB_AP_REQ: https://tools.ietf.org/html/rfc4120#section-5.5.1.
    30  type APReq struct {
    31  	PVNO                   int                 `asn1:"explicit,tag:0"`
    32  	MsgType                int                 `asn1:"explicit,tag:1"`
    33  	APOptions              asn1.BitString      `asn1:"explicit,tag:2"`
    34  	Ticket                 Ticket              `asn1:"explicit,tag:3"`
    35  	EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"`
    36  	Authenticator          types.Authenticator `asn1:"optional"`
    37  }
    38  
    39  // NewAPReq generates a new KRB_AP_REQ struct.
    40  func NewAPReq(tkt Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) {
    41  	var a APReq
    42  	ed, err := encryptAuthenticator(auth, sessionKey, tkt)
    43  	if err != nil {
    44  		return a, krberror.Errorf(err, krberror.KRBMsgError, "error creating Authenticator for AP_REQ")
    45  	}
    46  	a = APReq{
    47  		PVNO:                   iana.PVNO,
    48  		MsgType:                msgtype.KRB_AP_REQ,
    49  		APOptions:              types.NewKrbFlags(),
    50  		Ticket:                 tkt,
    51  		EncryptedAuthenticator: ed,
    52  	}
    53  	return a, nil
    54  }
    55  
    56  // Encrypt Authenticator
    57  func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey, tkt Ticket) (types.EncryptedData, error) {
    58  	var ed types.EncryptedData
    59  	m, err := a.Marshal()
    60  	if err != nil {
    61  		return ed, krberror.Errorf(err, krberror.EncodingError, "marshaling error of EncryptedData form of Authenticator")
    62  	}
    63  	usage := authenticatorKeyUsage(tkt.SName)
    64  	ed, err = crypto.GetEncryptedData(m, sessionKey, uint32(usage), tkt.EncPart.KVNO)
    65  	if err != nil {
    66  		return ed, krberror.Errorf(err, krberror.EncryptingError, "error encrypting Authenticator")
    67  	}
    68  	return ed, nil
    69  }
    70  
    71  // DecryptAuthenticator decrypts the Authenticator within the AP_REQ.
    72  // sessionKey may simply be the key within the decrypted EncPart of the ticket within the AP_REQ.
    73  func (a *APReq) DecryptAuthenticator(sessionKey types.EncryptionKey) error {
    74  	usage := authenticatorKeyUsage(a.Ticket.SName)
    75  	ab, e := crypto.DecryptEncPart(a.EncryptedAuthenticator, sessionKey, uint32(usage))
    76  	if e != nil {
    77  		return fmt.Errorf("error decrypting authenticator: %v", e)
    78  	}
    79  	err := a.Authenticator.Unmarshal(ab)
    80  	if err != nil {
    81  		return fmt.Errorf("error unmarshaling authenticator: %v", err)
    82  	}
    83  	return nil
    84  }
    85  
    86  func authenticatorKeyUsage(pn types.PrincipalName) int {
    87  	if pn.NameString[0] == "krbtgt" {
    88  		return keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR
    89  	}
    90  	return keyusage.AP_REQ_AUTHENTICATOR
    91  }
    92  
    93  // Unmarshal bytes b into the APReq struct.
    94  func (a *APReq) Unmarshal(b []byte) error {
    95  	var m marshalAPReq
    96  	_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREQ))
    97  	if err != nil {
    98  		return krberror.Errorf(err, krberror.EncodingError, "unmarshal error of AP_REQ")
    99  	}
   100  	if m.MsgType != msgtype.KRB_AP_REQ {
   101  		return NewKRBError(types.PrincipalName{}, "", errorcode.KRB_AP_ERR_MSG_TYPE, errorcode.Lookup(errorcode.KRB_AP_ERR_MSG_TYPE))
   102  	}
   103  	a.PVNO = m.PVNO
   104  	a.MsgType = m.MsgType
   105  	a.APOptions = m.APOptions
   106  	a.EncryptedAuthenticator = m.EncryptedAuthenticator
   107  	a.Ticket, err = unmarshalTicket(m.Ticket.Bytes)
   108  	if err != nil {
   109  		return krberror.Errorf(err, krberror.EncodingError, "unmarshaling error of Ticket within AP_REQ")
   110  	}
   111  	return nil
   112  }
   113  
   114  // Marshal APReq struct.
   115  func (a *APReq) Marshal() ([]byte, error) {
   116  	m := marshalAPReq{
   117  		PVNO:                   a.PVNO,
   118  		MsgType:                a.MsgType,
   119  		APOptions:              a.APOptions,
   120  		EncryptedAuthenticator: a.EncryptedAuthenticator,
   121  	}
   122  	var b []byte
   123  	b, err := a.Ticket.Marshal()
   124  	if err != nil {
   125  		return b, err
   126  	}
   127  	m.Ticket = asn1.RawValue{
   128  		Class:      asn1.ClassContextSpecific,
   129  		IsCompound: true,
   130  		Tag:        3,
   131  		Bytes:      b,
   132  	}
   133  	mk, err := asn1.Marshal(m)
   134  	if err != nil {
   135  		return mk, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AP_REQ")
   136  	}
   137  	mk = asn1tools.AddASNAppTag(mk, asnAppTag.APREQ)
   138  	return mk, nil
   139  }
   140  
   141  // Verify an AP_REQ using service's keytab, spn and max acceptable clock skew duration.
   142  // The service ticket encrypted part and authenticator will be decrypted as part of this operation.
   143  func (a *APReq) Verify(kt *keytab.Keytab, d time.Duration, cAddr types.HostAddress, snameOverride *types.PrincipalName) (bool, error) {
   144  	// Decrypt ticket's encrypted part with service key
   145  	//TODO decrypt with service's session key from its TGT is use-to-user. Need to figure out how to get TGT.
   146  	//if types.IsFlagSet(&a.APOptions, flags.APOptionUseSessionKey) {
   147  	//	err := a.Ticket.Decrypt(tgt.DecryptedEncPart.Key)
   148  	//	if err != nil {
   149  	//		return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of ticket provided using session key")
   150  	//	}
   151  	//} else {
   152  	//	err := a.Ticket.DecryptEncPart(*kt, &a.Ticket.SName)
   153  	//	if err != nil {
   154  	//		return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided")
   155  	//	}
   156  	//}
   157  	sname := &a.Ticket.SName
   158  	if snameOverride != nil {
   159  		sname = snameOverride
   160  	}
   161  	err := a.Ticket.DecryptEncPart(kt, sname)
   162  	if err != nil {
   163  		return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided")
   164  	}
   165  
   166  	// Check time validity of ticket
   167  	ok, err := a.Ticket.Valid(d)
   168  	if err != nil || !ok {
   169  		return ok, err
   170  	}
   171  
   172  	// Check client's address is listed in the client addresses in the ticket
   173  	if len(a.Ticket.DecryptedEncPart.CAddr) > 0 {
   174  		//If client addresses are present check if any of them match the source IP that sent the APReq
   175  		//If there is no match return KRB_AP_ERR_BADADDR error.
   176  		if !types.HostAddressesContains(a.Ticket.DecryptedEncPart.CAddr, cAddr) {
   177  			return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "client address not within the list contained in the service ticket")
   178  		}
   179  	}
   180  
   181  	// Decrypt authenticator with session key from ticket's encrypted part
   182  	err = a.DecryptAuthenticator(a.Ticket.DecryptedEncPart.Key)
   183  	if err != nil {
   184  		return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BAD_INTEGRITY, "could not decrypt authenticator")
   185  	}
   186  
   187  	// Check CName in authenticator is the same as that in the ticket
   188  	if !a.Authenticator.CName.Equal(a.Ticket.DecryptedEncPart.CName) {
   189  		return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BADMATCH, "CName in Authenticator does not match that in service ticket")
   190  	}
   191  
   192  	// Check the clock skew between the client and the service server
   193  	ct := a.Authenticator.CTime.Add(time.Duration(a.Authenticator.Cusec) * time.Microsecond)
   194  	t := time.Now().UTC()
   195  	if t.Sub(ct) > d || ct.Sub(t) > d {
   196  		return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_SKEW, fmt.Sprintf("clock skew with client too large. greater than %v seconds", d))
   197  	}
   198  	return true, nil
   199  }