126 lines
3.8 KiB
Rust
126 lines
3.8 KiB
Rust
use ldap3_proto::LdapMsg;
|
|
use ldap3_proto::proto::LdapOp;
|
|
|
|
use crate::db::{Database, DatabaseInterface};
|
|
use crate::error::GlobalError;
|
|
use crate::ldap::{
|
|
LdapClientState, LdapStream, LdapStreamError, op_bind, op_ext, search_by_mail_filter,
|
|
};
|
|
use crate::listener::Listener;
|
|
|
|
pub async fn ldap_listen<D: DatabaseInterface>(listener: Listener, db: Database<D>) {
|
|
// If the connection is None, it's because the client aborted early
|
|
// so there's nothing to do about it.
|
|
loop {
|
|
match listener.accept_ldap().await {
|
|
Ok(Some(stream)) => {
|
|
let db = db.clone();
|
|
tokio::spawn(ldap_handler(stream, db));
|
|
}
|
|
Ok(None) => {
|
|
panic!("LDAP listener closed");
|
|
}
|
|
Err(e) => {
|
|
panic!("Failed to listen on LDAP listener");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tracing::instrument(name = "ldap", skip(stream, db), fields(session = %stream.session))]
|
|
pub async fn ldap_handler<D: DatabaseInterface>(mut stream: LdapStream, mut db: Database<D>) {
|
|
tracing::info! {
|
|
remote_addr = ?stream.remote_addr,
|
|
"New client connection"
|
|
};
|
|
|
|
let mut state = LdapClientState::new();
|
|
|
|
loop {
|
|
match stream.next().await {
|
|
Ok(msg) => match ldap_handler_inner(&mut stream, msg, &mut state, &mut db).await {
|
|
Ok(should_keep_alive) => {
|
|
if !should_keep_alive {
|
|
tracing::debug!("Finished connection");
|
|
return;
|
|
}
|
|
}
|
|
Err(e) => {
|
|
tracing::debug!(
|
|
reason = ?e,
|
|
"Failed to respond"
|
|
);
|
|
return;
|
|
}
|
|
},
|
|
Err(e) => {
|
|
tracing::debug!(
|
|
reason = ?e,
|
|
"Closing connection"
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Return true to keep the connection going, false to close it.
|
|
#[tracing::instrument(name = "ldap-handler", skip(client_state, db, stream))]
|
|
pub async fn ldap_handler_inner<D: DatabaseInterface>(
|
|
stream: &mut LdapStream,
|
|
msg: LdapMsg,
|
|
client_state: &mut LdapClientState,
|
|
db: &mut Database<D>,
|
|
) -> Result<bool, LdapStreamError> {
|
|
tracing::debug!(msg = ?msg, "Received LDAP message");
|
|
match msg {
|
|
// Disconnect
|
|
LdapMsg {
|
|
msgid: _,
|
|
op: LdapOp::UnbindRequest,
|
|
ctrl: _,
|
|
} => {
|
|
client_state.unbind();
|
|
// TODO: keep the connection open?
|
|
Ok(true)
|
|
}
|
|
LdapMsg {
|
|
msgid,
|
|
op: LdapOp::ExtendedRequest(ler),
|
|
ctrl: _,
|
|
} => {
|
|
op_ext(stream, ler, msgid, client_state).await?;
|
|
Ok(true)
|
|
}
|
|
LdapMsg {
|
|
msgid,
|
|
op: LdapOp::BindRequest(lbr),
|
|
ctrl: _,
|
|
} => {
|
|
if let Some(bound_dn) = op_bind(stream, db, lbr, msgid).await? {
|
|
tracing::debug!("Successful bind");
|
|
client_state.bind(bound_dn);
|
|
Ok(true)
|
|
} else {
|
|
client_state.unbind();
|
|
tracing::debug!("Failed bind or anonymous bind");
|
|
// We keep the connection open in case it's an anonymous bind
|
|
Ok(true)
|
|
}
|
|
}
|
|
LdapMsg {
|
|
msgid,
|
|
op: LdapOp::SearchRequest(sr),
|
|
// TODO: ctrl for pagination
|
|
ctrl: _,
|
|
} => {
|
|
search_by_mail_filter(stream, db, sr, msgid).await?;
|
|
Ok(true)
|
|
}
|
|
// Unsupported message
|
|
_ => {
|
|
tracing::warn!("Unsupported client message, closing connection");
|
|
Ok(false)
|
|
}
|
|
}
|
|
}
|