]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Implement BlacklistedRange exempt flag (#29258)
authornikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com>
Fri, 21 Jun 2024 12:06:07 +0000 (12:06 +0000)
committerGitHub <noreply@github.com>
Fri, 21 Jun 2024 12:06:07 +0000 (14:06 +0200)
* Implement a new kind of ip range ban that only applies to new players

* Put determining whether a player record exists to its own function

* Make BlacklistedRange bans get bypassed by any ban exemption

* Stop trying to get another DbGuard while already having one

This does break with convention on the functions in that area but
considering the use of this function it's probably fine?
I could alternatively just move the place it's called from.

Also I was suppossed to wait for tests to finish locally just to be
sure, but nah. I am pushing this now

Content.Server.Database/Model.cs
Content.Server/Database/ServerDbBase.cs
Content.Server/Database/ServerDbPostgres.cs
Content.Server/Database/ServerDbSqlite.cs

index 3510a5422e3c55d583af156a0846702e04f5708d..164cf421bd255f95aed0c27ee5be4c010a54ab56 100644 (file)
@@ -693,6 +693,14 @@ namespace Content.Server.Database
         /// Intended use is for users with shared connections. This should not be used as an alternative to <see cref="Datacenter"/>.
         /// </remarks>
         IP = 1 << 1,
+
+        /// <summary>
+        /// Ban is an IP range that is only applied for first time joins.
+        /// </summary>
+        /// <remarks>
+        /// Intended for use with residential IP ranges that are often used maliciously.
+        /// </remarks>
+        BlacklistedRange = 1 << 2,
         // @formatter:on
     }
 
index cd03af7087b9950c179f2ecfc88bd1db4ae2e615..d91ef3919815820eb749fe8c2d095911e5e9658c 100644 (file)
@@ -625,6 +625,11 @@ namespace Content.Server.Database
             return record == null ? null : MakePlayerRecord(record);
         }
 
+        protected async Task<bool> PlayerRecordExists(DbGuard db, NetUserId userId)
+        {
+            return await db.DbContext.Player.AnyAsync(p => p.UserId == userId);
+        }
+
         [return: NotNullIfNotNull(nameof(player))]
         protected PlayerRecord? MakePlayerRecord(Player? player)
         {
index fd4699fff4e99691877f2ff65a6dc0f3ccd97c9e..f8eef1a554e9af3c62b37442cf4d09cbe21e2753 100644 (file)
@@ -78,7 +78,8 @@ namespace Content.Server.Database
             await using var db = await GetDbImpl();
 
             var exempt = await GetBanExemptionCore(db, userId);
-            var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt)
+            var newPlayer = userId == null || !await PlayerRecordExists(db, userId.Value);
+            var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt, newPlayer)
                 .OrderByDescending(b => b.BanTime);
 
             var ban = await query.FirstOrDefaultAsync();
@@ -98,7 +99,8 @@ namespace Content.Server.Database
             await using var db = await GetDbImpl();
 
             var exempt = await GetBanExemptionCore(db, userId);
-            var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt);
+            var newPlayer = !await db.PgDbContext.Player.AnyAsync(p => p.UserId == userId);
+            var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt, newPlayer);
 
             var queryBans = await query.ToArrayAsync();
             var bans = new List<ServerBanDef>(queryBans.Length);
@@ -122,7 +124,8 @@ namespace Content.Server.Database
             ImmutableArray<byte>? hwId,
             DbGuardImpl db,
             bool includeUnbanned,
-            ServerBanExemptFlags? exemptFlags)
+            ServerBanExemptFlags? exemptFlags,
+            bool newPlayer)
         {
             DebugTools.Assert(!(address == null && userId == null && hwId == null));
 
@@ -141,7 +144,9 @@ namespace Content.Server.Database
             {
                 var newQ = db.PgDbContext.Ban
                     .Include(p => p.Unban)
-                    .Where(b => b.Address != null && EF.Functions.ContainsOrEqual(b.Address.Value, address));
+                    .Where(b => b.Address != null
+                                && EF.Functions.ContainsOrEqual(b.Address.Value, address)
+                                && !(b.ExemptFlags.HasFlag(ServerBanExemptFlags.BlacklistedRange) && !newPlayer));
 
                 query = query == null ? newQ : query.Union(newQ);
             }
@@ -167,6 +172,9 @@ namespace Content.Server.Database
 
             if (exemptFlags is { } exempt)
             {
+                if (exempt != ServerBanExemptFlags.None)
+                    exempt |= ServerBanExemptFlags.BlacklistedRange; // Any kind of exemption should bypass BlacklistedRange
+
                 query = query.Where(b => (b.ExemptFlags & exempt) == 0);
             }
 
index ffec90bb43dc7295122b00da9fd40c6a7c3417df..ce6f97a117104da0ad06a9892cc62d9f0835255f 100644 (file)
@@ -86,11 +86,13 @@ namespace Content.Server.Database
 
             var exempt = await GetBanExemptionCore(db, userId);
 
+            var newPlayer = userId == null || !await PlayerRecordExists(db, userId.Value);
+
             // SQLite can't do the net masking stuff we need to match IP address ranges.
             // So just pull down the whole list into memory.
             var bans = await GetAllBans(db.SqliteDbContext, includeUnbanned: false, exempt);
 
-            return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt)) is { } foundBan
+            return bans.FirstOrDefault(b => BanMatches(b, address, userId, hwId, exempt, newPlayer)) is { } foundBan
                 ? ConvertBan(foundBan)
                 : null;
         }
@@ -103,12 +105,14 @@ namespace Content.Server.Database
 
             var exempt = await GetBanExemptionCore(db, userId);
 
+            var newPlayer = !await db.SqliteDbContext.Player.AnyAsync(p => p.UserId == userId);
+
             // SQLite can't do the net masking stuff we need to match IP address ranges.
             // So just pull down the whole list into memory.
             var queryBans = await GetAllBans(db.SqliteDbContext, includeUnbanned, exempt);
 
             return queryBans
-                .Where(b => BanMatches(b, address, userId, hwId, exempt))
+                .Where(b => BanMatches(b, address, userId, hwId, exempt, newPlayer))
                 .Select(ConvertBan)
                 .ToList()!;
         }
@@ -137,10 +141,18 @@ namespace Content.Server.Database
             IPAddress? address,
             NetUserId? userId,
             ImmutableArray<byte>? hwId,
-            ServerBanExemptFlags? exemptFlags)
+            ServerBanExemptFlags? exemptFlags,
+            bool newPlayer)
         {
+            // Any flag to bypass BlacklistedRange bans.
+            var exemptFromBlacklistedRange = exemptFlags != null && exemptFlags.Value != ServerBanExemptFlags.None;
+
             if (!exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP)
-                && address != null && ban.Address is not null && address.IsInSubnet(ban.Address.ToTuple().Value))
+                && address != null
+                && ban.Address is not null
+                && address.IsInSubnet(ban.Address.ToTuple().Value)
+                && (!ban.ExemptFlags.HasFlag(ServerBanExemptFlags.BlacklistedRange) ||
+                     newPlayer && !exemptFromBlacklistedRange))
             {
                 return true;
             }