AuthenticatorFuzzer.java
// Copyright 2023 the cncf-fuzzing authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import java.io.ByteArrayInputStream;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.authenticators.access.AllowAccessAuthenticatorFactory;
import org.keycloak.authentication.authenticators.access.DenyAccessAuthenticatorFactory;
import org.keycloak.authentication.authenticators.broker.IdpAutoLinkAuthenticatorFactory;
import org.keycloak.authentication.authenticators.broker.IdpConfirmLinkAuthenticatorFactory;
import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticatorFactory;
import org.keycloak.authentication.authenticators.broker.IdpDetectExistingBrokerUserAuthenticatorFactory;
import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticatorFactory;
import org.keycloak.authentication.authenticators.broker.IdpReviewProfileAuthenticatorFactory;
import org.keycloak.authentication.authenticators.broker.IdpUsernamePasswordFormFactory;
import org.keycloak.authentication.authenticators.x509.ValidateX509CertificateUsernameFactory;
import org.keycloak.authentication.authenticators.x509.X509ClientCertificateAuthenticatorFactory;
import org.keycloak.common.ClientConnection;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.DefaultKeycloakSession;
import org.keycloak.services.DefaultKeycloakSessionFactory;
import org.keycloak.services.x509.X509ClientCertificateLookup;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.mockito.Mockito;
public class AuthenticatorFuzzer {
private static MockObject mockObject;
private static CertificateFactory cf;
public static void fuzzerInitialize() {
try {
// Initialize certificate factory
cf = CertificateFactory.getInstance("X.509");
} catch (GeneralSecurityException e) {
// Directly exit if initialisation fails
throw new RuntimeException(e);
}
}
public static void fuzzerTestOneInput(FuzzedDataProvider data) {
try {
// Create mock object
mockObject = new MockObject();
// Create and randomize mock fields of mocked object instance
mockObject.mockInstance();
mockObject.randomizeMockData(data);
AuthenticatorFactory factory = null;
switch (data.consumeInt(1, 11)) {
case 1:
factory = new AllowAccessAuthenticatorFactory();
break;
case 2:
factory = new DenyAccessAuthenticatorFactory();
break;
case 3:
factory = new IdpAutoLinkAuthenticatorFactory();
break;
case 4:
factory = new IdpConfirmLinkAuthenticatorFactory();
break;
case 5:
factory = new IdpCreateUserIfUniqueAuthenticatorFactory();
break;
case 6:
factory = new IdpDetectExistingBrokerUserAuthenticatorFactory();
break;
case 7:
factory = new IdpEmailVerificationAuthenticatorFactory();
break;
case 8:
factory = new IdpReviewProfileAuthenticatorFactory();
break;
case 9:
factory = new IdpUsernamePasswordFormFactory();
break;
case 10:
factory = new ValidateX509CertificateUsernameFactory();
break;
case 11:
factory = new X509ClientCertificateAuthenticatorFactory();
break;
}
if (factory != null) {
Authenticator authenticator = factory.create(mockObject.getSession());
if (authenticator != null) {
AuthenticationExecutionModel model = generateRandomModel(data);
List<AuthenticationExecutionModel> executions = generateRandomModels(data);
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setAuthenticationSession(mockObject.getSessionModel());
processor.setSession(mockObject.getSession());
processor.setRealm(mockObject.getRealm());
processor.setConnection(mockObject.getConnection());
processor.newEvent();
AuthenticationFlowContext context =
processor.createAuthenticatorContext(model, authenticator, executions);
switch (data.consumeInt(1, 4)) {
case 1:
authenticator.authenticate(context);
break;
case 2:
authenticator.action(context);
break;
case 3:
authenticator.configuredFor(
mockObject.getSession(), mockObject.getRealm(), mockObject.getUser());
break;
case 4:
authenticator.areRequiredActionsEnabled(
mockObject.getSession(), mockObject.getRealm());
break;
}
}
}
} catch (IllegalArgumentException e) {
// Known exception
} finally {
cleanUpStaticMockObject();
}
}
private static AuthenticationExecutionModel generateRandomModel(FuzzedDataProvider data) {
AuthenticationExecutionModel model = new AuthenticationExecutionModel();
model.setId(data.consumeString(1024));
model.setAuthenticatorConfig(data.consumeString(1024));
model.setAuthenticator(data.consumeString(1024));
model.setFlowId(data.consumeString(1024));
model.setParentFlow(data.consumeString(1024));
model.setAuthenticatorFlow(data.consumeBoolean());
model.setPriority(data.consumeInt());
model.setRequirement(
data.pickValue(EnumSet.allOf(AuthenticationExecutionModel.Requirement.class)));
return model;
}
private static List<AuthenticationExecutionModel> generateRandomModels(FuzzedDataProvider data) {
List<AuthenticationExecutionModel> list = new LinkedList<AuthenticationExecutionModel>();
for (int i = 0; i < data.consumeInt(1, 5); i++) {
list.add(generateRandomModel(data));
}
return list;
}
private static class MockObject {
private KeycloakSession session;
private AuthenticationSessionModel model;
private ClientConnection clientConnection;
private X509ClientCertificateLookup certificateProvider;
private RealmModel realm;
private UserModel user;
private void mockInstance() {
mockAuthenticationSessionModel();
mockClientConnection();
mockRealmModel();
mockUserModel();
mockCertificateProvider();
mockKeycloakSession();
}
private void randomizeMockData(FuzzedDataProvider data) {
randomizeCertificateProvider(data);
randomizeAuthenticationSessionModel(data);
randomizeClientConnection(data);
randomizeRealmModel(data);
randomizeUserModel(data);
}
private void mockAuthenticationSessionModel() {
// Create and mock AuthenticationSessionModel with static data
model = Mockito.mock(AuthenticationSessionModel.class);
Mockito.doReturn(realm).when(model).getRealm();
}
private void mockClientConnection() {
// Create and mock ClientConnection with static data
clientConnection = Mockito.mock(ClientConnection.class);
}
private void mockRealmModel() {
// Create and mock RealmModel with static data
realm = Mockito.mock(RealmModel.class);
}
private void mockUserModel() {
// Create and mock UserModel with static data
user = Mockito.mock(UserModel.class);
}
private void mockCertificateProvider() {
// Create and mock X509ClientCertificateLookup with static data
certificateProvider = Mockito.mock(X509ClientCertificateLookup.class);
}
private void mockKeycloakSession() {
// Create and mock KeycloakSession with static data
session = Mockito.mock(DefaultKeycloakSession.class);
Mockito.doReturn(certificateProvider)
.when(session)
.getProvider(X509ClientCertificateLookup.class);
Mockito.doReturn(new DefaultKeycloakSessionFactory())
.when(session)
.getKeycloakSessionFactory();
}
private void randomizeAuthenticationSessionModel(FuzzedDataProvider data) {
// Randomize mock fields of AuthenticationSessionModel instance
Mockito.doReturn(data.consumeString(1024)).when(model).getAuthNote(Mockito.any(String.class));
}
private void randomizeClientConnection(FuzzedDataProvider data) {
// Randomize mock fields of Client Connection instance
Mockito.when(clientConnection.getRemoteAddr()).thenReturn(data.consumeString(1024));
Mockito.when(clientConnection.getRemoteHost()).thenReturn(data.consumeString(1024));
Mockito.when(clientConnection.getLocalAddr()).thenReturn(data.consumeString(1024));
Mockito.when(clientConnection.getRemotePort()).thenReturn(data.consumeInt(1, 65536));
Mockito.when(clientConnection.getLocalPort()).thenReturn(data.consumeInt(1, 65536));
}
private void randomizeRealmModel(FuzzedDataProvider data) {
// Randomize mock fields of RealmModel instance
Mockito.doReturn(data.consumeBoolean()).when(realm).isRegistrationEmailAsUsername();
Mockito.when(realm.getId()).thenReturn(data.consumeString(1024));
Mockito.when(realm.isEventsEnabled()).thenReturn(data.consumeBoolean());
Mockito.when(realm.getDefaultSignatureAlgorithm()).thenReturn(data.consumeString(1024));
}
private void randomizeUserModel(FuzzedDataProvider data) {
// Randomize mock fields of UserModel instance
Mockito.when(user.getUsername()).thenReturn(data.consumeString(1024));
}
private void randomizeCertificateProvider(FuzzedDataProvider data) {
// Randomize mock fields of CertificateProvider instance
// Generate certificate chain
Integer size = data.consumeInt(1, 5);
X509Certificate[] certs = new X509Certificate[size];
try {
for (Integer i = 0; i < size; i++) {
certs[i] =
(X509Certificate)
cf.generateCertificate(
new ByteArrayInputStream(data.consumeBytes(data.remainingBytes() / 2)));
}
Mockito.doReturn(certs).when(certificateProvider).getCertificateChain(Mockito.any());
} catch (GeneralSecurityException e) {
// Known exception
}
}
public AuthenticationSessionModel getSessionModel() {
return model;
}
public ClientConnection getConnection() {
return clientConnection;
}
public RealmModel getRealm() {
return realm;
}
public UserModel getUser() {
return user;
}
public KeycloakSession getSession() {
return session;
}
}
private static void cleanUpStaticMockObject() {
// Deference static mock object instance
mockObject = null;
// Clean up inline mocks of the mock objects
Mockito.framework().clearInlineMocks();
// Suggest the java garbage collector to clean up unused memory
System.gc();
}
}