Skip to content

Commit

Permalink
Password policy
Browse files Browse the repository at this point in the history
  • Loading branch information
SCadilhac committed Sep 5, 2024
1 parent f82a7c3 commit 14ecac1
Show file tree
Hide file tree
Showing 17 changed files with 757 additions and 141 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<polyglot.version>23.1.4</polyglot.version>
<hibernate.version>6.5.2.Final</hibernate.version>
<hibernate.version>6.6.0.Final</hibernate.version>
<grizzly.version>4.0.2</grizzly.version>
<jackson.version>2.17.2</jackson.version>
<jersey.version>3.1.7</jersey.version>
<swagger.version>2.2.22</swagger.version>
<swagger.version>2.2.23</swagger.version>
<slf4j.version>2.0.13</slf4j.version>
<jasypt.version>1.9.3</jasypt.version>
<lombok.version>1.18.34</lombok.version>
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/onl/netfishers/netshot/Netshot.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.util.FileSize;
import lombok.extern.slf4j.Slf4j;
import onl.netfishers.netshot.aaa.PasswordPolicy;
import onl.netfishers.netshot.aaa.Radius;
import onl.netfishers.netshot.aaa.Tacacs;
import onl.netfishers.netshot.cluster.ClusterManager;
Expand Down Expand Up @@ -589,6 +590,7 @@ public void handle(Signal sig) {
TakeSnapshotTask.loadConfig();
JavaScriptRule.loadConfig();
PythonRule.loadConfig();
PasswordPolicy.loadConfig();
}
});
log.warn("Netshot is started");
Expand Down
111 changes: 111 additions & 0 deletions src/main/java/onl/netfishers/netshot/aaa/PasswordPolicy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package onl.netfishers.netshot.aaa;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lombok.Getter;
import lombok.Setter;
import onl.netfishers.netshot.Netshot;

/**
* Password policy
*/
public class PasswordPolicy {

/**
* Exception to be thrown in case of password policy check failure
*/
static public class PasswordPolicyException extends Exception {
public PasswordPolicyException(String message) {
super(message);
}
}

/**
* Match for specific characters in the password.
*/
static public enum CharMatch {
ANY("mintotalchars", "minimum total length", ".", 1),
SPECIAL("minspecialchars", "minimum special characters count", "[!\"#$%&'()*+,-./:;<=>?@\\[\\]\\^_{}|~]", 0),
NUMERICAL("minnumericalchars", "minimum numerical character count", "[0-9]", 0),
LOWERCASE("minlowercasechars", "minimum lowercase character count", "[a-z]", 0),
UPPERCASE("minuppercasechars", "minimum uppercase character count", "[A-Z]", 0);

@Getter
private String name;

@Getter
private String description;

@Getter
private Pattern pattern;

@Getter
private int defaultValue;

private CharMatch(String name, String description, String pattern, int defaultValue) {
this.name = name;
this.description = description;
this.pattern = Pattern.compile(pattern);
this.defaultValue = defaultValue;
}

/**
* Count the number of matches in the given target string.
* @param target Where to find characters
* @return the number of matches
*/
public long countMatches(String target) {
Matcher m = this.pattern.matcher(target);
return m.results().count();
}
}

static private PasswordPolicy mainPolicy;

/**
* Load the main policy from configuration.
*/
public static void loadConfig() {
final String configPrefix = "netshot.aaa.passwordpolicy.";
PasswordPolicy policy = new PasswordPolicy();
policy.maxHistory = Netshot.getConfig(configPrefix + "maxhistory", 0, 0, Integer.MAX_VALUE);
policy.maxDuration = Netshot.getConfig(configPrefix + "maxduration", 0, 0, Integer.MAX_VALUE);
for (CharMatch m : CharMatch.values()) {
int min = Netshot.getConfig(configPrefix + m.getName(), m.getDefaultValue(), 0, 1024);
if (min > 0) {
policy.minCharMatchCounts.put(m, min);
}
}
PasswordPolicy.mainPolicy = policy;
}

static {
PasswordPolicy.loadConfig();
}

/**
* Get the main password policy (as configured in Netshot config file)
* @return the main password policy
*/
static public PasswordPolicy getMainPolicy() {
return PasswordPolicy.mainPolicy;
}

/** Max history count */
@Getter
@Setter
private int maxHistory = 0;

/** Max duration of the password, in days */
@Getter
@Setter
private int maxDuration = 0;

/** Min number of characters per character match */
@Getter
@Setter
private Map<CharMatch, Integer> minCharMatchCounts = new HashMap<>();
}
5 changes: 2 additions & 3 deletions src/main/java/onl/netfishers/netshot/aaa/Radius.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import java.util.List;

import onl.netfishers.netshot.Netshot;
import onl.netfishers.netshot.device.NetworkAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -158,7 +157,7 @@ public static boolean isAvailable() {
* @param password the password
* @return true, if successful
*/
public static UiUser authenticate(String username, String password, NetworkAddress remoteAddress) {
public static UiUser authenticate(String username, String password, String remoteAddress) {
if (!isAvailable()) {
return null;
}
Expand All @@ -170,7 +169,7 @@ public static UiUser authenticate(String username, String password, NetworkAddre
attributeList.add(new Attr_NASIdentifier(nasIdentifier));
}
if (remoteAddress != null) {
attributeList.add(new Attr_CallingStationId(remoteAddress.getIp()));
attributeList.add(new Attr_CallingStationId(remoteAddress));
}

boolean first = true;
Expand Down
32 changes: 19 additions & 13 deletions src/main/java/onl/netfishers/netshot/aaa/Tacacs.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import org.slf4j.MarkerFactory;

import onl.netfishers.netshot.Netshot;
import onl.netfishers.netshot.device.NetworkAddress;

/**
* The Tacacs class authenticates the users against a TACACS+ server.
Expand Down Expand Up @@ -122,13 +121,13 @@ public static boolean isAvailable() {
* @param password the password
* @return true, if successful
*/
public static UiUser authenticate(String username, String password, NetworkAddress remoteAddress) {
public static UiUser authenticate(String username, String password, String remoteAddress) {
if (!isAvailable()) {
return null;
}

try {
SessionClient authenSession = client.newSession(SVC.LOGIN, "rest", remoteAddress == null ? "0.0.0.0" : remoteAddress.getIp(), PRIV_LVL.USER.code());
SessionClient authenSession = client.newSession(SVC.LOGIN, "rest", remoteAddress, PRIV_LVL.USER.code());
AuthenReply authenReply = authenSession.authenticate_ASCII(username, password);

String roleAttribute = Netshot.getConfig("netshot.aaa.tacacs.role.attributename", "role");
Expand All @@ -138,7 +137,7 @@ public static UiUser authenticate(String username, String password, NetworkAddre
String operatorLevelRole = Netshot.getConfig("netshot.aaa.tacacs.role.operatorrole", "operator");

if (authenReply.isOK()) {
SessionClient authoSession = client.newSession(SVC.LOGIN, "rest", remoteAddress == null ? "0.0.0.0" : remoteAddress.getIp(), PRIV_LVL.USER.code());
SessionClient authoSession = client.newSession(SVC.LOGIN, "rest", remoteAddress, PRIV_LVL.USER.code());
AuthorReply authoReply = authoSession.authorize(
username,
METH.TACACSPLUS,
Expand Down Expand Up @@ -167,38 +166,45 @@ else if (role.equals(operatorLevelRole)) {
level = UiUser.LEVEL_OPERATOR;
}

aaaLogger.info(MarkerFactory.getMarker("AAA"), "The user {} passed TACACS+ authentication (with permission level {}).",
aaaLogger.info(MarkerFactory.getMarker("AAA"),
"The user {} passed TACACS+ authentication (with permission level {}).",
username, level);
UiUser user = new UiUser(username, level);
return user;
}
else {
// Authorization failed
if (authoReply.getData() != null) {
aaaLogger.info(MarkerFactory.getMarker("AAA"), "The user {} failed TACACS+ authorization. Server data: {}.",
aaaLogger.info(MarkerFactory.getMarker("AAA"),
"The user {} failed TACACS+ authorization. Server data: {}.",
username, authoReply.getData());
}
else if (authoReply.getServerMsg() != null) {
aaaLogger.info(MarkerFactory.getMarker("AAA"), "The user {} failed TACACS+ authorization. Server message: {}.",
aaaLogger.info(MarkerFactory.getMarker("AAA"),
"The user {} failed TACACS+ authorization. Server message: {}.",
username, authoReply.getServerMsg());
}
else {
aaaLogger.info(MarkerFactory.getMarker("AAA"), "The user {} failed TACACS+ authorization.", username);
aaaLogger.info(MarkerFactory.getMarker("AAA"),
"The user {} failed TACACS+ authorization.", username);
}
}
}
else {
// Authentication failed
if (authenReply.getData() != null) {
aaaLogger.info(MarkerFactory.getMarker("AAA"), "The user {} failed TACACS+ authentication. Server data: {}.",
aaaLogger.info(MarkerFactory.getMarker("AAA"),
"The user {} failed TACACS+ authentication. Server data: {}.",
username, authenReply.getData());
}
else if (authenReply.getServerMsg() != null) {
aaaLogger.info(MarkerFactory.getMarker("AAA"), "The user {} failed TACACS+ authentication. Server message: {}.",
aaaLogger.info(MarkerFactory.getMarker("AAA"),
"The user {} failed TACACS+ authentication. Server message: {}.",
username, authenReply.getServerMsg());
}
else {
aaaLogger.info(MarkerFactory.getMarker("AAA"), "The user {} failed TACACS+ authentication.", username);
aaaLogger.info(MarkerFactory.getMarker("AAA"),
"The user {} failed TACACS+ authentication.", username);
}
}
}
Expand All @@ -214,11 +220,11 @@ else if (authenReply.getServerMsg() != null) {
/**
* Log a message with TACACS+ accounting.
*/
public static void account(String method, String path, String username, String response, NetworkAddress remoteAddress) {
public static void account(String method, String path, String username, String response, String remoteAddress) {
if (!Tacacs.enableAccounting || !Tacacs.isAvailable()) {
return;
}
SessionClient acctSession = client.newSession(SVC.LOGIN, "rest", remoteAddress == null ? "0.0.0.0" : remoteAddress.getIp(), PRIV_LVL.USER.code());
SessionClient acctSession = client.newSession(SVC.LOGIN, "rest", remoteAddress, PRIV_LVL.USER.code());
try {
acctSession.account(ACCT.FLAG.STOP.code(), username, METH.TACACSPLUS, TYPE.ASCII, SVC.LOGIN, new Argument[] {
new Argument(String.format("%s %s => %s", method, path, response))
Expand Down
Loading

0 comments on commit 14ecac1

Please sign in to comment.