LDAP client library

Overview

LDAP client library

A pure-Rust LDAP client library using the Tokio stack.

Compatibility with Tokio versions

Tokio 1.0 is the long-term stable version of the async runtime, and ldap3 0.9 is compatible with it. For the earlier Tokio releases, use ldap3 0.8 (Tokio 0.3) or 0.7 (Tokio 0.2).

All functional changes in 0.9.2 have been backported to 0.8.3 and 0.7.4. The plan is to limit further 0.8.x and 0.7.x changes to bug- and compatibility fixes until June 2021, and continue development solely on 0.9.x.

Documentation

Note

The library is client-only. One cannot make an LDAP server or a proxy with it. It supports only version 3 of the protocol over connection-oriented transports.

Usage

The library can be used either synchronously or asynchronously. The aim is to offer essentially the same call interface for both flavors, with the necessary differences in interaction and return values according to the nature of I/O.

Add this to your Cargo.toml:

[dependencies.ldap3]
version = "0.9"

Examples

The following two examples perform exactly the same operation and should produce identical results. They should be run against the example server in the data subdirectory of the crate source. Other sample programs expecting the same server setup can be found in the examples subdirectory.

Synchronous search

use ldap3::{LdapConn, Scope, SearchEntry};
use ldap3::result::Result;

fn main() -> Result<()> {
    let mut ldap = LdapConn::new("ldap://localhost:2389")?;
    let (rs, _res) = ldap.search(
        "ou=Places,dc=example,dc=org",
        Scope::Subtree,
        "(&(objectClass=locality)(l=ma*))",
        vec!["l"]
    )?.success()?;
    for entry in rs {
        println!("{:?}", SearchEntry::construct(entry));
    }
    Ok(ldap.unbind()?)
}

Asynchronous search

use ldap3::{LdapConnAsync, Scope, SearchEntry};
use ldap3::result::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let (conn, mut ldap) = LdapConnAsync::new("ldap://localhost:2389").await?;
    ldap3::drive!(conn);
    let (rs, _res) = ldap.search(
        "ou=Places,dc=example,dc=org",
        Scope::Subtree,
        "(&(objectClass=locality)(l=ma*))",
        vec!["l"]
    ).await?.success()?;
    for entry in rs {
        println!("{:?}", SearchEntry::construct(entry));
    }
    Ok(ldap.unbind().await?)
}

Compile-time features

The following features are available at compile time:

  • sync (enabled by default): Synchronous API support.

  • tls (enabled by default): TLS support, backed by the native-tls crate, which uses a platform-specific TLS backend. This is an alias for tls-native.

  • tls-rustls (disabled by default): TLS support, backed by the Rustls library.

Without any features, only plain TCP connections (and Unix domain sockets on Unix-like platforms) are available. For TLS support, tls and tls-rustls are mutually exclusive: choosing both will produce a compile-time error.

License

Licensed under either of:

at your option.

Comments
  • unbind fails when using ldaps or starttls?

    unbind fails when using ldaps or starttls?

    I don't know if it's a bug or i am doing something wrong, but whenever i try to use ldaps or starttls it fails when i unbind.

    I seem to bind and even search just fine, but unbind will throw an error: ResultRecv { source: RecvError(()) } It seems to somtimes suceed once or twice for some reason but it's rare.

    Is someone able to test this?

    opened by Zerowalker 36
  • Leak when establishing connection

    Leak when establishing connection

    I'm using this crate in a project that should be 100% up (a Windows service).

    Connections are established periodically to perform some actions.

    The way the project is architectured, a new (sync) connection is established every time an ldap operation is required, something like the following:

    let ldap_conn = self.bind(self.connect()?, parameters)?;
    ldap_conn.search(
            &query,
            ldap3::Scope::SubTree,
            &filter,
            attributes,
        )?;
    

    Then ldap_conn is going out of scope (and should thus be dropped).

    Using macOS's Instruments and cargo-instrumentsI ran a memory leak (with cargo instruments --limit 60000 --template Leaks --open).

    I've placed generation markers at the period of ldap connections creation and isolated the following backtrace:

       0 libsystem_pthread.dylib _pthread_create
       1 project std::sys::unix::thread::Thread::new::h592cab8fcd31da40 /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/sys/unix/thread.rs:68
       2 project std::thread::Builder::spawn_unchecked::h134d2da1c8074b18 /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/thread/mod.rs:492
       3 project std::thread::Builder::spawn::h48a9f7495ba6efa0 /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/thread/mod.rs:386
       4 project tokio_reactor::background::Background::new::haf2838ed3d299015 /Users/nbigaouette/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-reactor-0.1.12/src/background.rs:75
       5 project tokio_reactor::Reactor::background::had1d3836f2167508 /Users/nbigaouette/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-reactor-0.1.12/src/lib.rs:364
       6 project tokio::runtime::threadpool::Runtime::reactor::hda8d03c31b7688f0 /Users/nbigaouette/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.22/src/runtime/threadpool/mod.rs:177
       7 project tokio_core::reactor::Core::remote::h9f1aae8ac9d65989 /Users/nbigaouette/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-core-0.1.17/src/reactor/mod.rs:186
       8 project tokio_core::reactor::Core::handle::h615c39a2723d4480 /Users/nbigaouette/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-core-0.1.17/src/reactor/mod.rs:167
       9 project ldap3::conn::LdapConn::with_settings::h7f914ba203306389 /Users/nbigaouette/.cargo/registry/src/github.com-1ecc6299db9ec823/ldap3-0.6.1/src/conn.rs:210
      10 project project::LdapClient::connect::hd7f7be3a60f42fbd /Users/nbigaouette/project/src/main.rs:163
      11 project _$LT$project..LdapClient$u20$as$u20$project..LdapAccess$GT$::execute::h7196beeb29a23d8d /Users/nbigaouette/project/src/main.rs:96
      18 project std::sys_common::backtrace::__rust_begin_short_backtrace::h0bc3e9aae8c075fa /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/sys_common/backtrace.rs:129
      19 project std::thread::Builder::spawn_unchecked::_$u7b$$u7b$closure$u7d$$u7d$::_$u7b$$u7b$closure$u7d$$u7d$::h844632ca31721a5d /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/thread/mod.rs:475
      20 project _$LT$std..panic..AssertUnwindSafe$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$$LP$$RP$$GT$$GT$::call_once::h46dde68d861876bc /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/panic.rs:318
      21 project std::panicking::try::do_call::hfcc6c29e84830a4d /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/panicking.rs:305
      22 project __rust_maybe_catch_panic /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libpanic_unwind/lib.rs:86
      23 project std::panicking::try::h016f17f065cf77aa /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/panicking.rs:281
      24 project std::panic::catch_unwind::hc7943b0fdacd3bc1 /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/panic.rs:394
      25 project std::thread::Builder::spawn_unchecked::_$u7b$$u7b$closure$u7d$$u7d$::h5291ad7674923a6d /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/thread/mod.rs:474
      26 project core::ops::function::FnOnce::call_once$u7b$$u7b$vtable.shim$u7d$$u7d$::hc532a0fb892ac35e /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libcore/ops/function.rs:232
      27 project _$LT$alloc..boxed..Box$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$::call_once::h606f2dced48ff3c4 /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/liballoc/boxed.rs:1015
      28 project _$LT$alloc..boxed..Box$LT$F$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$::call_once::h0ece8cce658c93ae /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/liballoc/boxed.rs:1015
      29 project std::sys_common::thread::start_thread::h30bbd9f3fb79e88c /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/sys_common/thread.rs:13
      30 project std::sys::unix::thread::Thread::new::thread_start::h7199626a1bd56873 /rustc/b8cedc00407a4c56a3bda1ed605c6fc166655447/src/libstd/sys/unix/thread.rs:80
      31 libsystem_pthread.dylib _pthread_start
      32 libsystem_pthread.dylib thread_start
    

    (Note that I have redacted some lines).

    Using macOS's Activity Monitor I can see the file descriptiors number increase with each connection.

    I'm not sure where to dig further. Is there something I am not doing properly when establishing my connections? Should I disconnect() something? Should tokio be shutdown manually?

    Some version information:

    • ldap3: 0.6.1
    • tokio-core 0.1.17
    • tokio 0.1.22

    I haven't tried 0.7.0-alpha.7 yet as the API is not compatible.

    opened by nbigaouette 25
  • Intermediate responses

    Intermediate responses

    Hi, I'm trying to make synchronization work using the sync request control of RFC4533 (1.3.6.1.4.1.4203.1.9.1.1).

    So far I have managed to create the custom control and pass it to streaming_search(). As expected, initial results are returned and search does not end because SearchResultDone message is not sent, but I'm missing a way to receive the intermediate Sync Info Message that should be sent at this point, and I don't receive any changed entries upon changes in LDAP data. No error is returned. The stream seems to be stuck.

    Am I missing something?

    (I'm willing to help with this, and possibly with making more controls/ASN.1 stuff)

    enhancement 
    opened by hlavaatch 14
  • panicked at 'message id', src/protocol.rs:85:13

    panicked at 'message id', src/protocol.rs:85:13

    Hi,

    I am using an async connection. I got a panic "message id" returned in protocol.rs.

    I printed the tags right before and I had :

    [StructureTag { class: Universal, id: 2, payload: P([0]) }, StructureTag { class: Application, id: 24, payload: C([StructureTag { class: Universal, id: 10, payload: P([52]) }, StructureTag { class: Universal, id: 4, payload: P([]) }, StructureTag { class: Universal, id: 4, payload: P([48, 48, 48, 48, 48, 48, 48, 51, 58, 32, 76, 100, 97, 112, 69, 114, 114, 58, 32, 68, 83, 73, 68, 45, 48, 67, 48, 54, 48, 54, 49, 65, 44, 32, 99, 111, 109, 109, 101, 110, 116, 58, 32, 69, 114, 114, 111, 114, 32, 100, 101, 99, 114, 121, 112, 116, 105, 110, 103, 32, 108, 100, 97, 112, 32, 109, 101, 115, 115, 97, 103, 101, 44, 32, 100, 97, 116, 97, 32, 48, 44, 32, 118, 52, 53, 54, 51, 0]) }]) }]
    

    Any idea ? I'm still trying to figure it out, but if you have an idea, let me know.

    Thank for your help

    opened by fdubois1 11
  • cannot recursively call into `Core`

    cannot recursively call into `Core`

    I'm trying to synchronise users from an LDAP server with my application. To do that, I added something like this

    let user_sync_task = Interval::new_interval(config.ldap.sync_interval).for_each(move |_| {
                info!("Starting user synchronization...");
    
                ...SYNCHRONIZATION_CODE...
                Ok(())
            }).into_future().map_err(|e| {
                error!("Error creating user synchronization task {:?}", e);
            });
    
            task_executor.spawn(user_sync_task);
    
    

    Of course, during the synchronisation, I open an LDAP connection. (LdapConn::new(&self.url)?;) But I get this error :

    thread 'tokio-runtime-worker-0' panicked at 'cannot recursively call into Core', src/libcore/option.rs:1036:5

    From what I understand, I can't open an LDAP connection in a future running in a TaskExecutor. Am I right ? How can I fix that on my side, knowing that ldap connection will create a new tokio Core and run it ?

    Thank you for the help

    opened by fdubois1 11
  • Issue with LDAPS

    Issue with LDAPS

    I'm able to simple bind and search using LDAP but LDAPS fails. I tried both nativetls and rustls which fail. I don't have the errors in front of me currently but can get those if needed. But from what I recall I was seeing an error referencing protocol. I also tested by using the openldap crate and LDAPS works as expected using that. The openldap crate examples show passing options for the protocol version so I'm wondering if there's a way to pass the protocol version using this LDAP3 crate?

    opened by cbass27 9
  • "Broken pipe" instead of "TLS authentication error"

    When I replace the certificate with a wrong one, I'd expect to get a "TLS authentication error" or something similar. Instead, I get just "broken pipe", and not on the attempt to connect to the LDAP server, but on the first attempt to communicate with it.

    I'm doing something like this:

        let cert = Certificate::from_der(LDAP_CERT)?;
        
        let mut tls_connector = TlsConnector::builder()?;
        tls_connector.add_root_certificate(cert)?;
        let tls_connector = tls_connector.build()?;
    
        let conn = LdapConnBuilder::<LdapConn>::new()
            .with_tls_connector(tls_connector)
            .connect(&*LDAP_URL)?;
    
        let res = conn.simple_bind(&bind_dm, &login_info.password)?;
    

    It doesn't explicitly fail when connecting, but on the simple_bind. The error looks like this, debug-printed:

    Error { repr: Custom(Custom { kind: BrokenPipe, error: StringError("broken pipe") }) }
    

    I just spent hours of trying to pinpoint where this bug originates from, but without much success – there's layers upon layers of Tokio abstractions :(

    documentation 
    opened by golddranks 9
  • [Question] Either the application has not called WSAStartup, or WSAStartup failed. (os error 10093)

    [Question] Either the application has not called WSAStartup, or WSAStartup failed. (os error 10093)

    Hi,

    A user of my app using ldap3 just send me logs containing:

    LDAP connection error: I/O error: Either the application has not called
    WSAStartup, or WSAStartup failed. (os error 10093)
    

    What does it mean?

    opened by Geobert 8
  • panicked at 'entry', /ldap3/src/search.rs

    panicked at 'entry', /ldap3/src/search.rs

    Hi, I moved to the alpha version to use async/await. I kept my code almost the same except that now, I use LdapConnAsync and all async call.

    I use streaming_search() in my code below and it panics with this : panicked at 'entry', /opt/wayk/dev/ldap3/src/search.rs:148:13

    The line who panic is SearchEntry::construct(entry). Any idea ?

    Here is my code. The connection is already opened, I receive it in parameter.

    async fn get_groups_internal(&self, ldap_conn: &mut Ldap) -> Result<Vec<AccountGroup>, AdAccountError> {
            let mut groups = Vec::new();
    
            let filter = "(&(objectCategory=group)(objectClass=group))";
    
            let mut entry_stream = ldap_conn
                .streaming_search(&self.base_dn, Scope::Subtree, &filter, AD_GROUP_ATTRIBUTES.to_vec())
                .await?;
    
            while let Some(entry) = entry_stream.next().await? {
                match self.build_group(&SearchEntry::construct(entry)) {
                    Ok(group) => groups.push(group),
                    Err(e) => error!("Build_group failed: {:?}", e),
                }
            }
            entry_stream.finish();
    
            Ok(groups)
        }
    

    Thank you in advance

    opened by fdubois1 8
  • Update to a more recent version of nom

    Update to a more recent version of nom

    Hey there!

    We are currently evaluating ldap3 because we are interested in using it in Proxmox Backup Server to enable LDAP login for our users. So far, we are pretty happy with it. As we try to keep the number of duplicated/outdated dependencies to a minimum, we were wondering: Are there currently any plans to update to a more recent version of nom?

    Best Regards, Lukas

    opened by lwagner94 7
  • [RFC] Implement rfc3876 (Matched Values)

    [RFC] Implement rfc3876 (Matched Values)

    Hi there,

    We are in the need of Matched Values Control and it's not available in ldap3.

    RFC https://tools.ietf.org/html/rfc3876.html

    Would you mind if we add it?

    Regards,

    opened by Geobert 7
  • Add support for GSS-SPNEGO bind

    Add support for GSS-SPNEGO bind

    This pull request adds support for the GSS-SPNEGO bind type with NTLM. There is no support for integrated Windows authentication or Kerberos at this point. This has been tested against Windows Active Directory, compared against sample Wireshark captures.

    opened by awakecoding 7
  • Multilegged bind support (GSS-SPNEGO) with saslBindInProgress response

    Multilegged bind support (GSS-SPNEGO) with saslBindInProgress response

    I am currently trying to add support for the GSS-SPNEGO bind type, which would be very useful to connect to Active Directory. I have sample wireshark captures of NTLM and Kerberos traffic, and I also have a working NTLM implementation that I can integrate.

    However, I am facing a blocker: even if I can send the first GSS-SPNEGO bind request and get a proper response from the server (saslBindInProgress with the NTLM "Challenge" message in the response payload), I struggle to find a way to extract the NTLM message from the bind response.

    The current code only has support for single request/response bind types, none of the implemented bind types are multilegged (meaning they require more than one request/response to complete). As far as I can see, bind success is currently only based on the LdapResult response code being zero.

    I figured out how to interpret the result code differently such that code 14 (saslBindInProgress) is not considered an error, but I cannot figure out how to properly modify the LdapResponse type and parsing code to allow me to fetch something other than a vector of controls. A bind response isn't a search result, so it's not a vector of controls :/

    Any guidance or hints on this problem?

    opened by awakecoding 3
Owner
null
LDAP support for the r2d2 connection pool

r2d2-ldap LDAP support for the r2d2 connection pool Install Add this to your Cargo.toml: [dependencies] r2d2-ldap = "0.1.1" Basic Usage use std::threa

Aitor Ruano 2 Nov 7, 2020
Affine-client is a client for AFFINE based on Tauri

Affine Client affine-client is a client for AFFINE based on Tauri Supported Platforms Windows Linux MacOS Download https://github.com/m1911star/affine

Horus 216 Dec 25, 2022
CouchDB client-side library for the Rust programming language

Chill Chill is a client-side CouchDB library for the Rust programming language, available on crates.io. It targets Rust Stable. Chill's three chief de

null 35 Jun 26, 2022
An etcd client library for Rust.

etcd An etcd client library for Rust. etcd on crates.io Documentation for the latest crates.io release Running the tests Install Docker and Docker Com

Jimmy Cuadra 138 Dec 27, 2022
Mysql client library implemented in rust.

mysql This crate offers: MySql database driver in pure rust; connection pool. Features: macOS, Windows and Linux support; TLS support via nativetls cr

Anatoly I 548 Dec 31, 2022
Skytable rust client support library for the bb8 connection pool

bb8-skytable Skytable rust client support library for the bb8 connection pool. Heavily based on bb8-redis Basic usage example use bb8_skytable::{

null 3 Sep 18, 2021
CouchDB client library for the Rust programming language

CouchDB This project is reborn! As of its v0.6.0 release, the couchdb crate has new life as a toolkit instead of providing a full-blown client. In a n

null 20 Jul 17, 2021
OBKV Table Client is Rust Library that can be used to access table data from OceanBase storage layer.

OBKV Table Client is Rust Library that can be used to access table data from OceanBase storage layer. Its access method is different from JDBC, it skips the SQL parsing layer, so it has significant performance advantage.

OceanBase 4 Nov 14, 2022
rustodrive is a rust client library for communicating with ODrives using the CAN protocol.

rustodrive is a WIP client library for communicating with ODrives using the CAN protocol. It is more than a simple CAN sender/receiver and has many co

null 5 Oct 31, 2022
Async Lightweight HTTP client using system native library if possible. (Currently under heavy development)

Async Lightweight HTTP Client (aka ALHC) What if we need async but also lightweight http client without using such a large library like reqwest, isahc

SteveXMH 7 Dec 15, 2022
An async-ready Phoenix Channels v2 client library in Rust

Phoenix Channels This crate implements a Phoenix Channels (v2) client in Rust. Status NOTE: This client is still a work-in-progress, though it has eno

LiveView Native 22 Jan 7, 2023
A simple, workable RCS client library.

rust-rcs-client A simple, workable RCS client library. RCS capabilities are mainly provided by your cellular network. This is a working client side im

EverfrosT 3 Jul 6, 2023
Cassandra DB native client written in Rust language. Find 1.x versions on https://github.com/AlexPikalov/cdrs/tree/v.1.x Looking for an async version? - Check WIP https://github.com/AlexPikalov/cdrs-async

CDRS CDRS is looking for maintainers CDRS is Apache Cassandra driver written in pure Rust. ?? Looking for an async version? async-std https://github.c

Alex Pikalov 338 Jan 1, 2023
A Rust client for the ElasticSearch REST API

rs-es Introduction An ElasticSearch client for Rust via the REST API. Targetting ElasticSearch 2.0 and higher. Other clients For later versions of Ela

Ben Ashford 218 Dec 27, 2022
An Elasticsearch REST API client for Rust

elastic elastic is an efficient, modular API client for Elasticsearch written in Rust. The API is targeting the Elastic Stack 7.x. elastic provides st

null 249 Oct 18, 2022
A easy-use client to influxdb

InfluxDBClient-rs A easy-use client to influxdb Overview This is an InfluxDB driver for Rust. Status This project has been able to run properly, PR is

漂流 75 Jul 22, 2022
Telegram bot API client for Rust

Frankenstein Telegram bot API client for Rust. It's a complete wrapper for Telegram bot API and it's up to date with version 5.2 of the API. Frankenst

Ayrat Badykov 136 Jan 1, 2023
Streaming STOMP client for Rust

tokio-stomp An async STOMP client (and maybe eventually, server) for Rust, using the Tokio stack. It aims to be fast and fully-featured with a simple

null 7 Jun 15, 2022
Official Skytable client driver for Rust

Skytable client Introduction This library is the official client for the free and open-source NoSQL database Skytable. First, go ahead and install Sky

Skytable 29 Nov 24, 2022