Class: NETSNMP::SecurityParameters

Inherits:
Object
  • Object
show all
Includes:
Loggable
Defined in:
lib/netsnmp/security_parameters.rb

Overview

This module encapsulates the public API for encrypting/decrypting and signing/verifying.

It doesn’t interact with other layers from the library, rather it is used and passed all the arguments (consisting mostly of primitive types). It also provides validation of the security options passed with a client is initialized in v3 mode.

Constant Summary collapse

IPAD =
"\x36" * 64
OPAD =
"\x5c" * 64
TIMELINESS_THRESHOLD =

Timeliness is part of SNMP V3 Security The topic is described very nice here www.snmpsharpnet.com/?page_id=28 www.ietf.org/rfc/rfc2574.txt 1.4.1 Timeliness The probe is outdated after 150 seconds which results in a PDU Error, therefore it should expire before that and be renewed The 150 Seconds is specified in www.ietf.org/rfc/rfc2574.txt 2.2.3

150

Constants included from Loggable

Loggable::DEBUG, Loggable::DEBUG_LEVEL

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Loggable

#initialize_logger

Constructor Details

#initialize(username:, engine_id: "", security_level: nil, auth_protocol: nil, auth_password: nil, priv_protocol: nil, priv_password: nil, **options) ⇒ SecurityParameters

Note:

if security level is set to :no_auth_no_priv, all other parameters are optional; if :auth_no_priv, :auth_protocol will be coerced to :md5 (if not explicitly set), and :auth_password is mandatory; if :auth_priv, the sentence before applies, and :priv_protocol will be coerced to :des (if not explicitly set), and :priv_password becomes mandatory.

Returns a new instance of SecurityParameters.



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
# File 'lib/netsnmp/security_parameters.rb', line 40

def initialize(
  username:,
  engine_id: "",
  security_level: nil,
  auth_protocol: nil,
  auth_password: nil,
  priv_protocol: nil,
  priv_password: nil,
  **options
)
  @security_level = case security_level
                    when /no_?auth/         then 0
                    when /auth_?no_?priv/   then 1
                    when /auth_?priv/       then 3
                    when Integer then security_level
                    else 3 # rubocop:disable Lint/DuplicateBranch
                    end
  @username = username
  @engine_id = engine_id
  @auth_protocol = auth_protocol.to_sym unless auth_protocol.nil?
  @priv_protocol = priv_protocol.to_sym unless priv_protocol.nil?

  if @security_level.positive?
    @auth_protocol ||= :md5 # this is the default
    raise "security level requires an auth password" if auth_password.nil?
    raise "auth password must have between 8 to 32 characters" unless (8..32).cover?(auth_password.length)
  end

  if @security_level > 1
    @priv_protocol ||= :des
    raise "security level requires a priv password" if priv_password.nil?
    raise "priv password must have between 8 to 32 characters" unless (8..32).cover?(priv_password.length)
  end

  @auth_pass_key = passkey(auth_password) if auth_password
  @priv_pass_key = passkey(priv_password) if priv_password
  initialize_logger(**options)
end

Instance Attribute Details

#auth_protocolObject (readonly)

Returns the value of attribute auth_protocol.



25
26
27
# File 'lib/netsnmp/security_parameters.rb', line 25

def auth_protocol
  @auth_protocol
end

#engine_idObject

Returns the value of attribute engine_id.



25
26
27
# File 'lib/netsnmp/security_parameters.rb', line 25

def engine_id
  @engine_id
end

#security_levelObject (readonly)

Returns the value of attribute security_level.



25
26
27
# File 'lib/netsnmp/security_parameters.rb', line 25

def security_level
  @security_level
end

#usernameObject (readonly)

Returns the value of attribute username.



25
26
27
# File 'lib/netsnmp/security_parameters.rb', line 25

def username
  @username
end

Instance Method Details

#decode(der, salt:, engine_time:, engine_boots:, security_level: @security_level) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/netsnmp/security_parameters.rb', line 110

def decode(der, salt:, engine_time:, engine_boots:, security_level: @security_level)
  asn = OpenSSL::ASN1.decode(der)
  return asn if security_level < 3

  encryptor = encryption
  return asn unless encryptor

  encrypted_pdu = asn.value
  pdu_der = encryptor.decrypt(encrypted_pdu, salt: salt, engine_time: engine_time, engine_boots: engine_boots)
  log(level: 2) { "message has been decrypted" }
  OpenSSL::ASN1.decode(pdu_der)
end

#encode(pdu, salt:, engine_time:, engine_boots:) ⇒ Array



91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/netsnmp/security_parameters.rb', line 91

def encode(pdu, salt:, engine_time:, engine_boots:)
  encryptor = encryption

  if encryptor
    encrypted_pdu, salt = encryptor.encrypt(pdu.to_der, engine_boots: engine_boots,
                                                        engine_time: engine_time)
    [
      OpenSSL::ASN1::OctetString.new(encrypted_pdu).with_label(:encrypted_pdu),
      OpenSSL::ASN1::OctetString.new(salt).with_label(:salt)
    ]
  else
    [pdu.to_asn, salt]
  end
end

#must_revalidate?Boolean



166
167
168
169
170
171
# File 'lib/netsnmp/security_parameters.rb', line 166

def must_revalidate?
  return @engine_id.empty? unless authorizable?
  return true if @engine_id.empty? || @timeliness.nil?

  (Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) - @timeliness) >= TIMELINESS_THRESHOLD
end

#sign(message) ⇒ String

Note:

this method is used in the process of authenticating a message

Returns the digest signature of the message payload.



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
# File 'lib/netsnmp/security_parameters.rb', line 127

def sign(message)
  # don't sign unless you have to
  return unless @auth_protocol

  key = auth_key.dup

  # SHA256 => https://datatracker.ietf.org/doc/html/rfc7860#section-4.2.2
  # The 24 first octets of HMAC are taken as the computed MAC value
  return OpenSSL::HMAC.digest("SHA256", key, message)[0, 24] if @auth_protocol == :sha256

  # MD5 => https://datatracker.ietf.org/doc/html/rfc3414#section-6.3.2
  # SHA1 => https://datatracker.ietf.org/doc/html/rfc3414#section-7.3.2
  key << ("\x00" * (@auth_protocol == :md5 ? 48 : 44))
  k1 = key.xor(IPAD)
  k2 = key.xor(OPAD)

  digest.reset
  digest << (k1 + message)
  d1 = digest.digest

  digest.reset
  digest << (k2 + d1)
  # The 12 first octets of the digest are taken as the computed MAC value
  digest.digest[0, 12]
end

#verify(stream, salt, security_level: @security_level) ⇒ Object

Raises:

  • (NETSNMP::Error)

    if the message’s integration has been violated



157
158
159
160
161
162
163
164
# File 'lib/netsnmp/security_parameters.rb', line 157

def verify(stream, salt, security_level: @security_level)
  return if security_level.nil? || security_level < 1

  verisalt = sign(stream)
  raise Error, "invalid message authentication salt" unless verisalt == salt

  log(level: 2) { "message has been verified" }
end