diff --git a/v8/client/network.go b/v8/client/network.go index 9ca0e37a..009f6f15 100644 --- a/v8/client/network.go +++ b/v8/client/network.go @@ -1,7 +1,6 @@ package client import ( - "bytes" "encoding/binary" "errors" "fmt" @@ -173,14 +172,11 @@ func (cl *Client) sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) { defer conn.Close() var r []byte // RFC 4120 7.2.2 specifies the first 4 bytes indicate the length of the message in big endian order. - var buf bytes.Buffer - err := binary.Write(&buf, binary.BigEndian, uint32(len(b))) - if err != nil { - return r, err - } - b = append(buf.Bytes(), b...) + hb := make([]byte, 4, 4) + binary.BigEndian.PutUint32(hb, uint32(len(b))) + b = append(hb, b...) - _, err = conn.Write(b) + _, err := conn.Write(b) if err != nil { return r, fmt.Errorf("error sending to KDC (%s): %v", conn.RemoteAddr().String(), err) } diff --git a/v8/messages/KDCRep.go b/v8/messages/KDCRep.go index 41d2b2ca..76a33167 100644 --- a/v8/messages/KDCRep.go +++ b/v8/messages/KDCRep.go @@ -8,6 +8,7 @@ import ( "time" "github.com/jcmturner/gofork/encoding/asn1" + "github.com/jcmturner/gokrb5/v8/asn1tools" "github.com/jcmturner/gokrb5/v8/config" "github.com/jcmturner/gokrb5/v8/credentials" "github.com/jcmturner/gokrb5/v8/crypto" @@ -103,6 +104,34 @@ func (k *ASRep) Unmarshal(b []byte) error { return nil } +// Marshal ASRep struct. +func (k *ASRep) Marshal() ([]byte, error) { + m := marshalKDCRep{ + PVNO: k.PVNO, + MsgType: k.MsgType, + PAData: k.PAData, + CRealm: k.CRealm, + CName: k.CName, + EncPart: k.EncPart, + } + b, err := k.Ticket.Marshal() + if err != nil { + return []byte{}, err + } + m.Ticket = asn1.RawValue{ + Class: asn1.ClassContextSpecific, + IsCompound: true, + Tag: 5, + Bytes: b, + } + mk, err := asn1.Marshal(m) + if err != nil { + return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REP") + } + mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREP) + return mk, nil +} + // Unmarshal bytes b into the TGSRep struct. func (k *TGSRep) Unmarshal(b []byte) error { var m marshalKDCRep @@ -130,6 +159,34 @@ func (k *TGSRep) Unmarshal(b []byte) error { return nil } +// Marshal TGSRep struct. +func (k *TGSRep) Marshal() ([]byte, error) { + m := marshalKDCRep{ + PVNO: k.PVNO, + MsgType: k.MsgType, + PAData: k.PAData, + CRealm: k.CRealm, + CName: k.CName, + EncPart: k.EncPart, + } + b, err := k.Ticket.Marshal() + if err != nil { + return []byte{}, err + } + m.Ticket = asn1.RawValue{ + Class: asn1.ClassContextSpecific, + IsCompound: true, + Tag: 5, + Bytes: b, + } + mk, err := asn1.Marshal(m) + if err != nil { + return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REP") + } + mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREP) + return mk, 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)) @@ -145,6 +202,16 @@ func (e *EncKDCRepPart) Unmarshal(b []byte) error { return nil } +// Marshal encrypted part of KRB_KDC_REP. +func (e *EncKDCRepPart) Marshal() ([]byte, error) { + b, err := asn1.Marshal(*e) + if err != nil { + return b, krberror.Errorf(err, krberror.EncodingError, "marshaling error of AS_REP encpart") + } + b = asn1tools.AddASNAppTag(b, asnAppTag.EncASRepPart) + return b, nil +} + // DecryptEncPart decrypts the encrypted part of an AS_REP. func (k *ASRep) DecryptEncPart(c *credentials.Credentials) (types.EncryptionKey, error) { var key types.EncryptionKey @@ -201,6 +268,7 @@ func (k *ASRep) Verify(cfg *config.Config, creds *credentials.Credentials, asReq 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) } + //TODO is there something wrong here...> 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) diff --git a/v8/messages/KDCRep_test.go b/v8/messages/KDCRep_test.go index 2224d215..34371720 100644 --- a/v8/messages/KDCRep_test.go +++ b/v8/messages/KDCRep_test.go @@ -91,6 +91,24 @@ func TestUnmarshalASRep_optionalsNULL(t *testing.T) { assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected") } +func TestMarshalASRep(t *testing.T) { + t.Parallel() + var a ASRep + b, err := hex.DecodeString(testdata.MarshaledKRB5as_rep) + if err != nil { + t.Fatalf("Test vector read error: %v", err) + } + err = a.Unmarshal(b) + if err != nil { + t.Fatalf("Unmarshal error: %v", err) + } + mb, err := a.Marshal() + if err != nil { + t.Fatalf("Marshal errored: %v", err) + } + assert.Equal(t, b, mb, "Marshal bytes of ASRep not as expected") +} + func TestUnmarshalTGSRep(t *testing.T) { t.Parallel() var a TGSRep @@ -157,6 +175,24 @@ func TestUnmarshalTGSRep_optionalsNULL(t *testing.T) { assert.Equal(t, testdata.TEST_CIPHERTEXT, string(a.EncPart.Cipher), "Ticket encrypted part cipher not as expected") } +func TestMarshalTGSRep(t *testing.T) { + t.Parallel() + var a TGSRep + b, err := hex.DecodeString(testdata.MarshaledKRB5tgs_rep) + if err != nil { + t.Fatalf("Test vector read error: %v", err) + } + err = a.Unmarshal(b) + if err != nil { + t.Fatalf("Unmarshal error: %v", err) + } + mb, err := a.Marshal() + if err != nil { + t.Fatalf("Marshal errored: %v", err) + } + assert.Equal(t, b, mb, "Marshal bytes of TGSRep not as expected") +} + func TestUnmarshalEncKDCRepPart(t *testing.T) { t.Parallel() var a EncKDCRepPart diff --git a/v8/messages/KRBError.go b/v8/messages/KRBError.go index 4c88949d..d2cf32d6 100644 --- a/v8/messages/KRBError.go +++ b/v8/messages/KRBError.go @@ -6,6 +6,7 @@ import ( "time" "github.com/jcmturner/gofork/encoding/asn1" + "github.com/jcmturner/gokrb5/v8/asn1tools" "github.com/jcmturner/gokrb5/v8/iana" "github.com/jcmturner/gokrb5/v8/iana/asnAppTag" "github.com/jcmturner/gokrb5/v8/iana/errorcode" @@ -59,6 +60,16 @@ func (k *KRBError) Unmarshal(b []byte) error { return nil } +// Marshal a KRBError into bytes. +func (k *KRBError) Marshal() ([]byte, error) { + b, err := asn1.Marshal(*k) + if err != nil { + return b, krberror.Errorf(err, krberror.EncodingError, "error marshaling KRBError") + } + b = asn1tools.AddASNAppTag(b, asnAppTag.KRBError) + return b, nil +} + // Error method implementing error interface on KRBError struct. func (k KRBError) Error() string { etxt := fmt.Sprintf("KRB Error: %s", errorcode.Lookup(k.ErrorCode)) diff --git a/v8/messages/KRBError_test.go b/v8/messages/KRBError_test.go index c5a44bab..c88e4b9b 100644 --- a/v8/messages/KRBError_test.go +++ b/v8/messages/KRBError_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestUnmarshalKRBError(t *testing.T) { +func TestUnmarshalMarshalKRBError(t *testing.T) { t.Parallel() var a KRBError b, err := hex.DecodeString(testdata.MarshaledKRB5error) @@ -44,9 +44,15 @@ func TestUnmarshalKRBError(t *testing.T) { assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Ticket SName name string entries not as expected") assert.Equal(t, "krb5data", a.EText, "EText not as expected") assert.Equal(t, []byte("krb5data"), a.EData, "EData not as expected") + + b2, err := a.Marshal() + if err != nil { + t.Errorf("error marshalling KRBError: %v", err) + } + assert.Equal(t, b, b2, "marshalled bytes not as expected") } -func TestUnmarshalKRBError_optionalsNULL(t *testing.T) { +func TestUnmarshalMarshalKRBError_optionalsNULL(t *testing.T) { t.Parallel() var a KRBError b, err := hex.DecodeString(testdata.MarshaledKRB5errorOptionalsNULL) @@ -70,4 +76,10 @@ func TestUnmarshalKRBError_optionalsNULL(t *testing.T) { assert.Equal(t, nametype.KRB_NT_PRINCIPAL, a.SName.NameType, "Ticket SName NameType not as expected") assert.Equal(t, len(testdata.TEST_PRINCIPALNAME_NAMESTRING), len(a.SName.NameString), "Ticket SName does not have the expected number of NameStrings") assert.Equal(t, testdata.TEST_PRINCIPALNAME_NAMESTRING, a.SName.NameString, "Ticket SName name string entries not as expected") + + b2, err := a.Marshal() + if err != nil { + t.Errorf("error marshalling KRBError: %v", err) + } + assert.Equal(t, b, b2, "marshalled bytes not as expected") } diff --git a/v8/messages/Ticket.go b/v8/messages/Ticket.go index 58bc97ed..11efad62 100644 --- a/v8/messages/Ticket.go +++ b/v8/messages/Ticket.go @@ -1,7 +1,6 @@ package messages import ( - "crypto/rand" "fmt" "log" "time" @@ -60,13 +59,11 @@ func NewTicket(cname types.PrincipalName, crealm string, sname types.PrincipalNa 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, + sessionKey, err := types.GenerateEncryptionKey(etype) + if err != nil { + return Ticket{}, types.EncryptionKey{}, krberror.Errorf(err, krberror.EncryptingError, "error generating session key") } + etp := EncTicketPart{ Flags: flags, Key: sessionKey, diff --git a/v8/types/Cryptosystem.go b/v8/types/Cryptosystem.go index 1f62d885..557e5f2f 100644 --- a/v8/types/Cryptosystem.go +++ b/v8/types/Cryptosystem.go @@ -1,7 +1,10 @@ package types import ( + "crypto/rand" + "github.com/jcmturner/gofork/encoding/asn1" + "github.com/jcmturner/gokrb5/v8/crypto/etype" ) // Reference: https://www.ietf.org/rfc/rfc4120.txt @@ -53,3 +56,16 @@ func (a *Checksum) Unmarshal(b []byte) error { _, err := asn1.Unmarshal(b, a) return err } + +func GenerateEncryptionKey(etype etype.EType) (EncryptionKey, error) { + k := EncryptionKey{ + KeyType: etype.GetETypeID(), + } + b := make([]byte, etype.GetKeyByteSize(), etype.GetKeyByteSize()) + _, err := rand.Read(b) + if err != nil { + return k, err + } + k.KeyValue = b + return k, nil +}