0001: /*_############################################################################
0002: _##
0003: _## SNMP4J - USM.java
0004: _##
0005: _## Copyright (C) 2003-2008 Frank Fock and Jochen Katz (SNMP4J.org)
0006: _##
0007: _## Licensed under the Apache License, Version 2.0 (the "License");
0008: _## you may not use this file except in compliance with the License.
0009: _## You may obtain a copy of the License at
0010: _##
0011: _## http://www.apache.org/licenses/LICENSE-2.0
0012: _##
0013: _## Unless required by applicable law or agreed to in writing, software
0014: _## distributed under the License is distributed on an "AS IS" BASIS,
0015: _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016: _## See the License for the specific language governing permissions and
0017: _## limitations under the License.
0018: _##
0019: _##########################################################################*/
0020:
0021: package org.snmp4j.security;
0022:
0023: import java.io.*;
0024: import java.nio.ByteBuffer;
0025: import java.util.Vector;
0026:
0027: import org.snmp4j.log.*;
0028: import org.snmp4j.asn1.*;
0029: import org.snmp4j.asn1.BER.*;
0030: import org.snmp4j.event.*;
0031: import org.snmp4j.mp.*;
0032: import org.snmp4j.smi.*;
0033:
0034: /**
0035: * The <code>USM</code> class implements the User Based Security Model (USM)
0036: * as defined in RFC3414.
0037: * <p>
0038: * When a user is added or removed from the USM, a <code>UsmUserEvent</code>
0039: * is fired and forwarded to registered listeners.
0040: *
0041: * @author Frank Fock
0042: * @version 1.2
0043: */
0044: public class USM implements SecurityModel {
0045:
0046: private static final int MAXLEN_USMUSERNAME = 32;
0047:
0048: private static final LogAdapter logger = LogFactory
0049: .getLogger(USM.class);
0050:
0051: // Table containing localized and non-localized users
0052: private UsmUserTable userTable;
0053:
0054: private UsmTimeTable timeTable;
0055:
0056: private OctetString localEngineID;
0057: private boolean engineDiscoveryEnabled = true;
0058:
0059: private SecurityProtocols securityProtocols;
0060: private transient Vector usmUserListeners;
0061: private CounterSupport counterSupport;
0062:
0063: /**
0064: * Creates a USM with the support for the supplied security protocols.
0065: *
0066: * @param securityProtocols
0067: * the security protocols to support.
0068: * @param localEngineID
0069: * the local engine ID.
0070: * @param engineBoots
0071: * the number of engine boots.
0072: * @since 1.2
0073: */
0074: public USM(SecurityProtocols securityProtocols,
0075: OctetString localEngineID, int engineBoots) {
0076: this .localEngineID = localEngineID;
0077: timeTable = new UsmTimeTable(localEngineID, engineBoots);
0078: userTable = new UsmUserTable();
0079: this .securityProtocols = securityProtocols;
0080: counterSupport = CounterSupport.getInstance();
0081: }
0082:
0083: public int getID() {
0084: return SECURITY_MODEL_USM;
0085: }
0086:
0087: /**
0088: * Sets the local engine ID, number of boots, and time after boot.
0089: * @param localEngineID
0090: * the local engine ID.
0091: * @param engineBoots
0092: * the number of engine boots.
0093: * @param engineTime
0094: * the number sendonds since the last boot.
0095: */
0096: public void setLocalEngine(OctetString localEngineID,
0097: int engineBoots, int engineTime) {
0098: timeTable.removeEntry(this .localEngineID);
0099: this .localEngineID = localEngineID;
0100: timeTable.addEntry(new UsmTimeEntry(localEngineID, engineBoots,
0101: engineTime));
0102: }
0103:
0104: /**
0105: * Returns the local engine ID.
0106: * @return
0107: * the local engine ID.
0108: * @since 1.2
0109: */
0110: public OctetString getLocalEngineID() {
0111: return localEngineID;
0112: }
0113:
0114: /**
0115: * Sets the number of engine boots.
0116: * @param engineBoots
0117: * the number of engine boots.
0118: */
0119: public void setEngineBoots(int engineBoots) {
0120: this .timeTable.setEngineBoots(engineBoots);
0121: }
0122:
0123: /**
0124: * Returns the number of engine boots counted for the local engine ID.
0125: * @return
0126: * the number of engine boots (zero based).
0127: */
0128: public int getEngineBoots() {
0129: return this .timeTable.getEngineBoots();
0130: }
0131:
0132: /**
0133: * Returns the number of seconds since the value of
0134: * the engineBoots object last changed. When incrementing this object's value
0135: * would cause it to exceed its maximum, engineBoots is incremented as if a
0136: * re-initialization had occurred, and this
0137: * object's value consequently reverts to zero.
0138: *
0139: * @return
0140: * a positive integer value denoting the number of seconds since
0141: * the engineBoots value has been changed.
0142: * @since 1.2
0143: */
0144: public int getEngineTime() {
0145: return this .timeTable.getEngineTime();
0146: }
0147:
0148: public SecurityParameters newSecurityParametersInstance() {
0149: return new UsmSecurityParameters();
0150: }
0151:
0152: public SecurityStateReference newSecurityStateReference() {
0153: return new UsmSecurityStateReference();
0154: }
0155:
0156: private static byte[] buildMessageBuffer(BERInputStream scopedPDU)
0157: throws IOException {
0158: // return scopedPDU.getBuffer().array();
0159: scopedPDU.mark(16);
0160: int readLengthBytes = (int) scopedPDU.getPosition();
0161: MutableByte mutableByte = new MutableByte();
0162: int length = BER.decodeHeader(scopedPDU, mutableByte);
0163: readLengthBytes = (int) scopedPDU.getPosition()
0164: - readLengthBytes;
0165: byte[] buf = new byte[length + readLengthBytes];
0166: scopedPDU.reset();
0167:
0168: int offset = 0;
0169: int avail = scopedPDU.available();
0170: while ((offset < buf.length) && (avail > 0)) {
0171: int read = scopedPDU.read(buf, offset, buf.length - offset);
0172: if (read < 0) {
0173: break;
0174: }
0175: offset += read;
0176: }
0177: return buf;
0178: }
0179:
0180: private static byte[] buildWholeMessage(Integer32 snmpVersion,
0181: byte[] scopedPdu, byte[] globalData,
0182: UsmSecurityParameters usmSecurityParameters)
0183: throws IOException {
0184: int length = snmpVersion.getBERLength() + globalData.length
0185: + usmSecurityParameters.getBERLength()
0186: + scopedPdu.length;
0187: int totalLength = BER.getBERLengthOfLength(length) + length + 1;
0188:
0189: ByteArrayOutputStream os = new ByteArrayOutputStream(
0190: totalLength);
0191: BER.encodeHeader(os, BER.SEQUENCE, length);
0192: snmpVersion.encodeBER(os);
0193: os.write(globalData);
0194: usmSecurityParameters.encodeBER(os);
0195: os.write(scopedPdu);
0196: int secParamsPos = 1 + snmpVersion.getBERLength()
0197: + BER.getBERLengthOfLength(length) + globalData.length;
0198: usmSecurityParameters
0199: .setSecurityParametersPosition(secParamsPos);
0200: return os.toByteArray();
0201: }
0202:
0203: public int generateRequestMessage(int snmpVersion,
0204: byte[] globalData, int maxMessageSize, int securityModel,
0205: byte[] securityEngineID, byte[] securityName,
0206: int securityLevel, BERInputStream scopedPDU,
0207: SecurityParameters securityParameters,
0208: BEROutputStream wholeMsg) throws IOException {
0209:
0210: return generateResponseMessage(snmpVersion, globalData,
0211: maxMessageSize, securityModel, securityEngineID,
0212: securityName, securityLevel, scopedPDU, null,
0213: securityParameters, wholeMsg);
0214: }
0215:
0216: public UsmUserEntry getUser(OctetString engineID,
0217: OctetString securityName) {
0218: if (logger.isDebugEnabled()) {
0219: logger
0220: .debug("getUser(engineID=" + engineID.toHexString()
0221: + ", securityName="
0222: + securityName.toString() + ")");
0223: }
0224: UsmUserEntry entry = userTable.getUser(engineID, securityName);
0225: if (entry == null) {
0226: entry = userTable.getUser(securityName);
0227: if ((entry == null) && (securityName.length() > 0)) {
0228: if (logger.isDebugEnabled()) {
0229: logger.debug("USM.getUser - User '" + securityName
0230: + "' unknown");
0231: }
0232: return null;
0233: } else {
0234: if ((entry == null) || (engineID.length() == 0)) {
0235: // do not add user
0236: entry = new UsmUserEntry();
0237: entry.setUserName(securityName);
0238: entry.setUsmUser(new UsmUser(securityName, null,
0239: null, null, null));
0240: return entry;
0241: } else {
0242: // add a new user
0243: OID authProtocolOID = entry.getUsmUser()
0244: .getAuthenticationProtocol();
0245: OID privProtocolOID = entry.getUsmUser()
0246: .getPrivacyProtocol();
0247: if (authProtocolOID != null) {
0248: byte[] authKey;
0249: if (entry.getUsmUser().isLocalized()) {
0250: authKey = entry.getUsmUser()
0251: .getAuthenticationPassphrase()
0252: .getValue();
0253: } else {
0254: authKey = securityProtocols
0255: .passwordToKey(
0256: authProtocolOID,
0257: entry
0258: .getUsmUser()
0259: .getAuthenticationPassphrase(),
0260: engineID.getValue());
0261: }
0262: byte[] privKey = null;
0263: if (privProtocolOID != null) {
0264: if (entry.getUsmUser().isLocalized()) {
0265: privKey = entry.getUsmUser()
0266: .getPrivacyPassphrase()
0267: .getValue();
0268: } else {
0269: privKey = securityProtocols
0270: .passwordToKey(
0271: privProtocolOID,
0272: authProtocolOID,
0273: entry
0274: .getUsmUser()
0275: .getPrivacyPassphrase(),
0276: engineID.getValue());
0277: }
0278: }
0279: entry = addLocalizedUser(engineID.getValue(),
0280: securityName, authProtocolOID, authKey,
0281: privProtocolOID, privKey);
0282: }
0283: }
0284: }
0285: }
0286: return entry;
0287: }
0288:
0289: public int generateResponseMessage(int snmpVersion,
0290: byte[] globalData, int maxMessageSize, int securityModel,
0291: byte[] securityEngineID, byte[] securityName,
0292: int securityLevel, BERInputStream scopedPDU,
0293: SecurityStateReference securityStateReference,
0294: SecurityParameters securityParameters,
0295: BEROutputStream wholeMsg) throws IOException {
0296:
0297: UsmSecurityParameters usmSecurityParams = (UsmSecurityParameters) securityParameters;
0298: if (securityStateReference != null) {
0299: // this is a response or report
0300: UsmSecurityStateReference usmSecurityStateReference = (UsmSecurityStateReference) securityStateReference;
0301: if (usmSecurityStateReference.getSecurityEngineID() == null) {
0302: usmSecurityParams
0303: .setAuthoritativeEngineID(securityEngineID);
0304: usmSecurityStateReference
0305: .setSecurityEngineID(securityEngineID);
0306: }
0307: if (usmSecurityStateReference.getSecurityName() == null) {
0308: OctetString userName = new OctetString(securityName);
0309: usmSecurityStateReference.setSecurityName(userName
0310: .getValue());
0311: usmSecurityParams.setUserName(userName);
0312:
0313: OctetString secName = getSecurityName(new OctetString(
0314: securityEngineID), userName);
0315:
0316: if ((secName != null)
0317: && (secName.length() <= MAXLEN_USMUSERNAME)) {
0318: usmSecurityParams.setUserName(secName);
0319: }
0320:
0321: } else {
0322: usmSecurityParams.setUserName(new OctetString(
0323: usmSecurityStateReference.getSecurityName()));
0324: }
0325: usmSecurityParams
0326: .setAuthenticationProtocol(usmSecurityStateReference
0327: .getAuthenticationProtocol());
0328: usmSecurityParams
0329: .setPrivacyProtocol(usmSecurityStateReference
0330: .getPrivacyProtocol());
0331: usmSecurityParams
0332: .setAuthenticationKey(usmSecurityStateReference
0333: .getAuthenticationKey());
0334: usmSecurityParams.setPrivacyKey(usmSecurityStateReference
0335: .getPrivacyKey());
0336: } else {
0337: OctetString secEngineID = new OctetString();
0338: if (securityEngineID != null) {
0339: secEngineID.setValue(securityEngineID);
0340: }
0341: OctetString secName = new OctetString(securityName);
0342:
0343: UsmUserEntry user;
0344: if (secEngineID.length() == 0) {
0345: if (isEngineDiscoveryEnabled()) {
0346: user = new UsmUserEntry();
0347: } else {
0348: if (logger.isDebugEnabled()) {
0349: logger
0350: .debug("Engine ID unknown and discovery disabled");
0351: }
0352: return SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID;
0353: }
0354: } else {
0355: user = getUser(secEngineID, secName);
0356: }
0357: if (user == null) {
0358: if (logger.isDebugEnabled()) {
0359: logger
0360: .debug("Security name not found for engineID="
0361: + secEngineID.toHexString()
0362: + ", securityName="
0363: + secName.toHexString());
0364: }
0365: return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
0366: }
0367: AuthenticationProtocol auth = securityProtocols
0368: .getAuthenticationProtocol(user.getUsmUser()
0369: .getAuthenticationProtocol());
0370: PrivacyProtocol priv = securityProtocols
0371: .getPrivacyProtocol(user.getUsmUser()
0372: .getPrivacyProtocol());
0373: usmSecurityParams.setAuthenticationProtocol(auth);
0374: usmSecurityParams.setPrivacyProtocol(priv);
0375: usmSecurityParams.setAuthenticationKey(user
0376: .getAuthenticationKey());
0377: usmSecurityParams.setPrivacyKey(user.getPrivacyKey());
0378: usmSecurityParams.setUserName(user.getUsmUser()
0379: .getSecurityName());
0380: usmSecurityParams.setAuthoritativeEngineID(secEngineID
0381: .getValue());
0382: }
0383:
0384: // Check length of userName and engineID
0385: if (usmSecurityParams.getAuthoritativeEngineID().length > MPv3.MAXLEN_ENGINE_ID) {
0386: logger
0387: .error("Engine ID too long: "
0388: + usmSecurityParams
0389: .getAuthoritativeEngineID().length
0390: + ">"
0391: + MPv3.MAXLEN_ENGINE_ID
0392: + " for "
0393: + new OctetString(usmSecurityParams
0394: .getAuthoritativeEngineID())
0395: .toHexString());
0396: return SnmpConstants.SNMPv3_USM_ERROR;
0397: }
0398: if (securityName.length > MAXLEN_USMUSERNAME) {
0399: logger
0400: .error("Security name too long: "
0401: + usmSecurityParams
0402: .getAuthoritativeEngineID().length
0403: + ">"
0404: + MAXLEN_USMUSERNAME
0405: + " for "
0406: + new OctetString(securityName)
0407: .toHexString());
0408: return SnmpConstants.SNMPv3_USM_ERROR;
0409: }
0410:
0411: if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
0412: if (securityStateReference != null) {
0413: // request or response
0414: usmSecurityParams
0415: .setAuthoritativeEngineBoots(getEngineBoots());
0416: usmSecurityParams
0417: .setAuthoritativeEngineTime(getEngineTime());
0418: } else {
0419: // get engineBoots, engineTime
0420: OctetString secEngineID = new OctetString(
0421: securityEngineID);
0422: UsmTimeEntry entry = timeTable.getTime(secEngineID);
0423: if (entry == null) {
0424: entry = new UsmTimeEntry(secEngineID,
0425: usmSecurityParams
0426: .getAuthoritativeEngineBoots(),
0427: usmSecurityParams
0428: .getAuthoritativeEngineTime());
0429:
0430: timeTable.addEntry(entry);
0431: } else {
0432: usmSecurityParams.setAuthoritativeEngineBoots(entry
0433: .getEngineBoots());
0434: usmSecurityParams.setAuthoritativeEngineTime(entry
0435: .getLatestReceivedTime());
0436: }
0437: }
0438: }
0439:
0440: if ((securityLevel >= SecurityLevel.AUTH_NOPRIV)
0441: && (usmSecurityParams.getAuthenticationProtocol() == null)) {
0442: return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
0443: }
0444:
0445: byte[] scopedPduBytes = buildMessageBuffer(scopedPDU);
0446:
0447: if (securityLevel == SecurityLevel.AUTH_PRIV) {
0448: if (usmSecurityParams.getPrivacyProtocol() == null) {
0449: if (logger.isDebugEnabled()) {
0450: logger
0451: .debug("Unsupported security level (missing or unsupported privacy protocol)");
0452: }
0453: return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
0454: }
0455: logger
0456: .debug("RFC3414 §3.1.4.a Outgoing message needs to be encrypted");
0457:
0458: DecryptParams decryptParams = new DecryptParams();
0459: byte[] encryptedScopedPdu = usmSecurityParams
0460: .getPrivacyProtocol().encrypt(
0461: scopedPduBytes,
0462: 0,
0463: scopedPduBytes.length,
0464: usmSecurityParams.getPrivacyKey(),
0465: usmSecurityParams
0466: .getAuthoritativeEngineBoots(),
0467: usmSecurityParams
0468: .getAuthoritativeEngineTime(),
0469: decryptParams);
0470: if (encryptedScopedPdu == null) {
0471: if (logger.isDebugEnabled()) {
0472: logger.debug("Encryption error");
0473: }
0474: return SnmpConstants.SNMPv3_USM_ENCRYPTION_ERROR;
0475: }
0476: usmSecurityParams.setPrivacyParameters(new OctetString(
0477: decryptParams.array));
0478: OctetString encryptedString = new OctetString(
0479: encryptedScopedPdu);
0480: BEROutputStream os = new BEROutputStream(ByteBuffer
0481: .allocate(encryptedString.getBERLength()));
0482: encryptedString.encodeBER(os);
0483: scopedPduBytes = os.getBuffer().array();
0484: } else {
0485: logger
0486: .debug("RFC3414 §3.1.4.b Outgoing message is not encrypted");
0487: usmSecurityParams.setPrivacyParameters(new OctetString());
0488: }
0489:
0490: byte[] wholeMessage;
0491: if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
0492: /* Build message with authentication */
0493: byte[] blank = new byte[AuthenticationProtocol.MESSAGE_AUTHENTICATION_CODE_LENGTH];
0494: usmSecurityParams
0495: .setAuthenticationParameters(new OctetString(blank));
0496: wholeMessage = buildWholeMessage(
0497: new Integer32(snmpVersion), scopedPduBytes,
0498: globalData, usmSecurityParams);
0499:
0500: int authParamsPos = usmSecurityParams
0501: .getAuthParametersPosition()
0502: + usmSecurityParams.getSecurityParametersPosition();
0503:
0504: boolean authOK = usmSecurityParams
0505: .getAuthenticationProtocol()
0506: .authenticate(
0507: usmSecurityParams.getAuthenticationKey(),
0508: wholeMessage,
0509: 0,
0510: wholeMessage.length,
0511: new ByteArrayWindow(
0512: wholeMessage,
0513: authParamsPos,
0514: AuthenticationProtocol.MESSAGE_AUTHENTICATION_CODE_LENGTH));
0515:
0516: if (!authOK) {
0517: if (logger.isDebugEnabled()) {
0518: logger
0519: .debug("Outgoing message could not be authenticated");
0520: }
0521: return SnmpConstants.SNMPv3_USM_AUTHENTICATION_ERROR;
0522: }
0523: } else {
0524: // Set engineBoots and enigneTime to zero!
0525: usmSecurityParams.setAuthoritativeEngineBoots(0);
0526: usmSecurityParams
0527: .setAuthenticationParameters(new OctetString());
0528: usmSecurityParams.setAuthoritativeEngineTime(0);
0529:
0530: //build Message without authentication
0531: wholeMessage = buildWholeMessage(
0532: new Integer32(snmpVersion), scopedPduBytes,
0533: globalData, usmSecurityParams);
0534: }
0535: ByteBuffer buf = (ByteBuffer) ByteBuffer.wrap(wholeMessage)
0536: .position(wholeMessage.length);
0537: wholeMsg.setBuffer(buf);
0538: // not necessary: wholeMsg.write(wholeMessage);
0539: return SnmpConstants.SNMPv3_USM_OK;
0540: }
0541:
0542: private OctetString getSecurityName(OctetString engineID,
0543: OctetString userName) {
0544: if (userName.length() == 0) {
0545: return userName;
0546: }
0547: UsmUserEntry user = userTable.getUser(engineID, userName);
0548: if (user != null) {
0549: return user.getUsmUser().getSecurityName();
0550: } else if (isEngineDiscoveryEnabled()) {
0551: user = userTable.getUser(userName);
0552: if (user != null) {
0553: return user.getUsmUser().getSecurityName();
0554: }
0555: }
0556: return null;
0557: }
0558:
0559: public int processIncomingMsg(int snmpVersion, // typically, SNMP version
0560: int maxMessageSize, // of the sending SNMP entity - maxHeaderLength of the MP
0561: SecurityParameters securityParameters, // for the received message
0562: SecurityModel securityModel, // for the received message
0563: int securityLevel, // Level of Security
0564: BERInputStream wholeMsg, // as received on the wire
0565: // output parameters
0566: OctetString securityEngineID, // authoritative SNMP entity
0567: OctetString securityName, // identification of the principal
0568: BEROutputStream scopedPDU, // message (plaintext) payload
0569: Integer32 maxSizeResponseScopedPDU, // maximum size of the Response PDU
0570: SecurityStateReference securityStateReference, // reference to security state information, needed for response
0571: StatusInformation statusInfo) throws IOException {
0572:
0573: UsmSecurityParameters usmSecurityParameters = (UsmSecurityParameters) securityParameters;
0574: UsmSecurityStateReference usmSecurityStateReference = (UsmSecurityStateReference) securityStateReference;
0575: securityEngineID.setValue(usmSecurityParameters
0576: .getAuthoritativeEngineID());
0577:
0578: byte[] message = buildMessageBuffer(wholeMsg);
0579:
0580: if ((securityEngineID.length() == 0)
0581: || (timeTable.checkEngineID(securityEngineID,
0582: isEngineDiscoveryEnabled()) != SnmpConstants.SNMPv3_USM_OK)) {
0583: // generate report
0584: if (logger.isDebugEnabled()) {
0585: logger.debug("RFC3414 §3.2.3 Unknown engine ID: "
0586: + securityEngineID.toHexString());
0587: }
0588: securityEngineID.setValue(usmSecurityParameters
0589: .getAuthoritativeEngineID());
0590: securityName.setValue(usmSecurityParameters.getUserName()
0591: .getValue());
0592:
0593: if (statusInfo != null) {
0594: CounterEvent event = new CounterEvent(this ,
0595: SnmpConstants.usmStatsUnknownEngineIDs);
0596: fireIncrementCounter(event);
0597: statusInfo
0598: .setSecurityLevel(new Integer32(securityLevel));
0599: statusInfo.setErrorIndication(new VariableBinding(event
0600: .getOid(), event.getCurrentValue()));
0601: }
0602: return SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID;
0603: }
0604:
0605: securityName.setValue(usmSecurityParameters.getUserName()
0606: .getValue());
0607:
0608: int scopedPDUPosition = usmSecurityParameters
0609: .getScopedPduPosition();
0610:
0611: // get security name
0612: if ((usmSecurityParameters.getUserName().length() > 0)
0613: || (securityLevel > SecurityLevel.NOAUTH_NOPRIV)) {
0614: OctetString secName = getSecurityName(securityEngineID,
0615: usmSecurityParameters.getUserName());
0616: if (secName == null) {
0617: if (logger.isDebugEnabled()) {
0618: logger
0619: .debug("RFC3414 §3.2.4 Unknown security name: "
0620: + securityName.toHexString());
0621: }
0622: if (statusInfo != null) {
0623: CounterEvent event = new CounterEvent(this ,
0624: SnmpConstants.usmStatsUnknownUserNames);
0625: fireIncrementCounter(event);
0626: statusInfo.setSecurityLevel(new Integer32(
0627: SecurityLevel.NOAUTH_NOPRIV));
0628: statusInfo.setErrorIndication(new VariableBinding(
0629: event.getOid(), event.getCurrentValue()));
0630: }
0631: return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
0632: }
0633: } else {
0634: if (logger.isDebugEnabled()) {
0635: logger.debug("Accepting zero length security name");
0636: }
0637: securityName.setValue(new byte[0]);
0638: }
0639:
0640: if ((usmSecurityParameters.getUserName().length() > 0)
0641: || (securityLevel > SecurityLevel.NOAUTH_NOPRIV)) {
0642: UsmUserEntry user = getUser(securityEngineID, securityName);
0643: if (user == null) {
0644: if (logger.isDebugEnabled()) {
0645: logger
0646: .debug("RFC3414 §3.2.4 Unknown security name: "
0647: + securityName.toHexString()
0648: + " for engine ID "
0649: + securityEngineID.toHexString());
0650: }
0651: CounterEvent event = new CounterEvent(this ,
0652: SnmpConstants.usmStatsUnknownUserNames);
0653: fireIncrementCounter(event);
0654: if (statusInfo != null) {
0655: statusInfo.setSecurityLevel(new Integer32(
0656: SecurityLevel.NOAUTH_NOPRIV));
0657: statusInfo.setErrorIndication(new VariableBinding(
0658: event.getOid(), event.getCurrentValue()));
0659: }
0660: return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
0661: }
0662:
0663: usmSecurityStateReference.setUserName(user.getUserName()
0664: .getValue());
0665:
0666: AuthenticationProtocol auth = securityProtocols
0667: .getAuthenticationProtocol(user.getUsmUser()
0668: .getAuthenticationProtocol());
0669: PrivacyProtocol priv = securityProtocols
0670: .getPrivacyProtocol(user.getUsmUser()
0671: .getPrivacyProtocol());
0672:
0673: if (((securityLevel >= SecurityLevel.AUTH_NOPRIV) && (auth == null))
0674: || (((securityLevel >= SecurityLevel.AUTH_PRIV) && (priv == null)))) {
0675: if (logger.isDebugEnabled()) {
0676: logger
0677: .debug("RFC3414 §3.2.5 - Unsupported security level: "
0678: + securityLevel);
0679: }
0680: CounterEvent event = new CounterEvent(this ,
0681: SnmpConstants.usmStatsUnsupportedSecLevels);
0682: fireIncrementCounter(event);
0683: statusInfo.setSecurityLevel(new Integer32(
0684: SecurityLevel.NOAUTH_NOPRIV));
0685: statusInfo.setErrorIndication(new VariableBinding(event
0686: .getOid(), event.getCurrentValue()));
0687: return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
0688: }
0689: if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
0690: if (statusInfo != null) {
0691: int authParamsPos = usmSecurityParameters
0692: .getAuthParametersPosition()
0693: + usmSecurityParameters
0694: .getSecurityParametersPosition();
0695: boolean authentic = auth
0696: .isAuthentic(
0697: user.getAuthenticationKey(),
0698: message,
0699: 0,
0700: message.length,
0701: new ByteArrayWindow(
0702: message,
0703: authParamsPos,
0704: AuthenticationProtocol.MESSAGE_AUTHENTICATION_CODE_LENGTH));
0705: if (!authentic) {
0706: if (logger.isDebugEnabled()) {
0707: logger
0708: .debug("RFC3414 §3.2.6 Wrong digest -> authentication failure: "
0709: + usmSecurityParameters
0710: .getAuthenticationParameters()
0711: .toHexString());
0712: }
0713: CounterEvent event = new CounterEvent(this ,
0714: SnmpConstants.usmStatsWrongDigests);
0715: fireIncrementCounter(event);
0716: statusInfo.setSecurityLevel(new Integer32(
0717: SecurityLevel.NOAUTH_NOPRIV));
0718: statusInfo
0719: .setErrorIndication(new VariableBinding(
0720: event.getOid(), event
0721: .getCurrentValue()));
0722: return SnmpConstants.SNMPv3_USM_AUTHENTICATION_FAILURE;
0723: }
0724: usmSecurityStateReference.setAuthenticationKey(user
0725: .getAuthenticationKey());
0726: usmSecurityStateReference.setPrivacyKey(user
0727: .getPrivacyKey());
0728: usmSecurityStateReference
0729: .setAuthenticationProtocol(auth);
0730: usmSecurityStateReference.setPrivacyProtocol(priv);
0731: // check time
0732: int status = timeTable.checkTime(new UsmTimeEntry(
0733: securityEngineID, usmSecurityParameters
0734: .getAuthoritativeEngineBoots(),
0735: usmSecurityParameters
0736: .getAuthoritativeEngineTime()));
0737:
0738: switch (status) {
0739: case SnmpConstants.SNMPv3_USM_NOT_IN_TIME_WINDOW: {
0740: logger
0741: .debug("RFC3414 §3.2.7.a Not in time window; engineID='"
0742: + securityEngineID
0743: + "', engineBoots="
0744: + usmSecurityParameters
0745: .getAuthoritativeEngineBoots()
0746: + ", engineTime="
0747: + usmSecurityParameters
0748: .getAuthoritativeEngineTime());
0749: CounterEvent event = new CounterEvent(this ,
0750: SnmpConstants.usmStatsNotInTimeWindows);
0751: fireIncrementCounter(event);
0752: statusInfo.setSecurityLevel(new Integer32(
0753: SecurityLevel.AUTH_NOPRIV));
0754: statusInfo
0755: .setErrorIndication(new VariableBinding(
0756: event.getOid(), event
0757: .getCurrentValue()));
0758: return status;
0759: }
0760: case SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID: {
0761: if (logger.isDebugEnabled()) {
0762: logger
0763: .debug("RFC3414 §3.2.7.b - Unkown engine ID: "
0764: + securityEngineID);
0765: }
0766: CounterEvent event = new CounterEvent(this ,
0767: SnmpConstants.usmStatsNotInTimeWindows);
0768: fireIncrementCounter(event);
0769: statusInfo.setSecurityLevel(new Integer32(
0770: SecurityLevel.AUTH_NOPRIV));
0771: statusInfo
0772: .setErrorIndication(new VariableBinding(
0773: event.getOid(), event
0774: .getCurrentValue()));
0775: return status;
0776:
0777: }
0778: }
0779: }
0780: if (securityLevel >= SecurityLevel.AUTH_PRIV) {
0781: OctetString privParams = usmSecurityParameters
0782: .getPrivacyParameters();
0783: DecryptParams decryptParams = new DecryptParams(
0784: privParams.getValue(), 0, privParams
0785: .length());
0786: try {
0787: int scopedPDUHeaderLength = message.length
0788: - scopedPDUPosition;
0789: ByteBuffer bis = ByteBuffer.wrap(message,
0790: scopedPDUPosition,
0791: scopedPDUHeaderLength);
0792: BERInputStream scopedPDUHeader = new BERInputStream(
0793: bis);
0794: long headerStartingPosition = scopedPDUHeader
0795: .getPosition();
0796: int scopedPDULength = BER.decodeHeader(
0797: scopedPDUHeader, new MutableByte());
0798: int scopedPDUPayloadPosition = scopedPDUPosition
0799: + (int) (scopedPDUHeader.getPosition() - headerStartingPosition);
0800: scopedPDUHeader.close();
0801: scopedPDUHeader = null;
0802: byte[] scopedPduBytes = priv.decrypt(message,
0803: scopedPDUPayloadPosition,
0804: scopedPDULength, user.getPrivacyKey(),
0805: usmSecurityParameters
0806: .getAuthoritativeEngineBoots(),
0807: usmSecurityParameters
0808: .getAuthoritativeEngineTime(),
0809: decryptParams);
0810: ByteBuffer buf = ByteBuffer
0811: .wrap(scopedPduBytes);
0812: scopedPDU.setFilledBuffer(buf);
0813: } catch (Exception ex) {
0814: logger
0815: .debug("RFC 3414 §3.2.8 Decryption error: "
0816: + ex.getMessage());
0817: return SnmpConstants.SNMPv3_USM_DECRYPTION_ERROR;
0818: }
0819: } else {
0820: int scopedPduLength = message.length
0821: - scopedPDUPosition;
0822: ByteBuffer buf = ByteBuffer.wrap(message,
0823: scopedPDUPosition, scopedPduLength);
0824: scopedPDU.setFilledBuffer(buf);
0825: }
0826: } else {
0827: int scopedPduLength = message.length
0828: - scopedPDUPosition;
0829: ByteBuffer buf = ByteBuffer.wrap(message,
0830: scopedPDUPosition, scopedPduLength);
0831: scopedPDU.setFilledBuffer(buf);
0832: }
0833: } else {
0834: int scopedPduLength = message.length - scopedPDUPosition;
0835: ByteBuffer buf = ByteBuffer.wrap(message,
0836: scopedPDUPosition, scopedPduLength);
0837: scopedPDU.setFilledBuffer(buf);
0838: }
0839: // compute real max size response pdu according to RFC3414 §3.2.9
0840: int maxSecParamsOverhead = usmSecurityParameters
0841: .getBERMaxLength(securityLevel);
0842: maxSizeResponseScopedPDU.setValue(maxMessageSize
0843: - maxSecParamsOverhead);
0844:
0845: usmSecurityStateReference.setSecurityName(securityName
0846: .getValue());
0847: return SnmpConstants.SNMPv3_USM_OK;
0848: }
0849:
0850: protected void fireIncrementCounter(CounterEvent e) {
0851: counterSupport.fireIncrementCounter(e);
0852: }
0853:
0854: /**
0855: * Adds an USM user to the internal user name table.
0856: * @param userName
0857: * a user name.
0858: * @param user
0859: * the <code>UsmUser</code> to add.
0860: */
0861: public void addUser(OctetString userName, UsmUser user) {
0862: addUser(userName, new OctetString(), user);
0863: }
0864:
0865: /**
0866: * Adds an USM user to the internal user name table and associates it with
0867: * an authoritative engine ID. This user can only be used with the specified
0868: * engine ID - other engine IDs cannot be discovered on behalf of this entry.
0869: * @param userName
0870: * a user name.
0871: * @param engineID
0872: * the authoritative engine ID to be associated with this entry. If
0873: * <code>engineID</code> is <code>null</code> this method behaves exactly
0874: * like {@link #addUser(OctetString userName, UsmUser user)}.
0875: * @param user
0876: * the <code>UsmUser</code> to add.
0877: */
0878: public void addUser(OctetString userName, OctetString engineID,
0879: UsmUser user) {
0880: byte[] authKey = null;
0881: byte[] privKey = null;
0882: if ((engineID != null) && (engineID.length() > 0)) {
0883: if (user.getAuthenticationProtocol() != null) {
0884: if (user.isLocalized()) {
0885: authKey = user.getAuthenticationPassphrase()
0886: .getValue();
0887: } else {
0888: authKey = securityProtocols.passwordToKey(user
0889: .getAuthenticationProtocol(), user
0890: .getAuthenticationPassphrase(), engineID
0891: .getValue());
0892: }
0893: if (user.getPrivacyProtocol() != null) {
0894: if (user.isLocalized()) {
0895: privKey = user.getPrivacyPassphrase()
0896: .getValue();
0897: } else {
0898: privKey = securityProtocols.passwordToKey(user
0899: .getPrivacyProtocol(), user
0900: .getAuthenticationProtocol(), user
0901: .getPrivacyPassphrase(), engineID
0902: .getValue());
0903: }
0904: }
0905: }
0906: }
0907: OctetString userEngineID;
0908: if (user.isLocalized()) {
0909: userEngineID = user.getLocalizationEngineID();
0910: } else {
0911: userEngineID = (engineID == null) ? new OctetString()
0912: : engineID;
0913: }
0914: UsmUserEntry entry = new UsmUserEntry(userName, userEngineID,
0915: user);
0916: entry.setAuthenticationKey(authKey);
0917: entry.setPrivacyKey(privKey);
0918: userTable.addUser(entry);
0919: fireUsmUserChange(new UsmUserEvent(this , entry,
0920: UsmUserEvent.USER_ADDED));
0921: }
0922:
0923: /**
0924: * Updates the USM user entry with the same engine ID and user name as the
0925: * supplied instance and fires an appropriate <code>UsmUserEvent</code>.
0926: * If the corresponding user entry does not yet exist then it will be added.
0927: * @param entry
0928: * an <code>UsmUserEntry</code> instance not necessarily the same as an
0929: * already existing entry.
0930: * @since 1.2
0931: */
0932: public void updateUser(UsmUserEntry entry) {
0933: UsmUserEntry oldEntry = userTable.addUser(entry);
0934: fireUsmUserChange(new UsmUserEvent(this , entry,
0935: (oldEntry == null) ? UsmUserEvent.USER_ADDED
0936: : UsmUserEvent.USER_CHANGED));
0937: }
0938:
0939: /**
0940: * Sets the users of this USM. All previously added users and all localized
0941: * user information will be discarded and replaced by the supplied users.
0942: *
0943: * @param users
0944: * a possibly empty <code>UsmUser</code> array of users.
0945: * @since 1.1
0946: */
0947: public void setUsers(UsmUser[] users) {
0948: if ((users == null) || (users.length == 0)) {
0949: userTable.clear();
0950: } else {
0951: Vector v = new Vector(users.length);
0952: for (int i = 0; i < users.length; i++) {
0953: UsmUserEntry entry = new UsmUserEntry(users[i]
0954: .getSecurityName(), (UsmUser) users[i].clone());
0955: v.add(entry);
0956: }
0957: userTable.setUsers(v);
0958: }
0959: }
0960:
0961: /**
0962: * Returns the <code>UsmUserTable</code> instance used by the USM for local
0963: * storage of USM user information. The returned table should not be modified,
0964: * because modifications will not be reported to registered
0965: * <code>UsmUserListener</code>s.
0966: *
0967: * @return
0968: * the <code>UsmUserTable</code> instance containing the users known by
0969: * this USM.
0970: */
0971: public UsmUserTable getUserTable() {
0972: return userTable;
0973: }
0974:
0975: /**
0976: * Returns the <code>UsmTimeTable</code> instance used by this USM for holding
0977: * timing information about the local and remote SNMP entities.
0978: *
0979: * @return UsmTimeTable
0980: * @since 1.6
0981: */
0982: public UsmTimeTable getTimeTable() {
0983: return timeTable;
0984: }
0985:
0986: /**
0987: * Removes an USM user from the internal user name table.
0988: * @param engineID
0989: * the authoritative engine ID associated with the user, or
0990: * <code>null</code>
0991: * @param userName
0992: * a user name.
0993: * @return
0994: * the removed <code>UsmUser</code> instance associate with the given
0995: * <code>userName</code> or <code>null</code> if such a user could not
0996: * be found.
0997: */
0998: public UsmUser removeUser(OctetString engineID, OctetString userName) {
0999: UsmUserEntry entry = userTable.removeUser(engineID, userName);
1000: if (entry != null) {
1001: fireUsmUserChange(new UsmUserEvent(this , entry,
1002: UsmUserEvent.USER_REMOVED));
1003: return entry.getUsmUser();
1004: }
1005: return null;
1006: }
1007:
1008: /**
1009: * Removes all users from the USM.
1010: */
1011: public void removeAllUsers() {
1012: userTable.clear();
1013: fireUsmUserChange(new UsmUserEvent(this , null,
1014: UsmUserEvent.USER_REMOVED));
1015: }
1016:
1017: /**
1018: * Adds a localized user to the USM.
1019: * @param engineID
1020: * the engine ID for which the user has been localized.
1021: * @param userName
1022: * the user's name.
1023: * @param authProtocol
1024: * the authentication protocol ID.
1025: * @param authKey
1026: * the authentication key.
1027: * @param privProtocol
1028: * the privacy protocol ID.
1029: * @param privKey
1030: * the privacy key.
1031: * @return
1032: * the added <code>UsmUserEntry</code>.
1033: */
1034: public UsmUserEntry addLocalizedUser(byte[] engineID,
1035: OctetString userName, OID authProtocol, byte[] authKey,
1036: OID privProtocol, byte[] privKey) {
1037: UsmUserEntry newEntry = new UsmUserEntry(engineID, userName,
1038: authProtocol, authKey, privProtocol, privKey);
1039: userTable.addUser(newEntry);
1040: fireUsmUserChange(new UsmUserEvent(this , newEntry,
1041: UsmUserEvent.USER_ADDED));
1042: return newEntry;
1043: }
1044:
1045: /**
1046: * Checks whether engine ID discovery is enabled or not. If enabled, the USM
1047: * will try to discover unknown engine IDs "on-the-fly" while processing the
1048: * message.
1049: * @return
1050: * <code>true</code> if discovery is enabled, <code>false</code> otherwise.
1051: */
1052: public boolean isEngineDiscoveryEnabled() {
1053: return engineDiscoveryEnabled;
1054: }
1055:
1056: /**
1057: * Enables or disables automatic engine ID discovery.
1058: * @param engineDiscoveryEnabled
1059: * <code>true</code> if discovery should be enabled,
1060: * <code>false</code> otherwise.
1061: */
1062: public void setEngineDiscoveryEnabled(boolean engineDiscoveryEnabled) {
1063: this .engineDiscoveryEnabled = engineDiscoveryEnabled;
1064: }
1065:
1066: /**
1067: * Removes a <code>UsmUserListener</code>.
1068: * @param l
1069: * a proeviously added <code>UsmUserListener</code>.
1070: */
1071: public synchronized void removeUsmUserListener(UsmUserListener l) {
1072: if (usmUserListeners != null && usmUserListeners.contains(l)) {
1073: Vector v = (Vector) usmUserListeners.clone();
1074: v.removeElement(l);
1075: usmUserListeners = v;
1076: }
1077: }
1078:
1079: /**
1080: * Adds a <code>UsmUserListener</code> that should be informed whenever the
1081: * internal USM user table is changed.
1082: *
1083: * @param l
1084: * a <code>UsmUserListener</code> that should be informed about
1085: * {@link UsmUserEvent} events.
1086: */
1087: public synchronized void addUsmUserListener(UsmUserListener l) {
1088: Vector v = (usmUserListeners == null) ? new Vector(2)
1089: : (Vector) usmUserListeners.clone();
1090: if (!v.contains(l)) {
1091: v.addElement(l);
1092: usmUserListeners = v;
1093: }
1094: }
1095:
1096: /**
1097: * Removes the specified engine ID from the internal time cache and thus
1098: * forces an engine time rediscovery the next time the SNMP engine with
1099: * the supplied ID is contacted.
1100: *
1101: * @param engineID
1102: * the SNMP engine ID whose engine time to remove.
1103: * @since 1.6
1104: */
1105: public void removeEngineTime(OctetString engineID) {
1106: timeTable.removeEntry(engineID);
1107: }
1108:
1109: /**
1110: * Fires a <code>UsmUserEvent</code>.
1111: * @param e
1112: * the <code>UsmUserEvent</code> to fire.
1113: */
1114: protected void fireUsmUserChange(UsmUserEvent e) {
1115: if (usmUserListeners != null) {
1116: Vector listeners = usmUserListeners;
1117: int count = listeners.size();
1118: for (int i = 0; i < count; i++) {
1119: ((UsmUserListener) listeners.elementAt(i))
1120: .usmUserChange(e);
1121: }
1122: }
1123: }
1124:
1125: /**
1126: * Gets the counter support instance that can be used to register for
1127: * counter incremnetation events.
1128: * @return
1129: * a <code>CounterSupport</code> instance that is used to fire
1130: * {@link CounterEvent}.
1131: */
1132: public CounterSupport getCounterSupport() {
1133: return counterSupport;
1134: }
1135:
1136: /**
1137: * Returns the security protocol collection used by this USM.
1138: * @return
1139: * a <code>SecurityProtocols</code> instance which is by default the
1140: * same instance as returned by {@link SecurityProtocols#getInstance()}.
1141: * @since 1.2
1142: */
1143: public SecurityProtocols getSecurityProtocols() {
1144: return securityProtocols;
1145: }
1146:
1147: /**
1148: * Sets the counter support instance. By default, the singleton instance
1149: * provided by the {@link CounterSupport} instance is used.
1150: * @param counterSupport
1151: * a <code>CounterSupport</code> subclass instance.
1152: */
1153: public void setCounterSupport(CounterSupport counterSupport) {
1154: if (counterSupport == null) {
1155: throw new NullPointerException();
1156: }
1157: this.counterSupport = counterSupport;
1158: }
1159: }
|