gosnmp
/
v3.go
507 строк · 14.6 Кб
1// Copyright 2012 The GoSNMP Authors. All rights reserved. Use of this
2// source code is governed by a BSD-style license that can be found in the
3// LICENSE file.
4
5// Copyright 2009 The Go Authors. All rights reserved.
6// Use of this source code is governed by a BSD-style
7// license that can be found in the LICENSE file.
8
9package gosnmp
10
11import (
12"bytes"
13"encoding/binary"
14"errors"
15"fmt"
16"runtime"
17)
18
19// SnmpV3MsgFlags contains various message flags to describe Authentication, Privacy, and whether a report PDU must be sent.
20type SnmpV3MsgFlags uint8
21
22// Possible values of SnmpV3MsgFlags
23const (
24NoAuthNoPriv SnmpV3MsgFlags = 0x0 // No authentication, and no privacy
25AuthNoPriv SnmpV3MsgFlags = 0x1 // Authentication and no privacy
26AuthPriv SnmpV3MsgFlags = 0x3 // Authentication and privacy
27Reportable SnmpV3MsgFlags = 0x4 // Report PDU must be sent.
28)
29
30//go:generate stringer -type=SnmpV3MsgFlags
31
32// SnmpV3SecurityModel describes the security model used by a SnmpV3 connection
33type SnmpV3SecurityModel uint8
34
35// UserSecurityModel is the only SnmpV3SecurityModel currently implemented.
36const (
37UserSecurityModel SnmpV3SecurityModel = 3
38)
39
40//go:generate stringer -type=SnmpV3SecurityModel
41
42// SnmpV3SecurityParameters is a generic interface type to contain various implementations of SnmpV3SecurityParameters
43type SnmpV3SecurityParameters interface {
44Log()
45Copy() SnmpV3SecurityParameters
46Description() string
47SafeString() string
48InitPacket(packet *SnmpPacket) error
49InitSecurityKeys() error
50validate(flags SnmpV3MsgFlags) error
51init(log Logger) error
52discoveryRequired() *SnmpPacket
53getDefaultContextEngineID() string
54setSecurityParameters(in SnmpV3SecurityParameters) error
55marshal(flags SnmpV3MsgFlags) ([]byte, error)
56unmarshal(flags SnmpV3MsgFlags, packet []byte, cursor int) (int, error)
57authenticate(packet []byte) error
58isAuthentic(packetBytes []byte, packet *SnmpPacket) (bool, error)
59encryptPacket(scopedPdu []byte) ([]byte, error)
60decryptPacket(packet []byte, cursor int) ([]byte, error)
61getIdentifier() string
62getLogger() Logger
63setLogger(log Logger)
64}
65
66func (x *GoSNMP) validateParametersV3() error {
67// update following code if you implement a new security model
68if x.SecurityModel != UserSecurityModel {
69return errors.New("the SNMPV3 User Security Model is the only SNMPV3 security model currently implemented")
70}
71if x.SecurityParameters == nil {
72return errors.New("SNMPV3 SecurityParameters must be set")
73}
74
75return x.SecurityParameters.validate(x.MsgFlags)
76}
77
78// authenticate the marshalled result of a snmp version 3 packet
79func (packet *SnmpPacket) authenticate(msg []byte) ([]byte, error) {
80defer func() {
81if e := recover(); e != nil {
82var buf = make([]byte, 8192)
83runtime.Stack(buf, true)
84fmt.Printf("[v3::authenticate]recover: %v. Stack=%v\n", e, string(buf))
85}
86}()
87if packet.Version != Version3 {
88return msg, nil
89}
90if packet.MsgFlags&AuthNoPriv > 0 {
91err := packet.SecurityParameters.authenticate(msg)
92if err != nil {
93return nil, err
94}
95}
96
97return msg, nil
98}
99
100func (x *GoSNMP) testAuthentication(packet []byte, result *SnmpPacket, useResponseSecurityParameters bool) error {
101if x.Version != Version3 {
102return fmt.Errorf("testAuthentication called with non Version3 connection")
103}
104msgFlags := x.MsgFlags
105if useResponseSecurityParameters {
106msgFlags = result.MsgFlags
107}
108
109if msgFlags&AuthNoPriv > 0 {
110var authentic bool
111var err error
112if useResponseSecurityParameters {
113authentic, err = result.SecurityParameters.isAuthentic(packet, result)
114} else {
115authentic, err = x.SecurityParameters.isAuthentic(packet, result)
116}
117if err != nil {
118return err
119}
120if !authentic {
121return fmt.Errorf("incoming packet is not authentic, discarding")
122}
123}
124
125return nil
126}
127
128func (x *GoSNMP) initPacket(packetOut *SnmpPacket) error {
129if x.MsgFlags&AuthPriv > AuthNoPriv {
130return x.SecurityParameters.InitPacket(packetOut)
131}
132
133return nil
134}
135
136// http://tools.ietf.org/html/rfc2574#section-2.2.3 This code does not
137// check if the last message received was more than 150 seconds ago The
138// snmpds that this code was tested on emit an 'out of time window'
139// error with the new time and this code will retransmit when that is
140// received.
141func (x *GoSNMP) negotiateInitialSecurityParameters(packetOut *SnmpPacket) error {
142if x.Version != Version3 || packetOut.Version != Version3 {
143return fmt.Errorf("negotiateInitialSecurityParameters called with non Version3 connection or packet")
144}
145
146if x.SecurityModel != packetOut.SecurityModel {
147return fmt.Errorf("connection security model does not match security model defined in packet")
148}
149
150if discoveryPacket := packetOut.SecurityParameters.discoveryRequired(); discoveryPacket != nil {
151discoveryPacket.ContextName = x.ContextName
152result, err := x.sendOneRequest(discoveryPacket, true)
153
154if err != nil {
155return err
156}
157
158err = x.storeSecurityParameters(result)
159if err != nil {
160return err
161}
162
163err = x.updatePktSecurityParameters(packetOut)
164if err != nil {
165return err
166}
167} else {
168err := packetOut.SecurityParameters.InitSecurityKeys()
169if err == nil {
170return err
171}
172}
173
174return nil
175}
176
177// save the connection security parameters after a request/response
178func (x *GoSNMP) storeSecurityParameters(result *SnmpPacket) error {
179if x.Version != Version3 || result.Version != Version3 {
180return fmt.Errorf("storeParameters called with non Version3 connection or packet")
181}
182
183if x.SecurityModel != result.SecurityModel {
184return fmt.Errorf("connection security model does not match security model extracted from packet")
185}
186
187if x.ContextEngineID == "" {
188x.ContextEngineID = result.SecurityParameters.getDefaultContextEngineID()
189}
190
191return x.SecurityParameters.setSecurityParameters(result.SecurityParameters)
192}
193
194// update packet security parameters to match connection security parameters
195func (x *GoSNMP) updatePktSecurityParameters(packetOut *SnmpPacket) error {
196if x.Version != Version3 || packetOut.Version != Version3 {
197return fmt.Errorf("updatePktSecurityParameters called with non Version3 connection or packet")
198}
199
200if x.SecurityModel != packetOut.SecurityModel {
201return fmt.Errorf("connection security model does not match security model extracted from packet")
202}
203
204err := packetOut.SecurityParameters.setSecurityParameters(x.SecurityParameters)
205if err != nil {
206return err
207}
208
209if packetOut.ContextEngineID == "" {
210packetOut.ContextEngineID = x.ContextEngineID
211}
212
213return nil
214}
215
216func (packet *SnmpPacket) marshalV3(buf *bytes.Buffer) (*bytes.Buffer, error) { //nolint:interfacer
217emptyBuffer := new(bytes.Buffer) // used when returning errors
218
219header, err := packet.marshalV3Header()
220if err != nil {
221return emptyBuffer, err
222}
223buf.Write([]byte{byte(Sequence), byte(len(header))})
224packet.Logger.Printf("Marshal V3 Header len=%d. Eaten Last 4 Bytes=%v", len(header), header[len(header)-4:])
225buf.Write(header)
226
227var securityParameters []byte
228securityParameters, err = packet.SecurityParameters.marshal(packet.MsgFlags)
229if err != nil {
230return emptyBuffer, err
231}
232packet.Logger.Printf("Marshal V3 SecurityParameters len=%d. Eaten Last 4 Bytes=%v",
233len(securityParameters), securityParameters[len(securityParameters)-4:])
234
235buf.Write([]byte{byte(OctetString)})
236secParamLen, err := marshalLength(len(securityParameters))
237if err != nil {
238return emptyBuffer, err
239}
240buf.Write(secParamLen)
241buf.Write(securityParameters)
242
243scopedPdu, err := packet.marshalV3ScopedPDU()
244if err != nil {
245return emptyBuffer, err
246}
247buf.Write(scopedPdu)
248return buf, nil
249}
250
251// marshal a snmp version 3 packet header
252func (packet *SnmpPacket) marshalV3Header() ([]byte, error) {
253buf := new(bytes.Buffer)
254
255// msg id
256buf.Write([]byte{byte(Integer), 4})
257err := binary.Write(buf, binary.BigEndian, packet.MsgID)
258if err != nil {
259return nil, err
260}
261oldLen := 0
262packet.Logger.Printf("MarshalV3Header msgID len=%v", buf.Len()-oldLen)
263oldLen = buf.Len()
264// maximum response msg size
265var maxBufSize uint32 = rxBufSize
266if packet.MsgMaxSize != 0 {
267maxBufSize = packet.MsgMaxSize
268}
269maxmsgsize, err := marshalUint32(maxBufSize)
270if err != nil {
271return nil, err
272}
273buf.Write([]byte{byte(Integer), byte(len(maxmsgsize))})
274buf.Write(maxmsgsize)
275packet.Logger.Printf("MarshalV3Header maxmsgsize len=%v", buf.Len()-oldLen)
276oldLen = buf.Len()
277
278// msg flags
279buf.Write([]byte{byte(OctetString), 1, byte(packet.MsgFlags)})
280
281packet.Logger.Printf("MarshalV3Header msg flags len=%v", buf.Len()-oldLen)
282oldLen = buf.Len()
283
284// msg security model
285buf.Write([]byte{byte(Integer), 1, byte(packet.SecurityModel)})
286
287packet.Logger.Printf("MarshalV3Header msg security model len=%v", buf.Len()-oldLen)
288
289return buf.Bytes(), nil
290}
291
292// marshal and encrypt (if necessary) a snmp version 3 Scoped PDU
293func (packet *SnmpPacket) marshalV3ScopedPDU() ([]byte, error) {
294var b []byte
295
296scopedPdu, err := packet.prepareV3ScopedPDU()
297if err != nil {
298return nil, err
299}
300pduLen, err := marshalLength(len(scopedPdu))
301if err != nil {
302return nil, err
303}
304b = append([]byte{byte(Sequence)}, pduLen...)
305scopedPdu = append(b, scopedPdu...)
306if packet.MsgFlags&AuthPriv > AuthNoPriv {
307scopedPdu, err = packet.SecurityParameters.encryptPacket(scopedPdu)
308if err != nil {
309return nil, err
310}
311}
312
313return scopedPdu, nil
314}
315
316// prepare the plain text of a snmp version 3 Scoped PDU
317func (packet *SnmpPacket) prepareV3ScopedPDU() ([]byte, error) {
318var buf bytes.Buffer
319
320// ContextEngineID
321idlen, err := marshalLength(len(packet.ContextEngineID))
322if err != nil {
323return nil, err
324}
325buf.Write(append([]byte{byte(OctetString)}, idlen...))
326buf.WriteString(packet.ContextEngineID)
327
328// ContextName
329namelen, err := marshalLength(len(packet.ContextName))
330if err != nil {
331return nil, err
332}
333buf.Write(append([]byte{byte(OctetString)}, namelen...))
334buf.WriteString(packet.ContextName)
335
336data, err := packet.marshalPDU()
337if err != nil {
338return nil, err
339}
340buf.Write(data)
341return buf.Bytes(), nil
342}
343
344func (x *GoSNMP) unmarshalV3Header(packet []byte,
345cursor int,
346response *SnmpPacket) (int, error) {
347if PDUType(packet[cursor]) != Sequence {
348return 0, fmt.Errorf("invalid SNMPV3 Header")
349}
350
351_, cursorTmp, err := parseLength(packet[cursor:])
352if err != nil {
353return 0, err
354}
355cursor += cursorTmp
356if cursor > len(packet) {
357return 0, errors.New("error parsing SNMPV3 message ID: truncted packet")
358}
359
360rawMsgID, count, err := parseRawField(x.Logger, packet[cursor:], "msgID")
361if err != nil {
362return 0, fmt.Errorf("error parsing SNMPV3 message ID: %w", err)
363}
364cursor += count
365if cursor > len(packet) {
366return 0, errors.New("error parsing SNMPV3 message ID: truncted packet")
367}
368
369if MsgID, ok := rawMsgID.(int); ok {
370response.MsgID = uint32(MsgID)
371x.Logger.Printf("Parsed message ID %d", MsgID)
372}
373
374rawMsgMaxSize, count, err := parseRawField(x.Logger, packet[cursor:], "msgMaxSize")
375if err != nil {
376return 0, fmt.Errorf("error parsing SNMPV3 msgMaxSize: %w", err)
377}
378cursor += count
379if cursor > len(packet) {
380return 0, errors.New("error parsing SNMPV3 message ID: truncted packet")
381}
382
383if MsgMaxSize, ok := rawMsgMaxSize.(int); ok {
384response.MsgMaxSize = uint32(MsgMaxSize)
385x.Logger.Printf("Parsed message max size %d", MsgMaxSize)
386}
387
388rawMsgFlags, count, err := parseRawField(x.Logger, packet[cursor:], "msgFlags")
389if err != nil {
390return 0, fmt.Errorf("error parsing SNMPV3 msgFlags: %w", err)
391}
392cursor += count
393if cursor > len(packet) {
394return 0, errors.New("error parsing SNMPV3 message ID: truncted packet")
395}
396
397if MsgFlags, ok := rawMsgFlags.(string); ok && len(MsgFlags) > 0 {
398response.MsgFlags = SnmpV3MsgFlags(MsgFlags[0])
399x.Logger.Printf("parsed msg flags %s", MsgFlags)
400}
401
402rawSecModel, count, err := parseRawField(x.Logger, packet[cursor:], "msgSecurityModel")
403if err != nil {
404return 0, fmt.Errorf("error parsing SNMPV3 msgSecModel: %w", err)
405}
406cursor += count
407if cursor >= len(packet) {
408return 0, errors.New("error parsing SNMPV3 message ID: truncted packet")
409}
410
411if SecModel, ok := rawSecModel.(int); ok {
412response.SecurityModel = SnmpV3SecurityModel(SecModel)
413x.Logger.Printf("Parsed security model %d", SecModel)
414}
415
416if PDUType(packet[cursor]) != PDUType(OctetString) {
417return 0, errors.New("invalid SNMPV3 Security Parameters")
418}
419_, cursorTmp, err = parseLength(packet[cursor:])
420if err != nil {
421return 0, err
422}
423cursor += cursorTmp
424if cursor > len(packet) {
425return 0, errors.New("error parsing SNMPV3 message ID: truncted packet")
426}
427if response.SecurityParameters == nil {
428response.SecurityParameters = &UsmSecurityParameters{Logger: x.Logger}
429}
430
431cursor, err = response.SecurityParameters.unmarshal(response.MsgFlags, packet, cursor)
432if err != nil {
433return 0, err
434}
435x.Logger.Printf("Parsed Security Parameters. now offset=%v,", cursor)
436
437return cursor, nil
438}
439
440func (x *GoSNMP) decryptPacket(packet []byte, cursor int, response *SnmpPacket) ([]byte, int, error) {
441var err error
442var decrypted = false
443
444if cursor >= len(packet) {
445return nil, 0, errors.New("error parsing SNMPV3: truncated packet")
446}
447
448switch PDUType(packet[cursor]) {
449case PDUType(OctetString):
450// pdu is encrypted
451packet, err = response.SecurityParameters.decryptPacket(packet, cursor)
452if err != nil {
453return nil, 0, err
454}
455decrypted = true
456fallthrough
457case Sequence:
458// pdu is plaintext or has been decrypted
459tlength, cursorTmp, err := parseLength(packet[cursor:])
460if err != nil {
461return nil, 0, err
462}
463if decrypted {
464// truncate padding that might have been included with
465// the encrypted PDU
466if cursor+tlength > len(packet) {
467return nil, 0, errors.New("error parsing SNMPV3: truncated packet")
468}
469packet = packet[:cursor+tlength]
470}
471cursor += cursorTmp
472if cursor > len(packet) {
473return nil, 0, errors.New("error parsing SNMPV3: truncated packet")
474}
475
476rawContextEngineID, count, err := parseRawField(x.Logger, packet[cursor:], "contextEngineID")
477if err != nil {
478return nil, 0, fmt.Errorf("error parsing SNMPV3 contextEngineID: %w", err)
479}
480cursor += count
481if cursor > len(packet) {
482return nil, 0, errors.New("error parsing SNMPV3: truncated packet")
483}
484
485if contextEngineID, ok := rawContextEngineID.(string); ok {
486response.ContextEngineID = contextEngineID
487x.Logger.Printf("Parsed contextEngineID %s", contextEngineID)
488}
489rawContextName, count, err := parseRawField(x.Logger, packet[cursor:], "contextName")
490if err != nil {
491return nil, 0, fmt.Errorf("error parsing SNMPV3 contextName: %w", err)
492}
493cursor += count
494if cursor > len(packet) {
495return nil, 0, errors.New("error parsing SNMPV3: truncated packet")
496}
497
498if contextName, ok := rawContextName.(string); ok {
499response.ContextName = contextName
500x.Logger.Printf("Parsed contextName %s", contextName)
501}
502
503default:
504return nil, 0, errors.New("error parsing SNMPV3 scoped PDU")
505}
506return packet, cursor, nil
507}
508