inejge / ldap3

A pure-Rust LDAP library using the Tokio stack
Apache License 2.0
226 stars 39 forks source link

Authentication error against Active Directory on windows client using simple bind #107

Closed yatesco closed 1 year ago

yatesco commented 1 year ago

Hi - I'm trying to convert a Java LDAP client to use this library and am consistently getting LDAP error 2 when authenticating with credentials that pass using the Java client.

The java client:

    protected List<Attributes> getAttributes() {
        List<Attributes> attributes;
        var env = this.buildEnv();
        InitialLdapContext ldapContext = null;
        NamingEnumeration<SearchResult> searchResults = null;

        try {
            ldapContext = new InitialLdapContext(env, null);
            ldapContext.setRequestControls(null);
            searchResults = ldapContext.search(this.searchPath, this.attributes, this.getSimpleSearchControls());
            attributes = new ArrayList<>();

            while(searchResults.hasMore()) {
                attributes.add(searchResults.next().getAttributes());
            }
        } catch (Exception exception) {
          // elided
        }
    }

    private Hashtable<String, String> buildEnv() {
        var env = new Hashtable<String, String>();
        env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
        env.put("java.naming.security.authentication", "simple");
        env.put("java.naming.referral", "follow");
        env.put("java.naming.provider.url", this.providerUrl);  // ldap://<server>
        env.put("java.naming.security.principal", this.securityPrinciple); // <username>
        env.put("java.naming.security.credentials", this.securityCredentials); // <password>
        return env;
    }

    private SearchControls getSimpleSearchControls() {
        SearchControls result = new SearchControls();
        result.setSearchScope(2);
        result.setTimeLimit(30000);
        return result;
    }

The rust client:

    let server = &args[1];
    let username = &args[2];
    let password = &args[3];
    let (conn, mut ldap) = LdapConnAsync::new(&server).await.unwrap();

    ldap3::drive!(conn);
    ldap.unbind().await.unwrap();
    println!("CONNECTED to server. Now authenticating...");

    let bind = ldap.simple_bind(username, password).await;
    println!("BIND WORKED? {}", bind.is_ok());
    println!("BIND: {:?}", &bind);

My understanding is that the two are essentially identical in terms of authenticating, but the Rust fails with:

CONNECTED to server. Now authenticating...
BIND WORKED? false
BIND: Err(OpSend { source: SendError((2, Single, Sequence(Sequence { id: 0, class: Application, inner: [Integer(Integer { id: 2, class: Universal, inner: 3 }), OctetString(OctetString { id: 4, class: Universal, inner: [112, 114, 105, 100, 101, 97, 110, 100, 106, 111, 121] }), OctetString(OctetString { id: 0, class: Context, inner: [79, 82, 68, 69, 82, 76, 89, 45, 99, 104, 97, 105, 114, 45, 104, 101, 108, 100, 45, 87, 79, 82, 75, 69, 82, 83] })] }), None, Sender { inner: Some(Inner { state: State { is_complete: false, is_closed: true, is_rx_task_set: false, is_tx_task_set: false } }) })) })

I appreciate this is very little to go on, but have I missed something? Help :-). Thanks!

inejge commented 1 year ago

This statement is misplaced in your Rust code:

    ldap.unbind().await.unwrap();

Comment it out and try again. The Unbind operation unconditionally drops the connection.

yatesco commented 1 year ago

I'm an idiot.

inejge commented 1 year ago

I'm not going to give the above comment a thumbs up to avoid misunderstanding, but you made my day :smiley: Don't worry, glitches happen.