gosnmp

Форк
0
/
v3_usm.go 
1068 строк · 30.2 Кб
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

9
package gosnmp
10

11
import (
12
	"bytes"
13
	"crypto"
14
	"crypto/aes"
15
	"crypto/cipher"
16
	"crypto/des" //nolint:gosec
17
	"crypto/hmac"
18
	"crypto/md5" //nolint:gosec
19
	crand "crypto/rand"
20
	"crypto/sha1"     //nolint:gosec
21
	_ "crypto/sha256" // Register hash function #4 (SHA224), #5 (SHA256)
22
	_ "crypto/sha512" // Register hash function #6 (SHA384), #7 (SHA512)
23
	"encoding/binary"
24
	"encoding/hex"
25
	"errors"
26
	"fmt"
27
	"hash"
28
	"strings"
29
	"sync"
30
	"sync/atomic"
31
)
32

33
// SnmpV3AuthProtocol describes the authentication protocol in use by an authenticated SnmpV3 connection.
34
type SnmpV3AuthProtocol uint8
35

36
// NoAuth, MD5, and SHA are implemented
37
const (
38
	NoAuth SnmpV3AuthProtocol = 1
39
	MD5    SnmpV3AuthProtocol = 2
40
	SHA    SnmpV3AuthProtocol = 3
41
	SHA224 SnmpV3AuthProtocol = 4
42
	SHA256 SnmpV3AuthProtocol = 5
43
	SHA384 SnmpV3AuthProtocol = 6
44
	SHA512 SnmpV3AuthProtocol = 7
45
)
46

47
//go:generate stringer -type=SnmpV3AuthProtocol
48

49
// HashType maps the AuthProtocol's hash type to an actual crypto.Hash object.
50
func (authProtocol SnmpV3AuthProtocol) HashType() crypto.Hash {
51
	switch authProtocol {
52
	default:
53
		return crypto.MD5
54
	case SHA:
55
		return crypto.SHA1
56
	case SHA224:
57
		return crypto.SHA224
58
	case SHA256:
59
		return crypto.SHA256
60
	case SHA384:
61
		return crypto.SHA384
62
	case SHA512:
63
		return crypto.SHA512
64
	}
65
}
66

67
//nolint:gochecknoglobals
68
var macVarbinds = [][]byte{
69
	{},
70
	{byte(OctetString), 0},
71
	{byte(OctetString), 12,
72
		0, 0, 0, 0,
73
		0, 0, 0, 0,
74
		0, 0, 0, 0},
75
	{byte(OctetString), 12,
76
		0, 0, 0, 0,
77
		0, 0, 0, 0,
78
		0, 0, 0, 0},
79
	{byte(OctetString), 16,
80
		0, 0, 0, 0,
81
		0, 0, 0, 0,
82
		0, 0, 0, 0,
83
		0, 0, 0, 0},
84
	{byte(OctetString), 24,
85
		0, 0, 0, 0,
86
		0, 0, 0, 0,
87
		0, 0, 0, 0,
88
		0, 0, 0, 0,
89
		0, 0, 0, 0,
90
		0, 0, 0, 0},
91
	{byte(OctetString), 32,
92
		0, 0, 0, 0,
93
		0, 0, 0, 0,
94
		0, 0, 0, 0,
95
		0, 0, 0, 0,
96
		0, 0, 0, 0,
97
		0, 0, 0, 0,
98
		0, 0, 0, 0,
99
		0, 0, 0, 0},
100
	{byte(OctetString), 48,
101
		0, 0, 0, 0,
102
		0, 0, 0, 0,
103
		0, 0, 0, 0,
104
		0, 0, 0, 0,
105
		0, 0, 0, 0,
106
		0, 0, 0, 0,
107
		0, 0, 0, 0,
108
		0, 0, 0, 0,
109
		0, 0, 0, 0,
110
		0, 0, 0, 0,
111
		0, 0, 0, 0,
112
		0, 0, 0, 0}}
113

114
// SnmpV3PrivProtocol is the privacy protocol in use by an private SnmpV3 connection.
115
type SnmpV3PrivProtocol uint8
116

117
// NoPriv, DES implemented, AES planned
118
// Changed: AES192, AES256, AES192C, AES256C added
119
const (
120
	NoPriv  SnmpV3PrivProtocol = 1
121
	DES     SnmpV3PrivProtocol = 2
122
	AES     SnmpV3PrivProtocol = 3
123
	AES192  SnmpV3PrivProtocol = 4 // Blumenthal-AES192
124
	AES256  SnmpV3PrivProtocol = 5 // Blumenthal-AES256
125
	AES192C SnmpV3PrivProtocol = 6 // Reeder-AES192
126
	AES256C SnmpV3PrivProtocol = 7 // Reeder-AES256
127
)
128

129
//go:generate stringer -type=SnmpV3PrivProtocol
130

131
// UsmSecurityParameters is an implementation of SnmpV3SecurityParameters for the UserSecurityModel
132
type UsmSecurityParameters struct {
133
	mu sync.Mutex
134
	// localAESSalt must be 64bit aligned to use with atomic operations.
135
	localAESSalt uint64
136
	localDESSalt uint32
137

138
	AuthoritativeEngineID    string
139
	AuthoritativeEngineBoots uint32
140
	AuthoritativeEngineTime  uint32
141
	UserName                 string
142
	AuthenticationParameters string
143
	PrivacyParameters        []byte
144

145
	AuthenticationProtocol SnmpV3AuthProtocol
146
	PrivacyProtocol        SnmpV3PrivProtocol
147

148
	AuthenticationPassphrase string
149
	PrivacyPassphrase        string
150

151
	SecretKey  []byte
152
	PrivacyKey []byte
153

154
	Logger Logger
155
}
156

157
func (sp *UsmSecurityParameters) getIdentifier() string {
158
	return sp.UserName
159
}
160

161
func (sp *UsmSecurityParameters) getLogger() Logger {
162
	return sp.Logger
163
}
164

165
func (sp *UsmSecurityParameters) setLogger(log Logger) {
166
	sp.Logger = log
167
}
168

169
// Description logs authentication paramater information to the provided GoSNMP Logger
170
func (sp *UsmSecurityParameters) Description() string {
171
	var sb strings.Builder
172
	sb.WriteString("user=")
173
	sb.WriteString(sp.UserName)
174

175
	sb.WriteString(",engine=(")
176
	sb.WriteString(hex.EncodeToString([]byte(sp.AuthoritativeEngineID)))
177
	// sb.WriteString(sp.AuthoritativeEngineID)
178
	sb.WriteString(")")
179

180
	switch sp.AuthenticationProtocol {
181
	case NoAuth:
182
		sb.WriteString(",auth=noauth")
183
	case MD5:
184
		sb.WriteString(",auth=md5")
185
	case SHA:
186
		sb.WriteString(",auth=sha")
187
	case SHA224:
188
		sb.WriteString(",auth=sha224")
189
	case SHA256:
190
		sb.WriteString(",auth=sha256")
191
	case SHA384:
192
		sb.WriteString(",auth=sha384")
193
	case SHA512:
194
		sb.WriteString(",auth=sha512")
195
	}
196
	sb.WriteString(",authPass=")
197
	sb.WriteString(sp.AuthenticationPassphrase)
198

199
	switch sp.PrivacyProtocol {
200
	case NoPriv:
201
		sb.WriteString(",priv=NoPriv")
202
	case DES:
203
		sb.WriteString(",priv=DES")
204
	case AES:
205
		sb.WriteString(",priv=AES")
206
	case AES192:
207
		sb.WriteString(",priv=AES192")
208
	case AES256:
209
		sb.WriteString(",priv=AES256")
210
	case AES192C:
211
		sb.WriteString(",priv=AES192C")
212
	case AES256C:
213
		sb.WriteString(",priv=AES256C")
214
	}
215
	sb.WriteString(",privPass=")
216
	sb.WriteString(sp.PrivacyPassphrase)
217

218
	return sb.String()
219
}
220

221
// SafeString returns a logging safe (no secrets) string of the UsmSecurityParameters
222
func (sp *UsmSecurityParameters) SafeString() string {
223
	return fmt.Sprintf("AuthoritativeEngineID:%s, AuthoritativeEngineBoots:%d, AuthoritativeEngineTimes:%d, UserName:%s, AuthenticationParameters:%s, PrivacyParameters:%v, AuthenticationProtocol:%s, PrivacyProtocol:%s",
224
		sp.AuthoritativeEngineID,
225
		sp.AuthoritativeEngineBoots,
226
		sp.AuthoritativeEngineTime,
227
		sp.UserName,
228
		sp.AuthenticationParameters,
229
		sp.PrivacyParameters,
230
		sp.AuthenticationProtocol,
231
		sp.PrivacyProtocol,
232
	)
233
}
234

235
// Log logs security paramater information to the provided GoSNMP Logger
236
func (sp *UsmSecurityParameters) Log() {
237
	sp.mu.Lock()
238
	defer sp.mu.Unlock()
239
	sp.Logger.Printf("SECURITY PARAMETERS:%s", sp.SafeString())
240
}
241

242
// Copy method for UsmSecurityParameters used to copy a SnmpV3SecurityParameters without knowing it's implementation
243
func (sp *UsmSecurityParameters) Copy() SnmpV3SecurityParameters {
244
	sp.mu.Lock()
245
	defer sp.mu.Unlock()
246
	return &UsmSecurityParameters{AuthoritativeEngineID: sp.AuthoritativeEngineID,
247
		AuthoritativeEngineBoots: sp.AuthoritativeEngineBoots,
248
		AuthoritativeEngineTime:  sp.AuthoritativeEngineTime,
249
		UserName:                 sp.UserName,
250
		AuthenticationParameters: sp.AuthenticationParameters,
251
		PrivacyParameters:        sp.PrivacyParameters,
252
		AuthenticationProtocol:   sp.AuthenticationProtocol,
253
		PrivacyProtocol:          sp.PrivacyProtocol,
254
		AuthenticationPassphrase: sp.AuthenticationPassphrase,
255
		PrivacyPassphrase:        sp.PrivacyPassphrase,
256
		SecretKey:                sp.SecretKey,
257
		PrivacyKey:               sp.PrivacyKey,
258
		localDESSalt:             sp.localDESSalt,
259
		localAESSalt:             sp.localAESSalt,
260
		Logger:                   sp.Logger,
261
	}
262
}
263

264
func (sp *UsmSecurityParameters) getDefaultContextEngineID() string {
265
	return sp.AuthoritativeEngineID
266
}
267

268
// InitSecurityKeys initializes the Priv and Auth keys if needed
269
func (sp *UsmSecurityParameters) InitSecurityKeys() error {
270
	sp.mu.Lock()
271
	defer sp.mu.Unlock()
272

273
	return sp.initSecurityKeysNoLock()
274
}
275

276
func (sp *UsmSecurityParameters) initSecurityKeysNoLock() error {
277
	var err error
278

279
	if sp.AuthenticationProtocol > NoAuth && len(sp.SecretKey) == 0 {
280
		sp.SecretKey, err = genlocalkey(sp.AuthenticationProtocol,
281
			sp.AuthenticationPassphrase,
282
			sp.AuthoritativeEngineID)
283
		if err != nil {
284
			return err
285
		}
286
	}
287
	if sp.PrivacyProtocol > NoPriv && len(sp.PrivacyKey) == 0 {
288
		switch sp.PrivacyProtocol {
289
		// Changed: The Output of SHA1 is a 20 octets array, therefore for AES128 (16 octets) either key extension algorithm can be used.
290
		case AES, AES192, AES256, AES192C, AES256C:
291
			// Use abstract AES key localization algorithms.
292
			sp.PrivacyKey, err = genlocalPrivKey(sp.PrivacyProtocol, sp.AuthenticationProtocol,
293
				sp.PrivacyPassphrase,
294
				sp.AuthoritativeEngineID)
295
			if err != nil {
296
				return err
297
			}
298
		default:
299
			sp.PrivacyKey, err = genlocalkey(sp.AuthenticationProtocol,
300
				sp.PrivacyPassphrase,
301
				sp.AuthoritativeEngineID)
302
			if err != nil {
303
				return err
304
			}
305
		}
306
	}
307
	return nil
308
}
309

310
func (sp *UsmSecurityParameters) setSecurityParameters(in SnmpV3SecurityParameters) error {
311
	var insp *UsmSecurityParameters
312
	var err error
313

314
	sp.mu.Lock()
315
	defer sp.mu.Unlock()
316

317
	if insp, err = castUsmSecParams(in); err != nil {
318
		return err
319
	}
320

321
	if sp.AuthoritativeEngineID != insp.AuthoritativeEngineID {
322
		sp.AuthoritativeEngineID = insp.AuthoritativeEngineID
323
		sp.SecretKey = nil
324
		sp.PrivacyKey = nil
325

326
		err = sp.initSecurityKeysNoLock()
327
		if err != nil {
328
			return err
329
		}
330
	}
331
	sp.AuthoritativeEngineBoots = insp.AuthoritativeEngineBoots
332
	sp.AuthoritativeEngineTime = insp.AuthoritativeEngineTime
333

334
	return nil
335
}
336

337
func (sp *UsmSecurityParameters) validate(flags SnmpV3MsgFlags) error {
338
	securityLevel := flags & AuthPriv // isolate flags that determine security level
339

340
	switch securityLevel {
341
	case AuthPriv:
342
		if sp.PrivacyProtocol <= NoPriv {
343
			return fmt.Errorf("securityParameters.PrivacyProtocol is required")
344
		}
345
		fallthrough
346
	case AuthNoPriv:
347
		if sp.AuthenticationProtocol <= NoAuth {
348
			return fmt.Errorf("securityParameters.AuthenticationProtocol is required")
349
		}
350
		fallthrough
351
	case NoAuthNoPriv:
352
		if sp.UserName == "" {
353
			return fmt.Errorf("securityParameters.UserName is required")
354
		}
355
	default:
356
		return fmt.Errorf("validate: MsgFlags must be populated with an appropriate security level")
357
	}
358

359
	if sp.PrivacyProtocol > NoPriv && len(sp.PrivacyKey) == 0 {
360
		if sp.PrivacyPassphrase == "" {
361
			return fmt.Errorf("securityParameters.PrivacyPassphrase is required when a privacy protocol is specified")
362
		}
363
	}
364

365
	if sp.AuthenticationProtocol > NoAuth && len(sp.SecretKey) == 0 {
366
		if sp.AuthenticationPassphrase == "" {
367
			return fmt.Errorf("securityParameters.AuthenticationPassphrase is required when an authentication protocol is specified")
368
		}
369
	}
370

371
	return nil
372
}
373

374
func (sp *UsmSecurityParameters) init(log Logger) error {
375
	var err error
376

377
	sp.Logger = log
378

379
	switch sp.PrivacyProtocol {
380
	case AES, AES192, AES256, AES192C, AES256C:
381
		salt := make([]byte, 8)
382
		_, err = crand.Read(salt)
383
		if err != nil {
384
			return fmt.Errorf("error creating a cryptographically secure salt: %w", err)
385
		}
386
		sp.localAESSalt = binary.BigEndian.Uint64(salt)
387
	case DES:
388
		salt := make([]byte, 4)
389
		_, err = crand.Read(salt)
390
		if err != nil {
391
			return fmt.Errorf("error creating a cryptographically secure salt: %w", err)
392
		}
393
		sp.localDESSalt = binary.BigEndian.Uint32(salt)
394
	}
395

396
	return nil
397
}
398

399
func castUsmSecParams(secParams SnmpV3SecurityParameters) (*UsmSecurityParameters, error) {
400
	s, ok := secParams.(*UsmSecurityParameters)
401
	if !ok || s == nil {
402
		return nil, fmt.Errorf("param SnmpV3SecurityParameters is not of type *UsmSecurityParameters")
403
	}
404
	return s, nil
405
}
406

407
var (
408
	passwordKeyHashCache = make(map[string][]byte) //nolint:gochecknoglobals
409
	passwordKeyHashMutex sync.RWMutex              //nolint:gochecknoglobals
410
	passwordCacheDisable atomic.Bool               //nolint:gochecknoglobals
411
)
412

413
// PasswordCaching is enabled by default for performance reason. If the cache was disabled then
414
// re-enabled, the cache is reset.
415
func PasswordCaching(enable bool) {
416
	oldCacheEnable := !passwordCacheDisable.Load()
417
	passwordKeyHashMutex.Lock()
418
	if !enable { // if off
419
		passwordKeyHashCache = nil
420
	} else if !oldCacheEnable && enable { // if off then on
421
		passwordKeyHashCache = make(map[string][]byte)
422
	}
423
	passwordCacheDisable.Store(!enable)
424
	passwordKeyHashMutex.Unlock()
425
}
426

427
func hashPassword(hash hash.Hash, password string) ([]byte, error) {
428
	if len(password) == 0 {
429
		return []byte{}, errors.New("hashPassword: password is empty")
430
	}
431
	var pi int // password index
432
	for i := 0; i < 1048576; i += 64 {
433
		var chunk []byte
434
		for e := 0; e < 64; e++ {
435
			chunk = append(chunk, password[pi%len(password)])
436
			pi++
437
		}
438
		if _, err := hash.Write(chunk); err != nil {
439
			return []byte{}, err
440
		}
441
	}
442
	hashed := hash.Sum(nil)
443
	return hashed, nil
444
}
445

446
// Common passwordToKey algorithm, "caches" the result to avoid extra computation each reuse
447
func cachedPasswordToKey(hash hash.Hash, cacheKey string, password string) ([]byte, error) {
448
	cacheDisable := passwordCacheDisable.Load()
449
	if !cacheDisable {
450
		passwordKeyHashMutex.RLock()
451
		value := passwordKeyHashCache[cacheKey]
452
		passwordKeyHashMutex.RUnlock()
453

454
		if value != nil {
455
			return value, nil
456
		}
457
	}
458

459
	hashed, err := hashPassword(hash, password)
460
	if err != nil {
461
		return nil, err
462
	}
463

464
	if !cacheDisable {
465
		passwordKeyHashMutex.Lock()
466
		passwordKeyHashCache[cacheKey] = hashed
467
		passwordKeyHashMutex.Unlock()
468
	}
469

470
	return hashed, nil
471
}
472

473
func hMAC(hash crypto.Hash, cacheKey string, password string, engineID string) ([]byte, error) {
474
	hashed, err := cachedPasswordToKey(hash.New(), cacheKey, password)
475
	if err != nil {
476
		return []byte{}, nil
477
	}
478

479
	local := hash.New()
480
	_, err = local.Write(hashed)
481
	if err != nil {
482
		return []byte{}, err
483
	}
484

485
	_, err = local.Write([]byte(engineID))
486
	if err != nil {
487
		return []byte{}, err
488
	}
489

490
	_, err = local.Write(hashed)
491
	if err != nil {
492
		return []byte{}, err
493
	}
494

495
	final := local.Sum(nil)
496
	return final, nil
497
}
498

499
func cacheKey(authProtocol SnmpV3AuthProtocol, passphrase string) string {
500
	if passwordCacheDisable.Load() {
501
		return ""
502
	}
503
	var cacheKey = make([]byte, 1+len(passphrase))
504
	cacheKey = append(cacheKey, 'h'+byte(authProtocol))
505
	cacheKey = append(cacheKey, []byte(passphrase)...)
506
	return string(cacheKey)
507
}
508

509
// Extending the localized privacy key according to Reeder Key extension algorithm:
510
// https://tools.ietf.org/html/draft-reeder-snmpv3-usm-3dese
511
// Many vendors, including Cisco, use the 3DES key extension algorithm to extend the privacy keys that are too short when using AES,AES192 and AES256.
512
// Previously implemented in net-snmp and pysnmp libraries.
513
// Tested for AES128 and AES256
514
func extendKeyReeder(authProtocol SnmpV3AuthProtocol, password string, engineID string) ([]byte, error) {
515
	var key []byte
516
	var err error
517

518
	key, err = hMAC(authProtocol.HashType(), cacheKey(authProtocol, password), password, engineID)
519

520
	if err != nil {
521
		return nil, err
522
	}
523

524
	newkey, err := hMAC(authProtocol.HashType(), cacheKey(authProtocol, string(key)), string(key), engineID)
525

526
	return append(key, newkey...), err
527
}
528

529
// Extending the localized privacy key according to Blumenthal key extension algorithm:
530
// https://tools.ietf.org/html/draft-blumenthal-aes-usm-04#page-7
531
// Not many vendors use this algorithm.
532
// Previously implemented in the net-snmp and pysnmp libraries.
533
// TODO: Not tested
534
func extendKeyBlumenthal(authProtocol SnmpV3AuthProtocol, password string, engineID string) ([]byte, error) {
535
	var key []byte
536
	var err error
537

538
	key, err = hMAC(authProtocol.HashType(), cacheKey(authProtocol, password), password, engineID)
539

540
	if err != nil {
541
		return nil, err
542
	}
543

544
	newkey := authProtocol.HashType().New()
545
	_, _ = newkey.Write(key)
546
	return append(key, newkey.Sum(nil)...), err
547
}
548

549
// Changed: New function to calculate the Privacy Key for abstract AES
550
func genlocalPrivKey(privProtocol SnmpV3PrivProtocol, authProtocol SnmpV3AuthProtocol, password string, engineID string) ([]byte, error) {
551
	var keylen int
552
	var localPrivKey []byte
553
	var err error
554

555
	switch privProtocol {
556
	case AES, DES:
557
		keylen = 16
558
	case AES192, AES192C:
559
		keylen = 24
560
	case AES256, AES256C:
561
		keylen = 32
562
	}
563

564
	switch privProtocol {
565
	case AES, AES192C, AES256C:
566
		localPrivKey, err = extendKeyReeder(authProtocol, password, engineID)
567

568
	case AES192, AES256:
569
		localPrivKey, err = extendKeyBlumenthal(authProtocol, password, engineID)
570

571
	default:
572
		localPrivKey, err = genlocalkey(authProtocol, password, engineID)
573
	}
574

575
	if err != nil {
576
		return nil, err
577
	}
578

579
	if len(localPrivKey) < keylen {
580
		return []byte{}, fmt.Errorf("genlocalPrivKey: privProtocol: %v len(localPrivKey): %d, keylen: %d",
581
			privProtocol, len(localPrivKey), keylen)
582
	}
583

584
	return localPrivKey[:keylen], nil
585
}
586

587
func genlocalkey(authProtocol SnmpV3AuthProtocol, passphrase string, engineID string) ([]byte, error) {
588
	var secretKey []byte
589
	var err error
590

591
	secretKey, err = hMAC(authProtocol.HashType(), cacheKey(authProtocol, passphrase), passphrase, engineID)
592

593
	if err != nil {
594
		return []byte{}, err
595
	}
596

597
	return secretKey, nil
598
}
599

600
// http://tools.ietf.org/html/rfc2574#section-8.1.1.1
601
// localDESSalt needs to be incremented on every packet.
602
func (sp *UsmSecurityParameters) usmAllocateNewSalt() interface{} {
603
	sp.mu.Lock()
604
	defer sp.mu.Unlock()
605
	var newSalt interface{}
606

607
	switch sp.PrivacyProtocol {
608
	case AES, AES192, AES256, AES192C, AES256C:
609
		newSalt = atomic.AddUint64(&(sp.localAESSalt), 1)
610
	default:
611
		newSalt = atomic.AddUint32(&(sp.localDESSalt), 1)
612
	}
613
	return newSalt
614
}
615

616
func (sp *UsmSecurityParameters) usmSetSalt(newSalt interface{}) error {
617
	sp.mu.Lock()
618
	defer sp.mu.Unlock()
619
	switch sp.PrivacyProtocol {
620
	case AES, AES192, AES256, AES192C, AES256C:
621
		aesSalt, ok := newSalt.(uint64)
622
		if !ok {
623
			return fmt.Errorf("salt provided to usmSetSalt is not the correct type for the AES privacy protocol")
624
		}
625
		var salt = make([]byte, 8)
626
		binary.BigEndian.PutUint64(salt, aesSalt)
627
		sp.PrivacyParameters = salt
628
	default:
629
		desSalt, ok := newSalt.(uint32)
630
		if !ok {
631
			return fmt.Errorf("salt provided to usmSetSalt is not the correct type for the DES privacy protocol")
632
		}
633
		var salt = make([]byte, 8)
634
		binary.BigEndian.PutUint32(salt, sp.AuthoritativeEngineBoots)
635
		binary.BigEndian.PutUint32(salt[4:], desSalt)
636
		sp.PrivacyParameters = salt
637
	}
638
	return nil
639
}
640

641
// InitPacket ensures the enc salt is incremented for packets marked for AuthPriv
642
func (sp *UsmSecurityParameters) InitPacket(packet *SnmpPacket) error {
643
	// http://tools.ietf.org/html/rfc2574#section-8.1.1.1
644
	// localDESSalt needs to be incremented on every packet.
645
	newSalt := sp.usmAllocateNewSalt()
646
	if packet.MsgFlags&AuthPriv > AuthNoPriv {
647
		s, err := castUsmSecParams(packet.SecurityParameters)
648
		if err != nil {
649
			return err
650
		}
651
		return s.usmSetSalt(newSalt)
652
	}
653
	return nil
654
}
655

656
func (sp *UsmSecurityParameters) discoveryRequired() *SnmpPacket {
657
	if sp.AuthoritativeEngineID == "" {
658
		var emptyPdus []SnmpPDU
659

660
		// send blank packet to discover authoriative engine ID/boots/time
661
		blankPacket := &SnmpPacket{
662
			Version:            Version3,
663
			MsgFlags:           Reportable | NoAuthNoPriv,
664
			SecurityModel:      UserSecurityModel,
665
			SecurityParameters: &UsmSecurityParameters{Logger: sp.Logger},
666
			PDUType:            GetRequest,
667
			Logger:             sp.Logger,
668
			Variables:          emptyPdus,
669
		}
670

671
		return blankPacket
672
	}
673
	return nil
674
}
675

676
func (sp *UsmSecurityParameters) calcPacketDigest(packet []byte) ([]byte, error) {
677
	return calcPacketDigest(packet, sp)
678
}
679

680
// calcPacketDigest calculate authenticate digest for incoming messages (TRAP or
681
// INFORM).
682
// Support MD5, SHA1, SHA224, SHA256, SHA384, SHA512 protocols
683
func calcPacketDigest(packetBytes []byte, secParams *UsmSecurityParameters) ([]byte, error) {
684
	var digest []byte
685
	var err error
686

687
	switch secParams.AuthenticationProtocol {
688
	case MD5, SHA:
689
		digest, err = digestRFC3414(
690
			secParams.AuthenticationProtocol,
691
			packetBytes,
692
			secParams.SecretKey)
693
	case SHA224, SHA256, SHA384, SHA512:
694
		digest, err = digestRFC7860(
695
			secParams.AuthenticationProtocol,
696
			packetBytes,
697
			secParams.SecretKey)
698
	}
699

700
	return digest, err
701
}
702

703
// digestRFC7860 calculate digest for incoming messages using HMAC-SHA2 protcols
704
// according to RFC7860 4.2.2
705
func digestRFC7860(h SnmpV3AuthProtocol, packet []byte, authKey []byte) ([]byte, error) {
706
	mac := hmac.New(h.HashType().New, authKey)
707
	_, err := mac.Write(packet)
708
	if err != nil {
709
		return []byte{}, err
710
	}
711
	msgDigest := mac.Sum(nil)
712
	return msgDigest, nil
713
}
714

715
// digestRFC3414 calculate digest for incoming messages using MD5 or SHA1
716
// according to RFC3414 6.3.2 and 7.3.2
717
func digestRFC3414(h SnmpV3AuthProtocol, packet []byte, authKey []byte) ([]byte, error) {
718
	var extkey [64]byte
719
	var err error
720
	var k1, k2 [64]byte
721
	var h1, h2 hash.Hash
722

723
	copy(extkey[:], authKey)
724

725
	switch h {
726
	case MD5:
727
		h1 = md5.New() //nolint:gosec
728
		h2 = md5.New() //nolint:gosec
729
	case SHA:
730
		h1 = sha1.New() //nolint:gosec
731
		h2 = sha1.New() //nolint:gosec
732
	}
733

734
	for i := 0; i < 64; i++ {
735
		k1[i] = extkey[i] ^ 0x36
736
		k2[i] = extkey[i] ^ 0x5c
737
	}
738

739
	_, err = h1.Write(k1[:])
740
	if err != nil {
741
		return []byte{}, err
742
	}
743

744
	_, err = h1.Write(packet)
745
	if err != nil {
746
		return []byte{}, err
747
	}
748

749
	d1 := h1.Sum(nil)
750

751
	_, err = h2.Write(k2[:])
752
	if err != nil {
753
		return []byte{}, err
754
	}
755

756
	_, err = h2.Write(d1)
757
	if err != nil {
758
		return []byte{}, err
759
	}
760

761
	return h2.Sum(nil)[:12], nil
762
}
763

764
func (sp *UsmSecurityParameters) authenticate(packet []byte) error {
765
	var msgDigest []byte
766
	var err error
767

768
	if msgDigest, err = sp.calcPacketDigest(packet); err != nil {
769
		return err
770
	}
771

772
	idx := bytes.Index(packet, macVarbinds[sp.AuthenticationProtocol])
773

774
	if idx < 0 {
775
		return fmt.Errorf("unable to locate the position in packet to write authentication key")
776
	}
777

778
	copy(packet[idx+2:idx+len(macVarbinds[sp.AuthenticationProtocol])], msgDigest)
779
	return nil
780
}
781

782
// determine whether a message is authentic
783
func (sp *UsmSecurityParameters) isAuthentic(packetBytes []byte, packet *SnmpPacket) (bool, error) {
784
	var msgDigest []byte
785
	var packetSecParams *UsmSecurityParameters
786
	var err error
787

788
	if packetSecParams, err = castUsmSecParams(packet.SecurityParameters); err != nil {
789
		return false, err
790
	}
791
	// TODO: investigate call chain to determine if this is really the best spot for this
792
	if msgDigest, err = calcPacketDigest(packetBytes, packetSecParams); err != nil {
793
		return false, err
794
	}
795

796
	for k, v := range []byte(packetSecParams.AuthenticationParameters) {
797
		if msgDigest[k] != v {
798
			return false, nil
799
		}
800
	}
801
	return true, nil
802
}
803

804
func (sp *UsmSecurityParameters) encryptPacket(scopedPdu []byte) ([]byte, error) {
805
	var b []byte
806

807
	switch sp.PrivacyProtocol {
808
	case AES, AES192, AES256, AES192C, AES256C:
809
		var iv [16]byte
810
		binary.BigEndian.PutUint32(iv[:], sp.AuthoritativeEngineBoots)
811
		binary.BigEndian.PutUint32(iv[4:], sp.AuthoritativeEngineTime)
812
		copy(iv[8:], sp.PrivacyParameters)
813
		// aes.NewCipher(sp.PrivacyKey[:16]) changed to aes.NewCipher(sp.PrivacyKey)
814
		block, err := aes.NewCipher(sp.PrivacyKey)
815
		if err != nil {
816
			return nil, err
817
		}
818
		stream := cipher.NewCFBEncrypter(block, iv[:])
819
		ciphertext := make([]byte, len(scopedPdu))
820
		stream.XORKeyStream(ciphertext, scopedPdu)
821
		pduLen, err := marshalLength(len(ciphertext))
822
		if err != nil {
823
			return nil, err
824
		}
825
		b = append([]byte{byte(OctetString)}, pduLen...)
826
		scopedPdu = append(b, ciphertext...) //nolint:gocritic
827
	case DES:
828
		preiv := sp.PrivacyKey[8:]
829
		var iv [8]byte
830
		for i := 0; i < len(iv); i++ {
831
			iv[i] = preiv[i] ^ sp.PrivacyParameters[i]
832
		}
833
		block, err := des.NewCipher(sp.PrivacyKey[:8]) //nolint:gosec
834
		if err != nil {
835
			return nil, err
836
		}
837
		mode := cipher.NewCBCEncrypter(block, iv[:])
838

839
		pad := make([]byte, des.BlockSize-len(scopedPdu)%des.BlockSize)
840
		scopedPdu = append(scopedPdu, pad...)
841

842
		ciphertext := make([]byte, len(scopedPdu))
843
		mode.CryptBlocks(ciphertext, scopedPdu)
844
		pduLen, err := marshalLength(len(ciphertext))
845
		if err != nil {
846
			return nil, err
847
		}
848
		b = append([]byte{byte(OctetString)}, pduLen...)
849
		scopedPdu = append(b, ciphertext...) //nolint:gocritic
850
	}
851

852
	return scopedPdu, nil
853
}
854

855
func (sp *UsmSecurityParameters) decryptPacket(packet []byte, cursor int) ([]byte, error) {
856
	_, cursorTmp, err := parseLength(packet[cursor:])
857
	if err != nil {
858
		return nil, err
859
	}
860
	cursorTmp += cursor
861
	if cursorTmp > len(packet) {
862
		return nil, errors.New("error decrypting ScopedPDU: truncated packet")
863
	}
864

865
	switch sp.PrivacyProtocol {
866
	case AES, AES192, AES256, AES192C, AES256C:
867
		var iv [16]byte
868
		binary.BigEndian.PutUint32(iv[:], sp.AuthoritativeEngineBoots)
869
		binary.BigEndian.PutUint32(iv[4:], sp.AuthoritativeEngineTime)
870
		copy(iv[8:], sp.PrivacyParameters)
871

872
		block, err := aes.NewCipher(sp.PrivacyKey)
873
		if err != nil {
874
			return nil, err
875
		}
876
		stream := cipher.NewCFBDecrypter(block, iv[:])
877
		plaintext := make([]byte, len(packet[cursorTmp:]))
878
		stream.XORKeyStream(plaintext, packet[cursorTmp:])
879
		copy(packet[cursor:], plaintext)
880
		packet = packet[:cursor+len(plaintext)]
881
	case DES:
882
		if len(packet[cursorTmp:])%des.BlockSize != 0 {
883
			return nil, errors.New("error decrypting ScopedPDU: not multiple of des block size")
884
		}
885
		preiv := sp.PrivacyKey[8:]
886
		var iv [8]byte
887
		for i := 0; i < len(iv); i++ {
888
			iv[i] = preiv[i] ^ sp.PrivacyParameters[i]
889
		}
890
		block, err := des.NewCipher(sp.PrivacyKey[:8]) //nolint:gosec
891
		if err != nil {
892
			return nil, err
893
		}
894
		mode := cipher.NewCBCDecrypter(block, iv[:])
895

896
		plaintext := make([]byte, len(packet[cursorTmp:]))
897
		mode.CryptBlocks(plaintext, packet[cursorTmp:])
898
		copy(packet[cursor:], plaintext)
899
		// truncate packet to remove extra space caused by the
900
		// octetstring/length header that was just replaced
901
		packet = packet[:cursor+len(plaintext)]
902
	}
903
	return packet, nil
904
}
905

906
// marshal a snmp version 3 security parameters field for the User Security Model
907
func (sp *UsmSecurityParameters) marshal(flags SnmpV3MsgFlags) ([]byte, error) {
908
	var buf bytes.Buffer
909
	var err error
910

911
	// msgAuthoritativeEngineID
912
	buf.Write([]byte{byte(OctetString), byte(len(sp.AuthoritativeEngineID))})
913
	buf.WriteString(sp.AuthoritativeEngineID)
914

915
	// msgAuthoritativeEngineBoots
916
	msgAuthoritativeEngineBoots, err := marshalUint32(sp.AuthoritativeEngineBoots)
917
	if err != nil {
918
		return nil, err
919
	}
920
	buf.Write([]byte{byte(Integer), byte(len(msgAuthoritativeEngineBoots))})
921
	buf.Write(msgAuthoritativeEngineBoots)
922

923
	// msgAuthoritativeEngineTime
924
	msgAuthoritativeEngineTime, err := marshalUint32(sp.AuthoritativeEngineTime)
925
	if err != nil {
926
		return nil, err
927
	}
928
	buf.Write([]byte{byte(Integer), byte(len(msgAuthoritativeEngineTime))})
929
	buf.Write(msgAuthoritativeEngineTime)
930

931
	// msgUserName
932
	buf.Write([]byte{byte(OctetString), byte(len(sp.UserName))})
933
	buf.WriteString(sp.UserName)
934

935
	// msgAuthenticationParameters
936
	if flags&AuthNoPriv > 0 {
937
		buf.Write(macVarbinds[sp.AuthenticationProtocol])
938
	} else {
939
		buf.Write([]byte{byte(OctetString), 0})
940
	}
941
	// msgPrivacyParameters
942
	if flags&AuthPriv > AuthNoPriv {
943
		privlen, err2 := marshalLength(len(sp.PrivacyParameters))
944
		if err2 != nil {
945
			return nil, err2
946
		}
947
		buf.Write([]byte{byte(OctetString)})
948
		buf.Write(privlen)
949
		buf.Write(sp.PrivacyParameters)
950
	} else {
951
		buf.Write([]byte{byte(OctetString), 0})
952
	}
953

954
	// wrap security parameters in a sequence
955
	paramLen, err := marshalLength(buf.Len())
956
	if err != nil {
957
		return nil, err
958
	}
959
	tmpseq := append([]byte{byte(Sequence)}, paramLen...)
960
	tmpseq = append(tmpseq, buf.Bytes()...)
961

962
	return tmpseq, nil
963
}
964

965
func (sp *UsmSecurityParameters) unmarshal(flags SnmpV3MsgFlags, packet []byte, cursor int) (int, error) {
966
	var err error
967

968
	if cursor >= len(packet) {
969
		return 0, errors.New("error parsing SNMPV3 User Security Model parameters: end of packet")
970
	}
971

972
	if PDUType(packet[cursor]) != Sequence {
973
		return 0, errors.New("error parsing SNMPV3 User Security Model parameters")
974
	}
975
	_, cursorTmp, err := parseLength(packet[cursor:])
976
	if err != nil {
977
		return 0, err
978
	}
979
	cursor += cursorTmp
980
	if cursorTmp > len(packet) {
981
		return 0, errors.New("error parsing SNMPV3 User Security Model parameters: truncated packet")
982
	}
983

984
	rawMsgAuthoritativeEngineID, count, err := parseRawField(sp.Logger, packet[cursor:], "msgAuthoritativeEngineID")
985
	if err != nil {
986
		return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgAuthoritativeEngineID: %w", err)
987
	}
988
	cursor += count
989
	if AuthoritativeEngineID, ok := rawMsgAuthoritativeEngineID.(string); ok {
990
		if sp.AuthoritativeEngineID != AuthoritativeEngineID {
991
			sp.AuthoritativeEngineID = AuthoritativeEngineID
992
			sp.SecretKey = nil
993
			sp.PrivacyKey = nil
994

995
			sp.Logger.Printf("Parsed authoritativeEngineID %0x", []byte(AuthoritativeEngineID))
996
			err = sp.initSecurityKeysNoLock()
997
			if err != nil {
998
				return 0, err
999
			}
1000
		}
1001
	}
1002

1003
	rawMsgAuthoritativeEngineBoots, count, err := parseRawField(sp.Logger, packet[cursor:], "msgAuthoritativeEngineBoots")
1004
	if err != nil {
1005
		return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgAuthoritativeEngineBoots: %w", err)
1006
	}
1007
	cursor += count
1008
	if AuthoritativeEngineBoots, ok := rawMsgAuthoritativeEngineBoots.(int); ok {
1009
		sp.AuthoritativeEngineBoots = uint32(AuthoritativeEngineBoots)
1010
		sp.Logger.Printf("Parsed authoritativeEngineBoots %d", AuthoritativeEngineBoots)
1011
	}
1012

1013
	rawMsgAuthoritativeEngineTime, count, err := parseRawField(sp.Logger, packet[cursor:], "msgAuthoritativeEngineTime")
1014
	if err != nil {
1015
		return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgAuthoritativeEngineTime: %w", err)
1016
	}
1017
	cursor += count
1018
	if AuthoritativeEngineTime, ok := rawMsgAuthoritativeEngineTime.(int); ok {
1019
		sp.AuthoritativeEngineTime = uint32(AuthoritativeEngineTime)
1020
		sp.Logger.Printf("Parsed authoritativeEngineTime %d", AuthoritativeEngineTime)
1021
	}
1022

1023
	rawMsgUserName, count, err := parseRawField(sp.Logger, packet[cursor:], "msgUserName")
1024
	if err != nil {
1025
		return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgUserName: %w", err)
1026
	}
1027
	cursor += count
1028
	if msgUserName, ok := rawMsgUserName.(string); ok {
1029
		sp.UserName = msgUserName
1030
		sp.Logger.Printf("Parsed userName %s", msgUserName)
1031
	}
1032

1033
	rawMsgAuthParameters, count, err := parseRawField(sp.Logger, packet[cursor:], "msgAuthenticationParameters")
1034
	if err != nil {
1035
		return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgAuthenticationParameters: %w", err)
1036
	}
1037
	if msgAuthenticationParameters, ok := rawMsgAuthParameters.(string); ok {
1038
		sp.AuthenticationParameters = msgAuthenticationParameters
1039
		sp.Logger.Printf("Parsed authenticationParameters %s", msgAuthenticationParameters)
1040
	}
1041
	// blank msgAuthenticationParameters to prepare for authentication check later
1042
	if flags&AuthNoPriv > 0 {
1043
		// In case if the authentication protocol is not configured or set to NoAuth, then the packet cannot
1044
		// be processed further
1045
		if sp.AuthenticationProtocol <= NoAuth {
1046
			return 0, errors.New("error parsing SNMPv3 User Security Model: authentication parameters are not configured to parse incoming authenticated message")
1047
		}
1048
		copy(packet[cursor+2:cursor+len(macVarbinds[sp.AuthenticationProtocol])], macVarbinds[sp.AuthenticationProtocol][2:])
1049
	}
1050
	cursor += count
1051

1052
	rawMsgPrivacyParameters, count, err := parseRawField(sp.Logger, packet[cursor:], "msgPrivacyParameters")
1053
	if err != nil {
1054
		return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgPrivacyParameters: %w", err)
1055
	}
1056
	cursor += count
1057
	if msgPrivacyParameters, ok := rawMsgPrivacyParameters.(string); ok {
1058
		sp.PrivacyParameters = []byte(msgPrivacyParameters)
1059
		sp.Logger.Printf("Parsed privacyParameters %s", msgPrivacyParameters)
1060
		if flags&AuthPriv >= AuthPriv {
1061
			if sp.PrivacyProtocol <= NoPriv {
1062
				return 0, errors.New("error parsing SNMPv3 User Security Model: privacy parameters are not configured to parse incoming encrypted message")
1063
			}
1064
		}
1065
	}
1066

1067
	return cursor, nil
1068
}
1069

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.