mirror of
https://github.com/apache/maven-mvnd.git
synced 2025-09-09 01:30:57 +00:00
Merge pull request #391 from Syquel/bugfix/390_token
#390 Restrict usage of mvnd daemons to the current user by utilizing a token check
This commit is contained in:
@@ -19,6 +19,7 @@ import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -415,7 +416,7 @@ public class DaemonConnector {
|
||||
throws DaemonException.ConnectException {
|
||||
LOGGER.debug("Connecting to Daemon");
|
||||
try {
|
||||
DaemonConnection connection = connect(daemon.getAddress());
|
||||
DaemonConnection connection = connect(daemon.getAddress(), daemon.getToken());
|
||||
return new DaemonClientConnection(connection, daemon, staleAddressDetector, newDaemon, parameters);
|
||||
} catch (DaemonException.ConnectException e) {
|
||||
staleAddressDetector.maybeStaleAddress(e);
|
||||
@@ -444,7 +445,7 @@ public class DaemonConnector {
|
||||
}
|
||||
}
|
||||
|
||||
public DaemonConnection connect(int port) throws DaemonException.ConnectException {
|
||||
public DaemonConnection connect(int port, byte[] token) throws DaemonException.ConnectException {
|
||||
InetSocketAddress address = new InetSocketAddress(InetAddress.getLoopbackAddress(), port);
|
||||
try {
|
||||
LOGGER.debug("Trying to connect to address {}.", address);
|
||||
@@ -456,6 +457,13 @@ public class DaemonConnector {
|
||||
throw new DaemonException.ConnectException(String.format("Socket connected to itself on %s.", address));
|
||||
}
|
||||
LOGGER.debug("Connected to address {}.", socket.getRemoteSocketAddress());
|
||||
|
||||
ByteBuffer tokenBuffer = ByteBuffer.wrap(token);
|
||||
do {
|
||||
socketChannel.write(tokenBuffer);
|
||||
} while (tokenBuffer.remaining() > 0);
|
||||
LOGGER.debug("Exchanged token successfully");
|
||||
|
||||
return new DaemonConnection(socketChannel);
|
||||
} catch (DaemonException.ConnectException e) {
|
||||
throw e;
|
||||
|
@@ -26,11 +26,14 @@ import static org.mvndaemon.mvnd.common.DaemonState.Idle;
|
||||
*/
|
||||
public class DaemonInfo {
|
||||
|
||||
public static final int TOKEN_SIZE = 16;
|
||||
|
||||
private final String id;
|
||||
private final String javaHome;
|
||||
private final String mvndHome;
|
||||
private final int pid;
|
||||
private final int address;
|
||||
private final byte[] token;
|
||||
private final String locale;
|
||||
private final List<String> options;
|
||||
private final DaemonState state;
|
||||
@@ -38,7 +41,7 @@ public class DaemonInfo {
|
||||
private final long lastBusy;
|
||||
|
||||
public DaemonInfo(String id, String javaHome, String mavenHome,
|
||||
int pid, int address,
|
||||
int pid, int address, byte[] token,
|
||||
String locale, List<String> options,
|
||||
DaemonState state, long lastIdle, long lastBusy) {
|
||||
this.id = id;
|
||||
@@ -46,6 +49,7 @@ public class DaemonInfo {
|
||||
this.mvndHome = mavenHome;
|
||||
this.pid = pid;
|
||||
this.address = address;
|
||||
this.token = token;
|
||||
this.locale = locale;
|
||||
this.options = options;
|
||||
this.state = state;
|
||||
@@ -73,6 +77,10 @@ public class DaemonInfo {
|
||||
return address;
|
||||
}
|
||||
|
||||
public byte[] getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public String getLocale() {
|
||||
return locale;
|
||||
}
|
||||
@@ -106,7 +114,7 @@ public class DaemonInfo {
|
||||
lb = lastBusy;
|
||||
}
|
||||
return new DaemonInfo(id, javaHome, mvndHome, pid, address,
|
||||
locale, options, state, li, lb);
|
||||
token, locale, options, state, li, lb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -181,6 +181,10 @@ public class DaemonRegistry implements AutoCloseable {
|
||||
String mavenHome = readString();
|
||||
int pid = buffer.getInt();
|
||||
int address = buffer.getInt();
|
||||
|
||||
byte[] token = new byte[DaemonInfo.TOKEN_SIZE];
|
||||
buffer.get(token);
|
||||
|
||||
String locale = readString();
|
||||
List<String> opts = new ArrayList<>();
|
||||
int nbOpts = buffer.getInt();
|
||||
@@ -190,8 +194,8 @@ public class DaemonRegistry implements AutoCloseable {
|
||||
DaemonState state = DaemonState.values()[buffer.get()];
|
||||
long lastIdle = buffer.getLong();
|
||||
long lastBusy = buffer.getLong();
|
||||
DaemonInfo di = new DaemonInfo(daemonId, javaHome, mavenHome, pid, address, locale, opts, state,
|
||||
lastIdle, lastBusy);
|
||||
DaemonInfo di = new DaemonInfo(daemonId, javaHome, mavenHome, pid, address, token, locale,
|
||||
opts, state, lastIdle, lastBusy);
|
||||
infosMap.putIfAbsent(di.getId(), di);
|
||||
}
|
||||
stopEvents.clear();
|
||||
@@ -216,6 +220,7 @@ public class DaemonRegistry implements AutoCloseable {
|
||||
writeString(di.getMvndHome());
|
||||
buffer.putInt(di.getPid());
|
||||
buffer.putInt(di.getAddress());
|
||||
buffer.put(di.getToken());
|
||||
writeString(di.getLocale());
|
||||
buffer.putInt(di.getOptions().size());
|
||||
for (String opt : di.getOptions()) {
|
||||
|
@@ -41,7 +41,7 @@ public class DaemonRegistryTest {
|
||||
byte[] token = new byte[16];
|
||||
new Random().nextBytes(token);
|
||||
reg1.store(new DaemonInfo("12345678", "/java/home/",
|
||||
"/data/reg/", 0x12345678, 7502,
|
||||
"/data/reg/", 0x12345678, 7502, token,
|
||||
Locale.getDefault().toLanguageTag(), Arrays.asList("-Xmx"),
|
||||
DaemonState.Idle, System.currentTimeMillis(), System.currentTimeMillis()));
|
||||
|
||||
|
@@ -19,8 +19,11 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -116,6 +119,10 @@ public class Server implements AutoCloseable, Runnable {
|
||||
strategy = DaemonExpiration.master();
|
||||
memoryStatus = new DaemonMemoryStatus(executor);
|
||||
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
byte[] token = new byte[DaemonInfo.TOKEN_SIZE];
|
||||
secureRandom.nextBytes(token);
|
||||
|
||||
List<String> opts = new ArrayList<>();
|
||||
Arrays.stream(Environment.values())
|
||||
.filter(Environment::isDiscriminating)
|
||||
@@ -130,6 +137,7 @@ public class Server implements AutoCloseable, Runnable {
|
||||
Environment.MVND_HOME.asString(),
|
||||
DaemonRegistry.getProcessId(),
|
||||
socket.socket().getLocalPort(),
|
||||
token,
|
||||
Locale.getDefault().toLanguageTag(),
|
||||
opts,
|
||||
Busy, cur, cur);
|
||||
@@ -224,6 +232,13 @@ public class Server implements AutoCloseable, Runnable {
|
||||
|
||||
private void client(SocketChannel socket) {
|
||||
LOGGER.info("Client connected");
|
||||
if (!checkToken(socket)) {
|
||||
LOGGER.error("Received invalid token, dropping connection");
|
||||
updateState(DaemonState.Idle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try (DaemonConnection connection = new DaemonConnection(socket)) {
|
||||
LOGGER.info("Waiting for request");
|
||||
SynchronousQueue<Message> request = new SynchronousQueue<>();
|
||||
@@ -246,6 +261,23 @@ public class Server implements AutoCloseable, Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkToken(SocketChannel socket) {
|
||||
byte[] token = new byte[info.getToken().length];
|
||||
ByteBuffer tokenBuffer = ByteBuffer.wrap(token);
|
||||
|
||||
try {
|
||||
do {
|
||||
if (socket.read(tokenBuffer) == -1) {
|
||||
break;
|
||||
}
|
||||
} while (tokenBuffer.remaining() > 0);
|
||||
} catch (final IOException e) {
|
||||
LOGGER.debug("Discarding EOFException: {}", e.toString(), e);
|
||||
}
|
||||
|
||||
return MessageDigest.isEqual(info.getToken(), token);
|
||||
}
|
||||
|
||||
private void expirationCheck() {
|
||||
if (expirationLock.tryLock()) {
|
||||
try {
|
||||
|
Reference in New Issue
Block a user