This is the first of two articles describing the security features of the SNMP version 3 protocol. The SNMPv3 RFCs describe a new framework that is used for defining the relationships between the SNMP versions 1, 2, and 3 specifications. This framework is partitioned in a modular fashion and is heavily based on previous work (i.e. SNMPv1, SNMPv2c, SNMPv2u, and SNMPv2*).
Two core modules within the framework are the User-based Security Model (USM) and the View-based Access Control Model (VACM). The USM is in charge of authenticating/encrypting/decrypting SNMP packets and the VACM is in charge of administering access to MIB data. This article gives an overview of the USM, and next month we will take a look at the VACM.
The USM is specified in RFC 2574. A weak point of all the previous well known SNMP versions has been the lack of a solid, agreed upon, security scheme. In designing the USM, these classic security threats had to be addressed:
The USM is able to protect SNMPv3 packets from the above threats by utilizing a concept of multiple users where each user provides secret keys for authentication and privacy. The authentication protocols specified for use are HMAC-MD5 and HMAC-SHA. The privacy protocol specified is CBC-DES. The RFC states that the security protocols used for the USM are considered acceptably secure at the time of its writing. However, the model allows for new authentication and privacy protocols to be specified at a future time if the need arises.
The SNMPv3 framework introduces many new concepts. Most of these concepts are used by multiple modules within the framework. Here are a few that are used by the USM:
SNMPv3 Packet Format
-------------------------
/|\ | msgVersion |
| |-----------------------|
| | msgID |
| |-----------------------| USM Security Parameters
| | msgMaxSize |
| |-----------------------| /-------------------------------
| | msgFlags | / | msgAuthoritativeEngineID |
scope of |-----------------------| / |-----------------------------|
authentication | msgSecurityModel | / | msgAuthoritativeEngineBoots |
| |-----------------------|/ |-----------------------------|
| | | | msgAuthoritativeEngineTime |
| | msgSecurityParameters | |-----------------------------|
| | | | msgUserName |
| |-----------------------|\ |-----------------------------|
| /|\ | | \ | msgAuthenticationParameters |
| | | | \ |-----------------------------|
| | | | \ | msgPrivacyParameters |
| scope of | scopedPDU | \-------------------------------
| encryption | |
| | | |
| | | |
| | | |
\|/ \|/ | |
-------------------------
The SNMP packet structure for version 3 has been changed to accommodate the use of a security model like the USM.
The USM uses the msgSecurityParameters to hold five values. These values are used by the authentication module to ensure data integrity and origin authentication, by the timeliness module to protect against message delay or replay, and by the privacy module to protect against message payload disclosure.
As mentioned above, the USM requires that the snmpEngineID, snmpEngineBoots, and snmpEngineTime of the authoritative engine be placed in the msgSecurityParameters. This requires the non-authoritative engine (i.e. manager) to know these values for the authoritative engine (i.e. agent) before a GET, NEXT, or SET operation can be completed.
This is achieved by a discovery process. There are two discovery transactions that occur. The first is to discover the snmpEngineID of the agent. The second is to discover the snmpEngineBoots and snmpEngineTime. The second transaction is only needed if the manager wants to use a security level of authNoPriv or authPriv. This is because the msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime are used by the timeliness module which is part of the authentication process.
The first discovery transaction is initiated by the manager sending an SNMPv3 packet with the msgAuthoritativeEngineID containing a bogus value. When the agent receives a packet where the msgAuthoritativeEngineID is different than its own, the packet is discarded and a discovery packet is returned to the manager. The returned discovery packet contains the correct snmpEngineID which must be used by the manager.
The second discovery transaction requires an authenticated packet be sent to the agent. This means that the authentication flag is set in the msgFlags, and the msgAuthenticationParameters contains the computed message digest for the packet. The secret key used for authenticating the packet is from the user specified in msgUserName. What makes this a discovery packet is that the msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime contain bogus values. When the agent receives this packet, it is first authenticated. Once the authentication is completed, the msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime values are checked. Since the values are bogus, the packet is discarded and a second discovery packet is returned to the manager. The returned discovery packet is authenticated, using the same user, and contains the correct values of the snmpEngineBoots and snmpEngineTime which must be used by the manager.
Once a manager has learned the snmpEngineBoots and snmpEngineTime of an agent, the manager must maintain its own local notion of what these values are supposed to be. This requires the manager to increment the learned snmpEngineTime every second so the value will be very close to the master values maintained by the agent. If the snmpEngineTime rolls over, then the snmpEngineBoots must be incremented. A manager must keep local notions of these values for each agent in which it wishes to communicate.
The timeliness checks by an agent are considered part of the authentication process and are done right after the received packet has been authenticated. If the msgAuthoritativeEngineBoots is different than the agent's current value of the snmpEngineBoots, the packet is discarded and a discovery packet is sent back to the manager. If that check passes, then the msgAuthoritativeEngineTime is checked against the agent's current value of the snmpEngineTime. If the difference between the two is more or less than 150 seconds, the packet is discarded and a discovery packet is sent back to the manager. If both of the checks pass, then the packet is considered to have been received in a timely manner and processing continues.
The value of +/- 150 seconds for the comparison of the snmpEngineTime is the default value specified by the RFC. This value could be modified to something more suitable based on the speed and size of your network.
The USM specifies the use of the Message Digest 5 (MD5) and Secure Hash Algorithm 1 (SHA-1) algorithms for authenticating SNMPv3 packets. These algorithms are used to create unique fixed sized message digests, also called digital signatures or fingerprints, of a variable length message. MD5 creates a digest of 128 bits (16 bytes) and SHA-1 creates a digest of 160 bits (20 bytes). Both MD5 and SHA-1 cannot be used directly as a message authentication code because they do not use secret keys as input to derivate the computed message digest. This is why the Keyed Hashing for Message Authentication (HMAC) algorithm in combination with MD5 and SHA-1 is used for computing message digests. The HMAC algorithm defines a procedure for appending a secret key to the data and then computing the MD5 or SHA-1 message digest. This guarantees that parties who wish to compute identical message digests for the same block of data must share a common secret key. Here are the steps that are taken for sending and receiving an authenticated SNMPv3 packet:
Sending an authenticated SNMPv3 packet:
Receiving an authenticated SNMPv3 packet:
Sending an encrypted SNMPv3 packet:
Receiving an encrypted SNMPv3 packet:
The User Table has a spin lock associated with it named usmUserSpinLock. A spin lock is an advisory lock which is used to allow several SNMP managers to coordinate their attempts in modifying a MIB table. The concept is simple. Whenever a change is going to be made to the User Table on an agent, the value of the usmUserSpinLock must be retrieved via a GET command by the manager. Then the manager sends a SET command to the agent which contains a PDU to set the usmUserSpinLock and whatever variables in the User Table that need to be modified. The value of the usmUserSpinLock in the SET command must be the value that was retrieved in the previous GET command. Once the agent receives the message containing the SET command, the usmUserSpinLock is immediately processed. If the current value of the usmUserSpinLock is not the same as the value specified in the SET command, the SET operation has failed and an error is returned. If the two values are the same, then the User Table variables specified in the PDU of the SET command are set. When the agent has finished processing the entire PDU, the usmUserSpinLock value is incremented by one. As you can see, if the value of the usmUserSpinLock is different when it is being set, then the User Table has been modified since the initial retrieval of the usmUserSpinLock value.
A localized key is a concept that allows the use of the same password for a user on many different agents. It would be very cumbersome to be forced to remember a different password for each agent the user exists. The localized key is computed using a one way hash function (e.g. MD5 or SHA-1) against a secret password and the snmpEngineID of the agent where the user exists. This results in secret keys that, although use the same password, are completely different for each agent.
Manager:
Agent: (after receiving the KeyChange SET operation)
It is possible to create a new user in an agent's User Table. This is performed by cloning an existing user. All the data for the clone-from user is copied to the newly created entry for the new user. Once the new user entry has been created, it is suggested to immediately change the keys. Note that you can only create a new user by cloning from another existing user. This requires that the User Table for a particular agent must be initialized to contain the minimum set of users needed in your environment.
Using your SNMPv3 manager of choice, here is an example of how to create a new user from an existing user named foo and changing the secret keys. Note that the actual name of the new user being created is not shown here because the name is specified in the OID index of the variables which are being set. The User Table is indexed by the user name.
First get the spin lock value and create the user. To ensure the new user entry cannot be immediately used, set the status to createAndWait.
sValue = GET (usmUserSpinLock)
SET((usmUserSpinLock = sValue),
(usmUserCloneFrom = foo),
(usmUserStatus = createAndWait))
Next, change the secret privacy key. Note you must know the secret privacy key
for foo to compute the KeyChange. The privKeyChange value is computed
using the privacy key of the clone-from user and the new secret key to be used
for the new user. Make sure to use the spin lock. Also, the usmUserPublic
variable can be written as part of the procedure for changing a user's secret
key, and later read to determine whether the change of the secret was
completed.
sValue = GET(usmUserSpinLock)
SET((usmUserSpinLock = sValue),
(usmUserPrivKeyChange = privKeyChange),
(usmUserPublic = randomValue))
if (randomValue != GET(usmUserPublic)) then try again
If the new user is never going to use encryption, then you can set the
privProtocol to usmNoPrivProtocol and not bother with changing the privacy key.
sValue = GET(usmUserSpinLock)
SET((usmUserSpinLock = sValue),
(usmUserPrivProtocol = usmNoPrivProtocol))
Now change the secret authentication key. Note you must know the secret
authentication key for foo to compute the KeyChange. The authKeyChange
value is computed using the authentication key of the clone-from user and the
new secret key to be used for the new user. Once again, make sure to use the
spin lock.
sValue = GET(usmUserSpinLock)
SET((usmUserSpinLock = sValue),
(usmUserAuthKeyChange = authKeyChange),
(usmUserPublic = randomValue))
if (randomValue != GET(usmUserPublic)) then try again
If the new user is never going to use authentication, then you can set the
authProtocol to usmNoAuthProtocol and not bother with changing the
authentication key.
sValue = GET(usmUserSpinLock)
SET((usmUserSpinLock = sValue),
(usmUserAuthProtocol = usmNoAuthProtocol))
Finally, activate this new user by setting the status to active.
sValue = GET(usmUserSpinLock)
SET((usmUserSpinLock = sValue),
(usmUserStatus = active))
This overview of the USM should give you an understanding of what is involved in authenticating and encrypting/decrypting SNMPv3 packets. In addition, you can start to think about how you will configure the agents in your environment when you deploy SNMPv3 capable routers, switches, and servers. Next month I will take a look at the VACM and show how it is used to administer access to MIB data.