Browse Source

beginning of client password change kadmin

Jonathan Turner 8 years ago
parent
commit
af02e3e5d7
6 changed files with 248 additions and 5 deletions
  1. 21 0
      kadmin/changepasswddata.go
  2. 52 0
      kadmin/message.go
  3. 62 0
      kadmin/passwd.go
  4. 47 3
      messages/KRBPriv.go
  5. 64 0
      messages/KRBPriv_test.go
  6. 2 2
      testdata/test_vectors.go

+ 21 - 0
kadmin/changepasswddata.go

@@ -0,0 +1,21 @@
+package kadmin
+
+import (
+	"github.com/jcmturner/gofork/encoding/asn1"
+	"gopkg.in/jcmturner/gokrb5.v4/types"
+)
+
+type ChangePasswdData struct {
+	NewPasswd []byte              `asn1:"explicit,tag:0"`
+	TargName  types.PrincipalName `asn1:"explicit,optional,tag:1"`
+	TargRealm string              `asn1:"generalstring,optional,explicit,tag:2"`
+}
+
+func (c *ChangePasswdData) Marshal() ([]byte, error) {
+	b, err := asn1.Marshal(*c)
+	if err != nil {
+		return []byte{}, err
+	}
+	//b = asn1tools.AddASNAppTag(b, asnAppTag.)
+	return b, nil
+}

+ 52 - 0
kadmin/message.go

@@ -0,0 +1,52 @@
+package kadmin
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"math"
+
+	"gopkg.in/jcmturner/gokrb5.v4/messages"
+)
+
+const (
+	verisonHex = "0xff80"
+)
+
+type Message struct {
+	APREQ   messages.APReq
+	KRBPriv messages.KRBPriv
+}
+
+func (m *Message) Marshal() (b []byte, err error) {
+	vb, _ := hex.DecodeString(verisonHex)
+	b = append(b, vb...)
+	ab, e := m.APREQ.Marshal()
+	if e != nil {
+		err = fmt.Errorf("error marshaling AP_REQ: %v", e)
+		return
+	}
+	if len(ab) > math.MaxUint16 {
+		err = errors.New("length of AP_REQ greater then max Uint16 size")
+		return
+	}
+	al := make([]byte, 2)
+	binary.BigEndian.PutUint16(al, uint16(len(ab)))
+	b = append(b, al...)
+	b = append(b, ab...)
+	pb, e := m.KRBPriv.Marshal()
+	if e != nil {
+		err = fmt.Errorf("error marshaling KRB_Priv: %v", e)
+		return
+	}
+	b = append(b, pb...)
+	if len(b)+2 > math.MaxUint16 {
+		err = errors.New("length of message greater then max Uint16 size")
+		return
+	}
+	ml := make([]byte, 2)
+	binary.BigEndian.PutUint16(ml, uint16(len(b)+2))
+	b = append(ml, b...)
+	return
+}

+ 62 - 0
kadmin/passwd.go

@@ -0,0 +1,62 @@
+package kadmin
+
+import (
+	"gopkg.in/jcmturner/gokrb5.v4/crypto"
+	"gopkg.in/jcmturner/gokrb5.v4/krberror"
+	"gopkg.in/jcmturner/gokrb5.v4/messages"
+	"gopkg.in/jcmturner/gokrb5.v4/types"
+)
+
+const (
+	spn = "kadmin/changepw@%s"
+)
+
+func ChangePasswdMsg(cname types.PrincipalName, realm, password string, tkt messages.Ticket, sessionKey types.EncryptionKey) (Message, error) {
+	// Create change password data struct and marshal to bytes
+	chgpasswd := ChangePasswdData{
+		NewPasswd: []byte(password),
+		TargName:  cname,
+		TargRealm: realm,
+	}
+	chpwdb, err := chgpasswd.Marshal()
+	if err != nil {
+		return Message{}, krberror.Errorf(err, krberror.KRBMsgError, "error marshaling change passwd data")
+	}
+
+	// Generate authenticator
+	auth, err := types.NewAuthenticator(realm, cname)
+	if err != nil {
+		return Message{}, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
+	}
+	etype, err := crypto.GetEtype(sessionKey.KeyType)
+	if err != nil {
+		return Message{}, krberror.Errorf(err, krberror.KRBMsgError, "error generating subkey etype")
+	}
+	err = auth.GenerateSeqNumberAndSubKey(sessionKey.KeyType, etype.GetKeyByteSize())
+	if err != nil {
+		return Message{}, krberror.Errorf(err, krberror.KRBMsgError, "error generating subkey")
+	}
+
+	// Generate AP_REQ
+	APreq, err := messages.NewAPReq(tkt, sessionKey, auth)
+	if err != nil {
+		return Message{}, err
+	}
+
+	// Form the KRBPriv encpart data
+	//TODO set the SAddress field???
+	kp := messages.EncKrbPrivPart{
+		Timestamp:      auth.CTime,
+		Usec:           auth.Cusec,
+		SequenceNumber: auth.SeqNumber,
+	}
+	_, kp.UserData, err = etype.EncryptData(auth.SubKey.KeyValue, chpwdb)
+	if err != nil {
+		return Message{}, krberror.Errorf(err, krberror.EncryptingError, "error encrypting change passwd data")
+	}
+
+	return Message{
+		APREQ:   APreq,
+		KRBPriv: messages.NewKRBPriv(kp),
+	}, nil
+}

+ 47 - 3
messages/KRBPriv.go

@@ -5,7 +5,11 @@ import (
 	"time"
 
 	"github.com/jcmturner/gofork/encoding/asn1"
+	"gopkg.in/jcmturner/gokrb5.v4/asn1tools"
+	"gopkg.in/jcmturner/gokrb5.v4/crypto"
+	"gopkg.in/jcmturner/gokrb5.v4/iana"
 	"gopkg.in/jcmturner/gokrb5.v4/iana/asnAppTag"
+	"gopkg.in/jcmturner/gokrb5.v4/iana/keyusage"
 	"gopkg.in/jcmturner/gokrb5.v4/iana/msgtype"
 	"gopkg.in/jcmturner/gokrb5.v4/krberror"
 	"gopkg.in/jcmturner/gokrb5.v4/types"
@@ -13,9 +17,10 @@ import (
 
 // 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"`
+	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.
@@ -28,6 +33,15 @@ type EncKrbPrivPart struct {
 	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))
@@ -49,3 +63,33 @@ func (k *EncKrbPrivPart) Unmarshal(b []byte) 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
+}

+ 64 - 0
messages/KRBPriv_test.go

@@ -10,6 +10,7 @@ import (
 	"gopkg.in/jcmturner/gokrb5.v4/iana/addrtype"
 	"gopkg.in/jcmturner/gokrb5.v4/iana/msgtype"
 	"gopkg.in/jcmturner/gokrb5.v4/testdata"
+	"gopkg.in/jcmturner/gokrb5.v4/types"
 )
 
 func TestUnmarshalKRBPriv(t *testing.T) {
@@ -69,3 +70,66 @@ func TestUnmarshalEncPrivPart_optionalsNULL(t *testing.T) {
 	assert.Equal(t, addrtype.IPv4, a.SAddress.AddrType, "SAddress type not as expected")
 	assert.Equal(t, "12d00023", hex.EncodeToString(a.SAddress.Address), "Address not as expected for SAddress")
 }
+
+func TestMarshalKRBPriv(t *testing.T) {
+	var a KRBPriv
+	v := "encode_krb5_priv"
+	b, err := hex.DecodeString(testdata.TestVectors[v])
+	if err != nil {
+		t.Fatalf("Test vector read error of %s: %v", v, err)
+	}
+	err = a.Unmarshal(b)
+	if err != nil {
+		t.Fatalf("Unmarshal error of %s: %v", v, err)
+	}
+	mb, err := a.Marshal()
+	if err != nil {
+		t.Fatalf("error marshaling KRBPriv: %v", err)
+	}
+	assert.Equal(t, b, mb, "marshaled bytes not as expected")
+
+	v = "encode_krb5_enc_priv_part"
+	be, err := hex.DecodeString(testdata.TestVectors[v])
+	if err != nil {
+		t.Fatalf("Test vector read error of %s: %v", v, err)
+	}
+	err = a.DecryptedEncPart.Unmarshal(be)
+	if err != nil {
+		t.Fatalf("Unmarshal error of %s: %v", v, err)
+	}
+	mb, err = a.Marshal()
+	if err != nil {
+		t.Fatalf("error marshaling KRBPriv: %v", err)
+	}
+	assert.Equal(t, b, mb, "marshaled bytes not as expected when it has decrypted encpart")
+}
+
+func TestKRBPriv_EncryptEncPart(t *testing.T) {
+	var a KRBPriv
+	v := "encode_krb5_priv"
+	b, err := hex.DecodeString(testdata.TestVectors[v])
+	if err != nil {
+		t.Fatalf("Test vector read error of %s: %v", v, err)
+	}
+	err = a.Unmarshal(b)
+	if err != nil {
+		t.Fatalf("Unmarshal error of %s: %v", v, err)
+	}
+	v = "encode_krb5_enc_priv_part"
+	b, err = hex.DecodeString(testdata.TestVectors[v])
+	if err != nil {
+		t.Fatalf("Test vector read error of %s: %v", v, err)
+	}
+	err = a.DecryptedEncPart.Unmarshal(b)
+	if err != nil {
+		t.Fatalf("Unmarshal error of %s: %v", v, err)
+	}
+	key := types.EncryptionKey{
+		KeyType:  int32(18),
+		KeyValue: []byte("12345678901234567890123456789012"),
+	}
+	err = a.EncryptEncPart(key)
+	if err != nil {
+		t.Fatalf("error encrypting encpart: %v", err)
+	}
+}

+ 2 - 2
testdata/test_vectors.go

@@ -154,12 +154,12 @@ const (
  }
   USER.GOKRB5 = {
   kdc = 10.80.88.48:88
-  admin_server = 10.80.88.48:749
+  admin_server = 10.80.88.48:464
   default_domain = user.gokrb5
  }
   RES.GOKRB5 = {
   kdc = 10.80.88.49:88
-  admin_server = 10.80.88.49:749
+  admin_server = 10.80.88.49:464
   default_domain = res.gokrb5
  }