| package kadmin |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| "errors" |
| "fmt" |
| "math" |
| |
| "github.com/jcmturner/gokrb5/v8/messages" |
| "github.com/jcmturner/gokrb5/v8/types" |
| ) |
| |
| const ( |
| verisonHex = "ff80" |
| ) |
| |
| // Request message for changing password. |
| type Request struct { |
| APREQ messages.APReq |
| KRBPriv messages.KRBPriv |
| } |
| |
| // Reply message for a password change. |
| type Reply struct { |
| MessageLength int |
| Version int |
| APREPLength int |
| APREP messages.APRep |
| KRBPriv messages.KRBPriv |
| KRBError messages.KRBError |
| IsKRBError bool |
| ResultCode uint16 |
| Result string |
| } |
| |
| // Marshal a Request into a byte slice. |
| func (m *Request) Marshal() (b []byte, err error) { |
| b = []byte{255, 128} // protocol version number: contains the hex constant 0xff80 (big-endian integer). |
| 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 |
| } |
| |
| // Unmarshal a byte slice into a Reply. |
| func (m *Reply) Unmarshal(b []byte) error { |
| m.MessageLength = int(binary.BigEndian.Uint16(b[0:2])) |
| m.Version = int(binary.BigEndian.Uint16(b[2:4])) |
| if m.Version != 1 { |
| return fmt.Errorf("kadmin reply has incorrect protocol version number: %d", m.Version) |
| } |
| m.APREPLength = int(binary.BigEndian.Uint16(b[4:6])) |
| if m.APREPLength != 0 { |
| err := m.APREP.Unmarshal(b[6 : 6+m.APREPLength]) |
| if err != nil { |
| return err |
| } |
| err = m.KRBPriv.Unmarshal(b[6+m.APREPLength : m.MessageLength]) |
| if err != nil { |
| return err |
| } |
| } else { |
| m.IsKRBError = true |
| m.KRBError.Unmarshal(b[6:m.MessageLength]) |
| m.ResultCode, m.Result = parseResponse(m.KRBError.EData) |
| } |
| return nil |
| } |
| |
| func parseResponse(b []byte) (c uint16, s string) { |
| c = binary.BigEndian.Uint16(b[0:2]) |
| buf := bytes.NewBuffer(b[2:]) |
| m := make([]byte, len(b)-2) |
| binary.Read(buf, binary.BigEndian, &m) |
| s = string(m) |
| return |
| } |
| |
| // Decrypt the encrypted part of the KRBError within the change password Reply. |
| func (m *Reply) Decrypt(key types.EncryptionKey) error { |
| if m.IsKRBError { |
| return m.KRBError |
| } |
| err := m.KRBPriv.DecryptEncPart(key) |
| if err != nil { |
| return err |
| } |
| m.ResultCode, m.Result = parseResponse(m.KRBPriv.DecryptedEncPart.UserData) |
| return nil |
| } |