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