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

     1  package messages
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/jcmturner/gofork/encoding/asn1"
     9  	"github.com/jcmturner/gokrb5/v8/asn1tools"
    10  	"github.com/jcmturner/gokrb5/v8/crypto"
    11  	"github.com/jcmturner/gokrb5/v8/iana"
    12  	"github.com/jcmturner/gokrb5/v8/iana/adtype"
    13  	"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
    14  	"github.com/jcmturner/gokrb5/v8/iana/errorcode"
    15  	"github.com/jcmturner/gokrb5/v8/iana/flags"
    16  	"github.com/jcmturner/gokrb5/v8/iana/keyusage"
    17  	"github.com/jcmturner/gokrb5/v8/keytab"
    18  	"github.com/jcmturner/gokrb5/v8/krberror"
    19  	"github.com/jcmturner/gokrb5/v8/pac"
    20  	"github.com/jcmturner/gokrb5/v8/types"
    21  )
    22  
    23  // Reference: https://www.ietf.org/rfc/rfc4120.txt
    24  // Section: 5.3
    25  
    26  // Ticket implements the Kerberos ticket.
    27  type Ticket struct {
    28  	TktVNO           int                 `asn1:"explicit,tag:0"`
    29  	Realm            string              `asn1:"generalstring,explicit,tag:1"`
    30  	SName            types.PrincipalName `asn1:"explicit,tag:2"`
    31  	EncPart          types.EncryptedData `asn1:"explicit,tag:3"`
    32  	DecryptedEncPart EncTicketPart       `asn1:"optional"` // Not part of ASN1 bytes so marked as optional so unmarshalling works
    33  }
    34  
    35  // EncTicketPart is the encrypted part of the Ticket.
    36  type EncTicketPart struct {
    37  	Flags             asn1.BitString          `asn1:"explicit,tag:0"`
    38  	Key               types.EncryptionKey     `asn1:"explicit,tag:1"`
    39  	CRealm            string                  `asn1:"generalstring,explicit,tag:2"`
    40  	CName             types.PrincipalName     `asn1:"explicit,tag:3"`
    41  	Transited         TransitedEncoding       `asn1:"explicit,tag:4"`
    42  	AuthTime          time.Time               `asn1:"generalized,explicit,tag:5"`
    43  	StartTime         time.Time               `asn1:"generalized,explicit,optional,tag:6"`
    44  	EndTime           time.Time               `asn1:"generalized,explicit,tag:7"`
    45  	RenewTill         time.Time               `asn1:"generalized,explicit,optional,tag:8"`
    46  	CAddr             types.HostAddresses     `asn1:"explicit,optional,tag:9"`
    47  	AuthorizationData types.AuthorizationData `asn1:"explicit,optional,tag:10"`
    48  }
    49  
    50  // TransitedEncoding part of the ticket's encrypted part.
    51  type TransitedEncoding struct {
    52  	TRType   int32  `asn1:"explicit,tag:0"`
    53  	Contents []byte `asn1:"explicit,tag:1"`
    54  }
    55  
    56  // NewTicket creates a new Ticket instance.
    57  func NewTicket(cname types.PrincipalName, crealm string, sname types.PrincipalName, srealm string, flags asn1.BitString, sktab *keytab.Keytab, eTypeID int32, kvno int, authTime, startTime, endTime, renewTill time.Time) (Ticket, types.EncryptionKey, error) {
    58  	etype, err := crypto.GetEtype(eTypeID)
    59  	if err != nil {
    60  		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting etype for new ticket")
    61  	}
    62  	sessionKey, err := types.GenerateEncryptionKey(etype)
    63  	if err != nil {
    64  		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error generating session key")
    65  	}
    66  
    67  	etp := EncTicketPart{
    68  		Flags:     flags,
    69  		Key:       sessionKey,
    70  		CRealm:    crealm,
    71  		CName:     cname,
    72  		Transited: TransitedEncoding{},
    73  		AuthTime:  authTime,
    74  		StartTime: startTime,
    75  		EndTime:   endTime,
    76  		RenewTill: renewTill,
    77  	}
    78  	b, err := asn1.Marshal(etp)
    79  	if err != nil {
    80  		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncodingError, "error marshalling ticket encpart")
    81  	}
    82  	b = asn1tools.AddASNAppTag(b, asnAppTag.EncTicketPart)
    83  	skey, _, err := sktab.GetEncryptionKey(sname, srealm, kvno, eTypeID)
    84  	if err != nil {
    85  		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting encryption key for new ticket")
    86  	}
    87  	ed, err := crypto.GetEncryptedData(b, skey, keyusage.KDC_REP_TICKET, kvno)
    88  	if err != nil {
    89  		return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error encrypting ticket encpart")
    90  	}
    91  	tkt := Ticket{
    92  		TktVNO:  iana.PVNO,
    93  		Realm:   srealm,
    94  		SName:   sname,
    95  		EncPart: ed,
    96  	}
    97  	return tkt, sessionKey, nil
    98  }
    99  
   100  // Unmarshal bytes b into a Ticket struct.
   101  func (t *Ticket) Unmarshal(b []byte) error {
   102  	_, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.Ticket))
   103  	return err
   104  }
   105  
   106  // Marshal the Ticket.
   107  func (t *Ticket) Marshal() ([]byte, error) {
   108  	b, err := asn1.Marshal(*t)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	b = asn1tools.AddASNAppTag(b, asnAppTag.Ticket)
   113  	return b, nil
   114  }
   115  
   116  // Unmarshal bytes b into the EncTicketPart struct.
   117  func (t *EncTicketPart) Unmarshal(b []byte) error {
   118  	_, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.EncTicketPart))
   119  	return err
   120  }
   121  
   122  // unmarshalTicket returns a ticket from the bytes provided.
   123  func unmarshalTicket(b []byte) (t Ticket, err error) {
   124  	err = t.Unmarshal(b)
   125  	return
   126  }
   127  
   128  // UnmarshalTicketsSequence returns a slice of Tickets from a raw ASN1 value.
   129  func unmarshalTicketsSequence(in asn1.RawValue) ([]Ticket, error) {
   130  	//This is a workaround to a asn1 decoding issue in golang - https://github.com/golang/go/issues/17321. It's not pretty I'm afraid
   131  	//We pull out raw values from the larger raw value (that is actually the data of the sequence of raw values) and track our position moving along the data.
   132  	b := in.Bytes
   133  	// Ignore the head of the asn1 stream (1 byte for tag and those for the length) as this is what tells us its a sequence but we're handling it ourselves
   134  	p := 1 + asn1tools.GetNumberBytesInLengthHeader(in.Bytes)
   135  	var tkts []Ticket
   136  	var raw asn1.RawValue
   137  	for p < (len(b)) {
   138  		_, err := asn1.UnmarshalWithParams(b[p:], &raw, fmt.Sprintf("application,tag:%d", asnAppTag.Ticket))
   139  		if err != nil {
   140  			return nil, fmt.Errorf("unmarshaling sequence of tickets failed getting length of ticket: %v", err)
   141  		}
   142  		t, err := unmarshalTicket(b[p:])
   143  		if err != nil {
   144  			return nil, fmt.Errorf("unmarshaling sequence of tickets failed: %v", err)
   145  		}
   146  		p += len(raw.FullBytes)
   147  		tkts = append(tkts, t)
   148  	}
   149  	MarshalTicketSequence(tkts)
   150  	return tkts, nil
   151  }
   152  
   153  // MarshalTicketSequence marshals a slice of Tickets returning an ASN1 raw value containing the ticket sequence.
   154  func MarshalTicketSequence(tkts []Ticket) (asn1.RawValue, error) {
   155  	raw := asn1.RawValue{
   156  		Class:      2,
   157  		IsCompound: true,
   158  	}
   159  	if len(tkts) < 1 {
   160  		// There are no tickets to marshal
   161  		return raw, nil
   162  	}
   163  	var btkts []byte
   164  	for i, t := range tkts {
   165  		b, err := t.Marshal()
   166  		if err != nil {
   167  			return raw, fmt.Errorf("error marshaling ticket number %d in sequence of tickets", i+1)
   168  		}
   169  		btkts = append(btkts, b...)
   170  	}
   171  	// The ASN1 wrapping consists of 2 bytes:
   172  	// 1st byte -> Identifier Octet - In this case an OCTET STRING (ASN TAG
   173  	// 2nd byte -> The length (this will be the size indicated in the input bytes + 2 for the additional bytes we add here.
   174  	// Application Tag:
   175  	//| Byte:       | 8                            | 7                          | 6                                         | 5 | 4 | 3 | 2 | 1             |
   176  	//| Value:      | 0                            | 1                          | 1                                         | From the RFC spec 4120        |
   177  	//| Explanation | Defined by the ASN1 encoding rules for an application tag | A value of 1 indicates a constructed type | The ASN Application tag value |
   178  	btkts = append(asn1tools.MarshalLengthBytes(len(btkts)), btkts...)
   179  	btkts = append([]byte{byte(32 + asn1.TagSequence)}, btkts...)
   180  	raw.Bytes = btkts
   181  	// If we need to create the full bytes then identifier octet is "context-specific" = 128 + "constructed" + 32 + the wrapping explicit tag (11)
   182  	//fmt.Fprintf(os.Stderr, "mRaw fb: %v\n", raw.FullBytes)
   183  	return raw, nil
   184  }
   185  
   186  // DecryptEncPart decrypts the encrypted part of the ticket.
   187  // The sname argument can be used to specify which service principal's key should be used to decrypt the ticket.
   188  // If nil is passed as the sname then the service principal specified within the ticket it used.
   189  func (t *Ticket) DecryptEncPart(keytab *keytab.Keytab, sname *types.PrincipalName) error {
   190  	if sname == nil {
   191  		sname = &t.SName
   192  	}
   193  	key, _, err := keytab.GetEncryptionKey(*sname, t.Realm, t.EncPart.KVNO, t.EncPart.EType)
   194  	if err != nil {
   195  		return NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err))
   196  	}
   197  	return t.Decrypt(key)
   198  }
   199  
   200  // Decrypt decrypts the encrypted part of the ticket using the key provided.
   201  func (t *Ticket) Decrypt(key types.EncryptionKey) error {
   202  	b, err := crypto.DecryptEncPart(t.EncPart, key, keyusage.KDC_REP_TICKET)
   203  	if err != nil {
   204  		return fmt.Errorf("error decrypting Ticket EncPart: %v", err)
   205  	}
   206  	var denc EncTicketPart
   207  	err = denc.Unmarshal(b)
   208  	if err != nil {
   209  		return fmt.Errorf("error unmarshaling encrypted part: %v", err)
   210  	}
   211  	t.DecryptedEncPart = denc
   212  	return nil
   213  }
   214  
   215  // GetPACType returns a Microsoft PAC that has been extracted from the ticket and processed.
   216  func (t *Ticket) GetPACType(keytab *keytab.Keytab, sname *types.PrincipalName, l *log.Logger) (bool, pac.PACType, error) {
   217  	var isPAC bool
   218  	for _, ad := range t.DecryptedEncPart.AuthorizationData {
   219  		if ad.ADType == adtype.ADIfRelevant {
   220  			var ad2 types.AuthorizationData
   221  			err := ad2.Unmarshal(ad.ADData)
   222  			if err != nil {
   223  				l.Printf("PAC authorization data could not be unmarshaled: %v", err)
   224  				continue
   225  			}
   226  			if ad2[0].ADType == adtype.ADWin2KPAC {
   227  				isPAC = true
   228  				var p pac.PACType
   229  				err = p.Unmarshal(ad2[0].ADData)
   230  				if err != nil {
   231  					return isPAC, p, fmt.Errorf("error unmarshaling PAC: %v", err)
   232  				}
   233  				if sname == nil {
   234  					sname = &t.SName
   235  				}
   236  				key, _, err := keytab.GetEncryptionKey(*sname, t.Realm, t.EncPart.KVNO, t.EncPart.EType)
   237  				if err != nil {
   238  					return isPAC, p, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err))
   239  				}
   240  				err = p.ProcessPACInfoBuffers(key, l)
   241  				return isPAC, p, err
   242  			}
   243  		}
   244  	}
   245  	return isPAC, pac.PACType{}, nil
   246  }
   247  
   248  // Valid checks it the ticket is currently valid. Max duration passed endtime passed in as argument.
   249  func (t *Ticket) Valid(d time.Duration) (bool, error) {
   250  	// Check for future tickets or invalid tickets
   251  	time := time.Now().UTC()
   252  	if t.DecryptedEncPart.StartTime.Sub(time) > d || types.IsFlagSet(&t.DecryptedEncPart.Flags, flags.Invalid) {
   253  		return false, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_TKT_NYV, "service ticket provided is not yet valid")
   254  	}
   255  
   256  	// Check for expired ticket
   257  	if time.Sub(t.DecryptedEncPart.EndTime) > d {
   258  		return false, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "service ticket provided has expired")
   259  	}
   260  
   261  	return true, nil
   262  }