@@ -210,7 +210,7 @@ public final class Api {
210210 private static final int IPTABLES_TRY_AGAIN = 4 ;
211211 private static final String [] dynChains = {"-3g-postcustom" , "-3g-fork" , "-wifi-postcustom" , "-wifi-fork" };
212212 private static final String [] natChains = {"" , "-tor-check" , "-tor-filter" };
213- private static final String [] staticChains = {"" , "-input" , "-3g" , "-wifi" , "-reject" , "-vpn" , "-3g-tether" , "-3g-home" , "-3g-roam" , "-wifi-tether" , "-wifi-wan" , "-wifi-lan" , "-usb-tether" , "-tor" , "-tor-reject" , "-tether" , "-3g-home-reject" , "-3g-roam-reject" , "-wifi-wan-reject" , "-wifi-lan-reject" , "-vpn-reject" , "-tether-reject" };
213+ private static final String [] staticChains = {"" , "-input" , "-localhost" , "- 3g" , "-wifi" , "-reject" , "-vpn" , "-3g-tether" , "-3g-home" , "-3g-roam" , "-wifi-tether" , "-wifi-wan" , "-wifi-lan" , "-usb-tether" , "-tor" , "-tor-reject" , "-tether" , "-3g-home-reject" , "-3g-roam-reject" , "-wifi-wan-reject" , "-wifi-lan-reject" , "-vpn-reject" , "-tether-reject" };
214214 private static volatile boolean globalStatus = false ;
215215
216216 private static final Object GLOBAL_STATUS_LOCK = new Object ();
@@ -1041,6 +1041,12 @@ private static boolean applyIptablesRulesImpl(final Context ctx, RuleDataSet rul
10411041
10421042 addInterfaceRouting (ctx , cmds , ipv6 , chainName );
10431043
1044+ // Route loopback traffic to localhost chain for per-app localhost blocking
1045+ // This is checked before other interface routing
1046+ if (G .enableLAN ()) {
1047+ cmds .add ("-A " + chainName + " -o lo -j " + chainName + "-localhost" );
1048+ }
1049+
10441050 // send wifi, 3G, VPN packets to the appropriate dynamic chain based on interface
10451051 if (G .enableVPN ()) {
10461052 // if !enableVPN then we ignore those interfaces (pass all traffic)
@@ -1156,6 +1162,23 @@ private static boolean applyIptablesRulesImpl(final Context ctx, RuleDataSet rul
11561162 if (G .enableTor ()) {
11571163 addTorRules (cmds , ruleDataSet .torList , whitelist , ipv6 , chainName );
11581164 }
1165+
1166+ // Add localhost blocking rules - apps with LAN permission can access localhost,
1167+ // others are blocked. This prevents cross-app localhost tracking (Issue #1421)
1168+ if (G .enableLAN ()) {
1169+ // Allow apps in lanList to access localhost
1170+ for (Integer uid : ruleDataSet .lanList ) {
1171+ if (uid != null && uid >= 0 ) {
1172+ cmds .add ("-A " + chainName + "-localhost -m owner --uid-owner " + uid + " -j RETURN" );
1173+ }
1174+ }
1175+ // Allow root for system services that need localhost
1176+ cmds .add ("-A " + chainName + "-localhost -m owner --uid-owner 0 -j RETURN" );
1177+ // Allow shell for adb
1178+ cmds .add ("-A " + chainName + "-localhost -m owner --uid-owner 2000 -j RETURN" );
1179+ // Block all other apps from accessing localhost
1180+ cmds .add ("-A " + chainName + "-localhost -j REJECT" );
1181+ }
11591182 cmds .add ("-P OUTPUT ACCEPT" );
11601183 } catch (Exception e ) {
11611184 Log .e (e .getClass ().getName (), e .getMessage (), e );
0 commit comments