From 0807783388343a8ab4035e50f0a542fadfe03423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Thu, 14 May 2020 00:19:50 +0200 Subject: [PATCH] Add ip on totp miss --- src/api/core/two_factor/authenticator.rs | 49 ++++++++++++++++++------ src/api/identity.rs | 9 +++-- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs index d9e8f494..6b651375 100644 --- a/src/api/core/two_factor/authenticator.rs +++ b/src/api/core/two_factor/authenticator.rs @@ -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 = "")] -fn activate_authenticator(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { +fn activate_authenticator( + data: JsonUpcase, + 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, 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, headers: He } #[put("/two-factor/authenticator", data = "")] -fn activate_authenticator_put(data: JsonUpcase, headers: Headers, conn: DbConn) -> JsonResult { - activate_authenticator(data, headers, conn) +fn activate_authenticator_put( + data: JsonUpcase, + 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 + )); } diff --git a/src/api/identity.rs b/src/api/identity.rs index a3032722..2aabfe95 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -38,7 +38,7 @@ fn login(data: Form, 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> { 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)?,