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

Constructor Details

#initialize(username:, engine_id: "", security_level: nil, auth_protocol: nil, auth_password: nil, priv_protocol: nil, priv_password: nil) ⇒ 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.

Parameters:

  • username (String)

    the snmp v3 username

  • engine_id (String) (defaults to: "")

    the device engine id (initialized to ” for report)

  • security_level (Symbol, integer) (defaults to: nil)

    allowed snmp v3 security level (:auth_priv, :auth_no_priv, etc)

  • auth_protocol (Symbol, nil) (defaults to: nil)

    a supported authentication protocol (currently supported: :md5, :sha, :sha256)

  • priv_protocol (Symbol, nil) (defaults to: nil)

    a supported privacy protocol (currently supported: :des, :aes)

  • auth_password (String, nil) (defaults to: nil)

    the authentication password

  • priv_password (String, nil) (defaults to: nil)

    the privacy password



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/netsnmp/security_parameters.rb', line 41

def initialize(
               username:,
               engine_id: "",
               security_level: nil,
               auth_protocol: nil,
               auth_password: nil,
               priv_protocol: nil,
               priv_password: nil
)
  @security_level = security_level
  @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?
  @auth_password = auth_password
  @priv_password = priv_password
  check_parameters
  @auth_pass_key = passkey(@auth_password) unless @auth_password.nil?
  @priv_pass_key = passkey(@priv_password) unless @priv_password.nil?
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.



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

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

Parameters:

  • der (String)

    the encoded der to be decoded

  • salt (String)

    the salt from the incoming der

  • engine_time (Integer)

    the reported engine time

  • engine_boots (Integer)

    the reported engine boots



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

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

  return asn unless encryption

  encrypted_pdu = asn.value
  pdu_der = encryption.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

Returns a pair, where the first argument in the asn structure with the encoded pdu, and the second is the calculated salt (if it has been encrypted).

Parameters:

  • pdu (#to_asn, #to_der)

    the pdu to encode (must quack like a asn1 type)

  • salt (String)

    the salt to use

  • engine_time (Integer)

    the reported engine time

  • engine_boots (Integer)

    the reported boots time

Returns:

  • (Array)

    a pair, where the first argument in the asn structure with the encoded pdu, and the second is the calculated salt (if it has been encrypted)



74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/netsnmp/security_parameters.rb', line 74

def encode(pdu, salt:, engine_time:, engine_boots:)
  if encryption
    encrypted_pdu, salt = encryption.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

Returns:

  • (Boolean)


144
145
146
147
148
# File 'lib/netsnmp/security_parameters.rb', line 144

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.

Parameters:

  • message (String)

    the already encoded snmp v3 message

Returns:

  • (String)

    the digest signature of the message payload



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

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

Parameters:

  • stream (String)

    the encoded incoming payload

  • salt (String)

    the incoming payload”s salt

Raises:

  • (NETSNMP::Error)

    if the message’s integration has been violated



137
138
139
140
141
142
# File 'lib/netsnmp/security_parameters.rb', line 137

def verify(stream, salt, security_level: @security_level)
  return if security_level < 1
  verisalt = sign(stream)
  raise Error, "invalid message authentication salt" unless verisalt == salt
  log(level: 2) { "message has been verified" }
end