Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/java/org/metricshub/winrm/light/CipherGen.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@
final byte[] challenge,
final byte[] targetInformation
) {
this.random = random;

Check warning on line 80 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP2

new org.metricshub.winrm.light.CipherGen(Random, long, String, String, String, byte[], byte[]) may expose internal representation by storing an externally mutable object into CipherGen.random
Raw output
 This code stores a reference to an externally mutable object into the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Storing a copy of the object is better approach in many situations.
this.currentTime = currentTime;

this.domain = domain;
this.user = user;
this.password = password;
this.challenge = challenge;

Check warning on line 86 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP2

new org.metricshub.winrm.light.CipherGen(Random, long, String, String, String, byte[], byte[]) may expose internal representation by storing an externally mutable object into CipherGen.challenge
Raw output
 This code stores a reference to an externally mutable object into the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Storing a copy of the object is better approach in many situations.
this.targetInformation = targetInformation;

Check warning on line 87 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP2

new org.metricshub.winrm.light.CipherGen(Random, long, String, String, String, byte[], byte[]) may expose internal representation by storing an externally mutable object into CipherGen.targetInformation
Raw output
 This code stores a reference to an externally mutable object into the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Storing a copy of the object is better approach in many situations.
}

/** Calculate and return client challenge */
Expand All @@ -108,7 +108,7 @@
if (secondaryKey == null) {
secondaryKey = makeSecondaryKey(random);
}
return secondaryKey;

Check warning on line 111 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getSecondaryKey() may expose internal representation by returning CipherGen.secondaryKey
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Calculate and return the LMHash */
Expand All @@ -124,7 +124,7 @@
if (lmResponse == null) {
lmResponse = lmResponse(getLMHash(), challenge);
}
return lmResponse;

Check warning on line 127 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getLMResponse() may expose internal representation by returning CipherGen.lmResponse
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Calculate and return the NTLMHash */
Expand All @@ -140,7 +140,7 @@
if (ntlmResponse == null) {
ntlmResponse = lmResponse(getNTLMHash(), challenge);
}
return ntlmResponse;

Check warning on line 143 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getNTLMResponse() may expose internal representation by returning CipherGen.ntlmResponse
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Calculate the LMv2 hash */
Expand Down Expand Up @@ -232,7 +232,7 @@
if (ntlmv2Response == null) {
ntlmv2Response = lmv2Response(getNTLMv2Hash(), challenge, getNTLMv2Blob());
}
return ntlmv2Response;

Check warning on line 235 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getNTLMv2Response() may expose internal representation by returning CipherGen.ntlmv2Response
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Calculate the LMv2Response */
Expand All @@ -240,7 +240,7 @@
if (lmv2Response == null) {
lmv2Response = lmv2Response(getLMv2Hash(), challenge, getClientChallenge());
}
return lmv2Response;

Check warning on line 243 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getLMv2Response() may expose internal representation by returning CipherGen.lmv2Response
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Get NTLM2SessionResponse */
Expand All @@ -248,7 +248,7 @@
if (ntlm2SessionResponse == null) {
ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(), challenge, getClientChallenge());
}
return ntlm2SessionResponse;

Check warning on line 251 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getNTLM2SessionResponse() may expose internal representation by returning CipherGen.ntlm2SessionResponse
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/**
Expand Down Expand Up @@ -402,7 +402,7 @@
System.arraycopy(clntChallenge, 0, lm2SessionResponse, 0, clntChallenge.length);
Arrays.fill(lm2SessionResponse, clntChallenge.length, lm2SessionResponse.length, (byte) 0x00);
}
return lm2SessionResponse;

Check warning on line 405 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getLM2SessionResponse() may expose internal representation by returning CipherGen.lm2SessionResponse
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Get LMUserSessionKey */
Expand All @@ -412,7 +412,7 @@
System.arraycopy(getLMHash(), 0, lmUserSessionKey, 0, 8);
Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00);
}
return lmUserSessionKey;

Check warning on line 415 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getLMUserSessionKey() may expose internal representation by returning CipherGen.lmUserSessionKey
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Get NTLMUserSessionKey */
Expand All @@ -422,7 +422,7 @@
md4.update(getNTLMHash());
ntlmUserSessionKey = md4.getOutput();
}
return ntlmUserSessionKey;

Check warning on line 425 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getNTLMUserSessionKey() may expose internal representation by returning CipherGen.ntlmUserSessionKey
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** GetNTLMv2UserSessionKey */
Expand All @@ -433,7 +433,7 @@
System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16);
ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash);
}
return ntlmv2UserSessionKey;

Check warning on line 436 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getNTLMv2UserSessionKey() may expose internal representation by returning CipherGen.ntlmv2UserSessionKey
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Get NTLM2SessionResponseUserSessionKey */
Expand All @@ -445,7 +445,7 @@
System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length);
ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce, getNTLMUserSessionKey());
}
return ntlm2SessionResponseUserSessionKey;

Check warning on line 448 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getNTLM2SessionResponseUserSessionKey() may expose internal representation by returning CipherGen.ntlm2SessionResponseUserSessionKey
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/** Get LAN Manager session key */
Expand All @@ -472,7 +472,7 @@
throw new NtlmException(e.getMessage(), e);
}
}
return lanManagerSessionKey;

Check warning on line 475 in src/main/java/org/metricshub/winrm/light/CipherGen.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP

org.metricshub.winrm.light.CipherGen.getLanManagerSessionKey() may expose internal representation by returning CipherGen.lanManagerSessionKey
Raw output
 Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Returning a new copy of the object is better approach in many situations.
}

/**
Expand Down Expand Up @@ -519,6 +519,10 @@
* Responses.
*/
private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash) throws NtlmException {
// CPD-OFF — near-duplicate of lmv2Hash() by design: both mirror the reference NTLM
// implementation (Apache HttpClient's NTLMEngineImpl), kept line-for-line comparable with
// upstream rather than factored through a shared helper. The one divergence is deliberate
// and easy to miss: LMv2 upper-cases the domain, NTLMv2 keeps its case.
if (NTLMEngineUtils.UNICODE_LITTLE_UNMARKED == null) {
throw new NtlmException("Unicode not supported");
}
Expand All @@ -529,6 +533,7 @@
hmacMD5.update(domain.getBytes(NTLMEngineUtils.UNICODE_LITTLE_UNMARKED));
}
return hmacMD5.getOutput();
// CPD-ON
}

/**
Expand Down
66 changes: 39 additions & 27 deletions src/main/java/org/metricshub/winrm/light/LightWinRMService.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down Expand Up @@ -167,24 +168,17 @@ public List<Map<String, Object>> executeWql(final String wqlQuery, final long ti

// Enforce the caller's timeout as a wall-clock deadline (throwing TimeoutException), matching
// the CXF WinRMService and bounding the WSMan Pull loop.
try {
return Utils.execute(
() -> {
final List<Map<String, String>> rows = client.wql(winRMEndpoint.getNamespace(), wqlQuery);
final List<Map<String, Object>> result = new ArrayList<>(rows.size());
for (final Map<String, String> row : rows) {
result.add(new LinkedHashMap<>(row));
}
return result;
},
timeout
);
} catch (final InterruptedException | ExecutionException e) {
if (e.getCause() != null) {
throw new WinRMException(e.getCause(), e.getCause().getMessage());
}
throw new WinRMException(e);
}
return executeWithTimeout(
() -> {
final List<Map<String, String>> rows = client.wql(winRMEndpoint.getNamespace(), wqlQuery);
final List<Map<String, Object>> result = new ArrayList<>(rows.size());
for (final Map<String, String> row : rows) {
result.add(new LinkedHashMap<>(row));
}
return result;
},
timeout
);
}

@Override
Expand All @@ -200,16 +194,34 @@ public WindowsRemoteCommandResult executeCommand(

// Enforce the caller's timeout as a wall-clock deadline (throwing TimeoutException), matching
// the CXF WinRMService and bounding the WSMan Receive loop.
return executeWithTimeout(
() -> {
final long start = Utils.getCurrentTimeMillis();
final WsmanClient.CommandOutput output = client.executeCommand(command, workingDirectory, charset);
final float executionTime = (Utils.getCurrentTimeMillis() - start) / 1000.0f;
return new WindowsRemoteCommandResult(output.stdout, output.stderr, executionTime, output.exitCode);
},
timeout
);
}

/**
* Run the task through {@link Utils#execute(Callable, long)} under the caller's wall-clock
* timeout, converting the executor's checked exceptions into {@link WinRMException} — the
* task's own failure is unwrapped from {@link ExecutionException} so the caller sees the real
* cause and its message.
*
* @param task the operation to run
* @param timeout timeout in milliseconds
* @param <T> the task's result type
* @return the task's result
* @throws TimeoutException when the deadline elapses first
* @throws WinRMException when the task fails or the wait is interrupted
*/
private static <T> T executeWithTimeout(final Callable<T> task, final long timeout)
throws TimeoutException, WinRMException {
try {
return Utils.execute(
() -> {
final long start = Utils.getCurrentTimeMillis();
final WsmanClient.CommandOutput output = client.executeCommand(command, workingDirectory, charset);
final float executionTime = (Utils.getCurrentTimeMillis() - start) / 1000.0f;
return new WindowsRemoteCommandResult(output.stdout, output.stderr, executionTime, output.exitCode);
},
timeout
);
return Utils.execute(task, timeout);
} catch (final InterruptedException | ExecutionException e) {
if (e.getCause() != null) {
throw new WinRMException(e.getCause(), e.getCause().getMessage());
Expand Down
Loading