mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
Spiritual successor and extension to #17798, an almost entire rebuild of the SQL ban system backend and interface. Bantypes are removed per #8584 and #6174. All bans are now 'role bans', server bans are when a ban's role is server. Admin bans are a column, meaning it's possible to ban admins from jobs. Bans now have only an expiry datetime, duration is calculated from this when queried. unbanned column is removed as it's superfluous, checking unban status is now done through checking unban_datetime. unban_round_id column added. Each ip and computerid columns rearranged so ip is always first, like in other tables. Bans now permit a null ckey, ip and computerid. Ban checking is split into two procs now is_banned_from() does a check if a ckey is banned from one or more roles and returns true or false. This effectively replaces jobban_isbanned() used in simple if() statements. If connected a client's ban cache is checked rather than querying the DB. This makes it possible for a client connected to two or more servers to ignore any bans made on one server until their ban cache is rebuilt on the others. Could be avoided with cross-server calls to update ban caches or just the removal of the ban cache but as is I've done neither since I think it's enough of an edge case to not be worth it. The second proc is is_banned_from_with_details(), this queries the DB for a role ban on a player's ckey, ip or CID and returns the details. This replaces direct queries in IsBanned.dm and the preferences menu. The legacy ban system is removed. The interfaces for banning, unbanning and editing bans have been remade to require less clicking and easier simultaneous operations. The banning and jobban panel are combined. They also store player connection details when opened so a client disconnecting no longer stops a ban being placed. New banning panel: Key, IP and CID can all be toggled to allow excluding them from a ban. Checking Use IP and CID from last connection lets you enter only a ckey and have the DB fill these fields in for you, if possible. Temporary bans have a drop-menu which lets you select between seconds, minutes, hours, days, weeks, months and years so you don't need to calculate how many minutes a long ban would be. The ban is still converted into minutes on the DB however. Checking any of the head roles will check both of the boxes for you. The red role box indicates there is already a ban on that role for this ckey. You can apply additional role bans to stack them. New unbanning panel: Unbanning panel is now separate from the banning panel but otherwise functionally the same. Ban editing panel: Actually just a modified banning panel, all the features from it work the same here. You can now edit almost all parameters of a ban instead of just the reason. You can't edit severity as it's not really part of the ban. The panels have been tested but I've not been able to get my local server to be accessible so ban functionality isn't properly confirmed. Plenty of testing will be required as I'd rather not break bans. cl admin: Ban interface rework. The banning and unbanning panels have received a new design which is easier to use and allows multiple role bans to be made at once. prefix: Ban search and unbanning moved to unbanning panel, which is now a separate panel to the old banning panel. /cl
214 lines
11 KiB
Plaintext
214 lines
11 KiB
Plaintext
//Blocks an attempt to connect before even creating our client datum thing.
|
|
|
|
//How many new ckey matches before we revert the stickyban to it's roundstart state
|
|
//These are exclusive, so once it goes over one of these numbers, it reverts the ban
|
|
#define STICKYBAN_MAX_MATCHES 20
|
|
#define STICKYBAN_MAX_EXISTING_USER_MATCHES 5 //ie, users who were connected before the ban triggered
|
|
#define STICKYBAN_MAX_ADMIN_MATCHES 2
|
|
|
|
/world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE)
|
|
if (!key || !address || !computer_id)
|
|
if(real_bans_only)
|
|
return FALSE
|
|
log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
|
|
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)")
|
|
|
|
if (text2num(computer_id) == 2147483647) //this cid causes stickybans to go haywire
|
|
log_access("Failed Login (invalid cid): [key] [address]-[computer_id]")
|
|
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided an invalid Computer ID.)")
|
|
var/admin = 0
|
|
var/ckey = ckey(key)
|
|
if(GLOB.admin_datums[ckey] || GLOB.deadmins[ckey])
|
|
admin = 1
|
|
|
|
//Whitelist
|
|
if(CONFIG_GET(flag/usewhitelist))
|
|
if(!check_whitelist(ckey))
|
|
if (admin)
|
|
log_admin("The admin [key] has been allowed to bypass the whitelist")
|
|
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass the whitelist</span>")
|
|
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass the whitelist</span>")
|
|
else
|
|
log_access("Failed Login: [key] - Not on whitelist")
|
|
return list("reason"="whitelist", "desc" = "\nReason: You are not on the white list for this server")
|
|
|
|
//Guest Checking
|
|
if(!real_bans_only && IsGuestKey(key))
|
|
if (CONFIG_GET(flag/guest_ban))
|
|
log_access("Failed Login: [key] - Guests not allowed")
|
|
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
|
|
if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect())
|
|
log_access("Failed Login: [key] - Guests not allowed during panic bunker")
|
|
return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.")
|
|
|
|
//Population Cap Checking
|
|
var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
|
|
if(!real_bans_only && extreme_popcap && living_player_count() >= extreme_popcap && !admin)
|
|
log_access("Failed Login: [key] - Population cap reached")
|
|
return list("reason"="popcap", "desc"= "\nReason: [CONFIG_GET(string/extreme_popcap_message)]")
|
|
if(CONFIG_GET(flag/sql_enabled))
|
|
if(!SSdbcore.Connect())
|
|
var/msg = "Ban database connection failure. Key [ckey] not checked"
|
|
log_world(msg)
|
|
message_admins(msg)
|
|
<<<<<<< HEAD
|
|
return
|
|
|
|
var/ipquery = ""
|
|
var/cidquery = ""
|
|
if(address)
|
|
ipquery = " OR ip = INET_ATON('[address]') "
|
|
|
|
if(computer_id)
|
|
cidquery = " OR computerid = '[computer_id]' "
|
|
|
|
var/datum/DBQuery/query_ban_check = SSdbcore.NewQuery("SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), reason, expiration_time, duration, bantime, bantype, id, round_id FROM [format_table_name("ban")] WHERE (ckey = '[ckey]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND (isnull(unbanned) OR unbanned = 0)") // yogs - Yog Bans
|
|
if(!query_ban_check.Execute(async = TRUE))
|
|
qdel(query_ban_check)
|
|
return
|
|
while(query_ban_check.NextRow())
|
|
var/pkey = query_ban_check.item[1]
|
|
var/akey = query_ban_check.item[2]
|
|
var/reason = query_ban_check.item[3]
|
|
var/expiration = query_ban_check.item[4]
|
|
var/duration = text2num(query_ban_check.item[5])
|
|
var/bantime = query_ban_check.item[6]
|
|
var/bantype = query_ban_check.item[7]
|
|
var/banid = query_ban_check.item[8]
|
|
var/ban_round_id = query_ban_check.item[9]
|
|
if (bantype == "ADMIN_PERMABAN" || bantype == "ADMIN_TEMPBAN")
|
|
//admin bans MUST match on ckey to prevent cid-spoofing attacks
|
|
// as well as dynamic ip abuse
|
|
if (ckey(pkey) != ckey)
|
|
continue
|
|
if (admin)
|
|
if (bantype == "ADMIN_PERMABAN" || bantype == "ADMIN_TEMPBAN")
|
|
log_admin("The admin [key] is admin banned (#[banid]), and has been disallowed access")
|
|
message_admins("<span class='adminnotice'>The admin [key] is admin banned (#[banid]), and has been disallowed access</span>")
|
|
else
|
|
log_admin("The admin [key] has been allowed to bypass a matching ban on [pkey] (#[banid])")
|
|
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching ban on [pkey] (#[banid])</span>")
|
|
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching ban on [pkey] (#[banid])</span>")
|
|
continue
|
|
var/expires = ""
|
|
if(duration > 0)
|
|
expires = "\nThe ban is for [DisplayTimeText(duration MINUTES)] and expires on [expiration] (server time)." //convert from minutes into deciseconds to display the amount of time in days, hours, minutes, ect.
|
|
else
|
|
expires = "\nThis is a permanent ban."
|
|
|
|
var/desc = "\nReason: You, or another user of this computer or connection ([pkey]) is banned from playing here. The ban reason is:\n[reason]\nThis ban (BanID #[banid]) was applied by [akey] on [bantime] during round ID [ban_round_id]. [expires]. If you wish to appeal this ban please use the keyword 'assistantgreytide' to register an account on the forums." //yogs
|
|
|
|
. = list("reason"="[bantype]", "desc"="[desc]")
|
|
|
|
|
|
log_access("Failed Login: [key] [computer_id] [address] - Banned (#[banid]) [.["reason"]]")
|
|
qdel(query_ban_check)
|
|
return .
|
|
qdel(query_ban_check)
|
|
|
|
=======
|
|
else
|
|
var/list/ban_details = is_banned_from_with_details(ckey, address, computer_id, "Server")
|
|
for(var/i in ban_details)
|
|
if(admin)
|
|
if(text2num(i["applies_to_admins"]))
|
|
var/msg = "Admin [key] is admin banned, and has been disallowed access."
|
|
log_admin(msg)
|
|
message_admins(msg)
|
|
else
|
|
var/msg = "Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]]."
|
|
log_admin(msg)
|
|
message_admins(msg)
|
|
addclientmessage(ckey,"<span class='adminnotice'>Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]].</span>")
|
|
continue
|
|
var/expires = "This is a permanent ban."
|
|
if(i["expiration_time"])
|
|
expires = " The ban is for [DisplayTimeText(text2num(i["duration"]) MINUTES)] and expires on [i["expiration_time"]] (server time)."
|
|
var/desc = {"You, or another user of this computer or connection ([i["key"]]) is banned from playing here.
|
|
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"]])")
|
|
return list("reason"="Banned","desc"="[desc]")
|
|
>>>>>>> 8a66665e95... Ban system and interface update (#41176)
|
|
var/list/ban = ..() //default pager ban stuff
|
|
if (ban)
|
|
var/bannedckey = "ERROR"
|
|
if (ban["ckey"])
|
|
bannedckey = ban["ckey"]
|
|
|
|
var/newmatch = FALSE
|
|
var/client/C = GLOB.directory[ckey]
|
|
var/cachedban = SSstickyban.cache[bannedckey]
|
|
|
|
//rogue ban in the process of being reverted.
|
|
if (cachedban && cachedban["reverting"])
|
|
return null
|
|
|
|
if (cachedban && ckey != bannedckey)
|
|
newmatch = TRUE
|
|
if (cachedban["keys"])
|
|
if (cachedban["keys"][ckey])
|
|
newmatch = FALSE
|
|
if (cachedban["matches_this_round"][ckey])
|
|
newmatch = FALSE
|
|
|
|
if (newmatch && cachedban)
|
|
var/list/newmatches = cachedban["matches_this_round"]
|
|
var/list/newmatches_connected = cachedban["existing_user_matches_this_round"]
|
|
var/list/newmatches_admin = cachedban["admin_matches_this_round"]
|
|
|
|
newmatches[ckey] = ckey
|
|
if (C)
|
|
newmatches_connected[ckey] = ckey
|
|
if (admin)
|
|
newmatches_admin[ckey] = ckey
|
|
|
|
if (\
|
|
newmatches.len > STICKYBAN_MAX_MATCHES || \
|
|
newmatches_connected.len > STICKYBAN_MAX_EXISTING_USER_MATCHES || \
|
|
newmatches_admin.len > STICKYBAN_MAX_ADMIN_MATCHES \
|
|
)
|
|
if (cachedban["reverting"])
|
|
return null
|
|
cachedban["reverting"] = TRUE
|
|
|
|
world.SetConfig("ban", bannedckey, null)
|
|
|
|
log_game("Stickyban on [bannedckey] detected as rogue, reverting to its roundstart state")
|
|
message_admins("Stickyban on [bannedckey] detected as rogue, reverting to its roundstart state")
|
|
//do not convert to timer.
|
|
spawn (5)
|
|
world.SetConfig("ban", bannedckey, null)
|
|
sleep(1)
|
|
world.SetConfig("ban", bannedckey, null)
|
|
cachedban["matches_this_round"] = list()
|
|
cachedban["existing_user_matches_this_round"] = list()
|
|
cachedban["admin_matches_this_round"] = list()
|
|
cachedban -= "reverting"
|
|
world.SetConfig("ban", bannedckey, list2stickyban(cachedban))
|
|
return null
|
|
|
|
//byond will not trigger isbanned() for "global" host bans,
|
|
//ie, ones where the "apply to this game only" checkbox is not checked (defaults to not checked)
|
|
//So it's safe to let admins walk thru host/sticky bans here
|
|
if (admin)
|
|
log_admin("The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]")
|
|
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
|
|
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
|
|
return null
|
|
|
|
if (C) //user is already connected!.
|
|
to_chat(C, "You are about to get disconnected for matching a sticky ban after you connected. If this turns out to be the ban evasion detection system going haywire, we will automatically detect this and revert the matches. if you feel that this is the case, please wait EXACTLY 6 seconds then reconnect using file -> reconnect to see if the match was reversed.")
|
|
|
|
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. If you wish to appeal this ban please use the keyword 'assistantgreytide' to register an account on the forums.\n" //yogs
|
|
. = list("reason" = "Stickyban", "desc" = desc)
|
|
log_access("Failed Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]")
|
|
|
|
return .
|
|
|
|
|
|
#undef STICKYBAN_MAX_MATCHES
|
|
#undef STICKYBAN_MAX_EXISTING_USER_MATCHES
|
|
#undef STICKYBAN_MAX_ADMIN_MATCHES
|