mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-30 21:31:13 +00:00 
			
		
		
		
	Add ip on totp miss
This commit is contained in:
		
					parent
					
						
							
								80d4061d14
							
						
					
				
			
			
				commit
				
					
						0807783388
					
				
			
		
					 2 changed files with 43 additions and 15 deletions
				
			
		|  | @ -4,7 +4,7 @@ use rocket_contrib::json::Json; | |||
| 
 | ||||
| use crate::api::core::two_factor::_generate_recover_code; | ||||
| use crate::api::{EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData}; | ||||
| use crate::auth::Headers; | ||||
| use crate::auth::{ClientIp, Headers}; | ||||
| use crate::crypto; | ||||
| use crate::db::{ | ||||
|     models::{TwoFactor, TwoFactorType}, | ||||
|  | @ -54,7 +54,12 @@ struct EnableAuthenticatorData { | |||
| } | ||||
| 
 | ||||
| #[post("/two-factor/authenticator", data = "<data>")] | ||||
| fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
| fn activate_authenticator( | ||||
|     data: JsonUpcase<EnableAuthenticatorData>, | ||||
|     headers: Headers, | ||||
|     ip: ClientIp, | ||||
|     conn: DbConn, | ||||
| ) -> JsonResult { | ||||
|     let data: EnableAuthenticatorData = data.into_inner().data; | ||||
|     let password_hash = data.MasterPasswordHash; | ||||
|     let key = data.Key; | ||||
|  | @ -77,7 +82,7 @@ fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: He | |||
|     } | ||||
| 
 | ||||
|     // Validate the token provided with the key, and save new twofactor
 | ||||
|     validate_totp_code(&user.uuid, token, &key.to_uppercase(), &conn)?; | ||||
|     validate_totp_code(&user.uuid, token, &key.to_uppercase(), &ip, &conn)?; | ||||
| 
 | ||||
|     _generate_recover_code(&mut user, &conn); | ||||
| 
 | ||||
|  | @ -89,20 +94,31 @@ fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: He | |||
| } | ||||
| 
 | ||||
| #[put("/two-factor/authenticator", data = "<data>")] | ||||
| fn activate_authenticator_put(data: JsonUpcase<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult { | ||||
|     activate_authenticator(data, headers, conn) | ||||
| fn activate_authenticator_put( | ||||
|     data: JsonUpcase<EnableAuthenticatorData>, | ||||
|     headers: Headers, | ||||
|     ip: ClientIp, | ||||
|     conn: DbConn, | ||||
| ) -> JsonResult { | ||||
|     activate_authenticator(data, headers, ip, conn) | ||||
| } | ||||
| 
 | ||||
| pub fn validate_totp_code_str(user_uuid: &str, totp_code: &str, secret: &str, conn: &DbConn) -> EmptyResult { | ||||
| pub fn validate_totp_code_str( | ||||
|     user_uuid: &str, | ||||
|     totp_code: &str, | ||||
|     secret: &str, | ||||
|     ip: &ClientIp, | ||||
|     conn: &DbConn, | ||||
| ) -> EmptyResult { | ||||
|     let totp_code: u64 = match totp_code.parse() { | ||||
|         Ok(code) => code, | ||||
|         _ => err!("TOTP code is not a number"), | ||||
|     }; | ||||
| 
 | ||||
|     validate_totp_code(user_uuid, totp_code, secret, &conn) | ||||
|     validate_totp_code(user_uuid, totp_code, secret, ip, &conn) | ||||
| } | ||||
| 
 | ||||
| pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, conn: &DbConn) -> EmptyResult { | ||||
| pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, ip: &ClientIp, conn: &DbConn) -> EmptyResult { | ||||
|     use oath::{totp_raw_custom_time, HashType}; | ||||
| 
 | ||||
|     let decoded_secret = match BASE32.decode(secret.as_bytes()) { | ||||
|  | @ -144,11 +160,22 @@ pub fn validate_totp_code(user_uuid: &str, totp_code: u64, secret: &str, conn: & | |||
|             twofactor.save(&conn)?; | ||||
|             return Ok(()); | ||||
|         } else if generated == totp_code && time_step <= twofactor.last_used as i64 { | ||||
|             warn!("This or a TOTP code within {} steps back and forward has already been used!", steps); | ||||
|             err!(format!("Invalid TOTP code! Server time: {}", current_time.format("%F %T UTC"))); | ||||
|             warn!( | ||||
|                 "This or a TOTP code within {} steps back and forward has already been used!", | ||||
|                 steps | ||||
|             ); | ||||
|             err!(format!( | ||||
|                 "Invalid TOTP code! Server time: {} IP: {}", | ||||
|                 current_time.format("%F %T UTC"), | ||||
|                 ip.ip | ||||
|             )); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Else no valide code received, deny access
 | ||||
|     err!(format!("Invalid TOTP code! Server time: {}", current_time.format("%F %T UTC"))); | ||||
|     err!(format!( | ||||
|         "Invalid TOTP code! Server time: {} IP: {}", | ||||
|         current_time.format("%F %T UTC"), | ||||
|         ip.ip | ||||
|     )); | ||||
| } | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ fn login(data: Form<ConnectData>, conn: DbConn, ip: ClientIp) -> JsonResult { | |||
|             _check_is_some(&data.device_name, "device_name cannot be blank")?; | ||||
|             _check_is_some(&data.device_type, "device_type cannot be blank")?; | ||||
| 
 | ||||
|             _password_login(data, conn, ip) | ||||
|             _password_login(data, conn, &ip) | ||||
|         } | ||||
|         t => err!("Invalid type", t), | ||||
|     } | ||||
|  | @ -71,7 +71,7 @@ fn _refresh_login(data: ConnectData, conn: DbConn) -> JsonResult { | |||
|     }))) | ||||
| } | ||||
| 
 | ||||
| fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult { | ||||
| fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult { | ||||
|     // Validate scope
 | ||||
|     let scope = data.scope.as_ref().unwrap(); | ||||
|     if scope != "api offline_access" { | ||||
|  | @ -127,7 +127,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult | |||
| 
 | ||||
|     let (mut device, new_device) = get_device(&data, &conn, &user); | ||||
| 
 | ||||
|     let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, &conn)?; | ||||
|     let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, &ip, &conn)?; | ||||
| 
 | ||||
|     if CONFIG.mail_enabled() && new_device { | ||||
|         if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &device.updated_at, &device.name) { | ||||
|  | @ -197,6 +197,7 @@ fn twofactor_auth( | |||
|     user_uuid: &str, | ||||
|     data: &ConnectData, | ||||
|     device: &mut Device, | ||||
|     ip: &ClientIp, | ||||
|     conn: &DbConn, | ||||
| ) -> ApiResult<Option<String>> { | ||||
|     let twofactors = TwoFactor::find_by_user(user_uuid, conn); | ||||
|  | @ -225,7 +226,7 @@ fn twofactor_auth( | |||
|     let mut remember = data.two_factor_remember.unwrap_or(0); | ||||
| 
 | ||||
|     match TwoFactorType::from_i32(selected_id) { | ||||
|         Some(TwoFactorType::Authenticator) => _tf::authenticator::validate_totp_code_str(user_uuid, twofactor_code, &selected_data?, conn)?, | ||||
|         Some(TwoFactorType::Authenticator) => _tf::authenticator::validate_totp_code_str(user_uuid, twofactor_code, &selected_data?, ip, conn)?, | ||||
|         Some(TwoFactorType::U2f) => _tf::u2f::validate_u2f_login(user_uuid, twofactor_code, conn)?, | ||||
|         Some(TwoFactorType::YubiKey) => _tf::yubikey::validate_yubikey_login(twofactor_code, &selected_data?)?, | ||||
|         Some(TwoFactorType::Duo) => _tf::duo::validate_duo_login(data.username.as_ref().unwrap(), twofactor_code, conn)?, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue