1package kadmin
2
3import (
4	"bytes"
5	"encoding/binary"
6	"errors"
7	"fmt"
8	"math"
9
10	"github.com/jcmturner/gokrb5/v8/messages"
11	"github.com/jcmturner/gokrb5/v8/types"
12)
13
14const (
15	verisonHex = "ff80"
16)
17
18// Request message for changing password.
19type Request struct {
20	APREQ   messages.APReq
21	KRBPriv messages.KRBPriv
22}
23
24// Reply message for a password change.
25type Reply struct {
26	MessageLength int
27	Version       int
28	APREPLength   int
29	APREP         messages.APRep
30	KRBPriv       messages.KRBPriv
31	KRBError      messages.KRBError
32	IsKRBError    bool
33	ResultCode    uint16
34	Result        string
35}
36
37// Marshal a Request into a byte slice.
38func (m *Request) Marshal() (b []byte, err error) {
39	b = []byte{255, 128} // protocol version number: contains the hex constant 0xff80 (big-endian integer).
40	ab, e := m.APREQ.Marshal()
41	if e != nil {
42		err = fmt.Errorf("error marshaling AP_REQ: %v", e)
43		return
44	}
45	if len(ab) > math.MaxUint16 {
46		err = errors.New("length of AP_REQ greater then max Uint16 size")
47		return
48	}
49	al := make([]byte, 2)
50	binary.BigEndian.PutUint16(al, uint16(len(ab)))
51	b = append(b, al...)
52	b = append(b, ab...)
53	pb, e := m.KRBPriv.Marshal()
54	if e != nil {
55		err = fmt.Errorf("error marshaling KRB_Priv: %v", e)
56		return
57	}
58	b = append(b, pb...)
59	if len(b)+2 > math.MaxUint16 {
60		err = errors.New("length of message greater then max Uint16 size")
61		return
62	}
63	ml := make([]byte, 2)
64	binary.BigEndian.PutUint16(ml, uint16(len(b)+2))
65	b = append(ml, b...)
66	return
67}
68
69// Unmarshal a byte slice into a Reply.
70func (m *Reply) Unmarshal(b []byte) error {
71	m.MessageLength = int(binary.BigEndian.Uint16(b[0:2]))
72	m.Version = int(binary.BigEndian.Uint16(b[2:4]))
73	if m.Version != 1 {
74		return fmt.Errorf("kadmin reply has incorrect protocol version number: %d", m.Version)
75	}
76	m.APREPLength = int(binary.BigEndian.Uint16(b[4:6]))
77	if m.APREPLength != 0 {
78		err := m.APREP.Unmarshal(b[6 : 6+m.APREPLength])
79		if err != nil {
80			return err
81		}
82		err = m.KRBPriv.Unmarshal(b[6+m.APREPLength : m.MessageLength])
83		if err != nil {
84			return err
85		}
86	} else {
87		m.IsKRBError = true
88		m.KRBError.Unmarshal(b[6:m.MessageLength])
89		m.ResultCode, m.Result = parseResponse(m.KRBError.EData)
90	}
91	return nil
92}
93
94func parseResponse(b []byte) (c uint16, s string) {
95	c = binary.BigEndian.Uint16(b[0:2])
96	buf := bytes.NewBuffer(b[2:])
97	m := make([]byte, len(b)-2)
98	binary.Read(buf, binary.BigEndian, &m)
99	s = string(m)
100	return
101}
102
103// Decrypt the encrypted part of the KRBError within the change password Reply.
104func (m *Reply) Decrypt(key types.EncryptionKey) error {
105	if m.IsKRBError {
106		return m.KRBError
107	}
108	err := m.KRBPriv.DecryptEncPart(key)
109	if err != nil {
110		return err
111	}
112	m.ResultCode, m.Result = parseResponse(m.KRBPriv.DecryptedEncPart.UserData)
113	return nil
114}
115