org.snmp4j.security.UsmUser Maven / Gradle / Ivy
/*_############################################################################
_##
_## SNMP4J - UsmUser.java
_##
_## Copyright (C) 2003-2024 Frank Fock (SNMP4J.org)
_##
_## Licensed under the Apache License, Version 2.0 (the "License");
_## you may not use this file except in compliance with the License.
_## You may obtain a copy of the License at
_##
_## http://www.apache.org/licenses/LICENSE-2.0
_##
_## Unless required by applicable law or agreed to in writing, software
_## distributed under the License is distributed on an "AS IS" BASIS,
_## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
_## See the License for the specific language governing permissions and
_## limitations under the License.
_##
_##########################################################################*/
package org.snmp4j.security;
import org.snmp4j.SNMP4JSettings;
import org.snmp4j.User;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.OID;
import java.util.Objects;
/**
* The {@code UsmUser} class represents USM user providing information
* to secure SNMPv3 message exchange. A user is characterized by its security
* name and optionally by a authentication protocol and passphrase as well as
* a privacy protocol and passphrase.
*
* There are no setters for the attributes of this class, to prevent
* inconsistent states in the USM, when a user is changed from outside.
* Since version 3.8.0, the creator of instances of this class can define
* for non-localized users for which kind of SNMP processing, i.e., incoming
* or outgoing, localization is allowed.
*
* @author Frank Fock
* @version 3.8.0
*/
public class UsmUser implements User, Comparable, Cloneable {
private static final long serialVersionUID = -2258973598142206767L;
/**
* This enum defines which Localization operations are allowed for a {@link UsmUser}. By default, and backward
* compatibility, {@link LocalizationGrant#any} is the default. Use {@link #incoming} for users that
* should receive notifications from unknown authoritative engine IDs. Use {@link #outgoing} if the user
* must only be localized for command generators sending requests to command responders.
* @since 3.8.0
*/
public enum LocalizationGrant {
/**
* Never allow localization (does only make sense for non-localized users that should not be used
* with USM request processing). This is the default for already localized users.
*/
never,
/**
* Allow localization for outgoing request processing where the using engine is non-authoritative,
* i.e. a command generator is sending a request message.
*/
outgoing,
/**
* Allow localization for incoming message processing, i.e. receiving notifications.
*/
incoming,
/**
* Allow any localization (default).
*/
any };
private final OctetString securityName;
private final SecretOctetString authenticationPassphrase;
private final SecretOctetString privacyPassphrase;
private final OID authenticationProtocol;
private final OID privacyProtocol;
private OctetString localizationEngineID;
private LocalizationGrant localizationGrant = LocalizationGrant.any;
/**
* Creates a USM user.
* @param securityName
* the security name of the user (typically the username).
* @param authenticationProtocol
* the authentication protocol ID to be associated with this user. If set
* to {@code null}, this user only supports unauthenticated messages.
* @param authenticationPassphrase
* the authentication passphrase. If not {@code null},
* {@code authenticationProtocol} must also be not {@code null}.
* RFC3414 §11.2 requires passphrases to have a minimum length of 8 bytes.
* If the length of {@code authenticationPassphrase} is less than 8
* bytes an {@code IllegalArgumentException} is thrown.
* @param privacyProtocol
* the privacy protocol ID to be associated with this user. If set
* to {@code null}, this user only supports unencrypted messages.
* @param privacyPassphrase
* the privacy passphrase. If not {@code null},
* {@code privacyProtocol} must also be not {@code null}.
* RFC3414 §11.2 requires passphrases to have a minimum length of 8 bytes.
* If the length of {@code authenticationPassphrase} is less than 8
* bytes an {@code IllegalArgumentException} is thrown.
*/
public UsmUser(OctetString securityName,
OID authenticationProtocol,
OctetString authenticationPassphrase,
OID privacyProtocol,
OctetString privacyPassphrase) {
this(securityName, authenticationProtocol, authenticationPassphrase, privacyProtocol, privacyPassphrase,
LocalizationGrant.any);
}
/**
* Creates a USM user.
* @param securityName
* the security name of the user (typically the username).
* @param authenticationProtocol
* the authentication protocol ID to be associated with this user. If set
* to {@code null}, this user only supports unauthenticated messages.
* @param authenticationPassphrase
* the authentication passphrase. If not {@code null},
* {@code authenticationProtocol} must also be not {@code null}.
* RFC3414 §11.2 requires passphrases to have a minimum length of 8 bytes.
* If the length of {@code authenticationPassphrase} is less than 8
* bytes an {@code IllegalArgumentException} is thrown.
* @param privacyProtocol
* the privacy protocol ID to be associated with this user. If set
* to {@code null}, this user only supports unencrypted messages.
* @param privacyPassphrase
* the privacy passphrase. If not {@code null},
* {@code privacyProtocol} must also be not {@code null}.
* RFC3414 §11.2 requires passphrases to have a minimum length of 8 bytes.
* If the length of {@code authenticationPassphrase} is less than 8
* bytes an {@code IllegalArgumentException} is thrown.
* @param localizationGrant
* defines for what kind of USM message processing this non-localized user is allowed to be localized.
* Note: for automatic localization to happen, {@link USM#setEngineDiscoveryEnabled(boolean)} must be enabled.
* @since 3.8.0
*/
public UsmUser(OctetString securityName,
OID authenticationProtocol,
OctetString authenticationPassphrase,
OID privacyProtocol,
OctetString privacyPassphrase,
LocalizationGrant localizationGrant) {
if (securityName == null) {
throw new NullPointerException();
}
if (SNMP4JSettings.isCheckUsmUserPassphraseLength()) {
if ((authenticationProtocol != null) &&
((authenticationPassphrase != null) &&
(authenticationPassphrase.length() < USM.RFC3414_11_2_MIN_PASSWORD_LENGTH))) {
throw new IllegalArgumentException(
"USM passphrases must be at least 8 bytes long (RFC3414 §11.2)");
}
if ((privacyProtocol != null) &&
((privacyPassphrase != null) &&
(privacyPassphrase.length() < USM.RFC3414_11_2_MIN_PASSWORD_LENGTH))) {
throw new IllegalArgumentException(
"USM passphrases must be at least 8 bytes long (RFC3414 §11.2)");
}
}
this.securityName = securityName;
this.authenticationProtocol = authenticationProtocol;
this.authenticationPassphrase = SecretOctetString.fromOctetString(authenticationPassphrase);
this.privacyProtocol = privacyProtocol;
this.privacyPassphrase = SecretOctetString.fromOctetString(privacyPassphrase);
this.localizationGrant = localizationGrant;
}
/**
* Creates a localized USM user.
* @param securityName
* the security name of the user (typically the username).
* @param authenticationProtocol
* the authentication protcol ID to be associated with this user. If set
* to {@code null}, this user only supports unauthenticated messages.
* @param authenticationPassphrase
* the authentication passphrase. If not {@code null},
* {@code authenticationProtocol} must also be not {@code null}.
* RFC3414 §11.2 requires passphrases to have a minimum length of 8 bytes.
* If the length of {@code authenticationPassphrase} is less than 8
* bytes an {@code IllegalArgumentException} is thrown.
* @param privacyProtocol
* the privacy protocol ID to be associated with this user. If set
* to {@code null}, this user only supports unencrypted messages.
* @param privacyPassphrase
* the privacy passphrase. If not {@code null},
* {@code privacyProtocol} must also be not {@code null}.
* RFC3414 §11.2 requires passphrases to have a minimum length of 8 bytes.
* If the length of {@code authenticationPassphrase} is less than 8
* bytes an {@code IllegalArgumentException} is thrown.
* @param localizationEngineID
* if not {@code null}, the localizationEngineID specifies the
* engine ID for which the supplied passphrases are already localized.
* Such an USM user can only be used with the target whose engine ID
* equals localizationEngineID.
*/
public UsmUser(OctetString securityName,
OID authenticationProtocol,
OctetString authenticationPassphrase,
OID privacyProtocol,
OctetString privacyPassphrase,
OctetString localizationEngineID) {
if (securityName == null) {
throw new NullPointerException();
}
this.securityName = securityName;
this.authenticationProtocol = authenticationProtocol;
this.authenticationPassphrase = SecretOctetString.fromOctetString(authenticationPassphrase);
this.privacyProtocol = privacyProtocol;
this.privacyPassphrase = SecretOctetString.fromOctetString(privacyPassphrase);
this.localizationEngineID = localizationEngineID;
this.localizationGrant = LocalizationGrant.never;
}
/**
* Gets the user's security name.
* @return
* a clone of the user's security name.
*/
public OctetString getSecurityName() {
return (OctetString) securityName.clone();
}
/**
* Gets the authentication protocol ID.
* @return
* a clone of the authentication protocol ID or {@code null}.
*/
public OID getAuthenticationProtocol() {
if (authenticationProtocol == null) {
return null;
}
return (OID) authenticationProtocol.clone();
}
/**
* Gets the privacy protocol ID.
* @return
* a clone of the privacy protocol ID or {@code null}.
*/
public OID getPrivacyProtocol() {
if (privacyProtocol == null) {
return null;
}
return (OID) privacyProtocol.clone();
}
/**
* Gets the authentication passphrase.
* @return
* a clone of the authentication passphrase or {@code null}.
*/
public OctetString getAuthenticationPassphrase() {
if (authenticationPassphrase == null) {
return null;
}
return (OctetString) authenticationPassphrase.clone();
}
/**
* Gets the privacy passphrase.
* @return
* a clone of the privacy passphrase or {@code null}.
*/
public OctetString getPrivacyPassphrase() {
if (privacyPassphrase == null) {
return null;
}
return (OctetString) privacyPassphrase.clone();
}
/**
* Returns the localization engine ID for which this USM user has been already
* localized.
* @return
* {@code null} if this USM user is not localized or the SNMP engine
* ID of the target for which this user has been localized.
* @since 1.6
*/
public OctetString getLocalizationEngineID() {
return localizationEngineID;
}
/**
* Indicates whether the passphrases of this USM user need to be localized
* or not ({@code true} is returned in that case).
* @return
* {@code true} if the passphrases of this USM user represent
* localized keys.
* @since 1.6
*/
public boolean isLocalized() {
return (localizationEngineID != null);
}
/**
* Check if the localization of this user is granted (and not yet done) for the requested localization kind.
* @param requiredLocalizationGrant
* the requested kind of localization.
* @return
* {«code true} if requested localization is granted and this user has not been localized yet.
* For a required {@link LocalizationGrant#incoming}, {@link LocalizationGrant#incoming} or
* {@link LocalizationGrant#any} is necessary, for example.
* @since 3.8.0
*/
public boolean isLocalizationGranted(LocalizationGrant requiredLocalizationGrant) {
LocalizationGrant currentGrant = getLocalizationGrant();
if (currentGrant == LocalizationGrant.never || requiredLocalizationGrant == LocalizationGrant.never) {
return false;
}
else if (currentGrant == LocalizationGrant.any) {
return true;
}
else {
return requiredLocalizationGrant == currentGrant;
}
}
/**
* Gets the security model ID of the USM.
* @return
* {@link USM#getID()}
*/
public int getSecurityModel() {
return SecurityModel.SECURITY_MODEL_USM;
}
/**
* Compares two USM users by their security names.
* @param other
* another {@code UsmUser} instance.
* @return
* a negative integer, zero, or a positive integer as this object is
* less than, equal to, or greater than the specified object.
*/
public int compareTo(UsmUser other) {
// allow only comparison with UsmUsers
return securityName.compareTo(other.securityName);
}
public Object clone() {
UsmUser copy = new UsmUser(this.securityName, this.authenticationProtocol,
this.authenticationPassphrase,
this.privacyProtocol, this.privacyPassphrase,
this.localizationEngineID);
copy.localizationGrant = this.localizationGrant;
return copy;
}
/**
* Return a copy of the current user with (updated) localized keys.
* @param localizationEngineID
* the {@code localizationEngineID} specifies the engine ID for which the supplied keys are already localized.
* Such an USM user can only be used with the target whose engine ID equals {@code localizationEngineID}.
* If {@code null}, then a {@link NullPointerException} will be thrown.
* @param localizedAuthenticationKey
* the optional new (localized) authentication key. If {@code null}, then the existing authentication key of this
* user is preserved and it is returned by {@link UsmUser} in its localized representation.
* @param localizedPrivacyKey
* the optional new (localized) privacy key. If {@code null}, then the existing privacy key of this user
* is preserved and it is returned by {@link UsmUser} in its localized representation.
* @param securityProtocols
* a collection of {@link SecurityProtocol} instances providing security protocols used by the
* {@link SecurityProtocols#passwordToKey(OID, OctetString, byte[])} operation to localize existing passphrases.
* If not provided (i.e. {@code null}) and at least one of the existing passphrases is not {@code null}, then
* a {@link NullPointerException} is thrown.
* @return
* a copy of this user but with localized (optionally new) authentication or privacy keys.
* @since 3.4.0
*/
public UsmUser localizeUser(OctetString localizationEngineID,
OctetString localizedAuthenticationKey, OctetString localizedPrivacyKey,
SecurityProtocols securityProtocols) {
if (getLocalizationEngineID() != null && !localizationEngineID.equals(getLocalizationEngineID()) &&
((localizedAuthenticationKey == null) || localizedPrivacyKey == null)) {
throw new IllegalArgumentException("Localization engine ID cannot be changed");
}
if (localizationEngineID == null || localizationEngineID.length() == 0) {
throw new NullPointerException();
}
OctetString newAuthKey;
OctetString newPrivKey;
if (localizedAuthenticationKey == null) {
if (getAuthenticationProtocol() != null && getAuthenticationPassphrase() != null) {
newAuthKey = new SecretOctetString(securityProtocols.passwordToKey(getAuthenticationProtocol(),
getAuthenticationPassphrase(), localizationEngineID.getValue()));
}
else {
newAuthKey = null;
}
}
else {
newAuthKey = localizedAuthenticationKey;
}
if (localizedPrivacyKey == null) {
if (getAuthenticationProtocol() != null && getPrivacyProtocol() != null && getPrivacyPassphrase() != null) {
newPrivKey = new SecretOctetString(securityProtocols.passwordToKey(getPrivacyProtocol(),
getAuthenticationProtocol(), getPrivacyPassphrase(), localizationEngineID.getValue()));
}
else {
newPrivKey = null;
}
}
else {
newPrivKey = localizedPrivacyKey;
}
return new UsmUser(getSecurityName(),
getAuthenticationProtocol(), newAuthKey, getPrivacyProtocol(), newPrivKey, localizationEngineID);
}
/**
* Returns for which kind of {@link USM} request processing a localization of this user is allowed.
* @return
* the allowed localization options. For already localized users, {@link LocalizationGrant#never} is returned.
* @since 3.8.0
*/
public LocalizationGrant getLocalizationGrant() {
return isLocalized() ? LocalizationGrant.never : localizationGrant;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UsmUser usmUser = (UsmUser) o;
if (!securityName.equals(usmUser.securityName)) return false;
if (!Objects.equals(authenticationPassphrase, usmUser.authenticationPassphrase))
return false;
if (!Objects.equals(privacyPassphrase, usmUser.privacyPassphrase))
return false;
if (!Objects.equals(authenticationProtocol, usmUser.authenticationProtocol))
return false;
if (!Objects.equals(privacyProtocol, usmUser.privacyProtocol))
return false;
if (!Objects.equals(localizationEngineID, usmUser.localizationEngineID))
return false;
if (!Objects.equals(localizationGrant, usmUser.localizationGrant))
return false;
return true;
}
@Override
public int hashCode() {
return securityName.hashCode();
}
public String toString() {
return "UsmUser[secName="+securityName+
",authProtocol="+authenticationProtocol+
",authPassphrase="+authenticationPassphrase+
",privProtocol="+privacyProtocol+
",privPassphrase="+privacyPassphrase+
",localizationEngineID="+getLocalizationEngineID()+
",allowedLocalization="+ localizationGrant +
"]";
}
}