diff --git a/.gitignore b/.gitignore index 345a792..2955c66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target -test.sqlite +db.sqlite diff --git a/Cargo.lock b/Cargo.lock index c7c0e25..8144eb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bytes", "futures-core", "futures-sink", @@ -31,7 +31,7 @@ dependencies = [ "actix-utils", "ahash 0.8.3", "base64", - "bitflags", + "bitflags 1.3.2", "brotli", "bytes", "bytestring", @@ -278,14 +278,24 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "baseauth" version = "0.1.0" dependencies = [ "actix-web", "anyhow", + "env_logger", "libinjection", + "log", + "pbkdf2", "serde", + "sha2", "sqlite", "tokio", ] @@ -296,7 +306,7 @@ version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -318,6 +328,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "block-buffer" version = "0.10.4" @@ -471,6 +487,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -488,6 +505,40 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "flate2" version = "1.0.26" @@ -576,7 +627,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libgit2-sys", "log", @@ -622,6 +673,15 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "0.2.9" @@ -645,6 +705,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "idna" version = "0.4.0" @@ -665,6 +731,17 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + [[package]] name = "itoa" version = "1.0.9" @@ -764,6 +841,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + [[package]] name = "local-channel" version = "0.1.3" @@ -913,12 +996,35 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -1009,7 +1115,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1062,6 +1168,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.15" @@ -1134,6 +1253,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.1.0" @@ -1214,6 +1344,12 @@ dependencies = [ "sqlite3-src", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -1236,6 +1372,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + [[package]] name = "time" version = "0.3.25" @@ -1427,6 +1572,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index b16e90b..33737fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,7 @@ actix-web = "4" serde = { version = "1.0.183", features = ["derive"] } sqlite = "0.31.0" libinjection = "0.3.2" +sha2 = "0.10.2" +pbkdf2 = { version = "0.12", features = ["simple"] } +env_logger = "0.10" +log = "0.4" diff --git a/src/api.rs b/src/api.rs index 41d1fc4..a39d52b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -4,6 +4,7 @@ use serde::Deserialize; use tokio::sync::{mpsc, oneshot}; use crate::call::{Call, Response, ResponseAuthenticate, ResponseRegister}; +use log::{error, info}; struct ApiState { tx: mpsc::Sender<(Call, oneshot::Sender>)>, @@ -28,7 +29,7 @@ async fn authenticate( ) -> impl Responder { let (tx, rx) = oneshot::channel(); - if let Err(_) = data + if let Err(e) = data .tx .send(( Call::Authenticate(body.username.clone(), body.password.clone()), @@ -36,6 +37,7 @@ async fn authenticate( )) .await { + error!("While handling `/authenticate` call: {:?}", e); return HttpResponse::InternalServerError().finish(); } @@ -47,9 +49,15 @@ async fn authenticate( ResponseAuthenticate::WrongPassword => HttpResponse::Unauthorized().finish(), ResponseAuthenticate::Rejected => HttpResponse::Forbidden().finish(), }, - _ => HttpResponse::InternalServerError().finish(), + _ => { + error!("While handling `/authenticate` call: Invalid response type for this call"); + HttpResponse::InternalServerError().finish() + } }, - _ => HttpResponse::InternalServerError().finish(), + e => { + error!("While handling `/authenticate` call: {:?}", e); + HttpResponse::InternalServerError().finish() + } } } @@ -68,7 +76,7 @@ struct RegisterData { async fn register(data: web::Data, body: web::Json) -> impl Responder { let (tx, rx) = oneshot::channel(); - if let Err(_) = data + if let Err(e) = data .tx .send(( Call::Register(body.username.clone(), body.password.clone()), @@ -76,6 +84,7 @@ async fn register(data: web::Data, body: web::Json) -> i )) .await { + error!("While handling `/register` call: {:?}", e); return HttpResponse::InternalServerError().finish(); } @@ -86,9 +95,15 @@ async fn register(data: web::Data, body: web::Json) -> i ResponseRegister::NameAlreadyInUse => HttpResponse::Conflict().finish(), ResponseRegister::Rejected => HttpResponse::Forbidden().finish(), }, - _ => HttpResponse::InternalServerError().finish(), + _ => { + error!("While handling `/register` call: Invalid response type for this call"); + HttpResponse::InternalServerError().finish() + } }, - _ => HttpResponse::InternalServerError().finish(), + e => { + error!("While handling `/register` call: {:?}", e); + HttpResponse::InternalServerError().finish() + } } } @@ -106,5 +121,6 @@ pub async fn start_worker( .bind(("127.0.0.1", port))? .run(), ); + info!("HTTP server started"); Ok(()) } diff --git a/src/backend.rs b/src/backend.rs index 3d36167..5004610 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,8 +1,14 @@ use anyhow::Result; use libinjection::sqli; +use log::{info, warn}; use crate::call::{Response, ResponseAuthenticate, ResponseDelete, ResponseRegister}; +use pbkdf2::{ + password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, + Pbkdf2, +}; + pub struct Backend { db: sqlite::Connection, } @@ -15,15 +21,47 @@ fn is_sql_injection(string: &String) -> bool { } impl Backend { + fn user_exists(&mut self, username: &String) -> Result { + let mut statement = self.db.prepare(format!( + "SELECT id from Accounts WHERE username='{}';", + username + ))?; + + for _ in statement.iter() { + return Ok(true); + } + Ok(false) + } + pub fn register(&mut self, username: &String, password: &String) -> Result { if is_sql_injection(username) { + warn!( + "Rejecting registration due to possible SQL injection in username: `{}`", + username + ); return Ok(Response::Register(ResponseRegister::Rejected)); } - println!( - " Registered Account: (Username: '{}' | Password: '{}')", - username, password - ); + if self.user_exists(username)? { + warn!( + "Rejecting registration because the username `{}` is already in use", + username + ); + return Ok(Response::Register(ResponseRegister::NameAlreadyInUse)); + } + + let salt = SaltString::generate(&mut OsRng); + let hash = Pbkdf2 + .hash_password(password.as_bytes(), &salt) + .map_err(|_| anyhow::Error::msg("Failed to hash the password"))? + .to_string(); + + self.db.execute(format!( + "INSERT INTO Accounts (username, salt, password) VALUES ('{}', '{}', '{}');", + username, salt, hash + ))?; + + info!("Registered account: {}", username); Ok(Response::Register(ResponseRegister::Success)) } @@ -54,11 +92,11 @@ impl Backend { let conn = sqlite::open(database)?; conn.execute( - "CREATE TABLE IF NOT EXISTS BaseAuth ( - id INTEGER, - username TEXT, - salt TEXT, - password TEXT, + "CREATE TABLE IF NOT EXISTS Accounts ( + id INTEGER NOT NULL, + username TEXT NOT NULL, + salt TEXT NOT NULL, + password TEXT NOT NULL, PRIMARY KEY(id) );", )?; diff --git a/src/call.rs b/src/call.rs index c10691c..cbe8f23 100644 --- a/src/call.rs +++ b/src/call.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use log::info; use tokio::sync::{mpsc, oneshot}; use crate::backend::Backend; @@ -69,6 +70,7 @@ pub async fn start_worker( panic!("{e}"); }; }); + info!("Event handler thread started"); Ok(tx) } diff --git a/src/main.rs b/src/main.rs index ed125e4..94bd0f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,14 @@ mod backend; mod call; use anyhow::Result; +use log::info; #[tokio::main] async fn main() -> Result<()> { - println!("Starting BaseAuth server v0.1"); + env_logger::init(); + info!("Starting BaseAuth server v0.1"); - let backend = backend::Backend::new(&std::path::Path::new("test.sqlite"))?; + let backend = backend::Backend::new(&std::path::Path::new("db.sqlite"))?; let tx = call::start_worker(backend).await?; api::start_worker(8080, tx).await?;