@@ -106,7 +106,7 @@ public class SaslServerAuthenticator implements Authenticator {
106
106
* state and likewise ends at either {@link #COMPLETE} or {@link #FAILED}.
107
107
*/
108
108
private enum SaslState {
109
- INITIAL_REQUEST , // May be GSSAPI token, SaslHandshake or ApiVersions for authentication
109
+ INITIAL_REQUEST , // May be SaslHandshake or ApiVersions for authentication
110
110
HANDSHAKE_OR_VERSIONS_REQUEST , // May be SaslHandshake or ApiVersions
111
111
HANDSHAKE_REQUEST , // After an ApiVersions request, next request must be SaslHandshake
112
112
AUTHENTICATE , // Authentication tokens (SaslHandshake v1 and above indicate SaslAuthenticate headers)
@@ -277,15 +277,11 @@ public void authenticate() throws IOException {
277
277
case REAUTH_PROCESS_HANDSHAKE :
278
278
case HANDSHAKE_OR_VERSIONS_REQUEST :
279
279
case HANDSHAKE_REQUEST :
280
+ case INITIAL_REQUEST :
280
281
handleKafkaRequest (clientToken );
281
282
break ;
282
283
case REAUTH_BAD_MECHANISM :
283
284
throw new SaslAuthenticationException (reauthInfo .badMechanismErrorMessage );
284
- case INITIAL_REQUEST :
285
- if (handleKafkaRequest (clientToken ))
286
- break ;
287
- // For default GSSAPI, fall through to authenticate using the client token as the first GSSAPI packet.
288
- // This is required for interoperability with 0.9.0.x clients which do not send handshake request
289
285
case AUTHENTICATE :
290
286
handleSaslToken (clientToken );
291
287
// When the authentication exchange is complete and no more tokens are expected from the client,
@@ -503,63 +499,51 @@ private void handleSaslToken(byte[] clientToken) throws IOException {
503
499
}
504
500
}
505
501
506
- private boolean handleKafkaRequest (byte [] requestBytes ) throws IOException , AuthenticationException {
507
- boolean isKafkaRequest = false ;
508
- String clientMechanism = null ;
502
+ /**
503
+ * @throws InvalidRequestException if the request is not in Kafka format or if the API key is invalid. Clients
504
+ * that support SASL without support for KIP-43 (e.g. Kafka Clients 0.9.x) are in the former bucket - the first
505
+ * packet such clients send is a GSSAPI token starting with 0x60.
506
+ */
507
+ private void handleKafkaRequest (byte [] requestBytes ) throws IOException , AuthenticationException {
509
508
try {
510
509
ByteBuffer requestBuffer = ByteBuffer .wrap (requestBytes );
511
510
RequestHeader header = RequestHeader .parse (requestBuffer );
512
511
ApiKeys apiKey = header .apiKey ();
513
512
514
- // A valid Kafka request header was received. SASL authentication tokens are now expected only
515
- // following a SaslHandshakeRequest since this is not a GSSAPI client token from a Kafka 0.9.0.x client.
516
- if (saslState == SaslState .INITIAL_REQUEST )
517
- setSaslState (SaslState .HANDSHAKE_OR_VERSIONS_REQUEST );
518
- isKafkaRequest = true ;
519
-
520
513
// Raise an error prior to parsing if the api cannot be handled at this layer. This avoids
521
514
// unnecessary exposure to some of the more complex schema types.
522
515
if (apiKey != ApiKeys .API_VERSIONS && apiKey != ApiKeys .SASL_HANDSHAKE )
523
- throw new IllegalSaslStateException ("Unexpected Kafka request of type " + apiKey + " during SASL handshake." );
516
+ throw new InvalidRequestException ("Unexpected Kafka request of type " + apiKey + " during SASL handshake." );
524
517
525
518
LOG .debug ("Handling Kafka request {} during {}" , apiKey , reauthInfo .authenticationOrReauthenticationText ());
526
519
527
-
528
520
RequestContext requestContext = new RequestContext (header , connectionId , clientAddress (), Optional .of (clientPort ()),
529
521
KafkaPrincipal .ANONYMOUS , listenerName , securityProtocol , ClientInformation .EMPTY , false );
530
522
RequestAndSize requestAndSize = requestContext .parseRequest (requestBuffer );
523
+
524
+ // A valid Kafka request was received, we can now update the sasl state
525
+ if (saslState == SaslState .INITIAL_REQUEST )
526
+ setSaslState (SaslState .HANDSHAKE_OR_VERSIONS_REQUEST );
527
+
531
528
if (apiKey == ApiKeys .API_VERSIONS )
532
529
handleApiVersionsRequest (requestContext , (ApiVersionsRequest ) requestAndSize .request );
533
- else
534
- clientMechanism = handleHandshakeRequest (requestContext , (SaslHandshakeRequest ) requestAndSize .request );
530
+ else {
531
+ String clientMechanism = handleHandshakeRequest (requestContext , (SaslHandshakeRequest ) requestAndSize .request );
532
+ if (!reauthInfo .reauthenticating () || reauthInfo .saslMechanismUnchanged (clientMechanism )) {
533
+ createSaslServer (clientMechanism );
534
+ setSaslState (SaslState .AUTHENTICATE );
535
+ }
536
+ }
535
537
} catch (InvalidRequestException e ) {
536
538
if (saslState == SaslState .INITIAL_REQUEST ) {
537
- // InvalidRequestException is thrown if the request is not in Kafka format or if the API key
538
- // is invalid. For compatibility with 0.9.0.x where the first packet is a GSSAPI token
539
- // starting with 0x60, revert to GSSAPI for both these exceptions.
540
- if (LOG .isDebugEnabled ()) {
541
- StringBuilder tokenBuilder = new StringBuilder ();
542
- for (byte b : requestBytes ) {
543
- tokenBuilder .append (String .format ("%02x" , b ));
544
- if (tokenBuilder .length () >= 20 )
545
- break ;
546
- }
547
- LOG .debug ("Received client packet of length {} starting with bytes 0x{}, process as GSSAPI packet" , requestBytes .length , tokenBuilder );
548
- }
549
- if (enabledMechanisms .contains (SaslConfigs .GSSAPI_MECHANISM )) {
550
- LOG .debug ("First client packet is not a SASL mechanism request, using default mechanism GSSAPI" );
551
- clientMechanism = SaslConfigs .GSSAPI_MECHANISM ;
552
- } else
553
- throw new UnsupportedSaslMechanismException ("Exception handling first SASL packet from client, GSSAPI is not supported by server" , e );
554
- } else
555
- throw e ;
556
- }
557
- if (clientMechanism != null && (!reauthInfo .reauthenticating ()
558
- || reauthInfo .saslMechanismUnchanged (clientMechanism ))) {
559
- createSaslServer (clientMechanism );
560
- setSaslState (SaslState .AUTHENTICATE );
539
+ // InvalidRequestException is thrown if the request is not in Kafka format or if the API key is invalid.
540
+ // If it's the initial request, this could be an ancient client (see method documentation for more details),
541
+ // a client configured with the wrong security protocol or a non kafka-client altogether (eg http client).
542
+ throw new InvalidRequestException ("Invalid request, potential reasons: kafka client configured with the " +
543
+ "wrong security protocol, it does not support KIP-43 or it is not a kafka client." , e );
544
+ }
545
+ throw e ;
561
546
}
562
- return isKafkaRequest ;
563
547
}
564
548
565
549
private String handleHandshakeRequest (RequestContext context , SaslHandshakeRequest handshakeRequest ) throws IOException , UnsupportedSaslMechanismException {
0 commit comments