BindAuthenticatorFuzzer.java

// Copyright 2022 Google LLC
//
// 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 com.code_intelligence.jazzer.api.FuzzerSecurityIssueHigh;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.security.ldap.authentication.BindAuthenticator;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.ldap.core.AuthenticationSource;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldif.LDIFReader;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.authentication.BadCredentialsException;

public class BindAuthenticatorFuzzer {
    private static InMemoryDirectoryServer directoryServer;

    public static void fuzzerTearDown() {
        if (directoryServer instanceof InMemoryDirectoryServer) {
            directoryServer.shutDown(true);
        }
    }

    public static void fuzzerTestOneInput(FuzzedDataProvider data) {
        String username = data.consumeString(100);
        String password = data.consumeRemainingAsString();

        if (username.isEmpty() || password.isEmpty() || (username.equals("admin") && password.equals("secret"))) {
            return;
        }

        if (directoryServer instanceof InMemoryDirectoryServer) {
            directoryServer.shutDown(true);
        }

        createInMemoryLdapServer();

        DefaultSpringSecurityContextSource context = new DefaultSpringSecurityContextSource("ldap://localhost:1234/dc=springframework,dc=org");
        context.setUserDn("uid=admin,ou=system");
        context.setPassword("secret");
        context.afterPropertiesSet();

        BindAuthenticator authenticator = new BindAuthenticator(context);
        authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        DirContextOperations user = null;

        try {
            user = authenticator.authenticate(token);
        } catch (BadCredentialsException e) {
            // BadCredentialsException is expected here
        } finally {
            if (user != null) {
                throw new FuzzerSecurityIssueHigh("Invalid user `" + username + "` could authenticate");
            }
        }
    }

    private static void createInMemoryLdapServer() {
        String defaultPartitionName = "dc=springframework,dc=org";
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(defaultPartitionName);
            config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", 1234));
            config.setEnforceSingleStructuralObjectClass(false);
            config.setEnforceAttributeSyntaxCompliance(true);
            Entry dc = new Entry(new DN("dc=springframework,dc=org"));
            dc.addAttribute("objectClass", "top", "domain", "extensibleObject");
            dc.addAttribute("dc", "springframework");
            dc.addAttribute("ou", "people");

            Entry ou = new Entry(new DN("ou=people,dc=springframework,dc=org"));
            ou.addAttribute("objectClass", "organizationalUnit");
            ou.addAttribute("ou", "people");

            Entry cn = new Entry(new DN("uid=admin,ou=people,dc=springframework,dc=org"));
            cn.addAttribute("objectClass", "person");
            cn.addAttribute("objectClass", "inetOrgPerson");
            cn.addAttribute("cn", "Adm");
            cn.addAttribute("sn", "In");
            cn.addAttribute("uid", "admin");
            cn.addAttribute("userPassword", "secret");
            directoryServer = new InMemoryDirectoryServer(config);

            directoryServer.add(dc);
            directoryServer.add(ou);
            directoryServer.add(cn);
            directoryServer.startListening();
        } catch (LDAPException e) {
            e.printStackTrace();
        }
    }
}