diff --git a/src/database/gree.rs b/src/database/gree.rs index ea39ece..6fc400c 100644 --- a/src/database/gree.rs +++ b/src/database/gree.rs @@ -60,9 +60,20 @@ fn vacuum_database() { DATABASE.lock_and_exec("VACUUM", params!()); } +pub fn is_registered(uuid: &str) -> bool { + match DATABASE.lock_and_select("SELECT cert FROM users WHERE uuid=?1;", params!(uuid)) { + Ok(cert) => cert != "none", + Err(_) => false + } +} + fn verify_signature(signature: &[u8], message: &[u8], public_key: &str) -> bool { - let pem = pem::parse(public_key).unwrap(); - let public_key = RsaPublicKey::from_public_key_der(&pem.contents()).unwrap(); + let Ok(pem) = pem::parse(public_key) else { + return false; + }; + let Ok(public_key) = RsaPublicKey::from_public_key_der(&pem.contents()) else { + return false; + }; let digest = Sha1::digest(message); public_key @@ -80,7 +91,9 @@ pub fn get_uuid(headers: &HeaderMap, body: &str) -> String { return String::new(); } - let cert = DATABASE.lock_and_select("SELECT cert FROM users WHERE user_id=?1;", params!(uid)).unwrap(); + let Ok(cert) = DATABASE.lock_and_select("SELECT cert FROM users WHERE user_id=?1;", params!(uid)) else { + return String::new(); + }; let data = format!("{}{}{}{}{}", uid, "sk1bdzb310n0s9tl", version, timestamp, body); let encoded = general_purpose::STANDARD.encode(data.as_bytes()); diff --git a/src/router.rs b/src/router.rs index 3f5eb82..b4f5234 100644 --- a/src/router.rs +++ b/src/router.rs @@ -50,6 +50,27 @@ pub struct Body(pub JsonValue); pub struct Login(pub String); +struct SessionError(HttpRequest); + +impl std::fmt::Debug for SessionError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "invalid session for uid {}", global::get_uid(self.0.headers())) + } +} + +impl std::fmt::Display for SessionError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl actix_web::ResponseError for SessionError { + fn error_response(&self) -> HttpResponse { + println!("Rejecting request from uid {}: bad session", global::get_uid(self.0.headers())); + global::api_error(&self.0, global::RESULT_SESSION) + } +} + pub struct Session { pub key: String, pub body: JsonValue, @@ -93,9 +114,14 @@ impl FromRequest for Login { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { let headers = req.headers().clone(); - let fut = String::from_request(req, payload); + let req = req.clone(); + let fut = String::from_request(&req, payload); async move { - Ok(Login(global::get_login(&headers, &encryption::decrypt_packet(&fut.await?).unwrap()))) + let key = global::get_login(&headers, &encryption::decrypt_packet(&fut.await?).unwrap()); + if key.is_empty() { + return Err(SessionError(req).into()); + } + Ok(Login(key)) }.boxed_local() } } @@ -106,10 +132,14 @@ impl FromRequest for Session { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { let headers = req.headers().clone(); - let fut = String::from_request(req, payload); + let req = req.clone(); + let fut = String::from_request(&req, payload); async move { let body = encryption::decrypt_packet(&fut.await?).unwrap(); let key = global::get_login(&headers, &body); + if key.is_empty() { + return Err(SessionError(req).into()); + } Ok(Session { key, body: jzon::parse(&body).unwrap() }) }.boxed_local() } diff --git a/src/router/global.rs b/src/router/global.rs index 3aa9967..b502ac4 100644 --- a/src/router/global.rs +++ b/src/router/global.rs @@ -118,6 +118,8 @@ pub const RESULT_GAME_VERSION_UPDATED: i32 = 12; pub const RESULT_RESOURCE_UPDATED: i32 = 13; +pub const RESULT_SESSION: i32 = 14; + pub const PROTOCOL_HEADER: &str = "X-Protocol-Version"; pub fn client_protocol_version(req: &HttpRequest) -> u32 { @@ -230,11 +232,7 @@ pub fn get_login(headers: &HeaderMap, body: &str) -> String { Some(token) => { token }, - None => { - let rv = gree::get_uuid(headers, body); - assert!(rv != String::new()); - rv - }, + None => gree::get_uuid(headers, body), } } diff --git a/src/router/gree.rs b/src/router/gree.rs index 5961481..41f5482 100644 --- a/src/router/gree.rs +++ b/src/router/gree.rs @@ -16,6 +16,7 @@ use crate::database::gree::*; const APP_ID: &str = "232610769078541"; const SRC_APP_ID: &str = "100900301"; const HMAC_SECRET_HEX: &str = "6438663638653238346566646636306262616563326432323563306366643432"; +const ERROR_MIGRATED_DEVICE: i32 = 20; struct RequireGreeAuth; @@ -84,7 +85,7 @@ fn get_uid(req: &HttpRequest) -> String { req.headers() .get("Authorization") .and_then(|h| h.to_str().ok()) - .and_then(|auth| auth.split(",xoauth_requestor_id=\"").nth(1)) + .and_then(|auth| auth.split("xoauth_requestor_id=\"").nth(1)) .and_then(|s| s.split('"').next()) .unwrap_or("") .to_string() @@ -95,13 +96,13 @@ fn send(req: HttpRequest, resp: JsonValue) -> impl Responder { jzon::stringify(resp) } -async fn not_found() -> impl Responder { +async fn not_found(req: HttpRequest) -> impl Responder { let resp = object!{ code: 10001, message: "Not Found", result: "NG" }; - jzon::stringify(resp) + send(req, resp) } async fn initialize(req: HttpRequest, body: String) -> impl Responder { @@ -119,8 +120,17 @@ async fn initialize(req: HttpRequest, body: String) -> impl Responder { } async fn authorize(req: HttpRequest, _body: String) -> impl Responder { - let resp = object!{ - result: "OK" + let resp = if is_registered(&get_uid(&req)) { + object!{ + result: "OK" + } + } else { + println!("Unregistered device authorizing: {}", get_uid(&req)); + object!{ + result: "NG", + code: ERROR_MIGRATED_DEVICE, + message: "Device is not registered" + } }; send(req, resp) diff --git a/src/router/tools/guest.rs b/src/router/tools/guest.rs index 96fc50b..fb7d6a9 100644 --- a/src/router/tools/guest.rs +++ b/src/router/tools/guest.rs @@ -65,15 +65,23 @@ pub enum UserView { Ranking, } +const DEFAULT_CARD: i64 = 10010001; + fn proxy_card_id(id: i64) -> i64 { let prefix = id / 10000; if prefix < 10000 { - id - } else if prefix < 14000 { + return id; + } + let rv = if prefix < 14000 { (prefix - 9000) * 10000 + 1 } else { - 10010001 + DEFAULT_CARD + }; + // Not every prefix has a real character behind it + if crate::router::databases::CARD_LIST[rv.to_string()].is_empty() { + return DEFAULT_CARD; } + rv } pub fn proxy_user_cards(user: &mut JsonValue) {