From 4561ee94f8d1fe14f6ca16fc236663d3210d0a5d Mon Sep 17 00:00:00 2001 From: Kyep Date: Mon, 22 Apr 2019 16:53:20 -0700 Subject: [PATCH 01/21] WIP ports XKeyScore from TG --- SQL/paradise_schema.sql | 14 +++ SQL/paradise_schema_prefixed.sql | 14 +++ SQL/updates/5-6.sql | 6 ++ code/controllers/configuration.dm | 30 +++++- code/controllers/subsystem/ipintel.dm | 14 +++ code/modules/admin/ipintel.dm | 138 ++++++++++++++++++++++++++ code/modules/client/client defines.dm | 2 + code/modules/client/client procs.dm | 14 +++ config/example/config.txt | 15 +++ paradise.dme | 2 + 10 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 SQL/updates/5-6.sql create mode 100644 code/controllers/subsystem/ipintel.dm create mode 100644 code/modules/admin/ipintel.dm diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 0379517190f..750add2fd86 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -535,3 +535,17 @@ CREATE TABLE IF NOT EXISTS `discord` ( PRIMARY KEY (`ckey`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ipintel` +-- +DROP TABLE IF EXISTS `ipintel`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ipintel` ( +`ip` INT UNSIGNED NOT NULL , +`date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL , +`intel` REAL NOT NULL DEFAULT '0', +PRIMARY KEY ( `ip` ) +) ENGINE = INNODB; +/*!40101 SET character_set_client = @saved_cs_client */; diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql index e9a622cf037..1a663563cab 100644 --- a/SQL/paradise_schema_prefixed.sql +++ b/SQL/paradise_schema_prefixed.sql @@ -534,3 +534,17 @@ CREATE TABLE IF NOT EXISTS `SS13_discord` ( PRIMARY KEY (`ckey`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `SS13_ipintel` +-- +DROP TABLE IF EXISTS `SS13_ipintel`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `SS13_ipintel` ( +`ip` INT UNSIGNED NOT NULL , +`date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL , +`intel` REAL NOT NULL DEFAULT '0', +PRIMARY KEY ( `ip` ) +) ENGINE = INNODB; +/*!40101 SET character_set_client = @saved_cs_client */; \ No newline at end of file diff --git a/SQL/updates/5-6.sql b/SQL/updates/5-6.sql new file mode 100644 index 00000000000..73362bacd99 --- /dev/null +++ b/SQL/updates/5-6.sql @@ -0,0 +1,6 @@ +CREATE TABLE `ipintel` ( +`ip` INT UNSIGNED NOT NULL , +`date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL , +`intel` REAL NOT NULL DEFAULT '0', +PRIMARY KEY ( `ip` ) +) ENGINE = INNODB; \ No newline at end of file diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 01cf57aad9e..53affcff45c 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -123,6 +123,14 @@ var/slime_delay = 0 var/animal_delay = 0 + //IP Intel vars + var/ipintel_email + var/ipintel_rating_bad = 1 + var/ipintel_save_good = 12 + var/ipintel_save_bad = 1 + var/ipintel_domain = "check.getipintel.net" + var/ipintel_maxplaytime = 0 + var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database @@ -213,7 +221,7 @@ // Automatic localhost admin disable var/disable_localhost_admin = 0 - + //Start now warning var/start_now_confirmation = 0 @@ -293,6 +301,20 @@ if("shadowling_max_age") config.shadowling_max_age = text2num(value) + if("ipintel_email") + if (value != "ch@nge.me") + config.ipintel_email = value + if("ipintel_rating_bad") + config.ipintel_rating_bad = text2num(value) + if("ipintel_domain") + config.ipintel_domain = value + if("ipintel_save_good") + config.ipintel_save_good = text2num(value) + if("ipintel_save_bad") + config.ipintel_save_bad = text2num(value) + if("ipintel_maxplaytime") + config.ipintel_maxplaytime = text2num(value) + if("log_ooc") config.log_ooc = 1 @@ -358,10 +380,10 @@ if("no_dead_vote") config.vote_no_dead = 1 - + if("vote_autotransfer_initial") config.vote_autotransfer_initial = text2num(value) - + if("vote_autotransfer_interval") config.vote_autotransfer_interval = text2num(value) @@ -645,7 +667,7 @@ if("disable_karma") config.disable_karma = 1 - + if("start_now_confirmation") config.start_now_confirmation = 1 diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm new file mode 100644 index 00000000000..6e31d903622 --- /dev/null +++ b/code/controllers/subsystem/ipintel.dm @@ -0,0 +1,14 @@ +SUBSYSTEM_DEF(ipintel) + name = "XKeyScore" + wait = 1 + flags = SS_NO_FIRE + var/enabled = 0 //disable at round start to avoid checking reconnects + var/throttle = 0 + var/errors = 0 + + var/list/cache = list() + +/datum/controller/subsystem/ipintel/Initialize(timeofday, zlevel) + enabled = 1 + . = ..() + diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm new file mode 100644 index 00000000000..5ef982b6079 --- /dev/null +++ b/code/modules/admin/ipintel.dm @@ -0,0 +1,138 @@ +/datum/ipintel + var/ip + var/intel = 0 + var/cache = FALSE + var/cacheminutesago = 0 + var/cachedate = "" + var/cacherealtime = 0 + +/datum/ipintel/New() + cachedate = SQLtime() + cacherealtime = world.realtime + +/datum/ipintel/proc/is_valid() + . = FALSE + if (intel < 0) + return + if (intel <= config.ipintel_rating_bad) + if (world.realtime < cacherealtime + (config.ipintel_save_good * 60 * 60 * 10)) + return TRUE + else + if (world.realtime < cacherealtime + (config.ipintel_save_bad * 60 * 60 * 10)) + return TRUE + +/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE) + var/datum/ipintel/res = new() + res.ip = ip + . = res + if (!ip || !config.ipintel_email || !SSipintel.enabled) + return + if (!bypasscache) + var/datum/ipintel/cachedintel = SSipintel.cache[ip] + if (cachedintel && cachedintel.is_valid()) + cachedintel.cache = TRUE + return cachedintel + + if(dbcon.IsConnected()) + var/rating_bad = config.ipintel_rating_bad + var/DBQuery/query_get_ip_intel = dbcon.NewQuery({" + SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW()) + FROM [format_table_name("ipintel")] + WHERE + ip = INET_ATON('[ip]') + AND (( + intel < [rating_bad] + AND + date + INTERVAL [config.ipintel_save_good] HOUR > NOW() + ) OR ( + intel >= [rating_bad] + AND + date + INTERVAL [config.ipintel_save_bad] HOUR > NOW() + )) + "}) + if(!query_get_ip_intel.Execute()) + qdel(query_get_ip_intel) + return + if (query_get_ip_intel.NextRow()) + res.cache = TRUE + res.cachedate = query_get_ip_intel.item[1] + res.intel = text2num(query_get_ip_intel.item[2]) + res.cacheminutesago = text2num(query_get_ip_intel.item[3]) + res.cacherealtime = world.realtime - (text2num(query_get_ip_intel.item[3])*10*60) + SSipintel.cache[ip] = res + qdel(query_get_ip_intel) + return + qdel(query_get_ip_intel) + res.intel = ip_intel_query(ip) + if (updatecache && res.intel >= 0) + SSipintel.cache[ip] = res + if(dbcon.IsConnected()) + var/DBQuery/query_add_ip_intel = dbcon.NewQuery("INSERT INTO [format_table_name("ipintel")] (ip, intel) VALUES (INET_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()") + query_add_ip_intel.Execute() + qdel(query_add_ip_intel) + + +/proc/ip_intel_query(ip, var/retryed=0) + . = -1 //default + if (!ip) + return + if (SSipintel.throttle > world.timeofday) + return + if (!SSipintel.enabled) + return + + var/list/http[] = world.Export("http://[config.ipintel_domain]/check.php?ip=[ip]&contact=[config.ipintel_email]&format=json&flags=f") + + if (http) + var/status = text2num(http["STATUS"]) + + if (status == 200) + var/response = json_decode(file2text(http["CONTENT"])) + if (response) + if (response["status"] == "success") + var/intelnum = text2num(response["result"]) + if (isnum(intelnum)) + return text2num(response["result"]) + else + ipintel_handle_error("Bad intel from server: [response["result"]].", ip, retryed) + if (!retryed) + sleep(25) + return .(ip, 1) + else + ipintel_handle_error("Bad response from server: [response["status"]].", ip, retryed) + if (!retryed) + sleep(25) + return .(ip, 1) + + else if (status == 429) + ipintel_handle_error("Error #429: We have exceeded the rate limit.", ip, 1) + return + else + ipintel_handle_error("Unknown status code: [status].", ip, retryed) + if (!retryed) + sleep(25) + return .(ip, 1) + else + ipintel_handle_error("Unable to connect to API.", ip, retryed) + if (!retryed) + sleep(25) + return .(ip, 1) + + +/proc/ipintel_handle_error(error, ip, retryed) + if (retryed) + SSipintel.errors++ + error += " Could not check [ip]. Disabling IPINTEL for [SSipintel.errors] minute[( SSipintel.errors == 1 ? "" : "s" )]" + SSipintel.throttle = world.timeofday + (10 * 120 * SSipintel.errors) + else + error += " Attempting retry on [ip]." + log_ipintel(error) + +/proc/log_ipintel(text) + log_game("IPINTEL: [text]") + log_debug("IPINTEL: [text]") + + + + + diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 00900fe5fd1..b9a6df2be00 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -71,6 +71,8 @@ control_freak = CONTROL_FREAK_ALL | CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS + var/ip_intel = "Disabled" + var/datum/click_intercept/click_intercept = null //datum that controls the displaying and hiding of tooltips diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index a6216c87b1a..8c4248593b6 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -374,6 +374,7 @@ to_chat(src, message) clientmessages.Remove(ckey) + check_ip_intel() send_resources() @@ -528,6 +529,19 @@ var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[ckey]','[sql_ip]','[sql_computerid]');") query_accesslog.Execute() +/client/proc/check_ip_intel() + set waitfor = 0 //we sleep when getting the intel, no need to hold up the client connection while we sleep + if (config.ipintel_email) + if(config.ipintel_maxplaytime && config.use_exp_tracking) + var/living_hours = get_exp_type_num(EXP_TYPE_LIVING) / 60 + if(living_hours >= config.ipintel_maxplaytime) + message_admins("Proxy Detection: [key_name_admin(src)] skips check due to [living_hours] playtime.") + return + + var/datum/ipintel/res = get_ip_intel(address) + if (res.intel >= config.ipintel_rating_bad) + message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.") + ip_intel = res.intel #undef TOPIC_SPAM_DELAY #undef UPLOAD_LIMIT diff --git a/config/example/config.txt b/config/example/config.txt index d93809c32cd..4a011939567 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -176,6 +176,21 @@ GUEST_JOBBAN ## Uncomment this to stop people connecting to your server without a registered ckey. (i.e. guest-* are all blocked from connecting) GUEST_BAN +### IPINTEL: +### This allows you to detect likely proxies by checking ips against getipintel.net +## Rating to warn at: (0.90 is good, 1 is 100% likely to be a spammer/proxy, 0.8 is 80%, etc) anything equal to or higher then this number triggers an admin warning +#IPINTEL_RATING_BAD 0.90 +## Contact email, (required to use the service, leaving blank or default disables IPINTEL) +#IPINTEL_EMAIL ch@nge.me +## How long to save good matches (ipintel rate limits to 15 per minute and 500 per day. so this shouldn't be too low, getipintel.net suggests 6 hours, time is in hours) (Your ip will get banned if you go over 500 a day too many times) +#IPINTEL_SAVE_GOOD 72 +## How long to save bad matches (these numbers can change as ips change hands, best not to save these for too long in case somebody gets a new ip used by a spammer/proxy before.) +#IPINTEL_SAVE_BAD 3 +## Domain name to query (leave commented out for the default, only needed if you pay getipintel.net for more querys) +#IPINTEL_DOMAIN check.getipintel.net +## Ignore players with this many hours of playtime. Requires USE_EXP_TRACKING +#IPINTEL_MAXPLAYTIME 10 + ## Comment to disable checking for the cid randomizer dll. (disabled if database isn't enabled or connected) CHECK_RANDOMIZER diff --git a/paradise.dme b/paradise.dme index e478048e4d8..013c04fa842 100644 --- a/paradise.dme +++ b/paradise.dme @@ -212,6 +212,7 @@ #include "code\controllers\subsystem\fires.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\icon_smooth.dm" +#include "code\controllers\subsystem\ipintel.dm" #include "code\controllers\subsystem\jobs.dm" #include "code\controllers\subsystem\machinery.dm" #include "code\controllers\subsystem\mapping.dm" @@ -1128,6 +1129,7 @@ #include "code\modules\admin\create_poll.dm" #include "code\modules\admin\create_turf.dm" #include "code\modules\admin\holder2.dm" +#include "code\modules\admin\ipintel.dm" #include "code\modules\admin\IsBanned.dm" #include "code\modules\admin\machine_upgrade.dm" #include "code\modules\admin\NewBan.dm" From 374f175cdce1d97f6bd1f25bfd0f77d085220d08 Mon Sep 17 00:00:00 2001 From: Kyep Date: Mon, 22 Apr 2019 17:37:10 -0700 Subject: [PATCH 02/21] SQL_VERSION / DB_VERSION --- code/__DEFINES/misc.dm | 2 +- config/example/dbconfig.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index dff9426c641..3dafd07e92b 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -367,7 +367,7 @@ #define INVESTIGATE_BOMB "bombs" // The SQL version required by this version of the code -#define SQL_VERSION 5 +#define SQL_VERSION 6 // Vending machine stuff #define CAT_NORMAL 1 diff --git a/config/example/dbconfig.txt b/config/example/dbconfig.txt index 6ca1f0201e8..8b411db15e1 100644 --- a/config/example/dbconfig.txt +++ b/config/example/dbconfig.txt @@ -9,7 +9,7 @@ ## This value must be set to the version of the paradise schema in use. ## If this value does not match, the SQL database will not be loaded and an error will be generated. ## Roundstart will be delayed. -DB_VERSION 5 +DB_VERSION 6 ## Server the MySQL database can be found at. # Examples: localhost, 200.135.5.43, www.mysqldb.com, etc. From a35ba2fc5f07975632acbc8439a844c5705446db Mon Sep 17 00:00:00 2001 From: Kyep Date: Mon, 22 Apr 2019 18:48:23 -0700 Subject: [PATCH 03/21] spacing --- code/controllers/configuration.dm | 2 +- code/modules/admin/ipintel.dm | 46 ++++++++++++++--------------- code/modules/client/client procs.dm | 4 +-- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 53affcff45c..ad9cb166970 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -302,7 +302,7 @@ config.shadowling_max_age = text2num(value) if("ipintel_email") - if (value != "ch@nge.me") + if(value != "ch@nge.me") config.ipintel_email = value if("ipintel_rating_bad") config.ipintel_rating_bad = text2num(value) diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 5ef982b6079..caa13e10652 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -12,24 +12,24 @@ /datum/ipintel/proc/is_valid() . = FALSE - if (intel < 0) + if(intel < 0) return - if (intel <= config.ipintel_rating_bad) - if (world.realtime < cacherealtime + (config.ipintel_save_good * 60 * 60 * 10)) + if(intel <= config.ipintel_rating_bad) + if(world.realtime < cacherealtime + (config.ipintel_save_good * 60 * 60 * 10)) return TRUE else - if (world.realtime < cacherealtime + (config.ipintel_save_bad * 60 * 60 * 10)) + if(world.realtime < cacherealtime + (config.ipintel_save_bad * 60 * 60 * 10)) return TRUE /proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE) var/datum/ipintel/res = new() res.ip = ip . = res - if (!ip || !config.ipintel_email || !SSipintel.enabled) + if(!ip || !config.ipintel_email || !SSipintel.enabled) return - if (!bypasscache) + if(!bypasscache) var/datum/ipintel/cachedintel = SSipintel.cache[ip] - if (cachedintel && cachedintel.is_valid()) + if(cachedintel && cachedintel.is_valid()) cachedintel.cache = TRUE return cachedintel @@ -53,7 +53,7 @@ if(!query_get_ip_intel.Execute()) qdel(query_get_ip_intel) return - if (query_get_ip_intel.NextRow()) + if(query_get_ip_intel.NextRow()) res.cache = TRUE res.cachedate = query_get_ip_intel.item[1] res.intel = text2num(query_get_ip_intel.item[2]) @@ -64,7 +64,7 @@ return qdel(query_get_ip_intel) res.intel = ip_intel_query(ip) - if (updatecache && res.intel >= 0) + if(updatecache && res.intel >= 0) SSipintel.cache[ip] = res if(dbcon.IsConnected()) var/DBQuery/query_add_ip_intel = dbcon.NewQuery("INSERT INTO [format_table_name("ipintel")] (ip, intel) VALUES (INET_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()") @@ -74,53 +74,53 @@ /proc/ip_intel_query(ip, var/retryed=0) . = -1 //default - if (!ip) + if(!ip) return - if (SSipintel.throttle > world.timeofday) + if(SSipintel.throttle > world.timeofday) return - if (!SSipintel.enabled) + if(!SSipintel.enabled) return var/list/http[] = world.Export("http://[config.ipintel_domain]/check.php?ip=[ip]&contact=[config.ipintel_email]&format=json&flags=f") - if (http) + if(http) var/status = text2num(http["STATUS"]) - if (status == 200) + if(status == 200) var/response = json_decode(file2text(http["CONTENT"])) - if (response) - if (response["status"] == "success") + if(response) + if(response["status"] == "success") var/intelnum = text2num(response["result"]) - if (isnum(intelnum)) + if(isnum(intelnum)) return text2num(response["result"]) else ipintel_handle_error("Bad intel from server: [response["result"]].", ip, retryed) - if (!retryed) + if(!retryed) sleep(25) return .(ip, 1) else ipintel_handle_error("Bad response from server: [response["status"]].", ip, retryed) - if (!retryed) + if(!retryed) sleep(25) return .(ip, 1) - else if (status == 429) + else if(status == 429) ipintel_handle_error("Error #429: We have exceeded the rate limit.", ip, 1) return else ipintel_handle_error("Unknown status code: [status].", ip, retryed) - if (!retryed) + if(!retryed) sleep(25) return .(ip, 1) else ipintel_handle_error("Unable to connect to API.", ip, retryed) - if (!retryed) + if(!retryed) sleep(25) return .(ip, 1) /proc/ipintel_handle_error(error, ip, retryed) - if (retryed) + if(retryed) SSipintel.errors++ error += " Could not check [ip]. Disabling IPINTEL for [SSipintel.errors] minute[( SSipintel.errors == 1 ? "" : "s" )]" SSipintel.throttle = world.timeofday + (10 * 120 * SSipintel.errors) diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 8c4248593b6..6153557db48 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -531,7 +531,7 @@ /client/proc/check_ip_intel() set waitfor = 0 //we sleep when getting the intel, no need to hold up the client connection while we sleep - if (config.ipintel_email) + if(config.ipintel_email) if(config.ipintel_maxplaytime && config.use_exp_tracking) var/living_hours = get_exp_type_num(EXP_TYPE_LIVING) / 60 if(living_hours >= config.ipintel_maxplaytime) @@ -539,7 +539,7 @@ return var/datum/ipintel/res = get_ip_intel(address) - if (res.intel >= config.ipintel_rating_bad) + if(res.intel >= config.ipintel_rating_bad) message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.") ip_intel = res.intel From d5cb816b0cea35912db4ff75f2c375369764d123 Mon Sep 17 00:00:00 2001 From: Kyep Date: Fri, 26 Apr 2019 01:17:53 -0700 Subject: [PATCH 04/21] removes message to admins in common use case of regular player --- code/modules/client/client procs.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 6153557db48..a597d0f0e3a 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -535,7 +535,6 @@ if(config.ipintel_maxplaytime && config.use_exp_tracking) var/living_hours = get_exp_type_num(EXP_TYPE_LIVING) / 60 if(living_hours >= config.ipintel_maxplaytime) - message_admins("Proxy Detection: [key_name_admin(src)] skips check due to [living_hours] playtime.") return var/datum/ipintel/res = get_ip_intel(address) From f9e70f3e61f387bfa6ce167be939c841cf414334 Mon Sep 17 00:00:00 2001 From: Kyep Date: Fri, 26 Apr 2019 01:32:51 -0700 Subject: [PATCH 05/21] increases default IPINTEL_SAVE_BAD --- config/example/config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/example/config.txt b/config/example/config.txt index 4a011939567..684b38c50fb 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -185,7 +185,7 @@ GUEST_BAN ## How long to save good matches (ipintel rate limits to 15 per minute and 500 per day. so this shouldn't be too low, getipintel.net suggests 6 hours, time is in hours) (Your ip will get banned if you go over 500 a day too many times) #IPINTEL_SAVE_GOOD 72 ## How long to save bad matches (these numbers can change as ips change hands, best not to save these for too long in case somebody gets a new ip used by a spammer/proxy before.) -#IPINTEL_SAVE_BAD 3 +#IPINTEL_SAVE_BAD 6 ## Domain name to query (leave commented out for the default, only needed if you pay getipintel.net for more querys) #IPINTEL_DOMAIN check.getipintel.net ## Ignore players with this many hours of playtime. Requires USE_EXP_TRACKING From 63980ff522862e761cf802038eab42eef4d4ed7a Mon Sep 17 00:00:00 2001 From: Kyep Date: Thu, 2 May 2019 17:41:23 -0700 Subject: [PATCH 06/21] switches from flags=f to flags=b, for <130ms response time --- code/modules/admin/ipintel.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index caa13e10652..138e9b4cc0f 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -81,7 +81,7 @@ if(!SSipintel.enabled) return - var/list/http[] = world.Export("http://[config.ipintel_domain]/check.php?ip=[ip]&contact=[config.ipintel_email]&format=json&flags=f") + var/list/http[] = world.Export("http://[config.ipintel_domain]/check.php?ip=[ip]&contact=[config.ipintel_email]&format=json&flags=b") if(http) var/status = text2num(http["STATUS"]) From 4ac50d4806c2d1295add4a15caae19ec62d03fa3 Mon Sep 17 00:00:00 2001 From: Kyep Date: Sat, 4 May 2019 22:27:56 -0700 Subject: [PATCH 07/21] add comment for SQL update --- SQL/updates/5-6.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SQL/updates/5-6.sql b/SQL/updates/5-6.sql index 73362bacd99..f5f04d0f441 100644 --- a/SQL/updates/5-6.sql +++ b/SQL/updates/5-6.sql @@ -1,3 +1,5 @@ +#Updating the SQL from version 5 to version 6. -Kyep +#Creating a table to track the results of VPN/proxy lookups for IPs CREATE TABLE `ipintel` ( `ip` INT UNSIGNED NOT NULL , `date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL , From 932232545b2a2ccc383c52d5ea137659311462a0 Mon Sep 17 00:00:00 2001 From: Kyep Date: Sat, 4 May 2019 22:44:12 -0700 Subject: [PATCH 08/21] INIT_ORDER_XKEYSCORE --- code/__DEFINES/subsystems.dm | 1 + code/controllers/subsystem/ipintel.dm | 1 + 2 files changed, 2 insertions(+) diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 29a93ae94f9..47d6317aa42 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -73,6 +73,7 @@ #define INIT_ORDER_XKEYSCORE -10 #define INIT_ORDER_STICKY_BAN -10 #define INIT_ORDER_TICKETS -10 +#define INIT_ORDER_XKEYSCORE -10 #define INIT_ORDER_LIGHTING -20 #define INIT_ORDER_SHUTTLE -21 #define INIT_ORDER_NIGHTSHIFT -22 diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm index 6e31d903622..92f02a404a1 100644 --- a/code/controllers/subsystem/ipintel.dm +++ b/code/controllers/subsystem/ipintel.dm @@ -2,6 +2,7 @@ SUBSYSTEM_DEF(ipintel) name = "XKeyScore" wait = 1 flags = SS_NO_FIRE + init_order = INIT_ORDER_XKEYSCORE // 10 var/enabled = 0 //disable at round start to avoid checking reconnects var/throttle = 0 var/errors = 0 From 25556790505eb329a568ee46ac5537b289ee0929 Mon Sep 17 00:00:00 2001 From: Kyep Date: Sat, 4 May 2019 22:47:04 -0700 Subject: [PATCH 09/21] is_connecting_from_localhost() --- code/modules/client/client procs.dm | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index aa6a95f8328..0fdf29f1097 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -74,7 +74,7 @@ //Admin PM if(href_list["priv_msg"]) var/client/C = locate(href_list["priv_msg"]) - + if(!C) // Might be a stealthmin ID, so pass it in straight C = href_list["priv_msg"] else if(C.UID() != href_list["priv_msg"]) @@ -320,8 +320,7 @@ //Admin Authorisation // Automatically makes localhost connection an admin if(!config.disable_localhost_admin) - var/localhost_addresses = list("127.0.0.1", "::1") // Adresses - if(!isnull(address) && address in localhost_addresses) + if(is_connecting_from_localhost()) new /datum/admins("!LOCALHOST!", R_HOST, ckey) // Makes localhost rank holder = admin_datums[ckey] if(holder) @@ -411,6 +410,12 @@ Master.UpdateTickRate() +/client/proc/is_connecting_from_localhost() + var/localhost_addresses = list("127.0.0.1", "::1") // Adresses + if(!isnull(address) && address in localhost_addresses) + return TRUE + return FALSE + ////////////// //DISCONNECT// ////////////// @@ -539,6 +544,10 @@ if(living_hours >= config.ipintel_maxplaytime) return + if(is_connecting_from_localhost()) + log_debug("IPIntel: skip check for player [key_name_admin(src)] connecting from localhost.") + return + var/datum/ipintel/res = get_ip_intel(address) if(res.intel >= config.ipintel_rating_bad) message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.") From 6a3c228f877bcab8ba08050f489a694851477e45 Mon Sep 17 00:00:00 2001 From: Kyep Date: Sun, 5 May 2019 01:07:08 -0700 Subject: [PATCH 10/21] removes duplicate definition --- code/__DEFINES/subsystems.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 47d6317aa42..29a93ae94f9 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -73,7 +73,6 @@ #define INIT_ORDER_XKEYSCORE -10 #define INIT_ORDER_STICKY_BAN -10 #define INIT_ORDER_TICKETS -10 -#define INIT_ORDER_XKEYSCORE -10 #define INIT_ORDER_LIGHTING -20 #define INIT_ORDER_SHUTTLE -21 #define INIT_ORDER_NIGHTSHIFT -22 From db950117ff3a28c480909cc7cd3f933b8441a4f9 Mon Sep 17 00:00:00 2001 From: Kyep Date: Fri, 10 May 2019 02:33:33 -0700 Subject: [PATCH 11/21] alphabetical paradise.dme --- paradise.dme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paradise.dme b/paradise.dme index c82574d48ef..cd0822bdd27 100644 --- a/paradise.dme +++ b/paradise.dme @@ -210,8 +210,8 @@ #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\holiday.dm" #include "code\controllers\subsystem\icon_smooth.dm" -#include "code\controllers\subsystem\ipintel.dm" #include "code\controllers\subsystem\idlenpcpool.dm" +#include "code\controllers\subsystem\ipintel.dm" #include "code\controllers\subsystem\jobs.dm" #include "code\controllers\subsystem\machinery.dm" #include "code\controllers\subsystem\mapping.dm" From f4de59a4f8de95b64f3e40b2d75efd73369c2aa3 Mon Sep 17 00:00:00 2001 From: Kyep Date: Fri, 10 May 2019 09:54:20 -0700 Subject: [PATCH 12/21] adds whitelist functionality --- SQL/paradise_schema.sql | 12 ++++++ SQL/paradise_schema_prefixed.sql | 14 ++++++- SQL/updates/5-6.sql | 10 ++++- code/controllers/configuration.dm | 3 ++ code/modules/admin/admin.dm | 9 +++++ code/modules/admin/admin_verbs.dm | 1 + code/modules/admin/ipintel.dm | 47 ++++++++++++++++++++++- code/modules/client/client defines.dm | 1 + code/modules/client/client procs.dm | 17 ++++++-- code/modules/mob/new_player/new_player.dm | 13 ++++++- config/example/config.txt | 4 +- 11 files changed, 122 insertions(+), 9 deletions(-) diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 750add2fd86..1845880ba52 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -549,3 +549,15 @@ CREATE TABLE `ipintel` ( PRIMARY KEY ( `ip` ) ) ENGINE = INNODB; /*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `vpn_whitelist` +-- +DROP TABLE IF EXISTS `vpn_whitelist`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `vpn_whitelist` ( + `ckey` VARCHAR(32) NOT NULL, + `reason` text + PRIMARY KEY (`ckey`) +) ENGINE=INNODB; diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql index 1a663563cab..75dc19ff8cb 100644 --- a/SQL/paradise_schema_prefixed.sql +++ b/SQL/paradise_schema_prefixed.sql @@ -547,4 +547,16 @@ CREATE TABLE `SS13_ipintel` ( `intel` REAL NOT NULL DEFAULT '0', PRIMARY KEY ( `ip` ) ) ENGINE = INNODB; -/*!40101 SET character_set_client = @saved_cs_client */; \ No newline at end of file +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `SS13_vpn_whitelist` +-- +DROP TABLE IF EXISTS `SS13_vpn_whitelist`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `SS13_vpn_whitelist` ( + `ckey` VARCHAR(32) NOT NULL, + `reason` text, + PRIMARY KEY (`ckey`) +) ENGINE=INNODB; \ No newline at end of file diff --git a/SQL/updates/5-6.sql b/SQL/updates/5-6.sql index f5f04d0f441..67021b90e8b 100644 --- a/SQL/updates/5-6.sql +++ b/SQL/updates/5-6.sql @@ -1,8 +1,16 @@ #Updating the SQL from version 5 to version 6. -Kyep + #Creating a table to track the results of VPN/proxy lookups for IPs CREATE TABLE `ipintel` ( `ip` INT UNSIGNED NOT NULL , `date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL , `intel` REAL NOT NULL DEFAULT '0', PRIMARY KEY ( `ip` ) -) ENGINE = INNODB; \ No newline at end of file +) ENGINE = INNODB; + +#Creating a table to track which ckeys are whitelisted for use of VPNs +CREATE TABLE `vpn_whitelist` ( + `ckey` VARCHAR(32) NOT NULL, + `reason` text, + PRIMARY KEY (`ckey`) +) ENGINE=INNODB; \ No newline at end of file diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index a424fb1ac55..b6f1fb4bc30 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -134,6 +134,7 @@ var/ipintel_save_bad = 1 var/ipintel_domain = "check.getipintel.net" var/ipintel_maxplaytime = 0 + var/ipintel_whitelist = 0 var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt @@ -323,6 +324,8 @@ config.ipintel_save_bad = text2num(value) if("ipintel_maxplaytime") config.ipintel_maxplaytime = text2num(value) + if("ipintel_whitelist") + config.ipintel_whitelist = 1 if("log_ooc") config.log_ooc = 1 diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 4fcf95dbfd2..ca5b91eaa1c 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -273,6 +273,15 @@ var/global/nologevent = 0 show_note(key) +/datum/admins/proc/vpn_whitelist() + set category = "Admin" + set name = "VPN Ckey Whitelist" + if(!check_rights(R_ADMIN)) + return + var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32) + if(key) + vpn_whitelist_panel(key) + /datum/admins/proc/access_news_network() //MARKER set category = "Event" set name = "Access Newscaster Network" diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 4a455cd8e5d..06c2361b90a 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -53,6 +53,7 @@ var/list/admin_verbs_admin = list( /datum/admins/proc/PlayerNotes, /client/proc/cmd_mentor_say, /datum/admins/proc/show_player_notes, + /datum/admins/proc/vpn_whitelist, /client/proc/free_slot, /*frees slot for chosen job*/ /client/proc/toggleattacklogs, /client/proc/toggleadminlogs, diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 138e9b4cc0f..83853aa17a4 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -132,7 +132,52 @@ log_game("IPINTEL: [text]") log_debug("IPINTEL: [text]") +/proc/vpn_whitelist_check(target_ckey) + var/target_sql_ckey = ckey(target_ckey) + var/DBQuery/query_whitelist_check = dbcon.NewQuery("SELECT * FROM [format_table_name("vpn_whitelist")] WHERE ckey = '[target_sql_ckey]'") + if(!query_whitelist_check.Execute()) + var/err = query_whitelist_check.ErrorMsg() + log_debug("SQL ERROR on proc/vpn_whitelist_check for ckey '[target_sql_ckey]'. Error : \[[err]\]\n") + return FALSE + if(query_whitelist_check.NextRow()) + return TRUE // At least one row in the whitelist names their ckey. That means they are whitelisted. + return FALSE +/proc/vpn_whitelist_add(target_ckey) + var/target_sql_ckey = ckey(target_ckey) + var/reason_string = input(usr, "Enter link to the URL of their whitelist request on the forum.","Reason required") as message|null + if(!reason_string) + return FALSE + reason_string = sanitizeSQL(reason_string) + var/DBQuery/query_whitelist_add = dbcon.NewQuery("INSERT INTO [format_table_name("vpn_whitelist")] (ckey,reason) VALUES ('[target_sql_ckey]','[reason_string]')") + if(!query_whitelist_add.Execute()) + var/err = query_whitelist_add.ErrorMsg() + log_debug("SQL ERROR on proc/vpn_whitelist_add for ckey '[target_sql_ckey]'. Error : \[[err]\]\n") + return FALSE + return TRUE +/proc/vpn_whitelist_remove(target_ckey) + var/target_sql_ckey = ckey(target_ckey) + var/DBQuery/query_whitelist_remove = dbcon.NewQuery("DELETE FROM [format_table_name("vpn_whitelist")] WHERE ckey = '[target_sql_ckey]'") + if(!query_whitelist_remove.Execute()) + var/err = query_whitelist_remove.ErrorMsg() + log_debug("SQL ERROR on proc/vpn_whitelist_remove for ckey '[target_sql_ckey]'. Error : \[[err]\]\n") + return FALSE + return TRUE - +/proc/vpn_whitelist_panel(var/target_ckey as text) + if(!check_rights(R_ADMIN)) + return + if(!target_ckey) + return + var/is_already_whitelisted = vpn_whitelist_check(target_ckey) + if(is_already_whitelisted) + if(vpn_whitelist_remove(target_ckey)) + to_chat(usr, "[target_ckey] was removed from the VPN whitelist.") + else + to_chat(usr, "VPN whitelist unchanged.") + else + if(vpn_whitelist_add(target_ckey)) + to_chat(usr, "[target_ckey] was added to the VPN whitelist.") + else + to_chat(usr, "VPN whitelist unchanged.") \ No newline at end of file diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 8f57b49092e..5cc9296dd6a 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -72,6 +72,7 @@ control_freak = CONTROL_FREAK_ALL | CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS var/ip_intel = "Disabled" + var/iprestricted = FALSE var/datum/click_intercept/click_intercept = null diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 0fdf29f1097..c11e68ff1b1 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -545,13 +545,24 @@ return if(is_connecting_from_localhost()) - log_debug("IPIntel: skip check for player [key_name_admin(src)] connecting from localhost.") + log_debug("check_ip_intel: skip check for player [key_name_admin(src)] connecting from localhost.") return var/datum/ipintel/res = get_ip_intel(address) - if(res.intel >= config.ipintel_rating_bad) - message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.") ip_intel = res.intel + if(ip_intel >= config.ipintel_rating_bad) + if(config.ipintel_whitelist) + if(vpn_whitelist_check(ckey)) + log_debug("IPIntel: Player [ckey] IP [address] rated [ip_intel*100]% passes check due to whitelisted status.") + else + message_admins("IPIntel: [key_name_admin(src)] IP [address] was restricted due to likely proxy/VPN.") + iprestricted = TRUE + vpn_warning() + else + message_admins("IPIntel: [key_name_admin(src)] IP [address] rated [ip_intel*100]% likely to be a Proxy/VPN.") + +/client/proc/vpn_warning() + to_chat(src, "You are using a proxy/VPN. That is not allowed. Either turn it off, or request whitelisting here: [config.banappeals]") #undef TOPIC_SPAM_DELAY #undef UPLOAD_LIMIT diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 427bcfcb659..fe78eaef3b2 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -165,6 +165,9 @@ if(!tos_consent) to_chat(usr, "You must consent to the terms of service before you can join!") return 0 + if(client.iprestricted) + client.vpn_warning() + return 0 ready = !ready new_player_panel_proc() @@ -182,7 +185,11 @@ return 0 if(alert(src,"Are you sure you wish to observe? You cannot normally join the round after doing this!","Player Setup","Yes","No") == "Yes") - if(!client) return 1 + if(!client) + return 1 + if(client.iprestricted) + client.vpn_warning() + return 0 var/mob/dead/observer/observer = new() src << browse(null, "window=playersetup") spawning = 1 @@ -220,7 +227,9 @@ if(!ticker || ticker.current_state != GAME_STATE_PLAYING) to_chat(usr, "The round is either not ready, or has already finished...") return - + if(client.iprestricted) + client.vpn_warning() + return 0 if(client.prefs.species in GLOB.whitelisted_species) if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist) diff --git a/config/example/config.txt b/config/example/config.txt index ca81181c027..8a08ef53e0c 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -191,11 +191,13 @@ GUEST_BAN ## How long to save good matches (ipintel rate limits to 15 per minute and 500 per day. so this shouldn't be too low, getipintel.net suggests 6 hours, time is in hours) (Your ip will get banned if you go over 500 a day too many times) #IPINTEL_SAVE_GOOD 72 ## How long to save bad matches (these numbers can change as ips change hands, best not to save these for too long in case somebody gets a new ip used by a spammer/proxy before.) -#IPINTEL_SAVE_BAD 6 +#IPINTEL_SAVE_BAD 24 ## Domain name to query (leave commented out for the default, only needed if you pay getipintel.net for more querys) #IPINTEL_DOMAIN check.getipintel.net ## Ignore players with this many hours of playtime. Requires USE_EXP_TRACKING #IPINTEL_MAXPLAYTIME 10 +## Require players (except the previous ones with playtime, if enabled) to be whitelisted to use proxies/VPNs. +#IPINTEL_WHITELIST ## Comment to disable checking for the cid randomizer dll. (disabled if database isn't enabled or connected) CHECK_RANDOMIZER From e9a382a6f2751719dfffd2d0a35ffece673a0e9b Mon Sep 17 00:00:00 2001 From: Kyep Date: Fri, 10 May 2019 19:14:51 -0700 Subject: [PATCH 13/21] show if ckey is whitelisted before removing them --- code/modules/admin/ipintel.dm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 83853aa17a4..1ed62b97337 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -172,7 +172,11 @@ return var/is_already_whitelisted = vpn_whitelist_check(target_ckey) if(is_already_whitelisted) - if(vpn_whitelist_remove(target_ckey)) + var/confirm = alert("[target_ckey] is already whitelisted. Remove them?", "Confirm Removal", "No", "Yes") + if(!confirm || confirm != "Yes") + to_chat(usr, "VPN whitelist alteration cancelled.") + return + else if(vpn_whitelist_remove(target_ckey)) to_chat(usr, "[target_ckey] was removed from the VPN whitelist.") else to_chat(usr, "VPN whitelist unchanged.") From 3662b902658786c0676a215479eb572aae75853e Mon Sep 17 00:00:00 2001 From: Kyep Date: Tue, 28 May 2019 11:35:53 -0700 Subject: [PATCH 14/21] fix, 'more info' for admins, better whitelist support, pre-client bans --- code/controllers/configuration.dm | 3 ++ code/modules/admin/IsBanned.dm | 17 ++++++++++- code/modules/admin/ipintel.dm | 37 +++++++++++++++++++++++ code/modules/client/client defines.dm | 1 - code/modules/client/client procs.dm | 28 +++++++++-------- code/modules/mob/new_player/new_player.dm | 9 ------ config/example/config.txt | 2 ++ goon/code/datums/browserOutput.dm | 2 +- 8 files changed, 75 insertions(+), 24 deletions(-) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 580bb6687fa..8577ce3d864 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -135,6 +135,7 @@ var/ipintel_domain = "check.getipintel.net" var/ipintel_maxplaytime = 0 var/ipintel_whitelist = 0 + var/ipintel_detailsurl = "https://iphub.info/?ip=" var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt @@ -326,6 +327,8 @@ config.ipintel_maxplaytime = text2num(value) if("ipintel_whitelist") config.ipintel_whitelist = 1 + if("ipintel_detailsurl") + config.ipintel_detailsurl = value if("log_ooc") config.log_ooc = 1 diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index ec9fa9af05e..9245f5174ce 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -1,5 +1,12 @@ //Blocks an attempt to connect before even creating our client datum thing. -world/IsBanned(key,address,computer_id) +world/IsBanned(key, address, computer_id, check_ipintel = TRUE) + + if(!config.ban_legacy_system) + if(address) + address = sanitizeSQL(address) + if(computer_id) + computer_id = sanitizeSQL(computer_id) + if(!key || !address || !computer_id) log_adminwarn("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.") @@ -31,6 +38,14 @@ world/IsBanned(key,address,computer_id) mistakemessage = "\nIf you believe this is a mistake, please request help at [config.banappeals]." return list("reason"="using Tor", "desc"="\nReason: The network you are using to connect has been banned.[mistakemessage]") + //check if the IP address is a known proxy/vpn, and the user is not whitelisted + if(check_ipintel && config.ipintel_email && config.ipintel_whitelist && ipintel_is_banned(key, address)) + log_adminwarn("Failed Login: [key] [computer_id] [address] - Proxy/VPN") + var/mistakemessage = "" + if(config.banappeals) + mistakemessage = "\nIf you have to use one, request whitelisting at: [config.banappeals]" + return list("reason"="using proxy or vpn", "desc"="\nReason: Proxies/VPNs are not allowed here. [mistakemessage]") + if(config.ban_legacy_system) //Ban Checking diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 1ed62b97337..76ae23f548e 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -132,7 +132,44 @@ log_game("IPINTEL: [text]") log_debug("IPINTEL: [text]") + +/proc/ipintel_is_banned(t_ckey, t_ip) + if(!config.ipintel_email) + return FALSE + if(!config.ipintel_whitelist) + return FALSE + if(!dbcon.IsConnected()) + return FALSE + if(!ipintel_badip_check(t_ip)) + return FALSE + if(vpn_whitelist_check(t_ckey)) + return FALSE + return TRUE + +/proc/ipintel_badip_check(target_ip) + var/rating_bad = config.ipintel_rating_bad + if(!rating_bad) + log_debug("ipintel_badip_check reports misconfigured rating_bad directive") + return FALSE + var/valid_hours = config.ipintel_save_bad + if(!valid_hours) + log_debug("ipintel_badip_check reports misconfigured ipintel_save_bad directive") + return FALSE + var/check_sql = {"SELECT * FROM [format_table_name("ipintel")] WHERE ip = INET_ATON('[target_ip]') AND intel >= [rating_bad] AND (date + INTERVAL [valid_hours] HOUR) > NOW()"} + var/DBQuery/query_get_ip_intel = dbcon.NewQuery(check_sql) + if(!query_get_ip_intel.Execute()) + log_debug("ipintel_badip_check reports failed query execution") + qdel(query_get_ip_intel) + return FALSE + if(!query_get_ip_intel.NextRow()) + qdel(query_get_ip_intel) + return FALSE + qdel(query_get_ip_intel) + return TRUE + /proc/vpn_whitelist_check(target_ckey) + if(!config.ipintel_whitelist) + return FALSE var/target_sql_ckey = ckey(target_ckey) var/DBQuery/query_whitelist_check = dbcon.NewQuery("SELECT * FROM [format_table_name("vpn_whitelist")] WHERE ckey = '[target_sql_ckey]'") if(!query_whitelist_check.Execute()) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index f6e8f984117..118dea16cb9 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -72,7 +72,6 @@ control_freak = CONTROL_FREAK_ALL | CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS var/ip_intel = "Disabled" - var/iprestricted = FALSE var/datum/click_intercept/click_intercept = null diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 5c9936fbfdb..f62ed3b3ac7 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -557,21 +557,25 @@ log_debug("check_ip_intel: skip check for player [key_name_admin(src)] connecting from localhost.") return + if(vpn_whitelist_check(ckey)) + log_debug("check_ip_intel: skip check for player [key_name_admin(src)] [address] on whitelist.") + return + var/datum/ipintel/res = get_ip_intel(address) ip_intel = res.intel - if(ip_intel >= config.ipintel_rating_bad) - if(config.ipintel_whitelist) - if(vpn_whitelist_check(ckey)) - log_debug("IPIntel: Player [ckey] IP [address] rated [ip_intel*100]% passes check due to whitelisted status.") - else - message_admins("IPIntel: [key_name_admin(src)] IP [address] was restricted due to likely proxy/VPN.") - iprestricted = TRUE - vpn_warning() - else - message_admins("IPIntel: [key_name_admin(src)] IP [address] rated [ip_intel*100]% likely to be a Proxy/VPN.") + verify_ip_intel() + +/client/proc/verify_ip_intel() + if(ip_intel >= config.ipintel_rating_bad) + var/detailsurl = config.ipintel_detailsurl ? "(IP Info)" : "" + if(config.ipintel_whitelist) + message_admins("IPIntel: [key_name_admin(src)] on IP [address] was rejected. [detailsurl]") + // Don't send them a message. They won't see it at this stage. Instead, they see a message when they try to reconnect. + qdel(src) + else + message_admins("IPIntel: [key_name_admin(src)] on IP [address] is likely to be using a Proxy/VPN. [detailsurl]") + -/client/proc/vpn_warning() - to_chat(src, "You are using a proxy/VPN. That is not allowed. Either turn it off, or request whitelisting here: [config.banappeals]") #undef TOPIC_SPAM_DELAY #undef UPLOAD_LIMIT diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 7f426e6997a..a30afc2f74a 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -165,9 +165,6 @@ if(!tos_consent) to_chat(usr, "You must consent to the terms of service before you can join!") return 0 - if(client.iprestricted) - client.vpn_warning() - return 0 ready = !ready new_player_panel_proc() @@ -187,9 +184,6 @@ if(alert(src,"Are you sure you wish to observe? You cannot normally join the round after doing this!","Player Setup","Yes","No") == "Yes") if(!client) return 1 - if(client.iprestricted) - client.vpn_warning() - return 0 var/mob/dead/observer/observer = new() src << browse(null, "window=playersetup") spawning = 1 @@ -227,9 +221,6 @@ if(!ticker || ticker.current_state != GAME_STATE_PLAYING) to_chat(usr, "The round is either not ready, or has already finished...") return - if(client.iprestricted) - client.vpn_warning() - return 0 if(client.prefs.species in GLOB.whitelisted_species) if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist) diff --git a/config/example/config.txt b/config/example/config.txt index f5cf11def32..740e29dbc15 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -198,6 +198,8 @@ GUEST_BAN #IPINTEL_MAXPLAYTIME 10 ## Require players (except the previous ones with playtime, if enabled) to be whitelisted to use proxies/VPNs. #IPINTEL_WHITELIST +## URL to show to admins to provide more information about an IP address. Leave undefined for default. +#IPINTEL_DETAILSURL https://iphub.info/?ip= ## Comment to disable checking for the cid randomizer dll. (disabled if database isn't enabled or connected) CHECK_RANDOMIZER diff --git a/goon/code/datums/browserOutput.dm b/goon/code/datums/browserOutput.dm index 28a2690b5f6..3456e0a2ce9 100644 --- a/goon/code/datums/browserOutput.dm +++ b/goon/code/datums/browserOutput.dm @@ -149,7 +149,7 @@ var/list/chatResources = list( var/list/row = connectionHistory[i] if(!row || row.len < 3 || !(row["ckey"] && row["compid"] && row["ip"])) return - if(world.IsBanned(row["ckey"], row["compid"], row["ip"])) + if(world.IsBanned(row["ckey"], row["compid"], row["ip"], FALSE)) found = row break From eaeea7ce2fe2995dd4b8d876ed0ae2c28e4f85a1 Mon Sep 17 00:00:00 2001 From: Kyep Date: Wed, 29 May 2019 14:56:28 -0700 Subject: [PATCH 15/21] adds forum link, removes boxen discord table --- SQL/paradise_schema.sql | 16 +----- SQL/paradise_schema_prefixed.sql | 16 +----- SQL/updates/5-6.sql | 24 +++++++-- code/controllers/configuration.dm | 5 ++ code/modules/admin/DB ban/functions.dm | 15 +++++- code/modules/client/client procs.dm | 45 +++++++++++++++- code/modules/client/preference/preferences.dm | 1 + .../client/preference/preferences_mysql.dm | 5 +- code/modules/discord/accountlink.dm | 52 ------------------- code/modules/discord/notify.dm | 48 ----------------- config/example/config.txt | 3 ++ paradise.dme | 2 - 12 files changed, 96 insertions(+), 136 deletions(-) delete mode 100644 code/modules/discord/accountlink.dm delete mode 100644 code/modules/discord/notify.dm diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 1845880ba52..e0f33226e8c 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -266,6 +266,8 @@ CREATE TABLE `player` ( `exp` mediumtext, `clientfps` smallint(4) DEFAULT '0', `atklog` smallint(4) DEFAULT '0', + `fuid` BIGINT(20) NULL DEFAULT NULL, + `fupdate` SMALLINT(4) NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `ckey` (`ckey`) ) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1; @@ -522,20 +524,6 @@ CREATE TABLE `memo` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Table structure for table `discord` --- -DROP TABLE IF EXISTS `discord`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE IF NOT EXISTS `discord` ( - `ckey` varchar(32) NOT NULL, - `discord_id` bigint(20) NOT NULL, - `notify` int(11) NOT NULL, - PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `ipintel` -- diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql index 75dc19ff8cb..ecb28a867e4 100644 --- a/SQL/paradise_schema_prefixed.sql +++ b/SQL/paradise_schema_prefixed.sql @@ -265,6 +265,8 @@ CREATE TABLE `SS13_player` ( `exp` mediumtext, `clientfps` smallint(4) DEFAULT '0', `atklog` smallint(4) DEFAULT '0', + `fuid` BIGINT(20) NULL DEFAULT NULL, + `fupdate` SMALLINT(4) NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `ckey` (`ckey`) ) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1; @@ -521,20 +523,6 @@ CREATE TABLE `SS13_memo` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; --- --- Table structure for table `SS13_discord` --- -DROP TABLE IF EXISTS `SS13_discord`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE IF NOT EXISTS `SS13_discord` ( - `ckey` varchar(32) NOT NULL, - `discord_id` bigint(20) NOT NULL, - `notify` int(11) NOT NULL, - PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - -- -- Table structure for table `SS13_ipintel` -- diff --git a/SQL/updates/5-6.sql b/SQL/updates/5-6.sql index 67021b90e8b..91bf4328a2a 100644 --- a/SQL/updates/5-6.sql +++ b/SQL/updates/5-6.sql @@ -1,6 +1,6 @@ #Updating the SQL from version 5 to version 6. -Kyep -#Creating a table to track the results of VPN/proxy lookups for IPs +#Make a table to track the results of VPN/proxy lookups for IPs (IPINTEL, TG PORT) CREATE TABLE `ipintel` ( `ip` INT UNSIGNED NOT NULL , `date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL , @@ -8,9 +8,27 @@ CREATE TABLE `ipintel` ( PRIMARY KEY ( `ip` ) ) ENGINE = INNODB; -#Creating a table to track which ckeys are whitelisted for use of VPNs +#Make a table to track which ckeys are whitelisted for use of VPNs (IPINTEL, CUSTOM) CREATE TABLE `vpn_whitelist` ( `ckey` VARCHAR(32) NOT NULL, `reason` text, PRIMARY KEY (`ckey`) -) ENGINE=INNODB; \ No newline at end of file +) ENGINE=INNODB; + +# Add fuid (forum userid) which enables quick lookup of which ckey is associated with a specific forum account. (FORUM LINK) +ALTER TABLE `player` ADD `fuid` BIGINT(20) NULL DEFAULT NULL; +ALTER TABLE `player` ADD INDEX(`fuid`); + +# Add fupdate (forum update required) which flags specific ckeys as having been banned/unbanned, which requires an update of their forum/etc permissions (FORUM LINK) +ALTER TABLE `player` ADD `fupdate` SMALLINT(4) NULL DEFAULT 0; +ALTER TABLE `player` ADD INDEX(`fupdate`); + +#Make a table to track oauth tokens for linking forum/web accounts (FORUM LINK) +CREATE TABLE `oauth_tokens` ( + `ckey` VARCHAR(32) NOT NULL, + `token` VARCHAR(32) NOT NULL, + PRIMARY KEY (`token`) +) ENGINE=INNODB; + +#Drop the old 'discord' table that is not used anymore +DROP TABLE `discord`; diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 8577ce3d864..c87af091f89 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -137,6 +137,8 @@ var/ipintel_whitelist = 0 var/ipintel_detailsurl = "https://iphub.info/?ip=" + var/forum_link_url + var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database @@ -330,6 +332,9 @@ if("ipintel_detailsurl") config.ipintel_detailsurl = value + if("forum_link_url") + config.forum_link_url = value + if("log_ooc") config.log_ooc = 1 diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 5eec9d76bdd..89d4c88d26a 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -150,6 +150,8 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = if(isjobban) jobban_client_fullban(ckey, job) + else + flag_account_for_forum_sync(ckey) datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") @@ -228,6 +230,8 @@ datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") DB_ban_unban_by_id(ban_id) if(isjobban) jobban_unban_client(ckey, job) + else + flag_account_for_forum_sync(ckey) datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) @@ -333,6 +337,8 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) var/DBQuery/query_update = dbcon.NewQuery(sql_update) query_update.Execute() + flag_account_for_forum_sync(pckey) + /client/proc/DB_ban_panel() set category = "Admin" @@ -566,4 +572,11 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) output += "" - usr << browse(output,"window=lookupbans;size=900x700") \ No newline at end of file + usr << browse(output,"window=lookupbans;size=900x700") + +/proc/flag_account_for_forum_sync(var/ckey) + if(!dbcon) + return + var/sql = "UPDATE [format_table_name("player")] SET fupdate = 1 WHERE ckey = '[ckey]'" + var/DBQuery/adm_query = dbcon.NewQuery(sql) + adm_query.Execute() diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index f62ed3b3ac7..dce3f86c6b9 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -229,7 +229,8 @@ if(href_list["ssdwarning"]) ssd_warning_acknowledged = TRUE to_chat(src, "SSD warning acknowledged.") - + if(href_list["link_forum_account"]) + link_forum_account() switch(href_list["action"]) if("openLink") src << link(href_list["link"]) @@ -393,6 +394,8 @@ generate_clickcatcher() apply_clickcatcher() + check_forum_link() + if(custom_event_msg && custom_event_msg != "") to_chat(src, "

Custom Event

") to_chat(src, "

A custom event is taking place. OOC Info:

") @@ -576,6 +579,46 @@ message_admins("IPIntel: [key_name_admin(src)] on IP [address] is likely to be using a Proxy/VPN. [detailsurl]") +/client/proc/check_forum_link() + if(config.forum_link_url && prefs && !prefs.fuid) + to_chat(src, "You do not have your forum account linked. LINK FORUM ACCOUNT") + +/client/proc/create_oauth_token() + var/DBQuery/query_find_token = dbcon.NewQuery("SELECT token FROM [format_table_name("oauth_tokens")] WHERE ckey = '[ckey]' limit 1") + if(!query_find_token.Execute()) + log_debug("create_oauth_token: failed db read") + return + if(query_find_token.NextRow()) + return query_find_token.item[1] + var/tokenstr = md5("[ckey][rand()]") + var/DBQuery/query_insert_token = dbcon.NewQuery("INSERT INTO [format_table_name("oauth_tokens")] (ckey, token) VALUES('[ckey]','[tokenstr]')") + if(!query_insert_token.Execute()) + return + return tokenstr + +/client/proc/link_forum_account() + if(IsGuestKey(key)) + to_chat(src, "Guest keys cannot be linked.") + return + if(prefs && prefs.fuid) + to_chat(src, "Your forum account is already set.") + return + var/DBQuery/query_find_link = dbcon.NewQuery("SELECT fuid FROM [format_table_name("player")] WHERE ckey = '[ckey]' limit 1") + if(!query_find_link.Execute()) + log_debug("link_forum_account: failed db read") + return + if(query_find_link.NextRow()) + if(query_find_link.item[1]) + to_chat(src, "Your forum account is already set. (" + query_find_link.item[1] + ")") + return + var/tokenid = create_oauth_token() + if(!tokenid) + to_chat(src, "link_forum_account: unable to create token") + return + var/url = "[config.forum_link_url][tokenid]" + to_chat(src, {"Now opening a windows to verify your information with the forums. If the window does not load, please go to: [url]."}) + src << link(url) + return #undef TOPIC_SPAM_DELAY #undef UPLOAD_LIMIT diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index 632c5053b48..da56e311fbf 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -92,6 +92,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/windowflashing = TRUE var/clientfps = 0 var/atklog = ATKLOG_ALL + var/fuid // forum userid //ghostly preferences var/ghost_anonsay = 0 diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm index 6b72d71bc9b..90a4bed6f14 100644 --- a/code/modules/client/preference/preferences_mysql.dm +++ b/code/modules/client/preference/preferences_mysql.dm @@ -18,7 +18,8 @@ ghost_anonsay, exp, clientfps, - atklog + atklog, + fuid FROM [format_table_name("player")] WHERE ckey='[C.ckey]'"} ) @@ -50,6 +51,7 @@ exp = query.item[16] clientfps = text2num(query.item[17]) atklog = text2num(query.item[18]) + fuid = text2num(query.item[19]) //Sanitize ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor)) @@ -69,6 +71,7 @@ exp = sanitize_text(exp, initial(exp)) clientfps = sanitize_integer(clientfps, 0, 1000, initial(clientfps)) atklog = sanitize_integer(atklog, 0, 100, initial(atklog)) + fuid = sanitize_integer(fuid, 0, 10000000, initial(fuid)) return 1 /datum/preferences/proc/save_preferences(client/C) diff --git a/code/modules/discord/accountlink.dm b/code/modules/discord/accountlink.dm deleted file mode 100644 index c65d7da1440..00000000000 --- a/code/modules/discord/accountlink.dm +++ /dev/null @@ -1,52 +0,0 @@ -// DONT TOUCH ANYTHING IN HERE UNLESS YOU KNOW WHAT YOU ARE DOING -affected -/client/verb/linkdiscord() - set category = "Special Verbs" - set name = "Link Discord Account" - set desc = "Link your discord account to your BYOND account." - var/user_ckey = sanitizeSQL(usr.ckey) // Probably not neccassary but better safe than sorry - if(!config.sql_enabled) - to_chat(src, "This is feature requires the SQL backend") - return - var/DBQuery/db_discord_id = dbcon.NewQuery("SELECT discord_id FROM [format_table_name("discord")] WHERE ckey = '[user_ckey]'") - if(!db_discord_id.Execute()) - var/err = db_discord_id.ErrorMsg() - log_game("SQL ERROR while selecting discord account. Error : \[[err]\]\n") - to_chat(src, "Error checking Discord account. Please inform an administrator") - return - var/stored_id - while(db_discord_id.NextRow()) - stored_id = db_discord_id.item[1] - if(!stored_id) // Not linked - var/know_how = alert("Do you know how to get a discord user ID? This ID is NOT your discord username and numbers!","Question","Yes","No") - if(know_how == "No") // Opens discord support on how to collect IDs - src << link("https://support.discordapp.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID") - var/entered_id = input("Please enter your Discord ID.", "Enter Discord ID", null, null) as text|null - var/sql_id = sanitizeSQL(entered_id) - var/DBQuery/store_discord_id = dbcon.NewQuery("INSERT INTO [format_table_name("discord")] (ckey, discord_id, notify) VALUES ('[user_ckey]', [sql_id], 0)") - if(!store_discord_id.Execute()) - var/err = db_discord_id.ErrorMsg() - log_game("SQL ERROR while linking discord account. Error : \[[err]\]\n") - to_chat(src, "Error linking Discord account. Please inform an administrator") - return - to_chat(src, "Successfully linked discord account [entered_id] to [user_ckey]") - else // Linked - var/choice = alert("You already have the Discord Account [stored_id] linked to [user_ckey]. Would you like to unlink or replace","Already Linked","Unlink","Replace") - switch(choice) - if("Unlink") - var/DBQuery/unlink_discord_id = dbcon.NewQuery("DELETE FROM [format_table_name("discord")] WHERE ckey = '[user_ckey]'") - if(!unlink_discord_id.Execute()) - var/err = unlink_discord_id.ErrorMsg() - log_game("SQL ERROR while unlinking discord account. Error : \[[err]\]\n") - to_chat(src, "Error unlinking Discord account. Please inform an administrator") - return - to_chat(src, "Successfully unlinked discord account") - if("Replace") - var/entered_id = input("Please enter your Discord ID. Instructions can be found at https://bit.ly/2AfUu40", "Enter Discord ID", null, null) as text|null - var/sql_id = sanitizeSQL(entered_id) - var/DBQuery/store_discord_id = dbcon.NewQuery("UPDATE [format_table_name("discord")] SET discord_id = '[sql_id]' WHERE ckey='[user_ckey]'") - if(!store_discord_id.Execute()) - var/err = db_discord_id.ErrorMsg() - to_chat(src, "Error replacing Discord account. Please inform an administrator") - log_game("SQL ERROR while linking discord account. Error : \[[err]\]\n") - return - to_chat(src, "Successfully linked discord account [entered_id] to [user_ckey]") diff --git a/code/modules/discord/notify.dm b/code/modules/discord/notify.dm deleted file mode 100644 index a5bfac04940..00000000000 --- a/code/modules/discord/notify.dm +++ /dev/null @@ -1,48 +0,0 @@ -// DONT TOUCH ANYTHING IN HERE UNLESS YOU KNOW WHAT YOU ARE DOING -affected -/client/verb/notify_restart() - set category = "Special Verbs" - set name = "Notify Restart" - set desc = "Notifies you on Discord when the server restarts." - if(!config.sql_enabled) - to_chat(src, "This is feature requires the SQL backend") - return - var/DBQuery/db_discord_id = dbcon.NewQuery("SELECT discord_id FROM [format_table_name("discord")] WHERE ckey = '[ckey]'") - if(!db_discord_id.Execute()) - var/err = db_discord_id.ErrorMsg() - log_game("SQL ERROR while selecting discord account. Error : \[[err]\]\n") - to_chat(src, "Error checking Discord account. Please inform an administrator") - return - var/stored_id - while(db_discord_id.NextRow()) - stored_id = db_discord_id.item[1] - if(!stored_id) // Not linked - to_chat(src, "This requires you to link your Discord account with the \"Link Discord Account\" verb.") - return - else // Linked - var/DBQuery/toggle_status = dbcon.NewQuery("SELECT notify FROM [format_table_name("discord")] WHERE ckey = '[ckey]'") - if(!toggle_status.Execute()) - var/err = toggle_status.ErrorMsg() - log_game("SQL ERROR while getting discord account. Error : \[[err]\]\n") - var/notify_status - while(toggle_status.NextRow()) - notify_status = toggle_status.item[1] - if(notify_status) // Data is there - switch(notify_status) - if("0") - var/DBQuery/update_notify = dbcon.NewQuery("UPDATE [format_table_name("discord")] SET notify = 1 WHERE ckey='[ckey]'") - if(!update_notify.Execute()) - var/err = db_discord_id.ErrorMsg() - to_chat(src, "Error updating notify status. Please inform an administrator") - log_game("SQL ERROR while updating notify status. Error : \[[err]\]\n") - return - to_chat(src, "You will now be notified on discord when the next round is starting") - if("1") - var/DBQuery/update_notify = dbcon.NewQuery("UPDATE [format_table_name("discord")] SET notify = 0 WHERE ckey='[ckey]'") - if(!update_notify.Execute()) - var/err = db_discord_id.ErrorMsg() - to_chat(src, "Error updating notify status. Please inform an administrator") - log_game("SQL ERROR while updating notify status. Error : \[[err]\]\n") - return - to_chat(src, "You will no longer be notified on discord when the next round is starting") - else // Oh fuck what the hell happened - to_chat(src, "Something has gone VERY wrong, or affected cant code.") diff --git a/config/example/config.txt b/config/example/config.txt index 740e29dbc15..ccc104d02c6 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -201,6 +201,9 @@ GUEST_BAN ## URL to show to admins to provide more information about an IP address. Leave undefined for default. #IPINTEL_DETAILSURL https://iphub.info/?ip= +## URL to use to link forum accounts. If not set, no link option will be offered. +#FORUM_LINK_URL https://example.com/link.php?token= + ## Comment to disable checking for the cid randomizer dll. (disabled if database isn't enabled or connected) CHECK_RANDOMIZER diff --git a/paradise.dme b/paradise.dme index 55165e5d4ca..39acaab02dc 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1385,8 +1385,6 @@ #include "code\modules\detective_work\evidence.dm" #include "code\modules\detective_work\footprints_and_rag.dm" #include "code\modules\detective_work\scanner.dm" -#include "code\modules\discord\accountlink.dm" -#include "code\modules\discord\notify.dm" #include "code\modules\economy\Accounts.dm" #include "code\modules\economy\Accounts_DB.dm" #include "code\modules\economy\ATM.dm" From 38ed982ad0a5f3d9e8c19419cd1e5ebc9ce4ac64 Mon Sep 17 00:00:00 2001 From: Kyep Date: Thu, 30 May 2019 13:49:46 -0700 Subject: [PATCH 16/21] removes ircNotify --- code/modules/ext_scripts/irc.dm | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/code/modules/ext_scripts/irc.dm b/code/modules/ext_scripts/irc.dm index 0e8c72496d2..7f87413694a 100644 --- a/code/modules/ext_scripts/irc.dm +++ b/code/modules/ext_scripts/irc.dm @@ -19,20 +19,3 @@ if(config.admin_irc) send2irc(config.admin_irc, msg) return - -/hook/startup/proc/ircNotify() - var/people_to_ping = "" - var/DBQuery/pull_notify = dbcon.NewQuery("SELECT discord_id FROM [format_table_name("discord")] WHERE notify = 1") - if(!pull_notify.Execute()) - var/err = pull_notify.ErrorMsg() - log_game("SQL ERROR while pulling notify people. Error : \[[err]\]\n") - else - while(pull_notify.NextRow()) - people_to_ping += "<@" + pull_notify.item[1] + "> " - send2mainirc("Server starting up on [station_name()]. Connect to: "+people_to_ping, 7355608) - // Set notify to 0 so people arent pinged round after round - var/DBQuery/reset_notify = dbcon.NewQuery("UPDATE [format_table_name("discord")] SET notify = 0 WHERE notify = 1") - if(!reset_notify.Execute()) - var/err = reset_notify.ErrorMsg() - log_game("SQL ERROR while resetting notify status. Error : \[[err]\]\n") - return 1 From fef59b477294aff5e17ca355ec55b3f464f17b8d Mon Sep 17 00:00:00 2001 From: Kyep Date: Thu, 30 May 2019 17:40:18 -0700 Subject: [PATCH 17/21] crazylemon comments --- code/modules/admin/DB ban/functions.dm | 5 +++-- code/modules/admin/ipintel.dm | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 89d4c88d26a..1ce6c2b90f0 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -574,9 +574,10 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) usr << browse(output,"window=lookupbans;size=900x700") -/proc/flag_account_for_forum_sync(var/ckey) +/proc/flag_account_for_forum_sync(ckey) if(!dbcon) return - var/sql = "UPDATE [format_table_name("player")] SET fupdate = 1 WHERE ckey = '[ckey]'" + var/skey = sanitizeSQL(ckey) + var/sql = "UPDATE [format_table_name("player")] SET fupdate = 1 WHERE ckey = '[skey]'" var/DBQuery/adm_query = dbcon.NewQuery(sql) adm_query.Execute() diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 76ae23f548e..21c79ade137 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -15,14 +15,15 @@ if(intel < 0) return if(intel <= config.ipintel_rating_bad) - if(world.realtime < cacherealtime + (config.ipintel_save_good * 60 * 60 * 10)) + if(world.realtime < cacherealtime + (config.ipintel_save_good * HOURS)) return TRUE else - if(world.realtime < cacherealtime + (config.ipintel_save_bad * 60 * 60 * 10)) + if(world.realtime < cacherealtime + (config.ipintel_save_bad * HOURS)) return TRUE /proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE) var/datum/ipintel/res = new() + ip = sanitizeSQL(ip) res.ip = ip . = res if(!ip || !config.ipintel_email || !SSipintel.enabled) @@ -123,7 +124,7 @@ if(retryed) SSipintel.errors++ error += " Could not check [ip]. Disabling IPINTEL for [SSipintel.errors] minute[( SSipintel.errors == 1 ? "" : "s" )]" - SSipintel.throttle = world.timeofday + (10 * 120 * SSipintel.errors) + SSipintel.throttle = world.timeofday + (2 * SSipintel.errors * MINUTES) else error += " Attempting retry on [ip]." log_ipintel(error) @@ -155,6 +156,9 @@ if(!valid_hours) log_debug("ipintel_badip_check reports misconfigured ipintel_save_bad directive") return FALSE + ipintel = sanitizeSQL(ipintel) + target_ip = sanitizeSQL(target_ip) + valid_hours = sanitizeSQL(valid_hours) var/check_sql = {"SELECT * FROM [format_table_name("ipintel")] WHERE ip = INET_ATON('[target_ip]') AND intel >= [rating_bad] AND (date + INTERVAL [valid_hours] HOUR) > NOW()"} var/DBQuery/query_get_ip_intel = dbcon.NewQuery(check_sql) if(!query_get_ip_intel.Execute()) @@ -202,7 +206,7 @@ return FALSE return TRUE -/proc/vpn_whitelist_panel(var/target_ckey as text) +/proc/vpn_whitelist_panel(target_ckey as text) if(!check_rights(R_ADMIN)) return if(!target_ckey) From 65f73b9a8ca34f97e703eb4985c8ba3ca71c0ff4 Mon Sep 17 00:00:00 2001 From: Kyep Date: Thu, 30 May 2019 17:46:16 -0700 Subject: [PATCH 18/21] reverts use of HOURS macro until I can find a way that works --- code/modules/admin/ipintel.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 21c79ade137..5e990c7b4d8 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -15,10 +15,10 @@ if(intel < 0) return if(intel <= config.ipintel_rating_bad) - if(world.realtime < cacherealtime + (config.ipintel_save_good * HOURS)) + if(world.realtime < cacherealtime + (config.ipintel_save_good * 60 * 60 * 10)) return TRUE else - if(world.realtime < cacherealtime + (config.ipintel_save_bad * HOURS)) + if(world.realtime < cacherealtime + (config.ipintel_save_bad * 60 * 60 * 10)) return TRUE /proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE) From 6fe42d596ded7ff6ea32206a6190dc300ea3c121 Mon Sep 17 00:00:00 2001 From: Kyep Date: Thu, 30 May 2019 17:49:19 -0700 Subject: [PATCH 19/21] squash hour macro changes --- code/modules/admin/ipintel.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 5e990c7b4d8..75f2422681a 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -15,10 +15,10 @@ if(intel < 0) return if(intel <= config.ipintel_rating_bad) - if(world.realtime < cacherealtime + (config.ipintel_save_good * 60 * 60 * 10)) + if(world.realtime < cacherealtime + (config.ipintel_save_good HOURS)) return TRUE else - if(world.realtime < cacherealtime + (config.ipintel_save_bad * 60 * 60 * 10)) + if(world.realtime < cacherealtime + (config.ipintel_save_bad HOURS)) return TRUE /proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE) @@ -124,7 +124,7 @@ if(retryed) SSipintel.errors++ error += " Could not check [ip]. Disabling IPINTEL for [SSipintel.errors] minute[( SSipintel.errors == 1 ? "" : "s" )]" - SSipintel.throttle = world.timeofday + (2 * SSipintel.errors * MINUTES) + SSipintel.throttle = world.timeofday + (2 * SSipintel.errors MINUTES) else error += " Attempting retry on [ip]." log_ipintel(error) @@ -156,8 +156,8 @@ if(!valid_hours) log_debug("ipintel_badip_check reports misconfigured ipintel_save_bad directive") return FALSE - ipintel = sanitizeSQL(ipintel) target_ip = sanitizeSQL(target_ip) + rating_bad = sanitizeSQL(rating_bad) valid_hours = sanitizeSQL(valid_hours) var/check_sql = {"SELECT * FROM [format_table_name("ipintel")] WHERE ip = INET_ATON('[target_ip]') AND intel >= [rating_bad] AND (date + INTERVAL [valid_hours] HOUR) > NOW()"} var/DBQuery/query_get_ip_intel = dbcon.NewQuery(check_sql) From cbfe1ce906d6665142c1e0ff442090f796bb572a Mon Sep 17 00:00:00 2001 From: Kyep Date: Thu, 30 May 2019 18:06:10 -0700 Subject: [PATCH 20/21] Message before disconnecting --- code/modules/client/client procs.dm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index dce3f86c6b9..9a41733e8a2 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -572,9 +572,13 @@ if(ip_intel >= config.ipintel_rating_bad) var/detailsurl = config.ipintel_detailsurl ? "(IP Info)" : "" if(config.ipintel_whitelist) - message_admins("IPIntel: [key_name_admin(src)] on IP [address] was rejected. [detailsurl]") - // Don't send them a message. They won't see it at this stage. Instead, they see a message when they try to reconnect. - qdel(src) + spawn(40) // This is necessary because without it, they won't see the message, and addtimer cannot be used because the timer system may not have initialized yet + message_admins("IPIntel: [key_name_admin(src)] on IP [address] was rejected. [detailsurl]") + var/blockmsg = "Error: proxy/VPN detected. Proxy/VPN use is not allowed here. Deactivate it before you reconnect." + if(config.banappeals) + blockmsg += "\nIf you are not actually using a proxy/VPN, or have no choice but to use one, request whitelisting at: [config.banappeals]" + to_chat(src, blockmsg) + qdel(src) else message_admins("IPIntel: [key_name_admin(src)] on IP [address] is likely to be using a Proxy/VPN. [detailsurl]") From 6582f22be5f5565a53ee496d9a551b93fdc076f5 Mon Sep 17 00:00:00 2001 From: ParadiseSS13-Bot Date: Thu, 30 May 2019 22:57:20 -0400 Subject: [PATCH 21/21] Automatic changelog generation for PR #11359 [ci skip] --- html/changelogs/AutoChangeLog-pr-11359.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-11359.yml diff --git a/html/changelogs/AutoChangeLog-pr-11359.yml b/html/changelogs/AutoChangeLog-pr-11359.yml new file mode 100644 index 00000000000..3f93a5677ef --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-11359.yml @@ -0,0 +1,6 @@ +author: "Kyep" +delete-after: True +changes: + - rscadd: "Ported XKeyScore from TG." + - rscadd: "New system to securely link ingame/forum accounts" + - rscdel: "Removed old insecure 'link discord account' system"