mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-11-04 12:18:20 +02:00 
			
		
		
		
	Merge branch 'main' into main
This commit is contained in:
		
							
								
								
									
										7
									
								
								migrations/mysql/2023-01-31-222222_add_argon2/up.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								migrations/mysql/2023-01-31-222222_add_argon2/up.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
ALTER TABLE users
 | 
			
		||||
    ADD COLUMN
 | 
			
		||||
    client_kdf_memory INTEGER DEFAULT NULL;
 | 
			
		||||
 | 
			
		||||
ALTER TABLE users
 | 
			
		||||
    ADD COLUMN
 | 
			
		||||
    client_kdf_parallelism INTEGER DEFAULT NULL;
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
ALTER TABLE users
 | 
			
		||||
    ADD COLUMN
 | 
			
		||||
    client_kdf_memory INTEGER DEFAULT NULL;
 | 
			
		||||
 | 
			
		||||
ALTER TABLE users
 | 
			
		||||
    ADD COLUMN
 | 
			
		||||
    client_kdf_parallelism INTEGER DEFAULT NULL;
 | 
			
		||||
							
								
								
									
										7
									
								
								migrations/sqlite/2023-01-31-222222_add_argon2/up.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								migrations/sqlite/2023-01-31-222222_add_argon2/up.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
ALTER TABLE users
 | 
			
		||||
    ADD COLUMN
 | 
			
		||||
    client_kdf_memory INTEGER DEFAULT NULL;
 | 
			
		||||
 | 
			
		||||
ALTER TABLE users
 | 
			
		||||
    ADD COLUMN
 | 
			
		||||
    client_kdf_parallelism INTEGER DEFAULT NULL;
 | 
			
		||||
@@ -49,6 +49,8 @@ pub struct RegisterData {
 | 
			
		||||
    Email: String,
 | 
			
		||||
    Kdf: Option<i32>,
 | 
			
		||||
    KdfIterations: Option<i32>,
 | 
			
		||||
    KdfMemory: Option<i32>,
 | 
			
		||||
    KdfParallelism: Option<i32>,
 | 
			
		||||
    Key: String,
 | 
			
		||||
    Keys: Option<KeysData>,
 | 
			
		||||
    MasterPasswordHash: String,
 | 
			
		||||
@@ -153,13 +155,16 @@ pub async fn _register(data: JsonUpcase<RegisterData>, mut conn: DbConn) -> Json
 | 
			
		||||
    // Make sure we don't leave a lingering invitation.
 | 
			
		||||
    Invitation::take(&email, &mut conn).await;
 | 
			
		||||
 | 
			
		||||
    if let Some(client_kdf_type) = data.Kdf {
 | 
			
		||||
        user.client_kdf_type = client_kdf_type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if let Some(client_kdf_iter) = data.KdfIterations {
 | 
			
		||||
        user.client_kdf_iter = client_kdf_iter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if let Some(client_kdf_type) = data.Kdf {
 | 
			
		||||
        user.client_kdf_type = client_kdf_type;
 | 
			
		||||
    }
 | 
			
		||||
    user.client_kdf_parallelism = data.KdfMemory;
 | 
			
		||||
    user.client_kdf_memory = data.KdfParallelism;
 | 
			
		||||
 | 
			
		||||
    user.set_password(&data.MasterPasswordHash, Some(data.Key), true, None);
 | 
			
		||||
    user.password_hint = password_hint;
 | 
			
		||||
@@ -337,6 +342,8 @@ async fn post_password(
 | 
			
		||||
struct ChangeKdfData {
 | 
			
		||||
    Kdf: i32,
 | 
			
		||||
    KdfIterations: i32,
 | 
			
		||||
    KdfMemory: Option<i32>,
 | 
			
		||||
    KdfParallelism: Option<i32>,
 | 
			
		||||
 | 
			
		||||
    MasterPasswordHash: String,
 | 
			
		||||
    NewMasterPasswordHash: String,
 | 
			
		||||
@@ -352,10 +359,31 @@ async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: D
 | 
			
		||||
        err!("Invalid password")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if data.KdfIterations < 100_000 {
 | 
			
		||||
        err!("KDF iterations lower then 100000 are not allowed.")
 | 
			
		||||
    if data.Kdf == UserKdfType::Pbkdf2 as i32 && data.KdfIterations < 100_000 {
 | 
			
		||||
        err!("PBKDF2 KDF iterations must be at least 100000.")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if data.Kdf == UserKdfType::Argon2id as i32 {
 | 
			
		||||
        if data.KdfIterations < 1 {
 | 
			
		||||
            err!("Argon2 KDF iterations must be at least 1.")
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(m) = data.KdfMemory {
 | 
			
		||||
            if !(15..=1024).contains(&m) {
 | 
			
		||||
                err!("Argon2 memory must be between 15 MB and 1024 MB.")
 | 
			
		||||
            }
 | 
			
		||||
            user.client_kdf_memory = data.KdfMemory;
 | 
			
		||||
        } else {
 | 
			
		||||
            err!("Argon2 memory parameter is required.")
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(p) = data.KdfParallelism {
 | 
			
		||||
            if !(1..=16).contains(&p) {
 | 
			
		||||
                err!("Argon2 parallelism must be between 1 and 16.")
 | 
			
		||||
            }
 | 
			
		||||
            user.client_kdf_parallelism = data.KdfParallelism;
 | 
			
		||||
        } else {
 | 
			
		||||
            err!("Argon2 parallelism parameter is required.")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    user.client_kdf_iter = data.KdfIterations;
 | 
			
		||||
    user.client_kdf_type = data.Kdf;
 | 
			
		||||
    user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None);
 | 
			
		||||
@@ -770,15 +798,22 @@ async fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> Json<Value> {
 | 
			
		||||
pub async fn _prelogin(data: JsonUpcase<PreloginData>, mut conn: DbConn) -> Json<Value> {
 | 
			
		||||
    let data: PreloginData = data.into_inner().data;
 | 
			
		||||
 | 
			
		||||
    let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &mut conn).await {
 | 
			
		||||
        Some(user) => (user.client_kdf_type, user.client_kdf_iter),
 | 
			
		||||
        None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT),
 | 
			
		||||
    let (kdf_type, kdf_iter, kdf_mem, kdf_para) = match User::find_by_mail(&data.Email, &mut conn).await {
 | 
			
		||||
        Some(user) => (user.client_kdf_type, user.client_kdf_iter, user.client_kdf_memory, user.client_kdf_parallelism),
 | 
			
		||||
        None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT, None, None),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Json(json!({
 | 
			
		||||
    let mut result = json!({
 | 
			
		||||
        "Kdf": kdf_type,
 | 
			
		||||
        "KdfIterations": kdf_iter
 | 
			
		||||
    }))
 | 
			
		||||
        "KdfIterations": kdf_iter,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if kdf_type == UserKdfType::Argon2id as i32 {
 | 
			
		||||
        result["KdfMemory"] = Value::Number(kdf_mem.expect("Argon2 memory parameter is required.").into());
 | 
			
		||||
        result["KdfParallelism"] = Value::Number(kdf_para.expect("Argon2 parallelism parameter is required.").into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Json(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// https://github.com/bitwarden/server/blob/master/src/Api/Models/Request/Accounts/SecretVerificationRequestModel.cs
 | 
			
		||||
 
 | 
			
		||||
@@ -620,12 +620,22 @@ async fn takeover_emergency_access(emer_id: String, headers: Headers, mut conn:
 | 
			
		||||
        None => err!("Grantor user not found."),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Ok(Json(json!({
 | 
			
		||||
      "Kdf": grantor_user.client_kdf_type,
 | 
			
		||||
      "KdfIterations": grantor_user.client_kdf_iter,
 | 
			
		||||
      "KeyEncrypted": &emergency_access.key_encrypted,
 | 
			
		||||
      "Object": "emergencyAccessTakeover",
 | 
			
		||||
    })))
 | 
			
		||||
    let mut result = json!({
 | 
			
		||||
        "Kdf": grantor_user.client_kdf_type,
 | 
			
		||||
        "KdfIterations": grantor_user.client_kdf_iter,
 | 
			
		||||
        "KeyEncrypted": &emergency_access.key_encrypted,
 | 
			
		||||
        "Object": "emergencyAccessTakeover",
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if grantor_user.client_kdf_type == UserKdfType::Argon2id as i32 {
 | 
			
		||||
        result["KdfMemory"] =
 | 
			
		||||
            Value::Number(grantor_user.client_kdf_memory.expect("Argon2 memory parameter is required.").into());
 | 
			
		||||
        result["KdfParallelism"] = Value::Number(
 | 
			
		||||
            grantor_user.client_kdf_parallelism.expect("Argon2 parallelism parameter is required.").into(),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(Json(result))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,7 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult {
 | 
			
		||||
    let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec);
 | 
			
		||||
    device.save(conn).await?;
 | 
			
		||||
 | 
			
		||||
    Ok(Json(json!({
 | 
			
		||||
    let mut result = json!({
 | 
			
		||||
        "access_token": access_token,
 | 
			
		||||
        "expires_in": expires_in,
 | 
			
		||||
        "token_type": "Bearer",
 | 
			
		||||
@@ -109,7 +109,16 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult {
 | 
			
		||||
        "ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing
 | 
			
		||||
        "scope": scope,
 | 
			
		||||
        "unofficialServer": true,
 | 
			
		||||
    })))
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if user.client_kdf_type == UserKdfType::Argon2id as i32 {
 | 
			
		||||
        result["KdfMemory"] =
 | 
			
		||||
            Value::Number(user.client_kdf_memory.expect("Argon2 memory parameter is required.").into());
 | 
			
		||||
        result["KdfParallelism"] =
 | 
			
		||||
            Value::Number(user.client_kdf_parallelism.expect("Argon2 parallelism parameter is required.").into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(Json(result))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn _password_login(
 | 
			
		||||
@@ -249,6 +258,13 @@ async fn _password_login(
 | 
			
		||||
        result["TwoFactorToken"] = Value::String(token);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if user.client_kdf_type == UserKdfType::Argon2id as i32 {
 | 
			
		||||
        result["KdfMemory"] =
 | 
			
		||||
            Value::Number(user.client_kdf_memory.expect("Argon2 memory parameter is required.").into());
 | 
			
		||||
        result["KdfParallelism"] =
 | 
			
		||||
            Value::Number(user.client_kdf_parallelism.expect("Argon2 parallelism parameter is required.").into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    info!("User {} logged in successfully. IP: {}", username, ip.ip);
 | 
			
		||||
    Ok(Json(result))
 | 
			
		||||
}
 | 
			
		||||
@@ -333,7 +349,7 @@ async fn _api_key_login(
 | 
			
		||||
 | 
			
		||||
    // Note: No refresh_token is returned. The CLI just repeats the
 | 
			
		||||
    // client_credentials login flow when the existing token expires.
 | 
			
		||||
    Ok(Json(json!({
 | 
			
		||||
    let mut result = json!({
 | 
			
		||||
        "access_token": access_token,
 | 
			
		||||
        "expires_in": expires_in,
 | 
			
		||||
        "token_type": "Bearer",
 | 
			
		||||
@@ -345,7 +361,16 @@ async fn _api_key_login(
 | 
			
		||||
        "ResetMasterPassword": false, // TODO: Same as above
 | 
			
		||||
        "scope": scope,
 | 
			
		||||
        "unofficialServer": true,
 | 
			
		||||
    })))
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if user.client_kdf_type == UserKdfType::Argon2id as i32 {
 | 
			
		||||
        result["KdfMemory"] =
 | 
			
		||||
            Value::Number(user.client_kdf_memory.expect("Argon2 memory parameter is required.").into());
 | 
			
		||||
        result["KdfParallelism"] =
 | 
			
		||||
            Value::Number(user.client_kdf_parallelism.expect("Argon2 parallelism parameter is required.").into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(Json(result))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Retrieves an existing device or creates a new device from ConnectData and the User
 | 
			
		||||
 
 | 
			
		||||
@@ -28,4 +28,4 @@ pub use self::organization::{Organization, UserOrgStatus, UserOrgType, UserOrgan
 | 
			
		||||
pub use self::send::{Send, SendType};
 | 
			
		||||
pub use self::two_factor::{TwoFactor, TwoFactorType};
 | 
			
		||||
pub use self::two_factor_incomplete::TwoFactorIncomplete;
 | 
			
		||||
pub use self::user::{Invitation, User, UserStampException};
 | 
			
		||||
pub use self::user::{Invitation, User, UserKdfType, UserStampException};
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,8 @@ db_object! {
 | 
			
		||||
 | 
			
		||||
        pub client_kdf_type: i32,
 | 
			
		||||
        pub client_kdf_iter: i32,
 | 
			
		||||
        pub client_kdf_memory: Option<i32>,
 | 
			
		||||
        pub client_kdf_parallelism: Option<i32>,
 | 
			
		||||
 | 
			
		||||
        pub api_key: Option<String>,
 | 
			
		||||
 | 
			
		||||
@@ -58,6 +60,11 @@ db_object! {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum UserKdfType {
 | 
			
		||||
    Pbkdf2 = 0,
 | 
			
		||||
    Argon2id = 1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum UserStatus {
 | 
			
		||||
    Enabled = 0,
 | 
			
		||||
    Invited = 1,
 | 
			
		||||
@@ -73,7 +80,7 @@ pub struct UserStampException {
 | 
			
		||||
 | 
			
		||||
/// Local methods
 | 
			
		||||
impl User {
 | 
			
		||||
    pub const CLIENT_KDF_TYPE_DEFAULT: i32 = 0; // PBKDF2: 0
 | 
			
		||||
    pub const CLIENT_KDF_TYPE_DEFAULT: i32 = UserKdfType::Pbkdf2 as i32;
 | 
			
		||||
    pub const CLIENT_KDF_ITER_DEFAULT: i32 = 600_000;
 | 
			
		||||
 | 
			
		||||
    pub fn new(email: String) -> Self {
 | 
			
		||||
@@ -113,6 +120,8 @@ impl User {
 | 
			
		||||
 | 
			
		||||
            client_kdf_type: Self::CLIENT_KDF_TYPE_DEFAULT,
 | 
			
		||||
            client_kdf_iter: Self::CLIENT_KDF_ITER_DEFAULT,
 | 
			
		||||
            client_kdf_memory: None,
 | 
			
		||||
            client_kdf_parallelism: None,
 | 
			
		||||
 | 
			
		||||
            api_key: None,
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -199,6 +199,8 @@ table! {
 | 
			
		||||
        excluded_globals -> Text,
 | 
			
		||||
        client_kdf_type -> Integer,
 | 
			
		||||
        client_kdf_iter -> Integer,
 | 
			
		||||
        client_kdf_memory -> Nullable<Integer>,
 | 
			
		||||
        client_kdf_parallelism -> Nullable<Integer>,
 | 
			
		||||
        api_key -> Nullable<Text>,
 | 
			
		||||
        avatar_color -> Nullable<Text>,
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -199,6 +199,8 @@ table! {
 | 
			
		||||
        excluded_globals -> Text,
 | 
			
		||||
        client_kdf_type -> Integer,
 | 
			
		||||
        client_kdf_iter -> Integer,
 | 
			
		||||
        client_kdf_memory -> Nullable<Integer>,
 | 
			
		||||
        client_kdf_parallelism -> Nullable<Integer>,
 | 
			
		||||
        api_key -> Nullable<Text>,
 | 
			
		||||
        avatar_color -> Nullable<Text>,
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -199,6 +199,8 @@ table! {
 | 
			
		||||
        excluded_globals -> Text,
 | 
			
		||||
        client_kdf_type -> Integer,
 | 
			
		||||
        client_kdf_iter -> Integer,
 | 
			
		||||
        client_kdf_memory -> Nullable<Integer>,
 | 
			
		||||
        client_kdf_parallelism -> Nullable<Integer>,
 | 
			
		||||
        api_key -> Nullable<Text>,
 | 
			
		||||
        avatar_color -> Nullable<Text>,
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@
 | 
			
		||||
// The more key/value pairs there are the more recursion occurs.
 | 
			
		||||
// We want to keep this as low as possible, but not higher then 128.
 | 
			
		||||
// If you go above 128 it will cause rust-analyzer to fail,
 | 
			
		||||
#![recursion_limit = "97"]
 | 
			
		||||
#![recursion_limit = "103"]
 | 
			
		||||
 | 
			
		||||
// When enabled use MiMalloc as malloc instead of the default malloc
 | 
			
		||||
#[cfg(feature = "enable_mimalloc")]
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,7 @@ impl Fairing for AppHeaders {
 | 
			
		||||
                base-uri 'self'; \
 | 
			
		||||
                form-action 'self'; \
 | 
			
		||||
                object-src 'self' blob:; \
 | 
			
		||||
                script-src 'self'; \
 | 
			
		||||
                script-src 'self' 'wasm-unsafe-eval'; \
 | 
			
		||||
                style-src 'self' 'unsafe-inline'; \
 | 
			
		||||
                child-src 'self' https://*.duosecurity.com https://*.duofederal.com; \
 | 
			
		||||
                frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; \
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user