1 /*
   2  * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.jmx.snmp;
  26 
  27 // java imports
  28 //
  29 import java.util.Vector;
  30 import java.util.logging.Level;
  31 import java.net.InetAddress;
  32 
  33 // import debug stuff
  34 //
  35 import static com.sun.jmx.defaults.JmxProperties.SNMP_LOGGER;
  36 import com.sun.jmx.snmp.internal.SnmpMsgProcessingSubSystem;
  37 import com.sun.jmx.snmp.internal.SnmpSecurityModel;
  38 import com.sun.jmx.snmp.internal.SnmpDecryptedPdu;
  39 import com.sun.jmx.snmp.internal.SnmpSecurityCache;
  40 
  41 import com.sun.jmx.snmp.SnmpMsg;
  42 import com.sun.jmx.snmp.SnmpPdu;
  43 import com.sun.jmx.snmp.SnmpStatusException;
  44 import com.sun.jmx.snmp.SnmpTooBigException;
  45 import com.sun.jmx.snmp.SnmpScopedPduBulk;
  46 import com.sun.jmx.snmp.BerException;
  47 import com.sun.jmx.snmp.SnmpScopedPduRequest;
  48 import com.sun.jmx.snmp.BerDecoder;
  49 import com.sun.jmx.snmp.SnmpDefinitions;
  50 import com.sun.jmx.snmp.SnmpEngineId;
  51 import com.sun.jmx.snmp.SnmpScopedPduPacket;
  52 import com.sun.jmx.snmp.BerEncoder;
  53 import com.sun.jmx.snmp.SnmpPduRequestType;
  54 import com.sun.jmx.snmp.SnmpPduBulkType;
  55 
  56 /**
  57  * Is a partially decoded representation of an SNMP V3 packet.
  58  * <P>
  59  * This class can be used when developing customized manager or agent.
  60  * <P>
  61  * The <CODE>SnmpV3Message</CODE> class is directly mapped onto the
  62  * message syntax defined in RFC 2572.
  63  * <BLOCKQUOTE>
  64  * <PRE>
  65  * SNMPv3Message ::= SEQUENCE {
  66  *          msgVersion INTEGER ( 0 .. 2147483647 ),
  67  *          -- administrative parameters
  68  *          msgGlobalData HeaderData,
  69  *          -- security model-specific parameters
  70  *          -- format defined by Security Model
  71  *          msgSecurityParameters OCTET STRING,
  72  *          msgData  ScopedPduData
  73  *      }
  74  *     HeaderData ::= SEQUENCE {
  75  *         msgID      INTEGER (0..2147483647),
  76  *         msgMaxSize INTEGER (484..2147483647),
  77  *
  78  *         msgFlags   OCTET STRING (SIZE(1)),
  79  *                    --  .... ...1   authFlag
  80  *                    --  .... ..1.   privFlag
  81  *                    --  .... .1..   reportableFlag
  82  *                    --              Please observe:
  83  *                    --  .... ..00   is OK, means noAuthNoPriv
  84  *                    --  .... ..01   is OK, means authNoPriv
  85  *                    --  .... ..10   reserved, must NOT be used.
  86  *                    --  .... ..11   is OK, means authPriv
  87  *
  88  *         msgSecurityModel INTEGER (1..2147483647)
  89  *     }
  90  * </BLOCKQUOTE>
  91  * </PRE>
  92  * <p><b>This API is a Sun Microsystems internal API  and is subject
  93  * to change without notice.</b></p>
  94  * @since 1.5
  95  */
  96 public class SnmpV3Message extends SnmpMsg {
  97 
  98     /**
  99      * Message identifier.
 100      */
 101     public int msgId = 0;
 102 
 103     /**
 104      * Message max size the pdu sender can deal with.
 105      */
 106     public int msgMaxSize = 0;
 107     /**
 108      * Message flags. Reportable flag  and security level.</P>
 109      *<PRE>
 110      * --  .... ...1   authFlag
 111      * --  .... ..1.   privFlag
 112      * --  .... .1..   reportableFlag
 113      * --              Please observe:
 114      * --  .... ..00   is OK, means noAuthNoPriv
 115      * --  .... ..01   is OK, means authNoPriv
 116      * --  .... ..10   reserved, must NOT be used.
 117      * --  .... ..11   is OK, means authPriv
 118      *</PRE>
 119      */
 120     public byte msgFlags = 0;
 121     /**
 122      * The security model the security sub system MUST use in order to deal with this pdu (eg: User based Security Model Id = 3).
 123      */
 124     public int msgSecurityModel = 0;
 125     /**
 126      * The unmarshalled security parameters.
 127      */
 128     public byte[] msgSecurityParameters = null;
 129     /**
 130      * The context engine Id in which the pdu must be handled (Generaly the local engine Id).
 131      */
 132     public byte[] contextEngineId = null;
 133     /**
 134      * The context name in which the OID has to be interpreted.
 135      */
 136     public byte[] contextName = null;
 137     /** The encrypted form of the scoped pdu (Only relevant when dealing with privacy).
 138      */
 139     public byte[] encryptedPdu = null;
 140 
 141     /**
 142      * Constructor.
 143      *
 144      */
 145     public SnmpV3Message() {
 146     }
 147     /**
 148      * Encodes this message and puts the result in the specified byte array.
 149      * For internal use only.
 150      *
 151      * @param outputBytes An array to receive the resulting encoding.
 152      *
 153      * @exception ArrayIndexOutOfBoundsException If the result does not fit
 154      *                                           into the specified array.
 155      */
 156     public int encodeMessage(byte[] outputBytes)
 157         throws SnmpTooBigException {
 158         int encodingLength = 0;
 159         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 160             SNMP_LOGGER.logp(Level.FINER, SnmpV3Message.class.getName(),
 161                     "encodeMessage",
 162                     "Can't encode directly V3Message! Need a SecuritySubSystem");
 163         }
 164         throw new IllegalArgumentException("Can't encode");
 165     }
 166 
 167     /**
 168      * Decodes the specified bytes and initializes this message.
 169      * For internal use only.
 170      *
 171      * @param inputBytes The bytes to be decoded.
 172      *
 173      * @exception SnmpStatusException If the specified bytes are not a valid encoding.
 174      */
 175     public void decodeMessage(byte[] inputBytes, int byteCount)
 176         throws SnmpStatusException {
 177 
 178         try {
 179             BerDecoder bdec = new BerDecoder(inputBytes);
 180             bdec.openSequence();
 181             version = bdec.fetchInteger();
 182             bdec.openSequence();
 183             msgId = bdec.fetchInteger();
 184             msgMaxSize = bdec.fetchInteger();
 185             msgFlags = bdec.fetchOctetString()[0];
 186             msgSecurityModel =bdec.fetchInteger();
 187             bdec.closeSequence();
 188             msgSecurityParameters = bdec.fetchOctetString();
 189             if( (msgFlags & SnmpDefinitions.privMask) == 0 ) {
 190                 bdec.openSequence();
 191                 contextEngineId = bdec.fetchOctetString();
 192                 contextName = bdec.fetchOctetString();
 193                 data = bdec.fetchAny();
 194                 dataLength = data.length;
 195                 bdec.closeSequence();
 196             }
 197             else {
 198                 encryptedPdu = bdec.fetchOctetString();
 199             }
 200             bdec.closeSequence() ;
 201         }
 202         catch(BerException x) {
 203             x.printStackTrace();
 204             throw new SnmpStatusException("Invalid encoding") ;
 205         }
 206 
 207         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 208             final StringBuilder strb = new StringBuilder()
 209             .append("Unmarshalled message : \n")
 210             .append("version : ").append(version)
 211             .append("\n")
 212             .append("msgId : ").append(msgId)
 213             .append("\n")
 214             .append("msgMaxSize : ").append(msgMaxSize)
 215             .append("\n")
 216             .append("msgFlags : ").append(msgFlags)
 217             .append("\n")
 218             .append("msgSecurityModel : ").append(msgSecurityModel)
 219             .append("\n")
 220             .append("contextEngineId : ").append(contextEngineId == null ? null :
 221                 SnmpEngineId.createEngineId(contextEngineId))
 222             .append("\n")
 223             .append("contextName : ").append(contextName)
 224             .append("\n")
 225             .append("data : ").append(data)
 226             .append("\n")
 227             .append("dat len : ").append((data == null) ? 0 : data.length)
 228             .append("\n")
 229             .append("encryptedPdu : ").append(encryptedPdu)
 230             .append("\n");
 231             SNMP_LOGGER.logp(Level.FINER, SnmpV3Message.class.getName(),
 232                     "decodeMessage", strb.toString());
 233         }
 234     }
 235 
 236     /**
 237      * Returns the associated request Id.
 238      * @param data The flat message.
 239      * @return The request Id.
 240      */
 241     public int getRequestId(byte[] data) throws SnmpStatusException {
 242         BerDecoder bdec = null;
 243         int msgId = 0;
 244         try {
 245             bdec = new BerDecoder(data);
 246             bdec.openSequence();
 247             bdec.fetchInteger();
 248             bdec.openSequence();
 249             msgId = bdec.fetchInteger();
 250         }catch(BerException x) {
 251             throw new SnmpStatusException("Invalid encoding") ;
 252         }
 253         try {
 254             bdec.closeSequence();
 255         }
 256         catch(BerException x) {
 257         }
 258 
 259         return msgId;
 260     }
 261 
 262     /**
 263      * Initializes this message with the specified <CODE>pdu</CODE>.
 264      * <P>
 265      * This method initializes the data field with an array of
 266      * <CODE>maxDataLength</CODE> bytes. It encodes the <CODE>pdu</CODE>.
 267      * The resulting encoding is stored in the data field
 268      * and the length of the encoding is stored in <CODE>dataLength</CODE>.
 269      * <p>
 270      * If the encoding length exceeds <CODE>maxDataLength</CODE>,
 271      * the method throws an exception.
 272      *
 273      * @param p The PDU to be encoded.
 274      * @param maxDataLength The maximum length permitted for the data field.
 275      *
 276      * @exception SnmpStatusException If the specified <CODE>pdu</CODE>
 277      *   is not valid.
 278      * @exception SnmpTooBigException If the resulting encoding does not fit
 279      * into <CODE>maxDataLength</CODE> bytes.
 280      * @exception ArrayIndexOutOfBoundsException If the encoding exceeds
 281      *    <CODE>maxDataLength</CODE>.
 282      */
 283     public void encodeSnmpPdu(SnmpPdu p,
 284                               int maxDataLength)
 285         throws SnmpStatusException, SnmpTooBigException {
 286 
 287         SnmpScopedPduPacket pdu = (SnmpScopedPduPacket) p;
 288 
 289         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 290             final StringBuilder strb = new StringBuilder()
 291             .append("PDU to marshall: \n")
 292             .append("security parameters : ").append(pdu.securityParameters)
 293             .append("\n")
 294             .append("type : ").append(pdu.type)
 295             .append("\n")
 296             .append("version : ").append(pdu.version)
 297             .append("\n")
 298             .append("requestId : ").append(pdu.requestId)
 299             .append("\n")
 300             .append("msgId : ").append(pdu.msgId)
 301             .append("\n")
 302             .append("msgMaxSize : ").append(pdu.msgMaxSize)
 303             .append("\n")
 304             .append("msgFlags : ").append(pdu.msgFlags)
 305             .append("\n")
 306             .append("msgSecurityModel : ").append(pdu.msgSecurityModel)
 307             .append("\n")
 308             .append("contextEngineId : ").append(pdu.contextEngineId)
 309             .append("\n")
 310             .append("contextName : ").append(pdu.contextName)
 311             .append("\n");
 312             SNMP_LOGGER.logp(Level.FINER, SnmpV3Message.class.getName(),
 313                     "encodeSnmpPdu", strb.toString());
 314         }
 315 
 316         version = pdu.version;
 317         address = pdu.address;
 318         port = pdu.port;
 319         msgId = pdu.msgId;
 320         msgMaxSize = pdu.msgMaxSize;
 321         msgFlags = pdu.msgFlags;
 322         msgSecurityModel = pdu.msgSecurityModel;
 323 
 324         contextEngineId = pdu.contextEngineId;
 325         contextName = pdu.contextName;
 326 
 327         securityParameters = pdu.securityParameters;
 328 
 329         //
 330         // Allocate the array to receive the encoding.
 331         //
 332         data = new byte[maxDataLength];
 333 
 334         //
 335         // Encode the pdu
 336         // Reminder: BerEncoder does backward encoding !
 337         //
 338 
 339         try {
 340             BerEncoder benc = new BerEncoder(data) ;
 341             benc.openSequence() ;
 342             encodeVarBindList(benc, pdu.varBindList) ;
 343 
 344             switch(pdu.type) {
 345 
 346             case pduGetRequestPdu :
 347             case pduGetNextRequestPdu :
 348             case pduInformRequestPdu :
 349             case pduGetResponsePdu :
 350             case pduSetRequestPdu :
 351             case pduV2TrapPdu :
 352             case pduReportPdu :
 353                 SnmpPduRequestType reqPdu = (SnmpPduRequestType) pdu;
 354                 benc.putInteger(reqPdu.getErrorIndex());
 355                 benc.putInteger(reqPdu.getErrorStatus());
 356                 benc.putInteger(pdu.requestId);
 357                 break;
 358 
 359             case pduGetBulkRequestPdu :
 360                 SnmpPduBulkType bulkPdu = (SnmpPduBulkType) pdu;
 361                 benc.putInteger(bulkPdu.getMaxRepetitions());
 362                 benc.putInteger(bulkPdu.getNonRepeaters());
 363                 benc.putInteger(pdu.requestId);
 364                 break ;
 365 
 366             default:
 367                 throw new SnmpStatusException("Invalid pdu type " + String.valueOf(pdu.type)) ;
 368             }
 369             benc.closeSequence(pdu.type) ;
 370             dataLength = benc.trim() ;
 371         }
 372         catch(ArrayIndexOutOfBoundsException x) {
 373             throw new SnmpTooBigException() ;
 374         }
 375     }
 376 
 377 
 378     /**
 379      * Gets the PDU encoded in this message.
 380      * <P>
 381      * This method decodes the data field and returns the resulting PDU.
 382      *
 383      * @return The resulting PDU.
 384      * @exception SnmpStatusException If the encoding is not valid.
 385      */
 386 
 387     public SnmpPdu decodeSnmpPdu()
 388         throws SnmpStatusException {
 389 
 390         SnmpScopedPduPacket pdu = null;
 391 
 392         BerDecoder bdec = new BerDecoder(data) ;
 393         try {
 394             int type = bdec.getTag() ;
 395             bdec.openSequence(type) ;
 396             switch(type) {
 397 
 398             case pduGetRequestPdu :
 399             case pduGetNextRequestPdu :
 400             case pduInformRequestPdu :
 401             case pduGetResponsePdu :
 402             case pduSetRequestPdu :
 403             case pduV2TrapPdu :
 404             case pduReportPdu :
 405                 SnmpScopedPduRequest reqPdu = new SnmpScopedPduRequest() ;
 406                 reqPdu.requestId = bdec.fetchInteger() ;
 407                 reqPdu.setErrorStatus(bdec.fetchInteger());
 408                 reqPdu.setErrorIndex(bdec.fetchInteger());
 409                 pdu = reqPdu ;
 410                 break ;
 411 
 412             case pduGetBulkRequestPdu :
 413                 SnmpScopedPduBulk bulkPdu = new SnmpScopedPduBulk() ;
 414                 bulkPdu.requestId = bdec.fetchInteger() ;
 415                 bulkPdu.setNonRepeaters(bdec.fetchInteger());
 416                 bulkPdu.setMaxRepetitions(bdec.fetchInteger());
 417                 pdu = bulkPdu ;
 418                 break ;
 419             default:
 420                 throw new SnmpStatusException(snmpRspWrongEncoding) ;
 421             }
 422             pdu.type = type;
 423             pdu.varBindList = decodeVarBindList(bdec);
 424             bdec.closeSequence() ;
 425         } catch(BerException e) {
 426             if (SNMP_LOGGER.isLoggable(Level.FINEST)) {
 427                 SNMP_LOGGER.logp(Level.FINEST, SnmpV3Message.class.getName(),
 428                         "decodeSnmpPdu", "BerException", e);
 429             }
 430             throw new SnmpStatusException(snmpRspWrongEncoding);
 431         }
 432 
 433         //
 434         // The easy work.
 435         //
 436         pdu.address = address;
 437         pdu.port = port;
 438         pdu.msgFlags = msgFlags;
 439         pdu.version = version;
 440         pdu.msgId = msgId;
 441         pdu.msgMaxSize = msgMaxSize;
 442         pdu.msgSecurityModel = msgSecurityModel;
 443         pdu.contextEngineId = contextEngineId;
 444         pdu.contextName = contextName;
 445 
 446         pdu.securityParameters = securityParameters;
 447 
 448         if (SNMP_LOGGER.isLoggable(Level.FINER)) {
 449             final StringBuilder strb = new StringBuilder()
 450             .append("Unmarshalled PDU : \n")
 451             .append("type : ").append(pdu.type)
 452             .append("\n")
 453             .append("version : ").append(pdu.version)
 454             .append("\n")
 455             .append("requestId : ").append(pdu.requestId)
 456             .append("\n")
 457             .append("msgId : ").append(pdu.msgId)
 458             .append("\n")
 459             .append("msgMaxSize : ").append(pdu.msgMaxSize)
 460             .append("\n")
 461             .append("msgFlags : ").append(pdu.msgFlags)
 462             .append("\n")
 463             .append("msgSecurityModel : ").append(pdu.msgSecurityModel)
 464             .append("\n")
 465             .append("contextEngineId : ").append(pdu.contextEngineId)
 466             .append("\n")
 467             .append("contextName : ").append(pdu.contextName)
 468             .append("\n");
 469             SNMP_LOGGER.logp(Level.FINER, SnmpV3Message.class.getName(),
 470                     "decodeSnmpPdu", strb.toString());
 471         }
 472         return pdu ;
 473     }
 474 
 475     /**
 476      * Dumps this message in a string.
 477      *
 478      * @return The string containing the dump.
 479      */
 480     public String printMessage() {
 481         StringBuilder sb = new StringBuilder();
 482         sb.append("msgId : ").append(msgId).append('\n');
 483         sb.append("msgMaxSize : ").append(msgMaxSize).append('\n');
 484         sb.append("msgFlags : ").append(msgFlags).append('\n');
 485         sb.append("msgSecurityModel : ").append(msgSecurityModel).append('\n');
 486 
 487         if (contextEngineId == null) {
 488             sb.append("contextEngineId : null");
 489         }
 490         else {
 491             sb.append("contextEngineId : {\n");
 492             sb.append(dumpHexBuffer(contextEngineId,
 493                                     0,
 494                                     contextEngineId.length));
 495             sb.append("\n}\n");
 496         }
 497 
 498         if (contextName == null) {
 499             sb.append("contextName : null");
 500         }
 501         else {
 502             sb.append("contextName : {\n");
 503             sb.append(dumpHexBuffer(contextName,
 504                                     0,
 505                                     contextName.length));
 506             sb.append("\n}\n");
 507         }
 508         return sb.append(super.printMessage()).toString();
 509     }
 510 
 511 }