commit 8defb7539802dd914644908d2444cfbaab88764a
parent 0dadd7b31887706d3653243742b87a9221b7ff50
Author: Zack Newman <zack@philomathiclife.com>
Date: Wed, 21 Aug 2024 18:56:05 -0600
update deps. handle lints. remove attestation
Diffstat:
8 files changed, 48 insertions(+), 84 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -8,36 +8,41 @@ keywords = ["password", "vaultwarden"]
license = "AGPL-3.0-only"
name = "vw_small"
publish = false
+readme = "README.md"
repository = "https://git.philomathiclife.com/repos/vw_small/"
-version = "2.0.1"
+rust-version = "1.78.0"
+version = "3.0.0"
-[features]
-priv_sep = ["dep:priv_sep"]
+[badges]
+maintenance = { status = "actively-developed" }
[target.'cfg(target_os = "openbsd")'.dependencies]
-priv_sep = { version = "1.0.1", default-features = false, features = ["openbsd"], optional = true }
+priv_sep = { version = "2.0.0", default-features = false, features = ["openbsd"], optional = true }
[dependencies]
chrono = { version = "0.4.38", default-features = false, features = ["serde"] }
data-encoding = { version = "2.6.0", default-features = false }
-diesel = { version = "2.1.6", default-features = false, features = ["32-column-tables", "chrono", "r2d2", "sqlite"] }
+diesel = { version = "2.2.2", default-features = false, features = ["32-column-tables", "chrono", "r2d2", "sqlite"] }
jsonwebtoken = { version = "9.3.0", default-features = false, features = ["use_pem"] }
-libsqlite3-sys = { version = "0.28.0", default-features = false, features = ["bundled"] }
-openssl = { version = "0.10.64", default-features = false }
-paste = { version = "1.0.14", default-features = false }
+libsqlite3-sys = { version = "0.29.0", default-features = false, features = ["bundled"] }
+openssl = { version = "0.10.66", default-features = false }
+paste = { version = "1.0.15", default-features = false }
rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
ring = { version = "0.17.8", default-features = false }
-rocket = { version = "0.5.0", default-features = false, features = ["json", "tls"] }
-semver = { version = "1.0.22", default-features = false }
-serde = { version = "1.0.200", default-features = false }
-serde_json = { version = "1.0.116", default-features = false }
-tokio = { version = "1.37.0", default-features = false }
-toml = { version = "0.8.12", default-features = false, features = ["parse"] }
+rocket = { version = "0.5.1", default-features = false, features = ["json", "tls"] }
+semver = { version = "1.0.23", default-features = false }
+serde = { version = "1.0.208", default-features = false }
+serde_json = { version = "1.0.125", default-features = false }
+tokio = { version = "1.39.3", default-features = false }
+toml = { version = "0.8.19", default-features = false, features = ["parse"] }
totp-lite = { version = "2.0.1", default-features = false }
-url = { version = "2.5.0", default-features = false }
-uuid = { version = "1.8.0", default-features = false, features = ["v4"] }
+url = { version = "2.5.2", default-features = false }
+uuid = { version = "1.10.0", default-features = false, features = ["v4"] }
webauthn-rs = { version = "0.4.8", default-features = false, features = ["danger-allow-state-serialisation", "danger-user-presence-only-security-keys"] }
+[features]
+priv_sep = ["dep:priv_sep"]
+
[patch.crates-io]
webauthn-rs-core = { git = "https://git.philomathiclife.com/repos/webauthn-rs-core", tag = "v0.4.10" }
webauthn-rs-proto = { git = "https://git.philomathiclife.com/repos/webauthn-rs-proto", tag = "v0.4.10" }
diff --git a/config.toml b/config.toml
@@ -6,7 +6,6 @@ ip="fdb5:d87:ae42:1::1"
#password_iterations=600000
port=8443
#web_vault_enabled=true
-webauthn_require_yubi=true
workers=4
[tls]
cert="/etc/ssl/pmd.philomathiclife.com.fullchain"
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
@@ -580,8 +580,8 @@ struct KnownDevice {
#[rocket::async_trait]
impl<'r> FromRequest<'r> for KnownDevice {
type Error = &'static str;
- async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
- let email = if let Some(email_b64) = req.headers().get_one("X-Request-Email") {
+ async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
+ let email = if let Some(email_b64) = request.headers().get_one("X-Request-Email") {
let Ok(email_bytes) = data_encoding::BASE64URL_NOPAD.decode(email_b64.as_bytes())
else {
return Outcome::Error((
@@ -602,7 +602,7 @@ impl<'r> FromRequest<'r> for KnownDevice {
return Outcome::Error((Status::BadRequest, "X-Request-Email value is required"));
};
- let uuid = if let Some(uuid) = req.headers().get_one("X-Device-Identifier") {
+ let uuid = if let Some(uuid) = request.headers().get_one("X-Device-Identifier") {
uuid.to_owned()
} else {
return Outcome::Error((Status::BadRequest, "X-Device-Identifier value is required"));
diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs
@@ -100,7 +100,11 @@ pub async fn validate_totp_code_str(
}
validate_totp_code(user_uuid, totp_code, secret, ip, conn).await
}
-#[allow(clippy::integer_division, clippy::redundant_else)]
+#[allow(
+ clippy::integer_division,
+ clippy::integer_division_remainder_used,
+ clippy::redundant_else
+)]
async fn validate_totp_code(
user_uuid: String,
totp_code: &str,
diff --git a/src/api/core/two_factor/webauthn.rs b/src/api/core/two_factor/webauthn.rs
@@ -12,8 +12,8 @@ use rocket::serde::json::Json;
use rocket::Route;
use url::Url;
use webauthn_rs::prelude::{
- AttestationCa, AttestationCaList, AuthenticatorAttachment, PublicKeyCredential,
- RegisterPublicKeyCredential, Uuid, Webauthn, WebauthnBuilder, WebauthnError,
+ PublicKeyCredential, RegisterPublicKeyCredential, Uuid, Webauthn, WebauthnBuilder,
+ WebauthnError,
};
pub fn routes() -> Vec<Route> {
@@ -49,54 +49,6 @@ async fn get_webauthn(
"Object": "twoFactorWebAuthn"
})))
}
-// When YubiKey enforcement is enabled, we generate the allowed list of attestation CAs.
-fn get_attestation_list() -> Option<AttestationCaList> {
- config::get_config().webauthn_require_yubi.then(|| {
- let mut attest_list = AttestationCaList::default();
- let mut ca = AttestationCa::yubico_u2f_root_ca_serial_457200631();
- ca.aaguids.clear();
- // We only allow FIDO2 YubiKeys with firmware 5.2.a, 5.4.b, 5.5.c, or 5.6.d (https://support.yubico.com/hc/en-us/articles/360016648959-YubiKey-Hardware-FIDO2-AAGUIDs).
- // YubiKey 5 (USB-A, No NFC), YubiKey 5 Nano, YubiKey 5 Nano CSPN, YubiKey 5C, YubiKey 5C CSPN,
- // YubiKey 5C Nano, and YubiKey 5C Nano CSPN.
- ca.aaguids
- .insert(Uuid::try_parse("ee882879-721c-4913-9775-3dfcce97072a").expect("invaild UUID"));
- // YubiKey 5 NFC, YubiKey 5 NFC CSPN, YubiKey 5C NFC, and YubiKey 5C NFC CSPN.
- ca.aaguids
- .insert(Uuid::try_parse("2fc0579f-8113-47ea-b116-bb5a8db9202a").expect("invaild UUID"));
- // YubiKey 5 NFC FIPS and YubiKey 5C NFC FIPS.
- ca.aaguids
- .insert(Uuid::try_parse("c1f9a0bc-1dd2-404a-b27f-8e29047a43fd").expect("invaild UUID"));
- // YubiKey 5 Nano FIPS, YubiKey 5C FIPS, and YubiKey 5C Nano FIPS.
- ca.aaguids
- .insert(Uuid::try_parse("73bb0cd4-e502-49b8-9c6f-b59445bf720b").expect("invaild UUID"));
- // YubiKey 5Ci and YubiKey 5Ci CSPN.
- ca.aaguids
- .insert(Uuid::try_parse("c5ef55ff-ad9a-4b9f-b580-adebafe026d0").expect("invaild UUID"));
- // YubiKey 5Ci FIPS.
- ca.aaguids
- .insert(Uuid::try_parse("85203421-48f9-4355-9bc8-8a53846e5083").expect("invaild UUID"));
- // Security Key By Yubico.
- ca.aaguids
- .insert(Uuid::try_parse("b92c3f9a-c014-4056-887f-140a2501163b").expect("invaild UUID"));
- // Security Key NFC (USB-A, USB-C).
- ca.aaguids
- .insert(Uuid::try_parse("149a2021-8ef6-4133-96b8-81f8d5b7f1f5").expect("invaild UUID"));
- // YubiKey Bio Series.
- ca.aaguids
- .insert(Uuid::try_parse("d8522d9f-575b-4866-88a9-ba99fa02f35b").expect("invaild UUID"));
- // Security Key NFC Black (USB-A, USB-C).
- ca.aaguids
- .insert(Uuid::try_parse("a4e9fc6d-4cbe-4758-b8ba-37598bb5bbaa").expect("invaild UUID"));
- // Security Key NFC - Enterprise Edition (USB-A, USB-C).
- ca.aaguids
- .insert(Uuid::try_parse("0bb43545-fd2c-4185-87dd-feb0b2916ace").expect("invaild UUID"));
- attest_list
- .insert(ca)
- .expect("unable to add attestation CAs");
- attest_list
- })
-}
-
#[post("/two-factor/get-webauthn-challenge", data = "<data>")]
async fn generate_webauthn_challenge(
data: JsonUpcase<PasswordOrOtpData>,
@@ -106,17 +58,13 @@ async fn generate_webauthn_challenge(
let data: PasswordOrOtpData = data.into_inner().data;
let user = headers.user;
data.validate(&user)?;
- let (attest_list, auth_plat) = get_attestation_list().map_or_else(
- || (None, None),
- |attest| (Some(attest), Some(AuthenticatorAttachment::CrossPlatform)),
- );
let (challenge, registration) = build_webauthn()?.start_securitykey_registration(
Uuid::try_parse(user.uuid.as_str()).expect("unable to create UUID"),
user.email.as_str(),
user.name.as_str(),
Some(WebAuthn::get_all_credentials_by_user(&user.uuid, &conn).await?),
- attest_list,
- auth_plat,
+ None,
+ None,
)?;
WebAuthnChallenge::Reg(WebAuthnReg::new(user.uuid, ®istration)?)
.replace(&conn)
diff --git a/src/auth.rs b/src/auth.rs
@@ -289,9 +289,10 @@ impl<'r> FromRequest<'r> for Host {
})
}
}
-
pub struct ClientHeaders {
+ #[allow(dead_code)]
pub host: String,
+ #[allow(dead_code)]
pub device_type: i32,
pub ip: ClientIp,
}
@@ -407,6 +408,7 @@ pub struct OrgHeaders {
user: User,
org_user_type: UserOrgType,
org_user: UserOrganization,
+ #[allow(dead_code)]
pub org_id: String,
ip: ClientIp,
}
@@ -547,6 +549,7 @@ pub struct ManagerHeaders {
host: String,
device: Device,
pub user: User,
+ #[allow(dead_code)]
pub org_user_type: UserOrgType,
ip: ClientIp,
}
@@ -666,9 +669,12 @@ impl ManagerHeaders {
}
pub struct OwnerHeaders {
+ #[allow(dead_code)]
pub host: String,
+ #[allow(dead_code)]
pub device: Device,
pub user: User,
+ #[allow(dead_code)]
pub ip: ClientIp,
}
@@ -703,8 +709,8 @@ pub struct ClientIp {
impl<'r> FromRequest<'r> for ClientIp {
type Error = ();
#[allow(clippy::map_err_ignore, clippy::string_slice)]
- async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
- let ip = req.headers().get_one("X-Real-IP").and_then(|ip| {
+ async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
+ let ip = request.headers().get_one("X-Real-IP").and_then(|ip| {
ip.find(',')
.map_or(ip, |idx| &ip[..idx])
.parse()
@@ -712,13 +718,14 @@ impl<'r> FromRequest<'r> for ClientIp {
.ok()
});
let ip = ip
- .or_else(|| req.remote().map(|r| r.ip()))
+ .or_else(|| request.remote().map(|r| r.ip()))
.unwrap_or_else(|| "0.0.0.0".parse().unwrap());
Outcome::Success(Self { ip })
}
}
pub struct WsAccessTokenHeader {
+ #[allow(dead_code)]
pub access_token: Option<String>,
}
diff --git a/src/config.rs b/src/config.rs
@@ -66,6 +66,7 @@ impl From<ParseError> for ConfigErr {
}
}
#[derive(serde::Deserialize)]
+#[serde(deny_unknown_fields)]
struct Tls {
ciphers: Option<Vec<CipherSuite>>,
cert: String,
@@ -73,6 +74,7 @@ struct Tls {
prefer_server_cipher_order: Option<bool>,
}
#[derive(serde::Deserialize)]
+#[serde(deny_unknown_fields)]
struct ConfigFile {
database_max_conns: Option<NonZeroU8>,
database_timeout: Option<u16>,
@@ -82,7 +84,6 @@ struct ConfigFile {
password_iterations: Option<u32>,
port: u16,
tls: Tls,
- webauthn_require_yubi: Option<bool>,
web_vault_enabled: Option<bool>,
workers: Option<NonZeroU8>,
}
@@ -95,7 +96,6 @@ pub struct Config {
pub password_iterations: u32,
pub rocket: rocket::Config,
pub web_vault_enabled: bool,
- pub webauthn_require_yubi: bool,
}
impl Config {
#[inline]
@@ -172,7 +172,6 @@ impl Config {
},
rocket,
web_vault_enabled: config_file.web_vault_enabled.unwrap_or(true),
- webauthn_require_yubi: config_file.webauthn_require_yubi.unwrap_or(false),
})
}
}
diff --git a/src/main.rs b/src/main.rs
@@ -1,7 +1,9 @@
#![deny(
+ unknown_lints,
future_incompatible,
let_underscore,
nonstandard_style,
+ refining_impl_trait,
rust_2018_compatibility,
rust_2018_idioms,
rust_2021_compatibility,