server: Prevent passwords and secrets from being printed

This commit is contained in:
Valentin Tolmer 2021-11-11 10:36:42 +01:00 committed by nitnelave
parent 617a0f53fa
commit 9124339b96
8 changed files with 41 additions and 19 deletions

11
Cargo.lock generated
View File

@ -1803,6 +1803,7 @@ dependencies = [
"orion",
"rand 0.8.4",
"sea-query",
"secstr",
"serde",
"serde_json",
"sha2",
@ -2757,6 +2758,16 @@ dependencies = [
"syn",
]
[[package]]
name = "secstr"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cce2c726741c320e5b8f1edd9a21b3c2c292ae94514afd001d41d81ba143dafc"
dependencies = [
"libc",
"serde",
]
[[package]]
name = "security-framework"
version = "2.4.2"

View File

@ -83,5 +83,9 @@ features = ["with-chrono"]
features = ["env", "toml"]
version = "*"
[dependencies.secstr]
features = ["serde"]
version = "*"
[dev-dependencies]
mockall = "0.9.1"

View File

@ -437,7 +437,7 @@ mod tests {
let sql_pool = get_in_memory_db().await;
let config = ConfigurationBuilder::default()
.ldap_user_dn("admin".to_string())
.ldap_user_pass("test".to_string())
.ldap_user_pass(secstr::SecUtf8::from("test"))
.build()
.unwrap();
let handler = SqlBackendHandler::new(config, sql_pool);

View File

@ -9,6 +9,7 @@ use async_trait::async_trait;
use lldap_auth::opaque;
use log::*;
use sea_query::{Expr, Iden, Query};
use secstr::SecUtf8;
use sqlx::Row;
type SqlOpaqueHandler = SqlBackendHandler;
@ -83,7 +84,7 @@ impl SqlBackendHandler {
impl LoginHandler for SqlBackendHandler {
async fn bind(&self, request: BindRequest) -> Result<()> {
if request.name == self.config.ldap_user_dn {
if request.password == self.config.ldap_user_pass {
if SecUtf8::from(request.password) == self.config.ldap_user_pass {
return Ok(());
} else {
debug!(r#"Invalid password for LDAP bind user"#);
@ -220,11 +221,12 @@ impl OpaqueHandler for SqlOpaqueHandler {
pub(crate) async fn register_password(
opaque_handler: &SqlOpaqueHandler,
username: &str,
password: &str,
password: &SecUtf8,
) -> Result<()> {
let mut rng = rand::rngs::OsRng;
use registration::*;
let registration_start = opaque::client::registration::start_registration(password, &mut rng)?;
let registration_start =
opaque::client::registration::start_registration(password.unsecure(), &mut rng)?;
let start_response = opaque_handler
.registration_start(ClientRegistrationStartRequest {
username: username.to_string(),
@ -321,7 +323,7 @@ mod tests {
attempt_login(&opaque_handler, "bob", "bob00")
.await
.unwrap_err();
register_password(&opaque_handler, "bob", "bob00").await?;
register_password(&opaque_handler, "bob", &secstr::SecUtf8::from("bob00")).await?;
attempt_login(&opaque_handler, "bob", "wrong_password")
.await
.unwrap_err();

View File

@ -6,6 +6,7 @@ use figment::{
};
use lettre::message::Mailbox;
use lldap_auth::opaque::{server::ServerSetup, KeyPair};
use secstr::SecUtf8;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, derive_builder::Builder)]
@ -23,8 +24,8 @@ pub struct MailOptions {
pub port: u16,
#[builder(default = r#""admin".to_string()"#)]
pub user: String,
#[builder(default = r#""".to_string()"#)]
pub password: String,
#[builder(default = r#"SecUtf8::from("")"#)]
pub password: SecUtf8,
#[builder(default = "true")]
pub tls_required: bool,
}
@ -47,14 +48,14 @@ pub struct Configuration {
pub ldaps_port: u16,
#[builder(default = "17170")]
pub http_port: u16,
#[builder(default = r#"String::from("secretjwtsecret")"#)]
pub jwt_secret: String,
#[builder(default = r#"SecUtf8::from("secretjwtsecret")"#)]
pub jwt_secret: SecUtf8,
#[builder(default = r#"String::from("dc=example,dc=com")"#)]
pub ldap_base_dn: String,
#[builder(default = r#"String::from("admin")"#)]
pub ldap_user_dn: String,
#[builder(default = r#"String::from("password")"#)]
pub ldap_user_pass: String,
#[builder(default = r#"SecUtf8::from("password")"#)]
pub ldap_user_pass: SecUtf8,
#[builder(default = r#"String::from("sqlite://users.db?mode=rwc")"#)]
pub database_url: String,
#[builder(default = "false")]
@ -188,7 +189,7 @@ impl ConfigOverrider for SmtpOpts {
config.smtp_options.user = user.clone();
}
if let Some(password) = &self.smtp_password {
config.smtp_options.password = password.clone();
config.smtp_options.password = SecUtf8::from(password.clone());
}
if let Some(tls_required) = self.smtp_tls_required {
config.smtp_options.tls_required = tls_required;
@ -219,10 +220,10 @@ where
println!("Configuration: {:#?}", &config);
}
config.server_setup = Some(get_server_setup(&config.key_file)?);
if config.jwt_secret == "secretjwtsecret" {
if config.jwt_secret == SecUtf8::from("secretjwtsecret") {
println!("WARNING: Default JWT secret used! This is highly unsafe and can allow attackers to log in as admin.");
}
if config.ldap_user_pass == "password" {
if config.ldap_user_pass == SecUtf8::from("password") {
println!("WARNING: Unsecure default admin password is used.");
}
Ok(config)

View File

@ -22,7 +22,10 @@ pub fn send_test_email(to: Mailbox, options: &MailOptions) -> Result<()> {
.to(to)
.subject("LLDAP test email")
.body("The test is successful! You can send emails from LLDAP".to_string())?;
let creds = Credentials::new(options.user.clone(), options.password.clone());
let creds = Credentials::new(
options.user.clone(),
options.password.unsecure().to_string(),
);
let mailer = SmtpTransport::relay(&options.server)?
.credentials(creds)
.build();

View File

@ -44,14 +44,14 @@ pub(crate) fn error_to_http_response(error: DomainError) -> HttpResponse {
fn http_config<Backend>(
cfg: &mut web::ServiceConfig,
backend_handler: Backend,
jwt_secret: String,
jwt_secret: secstr::SecUtf8,
jwt_blacklist: HashSet<u64>,
) where
Backend: TcpBackendHandler + BackendHandler + LoginHandler + OpaqueHandler + Sync + 'static,
{
cfg.app_data(web::Data::new(AppState::<Backend> {
backend_handler,
jwt_key: Hmac::new_varkey(jwt_secret.as_bytes()).unwrap(),
jwt_key: Hmac::new_varkey(jwt_secret.unsecure().as_bytes()).unwrap(),
jwt_blacklist: RwLock::new(jwt_blacklist),
}))
// Serve index.html and main.js, and default to index.html.

View File

@ -20,10 +20,11 @@ mod domain;
mod infra;
async fn create_admin_user(handler: &SqlBackendHandler, config: &Configuration) -> Result<()> {
let pass_length = config.ldap_user_pass.unsecure().len();
assert!(
config.ldap_user_pass.len() >= 8,
pass_length >= 8,
"Minimum password length is 8 characters, got {} characters",
config.ldap_user_pass.len()
pass_length
);
handler
.create_user(CreateUserRequest {