Package org.snmp4j.security

Source Code of org.snmp4j.security.USM

/*_############################################################################
  _##
  _##  SNMP4J - USM.java
  _##
  _##  Copyright (C) 2003-2009  Frank Fock and Jochen Katz (SNMP4J.org)
  _##
  _##  Licensed under the Apache License, Version 2.0 (the "License");
  _##  you may not use this file except in compliance with the License.
  _##  You may obtain a copy of the License at
  _##
  _##      http://www.apache.org/licenses/LICENSE-2.0
  _##
  _##  Unless required by applicable law or agreed to in writing, software
  _##  distributed under the License is distributed on an "AS IS" BASIS,
  _##  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  _##  See the License for the specific language governing permissions and
  _##  limitations under the License.
  _##
  _##########################################################################*/

package org.snmp4j.security;

import java.io.*;
import java.nio.ByteBuffer;
import java.util.Vector;

import org.snmp4j.log.*;
import org.snmp4j.asn1.*;
import org.snmp4j.asn1.BER.*;
import org.snmp4j.event.*;
import org.snmp4j.mp.*;
import org.snmp4j.smi.*;

/**
* The <code>USM</code> class implements the User Based Security Model (USM)
* as defined in RFC3414.
* <p>
* When a user is added or removed from the USM, a <code>UsmUserEvent</code>
* is fired and forwarded to registered listeners.
*
* @author Frank Fock
* @version 1.2
*/
public class USM implements SecurityModel {

  private static final int MAXLEN_USMUSERNAME = 32;

  private static final LogAdapter logger = LogFactory.getLogger(USM.class);

  // Table containing localized and non-localized users
  private UsmUserTable userTable;

  private UsmTimeTable timeTable;

  private OctetString localEngineID;
  private boolean engineDiscoveryEnabled = true;

  private SecurityProtocols securityProtocols;
  private transient Vector usmUserListeners;
  private CounterSupport counterSupport;

  /**
   * Creates a USM with the support for the supplied security protocols.
   *
   * @param securityProtocols
   *    the security protocols to support.
   * @param localEngineID
   *    the local engine ID.
   * @param engineBoots
   *    the number of engine boots.
   * @since 1.2
   */
  public USM(SecurityProtocols securityProtocols,
             OctetString localEngineID, int engineBoots) {
    this.localEngineID = localEngineID;
    timeTable = new UsmTimeTable(localEngineID, engineBoots);
    userTable = new UsmUserTable();
    this.securityProtocols = securityProtocols;
    counterSupport = CounterSupport.getInstance();
  }

  public int getID() {
    return SECURITY_MODEL_USM;
  }

  /**
   * Sets the local engine ID, number of boots, and time after boot.
   * @param localEngineID
   *    the local engine ID.
   * @param engineBoots
   *    the number of engine boots.
   * @param engineTime
   *    the number sendonds since the last boot.
   */
  public void setLocalEngine(OctetString localEngineID,
                             int engineBoots, int engineTime) {
    this.localEngineID = localEngineID;
    timeTable.setLocalTime(new UsmTimeEntry(localEngineID, engineBoots,
                                            engineTime));
  }

  /**
   * Returns the local engine ID.
   * @return
   *    the local engine ID.
   * @since 1.2
   */
  public OctetString getLocalEngineID() {
    return localEngineID;
  }

  /**
   * Sets the number of engine boots.
   * @param engineBoots
   *    the number of engine boots.
   */
  public void setEngineBoots(int engineBoots) {
    this.timeTable.setEngineBoots(engineBoots);
  }

  /**
   * Returns the number of engine boots counted for the local engine ID.
   * @return
   *    the number of engine boots (zero based).
   */
  public int getEngineBoots() {
    return this.timeTable.getEngineBoots();
  }

  /**
   * Returns the number of seconds since the value of
   * the engineBoots object last changed. When incrementing this object's value
   * would cause it to exceed its maximum, engineBoots is incremented as if a
   * re-initialization had occurred, and this
   * object's value consequently reverts to zero.
   *
   * @return
   *    a positive integer value denoting the number of seconds since
   *    the engineBoots value has been changed.
   * @since 1.2
   */
  public int getEngineTime() {
    return this.timeTable.getEngineTime();
  }

  public SecurityParameters newSecurityParametersInstance() {
    return new UsmSecurityParameters();
  }

  public SecurityStateReference newSecurityStateReference() {
    return new UsmSecurityStateReference();
  }

  private static byte[] buildMessageBuffer(BERInputStream scopedPDU)
      throws IOException
  {
//    return scopedPDU.getBuffer().array();
    scopedPDU.mark(16);
    int readLengthBytes = (int)scopedPDU.getPosition();
    MutableByte mutableByte = new MutableByte();
    int length = BER.decodeHeader(scopedPDU, mutableByte);
    readLengthBytes = (int)scopedPDU.getPosition() - readLengthBytes;
    byte[] buf = new byte[length + readLengthBytes];
    scopedPDU.reset();

    int offset = 0;
    int avail = scopedPDU.available();
    while ((offset < buf.length) && (avail > 0)) {
      int read = scopedPDU.read(buf, offset, buf.length - offset);
      if (read < 0) {
        break;
      }
      offset += read;
    }
    return buf;
  }

  private static byte[] buildWholeMessage(Integer32 snmpVersion,
                                          byte[] scopedPdu,
                                          byte[] globalData,
                                          UsmSecurityParameters
                                                          usmSecurityParameters)
      throws IOException
  {
    int length =
        snmpVersion.getBERLength() +
        globalData.length +
        usmSecurityParameters.getBERLength() +
        scopedPdu.length;
    int totalLength = BER.getBERLengthOfLength(length) + length + 1;

    ByteArrayOutputStream os = new ByteArrayOutputStream(totalLength);
    BER.encodeHeader(os, BER.SEQUENCE, length);
    snmpVersion.encodeBER(os);
    os.write(globalData);
    usmSecurityParameters.encodeBER(os);
    os.write(scopedPdu);
    int secParamsPos = 1 + snmpVersion.getBERLength() +
        BER.getBERLengthOfLength(length+ globalData.length;
    usmSecurityParameters.setSecurityParametersPosition(secParamsPos);
    return os.toByteArray();
  }

  public int generateRequestMessage(int snmpVersion,
                                    byte[] globalData,
                                    int maxMessageSize,
                                    int securityModel,
                                    byte[] securityEngineID,
                                    byte[] securityName,
                                    int securityLevel,
                                    BERInputStream scopedPDU,
                                    SecurityParameters securityParameters,
                                    BEROutputStream wholeMsg) throws IOException {

    return generateResponseMessage(snmpVersion,
                                   globalData,
                                   maxMessageSize,
                                   securityModel,
                                   securityEngineID,
                                   securityName,
                                   securityLevel,
                                   scopedPDU,
                                   null,
                                   securityParameters,
                                   wholeMsg);
  }

  /**
   * Checks if the specified user is known by this USM.
   * @param engineID
   *   the engineID of the user (may be <code>null</code> if any target should
   *   match).
   * @param securityName
   *   the security name of the user to earch for.
   * @return
   *   <code>true</code> if the user is either known for the specified engine ID
   *   or without a specific engine ID (discovery only).
   * @since
   */
  public boolean hasUser(OctetString engineID, OctetString securityName) {
    UsmUserEntry entry = userTable.getUser(engineID, securityName);
    if (entry == null) {
      entry = userTable.getUser(securityName);
      if ((entry == null) && (securityName.length() > 0)) {
        return false;
      }
    }
    return true;
  }

  public UsmUserEntry getUser(OctetString engineID, OctetString securityName) {
    if (logger.isDebugEnabled()) {
      logger.debug("getUser(engineID="+engineID.toHexString()+
                   ", securityName="+securityName.toString()+")");
    }
    UsmUserEntry entry = userTable.getUser(engineID, securityName);
    if (entry == null) {
      entry = userTable.getUser(securityName);
      if ((entry == null) && (securityName.length() > 0)) {
        if (logger.isDebugEnabled()) {
          logger.debug("USM.getUser - User '"+securityName+"' unknown");
        }
        return null;
      }
      else {
        if ((entry == null) || (engineID.length() == 0)) {
          // do not add user
          entry = new UsmUserEntry();
          entry.setUserName(securityName);
          entry.setUsmUser(new UsmUser(securityName, null, null, null, null));
          return entry;
        }
        else {
          // add a new user
          OID authProtocolOID = entry.getUsmUser().getAuthenticationProtocol();
          OID privProtocolOID = entry.getUsmUser().getPrivacyProtocol();
          if (authProtocolOID != null) {
            byte[] authKey;
            if (entry.getUsmUser().isLocalized()) {
              authKey =
                  entry.getUsmUser().getAuthenticationPassphrase().getValue();
            }
            else {
              authKey = securityProtocols.passwordToKey(authProtocolOID,
                  entry.getUsmUser().getAuthenticationPassphrase(),
                  engineID.getValue());
            }
            byte[] privKey = null;
            if (privProtocolOID != null) {
              if (entry.getUsmUser().isLocalized()) {
                privKey = entry.getUsmUser().getPrivacyPassphrase().getValue();
              }
              else {
                privKey = securityProtocols.passwordToKey(privProtocolOID,
                    authProtocolOID,
                    entry.getUsmUser().getPrivacyPassphrase(),
                    engineID.getValue());
              }
            }
            entry = addLocalizedUser(engineID.getValue(), securityName,
                                     authProtocolOID, authKey,
                                     privProtocolOID, privKey);
          }
        }
      }
    }
    return entry;
  }

  public int generateResponseMessage(int snmpVersion,
                                     byte[] globalData,
                                     int maxMessageSize,
                                     int securityModel,
                                     byte[] securityEngineID,
                                     byte[] securityName,
                                     int securityLevel,
                                     BERInputStream scopedPDU,
                                     SecurityStateReference
                                     securityStateReference,
                                     SecurityParameters securityParameters,
                                     BEROutputStream wholeMsg) throws IOException {

    UsmSecurityParameters usmSecurityParams =
        (UsmSecurityParameters) securityParameters;
    if (securityStateReference != null) {
      // this is a response or report
      UsmSecurityStateReference usmSecurityStateReference =
          (UsmSecurityStateReference) securityStateReference;
      if (usmSecurityStateReference.getSecurityEngineID() == null) {
        usmSecurityParams.setAuthoritativeEngineID(securityEngineID);
        usmSecurityStateReference.setSecurityEngineID(securityEngineID);
      }
      if (usmSecurityStateReference.getSecurityName() == null) {
        OctetString userName = new OctetString(securityName);
        usmSecurityStateReference.setSecurityName(userName.getValue());
        usmSecurityParams.setUserName(userName);

        OctetString secName =
            getSecurityName(new OctetString(securityEngineID), userName);

        if ((secName != null) &&
            (secName.length() <= MAXLEN_USMUSERNAME)) {
          usmSecurityParams.setUserName(secName);
        }

      }
      else {
        usmSecurityParams.setUserName(new OctetString(usmSecurityStateReference.getSecurityName()));
      }
      usmSecurityParams.setAuthenticationProtocol(usmSecurityStateReference.
                                                  getAuthenticationProtocol());
      usmSecurityParams.setPrivacyProtocol(usmSecurityStateReference.
                                           getPrivacyProtocol());
      usmSecurityParams.setAuthenticationKey(usmSecurityStateReference.
                                             getAuthenticationKey());
      usmSecurityParams.setPrivacyKey(usmSecurityStateReference.getPrivacyKey());
    }
    else {
      OctetString secEngineID = new OctetString();
      if (securityEngineID != null) {
        secEngineID.setValue(securityEngineID);
      }
      OctetString secName = new OctetString(securityName);

      UsmUserEntry user;
      if (secEngineID.length() == 0) {
        if (isEngineDiscoveryEnabled()) {
          user = new UsmUserEntry();
        }
        else {
          if (logger.isDebugEnabled()) {
            logger.debug("Engine ID unknown and discovery disabled");
          }
          return SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID;
        }
      }
      else {
        user = getUser(secEngineID, secName);
      }
      if (user == null) {
        if (logger.isDebugEnabled()) {
          logger.debug("Security name not found for engineID=" +
                       secEngineID.toHexString() + ", securityName=" +
                       secName.toHexString());
        }
        return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
      }
      AuthenticationProtocol auth =
          securityProtocols.getAuthenticationProtocol(user.getUsmUser().getAuthenticationProtocol());
      PrivacyProtocol priv =
          securityProtocols.getPrivacyProtocol(user.getUsmUser().getPrivacyProtocol());
      usmSecurityParams.setAuthenticationProtocol(auth);
      usmSecurityParams.setPrivacyProtocol(priv);
      usmSecurityParams.setAuthenticationKey(user.getAuthenticationKey());
      usmSecurityParams.setPrivacyKey(user.getPrivacyKey());
      usmSecurityParams.setUserName(user.getUsmUser().getSecurityName());
      usmSecurityParams.setAuthoritativeEngineID(secEngineID.getValue());
    }

    // Check length of userName and engineID
    if (usmSecurityParams.getAuthoritativeEngineID().length > MPv3.MAXLEN_ENGINE_ID) {
      logger.error("Engine ID too long: "+
                   usmSecurityParams.getAuthoritativeEngineID().length+">"+
                   MPv3.MAXLEN_ENGINE_ID+ " for "+
                   new OctetString(usmSecurityParams.getAuthoritativeEngineID())
                   .toHexString());
      return SnmpConstants.SNMPv3_USM_ERROR;
    }
    if (securityName.length > MAXLEN_USMUSERNAME) {
      logger.error("Security name too long: "+
                   usmSecurityParams.getAuthoritativeEngineID().length+">"+
                   MAXLEN_USMUSERNAME+ " for "+
                   new OctetString(securityName).toHexString());
      return SnmpConstants.SNMPv3_USM_ERROR;
    }

    if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
      if (securityStateReference != null) {
        // request or response
        usmSecurityParams.setAuthoritativeEngineBoots(getEngineBoots());
        usmSecurityParams.setAuthoritativeEngineTime(getEngineTime());
      }
      else {
        // get engineBoots, engineTime
        OctetString secEngineID = new OctetString(securityEngineID);
        UsmTimeEntry entry = timeTable.getTime(secEngineID);
        if (entry == null) {
          entry =
              new UsmTimeEntry(secEngineID,
                               usmSecurityParams.getAuthoritativeEngineBoots(),
                               usmSecurityParams.
                               getAuthoritativeEngineTime());

          timeTable.addEntry(entry);
        }
        else {
          usmSecurityParams.setAuthoritativeEngineBoots(entry.getEngineBoots());
          usmSecurityParams.setAuthoritativeEngineTime(entry.
              getLatestReceivedTime());
        }
      }
    }

    if ((securityLevel >= SecurityLevel.AUTH_NOPRIV) &&
        (usmSecurityParams.getAuthenticationProtocol() == null)) {
      return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
    }

    byte[] scopedPduBytes = buildMessageBuffer(scopedPDU);

    if (securityLevel == SecurityLevel.AUTH_PRIV) {
      if (usmSecurityParams.getPrivacyProtocol() == null) {
        if (logger.isDebugEnabled()) {
          logger.debug("Unsupported security level (missing or unsupported privacy protocol)");
        }
        return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
      }
      logger.debug("RFC3414 §3.1.4.a Outgoing message needs to be encrypted");

      DecryptParams decryptParams = new DecryptParams();
      byte[] encryptedScopedPdu =
          usmSecurityParams.getPrivacyProtocol().
          encrypt(scopedPduBytes, 0, scopedPduBytes.length,
                  usmSecurityParams.getPrivacyKey(),
                  usmSecurityParams.getAuthoritativeEngineBoots(),
                  usmSecurityParams.getAuthoritativeEngineTime(),
                  decryptParams);
      if (encryptedScopedPdu == null) {
        if (logger.isDebugEnabled()) {
          logger.debug("Encryption error");
        }
        return SnmpConstants.SNMPv3_USM_ENCRYPTION_ERROR;
      }
      usmSecurityParams.setPrivacyParameters(new OctetString(decryptParams.
          array));
      OctetString encryptedString = new OctetString(encryptedScopedPdu);
      BEROutputStream os =
          new BEROutputStream(ByteBuffer.allocate(encryptedString.getBERLength()));
      encryptedString.encodeBER(os);
      scopedPduBytes = os.getBuffer().array();
    }
    else {
      logger.debug("RFC3414 §3.1.4.b Outgoing message is not encrypted");
      usmSecurityParams.setPrivacyParameters(new OctetString());
    }

    byte[] wholeMessage;
    if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
      /* Build message with authentication */
      byte[] blank =
          new byte[AuthenticationProtocol.MESSAGE_AUTHENTICATION_CODE_LENGTH];
      usmSecurityParams.setAuthenticationParameters(new OctetString(blank));
      wholeMessage =
          buildWholeMessage(new Integer32(snmpVersion),
                            scopedPduBytes, globalData, usmSecurityParams);

      int authParamsPos =
          usmSecurityParams.getAuthParametersPosition() +
          usmSecurityParams.getSecurityParametersPosition();

      boolean authOK = usmSecurityParams.getAuthenticationProtocol().
          authenticate(usmSecurityParams.getAuthenticationKey(),
                       wholeMessage,
                       0,
                       wholeMessage.length,
                       new ByteArrayWindow(wholeMessage,
                                           authParamsPos,
                                           AuthenticationProtocol.
                                           MESSAGE_AUTHENTICATION_CODE_LENGTH));

      if (!authOK) {
        if (logger.isDebugEnabled()) {
          logger.debug("Outgoing message could not be authenticated");
        }
        return SnmpConstants.SNMPv3_USM_AUTHENTICATION_ERROR;
      }
    }
    else {
      // Set engineBoots and enigneTime to zero!
      usmSecurityParams.setAuthoritativeEngineBoots(0);
      usmSecurityParams.setAuthenticationParameters(new OctetString());
      usmSecurityParams.setAuthoritativeEngineTime(0);

      //build Message without authentication
      wholeMessage =
          buildWholeMessage(new Integer32(snmpVersion),
                            scopedPduBytes, globalData, usmSecurityParams);
    }
    ByteBuffer buf =
        (ByteBuffer)ByteBuffer.wrap(wholeMessage).position(wholeMessage.length);
    wholeMsg.setBuffer(buf);
    // not necessary: wholeMsg.write(wholeMessage);
    return SnmpConstants.SNMPv3_USM_OK;
  }

  private OctetString getSecurityName(OctetString engineID,
                                      OctetString userName) {
    if (userName.length() == 0) {
      return userName;
    }
    UsmUserEntry user = userTable.getUser(engineID, userName);
    if (user != null) {
      return user.getUsmUser().getSecurityName();
    }
    else if (isEngineDiscoveryEnabled()) {
      user = userTable.getUser(userName);
      if (user != null) {
        return user.getUsmUser().getSecurityName();
      }
    }
    return null;
  }

  public int processIncomingMsg(int snmpVersion, // typically, SNMP version
                                int maxMessageSize, // of the sending SNMP entity - maxHeaderLength of the MP
                                SecurityParameters securityParameters, // for the received message
                                SecurityModel securityModel, // for the received message
                                int securityLevel, // Level of Security
                                BERInputStream wholeMsg, // as received on the wire
                                // output parameters
                                OctetString securityEngineID, // authoritative SNMP entity
                                OctetString securityName, // identification of the principal
                                BEROutputStream scopedPDU, // message (plaintext) payload
                                Integer32 maxSizeResponseScopedPDU, // maximum size of the Response PDU
                                SecurityStateReference securityStateReference, // reference to security state information, needed for response
                                StatusInformation statusInfo
                                ) throws IOException {

    UsmSecurityParameters usmSecurityParameters =
        (UsmSecurityParameters) securityParameters;
    UsmSecurityStateReference usmSecurityStateReference =
        (UsmSecurityStateReference) securityStateReference;
    securityEngineID.setValue(usmSecurityParameters.getAuthoritativeEngineID());

    byte[] message = buildMessageBuffer(wholeMsg);

    if ((securityEngineID.length() == 0) ||
        (timeTable.checkEngineID(securityEngineID,
                                 isEngineDiscoveryEnabled()) !=
         SnmpConstants.SNMPv3_USM_OK)) {
      // generate report
      if (logger.isDebugEnabled()) {
        logger.debug("RFC3414 §3.2.3 Unknown engine ID: " +
                     securityEngineID.toHexString());
      }
      securityEngineID.setValue(usmSecurityParameters.
                                getAuthoritativeEngineID());
      securityName.setValue(usmSecurityParameters.getUserName().getValue());

      if (statusInfo != null) {
        CounterEvent event = new CounterEvent(this,
                                              SnmpConstants.
                                              usmStatsUnknownEngineIDs);
        fireIncrementCounter(event);
        statusInfo.setSecurityLevel(new Integer32(securityLevel));
        statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
              event.getCurrentValue()));
        }
        return SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID;
      }

    securityName.setValue(usmSecurityParameters.getUserName().getValue());

    int scopedPDUPosition = usmSecurityParameters.getScopedPduPosition();

    // get security name
    if ((usmSecurityParameters.getUserName().length() > 0) ||
        (securityLevel > SecurityLevel.NOAUTH_NOPRIV)) {
      OctetString secName = getSecurityName(securityEngineID,
                                            usmSecurityParameters.getUserName());
      if (secName == null) {
        if (logger.isDebugEnabled()) {
          logger.debug("RFC3414 §3.2.4 Unknown security name: " +
                       securityName.toHexString());
        }
        if (statusInfo != null) {
          CounterEvent event = new CounterEvent(this,
                                                SnmpConstants.usmStatsUnknownUserNames);
          fireIncrementCounter(event);
          statusInfo.setSecurityLevel(new Integer32(SecurityLevel.NOAUTH_NOPRIV));
          statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
              event.getCurrentValue()));
        }
        return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
      }
    }
    else {
      if (logger.isDebugEnabled()) {
        logger.debug("Accepting zero length security name");
      }
      securityName.setValue(new byte[0]);
    }

    if ((usmSecurityParameters.getUserName().length() > 0) ||
        (securityLevel > SecurityLevel.NOAUTH_NOPRIV)) {
      UsmUserEntry user = getUser(securityEngineID, securityName);
      if (user == null) {
        if (logger.isDebugEnabled()) {
          logger.debug("RFC3414 §3.2.4 Unknown security name: " +
                       securityName.toHexString()+ " for engine ID "+
                       securityEngineID.toHexString());
        }
        CounterEvent event =
            new CounterEvent(this, SnmpConstants.usmStatsUnknownUserNames);
        fireIncrementCounter(event);
        if (statusInfo != null) {
          statusInfo.setSecurityLevel(new Integer32(SecurityLevel.NOAUTH_NOPRIV));
          statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
              event.getCurrentValue()));
        }
        return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
      }

      usmSecurityStateReference.setUserName(user.getUserName().getValue());

      AuthenticationProtocol auth =
          securityProtocols.getAuthenticationProtocol(
                                user.getUsmUser().getAuthenticationProtocol());
      PrivacyProtocol priv =
          securityProtocols.getPrivacyProtocol(
                                user.getUsmUser().getPrivacyProtocol());

      if (((securityLevel >= SecurityLevel.AUTH_NOPRIV) && (auth == null)) ||
          (((securityLevel >= SecurityLevel.AUTH_PRIV) && (priv == null)))) {
        if (logger.isDebugEnabled()) {
          logger.debug("RFC3414 §3.2.5 - Unsupported security level: " +
                       securityLevel);
        }
        CounterEvent event =
            new CounterEvent(this, SnmpConstants.usmStatsUnsupportedSecLevels);
        fireIncrementCounter(event);
        statusInfo.setSecurityLevel(new Integer32(SecurityLevel.NOAUTH_NOPRIV));
        statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
            event.getCurrentValue()));
        return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
      }
      if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
        if (statusInfo != null) {
          int authParamsPos =
              usmSecurityParameters.getAuthParametersPosition() +
              usmSecurityParameters.getSecurityParametersPosition();
          boolean authentic =
              auth.isAuthentic(user.getAuthenticationKey(),
                               message, 0, message.length,
                               new ByteArrayWindow(message, authParamsPos,
              AuthenticationProtocol.MESSAGE_AUTHENTICATION_CODE_LENGTH));
          if (!authentic) {
            if (logger.isDebugEnabled()) {
              logger.debug(
                  "RFC3414 §3.2.6 Wrong digest -> authentication failure: " +
                  usmSecurityParameters.getAuthenticationParameters().toHexString());
            }
            CounterEvent event =
                new CounterEvent(this, SnmpConstants.usmStatsWrongDigests);
            fireIncrementCounter(event);
            statusInfo.setSecurityLevel(new Integer32(SecurityLevel.NOAUTH_NOPRIV));
            statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
                event.getCurrentValue()));
            return SnmpConstants.SNMPv3_USM_AUTHENTICATION_FAILURE;
          }
          usmSecurityStateReference.setAuthenticationKey(user.getAuthenticationKey());
          usmSecurityStateReference.setPrivacyKey(user.getPrivacyKey());
          usmSecurityStateReference.setAuthenticationProtocol(auth);
          usmSecurityStateReference.setPrivacyProtocol(priv);
          // check time
          int status = timeTable.checkTime(new UsmTimeEntry(securityEngineID,
            usmSecurityParameters.getAuthoritativeEngineBoots(),
            usmSecurityParameters.getAuthoritativeEngineTime()));

           switch (status) {
            case SnmpConstants.SNMPv3_USM_NOT_IN_TIME_WINDOW: {
              logger.debug("RFC3414 §3.2.7.a Not in time window; engineID='" +
                           securityEngineID +
                           "', engineBoots=" +
                           usmSecurityParameters.getAuthoritativeEngineBoots() +
                           ", engineTime=" +
                           usmSecurityParameters.getAuthoritativeEngineTime());
              CounterEvent event =
                  new CounterEvent(this, SnmpConstants.usmStatsNotInTimeWindows);
              fireIncrementCounter(event);
              statusInfo.setSecurityLevel(new Integer32(SecurityLevel.AUTH_NOPRIV));
              statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
                  event.getCurrentValue()));
              return status;
            }
            case SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID: {
              if (logger.isDebugEnabled()) {
                logger.debug("RFC3414 §3.2.7.b - Unkown engine ID: " +
                             securityEngineID);
              }
              CounterEvent event =
                  new CounterEvent(this, SnmpConstants.usmStatsUnknownEngineIDs);
              fireIncrementCounter(event);
              statusInfo.setSecurityLevel(new Integer32(SecurityLevel.AUTH_NOPRIV));
              statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
                  event.getCurrentValue()));
              return status;

            }
          }
        }
        if (securityLevel >= SecurityLevel.AUTH_PRIV) {
          OctetString privParams = usmSecurityParameters.getPrivacyParameters();
          DecryptParams decryptParams = new DecryptParams(privParams.getValue(),
                                                          0, privParams.length());
          try {
            int scopedPDUHeaderLength = message.length - scopedPDUPosition;
            ByteBuffer bis = ByteBuffer.wrap(message, scopedPDUPosition,
                                             scopedPDUHeaderLength);
            BERInputStream scopedPDUHeader = new BERInputStream(bis);
            long headerStartingPosition = scopedPDUHeader.getPosition();
            int scopedPDULength =
                BER.decodeHeader(scopedPDUHeader, new MutableByte());
            int scopedPDUPayloadPosition =
                scopedPDUPosition +
                  (int)(scopedPDUHeader.getPosition() - headerStartingPosition);
            scopedPDUHeader.close();
            scopedPDUHeader = null;
            byte[] scopedPduBytes =
                priv.decrypt(message, scopedPDUPayloadPosition, scopedPDULength,
                             user.getPrivacyKey(),
                             usmSecurityParameters.getAuthoritativeEngineBoots(),
                             usmSecurityParameters.getAuthoritativeEngineTime(),
                             decryptParams);
            ByteBuffer buf = ByteBuffer.wrap(scopedPduBytes);
            scopedPDU.setFilledBuffer(buf);
          }
          catch (Exception ex) {
            logger.debug("RFC 3414 §3.2.8 Decryption error: "+ex.getMessage());
            return SnmpConstants.SNMPv3_USM_DECRYPTION_ERROR;
          }
        }
        else {
          int scopedPduLength = message.length - scopedPDUPosition;
          ByteBuffer buf =
              ByteBuffer.wrap(message, scopedPDUPosition, scopedPduLength);
          scopedPDU.setFilledBuffer(buf);
        }
      }
      else {
        int scopedPduLength = message.length - scopedPDUPosition;
        ByteBuffer buf =
            ByteBuffer.wrap(message, scopedPDUPosition, scopedPduLength);
        scopedPDU.setFilledBuffer(buf);
      }
    }
    else {
      int scopedPduLength = message.length - scopedPDUPosition;
      ByteBuffer buf =
          ByteBuffer.wrap(message, scopedPDUPosition, scopedPduLength);
      scopedPDU.setFilledBuffer(buf);
    }
    // compute real max size response pdu according  to RFC3414 §3.2.9
    int maxSecParamsOverhead =
        usmSecurityParameters.getBERMaxLength(securityLevel);
    maxSizeResponseScopedPDU.setValue(maxMessageSize -
                                      maxSecParamsOverhead);

    usmSecurityStateReference.setSecurityName(securityName.getValue());
    return SnmpConstants.SNMPv3_USM_OK;
  }

  protected void fireIncrementCounter(CounterEvent e) {
    counterSupport.fireIncrementCounter(e);
  }

  /**
   * Adds an USM user to the internal user name table.
   * @param userName
   *    a user name.
   * @param user
   *    the <code>UsmUser</code> to add.
   */
  public void addUser(OctetString userName, UsmUser user) {
    addUser(userName, new OctetString(), user);
  }

  /**
   * Adds an USM user to the internal user name table and associates it with
   * an authoritative engine ID. This user can only be used with the specified
   * engine ID - other engine IDs cannot be discovered on behalf of this entry.
   * @param userName
   *    a user name.
   * @param engineID
   *    the authoritative engine ID to be associated with this entry. If
   *    <code>engineID</code> is <code>null</code> this method behaves exactly
   *    like {@link #addUser(OctetString userName, UsmUser user)}.
   * @param user
   *    the <code>UsmUser</code> to add.
   */
  public void addUser(OctetString userName, OctetString engineID, UsmUser user) {
    byte[] authKey = null;
    byte[] privKey = null;
    if ((engineID != null) && (engineID.length() > 0)) {
      if (user.getAuthenticationProtocol() != null) {
        if (user.isLocalized()) {
          authKey = user.getAuthenticationPassphrase().getValue();
        }
        else {
          authKey =
              securityProtocols.passwordToKey(user.getAuthenticationProtocol(),
                                              user.getAuthenticationPassphrase(),
                                              engineID.getValue());
        }
        if (user.getPrivacyProtocol() != null) {
          if (user.isLocalized()) {
            privKey = user.getPrivacyPassphrase().getValue();
          }
          else {
            privKey =
                securityProtocols.passwordToKey(user.getPrivacyProtocol(),
                                                user.getAuthenticationProtocol(),
                                                user.getPrivacyPassphrase(),
                                                engineID.getValue());
          }
        }
      }
    }
    OctetString userEngineID;
    if (user.isLocalized()) {
      userEngineID = user.getLocalizationEngineID();
    }
    else {
      userEngineID = (engineID == null) ? new OctetString() : engineID;
    }
    UsmUserEntry entry =
        new UsmUserEntry(userName, userEngineID, user);
    entry.setAuthenticationKey(authKey);
    entry.setPrivacyKey(privKey);
    userTable.addUser(entry);
    fireUsmUserChange(new UsmUserEvent(this, entry, UsmUserEvent.USER_ADDED));
  }

  /**
   * Updates the USM user entry with the same engine ID and user name as the
   * supplied instance and fires an appropriate <code>UsmUserEvent</code>.
   * If the corresponding user entry does not yet exist then it will be added.
   * @param entry
   *    an <code>UsmUserEntry</code> instance not necessarily the same as an
   *    already existing entry.
   * @since 1.2
   */
  public void updateUser(UsmUserEntry entry) {
    UsmUserEntry oldEntry = userTable.addUser(entry);
    fireUsmUserChange(new UsmUserEvent(this, entry,
                                       (oldEntry == null) ?
                                       UsmUserEvent.USER_ADDED:
                                       UsmUserEvent.USER_CHANGED));
  }

  /**
   * Sets the users of this USM. All previously added users and all localized
   * user information will be discarded and replaced by the supplied users.
   *
   * @param users
   *    a possibly empty <code>UsmUser</code> array of users.
   * @since 1.1
   */
  public void setUsers(UsmUser[] users) {
    if ((users == null) || (users.length == 0)) {
      userTable.clear();
    }
    else {
      Vector v = new Vector(users.length);
      for (int i=0; i<users.length; i++) {
        UsmUserEntry entry =
            new UsmUserEntry(users[i].getSecurityName(),
                             (UsmUser)users[i].clone());
        v.add(entry);
      }
      userTable.setUsers(v);
    }
  }

  /**
   * Returns the <code>UsmUserTable</code> instance used by the USM for local
   * storage of USM user information. The returned table should not be modified,
   * because modifications will not be reported to registered
   * <code>UsmUserListener</code>s.
   *
   * @return
   *    the <code>UsmUserTable</code> instance containing the users known by
   *    this USM.
   */
  public UsmUserTable getUserTable() {
    return userTable;
  }

  /**
   * Returns the <code>UsmTimeTable</code> instance used by this USM for holding
   * timing information about the local and remote SNMP entities.
   *
   * @return UsmTimeTable
   * @since 1.6
   */
  public UsmTimeTable getTimeTable() {
    return timeTable;
  }

  /**
   * Removes an USM user from the internal user name table.
   * @param engineID
   *    the authoritative engine ID associated with the user, or
   *    <code>null</code>
   * @param userName
   *    a user name.
   * @return
   *    the removed <code>UsmUser</code> instance associate with the given
   *    <code>userName</code> or <code>null</code> if such a user could not
   *    be found.
   */
  public UsmUser removeUser(OctetString engineID, OctetString userName) {
    UsmUserEntry entry = userTable.removeUser(engineID, userName);
    if (entry != null) {
      fireUsmUserChange(new UsmUserEvent(this, entry, UsmUserEvent.USER_REMOVED));
      return entry.getUsmUser();
    }
    return null;
  }

  /**
   * Removes all users from the USM.
   */
  public void removeAllUsers() {
    userTable.clear();
    fireUsmUserChange(new UsmUserEvent(this, null, UsmUserEvent.USER_REMOVED));
  }

  /**
   * Adds a localized user to the USM.
   * @param engineID
   *    the engine ID for which the user has been localized.
   * @param userName
   *    the user's name.
   * @param authProtocol
   *    the authentication protocol ID.
   * @param authKey
   *    the authentication key.
   * @param privProtocol
   *    the privacy protocol ID.
   * @param privKey
   *    the privacy key.
   * @return
   *    the added <code>UsmUserEntry</code>.
   */
  public UsmUserEntry addLocalizedUser(byte[] engineID,
                                       OctetString userName,
                                       OID authProtocol, byte[] authKey,
                                       OID privProtocol, byte[] privKey) {
    UsmUserEntry newEntry = new UsmUserEntry(engineID, userName,
                                             authProtocol, authKey,
                                             privProtocol, privKey);
    userTable.addUser(newEntry);
    fireUsmUserChange(new UsmUserEvent(this, newEntry,
                                       UsmUserEvent.USER_ADDED));
    return newEntry;
  }

  /**
   * Checks whether engine ID discovery is enabled or not. If enabled, the USM
   * will try to discover unknown engine IDs "on-the-fly" while processing the
   * message.
   * @return
   *    <code>true</code> if discovery is enabled, <code>false</code> otherwise.
   */
  public boolean isEngineDiscoveryEnabled() {
    return engineDiscoveryEnabled;
  }

  /**
   * Enables or disables automatic engine ID discovery.
   * @param engineDiscoveryEnabled
   *    <code>true</code> if discovery should be enabled,
   *    <code>false</code> otherwise.
   */
  public void setEngineDiscoveryEnabled(boolean engineDiscoveryEnabled) {
    this.engineDiscoveryEnabled = engineDiscoveryEnabled;
  }

  /**
   * Removes a <code>UsmUserListener</code>.
   * @param l
   *    a proeviously added <code>UsmUserListener</code>.
   */
  public synchronized void removeUsmUserListener(UsmUserListener l) {
    if (usmUserListeners != null && usmUserListeners.contains(l)) {
      Vector v = (Vector) usmUserListeners.clone();
      v.removeElement(l);
      usmUserListeners = v;
    }
  }

  /**
   * Adds a <code>UsmUserListener</code> that should be informed whenever the
   * internal USM user table is changed.
   *
   * @param l
   *    a <code>UsmUserListener</code> that should be informed about
   *    {@link UsmUserEvent} events.
   */
  public synchronized void addUsmUserListener(UsmUserListener l) {
    Vector v = (usmUserListeners == null) ? new Vector(2) :
        (Vector) usmUserListeners.clone();
    if (!v.contains(l)) {
      v.addElement(l);
      usmUserListeners = v;
    }
  }

  /**
   * Removes the specified engine ID from the internal time cache and thus
   * forces an engine time rediscovery the next time the SNMP engine with
   * the supplied ID is contacted.
   *
   * @param engineID
   *    the SNMP engine ID whose engine time to remove.
   * @since 1.6
   */
  public void removeEngineTime(OctetString engineID) {
    timeTable.removeEntry(engineID);
  }

  /**
   * Fires a <code>UsmUserEvent</code>.
   * @param e
   *    the <code>UsmUserEvent</code> to fire.
   */
  protected void fireUsmUserChange(UsmUserEvent e) {
    if (usmUserListeners != null) {
      Vector listeners = usmUserListeners;
      int count = listeners.size();
      for (int i = 0; i < count; i++) {
        ((UsmUserListener) listeners.elementAt(i)).usmUserChange(e);
      }
    }
  }

  /**
   * Gets the counter support instance that can be used to register for
   * counter incremnetation events.
   * @return
   *    a <code>CounterSupport</code> instance that is used to fire
   *    {@link CounterEvent}.
   */
  public CounterSupport getCounterSupport() {
    return counterSupport;
  }

  /**
   * Returns the security protocol collection used by this USM.
   * @return
   *    a <code>SecurityProtocols</code> instance which is by default the
   *    same instance as returned by {@link SecurityProtocols#getInstance()}.
   * @since 1.2
   */
  public SecurityProtocols getSecurityProtocols() {
    return securityProtocols;
  }

  /**
   * Sets the counter support instance. By default, the singleton instance
   * provided by the {@link CounterSupport} instance is used.
   * @param counterSupport
   *    a <code>CounterSupport</code> subclass instance.
   */
  public void setCounterSupport(CounterSupport counterSupport) {
    if (counterSupport == null) {
      throw new NullPointerException();
    }
    this.counterSupport = counterSupport;
  }
}
TOP

Related Classes of org.snmp4j.security.USM

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.