forked from facebookincubator/tacquito
-
Notifications
You must be signed in to change notification settings - Fork 0
/
packet.go
221 lines (196 loc) · 5.91 KB
/
packet.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*
Copyright (c) Facebook, Inc. and its affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
package tacquito
import (
"fmt"
"unicode"
)
const (
// MaxBodyLength is the total length of the packet body (not including the header).
// Implementations MUST allow control over maximum packet sizes
// accepted by TACACS+ Servers. The recommended maximum packet size
// is 2^(16).
MaxBodyLength uint32 = 65536
)
// EncoderDecoder will encode or decode from wire format, any of the tacacs packet types
type EncoderDecoder interface {
MarshalBinary() ([]byte, error)
UnmarshalBinary(data []byte) error
Fields() map[string]string
}
// Unmarshal will unmarshal tacacs bytes
func Unmarshal(v []byte, t EncoderDecoder) error {
if t == nil {
return fmt.Errorf("unmarshal cannot decode")
}
return t.UnmarshalBinary(v)
}
// PacketOption is used to inject options when creating new Packet types
type PacketOption func(*Packet)
// SetPacketHeader sets the header
func SetPacketHeader(v *Header) PacketOption {
return func(p *Packet) {
p.Header = v
}
}
// SetPacketBody sets the body of packet
func SetPacketBody(v []byte) PacketOption {
return func(p *Packet) {
if p.Header != nil {
p.Header.Length = uint32(len(v))
}
p.Body = v
}
}
// SetPacketBodyUnsafe sets the body of packet by calling MarshalBinary on v.
// errors trigger a panic. this setter is ONLY meant for testing scenarios
// if you use this in production handler code you're asking for panics to kill
// your service.
func SetPacketBodyUnsafe(v EncoderDecoder) PacketOption {
return func(p *Packet) {
b, err := v.MarshalBinary()
if err != nil {
panic(fmt.Errorf("error in SetPacketBodyUnsafe, this should not be used in a service. %v", err))
}
if p.Header != nil {
p.Header.Length = uint32(len(b))
}
p.Body = b
}
}
// NewPacket will create a new Packet based on the provided options.
func NewPacket(opts ...PacketOption) *Packet {
p := &Packet{}
for _, opt := range opts {
opt(p)
}
return p
}
// Packet is used as a request and response packet.
// Header is the decoded header fields from the tacacs packet
// RawBody may be obfuscated or deobfuscated, depending on where the packet is in the req/resp flow
// Body will always be a decoded type, eg AuthenStart, AuthenReply, AcctRequest, etc.
type Packet struct {
// Header is a well known structure, so it's always populated. it's also the only
// part of a tacacs message that isn't crypted, so it can be freely read.
Header *Header
// Body may be crypted or uncrypted bytes of the body, length indicated in the header.Length
Body []byte
}
// MarshalBinary encodes Packet into tacacs bytes. It is unaware of crypt.
// RawBody must have valid values
func (p *Packet) MarshalBinary() ([]byte, error) {
if p.Header == nil {
return nil, fmt.Errorf("header is nil, cannot MarshalBinary")
}
if p.Body == nil {
return nil, fmt.Errorf("body is nil, cannot MarshalBinary")
}
if p.Header.Length > MaxBodyLength {
return nil, fmt.Errorf("indicated size is too large to marshal; max allowed [%v] reported [%v]", MaxBodyLength, p.Header.Length)
}
head, err := p.Header.MarshalBinary()
if err != nil {
return nil, err
}
buf := make([]byte, 0, len(head)+len(p.Body))
buf = append(buf, head...)
buf = append(buf, p.Body...)
return buf, nil
}
// UnmarshalBinary decodes Packet into tacacs bytes. It is unaware of crypt.
func (p *Packet) UnmarshalBinary(v []byte) error {
if v == nil {
return fmt.Errorf("cannot unmarshal a nil slice")
}
// Unmarshal failure will lead to the connection being closed
var err error
var h Header
err = Unmarshal(v[:MaxHeaderLength], &h)
if err != nil {
return fmt.Errorf("failed header unmarshal: [%w]", err)
}
p.Header = &h
if h.Length > MaxBodyLength {
return fmt.Errorf("indicated size is too large to unmarshal; max allowed [%v] reported [%v]", MaxBodyLength, h.Length)
}
p.Body = v[MaxHeaderLength : MaxHeaderLength+int(h.Length)]
return nil
}
// Fields returns fields from this packet compatible with a structured logger
func (p *Packet) Fields() map[string]string {
return nil
}
// Field is a tacacs field interface used across all three AAA types.
type Field interface {
// Validate is executed on all MarshalBinary and UnmarshalBinary operations on
// Authenticate, Authorize and Accounting Packet types
Validate(condition interface{}) error
// Len of Field value
Len() int
// String representation for printing. Obscure operations also happen here
String() string
}
// readBuffer is used during the UnmarshallBinary operation.
// each call to any of readBuffer's methods will mutate the contents
// of b
type readBuffer []byte
// int returns a single byte as an int
func (b *readBuffer) int() int {
return int(b.byte())
}
// byte returns one byte from b
func (b *readBuffer) byte() byte {
s := (*b)
if len(s) < 1 {
return uint8(0)
}
c := s[0]
*b = s[1:]
return c
}
// uint16 extracts a uint16 from b and returns it as an int
// if only one byte is available, we just return that value
func (b *readBuffer) uint16() int {
s := (*b)
if len(s) == 1 {
return b.int()
}
if len(s) >= 2 {
n := int(s[0])<<8 | int(s[1])
*b = s[2:]
return n
}
return 0
}
// string will convert the bytes indicated by n to a string
// if n is larger than b, it is reduced to match
func (b *readBuffer) string(n int) string {
s := (*b)
if len(s) < 1 {
return ""
}
if len(s) < n {
n = len(s)
}
str := s[:n]
*b = s[n:]
return string(str)
}
// appendUint16 will append an int to a []byte as a uint16 but shifting bits
func appendUint16(b []byte, i int) []byte {
return append(b, byte(i>>8), byte(i))
}
// isAllASCII will ensure that the given string only uses ascii characters.
// it will return false if it has anything other than ascii, true, if it's safe ascii.
func isAllASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > unicode.MaxASCII {
return false
}
}
return true
}