1
0

Update module lib/pq to v1.6.0 (#572)

Update module lib/pq to v1.6.0

Reviewed-on: https://kolaente.dev/vikunja/api/pulls/572
This commit is contained in:
renovate
2020-05-29 17:47:28 +00:00
committed by konrad
parent 5a04f1ecf4
commit 54b18b3c59
161 changed files with 19472 additions and 7 deletions

View File

@ -0,0 +1,49 @@
package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
// APRep implements RFC 4120 KRB_AP_REP: https://tools.ietf.org/html/rfc4120#section-5.5.2.
type APRep struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
EncPart types.EncryptedData `asn1:"explicit,tag:2"`
}
// EncAPRepPart is the encrypted part of KRB_AP_REP.
type EncAPRepPart struct {
CTime time.Time `asn1:"generalized,explicit,tag:0"`
Cusec int `asn1:"explicit,tag:1"`
Subkey types.EncryptionKey `asn1:"optional,explicit,tag:2"`
SequenceNumber int64 `asn1:"optional,explicit,tag:3"`
}
// Unmarshal bytes b into the APRep struct.
func (a *APRep) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREP))
if err != nil {
return processUnmarshalReplyError(b, err)
}
expectedMsgType := msgtype.KRB_AP_REP
if a.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_AP_REP. Expected: %v; Actual: %v", expectedMsgType, a.MsgType)
}
return nil
}
// Unmarshal bytes b into the APRep encrypted part struct.
func (a *EncAPRepPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, a, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncAPRepPart))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "AP_REP unmarshal error")
}
return nil
}

199
vendor/github.com/jcmturner/gokrb5/v8/messages/APReq.go generated vendored Normal file
View File

@ -0,0 +1,199 @@
package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/asn1tools"
"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/errorcode"
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/keytab"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
type marshalAPReq struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
APOptions asn1.BitString `asn1:"explicit,tag:2"`
// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
Ticket asn1.RawValue `asn1:"explicit,tag:3"`
EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"`
}
// APReq implements RFC 4120 KRB_AP_REQ: https://tools.ietf.org/html/rfc4120#section-5.5.1.
type APReq struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
APOptions asn1.BitString `asn1:"explicit,tag:2"`
Ticket Ticket `asn1:"explicit,tag:3"`
EncryptedAuthenticator types.EncryptedData `asn1:"explicit,tag:4"`
Authenticator types.Authenticator `asn1:"optional"`
}
// NewAPReq generates a new KRB_AP_REQ struct.
func NewAPReq(tkt Ticket, sessionKey types.EncryptionKey, auth types.Authenticator) (APReq, error) {
var a APReq
ed, err := encryptAuthenticator(auth, sessionKey, tkt)
if err != nil {
return a, krberror.Errorf(err, krberror.KRBMsgError, "error creating Authenticator for AP_REQ")
}
a = APReq{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_AP_REQ,
APOptions: types.NewKrbFlags(),
Ticket: tkt,
EncryptedAuthenticator: ed,
}
return a, nil
}
// Encrypt Authenticator
func encryptAuthenticator(a types.Authenticator, sessionKey types.EncryptionKey, tkt Ticket) (types.EncryptedData, error) {
var ed types.EncryptedData
m, err := a.Marshal()
if err != nil {
return ed, krberror.Errorf(err, krberror.EncodingError, "marshaling error of EncryptedData form of Authenticator")
}
usage := authenticatorKeyUsage(tkt.SName)
ed, err = crypto.GetEncryptedData(m, sessionKey, uint32(usage), tkt.EncPart.KVNO)
if err != nil {
return ed, krberror.Errorf(err, krberror.EncryptingError, "error encrypting Authenticator")
}
return ed, nil
}
// DecryptAuthenticator decrypts the Authenticator within the AP_REQ.
// sessionKey may simply be the key within the decrypted EncPart of the ticket within the AP_REQ.
func (a *APReq) DecryptAuthenticator(sessionKey types.EncryptionKey) error {
usage := authenticatorKeyUsage(a.Ticket.SName)
ab, e := crypto.DecryptEncPart(a.EncryptedAuthenticator, sessionKey, uint32(usage))
if e != nil {
return fmt.Errorf("error decrypting authenticator: %v", e)
}
err := a.Authenticator.Unmarshal(ab)
if err != nil {
return fmt.Errorf("error unmarshaling authenticator: %v", err)
}
return nil
}
func authenticatorKeyUsage(pn types.PrincipalName) int {
if pn.NameString[0] == "krbtgt" {
return keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR
}
return keyusage.AP_REQ_AUTHENTICATOR
}
// Unmarshal bytes b into the APReq struct.
func (a *APReq) Unmarshal(b []byte) error {
var m marshalAPReq
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.APREQ))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "unmarshal error of AP_REQ")
}
if m.MsgType != msgtype.KRB_AP_REQ {
return NewKRBError(types.PrincipalName{}, "", errorcode.KRB_AP_ERR_MSG_TYPE, errorcode.Lookup(errorcode.KRB_AP_ERR_MSG_TYPE))
}
a.PVNO = m.PVNO
a.MsgType = m.MsgType
a.APOptions = m.APOptions
a.EncryptedAuthenticator = m.EncryptedAuthenticator
a.Ticket, err = unmarshalTicket(m.Ticket.Bytes)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "unmarshaling error of Ticket within AP_REQ")
}
return nil
}
// Marshal APReq struct.
func (a *APReq) Marshal() ([]byte, error) {
m := marshalAPReq{
PVNO: a.PVNO,
MsgType: a.MsgType,
APOptions: a.APOptions,
EncryptedAuthenticator: a.EncryptedAuthenticator,
}
var b []byte
b, err := a.Ticket.Marshal()
if err != nil {
return b, err
}
m.Ticket = asn1.RawValue{
Class: asn1.ClassContextSpecific,
IsCompound: true,
Tag: 3,
Bytes: b,
}
mk, err := asn1.Marshal(m)
if err != nil {
return mk, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AP_REQ")
}
mk = asn1tools.AddASNAppTag(mk, asnAppTag.APREQ)
return mk, nil
}
// Verify an AP_REQ using service's keytab, spn and max acceptable clock skew duration.
// The service ticket encrypted part and authenticator will be decrypted as part of this operation.
func (a *APReq) Verify(kt *keytab.Keytab, d time.Duration, cAddr types.HostAddress, snameOverride *types.PrincipalName) (bool, error) {
// Decrypt ticket's encrypted part with service key
//TODO decrypt with service's session key from its TGT is use-to-user. Need to figure out how to get TGT.
//if types.IsFlagSet(&a.APOptions, flags.APOptionUseSessionKey) {
// err := a.Ticket.Decrypt(tgt.DecryptedEncPart.Key)
// if err != nil {
// return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of ticket provided using session key")
// }
//} else {
// err := a.Ticket.DecryptEncPart(*kt, &a.Ticket.SName)
// if err != nil {
// return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided")
// }
//}
sname := &a.Ticket.SName
if snameOverride != nil {
sname = snameOverride
}
err := a.Ticket.DecryptEncPart(kt, sname)
if err != nil {
return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting encpart of service ticket provided")
}
// Check time validity of ticket
ok, err := a.Ticket.Valid(d)
if err != nil || !ok {
return ok, err
}
// Check client's address is listed in the client addresses in the ticket
if len(a.Ticket.DecryptedEncPart.CAddr) > 0 {
//If client addresses are present check if any of them match the source IP that sent the APReq
//If there is no match return KRB_AP_ERR_BADADDR error.
if !types.HostAddressesContains(a.Ticket.DecryptedEncPart.CAddr, cAddr) {
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")
}
}
// Decrypt authenticator with session key from ticket's encrypted part
err = a.DecryptAuthenticator(a.Ticket.DecryptedEncPart.Key)
if err != nil {
return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BAD_INTEGRITY, "could not decrypt authenticator")
}
// Check CName in authenticator is the same as that in the ticket
if !a.Authenticator.CName.Equal(a.Ticket.DecryptedEncPart.CName) {
return false, NewKRBError(a.Ticket.SName, a.Ticket.Realm, errorcode.KRB_AP_ERR_BADMATCH, "CName in Authenticator does not match that in service ticket")
}
// Check the clock skew between the client and the service server
ct := a.Authenticator.CTime.Add(time.Duration(a.Authenticator.Cusec) * time.Microsecond)
t := time.Now().UTC()
if t.Sub(ct) > d || ct.Sub(t) > d {
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))
}
return true, nil
}

View File

@ -0,0 +1,308 @@
package messages
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.4.2
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/config"
"github.com/jcmturner/gokrb5/v8/credentials"
"github.com/jcmturner/gokrb5/v8/crypto"
"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/patype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
type marshalKDCRep struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
PAData types.PADataSequence `asn1:"explicit,optional,tag:2"`
CRealm string `asn1:"generalstring,explicit,tag:3"`
CName types.PrincipalName `asn1:"explicit,tag:4"`
// Ticket needs to be a raw value as it is wrapped in an APPLICATION tag
Ticket asn1.RawValue `asn1:"explicit,tag:5"`
EncPart types.EncryptedData `asn1:"explicit,tag:6"`
}
// KDCRepFields represents the KRB_KDC_REP fields.
type KDCRepFields struct {
PVNO int
MsgType int
PAData []types.PAData
CRealm string
CName types.PrincipalName
Ticket Ticket
EncPart types.EncryptedData
DecryptedEncPart EncKDCRepPart
}
// ASRep implements RFC 4120 KRB_AS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
type ASRep struct {
KDCRepFields
}
// TGSRep implements RFC 4120 KRB_TGS_REP: https://tools.ietf.org/html/rfc4120#section-5.4.2.
type TGSRep struct {
KDCRepFields
}
// EncKDCRepPart is the encrypted part of KRB_KDC_REP.
type EncKDCRepPart struct {
Key types.EncryptionKey `asn1:"explicit,tag:0"`
LastReqs []LastReq `asn1:"explicit,tag:1"`
Nonce int `asn1:"explicit,tag:2"`
KeyExpiration time.Time `asn1:"generalized,explicit,optional,tag:3"`
Flags asn1.BitString `asn1:"explicit,tag:4"`
AuthTime time.Time `asn1:"generalized,explicit,tag:5"`
StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
EndTime time.Time `asn1:"generalized,explicit,tag:7"`
RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"`
SRealm string `asn1:"generalstring,explicit,tag:9"`
SName types.PrincipalName `asn1:"explicit,tag:10"`
CAddr []types.HostAddress `asn1:"explicit,optional,tag:11"`
EncPAData types.PADataSequence `asn1:"explicit,optional,tag:12"`
}
// LastReq part of KRB_KDC_REP.
type LastReq struct {
LRType int32 `asn1:"explicit,tag:0"`
LRValue time.Time `asn1:"generalized,explicit,tag:1"`
}
// Unmarshal bytes b into the ASRep struct.
func (k *ASRep) Unmarshal(b []byte) error {
var m marshalKDCRep
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREP))
if err != nil {
return processUnmarshalReplyError(b, err)
}
if m.MsgType != msgtype.KRB_AS_REP {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an AS_REP. Expected: %v; Actual: %v", msgtype.KRB_AS_REP, m.MsgType)
}
//Process the raw ticket within
tkt, err := unmarshalTicket(m.Ticket.Bytes)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within AS_REP")
}
k.KDCRepFields = KDCRepFields{
PVNO: m.PVNO,
MsgType: m.MsgType,
PAData: m.PAData,
CRealm: m.CRealm,
CName: m.CName,
Ticket: tkt,
EncPart: m.EncPart,
}
return nil
}
// Unmarshal bytes b into the TGSRep struct.
func (k *TGSRep) Unmarshal(b []byte) error {
var m marshalKDCRep
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREP))
if err != nil {
return processUnmarshalReplyError(b, err)
}
if m.MsgType != msgtype.KRB_TGS_REP {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate an TGS_REP. Expected: %v; Actual: %v", msgtype.KRB_TGS_REP, m.MsgType)
}
//Process the raw ticket within
tkt, err := unmarshalTicket(m.Ticket.Bytes)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling Ticket within TGS_REP")
}
k.KDCRepFields = KDCRepFields{
PVNO: m.PVNO,
MsgType: m.MsgType,
PAData: m.PAData,
CRealm: m.CRealm,
CName: m.CName,
Ticket: tkt,
EncPart: m.EncPart,
}
return nil
}
// Unmarshal bytes b into encrypted part of KRB_KDC_REP.
func (e *EncKDCRepPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncASRepPart))
if err != nil {
// Try using tag 26
// Ref: RFC 4120 - mentions that some implementations use application tag number 26 wether or not the reply is
// a AS-REP or a TGS-REP.
_, err = asn1.UnmarshalWithParams(b, e, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncTGSRepPart))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part within KDC_REP")
}
}
return nil
}
// DecryptEncPart decrypts the encrypted part of an AS_REP.
func (k *ASRep) DecryptEncPart(c *credentials.Credentials) (types.EncryptionKey, error) {
var key types.EncryptionKey
var err error
if c.HasKeytab() {
key, _, err = c.Keytab().GetEncryptionKey(k.CName, k.CRealm, k.EncPart.KVNO, k.EncPart.EType)
if err != nil {
return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
}
}
if c.HasPassword() {
key, _, err = crypto.GetKeyFromPassword(c.Password(), k.CName, k.CRealm, k.EncPart.EType, k.PAData)
if err != nil {
return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
}
}
if !c.HasKeytab() && !c.HasPassword() {
return key, krberror.NewErrorf(krberror.DecryptingError, "no secret available in credentials to perform decryption of AS_REP encrypted part")
}
b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.AS_REP_ENCPART)
if err != nil {
return key, krberror.Errorf(err, krberror.DecryptingError, "error decrypting AS_REP encrypted part")
}
var denc EncKDCRepPart
err = denc.Unmarshal(b)
if err != nil {
return key, krberror.Errorf(err, krberror.EncodingError, "error unmarshaling decrypted encpart of AS_REP")
}
k.DecryptedEncPart = denc
return key, nil
}
// Verify checks the validity of AS_REP message.
func (k *ASRep) Verify(cfg *config.Config, creds *credentials.Credentials, asReq ASReq) (bool, error) {
//Ref RFC 4120 Section 3.1.5
if k.CName.NameType != asReq.ReqBody.CName.NameType || k.CName.NameString == nil {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName)
}
for i := range k.CName.NameString {
if k.CName.NameString[i] != asReq.ReqBody.CName.NameString[i] {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", asReq.ReqBody.CName, k.CName)
}
}
if k.CRealm != asReq.ReqBody.Realm {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CRealm in response does not match what was requested. Requested: %s; Reply: %s", asReq.ReqBody.Realm, k.CRealm)
}
key, err := k.DecryptEncPart(creds)
if err != nil {
return false, krberror.Errorf(err, krberror.DecryptingError, "error decrypting EncPart of AS_REP")
}
if k.DecryptedEncPart.Nonce != asReq.ReqBody.Nonce {
return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
}
if k.DecryptedEncPart.SName.NameType != asReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
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)
}
for i := range k.CName.NameString {
if k.DecryptedEncPart.SName.NameString[i] != asReq.ReqBody.SName.NameString[i] {
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)
}
}
if k.DecryptedEncPart.SRealm != asReq.ReqBody.Realm {
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)
}
if len(asReq.ReqBody.Addresses) > 0 {
if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, asReq.ReqBody.Addresses) {
return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the AS_REP does not match those listed in the AS_REQ")
}
}
t := time.Now().UTC()
if t.Sub(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(t) > cfg.LibDefaults.Clockskew {
return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds", cfg.LibDefaults.Clockskew.Seconds())
}
// RFC 6806 https://tools.ietf.org/html/rfc6806.html#section-11
if asReq.PAData.Contains(patype.PA_REQ_ENC_PA_REP) && types.IsFlagSet(&k.DecryptedEncPart.Flags, flags.EncPARep) {
if len(k.DecryptedEncPart.EncPAData) < 2 || !k.DecryptedEncPart.EncPAData.Contains(patype.PA_FX_FAST) {
return false, krberror.NewErrorf(krberror.KRBMsgError, "KDC did not respond appropriately to FAST negotiation")
}
for _, pa := range k.DecryptedEncPart.EncPAData {
if pa.PADataType == patype.PA_REQ_ENC_PA_REP {
var pafast types.PAReqEncPARep
err := pafast.Unmarshal(pa.PADataValue)
if err != nil {
return false, krberror.Errorf(err, krberror.EncodingError, "KDC FAST negotiation response error, could not unmarshal PA_REQ_ENC_PA_REP")
}
etype, err := crypto.GetChksumEtype(pafast.ChksumType)
if err != nil {
return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response error")
}
ab, _ := asReq.Marshal()
if !etype.VerifyChecksum(key.KeyValue, ab, pafast.Chksum, keyusage.KEY_USAGE_AS_REQ) {
return false, krberror.Errorf(err, krberror.ChksumError, "KDC FAST negotiation response checksum invalid")
}
}
}
}
return true, nil
}
// DecryptEncPart decrypts the encrypted part of an TGS_REP.
func (k *TGSRep) DecryptEncPart(key types.EncryptionKey) error {
b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.TGS_REP_ENCPART_SESSION_KEY)
if err != nil {
return krberror.Errorf(err, krberror.DecryptingError, "error decrypting TGS_REP EncPart")
}
var denc EncKDCRepPart
err = denc.Unmarshal(b)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part")
}
k.DecryptedEncPart = denc
return nil
}
// Verify checks the validity of the TGS_REP message.
func (k *TGSRep) Verify(cfg *config.Config, tgsReq TGSReq) (bool, error) {
if k.CName.NameType != tgsReq.ReqBody.CName.NameType || k.CName.NameString == nil {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CName type in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
}
for i := range k.CName.NameString {
if k.CName.NameString[i] != tgsReq.ReqBody.CName.NameString[i] {
return false, krberror.NewErrorf(krberror.KRBMsgError, "CName in response does not match what was requested. Requested: %+v; Reply: %+v", tgsReq.ReqBody.CName, k.CName)
}
}
if k.Ticket.Realm != tgsReq.ReqBody.Realm {
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)
}
if k.DecryptedEncPart.Nonce != tgsReq.ReqBody.Nonce {
return false, krberror.NewErrorf(krberror.KRBMsgError, "possible replay attack, nonce in response does not match that in request")
}
//if k.Ticket.SName.NameType != tgsReq.ReqBody.SName.NameType || k.Ticket.SName.NameString == nil {
// 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)
//}
//for i := range k.Ticket.SName.NameString {
// if k.Ticket.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
// 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)
// }
//}
//if k.DecryptedEncPart.SName.NameType != tgsReq.ReqBody.SName.NameType || k.DecryptedEncPart.SName.NameString == nil {
// 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)
//}
//for i := range k.DecryptedEncPart.SName.NameString {
// if k.DecryptedEncPart.SName.NameString[i] != tgsReq.ReqBody.SName.NameString[i] {
// 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)
// }
//}
if k.DecryptedEncPart.SRealm != tgsReq.ReqBody.Realm {
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)
}
if len(k.DecryptedEncPart.CAddr) > 0 {
if !types.HostAddressesEqual(k.DecryptedEncPart.CAddr, tgsReq.ReqBody.Addresses) {
return false, krberror.NewErrorf(krberror.KRBMsgError, "addresses listed in the TGS_REP does not match those listed in the TGS_REQ")
}
}
if time.Since(k.DecryptedEncPart.StartTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.StartTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
if time.Since(k.DecryptedEncPart.AuthTime) > cfg.LibDefaults.Clockskew || k.DecryptedEncPart.AuthTime.Sub(time.Now().UTC()) > cfg.LibDefaults.Clockskew {
return false, krberror.NewErrorf(krberror.KRBMsgError, "clock skew with KDC too large. Greater than %v seconds.", cfg.LibDefaults.Clockskew.Seconds())
}
}
return true, nil
}

View File

@ -0,0 +1,432 @@
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
}

View File

@ -0,0 +1,102 @@
package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/crypto"
"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
type marshalKRBCred struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
Tickets asn1.RawValue `asn1:"explicit,tag:2"`
EncPart types.EncryptedData `asn1:"explicit,tag:3"`
}
// KRBCred implements RFC 4120 KRB_CRED: https://tools.ietf.org/html/rfc4120#section-5.8.1.
type KRBCred struct {
PVNO int
MsgType int
Tickets []Ticket
EncPart types.EncryptedData
DecryptedEncPart EncKrbCredPart
}
// EncKrbCredPart is the encrypted part of KRB_CRED.
type EncKrbCredPart struct {
TicketInfo []KrbCredInfo `asn1:"explicit,tag:0"`
Nouce int `asn1:"optional,explicit,tag:1"`
Timestamp time.Time `asn1:"generalized,optional,explicit,tag:2"`
Usec int `asn1:"optional,explicit,tag:3"`
SAddress types.HostAddress `asn1:"optional,explicit,tag:4"`
RAddress types.HostAddress `asn1:"optional,explicit,tag:5"`
}
// KrbCredInfo is the KRB_CRED_INFO part of KRB_CRED.
type KrbCredInfo struct {
Key types.EncryptionKey `asn1:"explicit,tag:0"`
PRealm string `asn1:"generalstring,optional,explicit,tag:1"`
PName types.PrincipalName `asn1:"optional,explicit,tag:2"`
Flags asn1.BitString `asn1:"optional,explicit,tag:3"`
AuthTime time.Time `asn1:"generalized,optional,explicit,tag:4"`
StartTime time.Time `asn1:"generalized,optional,explicit,tag:5"`
EndTime time.Time `asn1:"generalized,optional,explicit,tag:6"`
RenewTill time.Time `asn1:"generalized,optional,explicit,tag:7"`
SRealm string `asn1:"optional,explicit,ia5,tag:8"`
SName types.PrincipalName `asn1:"optional,explicit,tag:9"`
CAddr types.HostAddresses `asn1:"optional,explicit,tag:10"`
}
// Unmarshal bytes b into the KRBCred struct.
func (k *KRBCred) Unmarshal(b []byte) error {
var m marshalKRBCred
_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBCred))
if err != nil {
return processUnmarshalReplyError(b, err)
}
expectedMsgType := msgtype.KRB_CRED
if m.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_CRED. Expected: %v; Actual: %v", expectedMsgType, m.MsgType)
}
k.PVNO = m.PVNO
k.MsgType = m.MsgType
k.EncPart = m.EncPart
if len(m.Tickets.Bytes) > 0 {
k.Tickets, err = unmarshalTicketsSequence(m.Tickets)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling tickets within KRB_CRED")
}
}
return nil
}
// DecryptEncPart decrypts the encrypted part of a KRB_CRED.
func (k *KRBCred) DecryptEncPart(key types.EncryptionKey) error {
b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.KRB_CRED_ENCPART)
if err != nil {
return krberror.Errorf(err, krberror.DecryptingError, "error decrypting KRB_CRED EncPart")
}
var denc EncKrbCredPart
err = denc.Unmarshal(b)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling encrypted part of KRB_CRED")
}
k.DecryptedEncPart = denc
return nil
}
// Unmarshal bytes b into the encrypted part of KRB_CRED.
func (k *EncKrbCredPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncKrbCredPart))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling EncKrbCredPart")
}
return nil
}

View File

@ -0,0 +1,83 @@
// Package messages implements Kerberos 5 message types and methods.
package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/iana"
"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
"github.com/jcmturner/gokrb5/v8/iana/errorcode"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
// KRBError implements RFC 4120 KRB_ERROR: https://tools.ietf.org/html/rfc4120#section-5.9.1.
type KRBError struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
CTime time.Time `asn1:"generalized,optional,explicit,tag:2"`
Cusec int `asn1:"optional,explicit,tag:3"`
STime time.Time `asn1:"generalized,explicit,tag:4"`
Susec int `asn1:"explicit,tag:5"`
ErrorCode int32 `asn1:"explicit,tag:6"`
CRealm string `asn1:"generalstring,optional,explicit,tag:7"`
CName types.PrincipalName `asn1:"optional,explicit,tag:8"`
Realm string `asn1:"generalstring,explicit,tag:9"`
SName types.PrincipalName `asn1:"explicit,tag:10"`
EText string `asn1:"generalstring,optional,explicit,tag:11"`
EData []byte `asn1:"optional,explicit,tag:12"`
}
// NewKRBError creates a new KRBError.
func NewKRBError(sname types.PrincipalName, realm string, code int32, etext string) KRBError {
t := time.Now().UTC()
return KRBError{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_ERROR,
STime: t,
Susec: int((t.UnixNano() / int64(time.Microsecond)) - (t.Unix() * 1e6)),
ErrorCode: code,
SName: sname,
Realm: realm,
EText: etext,
}
}
// Unmarshal bytes b into the KRBError struct.
func (k *KRBError) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBError))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "KRB_ERROR unmarshal error")
}
expectedMsgType := msgtype.KRB_ERROR
if k.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_ERROR. Expected: %v; Actual: %v", expectedMsgType, k.MsgType)
}
return nil
}
// Error method implementing error interface on KRBError struct.
func (k KRBError) Error() string {
etxt := fmt.Sprintf("KRB Error: %s", errorcode.Lookup(k.ErrorCode))
if k.EText != "" {
etxt = fmt.Sprintf("%s - %s", etxt, k.EText)
}
return etxt
}
func processUnmarshalReplyError(b []byte, err error) error {
switch err.(type) {
case asn1.StructuralError:
var krberr KRBError
tmperr := krberr.Unmarshal(b)
if tmperr != nil {
return krberror.Errorf(err, krberror.EncodingError, "failed to unmarshal KDC's reply")
}
return krberr
default:
return krberror.Errorf(err, krberror.EncodingError, "failed to unmarshal KDC's reply")
}
}

View File

@ -0,0 +1,108 @@
package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/asn1tools"
"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/keyusage"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
// KRBPriv implements RFC 4120 type: https://tools.ietf.org/html/rfc4120#section-5.7.1.
type KRBPriv struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
EncPart types.EncryptedData `asn1:"explicit,tag:3"`
DecryptedEncPart EncKrbPrivPart `asn1:"optional,omitempty"` // Not part of ASN1 bytes so marked as optional so unmarshalling works
}
// EncKrbPrivPart is the encrypted part of KRB_PRIV.
type EncKrbPrivPart struct {
UserData []byte `asn1:"explicit,tag:0"`
Timestamp time.Time `asn1:"generalized,optional,explicit,tag:1"`
Usec int `asn1:"optional,explicit,tag:2"`
SequenceNumber int64 `asn1:"optional,explicit,tag:3"`
SAddress types.HostAddress `asn1:"explicit,tag:4"`
RAddress types.HostAddress `asn1:"optional,explicit,tag:5"`
}
// NewKRBPriv returns a new KRBPriv type.
func NewKRBPriv(part EncKrbPrivPart) KRBPriv {
return KRBPriv{
PVNO: iana.PVNO,
MsgType: msgtype.KRB_PRIV,
DecryptedEncPart: part,
}
}
// Unmarshal bytes b into the KRBPriv struct.
func (k *KRBPriv) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBPriv))
if err != nil {
return processUnmarshalReplyError(b, err)
}
expectedMsgType := msgtype.KRB_PRIV
if k.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_PRIV. Expected: %v; Actual: %v", expectedMsgType, k.MsgType)
}
return nil
}
// Unmarshal bytes b into the EncKrbPrivPart struct.
func (k *EncKrbPrivPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, k, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.EncKrbPrivPart))
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "KRB_PRIV unmarshal error")
}
return nil
}
// Marshal the KRBPriv.
func (k *KRBPriv) Marshal() ([]byte, error) {
tk := KRBPriv{
PVNO: k.PVNO,
MsgType: k.MsgType,
EncPart: k.EncPart,
}
b, err := asn1.Marshal(tk)
if err != nil {
return []byte{}, err
}
b = asn1tools.AddASNAppTag(b, asnAppTag.KRBPriv)
return b, nil
}
// EncryptEncPart encrypts the DecryptedEncPart within the KRBPriv.
// Use to prepare for marshaling.
func (k *KRBPriv) EncryptEncPart(key types.EncryptionKey) error {
b, err := asn1.Marshal(k.DecryptedEncPart)
if err != nil {
return err
}
b = asn1tools.AddASNAppTag(b, asnAppTag.EncKrbPrivPart)
k.EncPart, err = crypto.GetEncryptedData(b, key, keyusage.KRB_PRIV_ENCPART, 1)
if err != nil {
return err
}
return nil
}
// DecryptEncPart decrypts the encrypted part of the KRBPriv message.
func (k *KRBPriv) DecryptEncPart(key types.EncryptionKey) error {
b, err := crypto.DecryptEncPart(k.EncPart, key, keyusage.KRB_PRIV_ENCPART)
if err != nil {
return fmt.Errorf("error decrypting KRBPriv EncPart: %v", err)
}
err = k.DecryptedEncPart.Unmarshal(b)
if err != nil {
return fmt.Errorf("error unmarshaling encrypted part: %v", err)
}
return nil
}

View File

@ -0,0 +1,43 @@
package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
// KRBSafe implements RFC 4120 KRB_SAFE: https://tools.ietf.org/html/rfc4120#section-5.6.1.
type KRBSafe struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
SafeBody KRBSafeBody `asn1:"explicit,tag:2"`
Cksum types.Checksum `asn1:"explicit,tag:3"`
}
// KRBSafeBody implements the KRB_SAFE_BODY of KRB_SAFE.
type KRBSafeBody struct {
UserData []byte `asn1:"explicit,tag:0"`
Timestamp time.Time `asn1:"generalized,optional,explicit,tag:1"`
Usec int `asn1:"optional,explicit,tag:2"`
SequenceNumber int64 `asn1:"optional,explicit,tag:3"`
SAddress types.HostAddress `asn1:"explicit,tag:4"`
RAddress types.HostAddress `asn1:"optional,explicit,tag:5"`
}
// Unmarshal bytes b into the KRBSafe struct.
func (s *KRBSafe) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, s, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.KRBSafe))
if err != nil {
return processUnmarshalReplyError(b, err)
}
expectedMsgType := msgtype.KRB_SAFE
if s.MsgType != expectedMsgType {
return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a KRB_SAFE. Expected: %v; Actual: %v", expectedMsgType, s.MsgType)
}
return nil
}

View File

@ -0,0 +1,265 @@
package messages
import (
"crypto/rand"
"fmt"
"log"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/asn1tools"
"github.com/jcmturner/gokrb5/v8/crypto"
"github.com/jcmturner/gokrb5/v8/iana"
"github.com/jcmturner/gokrb5/v8/iana/adtype"
"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
"github.com/jcmturner/gokrb5/v8/iana/errorcode"
"github.com/jcmturner/gokrb5/v8/iana/flags"
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
"github.com/jcmturner/gokrb5/v8/keytab"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/pac"
"github.com/jcmturner/gokrb5/v8/types"
)
// Reference: https://www.ietf.org/rfc/rfc4120.txt
// Section: 5.3
// Ticket implements the Kerberos ticket.
type Ticket struct {
TktVNO int `asn1:"explicit,tag:0"`
Realm string `asn1:"generalstring,explicit,tag:1"`
SName types.PrincipalName `asn1:"explicit,tag:2"`
EncPart types.EncryptedData `asn1:"explicit,tag:3"`
DecryptedEncPart EncTicketPart `asn1:"optional"` // Not part of ASN1 bytes so marked as optional so unmarshalling works
}
// EncTicketPart is the encrypted part of the Ticket.
type EncTicketPart struct {
Flags asn1.BitString `asn1:"explicit,tag:0"`
Key types.EncryptionKey `asn1:"explicit,tag:1"`
CRealm string `asn1:"generalstring,explicit,tag:2"`
CName types.PrincipalName `asn1:"explicit,tag:3"`
Transited TransitedEncoding `asn1:"explicit,tag:4"`
AuthTime time.Time `asn1:"generalized,explicit,tag:5"`
StartTime time.Time `asn1:"generalized,explicit,optional,tag:6"`
EndTime time.Time `asn1:"generalized,explicit,tag:7"`
RenewTill time.Time `asn1:"generalized,explicit,optional,tag:8"`
CAddr types.HostAddresses `asn1:"explicit,optional,tag:9"`
AuthorizationData types.AuthorizationData `asn1:"explicit,optional,tag:10"`
}
// TransitedEncoding part of the ticket's encrypted part.
type TransitedEncoding struct {
TRType int32 `asn1:"explicit,tag:0"`
Contents []byte `asn1:"explicit,tag:1"`
}
// NewTicket creates a new Ticket instance.
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) {
etype, err := crypto.GetEtype(eTypeID)
if err != nil {
return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting etype for new ticket")
}
ks := etype.GetKeyByteSize()
kv := make([]byte, ks, ks)
rand.Read(kv)
sessionKey := types.EncryptionKey{
KeyType: eTypeID,
KeyValue: kv,
}
etp := EncTicketPart{
Flags: flags,
Key: sessionKey,
CRealm: crealm,
CName: cname,
Transited: TransitedEncoding{},
AuthTime: authTime,
StartTime: startTime,
EndTime: endTime,
RenewTill: renewTill,
}
b, err := asn1.Marshal(etp)
if err != nil {
return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncodingError, "error marshalling ticket encpart")
}
b = asn1tools.AddASNAppTag(b, asnAppTag.EncTicketPart)
skey, _, err := sktab.GetEncryptionKey(sname, srealm, kvno, eTypeID)
if err != nil {
return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error getting encryption key for new ticket")
}
ed, err := crypto.GetEncryptedData(b, skey, keyusage.KDC_REP_TICKET, kvno)
if err != nil {
return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error encrypting ticket encpart")
}
tkt := Ticket{
TktVNO: iana.PVNO,
Realm: srealm,
SName: sname,
EncPart: ed,
}
return tkt, sessionKey, nil
}
// Unmarshal bytes b into a Ticket struct.
func (t *Ticket) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.Ticket))
return err
}
// Marshal the Ticket.
func (t *Ticket) Marshal() ([]byte, error) {
b, err := asn1.Marshal(*t)
if err != nil {
return nil, err
}
b = asn1tools.AddASNAppTag(b, asnAppTag.Ticket)
return b, nil
}
// Unmarshal bytes b into the EncTicketPart struct.
func (t *EncTicketPart) Unmarshal(b []byte) error {
_, err := asn1.UnmarshalWithParams(b, t, fmt.Sprintf("application,explicit,tag:%d", asnAppTag.EncTicketPart))
return err
}
// unmarshalTicket returns a ticket from the bytes provided.
func unmarshalTicket(b []byte) (t Ticket, err error) {
err = t.Unmarshal(b)
return
}
// UnmarshalTicketsSequence returns a slice of Tickets from a raw ASN1 value.
func unmarshalTicketsSequence(in asn1.RawValue) ([]Ticket, error) {
//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
//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.
b := in.Bytes
// 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
p := 1 + asn1tools.GetNumberBytesInLengthHeader(in.Bytes)
var tkts []Ticket
var raw asn1.RawValue
for p < (len(b)) {
_, err := asn1.UnmarshalWithParams(b[p:], &raw, fmt.Sprintf("application,tag:%d", asnAppTag.Ticket))
if err != nil {
return nil, fmt.Errorf("unmarshaling sequence of tickets failed getting length of ticket: %v", err)
}
t, err := unmarshalTicket(b[p:])
if err != nil {
return nil, fmt.Errorf("unmarshaling sequence of tickets failed: %v", err)
}
p += len(raw.FullBytes)
tkts = append(tkts, t)
}
MarshalTicketSequence(tkts)
return tkts, nil
}
// MarshalTicketSequence marshals a slice of Tickets returning an ASN1 raw value containing the ticket sequence.
func MarshalTicketSequence(tkts []Ticket) (asn1.RawValue, error) {
raw := asn1.RawValue{
Class: 2,
IsCompound: true,
}
if len(tkts) < 1 {
// There are no tickets to marshal
return raw, nil
}
var btkts []byte
for i, t := range tkts {
b, err := t.Marshal()
if err != nil {
return raw, fmt.Errorf("error marshaling ticket number %d in sequence of tickets", i+1)
}
btkts = append(btkts, b...)
}
// The ASN1 wrapping consists of 2 bytes:
// 1st byte -> Identifier Octet - In this case an OCTET STRING (ASN TAG
// 2nd byte -> The length (this will be the size indicated in the input bytes + 2 for the additional bytes we add here.
// Application Tag:
//| Byte: | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
//| Value: | 0 | 1 | 1 | From the RFC spec 4120 |
//| Explanation | Defined by the ASN1 encoding rules for an application tag | A value of 1 indicates a constructed type | The ASN Application tag value |
btkts = append(asn1tools.MarshalLengthBytes(len(btkts)), btkts...)
btkts = append([]byte{byte(32 + asn1.TagSequence)}, btkts...)
raw.Bytes = btkts
// If we need to create the full bytes then identifier octet is "context-specific" = 128 + "constructed" + 32 + the wrapping explicit tag (11)
//fmt.Fprintf(os.Stderr, "mRaw fb: %v\n", raw.FullBytes)
return raw, nil
}
// DecryptEncPart decrypts the encrypted part of the ticket.
// The sname argument can be used to specify which service principal's key should be used to decrypt the ticket.
// If nil is passed as the sname then the service principal specified within the ticket it used.
func (t *Ticket) DecryptEncPart(keytab *keytab.Keytab, sname *types.PrincipalName) error {
if sname == nil {
sname = &t.SName
}
key, _, err := keytab.GetEncryptionKey(*sname, t.Realm, t.EncPart.KVNO, t.EncPart.EType)
if err != nil {
return NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err))
}
return t.Decrypt(key)
}
// Decrypt decrypts the encrypted part of the ticket using the key provided.
func (t *Ticket) Decrypt(key types.EncryptionKey) error {
b, err := crypto.DecryptEncPart(t.EncPart, key, keyusage.KDC_REP_TICKET)
if err != nil {
return fmt.Errorf("error decrypting Ticket EncPart: %v", err)
}
var denc EncTicketPart
err = denc.Unmarshal(b)
if err != nil {
return fmt.Errorf("error unmarshaling encrypted part: %v", err)
}
t.DecryptedEncPart = denc
return nil
}
// GetPACType returns a Microsoft PAC that has been extracted from the ticket and processed.
func (t *Ticket) GetPACType(keytab *keytab.Keytab, sname *types.PrincipalName, l *log.Logger) (bool, pac.PACType, error) {
var isPAC bool
for _, ad := range t.DecryptedEncPart.AuthorizationData {
if ad.ADType == adtype.ADIfRelevant {
var ad2 types.AuthorizationData
err := ad2.Unmarshal(ad.ADData)
if err != nil {
l.Printf("PAC authorization data could not be unmarshaled: %v", err)
continue
}
if ad2[0].ADType == adtype.ADWin2KPAC {
isPAC = true
var p pac.PACType
err = p.Unmarshal(ad2[0].ADData)
if err != nil {
return isPAC, p, fmt.Errorf("error unmarshaling PAC: %v", err)
}
if sname == nil {
sname = &t.SName
}
key, _, err := keytab.GetEncryptionKey(*sname, t.Realm, t.EncPart.KVNO, t.EncPart.EType)
if err != nil {
return isPAC, p, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_NOKEY, fmt.Sprintf("Could not get key from keytab: %v", err))
}
err = p.ProcessPACInfoBuffers(key, l)
return isPAC, p, err
}
}
}
return isPAC, pac.PACType{}, nil
}
// Valid checks it the ticket is currently valid. Max duration passed endtime passed in as argument.
func (t *Ticket) Valid(d time.Duration) (bool, error) {
// Check for future tickets or invalid tickets
time := time.Now().UTC()
if t.DecryptedEncPart.StartTime.Sub(time) > d || types.IsFlagSet(&t.DecryptedEncPart.Flags, flags.Invalid) {
return false, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_TKT_NYV, "service ticket provided is not yet valid")
}
// Check for expired ticket
if time.Sub(t.DecryptedEncPart.EndTime) > d {
return false, NewKRBError(t.SName, t.Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "service ticket provided has expired")
}
return true, nil
}