1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//! rustlavel-ldap: authenticate a person against a directory.
//!
//! LDAP is how most organisations answer "who is this and what is their
//! password", and an application that needs that answer should not need to
//! learn ASN.1 to get it. So the package has two faces: [`directory`], which is
//! one call, and [`protocol`] and [`ber`] underneath it for anything that call
//! does not cover.
//!
//! ```ignore
//! use rustlavel_ldap::prelude::*;
//!
//! let directory = Directory::new("ldaps://dc.example.test")?
//! .service_account("cn=svc,dc=example,dc=test", &service_password)
//! .base_dn("ou=people,dc=example,dc=test")
//! .username_attribute("uid")
//! .filter(Filter::equals("objectClass", "inetOrgPerson"))
//! .attributes(["cn", "mail"]);
//!
//! let user = directory.authenticate(&username, &password).await?;
//! println!("{} is {}", user.dn(), user.value("cn").unwrap_or("unnamed"));
//! ```
//!
//! # The protocol is written here
//!
//! LDAP v3 (RFC 4511) is ASN.1 BER over TCP. BER is a serialisation format
//! rather than cryptography, so rule one applies and it is written in [`ber`]:
//! a small, strict subset that refuses everything RFC 4511 says a directory may
//! not send. TLS is the exception, and comes from rustls with the `aws_lc_rs`
//! provider and `prefer-post-quantum`, like every other TLS in this framework.
//!
//! # Three things this package is firm about
//!
//! **A simple bind sends the password in the clear.** That is what a simple
//! bind *is* — RFC 4511 §4.2 puts the password in an octet string with nothing
//! around it. So one is refused over an unencrypted connection unless the
//! caller has said, in as many words, that it is acceptable
//! ([`LdapConfig::allow_plaintext_password`]). This is the most common LDAP
//! deployment mistake there is, and it is invisible from the outside because
//! everything works.
//!
//! **An empty password must not authenticate.** LDAP treats a simple bind with
//! a zero-length password as an *anonymous* bind, which succeeds — so a login
//! form with a blank password field would report a successful login for any
//! username that exists. This has been a real vulnerability in real products
//! more than once. It is refused in [`Operation::simple_bind`], where the bytes
//! are built, and again in [`Directory::authenticate`] before any I/O happens.
//!
//! **A username is a value, not text.** The classic LDAP injection is a filter
//! template with a name pasted into it: a username of `*` turns a lookup into a
//! wildcard that matches every account. [`Filter`] is a type, and
//! [`Filter::encode`] writes each value as a length-delimited BER octet string
//! where `*` and `)` have no syntax at all. [`escape_filter_value`] exists for
//! the cases that genuinely need the RFC 4515 string form.
pub use ;
pub use ;
pub use ;
pub use ;
/// What an application importing this package normally wants.