diff --git a/backend/src/errors.rs b/backend/src/errors.rs index fde8436..77c6a30 100644 --- a/backend/src/errors.rs +++ b/backend/src/errors.rs @@ -56,6 +56,10 @@ pub enum SpecialErrors { MissingFile, #[error("User email rejected: domain not allowed")] UnallowedDomain, + #[error("The primary key or a subkey does not expire")] + KeyNonExpiring, + #[error("The primary keys or a subkeys validity is too long")] + KeyValidityTooLong, } #[derive(Debug)] @@ -104,6 +108,8 @@ impl ResponseError for CompatErr { SpecialErrors::MalformedEmail => StatusCode::BAD_REQUEST, SpecialErrors::MissingFile => StatusCode::NOT_FOUND, SpecialErrors::UnallowedDomain => StatusCode::UNAUTHORIZED, + SpecialErrors::KeyNonExpiring => StatusCode::BAD_REQUEST, + SpecialErrors::KeyValidityTooLong => StatusCode::BAD_REQUEST, }, } } diff --git a/backend/src/settings.rs b/backend/src/settings.rs index db23705..3b330f1 100644 --- a/backend/src/settings.rs +++ b/backend/src/settings.rs @@ -18,6 +18,7 @@ pub struct Settings { pub bind_host: String, pub external_url: Url, pub mail_settings: MailSettings, + pub policy: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -31,6 +32,11 @@ pub struct MailSettings { pub mail_subject: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct Policy { + pub key_max_validity: Option, +} + #[derive(Serialize, Deserialize, Debug)] pub enum Variant { Advanced, diff --git a/backend/src/utils.rs b/backend/src/utils.rs index b07a8ca..9a8f94e 100644 --- a/backend/src/utils.rs +++ b/backend/src/utils.rs @@ -22,13 +22,36 @@ use sequoia_openpgp::{parse::Parse, Cert}; use std::{ fs, path::{Path, PathBuf}, + time::Duration, }; pub fn validate_cert(cert: &Cert) -> Result { - match log_err!(cert.with_policy(crate::settings::POLICY, None), debug) { - Ok(validcert) => Ok(validcert), + let validcert = match log_err!(cert.with_policy(crate::settings::POLICY, None), debug) { + Ok(validcert) => validcert, Err(_) => Err(SpecialErrors::InvalidCert)?, + }; + + 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 {