Scott Baker | 2c1c482 | 2019-10-16 11:02:41 -0700 | [diff] [blame] | 1 | package kadmin |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "encoding/binary" |
| 6 | "errors" |
| 7 | "fmt" |
| 8 | "math" |
| 9 | |
| 10 | "gopkg.in/jcmturner/gokrb5.v7/messages" |
| 11 | "gopkg.in/jcmturner/gokrb5.v7/types" |
| 12 | ) |
| 13 | |
| 14 | const ( |
| 15 | verisonHex = "ff80" |
| 16 | ) |
| 17 | |
| 18 | // Request message for changing password. |
| 19 | type Request struct { |
| 20 | APREQ messages.APReq |
| 21 | KRBPriv messages.KRBPriv |
| 22 | } |
| 23 | |
| 24 | // Reply message for a password change. |
| 25 | type 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. |
| 38 | func (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. |
| 70 | func (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 | |
| 94 | func 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. |
| 104 | func (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 | } |