diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index ef1f7c764d5..9419c2e22bf 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -111,6 +111,18 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
if (CONFIG_GET(flag/log_access))
WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]")
+/**
+ * Writes to a special log file if the log_suspicious_login config flag is set,
+ * which is intended to contain all logins that failed under suspicious circumstances.
+ *
+ * Mirrors this log entry to log_access as well, so this proc doesn't need to be used
+ * alongside log_access and can replace it where appropriate.
+ */
+/proc/log_suspicious_login(text)
+ if (CONFIG_GET(flag/log_suspicious_login))
+ WRITE_LOG(GLOB.world_suspicious_login_log, "SUSPICIOUS_ACCESS: [text]")
+ log_access(text)
+
/proc/log_law(text)
if (CONFIG_GET(flag/log_law))
WRITE_LOG(GLOB.world_game_log, "LAW: [text]")
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index d6add6a3423..2d4b9f65dbf 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -2,6 +2,9 @@ GLOBAL_VAR(log_directory)
GLOBAL_PROTECT(log_directory)
GLOBAL_VAR(world_game_log)
GLOBAL_PROTECT(world_game_log)
+/// Log associated with [/proc/log_suspicious_login()] - Intended to hold all logins that failed due to suspicious circumstances such as ban detection, CID randomisation etc.
+GLOBAL_VAR(world_suspicious_login_log)
+GLOBAL_PROTECT(world_suspicious_login_log)
GLOBAL_VAR(world_runtime_log)
GLOBAL_PROTECT(world_runtime_log)
GLOBAL_VAR(world_qdel_log)
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index c1f0d817f8e..583660220d5 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -52,6 +52,9 @@
/datum/config_entry/flag/log_access // log login/logout
+/// Config entry which special logging of failed logins under suspicious circumstances.
+/datum/config_entry/flag/log_suspicious_login
+
/datum/config_entry/flag/log_say // log client say
/datum/config_entry/flag/log_admin // log admin actions
diff --git a/code/game/world.dm b/code/game/world.dm
index c07299f38b9..986c55d45ed 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -119,6 +119,7 @@ GLOBAL_VAR(restart_counter)
GLOB.picture_log_directory = "data/picture_logs/[override_dir]"
GLOB.world_game_log = "[GLOB.log_directory]/game.log"
+ GLOB.world_suspicious_login_log = "[GLOB.log_directory]/suspicious_logins.log"
GLOB.world_mecha_log = "[GLOB.log_directory]/mecha.log"
GLOB.world_virus_log = "[GLOB.log_directory]/virus.log"
GLOB.world_cloning_log = "[GLOB.log_directory]/cloning.log"
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 02790fe08da..351cdff676e 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -92,7 +92,7 @@
The ban reason is: [i["reason"]]
This ban (BanID #[i["id"]]) was applied by [i["admin_key"]] on [i["bantime"]] during round ID [i["round_id"]].
[expires]"}
- log_access("Failed Login: [key] [computer_id] [address] - Banned (#[i["id"]])")
+ log_suspicious_login("Failed Login: [key] [computer_id] [address] - Banned (#[i["id"]])")
return list("reason"="Banned","desc"="[desc]")
if (admin)
if (GLOB.directory[ckey])
@@ -225,7 +225,7 @@
var/desc = "\nReason:(StickyBan) You, or another user of this computer or connection ([bannedckey]) is banned from playing here. The ban reason is:\n[ban["message"]]\nThis ban was applied by [ban["admin"]]\nThis is a BanEvasion Detection System ban, if you think this ban is a mistake, please wait EXACTLY 6 seconds, then try again before filing an appeal.\n"
. = list("reason" = "Stickyban", "desc" = desc)
- log_access("Failed Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]")
+ log_suspicious_login("Failed Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]")
return .
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 9dfec76cb45..87c886f758c 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -266,21 +266,21 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
var/list/joined_players = list()
for(var/player_ckey in GLOB.joined_player_list)
joined_players[player_ckey] = 1
-
+
for(var/joined_player_ckey in (GLOB.directory | joined_players))
if (!joined_player_ckey || joined_player_ckey == ckey)
continue
-
+
var/datum/preferences/joined_player_preferences = GLOB.preferences_datums[joined_player_ckey]
if(!joined_player_preferences)
continue //this shouldn't happen.
-
+
var/client/C = GLOB.directory[joined_player_ckey]
var/in_round = ""
if (joined_players[joined_player_ckey])
in_round = " who has played in the current round"
var/message_type = "Notice"
-
+
var/matches
if(joined_player_preferences.last_ip == address)
matches += "IP ([address])"
@@ -291,7 +291,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
message_type = "MULTIKEY"
matches += "Computer ID ([computer_id])"
alert_mob_dupe_login = TRUE
-
+
if(matches)
if(C)
message_admins(span_danger("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [key_name_admin(C)][in_round]."))
@@ -320,7 +320,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (!byond_build || byond_build < 1386)
message_admins(span_adminnotice("[key_name(src)] has been detected as spoofing their byond version. Connection rejected."))
add_system_note("Spoofed-Byond-Version", "Detected as using a spoofed byond version.")
- log_access("Failed Login: [key] - Spoofed byond version")
+ log_suspicious_login("Failed Login: [key] - Spoofed byond version")
qdel(src)
if (num2text(byond_build) in GLOB.blacklisted_builds)
@@ -351,7 +351,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
spawn(0.5 SECONDS) //needs to run during world init, do not convert to add timer
alert(mob, dupe_login_message) //players get banned if they don't see this message, do not convert to tgui_alert (or even tg_alert) please.
to_chat(mob, span_danger(dupe_login_message))
-
+
connection_time = world.time
connection_realtime = world.realtime
@@ -765,7 +765,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
cidcheck_failedckeys[ckey] = TRUE
note_randomizer_user()
- log_access("Failed Login: [key] [computer_id] [address] - CID randomizer confirmed (oldcid: [oldcid])")
+ log_suspicious_login("Failed Login: [key] [computer_id] [address] - CID randomizer confirmed (oldcid: [oldcid])")
qdel(src)
return TRUE
@@ -793,7 +793,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
/client/proc/cid_check_reconnect()
var/token = md5("[rand(0,9999)][world.time][rand(0,9999)][ckey][rand(0,9999)][address][rand(0,9999)][computer_id][rand(0,9999)]")
. = token
- log_access("Failed Login: [key] [computer_id] [address] - CID randomizer check")
+ log_suspicious_login("Failed Login: [key] [computer_id] [address] - CID randomizer check")
var/url = winget(src, null, "url")
//special javascript to make them reconnect under a new window.
src << browse({"byond://[url]?token=[token]"}, "border=0;titlebar=0;size=1x1;window=redirect")
diff --git a/config/config.txt b/config/config.txt
index 23787ed4604..b109a26bd71 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -93,6 +93,9 @@ LOG_ADMINCHAT
## log client access (logon/logoff)
LOG_ACCESS
+## Enables log entries for logins that failed due to suspicious circumstances (banned player, CID randomiser, spoofed BYOND version, etc.) to a dedicated file.
+LOG_SUSPICIOUS_LOGIN
+
## log game actions (start of round, results, etc.)
LOG_GAME