Java Dynamic Management Kit 5.0 Tutorial

Chapter 20 Security Mechanisms in the SNMP Toolkit

Both the SNMP protocol adaptor and the SNMP manager API provide mechanisms for ensuring the security of management operations. Under SNMPv1 and SNMPv2, agents act as information servers, and IP-based access control is used to protect this information from unauthorized access. The SNMPv3 protocol provides much more sophisticated security mechanisms, implementing a user-based security model (USM), allowing both authentication and encryption of the requests sent between agents and their managers, as well as user-based access control.

The complete source code for these examples is available in subdirectories of the examplesDir/Snmp/ directory (see “Directories and Classpath” in the Preface).

This chapter covers the following topics:

IP-Based Access Control Lists

For the SNMP adaptor, the Java Dynamic Management Kit (DMK) provides access control based on the IP address and community of the manager's host machine. Information on the access rights for communities and host machines is stored in access control lists (InetAddressAcl). The default implementation provided with the product uses an InetAddressAcl file, but you can provide your own implementation as described in Custom Access Control.

The InetAddressAcl mechanism can also be used to define the communities and managers to which the agent will send traps. When you rely on the InetAddressAcl trap group, the agent will send traps to all hosts listed in the InetAddressAcl file. See Specifying the Trap Destination for the different ways that an agent application can send traps.

The following code example gives the contents of the examplesDir/Snmp/Agent/jdmk.acl file used when running the SNMP example applications. When using it in the security examples, you should replace yourmanager with the complete IP address or hostname of the host running your SNMP manager application.


Example 20–1 jdmk.acl File

acl = {
 {
 communities = public
 access = read-only
 managers = yourmanager
 }
 {
 communities = private
 access = read-write
 managers = yourmanager
 } 
} 

trap = {
  {
  trap-community = public
  hosts = yourmanager
  }
}

InetAddressAcl File Format

An InetAddressAcl file contains an acl group defining community and manager access rights and a trap group defining the community and hosts for sending traps.

Format of the acl Group

The acl group contains one or more access configurations.

acl = {
   access1
   access2
     ...
   accessN
}

Each access configuration has the following format:

{
   communities = communityList
   access = accessRights
   managers = hostList
}

The communityList is a list of SNMP community names to which this access control applies. The community names in this list are separated by commas.

The accessRights specifies the rights to be granted to all managers connecting from the hosts specified in the hostList. There are two possible values: either read-write or read-only.

The hostList item gives the hosts of the managers to be granted the access rights. The hostList is a comma-separated list of hosts, each of which can be expressed as any one of the following:

The set of all access configurations defines the access policy of the SNMP agent. A manager whose host is specified in a hostList and that identifies itself in one of the communities of the same configuration will be granted the permissions defined by the corresponding accessRights. A manager's host can appear in several access configurations provided it is associated with a different community list. This will define different access communities with different rights from the same manager.

A manager whose host-community identification pair does not appear in any of the access configurations will be denied all access. This means that protocol data units (PDU) from this manager will be dropped without being processed.

Format of the Trap Group

The trap group specifies the hosts to which the agent will send traps if the InetAddressAcl mechanism is used. This group contains one or more trap community definitions.

trap = {
   community1
   community2
   ...
   communityN
}

Each community definition defines the association between a set of hosts and the SNMP community string in the traps to be sent to them. Each trap definition has the following format:

{
   trap-community = trapCommunityName
   hosts = trapHostList
}

The trapCommunityName item specifies a single SNMP community string. It will be included in the traps sent to the hosts specified in the hosts item. SNMPv3 does use the community string, so use IP addresses or the context name instead.

The trapHostList item specifies a comma-separated list of hosts. Each host must be identified by its name or complete IP address.

When the SNMP protocol adaptor is instructed to send a trap using the InetAddressAcl mechanism, it will send a trap to every host listed in the trap community definitions. If a host is present in more than one list, it will receive more than one trap, each one identified by its corresponding trap community.

Format of the Inform Group

The inform group specifies the hosts to which your agent or manager will send informs if the InetAddressAcl mechanism is used. This group contains one or more inform community definitions.

inform = {
   community1
   community2
   ...
   communityN
}

Each community definition defines the association between a set of hosts and the SNMP community string in the informs to be sent to them. Each inform definition has the following format:

{
   inform-community = informCommunityName
   hosts = informHostList
}

The informCommunityName item specifies a single SNMP community string. It will be included in the informs sent to the hosts specified in the hosts item. SNMPv3 does use the community string, so use IP addresses or the context name instead.

The informHostList item specifies a comma-separated list of hosts. Each host must be identified by its name or complete IP address.

When the SNMP protocol adaptor is instructed to send an inform using the InetAddressAcl mechanism, it will send an inform to every host listed in the inform community definitions. If a host is present in more than one list, it will receive more than one inform, each one identified by its corresponding inform community.

Enabling InetAddressAcl

The default InetAddressAcl mechanism provided with the Java DMK relies on an InetAddressAcl file to define the access rights and trap recipients. To enable access control with this mechanism, you must first write an InetAddressAcl file to reflect the access and trap policy of your SNMP agent. Then, there are two ways to enable file-based access control, one way to modify the file in use and one way to disable access control.

The simplest way of enabling access control and traps is to ensure that an InetAddressAcl file exists when the SNMP protocol adaptor MBean is instantiated. To be automatically detected, the InetAddressAcl file must be named jdmk.acl and must be located in the configuration directory of the Java DMK installation. On UNIX systems with a standard installation of the product, the configuration directory is owned by root and requires superuser privileges to write or modify the InetAddressAcl file.

Operating Environment 

Configuration Directory 

Solaris 

installDir/SUNWjdmk/jdmk5.0/etc/conf/

Windows 2000 

installDir\SUNWjdmk\jdmk5.0\etc\conf\

The other way of enabling file-based access control is to specify a different file through the jdmk.acl.file system property. The filename associated with the property will override any InetAddressAcl file in the configuration directory. This property can be set programmatically, but it is usually done on the command line when starting your agent. For example, if the full pathname of your InetAddressAcl file is MyAclFile, use this command to start the agent with SNMP access control enabled:


$ java -classpath classpath -Djdmk.acl.file=MyAclFile MyAgent

If an InetAddressAcl file exists, the access rights it defines apply to all management applications that access the agent through its SNMP adaptor. This includes managers on the agent's local host: the InetAddressAcl groups must explicitly give permissions to localhost or the host's name or IP address for such managers. If the InetAddressAcl file does not exist when the SNMP adaptor is instantiated, either in the configuration directory or defined as a property, all SNMP requests will be processed, and traps will be sent only to the local host.

The InetAddressAcl file-based mechanism relies on the JdmkAcl class to provide the access control functionality. This is the class that is initialized with the contents of the InetAddressAcl file. This class provides the rereadTheFile method to reset the access control and trap lists with the contents of the InetAddressAcl file. This method will reload the same file that was used originally, regardless of any new property definitions. After you have updated the InetAddressAcl file, call the following methods to update the access control lists:

// assuming mySnmpAdaptor is declared as an SnmpAdaptorServer object
JdmkAcl theAcl = (JdmkAcl)(mySnmpAdaptor.getInetAddressAcl());
theAcl.rereadTheFile();

The JdmkAcl class that is used by default might not be suitable for all environments. For example, it relies on the java.security.acl package that is not available in the PersonalJavaTM runtime environment. Therefore, one constructor of the SnmpAdaptorServer class lets you override this default, forcing the adaptor not to use access control, regardless of any existing InetAddressAcl file. If you specify false for the useAcl parameter of this constructor, the SNMP adaptor will not even search for an InetAddressAcl file. In this case, no access control is performed, as if there were no InetAddressAcl file: all SNMP requests will be processed, and traps will be sent only to the local host. For security considerations, the use of access control cannot be toggled once the SNMP adaptor has been instantiated.

Custom Access Control

The JdmkAcl class that relies on an ACL file is the default access control mechanism in the SNMP adaptor. For greater adaptability, the SnmpAdaptorServer class has constructors that let you specify your own access control mechanism. For example, if your agent runs on a device with no file system, you could implement a mechanism that does not rely on the jdmk.acl file.

To instantiate an SNMP adaptor with your own access control, use one of the constructors that takes an acl parameter of the type InetAddressAcl. Note that if this parameter's value is null, or if you use a constructor that does not specify an acl parameter, the SNMP adaptor will use the JdmkAcl class by default. If you want to instantiate an SNMP adaptor without access control, call the constructor with the useAcl parameter set to false.

Your access control mechanism must be a class that implements the InetAddressAcl interface. This interface specifies the methods that the SNMP adaptor uses to check permissions when processing a request. If you instantiate the SNMP adaptor with your access control class, the adaptor will call your implementation of the access control methods. Again, for security reasons, the InetAddressAcl implementation in use cannot be changed once the SNMP adaptor has been instantiated.

The JdmkAcl class implements the default access mechanism that uses the jdmk.acl file. It is also an implementation of the InetAddressAcl interface, and it provides a few other methods, such as rereadTheFile, to control the InetAddressAcl mechanism.

SNMPv3 User-Based Access Control

The user-based access control implemented by SNMPv3 is based on contexts and user names, rather than on IP addresses and community strings. It is a partial implementation of the view-based access control model (VACM) defined in SNMP RFC 2575.

The users, contexts and associated security information controlling access to the agents in an SNMP session are defined in the jdmk.uacl file, as shown in the following example, taken from the examplesDir/Snmp/Agent directory:


Example 20–2 jdmk.uacl File

acl = {
# {
# context-names = TEST-CONTEXT
# access = read-write
# security-level=authNoPriv
# users = defaultUser
# }

} 

In the jdmk.uacl file, you define the following:

By uncommenting the acl block in this example, you would limit access to MIBs in the TEST-CONTEXT context only, and grant read-write access to the user defaultUser. The security level in the file must also match that of user defaultUser. Therefore, any non-authenticated requests, any request with different security levels, or any requests from a user other than defaultUser, would be rejected.

Enabling User-Based Access Control

To enable user-based access control, you must create a UserAcl file. You must then direct the agent applying the access control to look in this file.

The simplest way of enabling access control and traps is to ensure that a user-based access control file UserAcl file exists when the SNMP protocol adaptor MBean is instantiated. To be automatically detected, the UserAcl file must be named jdmk.uacl and must be located in the configuration directory of the Java DMK installation. On UNIX systems with a standard installation of the product, the configuration directory is owned by root and requires superuser privileges to write or modify the UserAcl file.

Operating Environment 

Configuration Directory 

Solaris 

installDir/SUNWjdmk/jdmk5.0/etc/conf/

Windows 2000 

installDir\SUNWjdmk\jdmk5.0\etc\conf\

The other way of enabling file-based access control is to specify a different file through the jdmk.uacl.file system property. The filename associated with the property will override any UserAcl file in the configuration directory. This property can be set programmatically, but it is usually done on the command line when starting your agent. For example, if the full pathname of your UserAcl file is MyUaclFile, use this command to start the agent with SNMP access control enabled:


$ java -classpath classpath -Djdmk.uacl.file=MyUaclFile MyAgent

If a UserAcl file exists, the access rights it defines apply to all management applications that access the agent through its SNMP adaptor. If the UserAcl file does not exist when the SNMP adaptor is instantiated, either in the configuration directory or defined as a property, all SNMP requests will be accepted.

The UserAcl file-based mechanism relies on the SnmpEngineParameters class to provide the access control functionality. This is the class that is initialized with the contents of the UserAcl file. This class provides the rereadTheFile method to reset the access control and trap lists with the contents of the UserAcl file. This method will reload the same file that was used originally, regardless of any new property definitions. After you have updated the UserAcl file, call the following method to update the access control lists:

Uacl.rereadTheFile

The following procedure demonstrates how to enable Uacl, using the example of the simple synchronous manager we saw in Synchronous SNMPv3 Managers and the simple SNMPv3 agent from Example 18–2.

To Run a Simple Manager with Access Control
  1. If you have not already done so, build and compile the AgentV3 example in examplesDir/Snmp/Agent.

    Type the following commands:


    $ mibgen -mo -d . mib_II.txt
    $ javac -classpath classpath -d . *.java
    
  2. Start the AgentV3 example in its Agent directory, this time pointing it to its associated jdmk.uacl file, as well as to its jdmk.security file.


    $ java -classpath classpath 
    -Djdmk.security.file=./jdmk.security -Djdmk.uacl.file=./jdmk.uacl AgentV3
    
  3. If you have not already done so, in a separate window, compile the SyncManagerV3 example in examplesDir/Snmp/Manager.


    $ javac -classpath classpath -d . *.java
    
  4. Start the SyncManagerV3 SNMP manager in its Manager directory, specifying the agent's host and port.

    This is the manager we configured to communicate with AgentV3 in To Run the SyncManagerV3 Example. As before, we set the host to localhost and the port to 8085.


    $ java -classpath classpath 
    -Djdmk.security.file=./jdmk.security SyncManagerV3 localhost 8085
    

    You should see the following error message:


    SyncManagerV3::main:Send get request to SNMP agent 
    on localhost at port 8085
    Error status = authorizationError
    Error index = -1
    

    The agent refuses the manager's request because the level of security for this agent in the manager's jdmk.security file does not match the level of security set in the agent's jdmk.uacl file.

  5. Press Control-C to stop the manager.

  6. Uncomment the acl block in the agent's jdmk.uacl file

    Because the security settings in the example jdmk.uacl file in examplesDir/Snmp/Agent were commented out, they were not applied. Once you have uncommented the acl block, the security settings in the agent's jdmk.uacl file and in the manager's jdmk.security file will match.

  7. Start the SyncManagerV3 SNMP manager again


    $ java -classpath classpath 
    -Djdmk.security.file=./jdmk.security SyncManagerV3 localhost 8085
    

    You should now see the manager sending requests to the agent.


    $ SyncManagerV3::main:Send get request to SNMP agent 
    on localhost at port 8085
    Result: 
    [Object ID : 1.3.6.1.2.1.1.1.0  (Syntax : String)
    Value : SunOS sparc 5.8]
    >> Press Enter if you want to stop this SNMP manager.
    

SNMPv3 User-Based Security Model

As stated in the earlier SNMP chapters in this manual, SNMPv3 implements a much more sophisticated set of security mechanisms than the previous versions of SNMP. The SNMPv3 USM enables you to implement authentication and privacy in the communication that takes place between your SNMP agents and managers. SNMPv3 also introduces the concept of the authoritative SNMP engine and enables you to create authorized users for specific SNMPv3 agents.

Chapter 18, Creating an SNMP Agent and Chapter 19, Developing an SNMP Manager provided just enough information about configuring SNMPv3 security to enable you to run the examples in those chapters. The following sections provide a more complete description of SNMPv3 security.

SNMPv3 Engines

SNMPv3 introduces the notion of the authoritative SNMP engine. The concept of authoritative is defined as follows:

Being authoritative means that entities have the ability to accept or deny requests from other entities, depending upon whether or not both sides of the exchange have been appropriately configured to communicate with each other, and whether the request itself arrives in a timely fashion. To check the timeliness of a request, the authoritative engine checks the time of sending included in the request against its own internal clock. If the difference between the time of sending and the time of receipt recorded by the authoritative engine exceeds 150 seconds, the request is not considered timely and is rejected.

The authoritative engine also checks timeliness by reading the localEngineBoots value recorded in the request, and comparing it to the number of reboots that the sending engine has undergone. It checks this by calling the SnmpEngine.getEngineBoots. If the value recorded in the request and the value returned by SnmpEngine.getEngineBoots do not correspond, the request is rejected.

In general, agents are authoritative, and managers are non-authoritative. However, when receiving informs, managers are authoritative, and can accept or deny the informs according to their timeliness.

Java DMK 5.0 associates an SNMP engine with every SnmpV3AdaptorServer that is instantiated. Engines can be shared between several SNMP sessions. SNMP engines are identified by their engine ID.

Generating SNMPv3 Engine IDs

SNMPv3 engine ID objects are generated in accordance with SNMP RFC 2571. An engine discovers its ID in one of the following ways, each of which is tried in the order shown:

  1. It is found in the engine's associated jdmk.security file

  2. It is found using SnmpEngineParameters

  3. It is computed by the SNMP adaptor, based on the host and port information, or it is computed by the SNMP session, based on time

In Java DMK 5.0, you can create new SNMP engine IDs either manually using a Java command line tool called EngineIdGenerator, or automatically in the code of your applications using the SnmpEngineId class. You can see the EngineIdGenerator tool in the examplesDir/Snmp/EngineId directory. You must provide either of EngineIdGenerator or SnmpEngineId with any of the following information:

The EngineIdGenerator uses the SnmpEngineId class when generating engine IDs (see the Javadoc entry for SnmpEngineId for details). The SnmpEngineId class simplifies the configuration of SNMP entities by enabling you to identify engines using information that is easily comprehensible to humans, such as host names and port numbers, which it then converts into system-oriented hexadecimal engine IDs for you. The SnmpEngineId class also generates SNMP object identifiers (OIDs) from the engine IDs it creates. This is particularly useful when computing USM MIB user table indexes, as seen in Creating Users for SNMPv3 USM MIBs.

To Run the SNMP EngineIdGenerator Example
  1. Compile the EngineIdGenerator class in examplesDir/Snmp/EngineId.


    $ javac -classpath classpath EngineIdGenerator.java
    
  2. Start the EngineIdGenerator tool.


    $ javac -classpath classpath EngineIdGenerator
    Start making your engine Id construct:(h for help)
    #:
    

    Typing h will provide examples of information you can provide.

  3. Provide the relevant information to create the engine ID.

    Declare your information, using the appropriate separators, as follows:

    address:port:IANA number

    All three inputs are used

    address:port

    The address and port you specify are used; the IANA number defaults to 42 (SUN Microsystems)

    address

    The address you specify is used; the port defaults to 161 and the IANA number defaults to 42 (SUN Microsystems)

    :port

    The port you specify is used; the host defaults to localhost and the IANA number defaults to 42 (SUN Microsystems)

    ::IANA number

    The IANA number you specify is used; the host defaults to localhost and the IANA number defaults to 42 (SUN Microsystems)

    :port:IANA number

    The port and IANA number you specify are used; the host defaults to localhost

    address::IANA number

    The address and IANA number you specify are used; the port defaults to 161

    ::

    The port defaults to 161, the address defaults to localhost and the IANA number defaults to 42 (SUN Microsystems)

    For example, to specify all three of the address, port and IANA number, when prompted you might type:


    Start making your engine Id construct:(h for help)
    #:localhost:8087:42
    Generated engine Id ******** [0x8000002a05819dcb6200001f97] ********
    #:
    
  4. Press Control-C when you have finished generating engine IDs

SNMPv3 USM Configuration

The SNMPv3 USM is configured in a Java DMK text file, called jdmk.security. Every SNMP engine has an associated security file.

In a traditional agent and manager SNMP architecture, you will have one security file associated with the agent and one associated with the manager. Both files will have a very similar configuration.

The authoritative agent's security file contains all the security information users need when requests are received from the manager. The non-authoritative manager's security file contains all the security information users need when making requests of authoritative agents.

The following examples show typical security files for an agent and a manager.


Example 20–3 A Typical Agent jdmk.security File

localEngineID=myHost:8085
localEngineBoots=7

#Typical authenticated entry. Accepts requests from a user called 
#aSecureUser
userEntry=localEngineID,aSecureUser,aSecureUser,
usmHMACMD5AuthProtocol, mypasswd

#Typical authenticated and encrypted entry. Accepts requests from 
#aSecureUser
#userEntry=localEngineID,aSecureUser,aSecureUser,
#usmHMACMD5AuthProtocol, #mypasswd,usmDESPrivProtocol,mypasswd

The example agent jdmk.security file identifies the agent's associated SNMP engine using its host name and port number, records the number of times that engine has rebooted, and sets two possible security configurations for a user called aSecureUser. One possible configuration applies authentication to requests from aSecureUser. The second configuration, which is currently commented out and is therefore inactive, applies both authentication and privacy to requests from the same user.


Example 20–4 A Typical Manager jdmk.security File

#Typical authenticated entry. Makes requests to authoritative engine 
#myHost:8085 with some parameters.
userEntry=myHost:8085,aSecureUser,aSecureUser,usmHMACMD5AuthProtocol, 
mypasswd

#Typical authenticated and encrypted entry. Makes requests to authoritative 
#engine myHost:8085 with some parameters.
#userEntry=myHost:8085,aSecureUser,aSecureUser,usmHMACMD5AuthProtocol, 
#mypasswd,
#usmDESPrivProtocol,mypasswd

# #####APPENDED PROPERTY####
localEngineBoots=5

# #####APPENDED PROPERTY####
localEngineID=myOtherHost:8087

The example manager jdmk.security file sets two possible configurations to send requests from the user aSecureUser to the above agent. Again, the first configuration applies authentication to requests from aSecureUser, and the second configuration, which is currently commented out and is therefore inactive, applies both authentication and privacy.


Note –

The localEngineID for each of the manager and the agent must be different. If two entities that communicate with each other have the same local engine ID, behavior is unpredictable.


Adding Users to the Security Files

As you can see in Example 20–3 and Example 20–4, every user that has access to an agent is represented by a userEntry row in each of the agent's and the manager's security files. The example manager jdmk.security file is configured to send requests from aSecureUser to the agent, either with authentication only, or with privacy activated. The agent is configured to receive those requests.

You configure userEntry as follows, with the parameters separated commas:

userEntry=engine ID,user name,security name,authentication algorithm,authentication key, privacy algorithm,privacy key,storage type,template

The only mandatory parameters are the engine ID and the user name. All the other parameters are optional.

The possible values for the parameters are as follows:

Engine ID

A local or remote SNMP engine, defined in one of the following ways:

  • The string localEngineID, to denote the local engine

  • A hexadecimal string, as generated by EngineIdGenerator; for example, 0x8000002a05819dcb6e00001f95

  • A human readable string used to generate an engine ID, providing any or all of the host name, port and IANA number, as shown in Generating SNMPv3 Engine IDs

User name

Any human-readable string

Security name

Any human-readable string

Authentication algorithm

The following algorithms are permitted:

  • usmHMACMD5AuthProtocol

  • usmHMACSHAAuthProtocol

  • usmNoAuthProtocol

Authentication key

Any text password or any hexadecimal key starting with 0x; for example, 0x0098768905AB67EFAA855A453B665B12, of size:

  • 0 to 32 inclusive for HMACMD5

  • 0 to 40 inclusive for HMACSHA

Privacy algorithm

The following algorithms are permitted:

  • usmDESPrivProtocol

  • usmNoPrivProtocol

If no algorithm is specified, the default is usmNoPrivProtocol.

Any text password or any hexadecimal key starting with 0x; for example, 0x0098768905AB67EFAA855A453B665B12, of size 0 to 32 inclusive

If a hexadecimal string is provided, it must be a localized key

Storage type

A value of 3 denotes non-volatile, meaning that the user entry is flushed in the security file; any other value than 3 will be rejected, throwing an IllegalArgumentException

template

Can be either true or false:

If true, the row is a template, not seen from USM MIB. This kind of user is used when cloning users.

The default is false.

Enabling Privacy in SNMPv3 Agents

As shown in the example security files given in SNMPv3 USM Configuration, you can protect the communication between your SNMPv3 entities by enabling encryption, otherwise known as privacy.

The privacy algorithms used by SNMPv3 are the data encyption standard (DES) protocol from the Java Cryptography Extension (JCE) from the Java Development Kit (JDK) 1.4, as well as the secure hash algorithm (SHA) and message digest 5 (MD5) encryption protocols provided since JDK 1.2.

To run an SNMP entity with privacy enabled, you must configure both the entity itself and its corresponding security file. The following example shows the code for an SNMPv3 agent with privacy enabled, called AgentEncryptV3. This example is found in the examplesDir/Snmp/Agent directory.


Example 20–5 AgentEncryptV3 Agent with Privacy Enabled

public class AgentEncryptV3 {

    static SnmpV3AdaptorServer snmpAdaptor = null;
    
    private static int nbTraps = -1;

    public static void main(String args[]) {
        
        final MBeanServer server;
        final ObjectName htmlObjName;
        final ObjectName snmpObjName;
        final ObjectName mibObjName;
        final ObjectName trapGeneratorObjName;
        int htmlPort = 8082;
        int snmpPort = 161;

        // Parse the number of traps to be sent.
       
			[...]

        // Initialize trace property.
        

         [...]         
         
         // SNMP specific code:
         

	    	// Set up encryption 
	  
		   //First create parameters.
	    	SnmpEngineParameters parameters = new SnmpEngineParameters();

	    	//Then activate encryption
	    	parameters.activateEncryption();

	    	//Create the SNMPv3 adaptor and pass it the parameters.
            snmpAdaptor = new SnmpV3AdaptorServer(parameters,
						  null,
						  null,
						  snmpPort,
						  null);
	    
	    	// Register the SNMP Adaptor in the MBean Server 
		   //
            server.registerMBean(snmpAdaptor, snmpObjName);

	    	// Register the USM MIB
		   snmpAdaptor.registerUsmMib(server, null);

	    	// Start the adaptor.
            snmpAdaptor.start();

            // Send a coldStart SNMP Trap. 
            // Use port = snmpPort+1.
            //
            print("NOTE: Sending a coldStart SNMP trap to each " +
		  		"destination defined in the ACL file...");

            snmpAdaptor.setTrapPort(new Integer(snmpPort+1));
            snmpAdaptor.snmpV1Trap(0, 0, null);
            println("Done.");
      
            // Create the MIB II (RFC 1213) and add it to the MBean server.
            //
            mibObjName= new ObjectName("snmp:class=RFC1213_MIB");
            Trace.send(Trace.LEVEL_TRACE, Trace.INFO_MISC, "Agent", "main", 
                       "Adding RFC1213-MIB to MBean server with name \n\t" +
                       mibObjName);

            // Create an instance of the customized MIB
            //
            RFC1213_MIB mib2 = new RFC1213_MIB_IMPL();
            server.registerMBean(mib2, mibObjName);
      
            // Bind the SNMP adaptor to the MIB to make the MIB 
            // accessible through the SNMP protocol adaptor.
            //
			    snmpAdaptor.addMib(mib2, "TEST-CONTEXT");

            // Create a LinkTrapGenerator.
            // Specify the ifIndex to use in the object name.
            //
            String trapGeneratorClass = "LinkTrapGenerator";
            int ifIndex = 1;
            trapGeneratorObjName = new ObjectName("trapGenerator" + 
                            ":class=LinkTrapGenerator,ifIndex=" + ifIndex);
            Trace.send(Trace.LEVEL_TRACE, Trace.INFO_MISC, "Agent", "main", 
                  "Adding LinkTrapGenerator to MBean server with name \n\t"+
		  		trapGeneratorObjName);
            LinkTrapGenerator trapGenerator = 
					new LinkTrapGenerator(nbTraps);
            server.registerMBean(trapGenerator, trapGeneratorObjName);

            println("\n>> Press Enter if you want to start sending traps."+
		    " SNMP V1 and SNMP V3 traps will be sent.");
            println("   -or-");
            println(">> Press Ctrl-C if you want to stop this agent.");
            java.lang.System.in.read();
            
            trapGenerator.start();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

   }

By default, a Java DMK 5.0 agent handles requests that are authenticated, but not encrypted. To activate encryption, you need to set certain parameters when you instantiate the SNMP engine. As shown inExample 20–5, these parameters are passed to the engine using the SnmpEngineParameters class, as follows:

The AgentEncryptV3 application then continues with the registration of the SNMP adaptor server in the MBean server, binding the MIBs and calling LinkTrapGenerator in the same way as any other agent.

As well as the agent itself, you must also configure the security file associated with that agent. Example 20–6 shows the security file associated with AgentEncryptV3.


Example 20–6 Agent jdmkencrypt.security File

#Local engine Id. Will be read by the agent to configure the engine.
localEngineID=0x8000002a05819dcb6e00001f95
#Number of boots. Will be read by the agent to configure the engine.
localEngineBoots=0

#defaultUser configuration. Authentication and encryption.
userEntry=localEngineID,defaultUser,null,usmHMACMD5AuthProtocol,mypasswd,
usmDESPrivProtocol,mypasswd,3,

In this file, you can see that the DES privacy protocol is specified.

To Run the AgentEncryptV3 Example
  1. If you have not already done so, build and compile the AgentEncryptV3 example in examplesDir/Snmp/Agent.

    Type the following commands:


    $ mibgen -d . mib_II.txt
    $ javac -classpath classpath -d . *.java
    
  2. Start the AgentEncryptV3 agent, passing it its associated security file, jdmkencrypt.security.


    $ java -classpath classpath 
    -Djdmk.security.file=./jdmkencrypt.security AgentEncryptV3
    
  3. Press Enter to start sending traps


    NOTE: Sending a linkDown SNMP trap for the Interface 1 to each 
    destination defined in the ACL file...Done.
    NOTE: Sending a linkDown SNMP trap for the Interface 1 to each 
    destination defined in the ACL file...Done.
    
  4. Press Control-C to stop the agent

Enabling Privacy in SNMPv3 Managers

If you enable privacy in your SNMPv3 agents, then you must also enable privacy in the corresponding manager. The following example shows the code for an SNMPv3 agent with privacy enabled, called SyncManagerEncryptV3. This example is found in the examplesDir/Snmp/Manager directory.


Example 20–7 SyncManagerEncryptV3 Manager with Privacy Enabled

/**
 public class SyncManagerEncryptV3 {
   
    	public static void main(String argv[]) {
		SnmpSession session = null;
	
        if (argv.length != 2) {
            usage();
            java.lang.System.exit(1);
        }
	
			//Check arguments first
			//host and port.
        final String host = argv[0];
        final String port = argv[1];
	
        // Initialize trace property. 
 			[...] 
	

      	// Initialize the SNMP Manager API.
        //
	    	[...] 
	    

  		// Activate the encryption
	    
		   //

	   		// First create parameters.
	    	//
	    	final SnmpEngineParameters parameters = 
			new SnmpEngineParameters();

	    	// Then activate encryption
	    	parameters.activateEncryption();

	    	// Finaly create the session passing it the parameters.
	    	try {
			// When instantiating a session, a new SNMP V3 engine is 
			// instantiated.
			session= new SnmpSession(parameters,
					 null,
					 "SyncV3Manager session",
					 null);
	    	}catch(SnmpStatusException e) {
			println(e.getMessage());
			java.lang.System.exit(0);
	    }
	    	catch(IllegalArgumentException e) {
			//If the engine configuration is falty
			println(e.getMessage());
			java.lang.System.exit(0);
	    }
	    
	   		final SnmpEngine engine = session.getEngine();
	    
	    	// Create a SnmpPeer object 
		   //
	    	final SnmpUsmPeer agent = 
			new SnmpUsmPeer(engine, host, Integer.parseInt(port));
	    
	    	// Create parameters to associate to the entity to 
	    	// communicate with.
	  		//
	    	final SnmpUsmParameters p = 
			new SnmpUsmParameters(engine, "defaultUser");
	    
	    	// Set Security level 
	    	//
	     	p.setSecurityLevel(SnmpDefinitions.authPriv);

	    	// Register MIBS under the scope of a context.
			//
	    	p.setContextName("TEST-CONTEXT".getBytes());

	    	// Specify a contextEngineId. This is 
	    	//
	    	p.setContextEngineId(agent.getEngineId().getBytes());
	    
	    	// The newly created parameter must be associated to the agent.
	    	//
	    	agent.setParams(p);
	
	    
	    	// Discovery timeliness
	    	//
	    	agent.processUsmTimelinessDiscovery();
	    
	    	// A default peer (agent) can be associated to a SnmpSession. 
	   	   //
	   		session.setDefaultPeer(agent);
	    
	 	 	// Create a listener and dispatcher for SNMP traps 
	    	final SnmpEventReportDispatcher trapAgent =
			new SnmpEventReportDispatcher(engine, 
					      Integer.parseInt(port) + 1, 
					      taskServer, null);
	    	trapAgent.addTrapListener(new TrapListenerImpl());
            final Thread trapThread = new Thread(trapAgent);
	    	trapThread.setPriority(Thread.MAX_PRIORITY);
	    	trapThread.start();
	    
	    
	    	// Build the list of variables you want to query.
	    	// For debug purposes, you can associate a name to your list.
	    	//
	    	final SnmpVarBindList list = 
			new SnmpVarBindList("SyncManagerEncryptV3 varbind list");
	    
	    	// We want to read the "sysDescr" variable.
	    	//
            // We will thus query "sysDescr.0", as sysDescr is a scalar
	    		// variable (see RFC 1157, section 3.2.6.3.  Identification 
           	// of Object Instances, or RFC 2578, section 7.  Mapping of 
	    		// the OBJECT-TYPE macro).
	    		//
	    		list.addVarBind("sysDescr.0");
	    
	    	// Make the SNMP get request and wait for the result.
	    	//
	    	final SnmpRequest request = session.snmpGetRequest(null, list);
	    	println("SyncManagerEncryptV3::main:" + 
				 " Send get request to SNMP agent on " + 
				 host + " at port " + port);
	    	final boolean completed = request.waitForCompletion(10000);
	    
	    	// Check for a timeout of the request.
	    	//
            if (completed == false) {
                println("SyncManagerEncryptV3::main:" +
			" Request timed out. Check reachability of agent");
		
                // Print request.
                //
                println("Request: " + request.toString());
                java.lang.System.exit(0);
            }
	    
            // Check if the response contains an 
	    		// error.
            //
            final int errorStatus = request.getErrorStatus();
            if (errorStatus != SnmpDefinitions.snmpRspNoError) {
                println("Error status = " + 
				SnmpRequest.snmpErrorToString(errorStatus));
                println("Error index = " + 
				request.getErrorIndex());
                java.lang.System.exit(0);
            }
       
            // Display the content of the result.
            //
            final SnmpVarBindList result = request.getResponseVarBindList();
            println("Result: \n" + result);
       
            println("\n>> Press Enter if you want to stop" +
		    " this SNMP manager.\n");
            java.lang.System.in.read();
            
            // Nicely stop the session
            //
            session.destroySession();
       
	    		// End the SnmpEventReportDispatcher.
	    		//
	    		trapAgent.close();
	    		taskServer.terminate();

            //
            // That's all !
            //
            java.lang.System.exit(0);
     
				} catch(Exception e) {
            java.lang.System.err.println("SyncManagerEncryptV3::main:" +
					 " Exception occurred:" + e );
            e.printStackTrace();
        }
    }

    
}

By default, a Java DMK 5.0 manager handles requests that are authenticated, but not encrypted. To activate encryption, you need to set certain parameters when you instantiate the SNMP session. As shown in Example 20–7, these parameters are passed to the engine using the SnmpEngineParameters class, as follows:

The SyncManagerEncryptV3 manager application then continues with the generation of a USM peer, defining the context and setting trap listeners in the same way as any other manager. Note, however, that in this manager, the security level is set to authPriv.

As well as the manager itself, you must also configure the security file associated with that manager. Example 20–8 shows the security file associated with SyncManagerEncryptV3.


Example 20–8 Manager jdmkencrypt.security File

#Authentication and encryption.
userEntry=0x8000002a05819dcb6e00001f95,defaultUser,,
usmHMACMD5AuthProtocol,mypasswd,usmDESPrivProtocol,mypasswd

# #####APPENDED PROPERTY####
localEngineBoots=2

# #####APPENDED PROPERTY####
localEngineID=0x8000002a05000000ebffd342ca

As was the case for the AgentEncryptV3 agent, in this file, you can see that the DES privacy protocol is specified.

To Run the SyncManagerEncryptV3 Example
  1. If you have not already done so, build and compile the AgentEncryptV3 example in examplesDir/Snmp/Agent.

    Type the following commands:


    $ mibgen -d . mib_II.txt
    $ javac -classpath classpath -d . *.java
    
  2. Start the AgentEncryptV3 agent, passing it its associated security file, jdmkencrypt.security.


    $ java -classpath classpath -Djdmk.security.file=./jdmkencrypt.security 
    AgentEncryptV3
    

    Press Enter to start sending traps.

  3. Press Enter to start sending traps.

  4. In another window, if you have not already done so, build and compile the SyncManagerEncryptV3 example in examplesDir/Snmp/Manager.

    Type the following commands:


    $ mibgen -mo -d . mib_II.txt
    $ javac -classpath classpath -d . *.java
    
  5. Start the SyncManagerEncryptV3 manager, passing it its associated security file, jdmkencrypt.security, and specifying the host name and port number of the agent it is to communicate with.


    $ java -classpath classpath -Djdmk.security.file=./jdmkencrypt.security 
    SyncManagerEncryptV3 localhost 8085
    

    You should see the manager start to receive encrypted traps from the agent.


    SyncManagerEncryptV3::main: 
    Send get request to SNMP agent on localhost at port 8085
    Result: 
    [Object ID : 1.3.6.1.2.1.1.1.0  (Syntax : String)
    Value : SunOS sparc 5.8]
    
    >> Press Enter if you want to stop this SNMP manager.
    
    NOTE: TrapListenerImpl received trap V3:
            ContextEngineId : 0x8000002a05819dcb6e00001f95
            ContextName : TEST-CONTEXT
            VarBind list :
    oid : 1.3.6.1.2.1.1.3.0 val : 0:0:40
    oid : 1.3.6.1.6.3.1.1.4.1.0 val : 1.2.3.4.5.6.7.8.9.0
    oid : 1.3.6.1.2.1.2.2.1.1.1 val : 1
    
  6. Press Control-C in each window to stop both the agent and the manager

Creating Users for SNMPv3 USM MIBs

The SNMPv3 USM implemented in Java DMK 5.0 enables you to create users remotely in an SNMPv3 agent by accessing a MIB that has been registered in the SNMPv3 adaptor server. By default, the USM MIB is not registered in the adaptor server. You can register a MIB in the adaptor server by calling registerUsmMib.


$ snmpV3AdaptorServer.registerUsmMib()

Caution – Caution –

You can use registerUsmMib to register your MIB in the MBean server, making it available via the HTML server. This can be useful for debugging purposes, but this can also represent a security breach.


The CreateUsmMibUser example in the examplesDir/Snmp/UsmMib directory is a tool that uses the SNMPv3 manager API to instantiate a new user in an agent USM MIB. CreateUsmMibUser performs authenticated and encrypted communication with the agent Agent, which is found in the same directory.

The complete code for the CreateUsmMibUser example is too long to show here, but the process that it goes through to create new users remotely can be summarized as follows:

To Run the CreateUsmMibUser Example
  1. If you have not already done so, build and compile the examples in examplesDir/Snmp/UsmMib.

    Type the following commands:


    $ javac -classpath classpath -d . *.java
    
  2. Make sure that no other agents are running in examplesDir/Snmp/UsmMib, and start Agent.


    $ java -classpath classpath -Djdmk.security.file=./jdmk.security Agent
    

    Note –

    The jdmk.security file must be writable if CreateUsmMibUser is to be able to add new user entries.


  3. In another window, start the CreateUsmMibUser example.

    When starting CreateUsmMibUser, you must point it to the manager.security configuration file, and specify the user name, the security level, the agent's host name and the port on which the agent is running. In this example, the security level is authentication and privacy enabled, and the agent is running on the local host.


    $ java -classpath classpath -Djdmk.security.file=./manager.security 
    CreateUsmMibUser defaultUser authPriv localhost 8085
    

    You will see the following output:


    Initializing creator.
    Ready for new user inputs.
    
  4. When prompted, provide the configuration information for your new user.

    The information you provide must correspond to users that you have already configured into your manager's security file. In this example, we are remotely adding the user myNewUser that is defined in manager.security to the agent Agent. You therefore provide the following information, all of which is found in the manager.security file. You can enter any value for the auth key random and the priv key ramdom.


    Type the engine Id :0x000000000000000000000002
    Type the new user name :myNewUser
    Type the clone from user name :defaultUser
    Type the security level :authPriv
    Type the old priv password :maplesyrup
    Type the new priv password :newsyrup
    Type the priv key random :00000000000000000000000000000000
    Type the auth protocol :usmHMACMD5AuthProtocol
    Type the old auth password :maplesyrup
    Type the new auth password :newsyrup
    Type the auth key random :00000000000000000000000000000000
    

    You will see the following output:


    ********** Input summary ************ 
    
            * Engine Id : 0x000000000000000000000002
            * New user name : myNewUser
            * Clone from : defaultUser
            * Security level : authPriv
            * Old priv password : maplesyrup
            * New priv password : newsyrup
            * Priv key random : 00000000000000000000000000000000
            * Auth protocol : usmHMACMD5AuthProtocol
            * Old auth password : maplesyrup
            * New auth password : newsyrup
            * Auth key random : 00000000000000000000000000000000
    Do you agree (yes, no) [yes]:
    
  5. Press Enter to confirm your inputs.

    You should see the following confirmation:


    ***** New user [myNewUser] created.
    ***** Doing Priv key change
    ***** Priv key change DONE.
    ***** Doing Auth key change
    ***** Auth key change DONE.
    ***** Setting row status to active.
    ***** Setting row status to active DONE.
    
    ***** SUCCESSFULLY CREATED NEW ROW IN AGENT FOR USER : [myNewUser]*****
    
    
    ***** Making a sanity check get request on the spin 
    lock with the newly created 
    user [myNewUser] PRESS RETURN.
    
  6. Press Enter to perform the sanity check.

    You should see the following confirmation:


    SANITY CHECK SUCCESSFUL, SPIN LOCK VALUE IS: 5
    Ready for new user inputs.
    
    Type the engine Id (return to accept) [0x000000000000000000000002]:
    

    You are then invited to provide configuration information for any other users you want to allow to access Agent.

  7. Check that the new user has been granted access to the agent by looking at the agent's jdmk.security file.

    You should see a new userEntry for the new user in the jdmk.security file.


    Example 20–11 jdmk.security for Agent File after Running CreateUsmMibUser

    localEngineID=0x000000000000000000000002
    localEngineBoots=7
    
    userEntry=0x000000000000000000000002,myNewUser,myNewUser,
    usmHMACMD5AuthProtocol,0x87021D7BD9D101BA05EA6E3BF9D9BD4A,
    usmDESPrivProtocol,0x87021D7BD9D101BA05EA6E3BF9D9BD4A,3,
    
    userEntry=localEngineID,defaultUser,,usmHMACMD5AuthProtocol,maplesyrup,
    usmDESPrivProtocol,maplesyrup,3,true

  8. When you have added all your new users, press Control C in both windows to stop both Agent and CreateUsmMibUser

Legacy Security

Because Java DMK 5.0 implements an SNMPv3 adaptor, and all SNMPv3 security aspects are handled completely by this adaptor, MIB instrumentation does not depend on the version of SNMP via which it is accessed. MIBs that were developed under previous releases of Java DMK can thus be directly registered into the new SnmpV3AdaptorServer, and benefit from all the SNMPv3 security mechanisms.

However, earlier versions of Java DMK provided a hook via the SnmpPduFactory, that enabled the implementation of authentication and encryption on top of the SNMPv1 and v2 protocol adaptor. This can be used to implement proprietary security over the regular SNMPv1 and v2 PDUs. This hook has been preserved in Java DMK 5.0, for reasons of backwards compatibility.


Note –

Although the SNMPv1 and v2 community-based security mechanism is still available in Java DMK 5.0, you should migrate applications that require better security to SNMPv3. When migrating your applications to SNMPv3, applications which have implemented their own PDU factory must be revised before they can be imported into the SnmpV3AdaptorServer, as the SnmpPduFactory class developed for SNMPv1/v2 PDUs is not compatible with SNMPv3 PDUs.


Decoding and Encoding SNMP Packets

The SnmpPduFactory hook provided by Java DMK 5.0 involves the following Java classes:

javax.management.snmp.SnmpPduFactory

Defines the interface of the object in charge of encoding and decoding SNMP packets

com.sun.jdmk.snmp.SnmpPduFactoryBER

The default implementation of the SnmpPduFactory interface

javax.management.snmp.SnmpPdu

The fully decoded representation of an SNMP packet

javax.management.snmp.SnmpMsg

A partially decoded representation of an SNMP packet, containing the information stored in any SNMPv1, SNMPv2 or SNMPv3 message

After receiving an SNMP packet, Java DMK 5.0 performs the following steps:

  1. The received bytes are translated into an SnmpMsg object by the message processing subsystem. If the SNMP protocol version of the original request was either v1 or v2, this step simply involves the BER decoding of the ASN.1 Message sequence as defined in RFC 1901. If the SNMP protocol version of the original request was v3, the message processing subsystem will in addition invoke the security subsystem to authenticate and decrypt the message.

  2. The SnmpMsg object is then translated into an SnmpPdu object

  3. The SnmpPdu is analyzed and the corresponding operation is performed

Before sending an SNMP packet, Java DMK 5.0 performs the following steps:

  1. An SnmpPdu object is initialized according to the requested operation. This could be either an SnmpPduPacketfor SNMPv1or v2, or an SnmpScopedPduPacket for SNMPv3

  2. The SnmpPdu object is translated into an SnmpMsg

  3. The SnmpMsg is then passed to the message processing subsystem, and translated into bytes. If SNMPv1 or SNMPv2 is being used, this step simply involves the BER encoding of the ASN.1 message sequence as defined in RFC 1901. If SNMPv3 is being used, the message processing subsystem also invokes the security subsystem to sign and encrypt the message.

The SnmpPdu object is the fully decoded description of the SNMP request. In particular, it includes the operation type (get, set, and so on), the list of variables to be operated upon, the request identifier, and the protocol version, as shown in Example 20–12.


Example 20–12 Using the SnmpPdu Class

abstract class SnmpPdu {
             ...
             public int version ;
             public int type ;
             public int requestId ;
             public SnmpVarBind[] varBindList ;
             ...
     }

The use of the SnmpMsg class is shown in Example 20–13. The SnmpMsg class is a partially decoded representation of the SNMP request. Only the protocol version and security parameters are decoded. All the other parameters remain encoded.

The SnmpMsg class is the base class derived from the message syntax from RFC 1157 and RFC 1901, and SNMPv3Message from RFC 2572. The SnmpMessage class that was present in releases of Java DMK before 5.0 derives from SnmpMsg and represents an SNMPv1 or SNMPv2 message. Because SNMPv3 introduces additional security parameters, the SnmpMessage class can only be used for SNMPv1 or SNMPv2 messages. SnmpPduFactory implementations that make direct use of SnmpMessage will therefore need to be updated if they are to be imported into a Java DMK 5.0 SNMPv3 protocol adaptor. However, they do not need to be changed as long if the old SnmpAdaptorServer is used instead of SnmpV3AdaptorServer.


Example 20–13 Using the SnmpMsg Class

abstract class SnmpMsg {
             ...
             public int version ;
             ...
     }

     class SnmpMessage extends SnmpMsg {
             ...
             public byte[] community ;
             public byte[] data ;
             ...
     }

SnmpPduFactory Interface

When Java DMK 5.0 needs to translate an SnmpMsg object into an SnmpPdu object, it delegates this task to an object which implements SnmpPduFactory, as shown in Example 20–14:


Example 20–14 Using the SnmpPduFactory Interface

interface SnmpPduFactory {

       // Makes an SnmpPdu from an SnmpMsg
       public SnmpPdu decodeSnmpPdu(SnmpMsg msg) 
       throws SnmpStatusException ;
       
       // Makes an SnmpMsg from an SnmpPdu
       public SnmpMsg encodeSnmpPdu(SnmpPdu pdu, int maxPktSize)
       throws SnmpStatusException, SnmpTooBigException ;
       
     }


Note –

SnmpPduFactory has two additional methods inherited from Java DMK 4.2, decodePdu and encodePdu, that are now deprecated but are kept for backwards compatibility.


Java DMK 5.0 provides a default implementation of theSnmpPduFactory, called SnmpPduFactoryBER. SnmpPduFactoryBER is used automatically unless stated otherwise. The SnmpPduFactoryBER methods control every incoming or outgoing SNMP PDU.

Therefore, it is possible to implement a security policy using an SnmpPduFactory class. However, it is recommended to rely rather on the standard SNMPv3 policy. Using the SnmpPduFactory to implement additional levels of security only makes sense on an SNMPv1 or SNMPv2 framework, when SNMPv3 is not an option.

Implementing a New SnmpPduFactory Class

Java DMK expects decodeSnmpPdu to behave as follows:

Java DMK expects encodeSnmpPdu to behave as follows:

Because SnmpPdu and SnmpMsg are abstract classes, you should delegate their creation and initialization to an instance of SnmpPduFactoryBER and work on the result returned.

You can change the SnmpPduFactory object used by the SNMP adaptor by using the setPduFactory method, shown in Example 20–15.


Example 20–15 Changing the SnmpPduFactory object Using setPduFactory

		  ...
       myAdaptor.setPduFactory(new MyFireWallPduFactory()) ;
       ...

In Java DMK 4.2, the SnmpPduFactory was attached to the SnmpPeer object. In Java DMK 5.0, the SnmpPduFactory is attached to the SnmpSession. Factories set via the deprecated SnmpPeer API are reused in Java DMK 5.0. They can be changed using the setPduFactory method, as shown in Example 20–16:


Example 20–16 Updating Deprecated SnmpPeer Factories Using setPduFactory

...
SnmpSession mySession = new SnmpSession() ;
mySession.setPduFactory(new MyFireWallPduFactory()) ;
mySession.snmpGet(myPeer, this, myVarBindList) ;
...


Caution – Caution –

Setting two different factories in the peer and in the session can lead to unpredictable behavior. Use the same factory at both levels.