netsnmp 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60a9c68e56e5ad0600d056eedd315d8639cfd3965aa5e1eb62f5f3f935b4ad6a
4
- data.tar.gz: ea560ac1131bff9ca6af0fec5e426bd37edbfbc7520ceb629f1de279a905ced0
3
+ metadata.gz: 93b380d735dc2250a9c3528e7ab1e6c70147124a6dc439fe7dd4eac1f4c0fd55
4
+ data.tar.gz: 52db8f559bc3485ab4802d72ccba98fb29429a0b9a0cceec13fc5a489409e35e
5
5
  SHA512:
6
- metadata.gz: 8410a3a38d7d7eab07692009ae4d870dbae8e7abc50dbc724d9ad3f8a4e029b441ffba62e9477ae587f8845a561cb0ba2e2c5613540d615bdce0cf62cb448098
7
- data.tar.gz: 85d0d898983f99dedb4a71cf6bd33f665cfda496bb8a60eb76be36a96de6f28a9984505c564701bfab6bf74e8367544ec2f5adbcf8faf152d9a71679b73b5df1
6
+ metadata.gz: 1a7573fe1ca2f0d78a3ca4d88d8054b9fd872078352d8ed3f700525e049719d601fe8f7dda94f0ff77b866e75961eb26af80a4acd332e04d390c433405970fd7
7
+ data.tar.gz: 3524298a40a89f5364a27cfe0444f7e298016822439020c04b203960cc4c77622453f9a48d6c01ea1e072c3a004705299b6a8f34a89cb1ce00f66a98f3919d7a
data/README.md CHANGED
@@ -31,6 +31,7 @@ $ gem install netsnmp
31
31
  This gem provides:
32
32
 
33
33
  * Implementation in ruby of the SNMP Protocol for v3, v2c and v1 (most notable the rfc3414 and 3826).
34
+ * SNMPv3 USM supporting MD5/SHA/SHA256 auth and DES/AES128 privacy crypto algorithms.
34
35
  * Client/Manager API with simple interface for get, genext, set and walk.
35
36
  * Pure Ruby.
36
37
  * Support for concurrency and evented I/O.
@@ -80,7 +81,7 @@ manager.get(oid: "sysName.0") #=> 'tt'
80
81
  # SNMP walk
81
82
  # sysORDescr
82
83
  manager.walk(oid: "sysORDescr").each do |oid_code, value|
83
- # do something with them
84
+ # do something with them
84
85
  puts "for #{oid_code}: #{value}"
85
86
  end
86
87
 
@@ -214,8 +215,8 @@ gem 'netsnmp'
214
215
 
215
216
  # or, in the command line
216
217
 
217
- $ gem install netsnmp
218
- ```
218
+ $ gem install netsnmp
219
+ ```
219
220
 
220
221
  and `netsnmp` will automatically pick it up.
221
222
 
@@ -245,6 +246,8 @@ This library supports and is tested against ruby versions 2.1 or more recent, in
245
246
 
246
247
  All encoding/decoding/encryption/decryption/digests are done using `openssl`, which is (still) a part of the standard library. If at some point `openssl` is removed and not specifically distributed, you'll have to install it yourself. Hopefully this will never happen.
247
248
 
249
+ It also uses the `openssl` ASN.1 API to encode/decode BERs, which is known to be strict, and [may not be able to decode PDUs if not compliant with the supported RFC](https://github.com/swisscom/ruby-netsnmp/issues/47).
250
+
248
251
  ## Debugging
249
252
 
250
253
  You can either set the `NETSNMP_DEBUG` to the desided debug level (currently, 1 and 2). The logs will be written to stderr.
@@ -7,8 +7,7 @@ module NETSNMP
7
7
 
8
8
  prepend Loggable
9
9
 
10
- AUTHNONE = OpenSSL::ASN1::OctetString.new("\x00" * 12).with_label(:auth_mask)
11
- PRIVNONE = OpenSSL::ASN1::OctetString.new("")
10
+ PRIVNONE = OpenSSL::ASN1::OctetString.new("")
12
11
  MSG_MAX_SIZE = OpenSSL::ASN1::Integer.new(65507).with_label(:max_message_size)
13
12
  MSG_SECURITY_MODEL = OpenSSL::ASN1::Integer.new(3).with_label(:security_model) # usmSecurityModel
14
13
  MSG_VERSION = OpenSSL::ASN1::Integer.new(3).with_label(:message_version)
@@ -16,6 +15,10 @@ module NETSNMP
16
15
 
17
16
  def initialize(**); end
18
17
 
18
+ def verify(stream, auth_param, security_level, security_parameters:)
19
+ security_parameters.verify(stream.sub(auth_param, authnone(security_parameters.auth_protocol).value), auth_param, security_level: security_level)
20
+ end
21
+
19
22
  # @param [String] payload of an snmp v3 message which can be decoded
20
23
  # @param [NETSMP::SecurityParameters, #decode] security_parameters knowns how to decode the stream
21
24
  #
@@ -51,9 +54,7 @@ module NETSNMP
51
54
  log(level: 2) { asn_tree.to_hex }
52
55
  log(level: 2) { sec_params_asn.to_hex }
53
56
 
54
- # validate_authentication
55
57
  auth_param = auth_param.value
56
- security_parameters.verify(stream.sub(auth_param, AUTHNONE.value), auth_param, security_level: security_level)
57
58
 
58
59
  engine_boots = engine_boots.value.to_i
59
60
  engine_time = engine_time.value.to_i
@@ -65,6 +66,9 @@ module NETSNMP
65
66
 
66
67
  log { "received response PDU" }
67
68
  pdu = ScopedPDU.decode(encoded_pdu)
69
+ pdu.auth_param = auth_param
70
+ pdu.security_level = security_level
71
+
68
72
  log(level: 2) { pdu.to_hex }
69
73
  [pdu, engine_id.value, engine_boots, engine_time]
70
74
  end
@@ -86,7 +90,7 @@ module NETSNMP
86
90
  OpenSSL::ASN1::Integer.new(engine_boots).with_label(:engine_boots),
87
91
  OpenSSL::ASN1::Integer.new(engine_time).with_label(:engine_time),
88
92
  OpenSSL::ASN1::OctetString.new(security_parameters.username).with_label(:username),
89
- AUTHNONE,
93
+ authnone(security_parameters.auth_protocol),
90
94
  salt_param
91
95
  ]).with_label(:security_params)
92
96
  log(level: 2) { sec_params.to_hex }
@@ -115,10 +119,22 @@ module NETSNMP
115
119
  log { "signing V3 message..." }
116
120
  auth_salt = OpenSSL::ASN1::OctetString.new(signature).with_label(:auth)
117
121
  log(level: 2) { auth_salt.to_hex }
118
- encoded.sub!(AUTHNONE.to_der, auth_salt.to_der)
122
+ none_der = authnone(security_parameters.auth_protocol).to_der
123
+ encoded[encoded.index(none_der), none_der.size] = auth_salt.to_der
119
124
  log { Hexdump.dump(encoded) }
120
125
  end
121
126
  encoded
122
127
  end
128
+
129
+ private
130
+
131
+ # https://datatracker.ietf.org/doc/html/rfc7860#section-4.2.2 part 3
132
+ # https://datatracker.ietf.org/doc/html/rfc3414#section-6.3.2 part 3
133
+ def authnone(auth_protocol)
134
+ # The digest in the msgAuthenticationParameters field is replaced by the 12 zero octets.
135
+ # 24 octets for sha256
136
+ number_of_octets = auth_protocol == :sha256 ? 24 : 12
137
+ OpenSSL::ASN1::OctetString.new("\x00" * number_of_octets).with_label(:auth_mask)
138
+ end
123
139
  end
124
140
  end
data/lib/netsnmp/mib.rb CHANGED
@@ -33,7 +33,9 @@ module NETSNMP
33
33
  if idx
34
34
  mod = prefix[0..(idx - 1)]
35
35
  type = prefix[(idx + 2)..-1]
36
- return unless load(mod)
36
+ unless module_loaded?(mod)
37
+ return unless load(mod)
38
+ end
37
39
  else
38
40
  type = prefix
39
41
  end
@@ -84,6 +86,14 @@ module NETSNMP
84
86
  true
85
87
  end
86
88
 
89
+ def module_loaded?(mod)
90
+ if File.file?(mod)
91
+ @modules_loaded.include?(mod)
92
+ else
93
+ @modules_loaded.map { |path| File.basename(path, ".*") }.include?(mod)
94
+ end
95
+ end
96
+
87
97
  TYPES = ["OBJECT IDENTIFIER", "OBJECT-TYPE", "MODULE-IDENTITY"].freeze
88
98
 
89
99
  STATIC_MIB_TO_OID = {
data/lib/netsnmp/pdu.rb CHANGED
@@ -7,7 +7,7 @@ module NETSNMP
7
7
  class PDU
8
8
  using ASNExtensions
9
9
 
10
- MAXREQUESTID = 2147483647
10
+ MAXREQUESTID = 0x7fffffff
11
11
 
12
12
  using ASNExtensions
13
13
  class << self
@@ -6,6 +6,8 @@ module NETSNMP
6
6
 
7
7
  attr_reader :engine_id
8
8
 
9
+ attr_accessor :security_level, :auth_param
10
+
9
11
  def initialize(type:, headers:, **options)
10
12
  @engine_id, @context = headers
11
13
  super(type: type, headers: [3, nil], **options)
@@ -22,13 +22,13 @@ module NETSNMP
22
22
  # The 150 Seconds is specified in https://www.ietf.org/rfc/rfc2574.txt 2.2.3
23
23
  TIMELINESS_THRESHOLD = 150
24
24
 
25
- attr_reader :security_level, :username
25
+ attr_reader :security_level, :username, :auth_protocol
26
26
  attr_reader :engine_id
27
27
 
28
28
  # @param [String] username the snmp v3 username
29
29
  # @param [String] engine_id the device engine id (initialized to '' for report)
30
30
  # @param [Symbol, integer] security_level allowed snmp v3 security level (:auth_priv, :auth_no_priv, etc)
31
- # @param [Symbol, nil] auth_protocol a supported authentication protocol (currently supported: :md5, :sha)
31
+ # @param [Symbol, nil] auth_protocol a supported authentication protocol (currently supported: :md5, :sha, :sha256)
32
32
  # @param [Symbol, nil] priv_protocol a supported privacy protocol (currently supported: :des, :aes)
33
33
  # @param [String, nil] auth_password the authentication password
34
34
  # @param [String, nil] priv_password the privacy password
@@ -110,6 +110,12 @@ module NETSNMP
110
110
 
111
111
  key = auth_key.dup
112
112
 
113
+ # SHA256 => https://datatracker.ietf.org/doc/html/rfc7860#section-4.2.2
114
+ # The 24 first octets of HMAC are taken as the computed MAC value
115
+ return OpenSSL::HMAC.digest("SHA256", key, message)[0, 24] if @auth_protocol == :sha256
116
+
117
+ # MD5 => https://datatracker.ietf.org/doc/html/rfc3414#section-6.3.2
118
+ # SHA1 => https://datatracker.ietf.org/doc/html/rfc3414#section-7.3.2
113
119
  key << "\x00" * (@auth_protocol == :md5 ? 48 : 44)
114
120
  k1 = key.xor(IPAD)
115
121
  k2 = key.xor(OPAD)
@@ -120,6 +126,7 @@ module NETSNMP
120
126
 
121
127
  digest.reset
122
128
  digest << (k2 + d1)
129
+ # The 12 first octets of the digest are taken as the computed MAC value
123
130
  digest.digest[0, 12]
124
131
  end
125
132
 
@@ -204,6 +211,7 @@ module NETSNMP
204
211
  @digest ||= case @auth_protocol
205
212
  when :md5 then OpenSSL::Digest::MD5.new
206
213
  when :sha then OpenSSL::Digest::SHA1.new
214
+ when :sha256 then OpenSSL::Digest::SHA256.new
207
215
  else
208
216
  raise Error, "unsupported auth protocol: #{@auth_protocol}"
209
217
  end
@@ -75,7 +75,7 @@ module NETSNMP
75
75
 
76
76
  def initialize(host, port, timeout:)
77
77
  @socket = UDPSocket.new
78
- @socket.connect(host, port)
78
+ @destaddr = Socket.sockaddr_in(port, host)
79
79
  @timeout = timeout
80
80
  end
81
81
 
@@ -90,13 +90,13 @@ module NETSNMP
90
90
 
91
91
  def write(payload)
92
92
  perform_io do
93
- @socket.send(payload, 0)
93
+ @socket.sendmsg(payload, Socket::MSG_DONTWAIT | Socket::MSG_NOSIGNAL, @destaddr)
94
94
  end
95
95
  end
96
96
 
97
97
  def recv(bytesize = MAXPDUSIZE)
98
98
  perform_io do
99
- datagram, = @socket.recvfrom_nonblock(bytesize)
99
+ datagram, = @socket.recvmsg_nonblock(bytesize, Socket::MSG_DONTWAIT)
100
100
  datagram
101
101
  end
102
102
  end
@@ -24,14 +24,7 @@ module NETSNMP
24
24
  log { "sending request..." }
25
25
  encoded_request = encode(pdu)
26
26
  encoded_response = @transport.send(encoded_request)
27
- response_pdu, *args = decode(encoded_response)
28
- if response_pdu.type == 8
29
- varbind = response_pdu.varbinds.first
30
- if varbind.oid == "1.3.6.1.6.3.15.1.1.2.0" # IdNotInTimeWindow
31
- _, @engine_boots, @engine_time = args
32
- raise IdNotInTimeWindowError, "request timestamp is already out of time window"
33
- end
34
- end
27
+ response_pdu, * = decode(encoded_response)
35
28
  response_pdu
36
29
  end
37
30
 
@@ -85,7 +78,33 @@ module NETSNMP
85
78
  end
86
79
 
87
80
  def decode(stream, security_parameters: @security_parameters)
88
- @message_serializer.decode(stream, security_parameters: security_parameters)
81
+ return_pdu = @message_serializer.decode(stream, security_parameters: security_parameters)
82
+
83
+ pdu, *args = return_pdu
84
+
85
+ # usmStats: http://oidref.com/1.3.6.1.6.3.15.1.1
86
+ if pdu.type == 8
87
+ case pdu.varbinds.first.oid
88
+ when "1.3.6.1.6.3.15.1.1.1.0" # usmStatsUnsupportedSecLevels
89
+ raise Error, "Unsupported security level"
90
+ when "1.3.6.1.6.3.15.1.1.2.0" # usmStatsNotInTimeWindows
91
+ _, @engine_boots, @engine_time = args
92
+ raise IdNotInTimeWindowError, "Not in time window"
93
+ when "1.3.6.1.6.3.15.1.1.3.0" # usmStatsUnknownUserNames
94
+ raise Error, "Unknown user name"
95
+ when "1.3.6.1.6.3.15.1.1.4.0" # usmStatsUnknownEngineIDs
96
+ raise Error, "Unknown engine ID" unless @security_parameters.must_revalidate?
97
+ when "1.3.6.1.6.3.15.1.1.5.0" # usmStatsWrongDigests
98
+ raise Error, "Authentication failure (incorrect password, community or key)"
99
+ when "1.3.6.1.6.3.15.1.1.6.0" # usmStatsDecryptionErrors
100
+ raise Error, "Decryption error"
101
+ end
102
+ end
103
+
104
+ # validate_authentication
105
+ @message_serializer.verify(stream, pdu.auth_param, pdu.security_level, security_parameters: @security_parameters)
106
+
107
+ return_pdu
89
108
  end
90
109
  end
91
110
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NETSNMP
4
- VERSION = "0.4.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/sig/message.rbs CHANGED
@@ -2,8 +2,14 @@ module NETSNMP
2
2
  class Message
3
3
  prepend Loggable
4
4
 
5
+ def verify: (String stream, String auth_param, Integer? security_level, security_parameters: SecurityParameters) -> void
6
+
5
7
  def decode: (String stream, security_parameters: SecurityParameters) -> [ScopedPDU, String, Integer, Integer]
6
8
 
7
9
  def encode: (ScopedPDU pdu, security_parameters: SecurityParameters, ?engine_boots: Integer, ?engine_time: Integer) -> String
10
+
11
+ private
12
+
13
+ def authnone: (SecurityParameters::auth_protocol?) -> OpenSSL::ASN1::ASN1Data
8
14
  end
9
15
  end
data/sig/scoped_pdu.rbs CHANGED
@@ -1,6 +1,8 @@
1
1
  module NETSNMP
2
2
  class ScopedPDU < PDU
3
3
  attr_reader engine_id: String
4
+ attr_reader auth_param: String?
5
+ attr_reader security_level: Integer?
4
6
 
5
7
  private
6
8
 
@@ -4,11 +4,10 @@ module NETSNMP
4
4
 
5
5
  type security_level = :noauth | :auth_no_priv | :auth_priv | 0 | 1 | 3 | nil
6
6
 
7
- type auth_protocol = :md5 | :sha
7
+ type auth_protocol = :md5 | :sha | :sha256
8
8
  type priv_protocol = :des | :aes
9
9
 
10
10
 
11
- @auth_protocol: auth_protocol?
12
11
  @auth_password: String?
13
12
  @priv_protocol: priv_protocol?
14
13
  @priv_password: String?
@@ -18,6 +17,7 @@ module NETSNMP
18
17
  attr_reader security_level: security_level
19
18
  attr_reader username: String
20
19
  attr_reader engine_id: String
20
+ attr_reader auth_protocol: auth_protocol?
21
21
 
22
22
  def engine_id=: (String id) -> void
23
23
 
data/spec/client_spec.rb CHANGED
@@ -23,19 +23,19 @@ RSpec.describe NETSNMP::Client do
23
23
  let(:next_oid) { "1.3.6.1.2.1.1.6.0" }
24
24
  let(:walk_oid) { "1.3.6.1.2.1.1" }
25
25
  let(:set_oid) { "sysUpTime.0" } # sysUpTimeInstance
26
- let(:get_result) { "DEVICE-192.168.1.1" }
27
- let(:next_result) { "The Cloud" }
26
+ let(:get_result) { "zeus.snmplabs.com (you can change this!)" }
27
+ let(:next_result) { "San Francisco, California, United States" }
28
28
  let(:walk_result) do
29
- <<-WALK
30
- 1.3.6.1.2.1.1.1.0: Device description
31
- 1.3.6.1.2.1.1.2.0: 1.3.6.1.4.1.3454
32
- 1.3.6.1.2.1.1.3.0: Timeticks: (78171676) 9 days, 1:8:36.76
33
- 1.3.6.1.2.1.1.4.0: The Owner
34
- 1.3.6.1.2.1.1.5.0: DEVICE-192.168.1.1
35
- 1.3.6.1.2.1.1.6.0: The Cloud
36
- 1.3.6.1.2.1.1.7.0: 72
37
- 1.3.6.1.2.1.1.8.0: Timeticks: (0) 0 days, 0:0:0.0
38
- WALK
29
+ {
30
+ "1.3.6.1.2.1.1.1.0" => "Linux zeus 4.8.6.5-smp #2 SMP Sun Nov 13 14:58:11 CDT 2016 i686",
31
+ "1.3.6.1.2.1.1.2.0" => "1.3.6.1.4.1.8072.3.2.10",
32
+ "1.3.6.1.2.1.1.3.0" => /Timeticks: \(\d+\) \d+ days, \d+:\d+:\d+\.\d+/,
33
+ "1.3.6.1.2.1.1.4.0" => "SNMP Laboratories, info@snmplabs.com",
34
+ "1.3.6.1.2.1.1.5.0" => "zeus.snmplabs.com (you can change this!)",
35
+ "1.3.6.1.2.1.1.6.0" => "San Francisco, California, United States",
36
+ "1.3.6.1.2.1.1.7.0" => "72",
37
+ "1.3.6.1.2.1.1.8.0" => /Timeticks: \(\d+\) \d+ days, \d+:\d+:\d+\.\d+/
38
+ }
39
39
  end
40
40
  let(:set_oid_result) { 43 }
41
41
  end
@@ -52,19 +52,19 @@ RSpec.describe NETSNMP::Client do
52
52
  let(:next_oid) { "1.3.6.1.2.1.1.6.0" }
53
53
  let(:walk_oid) { "system" }
54
54
  let(:set_oid) { "sysUpTime.0" }
55
- let(:get_result) { "DEVICE-192.168.1.1" }
56
- let(:next_result) { "The Cloud" }
55
+ let(:get_result) { "zeus.snmplabs.com (you can change this!)" }
56
+ let(:next_result) { "San Francisco, California, United States" }
57
57
  let(:walk_result) do
58
- <<-WALK
59
- 1.3.6.1.2.1.1.1.0: Device description
60
- 1.3.6.1.2.1.1.2.0: 1.3.6.1.4.1.3454
61
- 1.3.6.1.2.1.1.3.0: Timeticks: (78171676) 9 days, 1:8:36.76
62
- 1.3.6.1.2.1.1.4.0: The Owner
63
- 1.3.6.1.2.1.1.5.0: DEVICE-192.168.1.1
64
- 1.3.6.1.2.1.1.6.0: The Cloud
65
- 1.3.6.1.2.1.1.7.0: 72
66
- 1.3.6.1.2.1.1.8.0: Timeticks: (0) 0 days, 0:0:0.0
67
- WALK
58
+ {
59
+ "1.3.6.1.2.1.1.1.0" => "Linux zeus 4.8.6.5-smp #2 SMP Sun Nov 13 14:58:11 CDT 2016 i686",
60
+ "1.3.6.1.2.1.1.2.0" => "1.3.6.1.4.1.8072.3.2.10",
61
+ "1.3.6.1.2.1.1.3.0" => /Timeticks: \(\d+\) \d+ days, \d+:\d+:\d+\.\d+/,
62
+ "1.3.6.1.2.1.1.4.0" => "SNMP Laboratories, info@snmplabs.com",
63
+ "1.3.6.1.2.1.1.5.0" => "zeus.snmplabs.com (you can change this!)",
64
+ "1.3.6.1.2.1.1.6.0" => "San Francisco, California, United States",
65
+ "1.3.6.1.2.1.1.7.0" => "72",
66
+ "1.3.6.1.2.1.1.8.0" => /Timeticks: \(\d+\) \d+ days, \d+:\d+:\d+\.\d+/
67
+ }
68
68
  end
69
69
  let(:set_oid_result) { 43 }
70
70
 
@@ -101,18 +101,18 @@ RSpec.describe NETSNMP::Client do
101
101
  let(:set_oid) { "sysUpTime.0" } # sysUpTimeInstance
102
102
  let(:walk_oid) { "1.3.6.1.2.1.1.9.1.3" }
103
103
  let(:get_result) { "tt" }
104
- let(:next_result) { "KK12" }
104
+ let(:next_result) { "KK12 (edit /etc/snmp/snmpd.conf)" }
105
105
  let(:walk_result) do
106
- <<-WALK
107
- 1.3.6.1.2.1.1.9.1.3.1: The SNMP Management Architecture MIB.
108
- 1.3.6.1.2.1.1.9.1.3.2: The MIB for Message Processing and Dispatching.
109
- 1.3.6.1.2.1.1.9.1.3.3: The management information definitions for the SNMP User-based Security Model.
110
- 1.3.6.1.2.1.1.9.1.3.4: The MIB module for SNMPv2 entities
111
- 1.3.6.1.2.1.1.9.1.3.5: The MIB module for managing TCP implementations
112
- 1.3.6.1.2.1.1.9.1.3.6: The MIB module for managing IP and ICMP implementations
113
- 1.3.6.1.2.1.1.9.1.3.7: The MIB module for managing UDP implementations
114
- 1.3.6.1.2.1.1.9.1.3.8: View-based Access Control Model for SNMP.
115
- WALK
106
+ {
107
+ "1.3.6.1.2.1.1.9.1.3.1" => "The SNMP Management Architecture MIB.",
108
+ "1.3.6.1.2.1.1.9.1.3.2" => "The MIB for Message Processing and Dispatching.",
109
+ "1.3.6.1.2.1.1.9.1.3.3" => "The management information definitions for the SNMP User-based Security Model.",
110
+ "1.3.6.1.2.1.1.9.1.3.4" => "The MIB module for SNMPv2 entities",
111
+ "1.3.6.1.2.1.1.9.1.3.5" => "The MIB module for managing TCP implementations",
112
+ "1.3.6.1.2.1.1.9.1.3.6" => "The MIB module for managing IP and ICMP implementations",
113
+ "1.3.6.1.2.1.1.9.1.3.7" => "The MIB module for managing UDP implementations",
114
+ "1.3.6.1.2.1.1.9.1.3.8" => "View-based Access Control Model for SNMP."
115
+ }
116
116
  end
117
117
  let(:set_oid_result) { 43 }
118
118
  context "with a no auth no priv policy" do
@@ -157,6 +157,15 @@ RSpec.describe NETSNMP::Client do
157
157
  let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
158
158
  end
159
159
  end
160
+ context "speaking sha256" do
161
+ let(:user_options) do
162
+ { username: "authsha256", security_level: :auth_no_priv,
163
+ auth_password: "maplesyrup", auth_protocol: :sha256 }
164
+ end
165
+ it_behaves_like "an snmp client" do
166
+ let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
167
+ end
168
+ end
160
169
  end
161
170
  context "with an auth priv policy" do
162
171
  context "auth in md5, encrypting in des" do
@@ -177,6 +186,18 @@ RSpec.describe NETSNMP::Client do
177
186
  end
178
187
  it_behaves_like "an snmp client" do
179
188
  let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
189
+
190
+ context "with wrong auth password and wrong encrypting password" do
191
+ let(:user_options) do
192
+ { username: "authprivmd5des", auth_password: "wrongpassword",
193
+ auth_protocol: :md5, priv_password: "maplesyrup",
194
+ priv_protocol: :des }
195
+ end
196
+ let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
197
+ it "raises authentication error" do
198
+ expect { subject.get(oid: get_oid) }.to raise_error(NETSNMP::Error, "Authentication failure (incorrect password, community or key)")
199
+ end
200
+ end
180
201
  end
181
202
  end
182
203
 
@@ -4,7 +4,7 @@ require "celluloid/io"
4
4
  require_relative "../support/request_examples"
5
5
  require_relative "../support/celluloid"
6
6
 
7
- RSpec.describe "with cellulloid", type: :celluloid, if: RUBY_ENGINE == "truffleruby" do
7
+ RSpec.describe "with cellulloid", type: :celluloid do
8
8
  include CelluloidHelpers
9
9
  let(:user_options) do
10
10
  { username: "authprivmd5des", auth_password: "maplesyrup",
@@ -17,18 +17,18 @@ RSpec.describe "with cellulloid", type: :celluloid, if: RUBY_ENGINE == "truffler
17
17
  let(:set_oid) { "1.3.6.1.2.1.1.3.0" } # sysUpTimeInstance
18
18
  let(:walk_oid) { "1.3.6.1.2.1.1.9.1.3" }
19
19
  let(:get_result) { "tt" }
20
- let(:next_result) { "KK12" }
20
+ let(:next_result) { "KK12 (edit /etc/snmp/snmpd.conf)" }
21
21
  let(:walk_result) do
22
- <<-WALK
23
- 1.3.6.1.2.1.1.9.1.3.1: The SNMP Management Architecture MIB.
24
- 1.3.6.1.2.1.1.9.1.3.2: The MIB for Message Processing and Dispatching.
25
- 1.3.6.1.2.1.1.9.1.3.3: The management information definitions for the SNMP User-based Security Model.
26
- 1.3.6.1.2.1.1.9.1.3.4: The MIB module for SNMPv2 entities
27
- 1.3.6.1.2.1.1.9.1.3.5: The MIB module for managing TCP implementations
28
- 1.3.6.1.2.1.1.9.1.3.6: The MIB module for managing IP and ICMP implementations
29
- 1.3.6.1.2.1.1.9.1.3.7: The MIB module for managing UDP implementations
30
- 1.3.6.1.2.1.1.9.1.3.8: View-based Access Control Model for SNMP.
31
- WALK
22
+ {
23
+ "1.3.6.1.2.1.1.9.1.3.1" => "The SNMP Management Architecture MIB.",
24
+ "1.3.6.1.2.1.1.9.1.3.2" => "The MIB for Message Processing and Dispatching.",
25
+ "1.3.6.1.2.1.1.9.1.3.3" => "The management information definitions for the SNMP User-based Security Model.",
26
+ "1.3.6.1.2.1.1.9.1.3.4" => "The MIB module for SNMPv2 entities",
27
+ "1.3.6.1.2.1.1.9.1.3.5" => "The MIB module for managing TCP implementations",
28
+ "1.3.6.1.2.1.1.9.1.3.6" => "The MIB module for managing IP and ICMP implementations",
29
+ "1.3.6.1.2.1.1.9.1.3.7" => "The MIB module for managing UDP implementations",
30
+ "1.3.6.1.2.1.1.9.1.3.8" => "View-based Access Control Model for SNMP."
31
+ }
32
32
  end
33
33
 
34
34
  before(:all) { Celluloid.boot }
@@ -13,6 +13,13 @@ RSpec.describe NETSNMP::SecurityParameters do
13
13
  subject { described_class.new(security_level: :auth_priv, auth_protocol: :sha, username: "username", engine_id: engine_id, auth_password: "maplesyrup", priv_password: "maplesyrup") }
14
14
  it { expect(subject.send(:passkey, password).b).to eq("\x9f\xb5\xcc\x03\x81\x49\x7b\x37\x93\x52\x89\x39\xff\x78\x8d\x5d\x79\x14\x52\x11".b) }
15
15
  end
16
+ context "sha256" do
17
+ subject do
18
+ described_class.new(security_level: :auth_priv, auth_protocol: :sha256, username: "username", engine_id: engine_id, auth_password: "maplesyrup", priv_password: "maplesyrup")
19
+ end
20
+
21
+ it { expect(subject.send(:passkey, password).b).to eq("\xABQ\x01M\x1E\a\x7F`\x17\xDF+\x12\xBE\xE5\xF5\xAAr\x991w\xE9\xBBV\x9CM\xFFZL\xA0\xB4\xAF\xAC".b) }
22
+ end
16
23
  end
17
24
 
18
25
  describe "keys" do
@@ -34,11 +41,22 @@ RSpec.describe NETSNMP::SecurityParameters do
34
41
  priv_password: password,
35
42
  engine_id: engine_id)
36
43
  end
44
+ let(:sha256_sec) do
45
+ described_class.new(security_level: :auth_priv,
46
+ auth_protocol: :sha256,
47
+ priv_protocol: :des,
48
+ username: "username",
49
+ auth_password: password,
50
+ priv_password: password,
51
+ engine_id: engine_id)
52
+ end
37
53
  it do
38
54
  expect(md5_sec.send(:auth_key)).to eq("\x52\x6f\x5e\xed\x9f\xcc\xe2\x6f\x89\x64\xc2\x93\x07\x87\xd8\x2b".b)
39
55
  expect(md5_sec.send(:priv_key)).to eq("\x52\x6f\x5e\xed\x9f\xcc\xe2\x6f\x89\x64\xc2\x93\x07\x87\xd8\x2b".b)
40
56
  expect(sha_sec.send(:auth_key)).to eq("\x66\x95\xfe\xbc\x92\x88\xe3\x62\x82\x23\x5f\xc7\x15\x1f\x12\x84\x97\xb3\x8f\x3f".b)
41
57
  expect(sha_sec.send(:priv_key)).to eq("\x66\x95\xfe\xbc\x92\x88\xe3\x62\x82\x23\x5f\xc7\x15\x1f\x12\x84\x97\xb3\x8f\x3f".b)
58
+ expect(sha256_sec.send(:auth_key)).to eq("\x89\x82\xE0\xE5I\xE8f\xDB6\x1Akb]\x84\xCC\xCC\x11\x16-E>\xE8\xCE:dE\xC2\xD6wo\x0F\x8B".b)
59
+ expect(sha256_sec.send(:priv_key)).to eq("\x89\x82\xE0\xE5I\xE8f\xDB6\x1Akb]\x84\xCC\xCC\x11\x16-E>\xE8\xCE:dE\xC2\xD6wo\x0F\x8B".b)
42
60
  end
43
61
  end
44
62
 
@@ -26,7 +26,7 @@ module CelluloidHelpers
26
26
  end
27
27
  end
28
28
 
29
- class Proxy < NETSNMP::Session::Transport
29
+ class Proxy
30
30
  MAXPDUSIZE = 0xffff + 1
31
31
 
32
32
  def initialize(host, port)
@@ -35,18 +35,22 @@ module CelluloidHelpers
35
35
  @timeout = 2
36
36
  end
37
37
 
38
- def close
39
- @socket.close
38
+ def send(payload)
39
+ @socket.send(payload, 0)
40
+ recv
40
41
  end
41
42
 
42
- private
43
-
44
- def wait(mode)
43
+ def recv(bytesize = MAXPDUSIZE)
45
44
  Celluloid.timeout(@timeout) do
46
- @socket.__send__(mode)
45
+ datagram, = @socket.recvfrom(bytesize)
46
+ datagram
47
47
  end
48
48
  rescue Celluloid::TaskTimeout
49
49
  raise Timeout::Error, "Timeout after #{@timeout} seconds"
50
50
  end
51
+
52
+ def close
53
+ @socket.close
54
+ end
51
55
  end
52
56
  end
@@ -22,8 +22,8 @@ RSpec.shared_examples "an snmp client" do
22
22
  let(:value) { subject.get({ oid: get_oid }, oid: next_oid) }
23
23
  it "returns the values for both" do
24
24
  expect(value).to be_a(Array)
25
- expect(value).to include(/#{get_result}/)
26
- expect(value).to include(/#{next_result}/)
25
+ expect(value).to include(get_result)
26
+ expect(value).to include(next_result)
27
27
  end
28
28
  end
29
29
  end
@@ -40,8 +40,17 @@ RSpec.shared_examples "an snmp client" do
40
40
  describe "#walk" do
41
41
  let(:value) { subject.walk(oid: walk_oid) }
42
42
  it "fetches the varbinds for the next oid" do
43
- values = value.map { |oid, val| "#{oid}: #{val}" }.join("\n") << "\n"
44
- expect(values).to eq(walk_result)
43
+ value.each do |oid, val|
44
+ match = walk_result[oid]
45
+ case match
46
+ when String
47
+ expect(val.to_s).to eq(match)
48
+ when Regexp
49
+ expect(val.to_s).to match(match)
50
+ else
51
+ next
52
+ end
53
+ end
45
54
  end
46
55
  end
47
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: netsnmp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-26 00:00:00.000000000 Z
11
+ date: 2021-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -104,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  version: '0'
105
105
  requirements:
106
106
  - net-snmp
107
- rubygems_version: 3.2.3
107
+ rubygems_version: 3.2.15
108
108
  signing_key:
109
109
  specification_version: 4
110
110
  summary: SNMP Client library