You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

235 lines
12 KiB

  1. //Blocks an attempt to connect before even creating our client datum thing.
  2. //How many new ckey matches before we revert the stickyban to it's roundstart state
  3. //These are exclusive, so once it goes over one of these numbers, it reverts the ban
  4. #define STICKYBAN_MAX_MATCHES 15
  5. #define STICKYBAN_MAX_EXISTING_USER_MATCHES 3 //ie, users who were connected before the ban triggered
  6. #define STICKYBAN_MAX_ADMIN_MATCHES 1
  7. /world/IsBanned(key, address, computer_id, type, real_bans_only=FALSE)
  8. debug_world_log("isbanned(): '[args.Join("', '")]'")
  9. if (!key || (!real_bans_only && (!address || !computer_id)))
  10. if(real_bans_only)
  11. return FALSE
  12. log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
  13. 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.)")
  14. if (type == "world")
  15. return ..() //shunt world topic banchecks to purely to byond's internal ban system
  16. var/admin = FALSE
  17. var/ckey = ckey(key)
  18. var/client/C = GLOB.directory[ckey]
  19. if (C && ckey == C.ckey && computer_id == C.computer_id && address == C.address)
  20. return //don't recheck connected clients.
  21. //IsBanned can get re-called on a user in certain situations, this prevents that leading to repeated messages to admins.
  22. var/static/list/checkedckeys = list()
  23. //magic voodo to check for a key in a list while also adding that key to the list without having to do two associated lookups
  24. var/message = !checkedckeys[ckey]++
  25. if(GLOB.admin_datums[ckey] || GLOB.deadmins[ckey])
  26. admin = TRUE
  27. //Whitelist
  28. if(!real_bans_only && !C && CONFIG_GET(flag/usewhitelist))
  29. if(!check_whitelist(ckey))
  30. if (admin)
  31. log_admin("The admin [key] has been allowed to bypass the whitelist")
  32. if (message)
  33. message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass the whitelist</span>")
  34. addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass the whitelist</span>")
  35. else
  36. log_access("Failed Login: [key] - Not on whitelist")
  37. return list("reason"="whitelist", "desc" = "\nReason: You are not on the white list for this server")
  38. //Guest Checking
  39. if(!real_bans_only && !C && IsGuestKey(key))
  40. if (CONFIG_GET(flag/guest_ban))
  41. log_access("Failed Login: [key] - Guests not allowed")
  42. return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
  43. if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect())
  44. log_access("Failed Login: [key] - Guests not allowed during panic bunker")
  45. 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.")
  46. //Population Cap Checking
  47. var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
  48. if(!real_bans_only && !C && extreme_popcap && !admin)
  49. var/popcap_value = GLOB.clients.len
  50. if(popcap_value >= extreme_popcap && !GLOB.joined_player_list.Find(ckey))
  51. if(!CONFIG_GET(flag/byond_member_bypass_popcap) || !world.IsSubscribed(ckey, "BYOND"))
  52. log_access("Failed Login: [key] - Population cap reached")
  53. return list("reason"="popcap", "desc"= "\nReason: [CONFIG_GET(string/extreme_popcap_message)]")
  54. if(CONFIG_GET(flag/sql_enabled))
  55. if(!SSdbcore.Connect())
  56. var/msg = "Ban database connection failure. Key [ckey] not checked"
  57. log_world(msg)
  58. if (message)
  59. message_admins(msg)
  60. else
  61. var/list/ban_details = is_banned_from_with_details(ckey, address, computer_id, "Server")
  62. for(var/i in ban_details)
  63. if(admin)
  64. if(text2num(i["applies_to_admins"]))
  65. var/msg = "Admin [key] is admin banned, and has been disallowed access."
  66. log_admin(msg)
  67. if (message)
  68. message_admins(msg)
  69. else
  70. var/msg = "Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]]."
  71. log_admin(msg)
  72. if (message)
  73. message_admins(msg)
  74. 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>")
  75. continue
  76. var/expires = "This is a permanent ban."
  77. if(i["expiration_time"])
  78. expires = " The ban is for [DisplayTimeText(text2num(i["duration"]) MINUTES)] and expires on [i["expiration_time"]] (server time)."
  79. var/desc = {"You, or another user of this computer or connection ([i["key"]]) is banned from playing here.
  80. The ban reason is: [i["reason"]]
  81. This ban (BanID #[i["id"]]) was applied by [i["admin_key"]] on [i["bantime"]] during round ID [i["round_id"]].
  82. [expires]"}
  83. log_access("Failed Login: [key] [computer_id] [address] - Banned (#[i["id"]])")
  84. return list("reason"="Banned","desc"="[desc]")
  85. if (admin)
  86. if (GLOB.directory[ckey])
  87. return
  88. //oh boy, so basically, because of a bug in byond, sometimes stickyban matches don't trigger here, so we can't exempt admins.
  89. // Whitelisting the ckey with the byond whitelist field doesn't work.
  90. // So we instead have to remove every stickyban than later re-add them.
  91. if (!length(GLOB.stickybanadminexemptions))
  92. for (var/banned_ckey in world.GetConfig("ban"))
  93. GLOB.stickybanadmintexts[banned_ckey] = world.GetConfig("ban", banned_ckey)
  94. world.SetConfig("ban", banned_ckey, null)
  95. if (!SSstickyban.initialized)
  96. return
  97. GLOB.stickybanadminexemptions[ckey] = world.time
  98. stoplag() // sleep a byond tick
  99. GLOB.stickbanadminexemptiontimerid = addtimer(CALLBACK(GLOBAL_PROC, /proc/restore_stickybans), 5 SECONDS, TIMER_STOPPABLE|TIMER_UNIQUE|TIMER_OVERRIDE)
  100. return
  101. var/list/ban = ..() //default pager ban stuff
  102. if (ban)
  103. if (!admin)
  104. . = ban
  105. if (real_bans_only)
  106. return
  107. var/bannedckey = "ERROR"
  108. if (ban["ckey"])
  109. bannedckey = ban["ckey"]
  110. var/newmatch = FALSE
  111. var/list/cachedban = SSstickyban.cache[bannedckey]
  112. //rogue ban in the process of being reverted.
  113. if (cachedban && (cachedban["reverting"] || cachedban["timeout"]))
  114. world.SetConfig("ban", bannedckey, null)
  115. return null
  116. if (cachedban && ckey != bannedckey)
  117. newmatch = TRUE
  118. if (cachedban["keys"])
  119. if (cachedban["keys"][ckey])
  120. newmatch = FALSE
  121. if (cachedban["matches_this_round"][ckey])
  122. newmatch = FALSE
  123. if (newmatch && cachedban)
  124. var/list/newmatches = cachedban["matches_this_round"]
  125. var/list/pendingmatches = cachedban["matches_this_round"]
  126. var/list/newmatches_connected = cachedban["existing_user_matches_this_round"]
  127. var/list/newmatches_admin = cachedban["admin_matches_this_round"]
  128. if (C)
  129. newmatches_connected[ckey] = ckey
  130. newmatches_connected = cachedban["existing_user_matches_this_round"]
  131. pendingmatches[ckey] = ckey
  132. sleep(STICKYBAN_ROGUE_CHECK_TIME)
  133. pendingmatches -= ckey
  134. if (admin)
  135. newmatches_admin[ckey] = ckey
  136. if (cachedban["reverting"] || cachedban["timeout"])
  137. return null
  138. newmatches[ckey] = ckey
  139. if (\
  140. newmatches.len+pendingmatches.len > STICKYBAN_MAX_MATCHES || \
  141. newmatches_connected.len > STICKYBAN_MAX_EXISTING_USER_MATCHES || \
  142. newmatches_admin.len > STICKYBAN_MAX_ADMIN_MATCHES \
  143. )
  144. var/action
  145. if (ban["fromdb"])
  146. cachedban["timeout"] = TRUE
  147. action = "putting it on timeout for the remainder of the round"
  148. else
  149. cachedban["reverting"] = TRUE
  150. action = "reverting to its roundstart state"
  151. world.SetConfig("ban", bannedckey, null)
  152. //we always report this
  153. log_game("Stickyban on [bannedckey] detected as rogue, [action]")
  154. message_admins("Stickyban on [bannedckey] detected as rogue, [action]")
  155. //do not convert to timer.
  156. spawn (5)
  157. world.SetConfig("ban", bannedckey, null)
  158. sleep(1)
  159. world.SetConfig("ban", bannedckey, null)
  160. if (!ban["fromdb"])
  161. cachedban = cachedban.Copy() //so old references to the list still see the ban as reverting
  162. cachedban["matches_this_round"] = list()
  163. cachedban["existing_user_matches_this_round"] = list()
  164. cachedban["admin_matches_this_round"] = list()
  165. cachedban -= "reverting"
  166. SSstickyban.cache[bannedckey] = cachedban
  167. world.SetConfig("ban", bannedckey, list2stickyban(cachedban))
  168. return null
  169. if (ban["fromdb"])
  170. if(SSdbcore.Connect())
  171. INVOKE_ASYNC(SSdbcore, /datum/controller/subsystem/dbcore/proc.QuerySelect, list(
  172. SSdbcore.NewQuery("INSERT INTO [format_table_name("stickyban_matched_ckey")] (matched_ckey, stickyban) VALUES ('[sanitizeSQL(ckey)]', '[sanitizeSQL(bannedckey)]') ON DUPLICATE KEY UPDATE last_matched = now()"),
  173. SSdbcore.NewQuery("INSERT INTO [format_table_name("stickyban_matched_ip")] (matched_ip, stickyban) VALUES ( INET_ATON('[sanitizeSQL(address)]'), '[sanitizeSQL(bannedckey)]') ON DUPLICATE KEY UPDATE last_matched = now()"),
  174. SSdbcore.NewQuery("INSERT INTO [format_table_name("stickyban_matched_cid")] (matched_cid, stickyban) VALUES ('[sanitizeSQL(computer_id)]', '[sanitizeSQL(bannedckey)]') ON DUPLICATE KEY UPDATE last_matched = now()")
  175. ), FALSE, TRUE)
  176. //byond will not trigger isbanned() for "global" host bans,
  177. //ie, ones where the "apply to this game only" checkbox is not checked (defaults to not checked)
  178. //So it's safe to let admins walk thru host/sticky bans here
  179. if (admin)
  180. log_admin("The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]")
  181. if (message)
  182. message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
  183. addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
  184. return null
  185. if (C) //user is already connected!.
  186. to_chat(C, "<span class='redtext'>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 automatically reversed.</span>", confidential = TRUE)
  187. 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"
  188. . = list("reason" = "Stickyban", "desc" = desc)
  189. log_access("Failed Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]")
  190. return .
  191. /proc/restore_stickybans()
  192. for (var/banned_ckey in GLOB.stickybanadmintexts)
  193. world.SetConfig("ban", banned_ckey, GLOB.stickybanadmintexts[banned_ckey])
  194. GLOB.stickybanadminexemptions = list()
  195. GLOB.stickybanadmintexts = list()
  196. if (GLOB.stickbanadminexemptiontimerid)
  197. deltimer(GLOB.stickbanadminexemptiontimerid)
  198. GLOB.stickbanadminexemptiontimerid = null
  199. #undef STICKYBAN_MAX_MATCHES
  200. #undef STICKYBAN_MAX_EXISTING_USER_MATCHES
  201. #undef STICKYBAN_MAX_ADMIN_MATCHES