mirror of
				https://github.com/nitnelave/lldap.git
				synced 2023-04-12 14:25:13 +00:00 
			
		
		
		
	server: Implement LDAPS support
This commit is contained in:
		
							parent
							
								
									62ff5523c2
								
							
						
					
					
						commit
						42884d3159
					
				
							
								
								
									
										6
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -1917,6 +1917,7 @@ dependencies = [
 | 
			
		||||
 "lldap_auth",
 | 
			
		||||
 "log",
 | 
			
		||||
 "mockall",
 | 
			
		||||
 "native-tls",
 | 
			
		||||
 "opaque-ke",
 | 
			
		||||
 "openssl-sys",
 | 
			
		||||
 "orion",
 | 
			
		||||
@ -1931,6 +1932,7 @@ dependencies = [
 | 
			
		||||
 "thiserror",
 | 
			
		||||
 "time 0.2.27",
 | 
			
		||||
 "tokio",
 | 
			
		||||
 "tokio-native-tls",
 | 
			
		||||
 "tokio-stream",
 | 
			
		||||
 "tokio-util",
 | 
			
		||||
 "tracing",
 | 
			
		||||
@ -2150,9 +2152,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "native-tls"
 | 
			
		||||
version = "0.2.8"
 | 
			
		||||
version = "0.2.10"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
 | 
			
		||||
checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "libc",
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ ldap3_server = ">=0.1.9"
 | 
			
		||||
lldap_auth = { path = "../auth" }
 | 
			
		||||
log = "*"
 | 
			
		||||
orion = "0.16"
 | 
			
		||||
native-tls = "0.2.10"
 | 
			
		||||
serde = "*"
 | 
			
		||||
serde_json = "1"
 | 
			
		||||
sha2 = "0.9"
 | 
			
		||||
@ -37,6 +38,7 @@ sqlx-core = "=0.5.1"
 | 
			
		||||
thiserror = "*"
 | 
			
		||||
time = "0.2"
 | 
			
		||||
tokio = { version = "1.2.0", features = ["full"] }
 | 
			
		||||
tokio-native-tls = "0.3"
 | 
			
		||||
tokio-util = "0.6.3"
 | 
			
		||||
tokio-stream = "*"
 | 
			
		||||
tracing = "*"
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    domain::{
 | 
			
		||||
        handler::{BackendHandler, LoginHandler},
 | 
			
		||||
        handler::{BackendHandler, LoginHandler, UserId},
 | 
			
		||||
        opaque_handler::OpaqueHandler,
 | 
			
		||||
    },
 | 
			
		||||
    infra::{configuration::Configuration, ldap_handler::LdapHandler},
 | 
			
		||||
@ -9,19 +9,21 @@ use actix_rt::net::TcpStream;
 | 
			
		||||
use actix_server::ServerBuilder;
 | 
			
		||||
use actix_service::{fn_service, ServiceFactoryExt};
 | 
			
		||||
use anyhow::{Context, Result};
 | 
			
		||||
use futures_util::future::ok;
 | 
			
		||||
use ldap3_server::{proto::LdapMsg, LdapCodec};
 | 
			
		||||
use log::*;
 | 
			
		||||
use tokio::net::tcp::WriteHalf;
 | 
			
		||||
use native_tls::{Identity, TlsAcceptor};
 | 
			
		||||
use tokio_native_tls::TlsAcceptor as NativeTlsAcceptor;
 | 
			
		||||
use tokio_util::codec::{FramedRead, FramedWrite};
 | 
			
		||||
 | 
			
		||||
async fn handle_incoming_message<Backend>(
 | 
			
		||||
async fn handle_incoming_message<Backend, Writer>(
 | 
			
		||||
    msg: Result<LdapMsg, std::io::Error>,
 | 
			
		||||
    resp: &mut FramedWrite<WriteHalf<'_>, LdapCodec>,
 | 
			
		||||
    resp: &mut Writer,
 | 
			
		||||
    session: &mut LdapHandler<Backend>,
 | 
			
		||||
) -> Result<bool>
 | 
			
		||||
where
 | 
			
		||||
    Backend: BackendHandler + LoginHandler + OpaqueHandler,
 | 
			
		||||
    Writer: futures_util::Sink<LdapMsg> + Unpin,
 | 
			
		||||
    <Writer as futures_util::Sink<LdapMsg>>::Error: std::error::Error + Send + Sync + 'static,
 | 
			
		||||
{
 | 
			
		||||
    use futures_util::SinkExt;
 | 
			
		||||
    let msg = msg.context("while receiving LDAP op")?;
 | 
			
		||||
@ -51,6 +53,56 @@ where
 | 
			
		||||
    Ok(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_file_as_byte_vec(filename: &str) -> Result<Vec<u8>> {
 | 
			
		||||
    (|| -> Result<Vec<u8>> {
 | 
			
		||||
        use std::fs::{metadata, File};
 | 
			
		||||
        use std::io::Read;
 | 
			
		||||
        let mut f = File::open(&filename).context("file not found")?;
 | 
			
		||||
        let metadata = metadata(&filename).context("unable to read metadata")?;
 | 
			
		||||
        let mut buffer = vec![0; metadata.len() as usize];
 | 
			
		||||
        f.read(&mut buffer).context("buffer overflow")?;
 | 
			
		||||
        Ok(buffer)
 | 
			
		||||
    })()
 | 
			
		||||
    .context(format!("while reading file {}", filename))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn handle_ldap_stream<Stream, Backend>(
 | 
			
		||||
    stream: Stream,
 | 
			
		||||
    backend_handler: Backend,
 | 
			
		||||
    ldap_base_dn: String,
 | 
			
		||||
    ldap_user_dn: UserId,
 | 
			
		||||
) -> Result<Stream>
 | 
			
		||||
where
 | 
			
		||||
    Backend: BackendHandler + LoginHandler + OpaqueHandler + 'static,
 | 
			
		||||
    Stream: tokio::io::AsyncRead + tokio::io::AsyncWrite,
 | 
			
		||||
{
 | 
			
		||||
    use tokio_stream::StreamExt;
 | 
			
		||||
    let (r, w) = tokio::io::split(stream);
 | 
			
		||||
    // Configure the codec etc.
 | 
			
		||||
    let mut requests = FramedRead::new(r, LdapCodec);
 | 
			
		||||
    let mut resp = FramedWrite::new(w, LdapCodec);
 | 
			
		||||
 | 
			
		||||
    let mut session = LdapHandler::new(backend_handler, ldap_base_dn, ldap_user_dn);
 | 
			
		||||
 | 
			
		||||
    while let Some(msg) = requests.next().await {
 | 
			
		||||
        if !handle_incoming_message(msg, &mut resp, &mut session)
 | 
			
		||||
            .await
 | 
			
		||||
            .context("while handling incoming messages")?
 | 
			
		||||
        {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Ok(requests.into_inner().unsplit(resp.into_inner()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_tls_acceptor(config: &Configuration) -> Result<NativeTlsAcceptor> {
 | 
			
		||||
    // Load TLS key and cert files
 | 
			
		||||
    let cert_file = get_file_as_byte_vec(&config.ldaps_options.cert_file)?;
 | 
			
		||||
    let key_file = get_file_as_byte_vec(&config.ldaps_options.key_file)?;
 | 
			
		||||
    let identity = Identity::from_pkcs8(&cert_file, &key_file)?;
 | 
			
		||||
    Ok(TlsAcceptor::new(identity)?.into())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn build_ldap_server<Backend>(
 | 
			
		||||
    config: &Configuration,
 | 
			
		||||
    backend_handler: Backend,
 | 
			
		||||
@ -59,44 +111,51 @@ pub fn build_ldap_server<Backend>(
 | 
			
		||||
where
 | 
			
		||||
    Backend: BackendHandler + LoginHandler + OpaqueHandler + 'static,
 | 
			
		||||
{
 | 
			
		||||
    use futures_util::StreamExt;
 | 
			
		||||
    let context = (
 | 
			
		||||
        backend_handler,
 | 
			
		||||
        config.ldap_base_dn.clone(),
 | 
			
		||||
        config.ldap_user_dn.clone(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let ldap_base_dn = config.ldap_base_dn.clone();
 | 
			
		||||
    let ldap_user_dn = config.ldap_user_dn.clone();
 | 
			
		||||
    server_builder
 | 
			
		||||
        .bind("ldap", ("0.0.0.0", config.ldap_port), move || {
 | 
			
		||||
            let backend_handler = backend_handler.clone();
 | 
			
		||||
            let ldap_base_dn = ldap_base_dn.clone();
 | 
			
		||||
            let ldap_user_dn = ldap_user_dn.clone();
 | 
			
		||||
            fn_service(move |mut stream: TcpStream| {
 | 
			
		||||
                let backend_handler = backend_handler.clone();
 | 
			
		||||
                let ldap_base_dn = ldap_base_dn.clone();
 | 
			
		||||
                let ldap_user_dn = ldap_user_dn.clone();
 | 
			
		||||
                async move {
 | 
			
		||||
                    // Configure the codec etc.
 | 
			
		||||
                    let (r, w) = stream.split();
 | 
			
		||||
                    let mut requests = FramedRead::new(r, LdapCodec);
 | 
			
		||||
                    let mut resp = FramedWrite::new(w, LdapCodec);
 | 
			
		||||
    let tls_context = (
 | 
			
		||||
        context.clone(),
 | 
			
		||||
        get_tls_acceptor(config).context("while setting up the SSL certificate")?,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
                    let mut session = LdapHandler::new(backend_handler, ldap_base_dn, ldap_user_dn);
 | 
			
		||||
 | 
			
		||||
                    while let Some(msg) = requests.next().await {
 | 
			
		||||
                        if !handle_incoming_message(msg, &mut resp, &mut session)
 | 
			
		||||
                            .await
 | 
			
		||||
                            .context("while handling incoming messages")?
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Ok(stream)
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .map_err(|err: anyhow::Error| error!("Service Error: {:#}", err))
 | 
			
		||||
            .and_then(move |_| {
 | 
			
		||||
                // finally
 | 
			
		||||
                ok(())
 | 
			
		||||
            })
 | 
			
		||||
    let binder = move || {
 | 
			
		||||
        let context = context.clone();
 | 
			
		||||
        fn_service(move |stream: TcpStream| {
 | 
			
		||||
            let context = context.clone();
 | 
			
		||||
            async move {
 | 
			
		||||
                let (handler, base_dn, user_dn) = context;
 | 
			
		||||
                handle_ldap_stream(stream, handler, base_dn, user_dn).await
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .with_context(|| format!("while binding to the port {}", config.ldap_port))
 | 
			
		||||
        .map_err(|err: anyhow::Error| error!("[LDAP] Service Error: {:#}", err))
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let tls_binder = move || {
 | 
			
		||||
        let tls_context = tls_context.clone();
 | 
			
		||||
        fn_service(move |stream: TcpStream| {
 | 
			
		||||
            let tls_context = tls_context.clone();
 | 
			
		||||
            async move {
 | 
			
		||||
                let ((handler, base_dn, user_dn), tls_acceptor) = tls_context;
 | 
			
		||||
                let tls_stream = tls_acceptor.clone().accept(stream).await?;
 | 
			
		||||
                handle_ldap_stream(tls_stream, handler, base_dn, user_dn).await
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        .map_err(|err: anyhow::Error| error!("[LDAPS] Service Error: {:#}", err))
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let server_builder = server_builder
 | 
			
		||||
        .bind("ldap", ("0.0.0.0", config.ldap_port), binder)
 | 
			
		||||
        .with_context(|| format!("while binding to the port {}", config.ldap_port));
 | 
			
		||||
    if config.ldaps_options.enabled {
 | 
			
		||||
        server_builder.and_then(|s| {
 | 
			
		||||
            s.bind("ldaps", ("0.0.0.0", config.ldaps_options.port), tls_binder)
 | 
			
		||||
                .with_context(|| format!("while binding to the port {}", config.ldaps_options.port))
 | 
			
		||||
        })
 | 
			
		||||
    } else {
 | 
			
		||||
        server_builder
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user