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

     1  package messages
     2  
     3  // Reference: https://www.ietf.org/rfc/rfc4120.txt
     4  // Section: 5.4.1
     5  
     6  import (
     7  	"crypto/rand"
     8  	"fmt"
     9  	"math"
    10  	"math/big"
    11  	"time"
    12  
    13  	"github.com/jcmturner/gofork/encoding/asn1"
    14  	"github.com/jcmturner/gokrb5/v8/asn1tools"
    15  	"github.com/jcmturner/gokrb5/v8/config"
    16  	"github.com/jcmturner/gokrb5/v8/crypto"
    17  	"github.com/jcmturner/gokrb5/v8/iana"
    18  	"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
    19  	"github.com/jcmturner/gokrb5/v8/iana/flags"
    20  	"github.com/jcmturner/gokrb5/v8/iana/keyusage"
    21  	"github.com/jcmturner/gokrb5/v8/iana/msgtype"
    22  	"github.com/jcmturner/gokrb5/v8/iana/nametype"
    23  	"github.com/jcmturner/gokrb5/v8/iana/patype"
    24  	"github.com/jcmturner/gokrb5/v8/krberror"
    25  	"github.com/jcmturner/gokrb5/v8/types"
    26  )
    27  
    28  type marshalKDCReq struct {
    29  	PVNO    int                  `asn1:"explicit,tag:1"`
    30  	MsgType int                  `asn1:"explicit,tag:2"`
    31  	PAData  types.PADataSequence `asn1:"explicit,optional,tag:3"`
    32  	ReqBody asn1.RawValue        `asn1:"explicit,tag:4"`
    33  }
    34  
    35  // KDCReqFields represents the KRB_KDC_REQ fields.
    36  type KDCReqFields struct {
    37  	PVNO    int
    38  	MsgType int
    39  	PAData  types.PADataSequence
    40  	ReqBody KDCReqBody
    41  	Renewal bool
    42  }
    43  
    44  // ASReq implements RFC 4120 KRB_AS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
    45  type ASReq struct {
    46  	KDCReqFields
    47  }
    48  
    49  // TGSReq implements RFC 4120 KRB_TGS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
    50  type TGSReq struct {
    51  	KDCReqFields
    52  }
    53  
    54  type marshalKDCReqBody struct {
    55  	KDCOptions  asn1.BitString      `asn1:"explicit,tag:0"`
    56  	CName       types.PrincipalName `asn1:"explicit,optional,tag:1"`
    57  	Realm       string              `asn1:"generalstring,explicit,tag:2"`
    58  	SName       types.PrincipalName `asn1:"explicit,optional,tag:3"`
    59  	From        time.Time           `asn1:"generalized,explicit,optional,tag:4"`
    60  	Till        time.Time           `asn1:"generalized,explicit,tag:5"`
    61  	RTime       time.Time           `asn1:"generalized,explicit,optional,tag:6"`
    62  	Nonce       int                 `asn1:"explicit,tag:7"`
    63  	EType       []int32             `asn1:"explicit,tag:8"`
    64  	Addresses   []types.HostAddress `asn1:"explicit,optional,tag:9"`
    65  	EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
    66  	// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
    67  	AdditionalTickets asn1.RawValue `asn1:"explicit,optional,tag:11"`
    68  }
    69  
    70  // KDCReqBody implements the KRB_KDC_REQ request body.
    71  type KDCReqBody struct {
    72  	KDCOptions        asn1.BitString      `asn1:"explicit,tag:0"`
    73  	CName             types.PrincipalName `asn1:"explicit,optional,tag:1"`
    74  	Realm             string              `asn1:"generalstring,explicit,tag:2"`
    75  	SName             types.PrincipalName `asn1:"explicit,optional,tag:3"`
    76  	From              time.Time           `asn1:"generalized,explicit,optional,tag:4"`
    77  	Till              time.Time           `asn1:"generalized,explicit,tag:5"`
    78  	RTime             time.Time           `asn1:"generalized,explicit,optional,tag:6"`
    79  	Nonce             int                 `asn1:"explicit,tag:7"`
    80  	EType             []int32             `asn1:"explicit,tag:8"`
    81  	Addresses         []types.HostAddress `asn1:"explicit,optional,tag:9"`
    82  	EncAuthData       types.EncryptedData `asn1:"explicit,optional,tag:10"`
    83  	AdditionalTickets []Ticket            `asn1:"explicit,optional,tag:11"`
    84  }
    85  
    86  // NewASReqForTGT generates a new KRB_AS_REQ struct for a TGT request.
    87  func NewASReqForTGT(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
    88  	sname := types.PrincipalName{
    89  		NameType:   nametype.KRB_NT_SRV_INST,
    90  		NameString: []string{"krbtgt", realm},
    91  	}
    92  	return NewASReq(realm, c, cname, sname)
    93  }
    94  
    95  // NewASReqForChgPasswd generates a new KRB_AS_REQ struct for a change password request.
    96  func NewASReqForChgPasswd(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
    97  	sname := types.PrincipalName{
    98  		NameType:   nametype.KRB_NT_PRINCIPAL,
    99  		NameString: []string{"kadmin", "changepw"},
   100  	}
   101  	return NewASReq(realm, c, cname, sname)
   102  }
   103  
   104  // NewASReq generates a new KRB_AS_REQ struct for a given SNAME.
   105  func NewASReq(realm string, c *config.Config, cname, sname types.PrincipalName) (ASReq, error) {
   106  	nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
   107  	if err != nil {
   108  		return ASReq{}, err
   109  	}
   110  	t := time.Now().UTC()
   111  	// Copy the default options to make this thread safe
   112  	kopts := types.NewKrbFlags()
   113  	copy(kopts.Bytes, c.LibDefaults.KDCDefaultOptions.Bytes)
   114  	kopts.BitLength = c.LibDefaults.KDCDefaultOptions.BitLength
   115  	a := ASReq{
   116  		KDCReqFields{
   117  			PVNO:    iana.PVNO,
   118  			MsgType: msgtype.KRB_AS_REQ,
   119  			PAData:  types.PADataSequence{},
   120  			ReqBody: KDCReqBody{
   121  				KDCOptions: kopts,
   122  				Realm:      realm,
   123  				CName:      cname,
   124  				SName:      sname,
   125  				Till:       t.Add(c.LibDefaults.TicketLifetime),
   126  				Nonce:      int(nonce.Int64()),
   127  				EType:      c.LibDefaults.DefaultTktEnctypeIDs,
   128  			},
   129  		},
   130  	}
   131  	if c.LibDefaults.Forwardable {
   132  		types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable)
   133  	}
   134  	if c.LibDefaults.Canonicalize {
   135  		types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize)
   136  	}
   137  	if c.LibDefaults.Proxiable {
   138  		types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable)
   139  	}
   140  	if c.LibDefaults.RenewLifetime != 0 {
   141  		types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
   142  		a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
   143  		a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour)
   144  	}
   145  	if !c.LibDefaults.NoAddresses {
   146  		ha, err := types.LocalHostAddresses()
   147  		if err != nil {
   148  			return a, fmt.Errorf("could not get local addresses: %v", err)
   149  		}
   150  		ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
   151  		a.ReqBody.Addresses = ha
   152  	}
   153  	return a, nil
   154  }
   155  
   156  // NewTGSReq generates a new KRB_TGS_REQ struct.
   157  func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tgt Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool) (TGSReq, error) {
   158  	a, err := tgsReq(cname, sname, kdcRealm, renewal, c)
   159  	if err != nil {
   160  		return a, err
   161  	}
   162  	err = a.setPAData(tgt, sessionKey)
   163  	return a, err
   164  }
   165  
   166  // NewUser2UserTGSReq returns a TGS-REQ suitable for user-to-user authentication (https://tools.ietf.org/html/rfc4120#section-3.7)
   167  func NewUser2UserTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, clientTGT Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool, verifyingTGT Ticket) (TGSReq, error) {
   168  	a, err := tgsReq(cname, sname, kdcRealm, renewal, c)
   169  	if err != nil {
   170  		return a, err
   171  	}
   172  	a.ReqBody.AdditionalTickets = []Ticket{verifyingTGT}
   173  	types.SetFlag(&a.ReqBody.KDCOptions, flags.EncTktInSkey)
   174  	err = a.setPAData(clientTGT, sessionKey)
   175  	return a, err
   176  }
   177  
   178  // tgsReq populates the fields for a TGS_REQ
   179  func tgsReq(cname, sname types.PrincipalName, kdcRealm string, renewal bool, c *config.Config) (TGSReq, error) {
   180  	nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
   181  	if err != nil {
   182  		return TGSReq{}, err
   183  	}
   184  	t := time.Now().UTC()
   185  	k := KDCReqFields{
   186  		PVNO:    iana.PVNO,
   187  		MsgType: msgtype.KRB_TGS_REQ,
   188  		ReqBody: KDCReqBody{
   189  			KDCOptions: types.NewKrbFlags(),
   190  			Realm:      kdcRealm,
   191  			CName:      cname, // Add the CName to make validation of the reply easier
   192  			SName:      sname,
   193  			Till:       t.Add(c.LibDefaults.TicketLifetime),
   194  			Nonce:      int(nonce.Int64()),
   195  			EType:      c.LibDefaults.DefaultTGSEnctypeIDs,
   196  		},
   197  		Renewal: renewal,
   198  	}
   199  	if c.LibDefaults.Forwardable {
   200  		types.SetFlag(&k.ReqBody.KDCOptions, flags.Forwardable)
   201  	}
   202  	if c.LibDefaults.Canonicalize {
   203  		types.SetFlag(&k.ReqBody.KDCOptions, flags.Canonicalize)
   204  	}
   205  	if c.LibDefaults.Proxiable {
   206  		types.SetFlag(&k.ReqBody.KDCOptions, flags.Proxiable)
   207  	}
   208  	if c.LibDefaults.RenewLifetime > time.Duration(0) {
   209  		types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable)
   210  		k.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
   211  	}
   212  	if !c.LibDefaults.NoAddresses {
   213  		ha, err := types.LocalHostAddresses()
   214  		if err != nil {
   215  			return TGSReq{}, fmt.Errorf("could not get local addresses: %v", err)
   216  		}
   217  		ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
   218  		k.ReqBody.Addresses = ha
   219  	}
   220  	if renewal {
   221  		types.SetFlag(&k.ReqBody.KDCOptions, flags.Renew)
   222  		types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable)
   223  	}
   224  	return TGSReq{
   225  		k,
   226  	}, nil
   227  }
   228  
   229  func (k *TGSReq) setPAData(tgt Ticket, sessionKey types.EncryptionKey) error {
   230  	// Marshal the request and calculate checksum
   231  	b, err := k.ReqBody.Marshal()
   232  	if err != nil {
   233  		return krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REQ body")
   234  	}
   235  	etype, err := crypto.GetEtype(sessionKey.KeyType)
   236  	if err != nil {
   237  		return krberror.Errorf(err, krberror.EncryptingError, "error getting etype to encrypt authenticator")
   238  	}
   239  	cb, err := etype.GetChecksumHash(sessionKey.KeyValue, b, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM)
   240  	if err != nil {
   241  		return krberror.Errorf(err, krberror.ChksumError, "error getting etype checksum hash")
   242  	}
   243  
   244  	// Form PAData for TGS_REQ
   245  	// Create authenticator
   246  	auth, err := types.NewAuthenticator(tgt.Realm, k.ReqBody.CName)
   247  	if err != nil {
   248  		return krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
   249  	}
   250  	auth.Cksum = types.Checksum{
   251  		CksumType: etype.GetHashID(),
   252  		Checksum:  cb,
   253  	}
   254  	// Create AP_REQ
   255  	apReq, err := NewAPReq(tgt, sessionKey, auth)
   256  	if err != nil {
   257  		return krberror.Errorf(err, krberror.KRBMsgError, "error generating new AP_REQ")
   258  	}
   259  	apb, err := apReq.Marshal()
   260  	if err != nil {
   261  		return krberror.Errorf(err, krberror.EncodingError, "error marshaling AP_REQ for pre-authentication data")
   262  	}
   263  	k.PAData = types.PADataSequence{
   264  		types.PAData{
   265  			PADataType:  patype.PA_TGS_REQ,
   266  			PADataValue: apb,
   267  		},
   268  	}
   269  	return nil
   270  }
   271  
   272  // Unmarshal bytes b into the ASReq struct.
   273  func (k *ASReq) Unmarshal(b []byte) error {
   274  	var m marshalKDCReq
   275  	_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ))
   276  	if err != nil {
   277  		return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling AS_REQ")
   278  	}
   279  	expectedMsgType := msgtype.KRB_AS_REQ
   280  	if m.MsgType != expectedMsgType {
   281  		return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a AS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
   282  	}
   283  	var reqb KDCReqBody
   284  	err = reqb.Unmarshal(m.ReqBody.Bytes)
   285  	if err != nil {
   286  		return krberror.Errorf(err, krberror.EncodingError, "error processing AS_REQ body")
   287  	}
   288  	k.MsgType = m.MsgType
   289  	k.PAData = m.PAData
   290  	k.PVNO = m.PVNO
   291  	k.ReqBody = reqb
   292  	return nil
   293  }
   294  
   295  // Unmarshal bytes b into the TGSReq struct.
   296  func (k *TGSReq) Unmarshal(b []byte) error {
   297  	var m marshalKDCReq
   298  	_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREQ))
   299  	if err != nil {
   300  		return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling TGS_REQ")
   301  	}
   302  	expectedMsgType := msgtype.KRB_TGS_REQ
   303  	if m.MsgType != expectedMsgType {
   304  		return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a TGS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
   305  	}
   306  	var reqb KDCReqBody
   307  	err = reqb.Unmarshal(m.ReqBody.Bytes)
   308  	if err != nil {
   309  		return krberror.Errorf(err, krberror.EncodingError, "error processing TGS_REQ body")
   310  	}
   311  	k.MsgType = m.MsgType
   312  	k.PAData = m.PAData
   313  	k.PVNO = m.PVNO
   314  	k.ReqBody = reqb
   315  	return nil
   316  }
   317  
   318  // Unmarshal bytes b into the KRB_KDC_REQ body struct.
   319  func (k *KDCReqBody) Unmarshal(b []byte) error {
   320  	var m marshalKDCReqBody
   321  	_, err := asn1.Unmarshal(b, &m)
   322  	if err != nil {
   323  		return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling KDC_REQ body")
   324  	}
   325  	k.KDCOptions = m.KDCOptions
   326  	if len(k.KDCOptions.Bytes) < 4 {
   327  		tb := make([]byte, 4-len(k.KDCOptions.Bytes))
   328  		k.KDCOptions.Bytes = append(tb, k.KDCOptions.Bytes...)
   329  		k.KDCOptions.BitLength = len(k.KDCOptions.Bytes) * 8
   330  	}
   331  	k.CName = m.CName
   332  	k.Realm = m.Realm
   333  	k.SName = m.SName
   334  	k.From = m.From
   335  	k.Till = m.Till
   336  	k.RTime = m.RTime
   337  	k.Nonce = m.Nonce
   338  	k.EType = m.EType
   339  	k.Addresses = m.Addresses
   340  	k.EncAuthData = m.EncAuthData
   341  	if len(m.AdditionalTickets.Bytes) > 0 {
   342  		k.AdditionalTickets, err = unmarshalTicketsSequence(m.AdditionalTickets)
   343  		if err != nil {
   344  			return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling additional tickets")
   345  		}
   346  	}
   347  	return nil
   348  }
   349  
   350  // Marshal ASReq struct.
   351  func (k *ASReq) Marshal() ([]byte, error) {
   352  	m := marshalKDCReq{
   353  		PVNO:    k.PVNO,
   354  		MsgType: k.MsgType,
   355  		PAData:  k.PAData,
   356  	}
   357  	b, err := k.ReqBody.Marshal()
   358  	if err != nil {
   359  		var mk []byte
   360  		return mk, err
   361  	}
   362  	m.ReqBody = asn1.RawValue{
   363  		Class:      asn1.ClassContextSpecific,
   364  		IsCompound: true,
   365  		Tag:        4,
   366  		Bytes:      b,
   367  	}
   368  	mk, err := asn1.Marshal(m)
   369  	if err != nil {
   370  		return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
   371  	}
   372  	mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREQ)
   373  	return mk, nil
   374  }
   375  
   376  // Marshal TGSReq struct.
   377  func (k *TGSReq) Marshal() ([]byte, error) {
   378  	m := marshalKDCReq{
   379  		PVNO:    k.PVNO,
   380  		MsgType: k.MsgType,
   381  		PAData:  k.PAData,
   382  	}
   383  	b, err := k.ReqBody.Marshal()
   384  	if err != nil {
   385  		var mk []byte
   386  		return mk, err
   387  	}
   388  	m.ReqBody = asn1.RawValue{
   389  		Class:      asn1.ClassContextSpecific,
   390  		IsCompound: true,
   391  		Tag:        4,
   392  		Bytes:      b,
   393  	}
   394  	mk, err := asn1.Marshal(m)
   395  	if err != nil {
   396  		return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
   397  	}
   398  	mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREQ)
   399  	return mk, nil
   400  }
   401  
   402  // Marshal KRB_KDC_REQ body struct.
   403  func (k *KDCReqBody) Marshal() ([]byte, error) {
   404  	var b []byte
   405  	m := marshalKDCReqBody{
   406  		KDCOptions:  k.KDCOptions,
   407  		CName:       k.CName,
   408  		Realm:       k.Realm,
   409  		SName:       k.SName,
   410  		From:        k.From,
   411  		Till:        k.Till,
   412  		RTime:       k.RTime,
   413  		Nonce:       k.Nonce,
   414  		EType:       k.EType,
   415  		Addresses:   k.Addresses,
   416  		EncAuthData: k.EncAuthData,
   417  	}
   418  	rawtkts, err := MarshalTicketSequence(k.AdditionalTickets)
   419  	if err != nil {
   420  		return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body additional tickets")
   421  	}
   422  	//The asn1.rawValue needs the tag setting on it for where it is in the KDCReqBody
   423  	rawtkts.Tag = 11
   424  	if len(rawtkts.Bytes) > 0 {
   425  		m.AdditionalTickets = rawtkts
   426  	}
   427  	b, err = asn1.Marshal(m)
   428  	if err != nil {
   429  		return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body")
   430  	}
   431  	return b, nil
   432  }