-
Notifications
You must be signed in to change notification settings - Fork 0
KAFAK-14604: SASL session expiration time will be overflowed when calculation #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -690,7 +690,7 @@ public void setAuthenticationEndAndSessionReauthenticationTimes(long nowNanos) { | |||||||||||||
double pctToUse = pctWindowFactorToTakeNetworkLatencyAndClockDriftIntoAccount + RNG.nextDouble() | ||||||||||||||
* pctWindowJitterToAvoidReauthenticationStormAcrossManyChannelsSimultaneously; | ||||||||||||||
sessionLifetimeMsToUse = (long) (positiveSessionLifetimeMs * pctToUse); | ||||||||||||||
clientSessionReauthenticationTimeNanos = authenticationEndNanos + 1000 * 1000 * sessionLifetimeMsToUse; | ||||||||||||||
clientSessionReauthenticationTimeNanos = Math.addExact(authenticationEndNanos, Utils.msToNs(sessionLifetimeMsToUse)); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Code DuplicationThe same overflow handling pattern is duplicated in SaslClientAuthenticator and SaslServerAuthenticator. Consider extracting a common helper method that both classes can use for session expiration time calculation to improve maintainability. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Client Authentication OverflowClient-side session re-authentication time calculation is vulnerable to arithmetic overflow when large session lifetimes are used. This could lead to premature session expiration or authentication failures. The fix properly handles this by using Math.addExact and a new Utils.msToNs method with overflow detection.
Suggested change
Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Method Extraction OpportunityThe calculation of session expiration time appears in both SaslClientAuthenticator and SaslServerAuthenticator classes with identical logic. This duplication creates maintenance burden where changes must be made in multiple places consistently. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Time Calculation OverflowDirect multiplication of milliseconds to nanoseconds can cause arithmetic overflow with large session lifetimes. This could lead to authentication timing vulnerabilities where sessions expire prematurely or never expire due to integer overflow. The fix correctly uses Math.addExact and a new utility method to detect overflow.
Suggested change
Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Exception HandlingThe code calls Utils.msToNs which can throw IllegalArgumentException, but there's no exception handling. If sessionLifetimeMsToUse is very large, the exception will propagate up the call stack potentially causing unexpected behavior in authentication logic. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate Code PatternSimilar overflow protection code was added to both SaslClientAuthenticator and SaslServerAuthenticator. The pattern of converting milliseconds to nanoseconds with overflow protection appears in multiple places, suggesting an opportunity for a more comprehensive time utility class. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Integer Overflow Vulnerability in Session Expiration CalculationInteger overflow when calculating session expiration time can lead to premature session expiration or authentication bypass. When sessionLifetimeMsToUse is very large, multiplying by 1000*1000 causes overflow, potentially setting expiration to a negative or small value. This could allow attackers to bypass authentication controls.
Suggested change
Standards
|
||||||||||||||
log.debug( | ||||||||||||||
"Finished {} with session expiration in {} ms and session re-authentication on or after {} ms", | ||||||||||||||
authenticationOrReauthenticationText(), positiveSessionLifetimeMs, sessionLifetimeMsToUse); | ||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -681,7 +681,7 @@ else if (!maxReauthSet) | |||||||||||||||||||||
else | ||||||||||||||||||||||
retvalSessionLifetimeMs = zeroIfNegative(Math.min(credentialExpirationMs - authenticationEndMs, connectionsMaxReauthMs)); | ||||||||||||||||||||||
|
||||||||||||||||||||||
sessionExpirationTimeNanos = authenticationEndNanos + 1000 * 1000 * retvalSessionLifetimeMs; | ||||||||||||||||||||||
sessionExpirationTimeNanos = Math.addExact(authenticationEndNanos, Utils.msToNs(retvalSessionLifetimeMs)); | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the change in
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Authentication Timing OverflowServer-side authentication expiration calculation is vulnerable to arithmetic overflow with large session lifetime values. This could cause incorrect session expiration timing, potentially leading to authentication failures or premature session termination.
Suggested change
Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Authentication Session CalculationDirect arithmetic calculation of session expiration time is vulnerable to integer overflow when large values (like Long.MAX_VALUE) are used. This could cause premature session expiration or authentication failures. The fix properly handles this by using Math.addExact and a new Utils.msToNs method with overflow detection.
Suggested change
Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Server Authentication OverflowDirect multiplication of milliseconds to nanoseconds can cause arithmetic overflow with large session lifetimes. This could lead to authentication timing vulnerabilities where sessions expire prematurely or never expire due to integer overflow. The fix correctly uses Math.addExact and a new utility method to detect overflow.
Suggested change
Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Exception HandlingThe code calls Utils.msToNs which can throw IllegalArgumentException, but there's no exception handling. If retvalSessionLifetimeMs is very large, the exception will propagate up the call stack potentially causing unexpected behavior in authentication logic. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Integer Overflow Vulnerability in Session Expiration CalculationInteger overflow when calculating session expiration time can lead to premature session termination or authentication bypass. When retvalSessionLifetimeMs is very large, multiplying by 1000*1000 causes overflow, potentially setting expiration to a negative or small value. This could allow attackers to bypass authentication controls.
Suggested change
Standards
|
||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
if (credentialExpirationMs != null) { | ||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1697,4 +1697,17 @@ public static ConfigDef mergeConfigs(List<ConfigDef> configDefs) { | |||||||||||||||||||||||||||||||||||||||||||||||
public interface ThrowingRunnable { | ||||||||||||||||||||||||||||||||||||||||||||||||
void run() throws Exception; | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||
* convert millisecond to nanosecond, or throw exception if overflow | ||||||||||||||||||||||||||||||||||||||||||||||||
* @param timeMs the time in millisecond | ||||||||||||||||||||||||||||||||||||||||||||||||
* @return the converted nanosecond | ||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||
public static long msToNs(long timeMs) { | ||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||
return Math.multiplyExact(1000 * 1000, timeMs); | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential Integer OverflowThe multiplication 1000 * 1000 is performed first before multiplying with timeMs, which could lead to integer overflow for large timeMs values. Reordering to Math.multiplyExact(timeMs, 1000 * 1000) would be safer to catch overflow earlier.
Suggested change
Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Magic Number UsageThe code uses magic numbers (1000 * 1000) for millisecond to nanosecond conversion. Extract these values to named constants to improve code readability and maintainability, making the intent clearer and facilitating future modifications. Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||
} catch (ArithmeticException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1706
to
+1709
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defensive Boundary CheckThe method relies solely on exception handling to detect overflow. Adding an explicit boundary check (timeMs > Long.MAX_VALUE / (1000 * 1000)) before multiplication would improve performance for edge cases by avoiding exception creation and provide clearer intent about the mathematical constraints. Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||
throw new IllegalArgumentException("Cannot convert " + timeMs + " millisecond to nanosecond due to arithmetic overflow", e); | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1707
to
+1710
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defensive Boundary CheckAdding a boundary check before the multiplication could prevent the exception entirely for known large values. For values exceeding Long.MAX_VALUE/1_000_000, an early check would be more efficient than throwing and catching exceptions, which have performance overhead. Standards
Comment on lines
+1708
to
+1710
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential Overflow RiskThe multiplication order creates unnecessary intermediate overflow risk. Calculating '1000 * 1000' first creates an intermediate value of 1,000,000 before multiplying by timeMs. Changing to 'Math.multiplyExact(timeMs, 1000L * 1000L)' would prevent potential overflow for smaller timeMs values. Standards
Comment on lines
+1708
to
+1710
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoded Error MessageError message is hardcoded and not internationalized. For a core utility method, this could make troubleshooting more difficult in non-English environments and violates internationalization best practices. Standards
Comment on lines
+1708
to
+1710
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error Message ClarityThe multiplication order (1000 * 1000 first, then result * timeMs) could cause intermediate overflow for large timeMs values. Reordering to (timeMs * 1000) * 1000 would provide better overflow protection by handling the largest value first. Standards
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent Error MessageError message uses singular 'millisecond' regardless of the timeMs value. For values other than 1, this creates grammatically incorrect error messages. Consistent error messaging improves troubleshooting and system observability. Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1706
to
+1711
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
Comment on lines
+1710
to
+1711
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incorrect Error MessageThe error message uses plural 'milliseconds' but singular 'nanosecond'. This inconsistency in plurality creates a grammatical error. The message should use either both singular or both plural forms for consistency. Standards
Comment on lines
+1707
to
+1711
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uncaught Arithmetic ExceptionThe multiplication 1000 * 1000 is performed before the multiplyExact call, bypassing overflow protection for that operation. This could lead to undetected overflow in the initial calculation before multiplyExact is applied to the result.
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1706
to
+1712
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Utility Method NamingThe utility method 'msToNs' uses abbreviated naming that reduces readability. While common in some contexts, fully descriptive method names like 'millisecondsToNanoseconds' improve code self-documentation, especially for utility methods that may be used across the codebase. Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -155,6 +155,7 @@ public class SaslAuthenticatorTest { | |||||||||||||||||||||||||||||
private static final long CONNECTIONS_MAX_REAUTH_MS_VALUE = 100L; | ||||||||||||||||||||||||||||||
private static final int BUFFER_SIZE = 4 * 1024; | ||||||||||||||||||||||||||||||
private static Time time = Time.SYSTEM; | ||||||||||||||||||||||||||||||
private static boolean needLargeExpiration = false; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private NioEchoServer server; | ||||||||||||||||||||||||||||||
private Selector selector; | ||||||||||||||||||||||||||||||
|
@@ -178,6 +179,7 @@ public void setup() throws Exception { | |||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@AfterEach | ||||||||||||||||||||||||||||||
public void teardown() throws Exception { | ||||||||||||||||||||||||||||||
needLargeExpiration = false; | ||||||||||||||||||||||||||||||
if (server != null) | ||||||||||||||||||||||||||||||
this.server.close(); | ||||||||||||||||||||||||||||||
if (selector != null) | ||||||||||||||||||||||||||||||
|
@@ -1607,6 +1609,42 @@ public void testCannotReauthenticateWithDifferentPrincipal() throws Exception { | |||||||||||||||||||||||||||||
server.verifyReauthenticationMetrics(0, 1); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||||||
public void testReauthenticateWithLargeReauthValue() throws Exception { | ||||||||||||||||||||||||||||||
// enable it, we'll get a large expiration timestamp token | ||||||||||||||||||||||||||||||
needLargeExpiration = true; | ||||||||||||||||||||||||||||||
String node = "0"; | ||||||||||||||||||||||||||||||
SecurityProtocol securityProtocol = SecurityProtocol.SASL_SSL; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
configureMechanisms(OAuthBearerLoginModule.OAUTHBEARER_MECHANISM, | ||||||||||||||||||||||||||||||
List.of(OAuthBearerLoginModule.OAUTHBEARER_MECHANISM)); | ||||||||||||||||||||||||||||||
// set a large re-auth timeout in server side | ||||||||||||||||||||||||||||||
saslServerConfigs.put(BrokerSecurityConfigs.CONNECTIONS_MAX_REAUTH_MS_CONFIG, Long.MAX_VALUE); | ||||||||||||||||||||||||||||||
Comment on lines
+1615
to
+1622
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential Connection LossTest uses Long.MAX_VALUE for CONNECTIONS_MAX_REAUTH_MS_CONFIG which triggers arithmetic overflow. This causes connection termination as shown in the test's verification logic. In production, similar overflow could cause unexpected connection drops affecting system availability.
Suggested change
Standards
|
||||||||||||||||||||||||||||||
server = createEchoServer(securityProtocol); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
// set to default value for sasl login configs for initialization in ExpiringCredentialRefreshConfig | ||||||||||||||||||||||||||||||
saslClientConfigs.put(SaslConfigs.SASL_LOGIN_REFRESH_WINDOW_FACTOR, SaslConfigs.DEFAULT_LOGIN_REFRESH_WINDOW_FACTOR); | ||||||||||||||||||||||||||||||
saslClientConfigs.put(SaslConfigs.SASL_LOGIN_REFRESH_WINDOW_JITTER, SaslConfigs.DEFAULT_LOGIN_REFRESH_WINDOW_JITTER); | ||||||||||||||||||||||||||||||
saslClientConfigs.put(SaslConfigs.SASL_LOGIN_REFRESH_MIN_PERIOD_SECONDS, SaslConfigs.DEFAULT_LOGIN_REFRESH_MIN_PERIOD_SECONDS); | ||||||||||||||||||||||||||||||
saslClientConfigs.put(SaslConfigs.SASL_LOGIN_REFRESH_BUFFER_SECONDS, SaslConfigs.DEFAULT_LOGIN_REFRESH_BUFFER_SECONDS); | ||||||||||||||||||||||||||||||
saslClientConfigs.put(SaslConfigs.SASL_LOGIN_CALLBACK_HANDLER_CLASS, AlternateLoginCallbackHandler.class); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
createCustomClientConnection(securityProtocol, OAuthBearerLoginModule.OAUTHBEARER_MECHANISM, node, true); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
// channel should be not null before sasl handshake | ||||||||||||||||||||||||||||||
assertNotNull(selector.channel(node)); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
TestUtils.waitForCondition(() -> { | ||||||||||||||||||||||||||||||
selector.poll(1000); | ||||||||||||||||||||||||||||||
// this channel should be closed due to session timeout calculation overflow | ||||||||||||||||||||||||||||||
return selector.channel(node) == null; | ||||||||||||||||||||||||||||||
}, "channel didn't close with large re-authentication value"); | ||||||||||||||||||||||||||||||
Comment on lines
+1637
to
+1641
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential Exception OverheadThe test waits for an exception to occur inside the poll operation, which may cause performance overhead in test execution. Exception-based control flow can introduce latency due to stack trace generation and exception handling machinery. Standards
Comment on lines
+1637
to
+1641
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exception Handling MissingTestUtils.waitForCondition lacks exception handling for timeout scenarios. If the channel doesn't close as expected, the test will fail with a timeout exception without clear diagnostic information, making troubleshooting difficult.
Suggested change
Standards
Comment on lines
+1637
to
+1641
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Error HandlingThe test asserts channel closure but doesn't verify the specific error condition. Without checking logs or exception type, it cannot confirm whether the channel closed due to the expected overflow error or some other unrelated issue. Standards
Comment on lines
+1637
to
+1641
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Error HandlingThe TestUtils.waitForCondition doesn't handle timeout exceptions. If the condition never satisfies (channel doesn't close), the test will hang indefinitely rather than failing with a clear timeout message. Standards
Comment on lines
+1637
to
+1641
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incomplete Error HandlingThe test expects channel closure on overflow but doesn't verify the specific error. Without validating the exact exception type or error message, the test might pass for wrong reasons. This could mask authentication bypass vulnerabilities if channel closes for unrelated reasons. Standards
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
// ensure metrics are as expected | ||||||||||||||||||||||||||||||
server.verifyAuthenticationMetrics(0, 0); | ||||||||||||||||||||||||||||||
server.verifyReauthenticationMetrics(0, 0); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@Test | ||||||||||||||||||||||||||||||
public void testCorrelationId() { | ||||||||||||||||||||||||||||||
SaslClientAuthenticator authenticator = new SaslClientAuthenticator( | ||||||||||||||||||||||||||||||
|
@@ -1936,7 +1974,7 @@ private void createClientConnection(SecurityProtocol securityProtocol, String sa | |||||||||||||||||||||||||||||
if (enableSaslAuthenticateHeader) | ||||||||||||||||||||||||||||||
createClientConnection(securityProtocol, node); | ||||||||||||||||||||||||||||||
else | ||||||||||||||||||||||||||||||
createClientConnectionWithoutSaslAuthenticateHeader(securityProtocol, saslMechanism, node); | ||||||||||||||||||||||||||||||
createCustomClientConnection(securityProtocol, saslMechanism, node, false); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private NioEchoServer startServerApiVersionsUnsupportedByClient(final SecurityProtocol securityProtocol, String saslMechanism) throws Exception { | ||||||||||||||||||||||||||||||
|
@@ -2024,15 +2062,13 @@ protected void enableKafkaSaslAuthenticateHeaders(boolean flag) { | |||||||||||||||||||||||||||||
return server; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private void createClientConnectionWithoutSaslAuthenticateHeader(final SecurityProtocol securityProtocol, | ||||||||||||||||||||||||||||||
final String saslMechanism, String node) throws Exception { | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
final ListenerName listenerName = ListenerName.forSecurityProtocol(securityProtocol); | ||||||||||||||||||||||||||||||
final Map<String, ?> configs = Collections.emptyMap(); | ||||||||||||||||||||||||||||||
final JaasContext jaasContext = JaasContext.loadClientContext(configs); | ||||||||||||||||||||||||||||||
final Map<String, JaasContext> jaasContexts = Collections.singletonMap(saslMechanism, jaasContext); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
SaslChannelBuilder clientChannelBuilder = new SaslChannelBuilder(ConnectionMode.CLIENT, jaasContexts, | ||||||||||||||||||||||||||||||
private SaslChannelBuilder saslChannelBuilderWithoutHeader( | ||||||||||||||||||||||||||||||
final SecurityProtocol securityProtocol, | ||||||||||||||||||||||||||||||
final String saslMechanism, | ||||||||||||||||||||||||||||||
final Map<String, JaasContext> jaasContexts, | ||||||||||||||||||||||||||||||
final ListenerName listenerName | ||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||
return new SaslChannelBuilder(ConnectionMode.CLIENT, jaasContexts, | ||||||||||||||||||||||||||||||
securityProtocol, listenerName, false, saslMechanism, | ||||||||||||||||||||||||||||||
null, null, null, time, new LogContext(), null) { | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
@@ -2059,6 +2095,42 @@ protected void setSaslAuthenticateAndHandshakeVersions(ApiVersionsResponse apiVe | |||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private void createCustomClientConnection( | ||||||||||||||||||||||||||||||
final SecurityProtocol securityProtocol, | ||||||||||||||||||||||||||||||
final String saslMechanism, | ||||||||||||||||||||||||||||||
String node, | ||||||||||||||||||||||||||||||
boolean withSaslAuthenticateHeader | ||||||||||||||||||||||||||||||
) throws Exception { | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
final ListenerName listenerName = ListenerName.forSecurityProtocol(securityProtocol); | ||||||||||||||||||||||||||||||
final Map<String, ?> configs = Collections.emptyMap(); | ||||||||||||||||||||||||||||||
final JaasContext jaasContext = JaasContext.loadClientContext(configs); | ||||||||||||||||||||||||||||||
final Map<String, JaasContext> jaasContexts = Collections.singletonMap(saslMechanism, jaasContext); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
SaslChannelBuilder clientChannelBuilder; | ||||||||||||||||||||||||||||||
if (!withSaslAuthenticateHeader) { | ||||||||||||||||||||||||||||||
clientChannelBuilder = saslChannelBuilderWithoutHeader(securityProtocol, saslMechanism, jaasContexts, listenerName); | ||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||
clientChannelBuilder = new SaslChannelBuilder(ConnectionMode.CLIENT, jaasContexts, | ||||||||||||||||||||||||||||||
securityProtocol, listenerName, false, saslMechanism, | ||||||||||||||||||||||||||||||
null, null, null, time, new LogContext(), null) { | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||
protected SaslClientAuthenticator buildClientAuthenticator(Map<String, ?> configs, | ||||||||||||||||||||||||||||||
AuthenticateCallbackHandler callbackHandler, | ||||||||||||||||||||||||||||||
String id, | ||||||||||||||||||||||||||||||
String serverHost, | ||||||||||||||||||||||||||||||
String servicePrincipal, | ||||||||||||||||||||||||||||||
TransportLayer transportLayer, | ||||||||||||||||||||||||||||||
Subject subject) { | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
return new SaslClientAuthenticator(configs, callbackHandler, id, subject, | ||||||||||||||||||||||||||||||
servicePrincipal, serverHost, saslMechanism, transportLayer, time, new LogContext()); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
Comment on lines
+2112
to
+2133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extract Common MethodThe SaslChannelBuilder creation with header could be extracted into a separate method similar to saslChannelBuilderWithoutHeader. This would improve code symmetry, reduce duplication, and make the createCustomClientConnection method more concise and easier to maintain. Standards
|
||||||||||||||||||||||||||||||
clientChannelBuilder.configure(saslClientConfigs); | ||||||||||||||||||||||||||||||
this.selector = NetworkTestUtils.createSelector(clientChannelBuilder, time); | ||||||||||||||||||||||||||||||
InetSocketAddress addr = new InetSocketAddress("localhost", server.port()); | ||||||||||||||||||||||||||||||
|
@@ -2507,10 +2579,11 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback | |||||||||||||||||||||||||||||
+ ++numInvocations; | ||||||||||||||||||||||||||||||
String headerJson = "{" + claimOrHeaderJsonText("alg", "none") + "}"; | ||||||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||||||
* Use a short lifetime so the background refresh thread replaces it before we | ||||||||||||||||||||||||||||||
* If we're testing large expiration scenario, use a large lifetime. | ||||||||||||||||||||||||||||||
* Otherwise, use a short lifetime so the background refresh thread replaces it before we | ||||||||||||||||||||||||||||||
* re-authenticate | ||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||
String lifetimeSecondsValueToUse = "1"; | ||||||||||||||||||||||||||||||
String lifetimeSecondsValueToUse = needLargeExpiration ? String.valueOf(Long.MAX_VALUE) : "1"; | ||||||||||||||||||||||||||||||
String claimsJson; | ||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||
claimsJson = String.format("{%s,%s,%s}", | ||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -269,6 +269,35 @@ public void testSessionExpiresAtTokenExpiry() throws IOException { | |
} | ||
} | ||
|
||
@Test | ||
public void testSessionWontExpireWithLargeExpirationTime() throws IOException { | ||
String mechanism = OAuthBearerLoginModule.OAUTHBEARER_MECHANISM; | ||
SaslServer saslServer = mock(SaslServer.class); | ||
MockTime time = new MockTime(0, 1, 1000); | ||
// set a Long.MAX_VALUE as the expiration time | ||
Duration largeExpirationTime = Duration.ofMillis(Long.MAX_VALUE); | ||
|
||
try ( | ||
MockedStatic<?> ignored = mockSaslServer(saslServer, mechanism, time, largeExpirationTime); | ||
MockedStatic<?> ignored2 = mockKafkaPrincipal("[principal-type]", "[principal-name"); | ||
TransportLayer transportLayer = mockTransportLayer() | ||
) { | ||
|
||
SaslServerAuthenticator authenticator = getSaslServerAuthenticatorForOAuth(mechanism, transportLayer, time, largeExpirationTime.toMillis()); | ||
|
||
mockRequest(saslHandshakeRequest(mechanism), transportLayer); | ||
authenticator.authenticate(); | ||
|
||
when(saslServer.isComplete()).thenReturn(false).thenReturn(true); | ||
mockRequest(saslAuthenticateRequest(), transportLayer); | ||
|
||
Throwable t = assertThrows(IllegalArgumentException.class, () -> authenticator.authenticate()); | ||
assertEquals(ArithmeticException.class, t.getCause().getClass()); | ||
assertEquals("Cannot convert " + Long.MAX_VALUE + " millisecond to nanosecond due to arithmetic overflow", | ||
Comment on lines
+295
to
+296
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent Error MessageThe test expects a specific error message that doesn't match the actual implementation in Utils.msToNs(). The test checks for "arithmetic overflow" while the implementation throws "due to arithmetic overflow", which will cause the test to fail. Standards
|
||
t.getMessage()); | ||
Comment on lines
+294
to
+297
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Exception HandlingThe test verifies exception type and message but doesn't verify the complete exception chain. The test should also verify that the ArithmeticException is properly wrapped inside the IllegalArgumentException to ensure proper exception propagation. Standards
Comment on lines
+294
to
+297
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Improve Error HandlingTest verifies exception handling but doesn't check if the connection is properly terminated after overflow. In production, a failed authentication due to overflow should ensure the connection is closed securely. Consider enhancing the test to verify proper connection termination to prevent potential authentication bypass. Standards
|
||
} | ||
} | ||
|
||
private SaslServerAuthenticator getSaslServerAuthenticatorForOAuth(String mechanism, TransportLayer transportLayer, Time time, Long maxReauth) { | ||
Map<String, ?> configs = Collections.singletonMap(BrokerSecurityConfigs.SASL_ENABLED_MECHANISMS_CONFIG, | ||
Collections.singletonList(mechanism)); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1109,6 +1109,13 @@ public void testTryAll() throws Throwable { | |
assertEquals(expected, recorded); | ||
} | ||
|
||
@Test | ||
public void testMsToNs() { | ||
assertEquals(1000000, Utils.msToNs(1)); | ||
assertEquals(0, Utils.msToNs(0)); | ||
assertThrows(IllegalArgumentException.class, () -> Utils.msToNs(Long.MAX_VALUE)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test case verifies that an assertThrows(IllegalArgumentException.class, () -> Utils.msToNs(Long.MAX_VALUE));
assertThrows(IllegalArgumentException.class, () -> Utils.msToNs(Long.MAX_VALUE - 1));
assertThrows(IllegalArgumentException.class, () -> Utils.msToNs(Long.MAX_VALUE / 2));
Comment on lines
+1114
to
+1116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent Test ValuesThe test verifies positive and zero cases but lacks testing for negative values. Since the msToNs method doesn't explicitly prohibit negative values, the test should verify correct handling of negative millisecond inputs to ensure complete logical coverage. Standards
Comment on lines
+1114
to
+1116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Boundary Value TestingTest only checks extreme values (0, 1, Long.MAX_VALUE) but misses important boundary cases. Adding tests for values near overflow threshold (Long.MAX_VALUE/1_000_000) would better verify the reliability of the msToNs method under edge conditions. Standards
Comment on lines
+1115
to
+1116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test Coverage EnhancementTest coverage for msToNs() only verifies extreme values (0 and Long.MAX_VALUE). Adding tests for boundary values (e.g., Long.MAX_VALUE/1000000) would ensure robust overflow detection across the full range of potential inputs. Standards
|
||
} | ||
|
||
private Callable<Void> recordingCallable(Map<String, Object> recordingMap, String success, TestException failure) { | ||
return () -> { | ||
if (success == null) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arithmetic Overflow Vulnerability
Direct multiplication without overflow checking can cause arithmetic overflow when large session lifetime values are used. This could lead to incorrect re-authentication timing calculations, potentially causing premature session expiration or authentication failures.
Standards