123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- package messages
- // Reference: https://www.ietf.org/rfc/rfc4120.txt
- // Section: 5.4.1
- import (
- "crypto/rand"
- "fmt"
- "math"
- "math/big"
- "time"
- "github.com/jcmturner/gofork/encoding/asn1"
- "github.com/jcmturner/gokrb5/v8/asn1tools"
- "github.com/jcmturner/gokrb5/v8/config"
- "github.com/jcmturner/gokrb5/v8/crypto"
- "github.com/jcmturner/gokrb5/v8/iana"
- "github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
- "github.com/jcmturner/gokrb5/v8/iana/flags"
- "github.com/jcmturner/gokrb5/v8/iana/keyusage"
- "github.com/jcmturner/gokrb5/v8/iana/msgtype"
- "github.com/jcmturner/gokrb5/v8/iana/nametype"
- "github.com/jcmturner/gokrb5/v8/iana/patype"
- "github.com/jcmturner/gokrb5/v8/krberror"
- "github.com/jcmturner/gokrb5/v8/types"
- )
- type marshalKDCReq struct {
- PVNO int `asn1:"explicit,tag:1"`
- MsgType int `asn1:"explicit,tag:2"`
- PAData types.PADataSequence `asn1:"explicit,optional,tag:3"`
- ReqBody asn1.RawValue `asn1:"explicit,tag:4"`
- }
- // KDCReqFields represents the KRB_KDC_REQ fields.
- type KDCReqFields struct {
- PVNO int
- MsgType int
- PAData types.PADataSequence
- ReqBody KDCReqBody
- Renewal bool
- }
- // ASReq implements RFC 4120 KRB_AS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
- type ASReq struct {
- KDCReqFields
- }
- // TGSReq implements RFC 4120 KRB_TGS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1.
- type TGSReq struct {
- KDCReqFields
- }
- type marshalKDCReqBody struct {
- KDCOptions asn1.BitString `asn1:"explicit,tag:0"`
- CName types.PrincipalName `asn1:"explicit,optional,tag:1"`
- Realm string `asn1:"generalstring,explicit,tag:2"`
- SName types.PrincipalName `asn1:"explicit,optional,tag:3"`
- From time.Time `asn1:"generalized,explicit,optional,tag:4"`
- Till time.Time `asn1:"generalized,explicit,tag:5"`
- RTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
- Nonce int `asn1:"explicit,tag:7"`
- EType []int32 `asn1:"explicit,tag:8"`
- Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"`
- EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
- // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
- AdditionalTickets asn1.RawValue `asn1:"explicit,optional,tag:11"`
- }
- // KDCReqBody implements the KRB_KDC_REQ request body.
- type KDCReqBody struct {
- KDCOptions asn1.BitString `asn1:"explicit,tag:0"`
- CName types.PrincipalName `asn1:"explicit,optional,tag:1"`
- Realm string `asn1:"generalstring,explicit,tag:2"`
- SName types.PrincipalName `asn1:"explicit,optional,tag:3"`
- From time.Time `asn1:"generalized,explicit,optional,tag:4"`
- Till time.Time `asn1:"generalized,explicit,tag:5"`
- RTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
- Nonce int `asn1:"explicit,tag:7"`
- EType []int32 `asn1:"explicit,tag:8"`
- Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"`
- EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"`
- AdditionalTickets []Ticket `asn1:"explicit,optional,tag:11"`
- }
- // NewASReqForTGT generates a new KRB_AS_REQ struct for a TGT request.
- func NewASReqForTGT(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
- sname := types.PrincipalName{
- NameType: nametype.KRB_NT_SRV_INST,
- NameString: []string{"krbtgt", realm},
- }
- return NewASReq(realm, c, cname, sname)
- }
- // NewASReqForChgPasswd generates a new KRB_AS_REQ struct for a change password request.
- func NewASReqForChgPasswd(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) {
- sname := types.PrincipalName{
- NameType: nametype.KRB_NT_PRINCIPAL,
- NameString: []string{"kadmin", "changepw"},
- }
- return NewASReq(realm, c, cname, sname)
- }
- // NewASReq generates a new KRB_AS_REQ struct for a given SNAME.
- func NewASReq(realm string, c *config.Config, cname, sname types.PrincipalName) (ASReq, error) {
- nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
- if err != nil {
- return ASReq{}, err
- }
- t := time.Now().UTC()
- // Copy the default options to make this thread safe
- kopts := types.NewKrbFlags()
- copy(kopts.Bytes, c.LibDefaults.KDCDefaultOptions.Bytes)
- kopts.BitLength = c.LibDefaults.KDCDefaultOptions.BitLength
- a := ASReq{
- KDCReqFields{
- PVNO: iana.PVNO,
- MsgType: msgtype.KRB_AS_REQ,
- PAData: types.PADataSequence{},
- ReqBody: KDCReqBody{
- KDCOptions: kopts,
- Realm: realm,
- CName: cname,
- SName: sname,
- Till: t.Add(c.LibDefaults.TicketLifetime),
- Nonce: int(nonce.Int64()),
- EType: c.LibDefaults.DefaultTktEnctypeIDs,
- },
- },
- }
- if c.LibDefaults.Forwardable {
- types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable)
- }
- if c.LibDefaults.Canonicalize {
- types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize)
- }
- if c.LibDefaults.Proxiable {
- types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable)
- }
- if c.LibDefaults.RenewLifetime != 0 {
- types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable)
- a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
- a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour)
- }
- if !c.LibDefaults.NoAddresses {
- ha, err := types.LocalHostAddresses()
- if err != nil {
- return a, fmt.Errorf("could not get local addresses: %v", err)
- }
- ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
- a.ReqBody.Addresses = ha
- }
- return a, nil
- }
- // NewTGSReq generates a new KRB_TGS_REQ struct.
- func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tgt Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool) (TGSReq, error) {
- a, err := tgsReq(cname, sname, kdcRealm, renewal, c)
- if err != nil {
- return a, err
- }
- err = a.setPAData(tgt, sessionKey)
- return a, err
- }
- // NewUser2UserTGSReq returns a TGS-REQ suitable for user-to-user authentication (https://tools.ietf.org/html/rfc4120#section-3.7)
- func NewUser2UserTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, clientTGT Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool, verifyingTGT Ticket) (TGSReq, error) {
- a, err := tgsReq(cname, sname, kdcRealm, renewal, c)
- if err != nil {
- return a, err
- }
- a.ReqBody.AdditionalTickets = []Ticket{verifyingTGT}
- types.SetFlag(&a.ReqBody.KDCOptions, flags.EncTktInSkey)
- err = a.setPAData(clientTGT, sessionKey)
- return a, err
- }
- // tgsReq populates the fields for a TGS_REQ
- func tgsReq(cname, sname types.PrincipalName, kdcRealm string, renewal bool, c *config.Config) (TGSReq, error) {
- nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
- if err != nil {
- return TGSReq{}, err
- }
- t := time.Now().UTC()
- k := KDCReqFields{
- PVNO: iana.PVNO,
- MsgType: msgtype.KRB_TGS_REQ,
- ReqBody: KDCReqBody{
- KDCOptions: types.NewKrbFlags(),
- Realm: kdcRealm,
- CName: cname, // Add the CName to make validation of the reply easier
- SName: sname,
- Till: t.Add(c.LibDefaults.TicketLifetime),
- Nonce: int(nonce.Int64()),
- EType: c.LibDefaults.DefaultTGSEnctypeIDs,
- },
- Renewal: renewal,
- }
- if c.LibDefaults.Forwardable {
- types.SetFlag(&k.ReqBody.KDCOptions, flags.Forwardable)
- }
- if c.LibDefaults.Canonicalize {
- types.SetFlag(&k.ReqBody.KDCOptions, flags.Canonicalize)
- }
- if c.LibDefaults.Proxiable {
- types.SetFlag(&k.ReqBody.KDCOptions, flags.Proxiable)
- }
- if c.LibDefaults.RenewLifetime > time.Duration(0) {
- types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable)
- k.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime)
- }
- if !c.LibDefaults.NoAddresses {
- ha, err := types.LocalHostAddresses()
- if err != nil {
- return TGSReq{}, fmt.Errorf("could not get local addresses: %v", err)
- }
- ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...)
- k.ReqBody.Addresses = ha
- }
- if renewal {
- types.SetFlag(&k.ReqBody.KDCOptions, flags.Renew)
- types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable)
- }
- return TGSReq{
- k,
- }, nil
- }
- func (k *TGSReq) setPAData(tgt Ticket, sessionKey types.EncryptionKey) error {
- // Marshal the request and calculate checksum
- b, err := k.ReqBody.Marshal()
- if err != nil {
- return krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REQ body")
- }
- etype, err := crypto.GetEtype(sessionKey.KeyType)
- if err != nil {
- return krberror.Errorf(err, krberror.EncryptingError, "error getting etype to encrypt authenticator")
- }
- cb, err := etype.GetChecksumHash(sessionKey.KeyValue, b, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM)
- if err != nil {
- return krberror.Errorf(err, krberror.ChksumError, "error getting etype checksum hash")
- }
- // Form PAData for TGS_REQ
- // Create authenticator
- auth, err := types.NewAuthenticator(tgt.Realm, k.ReqBody.CName)
- if err != nil {
- return krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
- }
- auth.Cksum = types.Checksum{
- CksumType: etype.GetHashID(),
- Checksum: cb,
- }
- // Create AP_REQ
- apReq, err := NewAPReq(tgt, sessionKey, auth)
- if err != nil {
- return krberror.Errorf(err, krberror.KRBMsgError, "error generating new AP_REQ")
- }
- apb, err := apReq.Marshal()
- if err != nil {
- return krberror.Errorf(err, krberror.EncodingError, "error marshaling AP_REQ for pre-authentication data")
- }
- k.PAData = types.PADataSequence{
- types.PAData{
- PADataType: patype.PA_TGS_REQ,
- PADataValue: apb,
- },
- }
- return nil
- }
- // Unmarshal bytes b into the ASReq struct.
- func (k *ASReq) Unmarshal(b []byte) error {
- var m marshalKDCReq
- _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ))
- if err != nil {
- return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling AS_REQ")
- }
- expectedMsgType := msgtype.KRB_AS_REQ
- if m.MsgType != expectedMsgType {
- return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a AS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
- }
- var reqb KDCReqBody
- err = reqb.Unmarshal(m.ReqBody.Bytes)
- if err != nil {
- return krberror.Errorf(err, krberror.EncodingError, "error processing AS_REQ body")
- }
- k.MsgType = m.MsgType
- k.PAData = m.PAData
- k.PVNO = m.PVNO
- k.ReqBody = reqb
- return nil
- }
- // Unmarshal bytes b into the TGSReq struct.
- func (k *TGSReq) Unmarshal(b []byte) error {
- var m marshalKDCReq
- _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREQ))
- if err != nil {
- return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling TGS_REQ")
- }
- expectedMsgType := msgtype.KRB_TGS_REQ
- if m.MsgType != expectedMsgType {
- return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a TGS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
- }
- var reqb KDCReqBody
- err = reqb.Unmarshal(m.ReqBody.Bytes)
- if err != nil {
- return krberror.Errorf(err, krberror.EncodingError, "error processing TGS_REQ body")
- }
- k.MsgType = m.MsgType
- k.PAData = m.PAData
- k.PVNO = m.PVNO
- k.ReqBody = reqb
- return nil
- }
- // Unmarshal bytes b into the KRB_KDC_REQ body struct.
- func (k *KDCReqBody) Unmarshal(b []byte) error {
- var m marshalKDCReqBody
- _, err := asn1.Unmarshal(b, &m)
- if err != nil {
- return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling KDC_REQ body")
- }
- k.KDCOptions = m.KDCOptions
- if len(k.KDCOptions.Bytes) < 4 {
- tb := make([]byte, 4-len(k.KDCOptions.Bytes))
- k.KDCOptions.Bytes = append(tb, k.KDCOptions.Bytes...)
- k.KDCOptions.BitLength = len(k.KDCOptions.Bytes) * 8
- }
- k.CName = m.CName
- k.Realm = m.Realm
- k.SName = m.SName
- k.From = m.From
- k.Till = m.Till
- k.RTime = m.RTime
- k.Nonce = m.Nonce
- k.EType = m.EType
- k.Addresses = m.Addresses
- k.EncAuthData = m.EncAuthData
- if len(m.AdditionalTickets.Bytes) > 0 {
- k.AdditionalTickets, err = unmarshalTicketsSequence(m.AdditionalTickets)
- if err != nil {
- return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling additional tickets")
- }
- }
- return nil
- }
- // Marshal ASReq struct.
- func (k *ASReq) Marshal() ([]byte, error) {
- m := marshalKDCReq{
- PVNO: k.PVNO,
- MsgType: k.MsgType,
- PAData: k.PAData,
- }
- b, err := k.ReqBody.Marshal()
- if err != nil {
- var mk []byte
- return mk, err
- }
- m.ReqBody = asn1.RawValue{
- Class: asn1.ClassContextSpecific,
- IsCompound: true,
- Tag: 4,
- Bytes: b,
- }
- mk, err := asn1.Marshal(m)
- if err != nil {
- return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
- }
- mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREQ)
- return mk, nil
- }
- // Marshal TGSReq struct.
- func (k *TGSReq) Marshal() ([]byte, error) {
- m := marshalKDCReq{
- PVNO: k.PVNO,
- MsgType: k.MsgType,
- PAData: k.PAData,
- }
- b, err := k.ReqBody.Marshal()
- if err != nil {
- var mk []byte
- return mk, err
- }
- m.ReqBody = asn1.RawValue{
- Class: asn1.ClassContextSpecific,
- IsCompound: true,
- Tag: 4,
- Bytes: b,
- }
- mk, err := asn1.Marshal(m)
- if err != nil {
- return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ")
- }
- mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREQ)
- return mk, nil
- }
- // Marshal KRB_KDC_REQ body struct.
- func (k *KDCReqBody) Marshal() ([]byte, error) {
- var b []byte
- m := marshalKDCReqBody{
- KDCOptions: k.KDCOptions,
- CName: k.CName,
- Realm: k.Realm,
- SName: k.SName,
- From: k.From,
- Till: k.Till,
- RTime: k.RTime,
- Nonce: k.Nonce,
- EType: k.EType,
- Addresses: k.Addresses,
- EncAuthData: k.EncAuthData,
- }
- rawtkts, err := MarshalTicketSequence(k.AdditionalTickets)
- if err != nil {
- return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body additional tickets")
- }
- //The asn1.rawValue needs the tag setting on it for where it is in the KDCReqBody
- rawtkts.Tag = 11
- if len(rawtkts.Bytes) > 0 {
- m.AdditionalTickets = rawtkts
- }
- b, err = asn1.Marshal(m)
- if err != nil {
- return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body")
- }
- return b, nil
- }
|