mirror of
https://git.verdigado.com/NB-Public/simple-wkd.git
synced 2024-12-06 14:52:41 +01:00
Merge 48ff058234
into a61f03f412
This commit is contained in:
commit
704b463949
9 changed files with 134 additions and 16 deletions
|
@ -37,6 +37,7 @@ mail_settings.smtp_port | Any positive number | The port of the SMTP server
|
||||||
mail_settings.smtp_tls | `Tls` or `Starttls` | The encryption method to use
|
mail_settings.smtp_tls | `Tls` or `Starttls` | The encryption method to use
|
||||||
mail_settings.mail_from | String | The email address to be used
|
mail_settings.mail_from | String | The email address to be used
|
||||||
mail_settings.mail_subject | String | The confirmation email's subject
|
mail_settings.mail_subject | String | The confirmation email's subject
|
||||||
|
policy.key_max_validity | Any positive number | The maximum allowed validity period of a key in seconds. (optional)
|
||||||
|
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
|
@ -20,3 +20,4 @@ tokio = { version = "1.27.0", features = ["time"] }
|
||||||
toml = "0.7.3"
|
toml = "0.7.3"
|
||||||
url = { version = "2.3.1", features = ["serde"] }
|
url = { version = "2.3.1", features = ["serde"] }
|
||||||
zbase32 = "0.1.2"
|
zbase32 = "0.1.2"
|
||||||
|
sequoia-policy-config = { version = "0.6" }
|
||||||
|
|
|
@ -5,8 +5,8 @@ use log::{debug, error, warn};
|
||||||
use crate::errors::SpecialErrors;
|
use crate::errors::SpecialErrors;
|
||||||
use crate::management::{delete_key, Action, Pending};
|
use crate::management::{delete_key, Action, Pending};
|
||||||
use crate::settings::{MAILER, SETTINGS};
|
use crate::settings::{MAILER, SETTINGS};
|
||||||
use crate::utils::{get_email_from_cert, insert_key, parse_pem, read_file};
|
use crate::utils::{get_email_from_cert, insert_key, parse_pem, read_file, validate_cert};
|
||||||
use crate::{log_err, pending_path, validate_cert};
|
use crate::{log_err, pending_path};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use lettre::{AsyncTransport, Message};
|
use lettre::{AsyncTransport, Message};
|
||||||
|
@ -25,7 +25,7 @@ pub fn confirm_action(token: &str) -> Result<(Action, String)> {
|
||||||
let address = match key.action() {
|
let address = match key.action() {
|
||||||
Action::Add => {
|
Action::Add => {
|
||||||
let cert = parse_pem(key.data())?;
|
let cert = parse_pem(key.data())?;
|
||||||
let validcert = validate_cert!(cert)?;
|
let validcert = validate_cert(&cert)?;
|
||||||
let email = get_email_from_cert(&validcert)?;
|
let email = get_email_from_cert(&validcert)?;
|
||||||
log_err!(insert_key(&validcert), warn)?;
|
log_err!(insert_key(&validcert), warn)?;
|
||||||
email
|
email
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub enum SpecialErrors {
|
||||||
ExpiredRequest,
|
ExpiredRequest,
|
||||||
#[error("The key for the requested user does not exist!")]
|
#[error("The key for the requested user does not exist!")]
|
||||||
InexistingUser,
|
InexistingUser,
|
||||||
#[error("The key is either expired or uses an obsolete cipher!")]
|
#[error("The primary key is either expired or uses an obsolete cipher!")]
|
||||||
InvalidCert,
|
InvalidCert,
|
||||||
#[error("Error while sending email")]
|
#[error("Error while sending email")]
|
||||||
MailErr,
|
MailErr,
|
||||||
|
@ -56,6 +56,12 @@ pub enum SpecialErrors {
|
||||||
MissingFile,
|
MissingFile,
|
||||||
#[error("User email rejected: domain not allowed")]
|
#[error("User email rejected: domain not allowed")]
|
||||||
UnallowedDomain,
|
UnallowedDomain,
|
||||||
|
#[error("The primary key or a subkey does not expire")]
|
||||||
|
KeyNonExpiring,
|
||||||
|
#[error("The primary keys or a subkeys validity is too long")]
|
||||||
|
KeyValidityTooLong,
|
||||||
|
#[error("A subkey is either expired or uses an obsolete cipher!")]
|
||||||
|
KeyPolicyViolation,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -104,6 +110,9 @@ impl ResponseError for CompatErr {
|
||||||
SpecialErrors::MalformedEmail => StatusCode::BAD_REQUEST,
|
SpecialErrors::MalformedEmail => StatusCode::BAD_REQUEST,
|
||||||
SpecialErrors::MissingFile => StatusCode::NOT_FOUND,
|
SpecialErrors::MissingFile => StatusCode::NOT_FOUND,
|
||||||
SpecialErrors::UnallowedDomain => StatusCode::UNAUTHORIZED,
|
SpecialErrors::UnallowedDomain => StatusCode::UNAUTHORIZED,
|
||||||
|
SpecialErrors::KeyNonExpiring => StatusCode::BAD_REQUEST,
|
||||||
|
SpecialErrors::KeyValidityTooLong => StatusCode::BAD_REQUEST,
|
||||||
|
SpecialErrors::KeyPolicyViolation => StatusCode::BAD_REQUEST,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::management::{clean_stale, store_pending_addition, store_pending_delet
|
||||||
use crate::settings::{ROOT_FOLDER, SETTINGS};
|
use crate::settings::{ROOT_FOLDER, SETTINGS};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
gen_random_token, get_email_from_cert, is_email_allowed, key_exists, parse_pem, read_file,
|
gen_random_token, get_email_from_cert, is_email_allowed, key_exists, parse_pem, read_file,
|
||||||
return_outcome,
|
return_outcome, validate_cert
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_files::Files;
|
use actix_files::Files;
|
||||||
|
@ -102,7 +102,7 @@ async fn index(req: HttpRequest) -> Result<HttpResponse, CompatErr> {
|
||||||
#[post("/api/submit")]
|
#[post("/api/submit")]
|
||||||
async fn submit(pem: web::Form<Key>) -> Result<HttpResponse, CompatErr> {
|
async fn submit(pem: web::Form<Key>) -> Result<HttpResponse, CompatErr> {
|
||||||
let cert = parse_pem(&pem.key)?;
|
let cert = parse_pem(&pem.key)?;
|
||||||
let validcert = validate_cert!(cert)?;
|
let validcert = validate_cert(&cert)?;
|
||||||
if validcert.is_tsk() {
|
if validcert.is_tsk() {
|
||||||
Err(SpecialErrors::ContainsSecret)?
|
Err(SpecialErrors::ContainsSecret)?
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use lettre::{transport::smtp::authentication::Credentials, AsyncSmtpTransport, T
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use sequoia_openpgp::policy::StandardPolicy;
|
use sequoia_openpgp::policy::StandardPolicy;
|
||||||
|
use sequoia_policy_config::ConfiguredStandardPolicy;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -18,6 +19,7 @@ pub struct Settings {
|
||||||
pub bind_host: String,
|
pub bind_host: String,
|
||||||
pub external_url: Url,
|
pub external_url: Url,
|
||||||
pub mail_settings: MailSettings,
|
pub mail_settings: MailSettings,
|
||||||
|
pub policy: Option<Policy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
@ -31,6 +33,11 @@ pub struct MailSettings {
|
||||||
pub mail_subject: String,
|
pub mail_subject: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Policy {
|
||||||
|
pub key_max_validity: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum Variant {
|
pub enum Variant {
|
||||||
Advanced,
|
Advanced,
|
||||||
|
@ -87,8 +94,19 @@ fn get_mailer() -> AsyncSmtpTransport<Tokio1Executor> {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_policy<'a>() -> StandardPolicy<'a> {
|
||||||
|
let mut p = ConfiguredStandardPolicy::new();
|
||||||
|
|
||||||
|
match p.parse_default_config() {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => error!("{e}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
p.build()
|
||||||
|
}
|
||||||
|
|
||||||
pub const ERROR_TEXT: &str = "An error occoured:";
|
pub const ERROR_TEXT: &str = "An error occoured:";
|
||||||
pub const POLICY: &StandardPolicy = &StandardPolicy::new();
|
pub static POLICY: Lazy<StandardPolicy> = Lazy::new(get_policy);
|
||||||
pub const ROOT_FOLDER: &str = "data";
|
pub const ROOT_FOLDER: &str = "data";
|
||||||
pub static SETTINGS: Lazy<Settings> = Lazy::new(get_settings);
|
pub static SETTINGS: Lazy<Settings> = Lazy::new(get_settings);
|
||||||
pub static MAILER: Lazy<AsyncSmtpTransport<Tokio1Executor>> = Lazy::new(get_mailer);
|
pub static MAILER: Lazy<AsyncSmtpTransport<Tokio1Executor>> = Lazy::new(get_mailer);
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::log_err;
|
||||||
use crate::settings::Variant;
|
use crate::settings::Variant;
|
||||||
use crate::settings::ROOT_FOLDER;
|
use crate::settings::ROOT_FOLDER;
|
||||||
use crate::settings::SETTINGS;
|
use crate::settings::SETTINGS;
|
||||||
|
use crate::settings::POLICY;
|
||||||
|
|
||||||
use actix_web::ResponseError;
|
use actix_web::ResponseError;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
|
@ -16,22 +17,56 @@ use log::debug;
|
||||||
use log::error;
|
use log::error;
|
||||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
use sequoia_openpgp::cert::ValidCert;
|
use sequoia_openpgp::cert::ValidCert;
|
||||||
|
use sequoia_openpgp::cert::amalgamation::ValidateAmalgamation;
|
||||||
use sequoia_openpgp::serialize::Marshal;
|
use sequoia_openpgp::serialize::Marshal;
|
||||||
use sequoia_openpgp::types::HashAlgorithm;
|
use sequoia_openpgp::types::HashAlgorithm;
|
||||||
use sequoia_openpgp::{parse::Parse, Cert};
|
use sequoia_openpgp::{parse::Parse, Cert};
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[macro_export]
|
pub fn validate_cert(cert: &Cert) -> Result<ValidCert> {
|
||||||
macro_rules! validate_cert {
|
let validcert = match log_err!(cert.with_policy(&*POLICY, None), debug) {
|
||||||
( $x:expr ) => {
|
Ok(validcert) => validcert,
|
||||||
match log_err!($x.with_policy($crate::settings::POLICY, None), debug) {
|
Err(e) => {
|
||||||
Ok(validcert) => Ok(validcert),
|
debug!("Certificate was rejected: The primary key violates the policy: {}", e.source().unwrap());
|
||||||
Err(_) => Err($crate::errors::SpecialErrors::InvalidCert),
|
Err(SpecialErrors::InvalidCert)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for key in cert.keys().subkeys() {
|
||||||
|
match log_err!(key.with_policy(&*POLICY, None), debug) {
|
||||||
|
Ok(_) => continue,
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Certificate was rejected: A sub key violates the policy: {}", e.source().unwrap());
|
||||||
|
Err(SpecialErrors::KeyPolicyViolation)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(policy_settings) = &SETTINGS.policy {
|
||||||
|
if let Some(max_validity_setting) = policy_settings.key_max_validity {
|
||||||
|
let max_validity = Duration::from_secs(max_validity_setting);
|
||||||
|
|
||||||
|
if !max_validity.is_zero() {
|
||||||
|
for key in validcert.keys() {
|
||||||
|
let validity = key.key_validity_period();
|
||||||
|
|
||||||
|
if validity.is_none() {
|
||||||
|
debug!("Certificate was rejected: The primary key or a subkey has validity period of zero");
|
||||||
|
return Err(SpecialErrors::KeyNonExpiring)?
|
||||||
|
} else if validity > Some(max_validity) {
|
||||||
|
debug!("Certificate was rejected: The primary key or a subkey has a validity period greater than {max_validity_setting} seconds");
|
||||||
|
return Err(SpecialErrors::KeyValidityTooLong)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(validcert)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_local(local: &str) -> String {
|
pub fn encode_local(local: &str) -> String {
|
||||||
|
@ -106,7 +141,7 @@ pub fn parse_pem(pemfile: &str) -> Result<Cert> {
|
||||||
Ok(cert) => cert,
|
Ok(cert) => cert,
|
||||||
Err(_) => Err(SpecialErrors::MalformedCert)?,
|
Err(_) => Err(SpecialErrors::MalformedCert)?,
|
||||||
};
|
};
|
||||||
validate_cert!(cert)?;
|
validate_cert(&cert)?;
|
||||||
Ok(cert)
|
Ok(cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,4 +13,7 @@ smtp_password = "verysecurepassword"
|
||||||
smtp_port = 465
|
smtp_port = 465
|
||||||
smtp_tls = "Tls"
|
smtp_tls = "Tls"
|
||||||
mail_from = "key-submission@example.org"
|
mail_from = "key-submission@example.org"
|
||||||
mail_subject = "Please confirm to %a your public key"
|
mail_subject = "Please confirm to %a your public key"
|
||||||
|
|
||||||
|
[policy]
|
||||||
|
key_max_validity = 0
|
||||||
|
|
51
example.policy.toml
Normal file
51
example.policy.toml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
[hash_algorithms]
|
||||||
|
md5.collision_resistance = "never"
|
||||||
|
md5.second_preimage_resistance = "never"
|
||||||
|
sha1.collision_resistance = "never"
|
||||||
|
sha1.second_preimage_resistance = "never"
|
||||||
|
ripemd160.collision_resistance = "never"
|
||||||
|
ripemd160.second_preimage_resistance = "never"
|
||||||
|
sha224.collision_resistance = "always"
|
||||||
|
sha224.second_preimage_resistance = "always"
|
||||||
|
sha256.collision_resistance = "always"
|
||||||
|
sha256.second_preimage_resistance = "always"
|
||||||
|
sha384.collision_resistance = "always"
|
||||||
|
sha384.second_preimage_resistance = "always"
|
||||||
|
sha512.collision_resistance = "always"
|
||||||
|
sha512.second_preimage_resistance = "always"
|
||||||
|
default_disposition = "never"
|
||||||
|
|
||||||
|
[symmetric_algorithms]
|
||||||
|
idea = "never"
|
||||||
|
tripledes = "never"
|
||||||
|
cast5 = "never"
|
||||||
|
blowfish = "never"
|
||||||
|
aes128 = "always"
|
||||||
|
aes192 = "never"
|
||||||
|
aes256 = "always"
|
||||||
|
twofish = "never"
|
||||||
|
camellia128 = "never"
|
||||||
|
camellia192 = "never"
|
||||||
|
camellia256 = "never"
|
||||||
|
default_disposition = "never"
|
||||||
|
|
||||||
|
[asymmetric_algorithms]
|
||||||
|
elgamal1024 = "never"
|
||||||
|
elgamal2048 = "never"
|
||||||
|
elgamal3072 = "never"
|
||||||
|
elgamal4096 = "never"
|
||||||
|
brainpoolp256 = "never"
|
||||||
|
brainpoolp512 = "never"
|
||||||
|
rsa1024 = "never"
|
||||||
|
rsa2048 = "always"
|
||||||
|
rsa3072 = "always"
|
||||||
|
rsa4096 = "always"
|
||||||
|
dsa1024 = "never"
|
||||||
|
dsa2048 = "never"
|
||||||
|
dsa3072 = "never"
|
||||||
|
dsa4096 = "never"
|
||||||
|
nistp256 = "always"
|
||||||
|
nistp384 = "always"
|
||||||
|
nistp521 = "always"
|
||||||
|
cv25519 = "always"
|
||||||
|
default_disposition = "never"
|
Loading…
Add table
Reference in a new issue