diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/ServerAdmin.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/ServerAdmin.java index 2913e84549..66928c7c96 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/ServerAdmin.java +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/ServerAdmin.java @@ -289,16 +289,17 @@ public class ServerAdmin implements GhidraLaunchable { System.exit(-1); } String sid = args[i]; - if (!NamingUtilities.isValidName(sid) || sid.indexOf(' ') >= 0) { + if (!UserManager.isValidUserName(sid)) { Msg.error(UserAdmin.class, "Invalid username/sid: " + sid); System.exit(-1); } } /** - * Validate username/sid + * Validate repository name * @param args * @param i argument index + * @param rootDirFile base repository directory */ private void validateRepName(String[] args, int i, File rootDirFile) { if (args.length < (i + 1)) { diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/UserAdmin.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/UserAdmin.java index 74dff0ec4e..a8c6fadc16 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/UserAdmin.java +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/UserAdmin.java @@ -25,6 +25,7 @@ import org.apache.logging.log4j.Logger; import ghidra.framework.store.local.LocalFileSystem; import ghidra.util.exception.DuplicateNameException; +import utilities.util.FileUtilities; /** * UserAdmin is an Application for generating administrative @@ -205,35 +206,17 @@ public class UserAdmin { log.info("Processing " + files.length + " queued commands"); for (File file : files) { - ArrayList cmdList = readCommands(file); - Iterator it = cmdList.iterator(); - while (it.hasNext()) { - processCommand(repositoryMgr, it.next()); + List cmdList = FileUtilities.getLines(file); + for (String cmdStr : cmdList) { + if (cmdStr.isBlank()) { + continue; + } + processCommand(repositoryMgr, cmdStr.trim()); } file.delete(); } } - /** - * Read all command strings contained within a file. - * @param cmdFile command file - * @return list of command strings - * @throws IOException - */ - private static ArrayList readCommands(File cmdFile) throws IOException { - ArrayList cmdList = new ArrayList<>(); - BufferedReader rdr = new BufferedReader(new FileReader(cmdFile)); - String cmd; - while ((cmd = rdr.readLine()) != null) { - if (cmd.length() == 0) { - continue; - } - cmdList.add(cmd.trim()); - } - rdr.close(); - return cmdList; - } - /** * Store a list of command strings to a new command file. * @param cmdList list of command strings diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/UserManager.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/UserManager.java index eb57ecbd25..15aaf9e0de 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/UserManager.java +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/UserManager.java @@ -17,6 +17,7 @@ package ghidra.server; import java.io.*; import java.util.*; +import java.util.regex.Pattern; import javax.security.auth.login.FailedLoginException; import javax.security.auth.x500.X500Principal; @@ -26,8 +27,7 @@ import org.apache.logging.log4j.Logger; import ghidra.framework.remote.User; import ghidra.framework.store.local.LocalFileSystem; -import ghidra.util.HashUtilities; -import ghidra.util.NumericUtilities; +import ghidra.util.*; import ghidra.util.exception.DuplicateNameException; /** @@ -183,7 +183,7 @@ public class UserManager { if (x500User != null) { dnLookupMap.put(x500User, entry); } - putUserList(); + writeUserList(); } /** @@ -260,7 +260,7 @@ public class UserManager { if (x500User != null) { dnLookupMap.put(x500User, entry); } - putUserList(); + writeUserList(); return true; } return false; @@ -338,7 +338,7 @@ public class UserManager { if (entry.x500User != null) { dnLookupMap.put(entry.x500User, entry); } - putUserList(); + writeUserList(); return true; } return false; @@ -426,7 +426,7 @@ public class UserManager { if (oldEntry.x500User != null) { dnLookupMap.remove(oldEntry.x500User); } - putUserList(); + writeUserList(); } } @@ -457,7 +457,7 @@ public class UserManager { } userListUpdateInProgress = true; try { - getUserList(); + readUserListIfNeeded(); clearExpiredPasswords(); if (processCmds) { UserAdmin.processCommands(repositoryMgr); @@ -488,21 +488,22 @@ public class UserManager { } } if (dataChanged) { - putUserList(); + writeUserList(); } } /** - * Read user data from file. + * Read user data from file if the timestamp on the file has changed. + * * @throws IOException */ - private void getUserList() throws IOException { + private void readUserListIfNeeded() throws IOException { long lastMod = userFile.lastModified(); if (lastUserListChange == lastMod) { if (lastMod == 0) { // Create empty file if it does not yet exist - putUserList(); + writeUserList(); } return; } @@ -546,58 +547,62 @@ public class UserManager { } } - private static void readUserList(File file, LinkedHashMap list, - HashMap x500LookupMap) throws IOException { + private static void readUserList(File file, Map usersIndexByName, + Map x500LookupMap) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(file))) { - String line = br.readLine(); - while (line != null) { - if (!line.startsWith("#")) { - try { - StringTokenizer st = new StringTokenizer(line, ":"); - UserEntry entry = new UserEntry(); - entry.username = st.nextToken(); + String line; + while ((line = br.readLine()) != null) { + if (line.startsWith("#")) { + continue; + } + try { + StringTokenizer st = new StringTokenizer(line, ":"); + UserEntry entry = new UserEntry(); + entry.username = st.nextToken(); + if (!isValidUserName(entry.username)) { + log.error("Invalid user name, skipping: " + entry.username); + continue; + } - // Password Hash + // Password Hash + if (st.hasMoreTokens()) { + entry.passwordHash = st.nextToken().toCharArray(); + + // Password Time if (st.hasMoreTokens()) { - entry.passwordHash = st.nextToken().toCharArray(); - - // Password Time - if (st.hasMoreTokens()) { - try { - String timeStr = st.nextToken(); - if ("*".equals(timeStr)) { - entry.passwordTime = NO_EXPIRATION; - } - else { - entry.passwordTime = NumericUtilities.parseHexLong(timeStr); - } + try { + String timeStr = st.nextToken(); + if ("*".equals(timeStr)) { + entry.passwordTime = NO_EXPIRATION; } - catch (NumberFormatException e) { - log.error("Invalid password time - forced expiration: " + - entry.username); - entry.passwordTime = 0; - } - - // Distinguished Name - if (st.hasMoreTokens()) { - String dn = st.nextToken(); - if (dn.length() > 0) { - entry.x500User = new X500Principal(dn); - } - + else { + entry.passwordTime = NumericUtilities.parseHexLong(timeStr); } } - } - list.put(entry.username, entry); - if (entry.x500User != null) { - x500LookupMap.put(entry.x500User, entry); + catch (NumberFormatException e) { + log.error( + "Invalid password time - forced expiration: " + entry.username); + entry.passwordTime = 0; + } + + // Distinguished Name + if (st.hasMoreTokens()) { + String dn = st.nextToken(); + if (dn.length() > 0) { + entry.x500User = new X500Principal(dn); + } + + } } } - catch (NoSuchElementException e) { - // skip entry + usersIndexByName.put(entry.username, entry); + if (entry.x500User != null) { + x500LookupMap.put(entry.x500User, entry); } } - line = br.readLine(); + catch (NoSuchElementException e) { + // skip entry + } } } } @@ -606,12 +611,9 @@ public class UserManager { * Write user data to file. * @throws IOException */ - private void putUserList() throws IOException { - BufferedWriter bw = new BufferedWriter(new FileWriter(userFile)); - try { - Iterator iter = userList.values().iterator(); - while (iter.hasNext()) { - UserEntry entry = iter.next(); + private void writeUserList() throws IOException { + try (BufferedWriter bw = new BufferedWriter(new FileWriter(userFile))) { + for (UserEntry entry : userList.values()) { bw.write(entry.username); bw.write(":"); if (entry.passwordHash != null) { @@ -634,13 +636,6 @@ public class UserManager { bw.newLine(); } } - finally { - try { - bw.close(); - } - catch (IOException e) { - } - } lastUserListChange = userFile.lastModified(); } @@ -743,4 +738,20 @@ public class UserManager { } } + /* + * Regex: matches if the entire string is alpha, digit, ".", "-", "_", fwd or back slash. + */ + private static final Pattern VALID_USERNAME_REGEX = Pattern.compile("[a-zA-Z0-9.-_/\\\\]+"); + + /** + * Ensures a name only contains valid characters and meets length limitations. + * + * @param s name string + * @return boolean true if valid name, false if not valid + */ + public static boolean isValidUserName(String s) { + return VALID_USERNAME_REGEX.matcher(s).matches() && + s.length() <= NamingUtilities.MAX_NAME_LENGTH; + } + } diff --git a/Ghidra/Framework/Utility/src/main/java/utilities/util/FileUtilities.java b/Ghidra/Framework/Utility/src/main/java/utilities/util/FileUtilities.java index 9d7a04ca6d..9651c11619 100644 --- a/Ghidra/Framework/Utility/src/main/java/utilities/util/FileUtilities.java +++ b/Ghidra/Framework/Utility/src/main/java/utilities/util/FileUtilities.java @@ -785,13 +785,13 @@ public final class FileUtilities { * @throws IOException if there are any issues reading the file */ public static List getLines(BufferedReader in) throws IOException { - List fileLines = new ArrayList<>(); try { + List fileLines = new ArrayList<>(); String line; while ((line = in.readLine()) != null) { fileLines.add(line); } - in.close(); + return fileLines; } finally { try { @@ -801,7 +801,6 @@ public final class FileUtilities { // don't care; we tried } } - return fileLines; } /**