001: /*_############################################################################
002: _##
003: _## SNMP4J-Agent - UsmMIB.java
004: _##
005: _## Copyright (C) 2005-2007 Frank Fock (SNMP4J.org)
006: _##
007: _## Licensed under the Apache License, Version 2.0 (the "License");
008: _## you may not use this file except in compliance with the License.
009: _## You may obtain a copy of the License at
010: _##
011: _## http://www.apache.org/licenses/LICENSE-2.0
012: _##
013: _## Unless required by applicable law or agreed to in writing, software
014: _## distributed under the License is distributed on an "AS IS" BASIS,
015: _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: _## See the License for the specific language governing permissions and
017: _## limitations under the License.
018: _##
019: _##########################################################################*/
020:
021: package org.snmp4j.agent.mo.snmp;
022:
023: import org.snmp4j.agent.*;
024: import org.snmp4j.agent.mo.*;
025: import org.snmp4j.agent.request.*;
026: import org.snmp4j.event.*;
027: import org.snmp4j.mp.*;
028: import org.snmp4j.security.*;
029: import org.snmp4j.smi.*;
030: import org.snmp4j.PDU;
031: import org.snmp4j.agent.mo.DefaultMOTable.ChangeSet;
032:
033: /**
034: * The <code>UsmMIB</code> implements the SNMP-USER-BASED-SM-MIB defined in
035: * RFC 3414. The MIB implementation is backed by a {@link USM} instance.
036: * The configuration of the user based security model can be changed
037: * programatically by changing the underlying {@link USM} or via SNMP but at
038: * least one user must be created programatically in order to allow any access
039: * to the agent via SNMP.
040: * <p>
041: * When modifying the USM after having created this MIB, you will have to
042: * register this object as {@link UsmUserListener} to the USM.
043: * <p>
044: * By using SNMP, a new users can only be created by cloning it from an existing
045: * user with the same or higher security level.
046: *
047: * @author Frank Fock
048: * @version 1.0
049: */
050: public class UsmMIB implements MOGroup, CounterListener,
051: MOValueValidationListener, UsmUserListener {
052:
053: private static final OID noAuthProtocol = new OID(new int[] { 1, 3,
054: 6, 1, 6, 3, 10, 1, 1, 1 });
055: private static final OID noPrivProtocol = new OID(new int[] { 1, 3,
056: 6, 1, 6, 3, 10, 1, 2, 1 });
057:
058: private static final OID usmUserSpinLockOID = new OID(new int[] {
059: 1, 3, 6, 1, 6, 3, 15, 1, 2, 1, 0 });
060: private static final OID usmUserEntryOID = new OID(new int[] { 1,
061: 3, 6, 1, 6, 3, 15, 1, 2, 2, 1 });
062:
063: static final int colUsmUserSecurityName = 0;
064: static final int colUsmUserCloneFrom = 1;
065: static final int colUsmUserAuthProtocol = 2;
066: static final int colUsmUserAuthKeyChange = 3;
067: static final int colUsmUserOwnAuthKeyChange = 4;
068: static final int colUsmUserPrivProtocol = 5;
069: static final int colUsmUserPrivKeyChange = 6;
070: static final int colUsmUserOwnPrivKeyChange = 7;
071: static final int colUsmUserPublic = 8;
072: static final int colUsmUserStorageType = 9;
073: static final int colUsmUserStatus = 10;
074:
075: private static final int[][] keyChangeColumns = {
076: { colUsmUserAuthKeyChange, colUsmUserOwnAuthKeyChange },
077: { colUsmUserPrivKeyChange, colUsmUserOwnPrivKeyChange } };
078: private USM usm;
079: private SecurityProtocols securityProtocols;
080:
081: private static final OID usmStatsPrefix = new OID(
082: SnmpConstants.usmStatsUnsupportedSecLevels.getValue(), 0,
083: SnmpConstants.usmStatsUnsupportedSecLevels.size() - 2);
084:
085: private static final OID[] usmStatOIDs = new OID[] {
086: SnmpConstants.usmStatsUnsupportedSecLevels,
087: SnmpConstants.usmStatsNotInTimeWindows,
088: SnmpConstants.usmStatsUnknownUserNames,
089: SnmpConstants.usmStatsUnknownEngineIDs,
090: SnmpConstants.usmStatsWrongDigests,
091: SnmpConstants.usmStatsDecryptionErrors };
092:
093: private MOScalar[] usmStats;
094: private TestAndIncr usmUserSpinLock;
095: private DefaultMOTable usmUserEntry;
096: private UsmTableModel usmUserTableModel;
097:
098: /**
099: * Creates a USM MIB implementation connected to the supplied USM. The MIB
100: * contents will reflect any changes to the USM after completion of this
101: * constructor if you register this object as {@link UsmUserListener} to the
102: * USM!
103: * @param usm
104: * a User-based Security Model.
105: * @param securityProtocols
106: * the supported <code>SecurityProtocols</code>.
107: */
108: public UsmMIB(USM usm, SecurityProtocols securityProtocols) {
109: this .usm = usm;
110: this .securityProtocols = securityProtocols;
111: usm.getCounterSupport().addCounterListener(this );
112: createUsmStats();
113: createUsmUser();
114: }
115:
116: private void createUsmUser() {
117: usmUserSpinLock = new TestAndIncr(usmUserSpinLockOID);
118: MOTableSubIndex[] usmUserSubIndexes = new MOTableSubIndex[] {
119: new MOTableSubIndex(SMIConstants.SYNTAX_OCTET_STRING,
120: 5, 32),
121: new MOTableSubIndex(SMIConstants.SYNTAX_OCTET_STRING,
122: 1, 32) };
123: MOColumn[] usmUserColumns = new MOColumn[] {
124: new SnmpAdminString(colUsmUserSecurityName + 3,
125: MOAccessImpl.ACCESS_READ_ONLY, null, false),
126: new UsmRowPointer(colUsmUserCloneFrom + 3,
127: MOAccessImpl.ACCESS_READ_CREATE, null, true),
128: new AutonomousType(colUsmUserAuthProtocol + 3,
129: MOAccessImpl.ACCESS_READ_CREATE,
130: noAuthProtocol, true),
131: new UsmKeyChange(colUsmUserAuthKeyChange + 3,
132: MOAccessImpl.ACCESS_READ_CREATE,
133: UsmKeyChange.AUTH_KEY_CHANGE),
134: new UsmOwnKeyChange(colUsmUserOwnAuthKeyChange + 3,
135: MOAccessImpl.ACCESS_READ_CREATE,
136: UsmKeyChange.AUTH_KEY_CHANGE),
137: new AutonomousType(colUsmUserPrivProtocol + 3,
138: MOAccessImpl.ACCESS_READ_CREATE,
139: noPrivProtocol, true),
140: new UsmKeyChange(colUsmUserPrivKeyChange + 3,
141: MOAccessImpl.ACCESS_READ_CREATE,
142: UsmKeyChange.PRIV_KEY_CHANGE),
143: new UsmOwnKeyChange(colUsmUserOwnPrivKeyChange + 3,
144: MOAccessImpl.ACCESS_READ_CREATE,
145: UsmKeyChange.PRIV_KEY_CHANGE),
146: new SnmpAdminString(colUsmUserPublic + 3,
147: MOAccessImpl.ACCESS_READ_CREATE,
148: new OctetString(), true, 0, 32),
149: new StorageType(colUsmUserStorageType + 3,
150: MOAccessImpl.ACCESS_READ_CREATE, new Integer32(
151: StorageType.nonVolatile), true),
152: new RowStatus(colUsmUserStatus + 3,
153: MOAccessImpl.ACCESS_READ_CREATE) };
154: MOTableIndex usmUserIndex = new MOTableIndex(usmUserSubIndexes,
155: false);
156: usmUserTableModel = new UsmTableModel(usmUserIndex);
157: usmUserEntry = new DefaultMOTable(usmUserEntryOID,
158: usmUserIndex, usmUserColumns, usmUserTableModel);
159: ((AutonomousType) usmUserColumns[colUsmUserAuthProtocol])
160: .addMOValueValidationListener(this );
161: ((AutonomousType) usmUserColumns[colUsmUserPrivProtocol])
162: .addMOValueValidationListener(this );
163: ((UsmRowPointer) usmUserColumns[colUsmUserCloneFrom])
164: .setTargetTable(usmUserEntry);
165: }
166:
167: private void createUsmStats() {
168: usmStats = new MOScalar[usmStatOIDs.length];
169: for (int i = 0; i < usmStats.length; i++) {
170: usmStats[i] = new MOScalar(usmStatOIDs[i],
171: MOAccessImpl.ACCESS_READ_ONLY, new Counter32(0));
172: }
173: }
174:
175: public void registerMOs(MOServer server, OctetString context)
176: throws DuplicateRegistrationException {
177: for (int i = 0; i < usmStats.length; i++) {
178: server.register(usmStats[i], context);
179: }
180: server.register(usmUserSpinLock, context);
181: server.register(usmUserEntry, context);
182: }
183:
184: public void unregisterMOs(MOServer server, OctetString context) {
185: for (int i = 0; i < usmStats.length; i++) {
186: server.unregister(usmStats[i], context);
187: }
188: server.unregister(usmUserSpinLock, context);
189: server.unregister(usmUserEntry, context);
190: }
191:
192: public void incrementCounter(CounterEvent event) {
193: if ((event.getOid().startsWith(usmStatsPrefix))
194: && (event.getOid().size() > usmStatsPrefix.size())) {
195: Counter32 current = (Counter32) usmStats[event.getOid()
196: .get(usmStatsPrefix.size()) - 1].getValue();
197: current.increment();
198: event.setCurrentValue((Counter32) current.clone());
199: }
200: }
201:
202: public void validate(MOValueValidationEvent validationEvent) {
203: if (validationEvent.getSource() instanceof MOColumn) {
204: MOColumn col = (MOColumn) validationEvent.getSource();
205: switch (col.getColumnID() - 4) {
206: case colUsmUserAuthProtocol: {
207: OID value = (OID) validationEvent.getNewValue();
208: if (!noAuthProtocol.equals(value)) {
209: AuthenticationProtocol authProtocol = SecurityProtocols
210: .getInstance()
211: .getAuthenticationProtocol(
212: (OID) validationEvent.getNewValue());
213: if (authProtocol == null) {
214: validationEvent
215: .setValidationStatus(SnmpConstants.SNMP_ERROR_WRONG_VALUE);
216: }
217: }
218: break;
219: }
220: case colUsmUserPrivProtocol: {
221: OID value = (OID) validationEvent.getNewValue();
222: if (!noPrivProtocol.equals(value)) {
223: PrivacyProtocol privProtocol = SecurityProtocols
224: .getInstance().getPrivacyProtocol(value);
225: if (privProtocol == null) {
226: validationEvent
227: .setValidationStatus(SnmpConstants.SNMP_ERROR_WRONG_VALUE);
228: }
229: }
230: break;
231: }
232: }
233: }
234: }
235:
236: private Variable[] getValuesFromUsmUser(UsmUserEntry user) {
237: Variable[] row = new Variable[usmUserEntry.getColumnCount()];
238: int n = 0;
239: row[n++] = user.getUsmUser().getSecurityName();
240: row[n++] = null;
241: row[n++] = user.getUsmUser().getAuthenticationProtocol();
242: row[n++] = null;
243: row[n++] = null;
244: row[n++] = user.getUsmUser().getPrivacyProtocol();
245: row[n++] = null;
246: row[n++] = null;
247: row[n++] = new OctetString();
248: row[n++] = new Integer32(StorageType.nonVolatile);
249: row[n++] = new Integer32(RowStatus.active);
250: return row;
251: }
252:
253: private OID createIndex(OctetString engineID, OctetString userName) {
254: if (engineID.length() == 0) {
255: engineID = usm.getLocalEngineID();
256: }
257: OID index = engineID.toSubIndex(false);
258: index.append(userName.toSubIndex(false));
259: return index;
260: }
261:
262: public void usmUserChange(UsmUserEvent event) {
263: switch (event.getType()) {
264: case UsmUserEvent.USER_ADDED: {
265: Variable[] values = getValuesFromUsmUser(event.getUser());
266: OID index = createIndex(event.getUser().getEngineID(),
267: event.getUser().getUserName());
268: MOMutableRow2PC row = (MOMutableRow2PC) usmUserTableModel
269: .createRow(index, values);
270: usmUserTableModel.addRow(row);
271: break;
272: }
273: case UsmUserEvent.USER_REMOVED: {
274: OID index = createIndex(event.getUser().getEngineID(),
275: event.getUser().getUserName());
276: usmUserTableModel.removeRow(index);
277: }
278: }
279: }
280:
281: private static byte[] getKey(UsmUserEntry entry, int authVsPriv) {
282: return (authVsPriv == 0) ? entry.getAuthenticationKey() : entry
283: .getPrivacyKey();
284: }
285:
286: public class UsmTableModel extends DefaultMOMutableTableModel {
287:
288: private MOTableIndex indexDef;
289:
290: public UsmTableModel(MOTableIndex indexDef) {
291: super ();
292: this .indexDef = indexDef;
293: }
294:
295: public MOTableRow createRow(OID index, Variable[] values) {
296: return new UsmTableRow(this , index, values);
297: }
298:
299: public MOTableIndex getIndexDef() {
300: return indexDef;
301: }
302: }
303:
304: private static boolean isKeyChanged(MOTableRow changeSet,
305: int keyChangeColumn) {
306: OctetString value = (OctetString) changeSet
307: .getValue(keyChangeColumn);
308: if ((value != null) && (value.length() > 0)) {
309: return true;
310: }
311: return false;
312: }
313:
314: private static boolean isKeyChanged(MOTableRow changeSet) {
315: return ((isKeyChanged(changeSet, colUsmUserOwnAuthKeyChange))
316: || (isKeyChanged(changeSet, colUsmUserAuthKeyChange))
317: || (isKeyChanged(changeSet, colUsmUserOwnPrivKeyChange)) || (isKeyChanged(
318: changeSet, colUsmUserPrivKeyChange)));
319: }
320:
321: public class UsmTableRow extends DefaultMOMutableRow2PC {
322:
323: private UsmTableModel tableModel;
324: private boolean cloned = false;
325:
326: public UsmTableRow(UsmTableModel model, OID index,
327: Variable[] values) {
328: super (index, values);
329: this .tableModel = model;
330: }
331:
332: public void setCloned(boolean cloned) {
333: this .cloned = cloned;
334: }
335:
336: public boolean isCloned() {
337: return cloned;
338: }
339:
340: public MOTableIndex getIndexDef() {
341: return tableModel.getIndexDef();
342: }
343:
344: public AuthenticationProtocol getAuthProtocol(
345: MOTableRow changeSet) {
346: OID authOID = getAuthProtocolOID(changeSet);
347: AuthenticationProtocol a = securityProtocols
348: .getAuthenticationProtocol(authOID);
349: return a;
350: }
351:
352: public PrivacyProtocol getPrivProtocol(MOTableRow changeSet) {
353: OID privOID = getPrivProtocolOID(changeSet);
354: PrivacyProtocol p = securityProtocols
355: .getPrivacyProtocol(privOID);
356: return p;
357: }
358:
359: public OID getPrivProtocolOID(MOTableRow preparedChanges) {
360: OID privID = null;
361: if (preparedChanges.getValue(colUsmUserCloneFrom) == null) {
362: privID = (OID) preparedChanges
363: .getValue(colUsmUserPrivProtocol);
364: }
365: if (privID == null) {
366: privID = (OID) getValue(colUsmUserPrivProtocol);
367: }
368: return privID;
369: }
370:
371: public void prepare(SubRequest subRequest,
372: MOTableRow preparedChanges, int column) {
373: switch (column) {
374: case colUsmUserAuthProtocol: {
375: OID authProtocol = (OID) subRequest
376: .getVariableBinding().getVariable();
377: if (!authProtocol.equals(noAuthProtocol)) {
378: subRequest.getStatus().setErrorStatus(
379: PDU.inconsistentValue);
380: } else {
381: OID privProtocol = null;
382: Variable privProtocolVariable = preparedChanges
383: .getValue(colUsmUserPrivProtocol);
384: if (privProtocolVariable instanceof OID) {
385: privProtocol = (OID) privProtocolVariable;
386: } else if (privProtocolVariable == null) {
387: privProtocol = (OID) getValue(colUsmUserPrivProtocol);
388: }
389: if ((privProtocol == null)
390: || (!privProtocol.equals(noPrivProtocol))) {
391: subRequest.getStatus().setErrorStatus(
392: PDU.inconsistentValue);
393: }
394: }
395: break;
396: }
397: case colUsmUserPrivProtocol: {
398: OID privProtocol = (OID) subRequest
399: .getVariableBinding().getVariable();
400: if (!privProtocol.equals(noPrivProtocol)) {
401: subRequest.getStatus().setErrorStatus(
402: PDU.inconsistentValue);
403: }
404: break;
405: }
406: }
407: }
408:
409: private OID getCloneFromIndex(MOTableRow changeSet) {
410: OID cloneFrom = (OID) changeSet
411: .getValue(colUsmUserCloneFrom);
412: if (cloneFrom == null) {
413: cloneFrom = (OID) getValue(colUsmUserCloneFrom);
414: }
415: if ((cloneFrom == null)
416: || (cloneFrom.size() <= usmUserEntryOID.size())) {
417: return null;
418: }
419: return new OID(cloneFrom.getValue(),
420: usmUserEntryOID.size() + 1, cloneFrom.size()
421: - (usmUserEntryOID.size() + 1));
422: }
423:
424: public synchronized void commitRow(SubRequest subRequest,
425: MOTableRow changeSet) {
426: if (subRequest.hasError()) {
427: return;
428: }
429: Variable[] indexValues = getIndexDef().getIndexValues(
430: getIndex());
431: OctetString engineID = (OctetString) indexValues[0];
432: OctetString userName = (OctetString) indexValues[1];
433: UsmUserEntry oldUserEntry;
434: OID cloneFromUserIndex = getCloneFromIndex(changeSet);
435: if (cloneFromUserIndex != null) {
436: Variable[] cloneFromIndexValues = getIndexDef()
437: .getIndexValues(cloneFromUserIndex);
438: OctetString cloneFromEngineID = (OctetString) cloneFromIndexValues[0];
439: OctetString cloneFromUserName = (OctetString) cloneFromIndexValues[1];
440:
441: oldUserEntry = usm.getUser(cloneFromEngineID,
442: cloneFromUserName);
443: // assign protocols
444: if (oldUserEntry != null) {
445: setValue(colUsmUserAuthProtocol, oldUserEntry
446: .getUsmUser().getAuthenticationProtocol());
447: setValue(colUsmUserPrivProtocol, oldUserEntry
448: .getUsmUser().getPrivacyProtocol());
449: }
450: } else {
451: oldUserEntry = usm.getUser(engineID, userName);
452: }
453: Integer32 newStatus = (Integer32) changeSet
454: .getValue(colUsmUserStatus);
455: if (((newStatus != null) && ((newStatus.getValue() == RowStatus.active) || (newStatus
456: .getValue() == RowStatus.createAndGo)))
457: || ((getValue(colUsmUserStatus) != null)
458: && (((Integer32) getValue(colUsmUserStatus))
459: .getValue() == RowStatus.active) && (isKeyChanged(changeSet)))) {
460: if (cloneFromUserIndex != null) {
461: // save undo value
462: setUserObject(oldUserEntry);
463: }
464: if (oldUserEntry == null) {
465: subRequest.getStatus().setErrorStatus(
466: PDU.commitFailed);
467: return;
468: }
469: OctetString[] newKeys = new OctetString[2];
470: OctetString[] oldKeys = new OctetString[2];
471:
472: AuthenticationProtocol a = getAuthProtocol(changeSet);
473: if (a != null) {
474: for (int p = 0; p < 2; p++) {
475: oldKeys[p] = new OctetString(getKey(
476: oldUserEntry, p));
477: for (int i = 0; i < keyChangeColumns[p].length; i++) {
478: OctetString keyChange = (OctetString) getValue(keyChangeColumns[p][i]);
479: if ((keyChange != null)
480: && (keyChange.length() > 0)) {
481: int keyLength = a.getDigestLength();
482:
483: if (p == 1) {
484: // privacy protocol key change
485: PrivacyProtocol privProtocol = getPrivProtocol(changeSet);
486: keyLength = Math.min(keyLength,
487: privProtocol
488: .getMaxKeyLength());
489: }
490: newKeys[p] = KeyChange.changeKey(a,
491: oldKeys[p], keyChange,
492: keyLength);
493: break; // only one key change per protocol
494: }
495: }
496: }
497: }
498: UsmUserEntry newEntry = new UsmUserEntry(
499: engineID.getValue(),
500: userName,
501: getAuthProtocolOID(changeSet),
502: (newKeys[0] == null) ? ((oldKeys[0] == null) ? null
503: : oldKeys[0].getValue())
504: : newKeys[0].getValue(),
505: getPrivProtocolOID(changeSet),
506: (newKeys[1] == null) ? ((oldKeys[1] == null) ? null
507: : oldKeys[1].getValue())
508: : newKeys[1].getValue());
509: usm.updateUser(newEntry);
510: setValue(colUsmUserCloneFrom, null);
511: setValue(colUsmUserAuthKeyChange, null);
512: setValue(colUsmUserOwnAuthKeyChange, null);
513: setValue(colUsmUserPrivKeyChange, null);
514: setValue(colUsmUserOwnPrivKeyChange, null);
515: }
516: if (newStatus != null) {
517: switch (newStatus.getValue()) {
518: case RowStatus.createAndWait:
519: case RowStatus.createAndGo: {
520: setValue(colUsmUserSecurityName, userName);
521: break;
522: }
523: }
524: }
525: }
526:
527: public OID getAuthProtocolOID(MOTableRow changeSet) {
528: OID authID = null;
529: if (changeSet.getValue(colUsmUserCloneFrom) == null) {
530: authID = (OID) changeSet
531: .getValue(colUsmUserAuthProtocol);
532: }
533: if (authID == null) {
534: authID = (OID) getValue(colUsmUserAuthProtocol);
535: }
536: return authID;
537: }
538:
539: public void cleanupRow(SubRequest subRequest,
540: ChangeSet changeSet) {
541: setUserObject(null);
542: }
543:
544: public void undoRow(SubRequest subRequest, ChangeSet changeSet) {
545: if (getUserObject() != null) {
546: usm.updateUser((UsmUserEntry) getUserObject());
547: }
548: }
549:
550: }
551:
552: class UsmRowPointer extends RowPointer {
553:
554: public UsmRowPointer(int columnID, MOAccess access,
555: OID defaultValue, boolean mutableInService) {
556: super (columnID, access, defaultValue, mutableInService);
557: }
558:
559: public void prepare(SubRequest subRequest, MOTableRow row,
560: MOTableRow changeSet, int column) {
561: super .prepare(subRequest, row, changeSet, column);
562: if (!subRequest.hasError()) {
563: OID rowPointer = (OID) subRequest.getVariableBinding()
564: .getVariable();
565: MOTableCellInfo cell = getTargetTable().getCellInfo(
566: rowPointer);
567: if (cell.getIndex().equals(row.getIndex())) {
568: // cannot clone from self
569: subRequest.getStatus().setErrorStatus(
570: PDU.inconsistentValue);
571: }
572: }
573: }
574:
575: public Variable getValue(MOTableRow row, int column) {
576: return SnmpConstants.zeroDotZero;
577: }
578: }
579:
580: public void rowStatusChanged(RowStatusEvent event) {
581: }
582: }
|