KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > snmp4j > security > USM


1 /*_############################################################################
2   _##
3   _## SNMP4J - USM.java
4   _##
5   _## Copyright 2003-2007 Frank Fock and Jochen Katz (SNMP4J.org)
6   _##
7   _## Licensed under the Apache License, Version 2.0 (the "License");
8   _## you may not use this file except in compliance with the License.
9   _## You may obtain a copy of the License at
10   _##
11   _## http://www.apache.org/licenses/LICENSE-2.0
12   _##
13   _## Unless required by applicable law or agreed to in writing, software
14   _## distributed under the License is distributed on an "AS IS" BASIS,
15   _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   _## See the License for the specific language governing permissions and
17   _## limitations under the License.
18   _##
19   _##########################################################################*/

20
21 package org.snmp4j.security;
22
23 import java.io.*;
24 import java.nio.ByteBuffer JavaDoc;
25 import java.util.Vector JavaDoc;
26
27 import org.snmp4j.log.*;
28 import org.snmp4j.asn1.*;
29 import org.snmp4j.asn1.BER.*;
30 import org.snmp4j.event.*;
31 import org.snmp4j.mp.*;
32 import org.snmp4j.smi.*;
33
34 /**
35  * The <code>USM</code> class implements the User Based Security Model (USM)
36  * as defined in RFC3414.
37  * <p>
38  * When a user is added or removed from the USM, a <code>UsmUserEvent</code>
39  * is fired and forwarded to registered listeners.
40  *
41  * @author Frank Fock
42  * @version 1.2
43  */

44 public class USM implements SecurityModel {
45
46   private static final int MAXLEN_USMUSERNAME = 32;
47
48   private static final LogAdapter logger = LogFactory.getLogger(USM.class);
49
50   // Table containing localized and non-localized users
51
private UsmUserTable userTable;
52
53   private UsmTimeTable timeTable;
54
55   private OctetString localEngineID;
56   private boolean engineDiscoveryEnabled = true;
57
58   private SecurityProtocols securityProtocols;
59   private transient Vector JavaDoc usmUserListeners;
60   private CounterSupport counterSupport;
61
62   /**
63    * Creates a USM with the support for the supplied security protocols.
64    *
65    * @param securityProtocols
66    * the security protocols to support.
67    * @param localEngineID
68    * the local engine ID.
69    * @param engineBoots
70    * the number of engine boots.
71    * @since 1.2
72    */

73   public USM(SecurityProtocols securityProtocols,
74              OctetString localEngineID, int engineBoots) {
75     this.localEngineID = localEngineID;
76     timeTable = new UsmTimeTable(localEngineID, engineBoots);
77     userTable = new UsmUserTable();
78     this.securityProtocols = securityProtocols;
79     counterSupport = CounterSupport.getInstance();
80   }
81
82   public int getID() {
83     return SECURITY_MODEL_USM;
84   }
85
86   /**
87    * Sets the local engine ID, number of boots, and time after boot.
88    * @param localEngineID
89    * the local engine ID.
90    * @param engineBoots
91    * the number of engine boots.
92    * @param engineTime
93    * the number sendonds since the last boot.
94    */

95   public void setLocalEngine(OctetString localEngineID,
96                              int engineBoots, int engineTime) {
97     timeTable.removeEntry(this.localEngineID);
98     this.localEngineID = localEngineID;
99     timeTable.addEntry(new UsmTimeEntry(localEngineID, engineBoots, engineTime));
100   }
101
102   /**
103    * Returns the local engine ID.
104    * @return
105    * the local engine ID.
106    * @since 1.2
107    */

108   public OctetString getLocalEngineID() {
109     return localEngineID;
110   }
111
112   /**
113    * Sets the number of engine boots.
114    * @param engineBoots
115    * the number of engine boots.
116    */

117   public void setEngineBoots(int engineBoots) {
118     this.timeTable.setEngineBoots(engineBoots);
119   }
120
121   /**
122    * Returns the number of engine boots counted for the local engine ID.
123    * @return
124    * the number of engine boots (zero based).
125    */

126   public int getEngineBoots() {
127     return this.timeTable.getEngineBoots();
128   }
129
130   /**
131    * Returns the number of seconds since the value of
132    * the engineBoots object last changed. When incrementing this object's value
133    * would cause it to exceed its maximum, engineBoots is incremented as if a
134    * re-initialization had occurred, and this
135    * object's value consequently reverts to zero.
136    *
137    * @return
138    * a positive integer value denoting the number of seconds since
139    * the engineBoots value has been changed.
140    * @since 1.2
141    */

142   public int getEngineTime() {
143     return this.timeTable.getEngineTime();
144   }
145
146   public SecurityParameters newSecurityParametersInstance() {
147     return new UsmSecurityParameters();
148   }
149
150   public SecurityStateReference newSecurityStateReference() {
151     return new UsmSecurityStateReference();
152   }
153
154   private static byte[] buildMessageBuffer(BERInputStream scopedPDU)
155       throws IOException
156   {
157 // return scopedPDU.getBuffer().array();
158
scopedPDU.mark(16);
159     int readLengthBytes = (int)scopedPDU.getPosition();
160     MutableByte mutableByte = new MutableByte();
161     int length = BER.decodeHeader(scopedPDU, mutableByte);
162     readLengthBytes = (int)scopedPDU.getPosition() - readLengthBytes;
163     byte[] buf = new byte[length + readLengthBytes];
164     scopedPDU.reset();
165
166     int offset = 0;
167     int avail = scopedPDU.available();
168     while ((offset < buf.length) && (avail > 0)) {
169       int read = scopedPDU.read(buf, offset, buf.length - offset);
170       if (read < 0) {
171         break;
172       }
173       offset += read;
174     }
175     return buf;
176   }
177
178   private static byte[] buildWholeMessage(Integer32 snmpVersion,
179                                           byte[] scopedPdu,
180                                           byte[] globalData,
181                                           UsmSecurityParameters
182                                                           usmSecurityParameters)
183       throws IOException
184   {
185     int length =
186         snmpVersion.getBERLength() +
187         globalData.length +
188         usmSecurityParameters.getBERLength() +
189         scopedPdu.length;
190     int totalLength = BER.getBERLengthOfLength(length) + length + 1;
191
192     ByteArrayOutputStream os = new ByteArrayOutputStream(totalLength);
193     BER.encodeHeader(os, BER.SEQUENCE, length);
194     snmpVersion.encodeBER(os);
195     os.write(globalData);
196     usmSecurityParameters.encodeBER(os);
197     os.write(scopedPdu);
198     int secParamsPos = 1 + snmpVersion.getBERLength() +
199         BER.getBERLengthOfLength(length) + globalData.length;
200     usmSecurityParameters.setSecurityParametersPosition(secParamsPos);
201     return os.toByteArray();
202   }
203
204   public int generateRequestMessage(int snmpVersion,
205                                     byte[] globalData,
206                                     int maxMessageSize,
207                                     int securityModel,
208                                     byte[] securityEngineID,
209                                     byte[] securityName,
210                                     int securityLevel,
211                                     BERInputStream scopedPDU,
212                                     SecurityParameters securityParameters,
213                                     BEROutputStream wholeMsg) throws IOException {
214
215     return generateResponseMessage(snmpVersion,
216                                    globalData,
217                                    maxMessageSize,
218                                    securityModel,
219                                    securityEngineID,
220                                    securityName,
221                                    securityLevel,
222                                    scopedPDU,
223                                    null,
224                                    securityParameters,
225                                    wholeMsg);
226   }
227
228   public UsmUserEntry getUser(OctetString engineID, OctetString securityName) {
229     if (logger.isDebugEnabled()) {
230       logger.debug("getUser(engineID="+engineID.toHexString()+
231                    ", securityName="+securityName.toString()+")");
232     }
233     UsmUserEntry entry = userTable.getUser(engineID, securityName);
234     if (entry == null) {
235       entry = userTable.getUser(securityName);
236       if ((entry == null) && (securityName.length() > 0)) {
237         if (logger.isDebugEnabled()) {
238           logger.debug("USM.getUser - User '"+securityName+"' unknown");
239         }
240         return null;
241       }
242       else {
243         if ((entry == null) || (engineID.length() == 0)) {
244           // do not add user
245
entry = new UsmUserEntry();
246           entry.setUserName(securityName);
247           entry.setUsmUser(new UsmUser(securityName, null, null, null, null));
248           return entry;
249         }
250         else {
251           // add a new user
252
OID authProtocolOID = entry.getUsmUser().getAuthenticationProtocol();
253           OID privProtocolOID = entry.getUsmUser().getPrivacyProtocol();
254           if (authProtocolOID != null) {
255             byte[] authKey;
256             if (entry.getUsmUser().isLocalized()) {
257               authKey =
258                   entry.getUsmUser().getAuthenticationPassphrase().getValue();
259             }
260             else {
261               authKey = securityProtocols.passwordToKey(authProtocolOID,
262                   entry.getUsmUser().getAuthenticationPassphrase(),
263                   engineID.getValue());
264             }
265             byte[] privKey = null;
266             if (privProtocolOID != null) {
267               if (entry.getUsmUser().isLocalized()) {
268                 privKey = entry.getUsmUser().getPrivacyPassphrase().getValue();
269               }
270               else {
271                 privKey = securityProtocols.passwordToKey(privProtocolOID,
272                     authProtocolOID,
273                     entry.getUsmUser().getPrivacyPassphrase(),
274                     engineID.getValue());
275               }
276             }
277             entry = addLocalizedUser(engineID.getValue(), securityName,
278                                      authProtocolOID, authKey,
279                                      privProtocolOID, privKey);
280           }
281         }
282       }
283     }
284     return entry;
285   }
286
287   public int generateResponseMessage(int snmpVersion,
288                                      byte[] globalData,
289                                      int maxMessageSize,
290                                      int securityModel,
291                                      byte[] securityEngineID,
292                                      byte[] securityName,
293                                      int securityLevel,
294                                      BERInputStream scopedPDU,
295                                      SecurityStateReference
296                                      securityStateReference,
297                                      SecurityParameters securityParameters,
298                                      BEROutputStream wholeMsg) throws IOException {
299
300     UsmSecurityParameters usmSecurityParams =
301         (UsmSecurityParameters) securityParameters;
302     if (securityStateReference != null) {
303       // this is a response or report
304
UsmSecurityStateReference usmSecurityStateReference =
305           (UsmSecurityStateReference) securityStateReference;
306       if (usmSecurityStateReference.getSecurityEngineID() == null) {
307         usmSecurityParams.setAuthoritativeEngineID(securityEngineID);
308         usmSecurityStateReference.setSecurityEngineID(securityEngineID);
309       }
310       if (usmSecurityStateReference.getSecurityName() == null) {
311         OctetString userName = new OctetString(securityName);
312         usmSecurityStateReference.setSecurityName(userName.getValue());
313         usmSecurityParams.setUserName(userName);
314
315         OctetString secName =
316             getSecurityName(new OctetString(securityEngineID), userName);
317
318         if ((secName != null) &&
319             (secName.length() <= MAXLEN_USMUSERNAME)) {
320           usmSecurityParams.setUserName(secName);
321         }
322
323       }
324       else {
325         usmSecurityParams.setUserName(new OctetString(usmSecurityStateReference.getSecurityName()));
326       }
327       usmSecurityParams.setAuthenticationProtocol(usmSecurityStateReference.
328                                                   getAuthenticationProtocol());
329       usmSecurityParams.setPrivacyProtocol(usmSecurityStateReference.
330                                            getPrivacyProtocol());
331       usmSecurityParams.setAuthenticationKey(usmSecurityStateReference.
332                                              getAuthenticationKey());
333       usmSecurityParams.setPrivacyKey(usmSecurityStateReference.getPrivacyKey());
334     }
335     else {
336       OctetString secEngineID = new OctetString();
337       if (securityEngineID != null) {
338         secEngineID.setValue(securityEngineID);
339       }
340       OctetString secName = new OctetString(securityName);
341
342       UsmUserEntry user;
343       if (secEngineID.length() == 0) {
344         if (isEngineDiscoveryEnabled()) {
345           user = new UsmUserEntry();
346         }
347         else {
348           if (logger.isDebugEnabled()) {
349             logger.debug("Engine ID unknown and discovery disabled");
350           }
351           return SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID;
352         }
353       }
354       else {
355         user = getUser(secEngineID, secName);
356       }
357       if (user == null) {
358         if (logger.isDebugEnabled()) {
359           logger.debug("Security name not found for engineID=" +
360                        secEngineID.toHexString() + ", securityName=" +
361                        secName.toHexString());
362         }
363         return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
364       }
365       AuthenticationProtocol auth =
366           securityProtocols.getAuthenticationProtocol(user.getUsmUser().getAuthenticationProtocol());
367       PrivacyProtocol priv =
368           securityProtocols.getPrivacyProtocol(user.getUsmUser().getPrivacyProtocol());
369       usmSecurityParams.setAuthenticationProtocol(auth);
370       usmSecurityParams.setPrivacyProtocol(priv);
371       usmSecurityParams.setAuthenticationKey(user.getAuthenticationKey());
372       usmSecurityParams.setPrivacyKey(user.getPrivacyKey());
373       usmSecurityParams.setUserName(user.getUsmUser().getSecurityName());
374       usmSecurityParams.setAuthoritativeEngineID(secEngineID.getValue());
375     }
376
377     // Check length of userName and engineID
378
if (usmSecurityParams.getAuthoritativeEngineID().length > MPv3.MAXLEN_ENGINE_ID) {
379       logger.error("Engine ID too long: "+
380                    usmSecurityParams.getAuthoritativeEngineID().length+">"+
381                    MPv3.MAXLEN_ENGINE_ID+ " for "+
382                    new OctetString(usmSecurityParams.getAuthoritativeEngineID())
383                    .toHexString());
384       return SnmpConstants.SNMPv3_USM_ERROR;
385     }
386     if (securityName.length > MAXLEN_USMUSERNAME) {
387       logger.error("Security name too long: "+
388                    usmSecurityParams.getAuthoritativeEngineID().length+">"+
389                    MAXLEN_USMUSERNAME+ " for "+
390                    new OctetString(securityName).toHexString());
391       return SnmpConstants.SNMPv3_USM_ERROR;
392     }
393
394     if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
395       if (securityStateReference != null) {
396         // request or response
397
usmSecurityParams.setAuthoritativeEngineBoots(getEngineBoots());
398         usmSecurityParams.setAuthoritativeEngineTime(getEngineTime());
399       }
400       else {
401         // get engineBoots, engineTime
402
OctetString secEngineID = new OctetString(securityEngineID);
403         UsmTimeEntry entry = timeTable.getTime(secEngineID);
404         if (entry == null) {
405           entry =
406               new UsmTimeEntry(secEngineID,
407                                usmSecurityParams.getAuthoritativeEngineBoots(),
408                                usmSecurityParams.
409                                getAuthoritativeEngineTime());
410
411           timeTable.addEntry(entry);
412         }
413         else {
414           usmSecurityParams.setAuthoritativeEngineBoots(entry.getEngineBoots());
415           usmSecurityParams.setAuthoritativeEngineTime(entry.
416               getLatestReceivedTime());
417         }
418       }
419     }
420
421     if ((securityLevel >= SecurityLevel.AUTH_NOPRIV) &&
422         (usmSecurityParams.getAuthenticationProtocol() == null)) {
423       return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
424     }
425
426     byte[] scopedPduBytes = buildMessageBuffer(scopedPDU);
427
428     if (securityLevel == SecurityLevel.AUTH_PRIV) {
429       if (usmSecurityParams.getPrivacyProtocol() == null) {
430         if (logger.isDebugEnabled()) {
431           logger.debug("Unsupported security level (missing or unsupported privacy protocol)");
432         }
433         return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
434       }
435       logger.debug("RFC3414 §3.1.4.a Outgoing message needs to be encrypted");
436
437       DecryptParams decryptParams = new DecryptParams();
438       byte[] encryptedScopedPdu =
439           usmSecurityParams.getPrivacyProtocol().
440           encrypt(scopedPduBytes, 0, scopedPduBytes.length,
441                   usmSecurityParams.getPrivacyKey(),
442                   usmSecurityParams.getAuthoritativeEngineBoots(),
443                   usmSecurityParams.getAuthoritativeEngineTime(),
444                   decryptParams);
445       if (encryptedScopedPdu == null) {
446         if (logger.isDebugEnabled()) {
447           logger.debug("Encryption error");
448         }
449         return SnmpConstants.SNMPv3_USM_ENCRYPTION_ERROR;
450       }
451       usmSecurityParams.setPrivacyParameters(new OctetString(decryptParams.
452           array));
453       OctetString encryptedString = new OctetString(encryptedScopedPdu);
454       BEROutputStream os =
455           new BEROutputStream(ByteBuffer.allocate(encryptedString.getBERLength()));
456       encryptedString.encodeBER(os);
457       scopedPduBytes = os.getBuffer().array();
458     }
459     else {
460       logger.debug("RFC3414 §3.1.4.b Outgoing message is not encrypted");
461       usmSecurityParams.setPrivacyParameters(new OctetString());
462     }
463
464     byte[] wholeMessage;
465     if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
466       /* Build message with authentication */
467       byte[] blank =
468           new byte[AuthenticationProtocol.MESSAGE_AUTHENTICATION_CODE_LENGTH];
469       usmSecurityParams.setAuthenticationParameters(new OctetString(blank));
470       wholeMessage =
471           buildWholeMessage(new Integer32(snmpVersion),
472                             scopedPduBytes, globalData, usmSecurityParams);
473
474       int authParamsPos =
475           usmSecurityParams.getAuthParametersPosition() +
476           usmSecurityParams.getSecurityParametersPosition();
477
478       boolean authOK = usmSecurityParams.getAuthenticationProtocol().
479           authenticate(usmSecurityParams.getAuthenticationKey(),
480                        wholeMessage,
481                        0,
482                        wholeMessage.length,
483                        new ByteArrayWindow(wholeMessage,
484                                            authParamsPos,
485                                            AuthenticationProtocol.
486                                            MESSAGE_AUTHENTICATION_CODE_LENGTH));
487
488       if (!authOK) {
489         if (logger.isDebugEnabled()) {
490           logger.debug("Outgoing message could not be authenticated");
491         }
492         return SnmpConstants.SNMPv3_USM_AUTHENTICATION_ERROR;
493       }
494     }
495     else {
496       // Set engineBoots and enigneTime to zero!
497
usmSecurityParams.setAuthoritativeEngineBoots(0);
498       usmSecurityParams.setAuthenticationParameters(new OctetString());
499       usmSecurityParams.setAuthoritativeEngineTime(0);
500
501       //build Message without authentication
502
wholeMessage =
503           buildWholeMessage(new Integer32(snmpVersion),
504                             scopedPduBytes, globalData, usmSecurityParams);
505     }
506     ByteBuffer JavaDoc buf =
507         (ByteBuffer JavaDoc)ByteBuffer.wrap(wholeMessage).position(wholeMessage.length);
508     wholeMsg.setBuffer(buf);
509     // not necessary: wholeMsg.write(wholeMessage);
510
return SnmpConstants.SNMPv3_USM_OK;
511   }
512
513   private OctetString getSecurityName(OctetString engineID,
514                                       OctetString userName) {
515     if (userName.length() == 0) {
516       return userName;
517     }
518     UsmUserEntry user = userTable.getUser(engineID, userName);
519     if (user != null) {
520       return user.getUsmUser().getSecurityName();
521     }
522     else if (isEngineDiscoveryEnabled()) {
523       user = userTable.getUser(userName);
524       if (user != null) {
525         return user.getUsmUser().getSecurityName();
526       }
527     }
528     return null;
529   }
530
531   public int processIncomingMsg(int snmpVersion, // typically, SNMP version
532
int maxMessageSize, // of the sending SNMP entity - maxHeaderLength of the MP
533
SecurityParameters securityParameters, // for the received message
534
SecurityModel securityModel, // for the received message
535
int securityLevel, // Level of Security
536
BERInputStream wholeMsg, // as received on the wire
537
// output parameters
538
OctetString securityEngineID, // authoritative SNMP entity
539
OctetString securityName, // identification of the principal
540
BEROutputStream scopedPDU, // message (plaintext) payload
541
Integer32 maxSizeResponseScopedPDU, // maximum size of the Response PDU
542
SecurityStateReference securityStateReference, // reference to security state information, needed for response
543
StatusInformation statusInfo
544                                 ) throws IOException {
545
546     UsmSecurityParameters usmSecurityParameters =
547         (UsmSecurityParameters) securityParameters;
548     UsmSecurityStateReference usmSecurityStateReference =
549         (UsmSecurityStateReference) securityStateReference;
550     securityEngineID.setValue(usmSecurityParameters.getAuthoritativeEngineID());
551
552     byte[] message = buildMessageBuffer(wholeMsg);
553
554     if ((securityEngineID.length() == 0) ||
555         (timeTable.checkEngineID(securityEngineID,
556                                  isEngineDiscoveryEnabled()) !=
557          SnmpConstants.SNMPv3_USM_OK)) {
558       // generate report
559
if (logger.isDebugEnabled()) {
560         logger.debug("RFC3414 §3.2.3 Unknown engine ID: " +
561                      securityEngineID.toHexString());
562       }
563       securityEngineID.setValue(usmSecurityParameters.
564                                 getAuthoritativeEngineID());
565       securityName.setValue(usmSecurityParameters.getUserName().getValue());
566
567       if (statusInfo != null) {
568         CounterEvent event = new CounterEvent(this,
569                                               SnmpConstants.
570                                               usmStatsUnknownEngineIDs);
571         fireIncrementCounter(event);
572         statusInfo.setSecurityLevel(new Integer32(securityLevel));
573         statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
574               event.getCurrentValue()));
575         }
576         return SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID;
577       }
578
579     securityName.setValue(usmSecurityParameters.getUserName().getValue());
580
581     int scopedPDUPosition = usmSecurityParameters.getScopedPduPosition();
582
583     // get security name
584
if ((usmSecurityParameters.getUserName().length() > 0) ||
585         (securityLevel > SecurityLevel.NOAUTH_NOPRIV)) {
586       OctetString secName = getSecurityName(securityEngineID,
587                                             usmSecurityParameters.getUserName());
588       if (secName == null) {
589         if (logger.isDebugEnabled()) {
590           logger.debug("RFC3414 §3.2.4 Unknown security name: " +
591                        securityName.toHexString());
592         }
593         if (statusInfo != null) {
594           CounterEvent event = new CounterEvent(this,
595                                                 SnmpConstants.usmStatsUnknownUserNames);
596           fireIncrementCounter(event);
597           statusInfo.setSecurityLevel(new Integer32(SecurityLevel.NOAUTH_NOPRIV));
598           statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
599               event.getCurrentValue()));
600         }
601         return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
602       }
603     }
604     else {
605       if (logger.isDebugEnabled()) {
606         logger.debug("Accepting zero length security name");
607       }
608       securityName.setValue(new byte[0]);
609     }
610
611     if ((usmSecurityParameters.getUserName().length() > 0) ||
612         (securityLevel > SecurityLevel.NOAUTH_NOPRIV)) {
613       UsmUserEntry user = getUser(securityEngineID, securityName);
614       if (user == null) {
615         if (logger.isDebugEnabled()) {
616           logger.debug("RFC3414 §3.2.4 Unknown security name: " +
617                        securityName.toHexString()+ " for engine ID "+
618                        securityEngineID.toHexString());
619         }
620         CounterEvent event =
621             new CounterEvent(this, SnmpConstants.usmStatsUnknownUserNames);
622         fireIncrementCounter(event);
623         if (statusInfo != null) {
624           statusInfo.setSecurityLevel(new Integer32(SecurityLevel.NOAUTH_NOPRIV));
625           statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
626               event.getCurrentValue()));
627         }
628         return SnmpConstants.SNMPv3_USM_UNKNOWN_SECURITY_NAME;
629       }
630
631       usmSecurityStateReference.setUserName(user.getUserName().getValue());
632
633       AuthenticationProtocol auth =
634           securityProtocols.getAuthenticationProtocol(
635                                 user.getUsmUser().getAuthenticationProtocol());
636       PrivacyProtocol priv =
637           securityProtocols.getPrivacyProtocol(
638                                 user.getUsmUser().getPrivacyProtocol());
639
640       if (((securityLevel >= SecurityLevel.AUTH_NOPRIV) && (auth == null)) ||
641           (((securityLevel >= SecurityLevel.AUTH_PRIV) && (priv == null)))) {
642         if (logger.isDebugEnabled()) {
643           logger.debug("RFC3414 §3.2.5 - Unsupported security level: " +
644                        securityLevel);
645         }
646         CounterEvent event =
647             new CounterEvent(this, SnmpConstants.usmStatsUnsupportedSecLevels);
648         fireIncrementCounter(event);
649         statusInfo.setSecurityLevel(new Integer32(SecurityLevel.NOAUTH_NOPRIV));
650         statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
651             event.getCurrentValue()));
652         return SnmpConstants.SNMPv3_USM_UNSUPPORTED_SECURITY_LEVEL;
653       }
654       if (securityLevel >= SecurityLevel.AUTH_NOPRIV) {
655         if (statusInfo != null) {
656           int authParamsPos =
657               usmSecurityParameters.getAuthParametersPosition() +
658               usmSecurityParameters.getSecurityParametersPosition();
659           boolean authentic =
660               auth.isAuthentic(user.getAuthenticationKey(),
661                                message, 0, message.length,
662                                new ByteArrayWindow(message, authParamsPos,
663               AuthenticationProtocol.MESSAGE_AUTHENTICATION_CODE_LENGTH));
664           if (!authentic) {
665             if (logger.isDebugEnabled()) {
666               logger.debug(
667                   "RFC3414 §3.2.6 Wrong digest -> authentication failure: " +
668                   usmSecurityParameters.getAuthenticationParameters().toHexString());
669             }
670             CounterEvent event =
671                 new CounterEvent(this, SnmpConstants.usmStatsWrongDigests);
672             fireIncrementCounter(event);
673             statusInfo.setSecurityLevel(new Integer32(SecurityLevel.NOAUTH_NOPRIV));
674             statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
675                 event.getCurrentValue()));
676             return SnmpConstants.SNMPv3_USM_AUTHENTICATION_FAILURE;
677           }
678           usmSecurityStateReference.setAuthenticationKey(user.getAuthenticationKey());
679           usmSecurityStateReference.setPrivacyKey(user.getPrivacyKey());
680           usmSecurityStateReference.setAuthenticationProtocol(auth);
681           usmSecurityStateReference.setPrivacyProtocol(priv);
682           // check time
683
int status = timeTable.checkTime(new UsmTimeEntry(securityEngineID,
684             usmSecurityParameters.getAuthoritativeEngineBoots(),
685             usmSecurityParameters.getAuthoritativeEngineTime()));
686
687            switch (status) {
688             case SnmpConstants.SNMPv3_USM_NOT_IN_TIME_WINDOW: {
689               logger.debug("RFC3414 §3.2.7.a Not in time window; engineID='" +
690                            securityEngineID +
691                            "', engineBoots=" +
692                            usmSecurityParameters.getAuthoritativeEngineBoots() +
693                            ", engineTime=" +
694                            usmSecurityParameters.getAuthoritativeEngineTime());
695               CounterEvent event =
696                   new CounterEvent(this, SnmpConstants.usmStatsNotInTimeWindows);
697               fireIncrementCounter(event);
698               statusInfo.setSecurityLevel(new Integer32(SecurityLevel.AUTH_NOPRIV));
699               statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
700                   event.getCurrentValue()));
701               return status;
702             }
703             case SnmpConstants.SNMPv3_USM_UNKNOWN_ENGINEID: {
704               if (logger.isDebugEnabled()) {
705                 logger.debug("RFC3414 §3.2.7.b - Unkown engine ID: " +
706                              securityEngineID);
707               }
708               CounterEvent event =
709                   new CounterEvent(this, SnmpConstants.usmStatsNotInTimeWindows);
710               fireIncrementCounter(event);
711               statusInfo.setSecurityLevel(new Integer32(SecurityLevel.AUTH_NOPRIV));
712               statusInfo.setErrorIndication(new VariableBinding(event.getOid(),
713                   event.getCurrentValue()));
714               return status;
715
716             }
717           }
718         }
719         if (securityLevel >= SecurityLevel.AUTH_PRIV) {
720           OctetString privParams = usmSecurityParameters.getPrivacyParameters();
721           DecryptParams decryptParams = new DecryptParams(privParams.getValue(),
722                                                           0, privParams.length());
723           try {
724             int scopedPDUHeaderLength = message.length - scopedPDUPosition;
725             ByteBuffer JavaDoc bis = ByteBuffer.wrap(message, scopedPDUPosition,
726                                              scopedPDUHeaderLength);
727             BERInputStream scopedPDUHeader = new BERInputStream(bis);
728             long headerStartingPosition = scopedPDUHeader.getPosition();
729             int scopedPDULength =
730                 BER.decodeHeader(scopedPDUHeader, new MutableByte());
731             int scopedPDUPayloadPosition =
732                 scopedPDUPosition +
733                   (int)(scopedPDUHeader.getPosition() - headerStartingPosition);
734             scopedPDUHeader.close();
735             scopedPDUHeader = null;
736             byte[] scopedPduBytes =
737                 priv.decrypt(message, scopedPDUPayloadPosition, scopedPDULength,
738                              user.getPrivacyKey(),
739                              usmSecurityParameters.getAuthoritativeEngineBoots(),
740                              usmSecurityParameters.getAuthoritativeEngineTime(),
741                              decryptParams);
742             ByteBuffer JavaDoc buf = ByteBuffer.wrap(scopedPduBytes);
743             scopedPDU.setFilledBuffer(buf);
744           }
745           catch (Exception JavaDoc ex) {
746             logger.debug("RFC 3414 §3.2.8 Decryption error: "+ex.getMessage());
747             return SnmpConstants.SNMPv3_USM_DECRYPTION_ERROR;
748           }
749         }
750         else {
751           int scopedPduLength = message.length - scopedPDUPosition;
752           ByteBuffer JavaDoc buf =
753               ByteBuffer.wrap(message, scopedPDUPosition, scopedPduLength);
754           scopedPDU.setFilledBuffer(buf);
755         }
756       }
757       else {
758         int scopedPduLength = message.length - scopedPDUPosition;
759         ByteBuffer JavaDoc buf =
760             ByteBuffer.wrap(message, scopedPDUPosition, scopedPduLength);
761         scopedPDU.setFilledBuffer(buf);
762       }
763     }
764     else {
765       int scopedPduLength = message.length - scopedPDUPosition;
766       ByteBuffer JavaDoc buf =
767           ByteBuffer.wrap(message, scopedPDUPosition, scopedPduLength);
768       scopedPDU.setFilledBuffer(buf);
769     }
770     // compute real max size response pdu according to RFC3414 §3.2.9
771
int maxSecParamsOverhead =
772         usmSecurityParameters.getBERMaxLength(securityLevel);
773     maxSizeResponseScopedPDU.setValue(maxMessageSize -
774                                       maxSecParamsOverhead);
775
776     usmSecurityStateReference.setSecurityName(securityName.getValue());
777     return SnmpConstants.SNMPv3_USM_OK;
778   }
779
780   protected void fireIncrementCounter(CounterEvent e) {
781     counterSupport.fireIncrementCounter(e);
782   }
783
784   /**
785    * Adds an USM user to the internal user name table.
786    * @param userName
787    * a user name.
788    * @param user
789    * the <code>UsmUser</code> to add.
790    */

791   public void addUser(OctetString userName, UsmUser user) {
792     addUser(userName, new OctetString(), user);
793   }
794
795   /**
796    * Adds an USM user to the internal user name table and associates it with
797    * an authoritative engine ID. This user can only be used with the specified
798    * engine ID - other engine IDs cannot be discovered on behalf of this entry.
799    * @param userName
800    * a user name.
801    * @param engineID
802    * the authoritative engine ID to be associated with this entry. If
803    * <code>engineID</code> is <code>null</code> this method behaves exactly
804    * like {@link #addUser(OctetString userName, UsmUser user)}.
805    * @param user
806    * the <code>UsmUser</code> to add.
807    */

808   public void addUser(OctetString userName, OctetString engineID, UsmUser user) {
809     byte[] authKey = null;
810     byte[] privKey = null;
811     if ((engineID != null) && (engineID.length() > 0)) {
812       if (user.getAuthenticationProtocol() != null) {
813         if (user.isLocalized()) {
814           authKey = user.getAuthenticationPassphrase().getValue();
815         }
816         else {
817           authKey =
818               securityProtocols.passwordToKey(user.getAuthenticationProtocol(),
819                                               user.getAuthenticationPassphrase(),
820                                               engineID.getValue());
821         }
822         if (user.getPrivacyProtocol() != null) {
823           if (user.isLocalized()) {
824             privKey = user.getPrivacyPassphrase().getValue();
825           }
826           else {
827             privKey =
828                 securityProtocols.passwordToKey(user.getPrivacyProtocol(),
829                                                 user.getAuthenticationProtocol(),
830                                                 user.getPrivacyPassphrase(),
831                                                 engineID.getValue());
832           }
833         }
834       }
835     }
836     OctetString userEngineID;
837     if (user.isLocalized()) {
838       userEngineID = user.getLocalizationEngineID();
839     }
840     else {
841       userEngineID = (engineID == null) ? new OctetString() : engineID;
842     }
843     UsmUserEntry entry =
844         new UsmUserEntry(userName, userEngineID, user);
845     entry.setAuthenticationKey(authKey);
846     entry.setPrivacyKey(privKey);
847     userTable.addUser(entry);
848     fireUsmUserChange(new UsmUserEvent(this, entry, UsmUserEvent.USER_ADDED));
849   }
850
851   /**
852    * Updates the USM user entry with the same engine ID and user name as the
853    * supplied instance and fires an appropriate <code>UsmUserEvent</code>.
854    * If the corresponding user entry does not yet exist then it will be added.
855    * @param entry
856    * an <code>UsmUserEntry</code> instance not necessarily the same as an
857    * already existing entry.
858    * @since 1.2
859    */

860   public void updateUser(UsmUserEntry entry) {
861     UsmUserEntry oldEntry = userTable.addUser(entry);
862     fireUsmUserChange(new UsmUserEvent(this, entry,
863                                        (oldEntry == null) ?
864                                        UsmUserEvent.USER_ADDED:
865                                        UsmUserEvent.USER_CHANGED));
866   }
867
868   /**
869    * Sets the users of this USM. All previously added users and all localized
870    * user information will be discarded and replaced by the supplied users.
871    *
872    * @param users
873    * a possibly empty <code>UsmUser</code> array of users.
874    * @since 1.1
875    */

876   public void setUsers(UsmUser[] users) {
877     if ((users == null) || (users.length == 0)) {
878       userTable.clear();
879     }
880     else {
881       Vector JavaDoc v = new Vector JavaDoc(users.length);
882       for (int i=0; i<users.length; i++) {
883         UsmUserEntry entry =
884             new UsmUserEntry(users[i].getSecurityName(),
885                              (UsmUser)users[i].clone());
886         v.add(entry);
887       }
888       userTable.setUsers(v);
889     }
890   }
891
892   /**
893    * Returns the <code>UsmUserTable</code> instance used by the USM for local
894    * storage of USM user information. The returned table should not be modified,
895    * because modifications will not be reported to registered
896    * <code>UsmUserListener</code>s.
897    *
898    * @return
899    * the <code>UsmUserTable</code> instance containing the users known by
900    * this USM.
901    */

902   public UsmUserTable getUserTable() {
903     return userTable;
904   }
905
906   /**
907    * Returns the <code>UsmTimeTable</code> instance used by this USM for holding
908    * timing information about the local and remote SNMP entities.
909    *
910    * @return UsmTimeTable
911    * @since 1.6
912    */

913   public UsmTimeTable getTimeTable() {
914     return timeTable;
915   }
916
917   /**
918    * Removes an USM user from the internal user name table.
919    * @param engineID
920    * the authoritative engine ID associated with the user, or
921    * <code>null</code>
922    * @param userName
923    * a user name.
924    * @return
925    * the removed <code>UsmUser</code> instance associate with the given
926    * <code>userName</code> or <code>null</code> if such a user could not
927    * be found.
928    */

929   public UsmUser removeUser(OctetString engineID, OctetString userName) {
930     UsmUserEntry entry = userTable.removeUser(engineID, userName);
931     if (entry != null) {
932       fireUsmUserChange(new UsmUserEvent(this, entry, UsmUserEvent.USER_REMOVED));
933       return entry.getUsmUser();
934     }
935     return null;
936   }
937
938   /**
939    * Removes all users from the USM.
940    */

941   public void removeAllUsers() {
942     userTable.clear();
943     fireUsmUserChange(new UsmUserEvent(this, null, UsmUserEvent.USER_REMOVED));
944   }
945
946   /**
947    * Adds a localized user to the USM.
948    * @param engineID
949    * the engine ID for which the user has been localized.
950    * @param userName
951    * the user's name.
952    * @param authProtocol
953    * the authentication protocol ID.
954    * @param authKey
955    * the authentication key.
956    * @param privProtocol
957    * the privacy protocol ID.
958    * @param privKey
959    * the privacy key.
960    * @return
961    * the added <code>UsmUserEntry</code>.
962    */

963   public UsmUserEntry addLocalizedUser(byte[] engineID,
964                                        OctetString userName,
965                                        OID authProtocol, byte[] authKey,
966                                        OID privProtocol, byte[] privKey) {
967     UsmUserEntry newEntry = new UsmUserEntry(engineID, userName,
968                                              authProtocol, authKey,
969                                              privProtocol, privKey);
970     userTable.addUser(newEntry);
971     fireUsmUserChange(new UsmUserEvent(this, newEntry,
972                                        UsmUserEvent.USER_ADDED));
973     return newEntry;
974   }
975
976   /**
977    * Checks whether engine ID discovery is enabled or not. If enabled, the USM
978    * will try to discover unknown engine IDs "on-the-fly" while processing the
979    * message.
980    * @return
981    * <code>true</code> if discovery is enabled, <code>false</code> otherwise.
982    */

983   public boolean isEngineDiscoveryEnabled() {
984     return engineDiscoveryEnabled;
985   }
986
987   /**
988    * Enables or disables automatic engine ID discovery.
989    * @param engineDiscoveryEnabled
990    * <code>true</code> if discovery should be enabled,
991    * <code>false</code> otherwise.
992    */

993   public void setEngineDiscoveryEnabled(boolean engineDiscoveryEnabled) {
994     this.engineDiscoveryEnabled = engineDiscoveryEnabled;
995   }
996
997   /**
998    * Removes a <code>UsmUserListener</code>.
999    * @param l
1000   * a proeviously added <code>UsmUserListener</code>.
1001   */

1002  public synchronized void removeUsmUserListener(UsmUserListener l) {
1003    if (usmUserListeners != null && usmUserListeners.contains(l)) {
1004      Vector JavaDoc v = (Vector JavaDoc) usmUserListeners.clone();
1005      v.removeElement(l);
1006      usmUserListeners = v;
1007    }
1008  }
1009
1010  /**
1011   * Adds a <code>UsmUserListener</code> that should be informed whenever the
1012   * internal USM user table is changed.
1013   *
1014   * @param l
1015   * a <code>UsmUserListener</code> that should be informed about
1016   * {@link UsmUserEvent} events.
1017   */

1018  public synchronized void addUsmUserListener(UsmUserListener l) {
1019    Vector JavaDoc v = (usmUserListeners == null) ? new Vector JavaDoc(2) :
1020        (Vector JavaDoc) usmUserListeners.clone();
1021    if (!v.contains(l)) {
1022      v.addElement(l);
1023      usmUserListeners = v;
1024    }
1025  }
1026
1027  /**
1028   * Removes the specified engine ID from the internal time cache and thus
1029   * forces an engine time rediscovery the next time the SNMP engine with
1030   * the supplied ID is contacted.
1031   *
1032   * @param engineID
1033   * the SNMP engine ID whose engine time to remove.
1034   * @since 1.6
1035   */

1036  public void removeEngineTime(OctetString engineID) {
1037    timeTable.removeEntry(engineID);
1038  }
1039
1040  /**
1041   * Fires a <code>UsmUserEvent</code>.
1042   * @param e
1043   * the <code>UsmUserEvent</code> to fire.
1044   */

1045  protected void fireUsmUserChange(UsmUserEvent e) {
1046    if (usmUserListeners != null) {
1047      Vector JavaDoc listeners = usmUserListeners;
1048      int count = listeners.size();
1049      for (int i = 0; i < count; i++) {
1050        ((UsmUserListener) listeners.elementAt(i)).usmUserChange(e);
1051      }
1052    }
1053  }
1054
1055  /**
1056   * Gets the counter support instance that can be used to register for
1057   * counter incremnetation events.
1058   * @return
1059   * a <code>CounterSupport</code> instance that is used to fire
1060   * {@link CounterEvent}.
1061   */

1062  public CounterSupport getCounterSupport() {
1063    return counterSupport;
1064  }
1065
1066  /**
1067   * Returns the security protocol collection used by this USM.
1068   * @return
1069   * a <code>SecurityProtocols</code> instance which is by default the
1070   * same instance as returned by {@link SecurityProtocols#getInstance()}.
1071   * @since 1.2
1072   */

1073  public SecurityProtocols getSecurityProtocols() {
1074    return securityProtocols;
1075  }
1076
1077  /**
1078   * Sets the counter support instance. By default, the singleton instance
1079   * provided by the {@link CounterSupport} instance is used.
1080   * @param counterSupport
1081   * a <code>CounterSupport</code> subclass instance.
1082   */

1083  public void setCounterSupport(CounterSupport counterSupport) {
1084    if (counterSupport == null) {
1085      throw new NullPointerException JavaDoc();
1086    }
1087    this.counterSupport = counterSupport;
1088  }
1089}
1090
Popular Tags