Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker based examples #8

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "openldap"
version = "1.2.2"
authors = ["Josh Leverette <[email protected]>", "Ross Delinger <[email protected]>", "Stephen Holsapple <[email protected]>", "Yong Wen Chua <[email protected]>"]
authors = ["Josh Leverette <[email protected]>", "Ross Delinger <[email protected]>", "Stephen Holsapple <[email protected]>", "Yong Wen Chua <[email protected]>", "Mathias Myrland <[email protected]>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/coder543/rust-cldap"
Expand All @@ -11,3 +11,8 @@ description = "Straightforward Rust bindings to the C openldap library. This is

[dependencies]
libc = "0.2.10"

[workspace]
members = [
"examples/simple_bind_authentication"
]
11 changes: 11 additions & 0 deletions examples/simple_bind_authentication/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "simple_bind"
version = "0.1.0"
authors = ["Mathias Myrland <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = {git="https://github.com/clap-rs/clap.git"}
openldap = { path = "../../" }
33 changes: 33 additions & 0 deletions examples/simple_bind_authentication/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Simple Bind with start_tls

This example shows how to use simple_bind in combination with start_tls
and a manager account to do a typical user authentication lookup.

Start TLS is the recommended way to do secure LDAP; ldaps:// on port 636 is deprecated.

## Running

Start the example docker using the start_example_server.sh script from
the examples directory. Then, from the simple_bind directory, do

```shell script
cargo run -- -u fry -p fry
```

## Steps that are being performed

The first step is to set up the LDAPRust instance, and perform start_tls on it.
This ensures that our communication is encrypted. Note that we are not verifying
the server certificate in this example; this is something you should do in production.

The next step is to simple_bind using our manger accounts DN and password. This
will allow us to perform an ldap_search later on.

Now, we take the incoming user name string, and perform an ldap_search for it.
Note how we are matching either email or username. Our search yields the DN
for the provided credentials.

The last step is to attempt a simple bind with the discovered DN and provided
user password. If all goes well, we are authenticated, otherwise, something
went wrong.

139 changes: 139 additions & 0 deletions examples/simple_bind_authentication/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#[macro_use]
extern crate clap;

extern crate openldap;

use openldap::errors::*;
use openldap::*;
use std::ptr;

#[derive(Clap)]
#[clap(
name = "LDAP simple_bind_authentication with start_tls authentication example",
author = "Mathias Myrland <[email protected]>",
version = "0.1.0"
)]
struct AuthOpts {
#[clap(short = "u")]
user: String,

#[clap(short = "p")]
password: String,
}

fn ldap_with_start_tls(ldap_uri: &str) -> Result<RustLDAP, LDAPError> {
let ldap = RustLDAP::new(ldap_uri).unwrap();

ldap.set_option(
codes::options::LDAP_OPT_PROTOCOL_VERSION,
&codes::versions::LDAP_VERSION3,
);

// WARNING: Normally you would want to verify the server certificate to avoid
// man in the middle attacks, but for this testing scenario we're using a
// generated self signed certificate from the docker container.
//
// To set up certificate validation, use the LDAP_OPT_X_TLS_CACERT* options
ldap.set_option(
codes::options::LDAP_OPT_X_TLS_REQUIRE_CERT,
&codes::options::LDAP_OPT_X_TLS_NEVER,
);

ldap.set_option(openldap::codes::options::LDAP_OPT_X_TLS_NEWCTX, &0);

ldap.start_tls(None, None)?;

Ok(ldap)
}

fn do_simple_bind(
ldap: &RustLDAP,
ldap_manager_user: &str,
ldap_manager_pass: &str,
) -> Result<(), LDAPError> {
let bind_result = ldap.simple_bind(ldap_manager_user, ldap_manager_pass)?;

match bind_result {
v if v == openldap::codes::results::LDAP_SUCCESS => Ok(()),
_ => Err(LDAPError::from(String::from(
"Authentication with simple bind failed",
))),
}
}

fn ldap_dn_lookup(ldap: &RustLDAP, who: &str) -> Result<String, LDAPError> {
// First, escape the who parameter to prevent LDAP injection attacks
let safe_who = escape_filter_assertion_value(who)?;

// Show all DNs matching the description "Human"
// ldap_search is a powerful query language, look at
// https://confluence.atlassian.com/kb/how-to-write-ldap-search-filters-792496933.html
// for an overview
//
// This particular filter allows the user to sign in with either
// uid or email
let filter = format!("(|(uid={})(mail={}))", safe_who, safe_who);

match ldap.ldap_search(
"ou=people,dc=planetexpress,dc=com",
codes::scopes::LDAP_SCOPE_SUBTREE,
Some(filter.as_str()),
Some(vec!["dn"]),
true,
None,
None,
ptr::null_mut(),
-1,
) {
Ok(search_results) => {
for result_map in search_results {
for result_tuple in result_map {
println!("Found result map with key {}", result_tuple.0);
for result_data in result_tuple.1 {
println!("\t {}", result_data);
return Ok(result_data);
}
}
}

Err(LDAPError::from(String::from(
"Authentication with simple bind failed",
)))
}
_ => Err(LDAPError::from(String::from(
"Authentication with simple bind failed",
))),
}
}

fn main() {
let options = AuthOpts::parse();
let user_to_authenticate = options.user;
let pwd_to_authenticate = options.password;

let ldap_uri = "ldap://localhost:389";
let ldap_manager_dn = "cn=Hubert J. Farnsworth,ou=people,dc=planetexpress,dc=com";
let ldap_manager_pass = "professor";

let ldap = ldap_with_start_tls(ldap_uri).unwrap();

// Bind to the LDAP server with the manager account,
// this is done to perform a search for the DN to
// use when authenticating the user attempting to
// sign in. Obviously, the manager credentials should
// be kept secret, and not be put under version control.
// In our test scenario, the professor is the manager.
do_simple_bind(&ldap, ldap_manager_dn, ldap_manager_pass).unwrap();

let (dn, passwd, valid) = match ldap_dn_lookup(&ldap, user_to_authenticate.as_str()) {
Ok(dn) => (dn, pwd_to_authenticate, true),
_ => ("".into(), "".into(), false),
};

// We do the simple bind regardless of the user existence, to protect against timing attacks
// to probe existing users
match do_simple_bind(&ldap, dn.as_str(), passwd.as_str()).is_ok() && valid {
true => println!("Successfully signed in as {}", dn),
false => println!("Could not log in"),
}
}
3 changes: 3 additions & 0 deletions examples/start_example_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#! /usr/bin/env bash

docker run -p 389:389 -p 636:636 rroemhild/test-openldap
7 changes: 3 additions & 4 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//! Errors and trait implementations.
//!
use std::fmt;
use std::error;
use std::convert;

use std::error;
use std::fmt;

/// A LDAP error.
///
Expand Down Expand Up @@ -40,7 +39,7 @@ impl error::Error for LDAPError {
///
/// Note, currently this method always return `None` as we do not know the root cause of the
/// error.
fn cause(&self) -> Option<&error::Error> {
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}
Expand Down
Loading