Both the SNMP protocol adaptor and the SNMP manager API provide mechanisms for ensuring the security of management operations. Agents act as information servers, and access control is used to protect this information from unauthorized access. This topic covers the different ways that the SNMP protocol adaptor can limit access to the data in the agent.
Security in a manager involves positively identifying the source of management responses, to ensure that the expected agent answered the request. This amounts to securing the communication against falsification of data and usurpation of identity, which must both be performed at the communication level. Both the agent and the manager handle the communication packets and both provide hooks for implementing your own security scheme.
The complete source code for these examples is available in the Snmp/Agent directory located in the main examplesDir (see "Directories and Classpath" in the preface).
Contents:
"Access Control Lists (ACL)" shows how to specify host communities to control manager access and send traps.
"Message-Level Security" demonstrates a more advanced way of limiting access to the SNMP agent.
"SNMP Manager Security" gives the API hook for implementing message-level security in a manager application.
For the SNMP adaptor, the Java Dynamic Management Kit 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 (ACL). The default implementation provided with the product uses an ACL file, but you may provide your own implementation as described in "Custom Access Control".
The ACL mechanism can also be used to define the communities and managers to which the agent will send traps. When you rely on the ACL trap group, the agent will send traps to all hosts listed in the ACL file. See "Specifying the Trap Destination" for the different ways that an agent application may sends 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 machine running your SNMP manager application.
acl = { { communities = public access = read-only managers = yourmanager } { communities = private access = read-write managers = yourmanager } } trap = { { trap-community = public hosts = yourmanager } } |
An ACL file contains an acl group defining community and manager access rights and a trap group defining the community and hosts for sending traps.
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 machines specified in the hostList. There are two possible values: either read-write or read-only.
The hostList item gives the host machines 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:
A host name
An IP address
A subnet mask
To distinguish between IP addresses and subnet masks in an ACL file, each integer in a subnet mask is separated by an exclamation mark (!) instead of a dot (.).
The set of all access configurations defines the access policy of the SNMP agent. A manager whose host is specified in a hostList and which 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 may 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 PDUs from this manager will be dropped without being processed.
The trap group specifies the hosts to which the agent will send traps if the ACL mechanism is used. This group contains one or more trap community definitions.
trap = { community1 community2 ... communityN }
Each 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.
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 ACL 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.
The default ACL mechanism provided with the Java Dynamic Management Kit relies on an ACL file to define the access rights and trap recipients. To enable access control with this mechanism, you must first write an ACL 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 ACL file exists when the SNMP protocol adaptor MBean is instantiated. In order to be automatically detected, the ACL file must be named jdmk.acl and must be located in the configuration directory of the Java Dynamic Management Kit installation. On Unix systems with a standard installation of the product, the configuration directory is owned by root and requires super-user privileges in order to write or modify the ACL file.
Operating Environment |
Configuration Directory |
---|---|
Solaris |
installDir/SUNWjdmk/jdmk4.2/JDKversion/etc/conf/ |
Windows NT |
installDir\SUNWjdmk\jdmk4.2\JDKversion\etc\conf\ |
In order for the application to locate the configuration directory, the classpath of the Java virtual machine running the agent must include the full path of the jdmkrt.jar file.
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 ACL file in the configuration directory. This property may be set programmatically, but it is usually done on the command line when launching your agent. For example, if the full pathname of your ACL file is MyAclFile, use this command to launch the agent with SNMP access control enabled:
$ java -classpath classpath -Djdmk.acl.file=MyAclFile MyAgent |
If an ACL 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 machine: the ACL groups must explicitly give permissions to localhost or the host's machine name or IP address for such managers. If the ACL 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 localhost.
The ACL 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 ACL file. This class provides the rereadTheFile method to reset the access control and trap lists with the contents of the ACL file. This method will reload the same file that was used originally, regardless of any new property definitions. After you have updated the ACL file, call the following methods to update the access control lists:
// assuming mySnmpAdaptor is declared as an SnmpAdaptorServer object JdmkAcl theAcl = (JdmkAcl)(mySnmpAdaptor.getIPAcl()); 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 which 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 ACL file. If you specify false for the useAcl parameter of this constructor, the SNMP adaptor won't even search for an ACL file. In this case, no access control is performed, as if there were no ACL file: all SNMP requests will be processed, and traps will be sent only to the localhost. For security considerations, the use of access control cannot be toggled once the SNMP adaptor has been instantiated.
The JdmkAcl class which 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 which doesn't rely on the jdmk.acl file.
In order to instantiate an SNMP adaptor with your own access control, use one of the constructors which takes an acl parameter of the type IPAcl. Note that if this parameter's value is null, or if you use a constructor that doesn't 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 IPAcl 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 IPAcl 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 IPAcl interface, and it provides a few other methods, such as rereadTheFile, to control the ACL mechanism.
The SecureAgent example shows another level of security at the SNMP message level. Whereas the access control mechanism handles access rights to all MIBs for communities of manager hosts, message-level security lets you control how PDUs (Protocol Data Units) representing requests are encoded and decoded by the SNMP protocol adaptor.
The data in an SNMP message is stored in its raw form as a byte array. When receiving a message, the SNMP protocol adaptor must decode the data to obtain the corresponding request, and before sending a request, the adaptor must encode it as a message. By default, the basic encoding rules (BER) are used to translate back and forth between message and decoded PDU. The SNMP protocol adaptor provides a hook to let you implement your own encoding and decoding mechanism.
Message-level security relies on the following classes in the javax.management.snmp package:
SnmpPduPacket class
SnmpMessage class
SnmpPduFactory interface
An SnmpPduPacket object represents the fully decoded description of an SNMP request. In particular, it includes the operation type (get, set, ...), the list of variables to which the operation applies, the request identifier, and the protocol version.
The SnmpMessage object is a partially decoded representation of the SNMP request. The type of the request, the variables and their values all remain encoded as a byte array. This object also contains the default BER encoding and decoding methods. The SnmpMessage class is derived from the Message syntax in RFC 1157 and RFC 1902.
The SnmpPduFactory interface defines the method signatures for encoding PDUs into messages and decoding messages into PDUs. By providing an implementation of this class, you can fully control the contents of messages and see the contents of packets before they are processed.
Both the packet and message classes also contain port and address information of the SNMP manager. When implementing your own security mechanism, you also have access to the contents of the PDU or message you are handling. This lets you implement security based on several factors:
The host or community of the SNMP manager in a message before it is decoded
The type or contents of a request after it is decoded
Some encryption of the raw data
Because message-based security gives you access to all these different factors, you can perform elaborate filtering of incoming requests. For example, you could limit the access of certain SNMP managers to certain variables, or you could filter variable bindings, such as untrusted IP addresses, before they are assigned.
If your security is based on encryption of the message data, your manager must of course be using the same encryption. Because the SNMP classes are also part of the SNMP manager API, you can reuse the same encryption code in your manager if it is developed in the Java programming language. Security in the SNMP manager API is implemented in the SnmpPeer and SnmpEventReportDispatcher objects, see "SNMP Manager Security" for more information.
If your security scheme is based only on the sender of the message or contents of the PDU, it can be applied unilaterally by the agent, without requiring any coordination with the manager application. This is what is demonstrated in the SecureAgent example.
In the SNMP protocol adaptor, the task of translating an SnmpMessage object into an SnmpPduPacket object is delegated to an object which implements the SnmpPduFactory interface. This interface defines two methods, one for decoding messages into PDUs and one for encoding PDUs into messages:
decodePdu takes an SnmpMessage and should return a decoded SnmpPduPacket object; if it returns null or raises an exception, the incoming message is assumed to have failed the security check
encodePdu takes an SnmpPduPacket and should return an encoded SnmpMessage to send
In our example, the SnmpPduFactoryImpl class implements the decodePdu method to reject messages if they originate from certain hosts. The list of hosts to refuse is passed to the class constructor at instantiation. The encodePdu method only does the standard BER encoding of outgoing messages.
public class SnmpPduFactoryImpl implements SnmpPduFactory { private String[] hostNames; // HostNames is the array of the host names whose requests will be // refused by the agent public SnmpPduFactoryImpl(String[] hostNames) { this.hostNames = hostNames; } public SnmpPduPacket decodePdu(SnmpMessage msg) throws SnmpStatusException { // Get the sender's hostname String from = msg.address.getHostName(); for (int i = 0; i < hostNames.length; i++) { if (from.equals(hostNames[i])) { java.lang.System.out.println("Pdu rejected from " + from); return null; } } // If the host is accepted, we return the standard BER decoding return msg.decodePdu(); } // No security when sending, just do the standard BER encoding public SnmpMessage encodePdu(SnmpPduPacket pdu, int maxPktSize) throws SnmpStatusException, SnmpTooBigException { SnmpMessage result = new SnmpMessage(); result.encodePdu(pdu, maxPktSize); return result; } } |
Beyond our simple check of the sender's hostname, our example relies on the standard BER encoding and decoding of the SnmpMessage class. Even if you choose to implement encryption, it can still be implemented on top of the standard BER for simplicity. In this case, you only need to encrypt the message's byte array after the standard encoding and decrypt it before the standard decoding.
When you implement the encodePdu, you must ensure that it also handles trap PDUs, by encoding them as they will be expected by the manager application (see "SNMP Manager Security"). For example, trap messages are decoded separately from response messages in applications based on the SNMP manager API.
To use your custom PDU factory in your SNMP agent, you need to call the usePduFactory method of your SnmpAdaptorServer instance. First instantiate your PDU factory implementation and then pass it to this method. Your encoding and decoding scheme will then replace the standard one used by the SNMP protocol adaptor.
The SecureAgent.java file contains a simple agent like the one presented in "The SNMP Protocol Adaptor". It only adds the call to force the SNMP adaptor to use the new PDU factory that we specify.
// Use SnmpPduFactoryImpl class for SnmpPduFactory to filter requests. // Enter your list of refused hosts as arguments when launching this agent. // The agent will reject requests coming from the specified hosts. // String[] refusedHosts = new String[args.length]; refusedHosts = args; snmpAdaptor.usePduFactory(new SnmpPduFactoryImpl(refusedHosts)); |
The SNMP adaptor will then use this PDU factory to filter incoming requests based on their originating host. It will also encode all outgoing messages, including any traps that are sent, though it does nothing more than standard BER encoding. The secure agent example does not demonstrate traps, and the LinkTrapGenerator class is not written to function with the SecureAgent class. However, the SnmpPduFactoryImpl could be used as it is shown above in the SNMP Agent example.
You can only demonstrate the output of our custom PDU factory if you have an SNMP manager application which can connect to the secure agent. See "Developing an SNMP Manager" for example applications you can use.
The SecureAgent class takes command line arguments to create the list of hosts from which it will refuse SNMP requests. Use the following command to launch the secure agent example:
$ java -classpath classpath SecureAgent [host1..hostN] |
Whenever one of the refused hosts sends a request, you should see the message displayed by our custom PDU factory implementation. Type "Control-C" when you are finished running the secure agent.
You can combine message-level security and access control defined by the presence of an ACL file. The ACL file indicates trusted hosts and communities from which messages are accepted. After they are accepted, they are decoded with the message-level security you provide. This lets you provide more precise security based on types of requests or the target variable, as well as any encryption.
Since the role of SNMP managers is that of a client, their security needs revolve around sending and receiving data safely. To do this, the SNMP manager API also relies on implementations of the SnmpPduFactory interface in order to control message-level encoding and decoding.
By default, the SNMP manager API relies on the standard BER encoding implemented by the SnmpPduFactoryBER class, which is the encoding used by default by all SNMP agents, not just those developed with the Java Dynamic Management Kit. You may change the encoding to use your own implementation of the SnmpPduFactory interface through the following two hooks:
The setPduFactory method of an SnmpPeer instance lets you control how all requests are encoded and their responses decoded; because the custom PDU encoding is associated with a peer, you may have different encodings for the different peers that are accessed
The setPduFactory method of an SnmpEventReportDispatcher instance lets you control how unsolicited inform requests and trap messages are decoded and the inform response encoded
Using these hooks, message-level security can be implemented regardless of whether the managers are synchronous or asynchronous. If you are using a symmetrical encryption of messages between your agent and manager, you may also reuse the same classes for your PDU factory implementation on both agent and manager sides, assuming they are both using the Java Dynamic Management Kit SNMP toolkit.
Otherwise, if your encryption is not symmetrical or if your agents do not use the SNMP protocol adaptor, your PDU factory implementation will necessarily be specific to the security scheme you choose to implement in your manager application.
The example applications do not cover the security features in the SNMP manager API. Please refer to the Java Management Extensions SNMP Manager API document and the Javadoc API for more details about using these hooks to implement manager security.