1 /*
2  * Copyright 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * FirewallControllerTest.cpp - unit tests for FirewallController.cpp
17  */
18 
19 #include <string>
20 #include <vector>
21 #include <stdio.h>
22 
23 #include <gtest/gtest.h>
24 
25 #include <android-base/file.h>
26 #include <android-base/stringprintf.h>
27 #include <android-base/strings.h>
28 
29 #include "FirewallController.h"
30 #include "IptablesBaseTest.h"
31 
32 using android::base::Join;
33 using android::base::WriteStringToFile;
34 
35 namespace android {
36 namespace net {
37 
38 class FirewallControllerTest : public IptablesBaseTest {
39 protected:
FirewallControllerTest()40     FirewallControllerTest() {
41         FirewallController::execIptablesRestore = fakeExecIptablesRestore;
42         // This unit test currently doesn't cover the eBPF owner match case so
43         // we have to manually turn eBPF support off.
44         // TODO: find a way to unit test the eBPF code path.
45         mFw.mUseBpfOwnerMatch = false;
46     }
47     FirewallController mFw;
48 
makeUidRules(IptablesTarget a,const char * b,bool c,const std::vector<int32_t> & d)49     std::string makeUidRules(IptablesTarget a, const char* b, bool c,
50                              const std::vector<int32_t>& d) {
51         return mFw.makeUidRules(a, b, c, d);
52     }
53 
createChain(const char * a,FirewallType b)54     int createChain(const char* a, FirewallType b) {
55         return mFw.createChain(a, b);
56     }
57 };
58 
TEST_F(FirewallControllerTest,TestCreateAllowlistChain)59 TEST_F(FirewallControllerTest, TestCreateAllowlistChain) {
60     std::vector<std::string> expectedRestore4 = {
61             "*filter",
62             ":fw_allowlist -",
63             "-A fw_allowlist -m owner --uid-owner 0-9999 -j RETURN",
64             "-A fw_allowlist -m owner ! --uid-owner 0-4294967294 -j RETURN",
65             "-A fw_allowlist -p esp -j RETURN",
66             "-A fw_allowlist -i lo -j RETURN",
67             "-A fw_allowlist -o lo -j RETURN",
68             "-A fw_allowlist -p tcp --tcp-flags RST RST -j RETURN",
69             "-A fw_allowlist -j DROP",
70             "COMMIT\n"};
71     std::vector<std::string> expectedRestore6 = {
72             "*filter",
73             ":fw_allowlist -",
74             "-A fw_allowlist -m owner --uid-owner 0-9999 -j RETURN",
75             "-A fw_allowlist -m owner ! --uid-owner 0-4294967294 -j RETURN",
76             "-A fw_allowlist -p esp -j RETURN",
77             "-A fw_allowlist -i lo -j RETURN",
78             "-A fw_allowlist -o lo -j RETURN",
79             "-A fw_allowlist -p tcp --tcp-flags RST RST -j RETURN",
80             "-A fw_allowlist -p icmpv6 --icmpv6-type packet-too-big -j RETURN",
81             "-A fw_allowlist -p icmpv6 --icmpv6-type router-solicitation -j RETURN",
82             "-A fw_allowlist -p icmpv6 --icmpv6-type router-advertisement -j RETURN",
83             "-A fw_allowlist -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN",
84             "-A fw_allowlist -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN",
85             "-A fw_allowlist -p icmpv6 --icmpv6-type redirect -j RETURN",
86             "-A fw_allowlist -j DROP",
87             "COMMIT\n"};
88     std::vector<std::pair<IptablesTarget, std::string>> expectedRestoreCommands = {
89             {V4, Join(expectedRestore4, '\n')},
90             {V6, Join(expectedRestore6, '\n')},
91     };
92 
93     createChain("fw_allowlist", ALLOWLIST);
94     expectIptablesRestoreCommands(expectedRestoreCommands);
95 }
96 
TEST_F(FirewallControllerTest,TestCreateDenylistChain)97 TEST_F(FirewallControllerTest, TestCreateDenylistChain) {
98     std::vector<std::string> expectedRestore = {
99             "*filter",
100             ":fw_denylist -",
101             "-A fw_denylist -i lo -j RETURN",
102             "-A fw_denylist -o lo -j RETURN",
103             "-A fw_denylist -p tcp --tcp-flags RST RST -j RETURN",
104             "COMMIT\n"};
105     std::vector<std::pair<IptablesTarget, std::string>> expectedRestoreCommands = {
106             {V4, Join(expectedRestore, '\n')},
107             {V6, Join(expectedRestore, '\n')},
108     };
109 
110     createChain("fw_denylist", DENYLIST);
111     expectIptablesRestoreCommands(expectedRestoreCommands);
112 }
113 
TEST_F(FirewallControllerTest,TestSetStandbyRule)114 TEST_F(FirewallControllerTest, TestSetStandbyRule) {
115     ExpectedIptablesCommands expected = {
116         { V4V6, "*filter\n-D fw_standby -m owner --uid-owner 12345 -j DROP\nCOMMIT\n" }
117     };
118     mFw.setUidRule(STANDBY, 12345, ALLOW);
119     expectIptablesRestoreCommands(expected);
120 
121     expected = {
122         { V4V6, "*filter\n-A fw_standby -m owner --uid-owner 12345 -j DROP\nCOMMIT\n" }
123     };
124     mFw.setUidRule(STANDBY, 12345, DENY);
125     expectIptablesRestoreCommands(expected);
126 }
127 
TEST_F(FirewallControllerTest,TestSetDozeRule)128 TEST_F(FirewallControllerTest, TestSetDozeRule) {
129     ExpectedIptablesCommands expected = {
130         { V4V6, "*filter\n-I fw_dozable -m owner --uid-owner 54321 -j RETURN\nCOMMIT\n" }
131     };
132     mFw.setUidRule(DOZABLE, 54321, ALLOW);
133     expectIptablesRestoreCommands(expected);
134 
135     expected = {
136         { V4V6, "*filter\n-D fw_dozable -m owner --uid-owner 54321 -j RETURN\nCOMMIT\n" }
137     };
138     mFw.setUidRule(DOZABLE, 54321, DENY);
139     expectIptablesRestoreCommands(expected);
140 }
141 
TEST_F(FirewallControllerTest,TestSetFirewallRule)142 TEST_F(FirewallControllerTest, TestSetFirewallRule) {
143     ExpectedIptablesCommands expected = {
144         { V4V6, "*filter\n"
145                 "-A fw_INPUT -m owner --uid-owner 54321 -j DROP\n"
146                 "-A fw_OUTPUT -m owner --uid-owner 54321 -j DROP\n"
147                 "COMMIT\n" }
148     };
149     mFw.setUidRule(NONE, 54321, DENY);
150     expectIptablesRestoreCommands(expected);
151 
152     expected = {
153         { V4V6, "*filter\n"
154                 "-D fw_INPUT -m owner --uid-owner 54321 -j DROP\n"
155                 "-D fw_OUTPUT -m owner --uid-owner 54321 -j DROP\n"
156                 "COMMIT\n" }
157     };
158     mFw.setUidRule(NONE, 54321, ALLOW);
159     expectIptablesRestoreCommands(expected);
160 }
161 
TEST_F(FirewallControllerTest,TestReplaceAllowlistUidRule)162 TEST_F(FirewallControllerTest, TestReplaceAllowlistUidRule) {
163     std::string expected =
164             "*filter\n"
165             ":FW_allowchain -\n"
166             "-A FW_allowchain -m owner --uid-owner 10023 -j RETURN\n"
167             "-A FW_allowchain -m owner --uid-owner 10059 -j RETURN\n"
168             "-A FW_allowchain -m owner --uid-owner 10124 -j RETURN\n"
169             "-A FW_allowchain -m owner --uid-owner 10111 -j RETURN\n"
170             "-A FW_allowchain -m owner --uid-owner 110122 -j RETURN\n"
171             "-A FW_allowchain -m owner --uid-owner 210153 -j RETURN\n"
172             "-A FW_allowchain -m owner --uid-owner 210024 -j RETURN\n"
173             "-A FW_allowchain -m owner --uid-owner 0-9999 -j RETURN\n"
174             "-A FW_allowchain -m owner ! --uid-owner 0-4294967294 -j RETURN\n"
175             "-A FW_allowchain -p esp -j RETURN\n"
176             "-A FW_allowchain -i lo -j RETURN\n"
177             "-A FW_allowchain -o lo -j RETURN\n"
178             "-A FW_allowchain -p tcp --tcp-flags RST RST -j RETURN\n"
179             "-A FW_allowchain -p icmpv6 --icmpv6-type packet-too-big -j RETURN\n"
180             "-A FW_allowchain -p icmpv6 --icmpv6-type router-solicitation -j RETURN\n"
181             "-A FW_allowchain -p icmpv6 --icmpv6-type router-advertisement -j RETURN\n"
182             "-A FW_allowchain -p icmpv6 --icmpv6-type neighbour-solicitation -j RETURN\n"
183             "-A FW_allowchain -p icmpv6 --icmpv6-type neighbour-advertisement -j RETURN\n"
184             "-A FW_allowchain -p icmpv6 --icmpv6-type redirect -j RETURN\n"
185             "-A FW_allowchain -j DROP\n"
186             "COMMIT\n";
187 
188     std::vector<int32_t> uids = { 10023, 10059, 10124, 10111, 110122, 210153, 210024 };
189     EXPECT_EQ(expected, makeUidRules(V6, "FW_allowchain", true, uids));
190 }
191 
TEST_F(FirewallControllerTest,TestReplaceDenylistUidRule)192 TEST_F(FirewallControllerTest, TestReplaceDenylistUidRule) {
193     std::string expected =
194             "*filter\n"
195             ":FW_denychain -\n"
196             "-A FW_denychain -i lo -j RETURN\n"
197             "-A FW_denychain -o lo -j RETURN\n"
198             "-A FW_denychain -p tcp --tcp-flags RST RST -j RETURN\n"
199             "-A FW_denychain -m owner --uid-owner 10023 -j DROP\n"
200             "-A FW_denychain -m owner --uid-owner 10059 -j DROP\n"
201             "-A FW_denychain -m owner --uid-owner 10124 -j DROP\n"
202             "COMMIT\n";
203 
204     std::vector<int32_t> uids = { 10023, 10059, 10124 };
205     EXPECT_EQ(expected, makeUidRules(V4, "FW_denychain", false, uids));
206 }
207 
TEST_F(FirewallControllerTest,TestEnableChildChains)208 TEST_F(FirewallControllerTest, TestEnableChildChains) {
209     std::vector<std::string> expected = {
210         "*filter\n"
211         "-A fw_INPUT -j fw_dozable\n"
212         "-A fw_OUTPUT -j fw_dozable\n"
213         "COMMIT\n"
214     };
215     EXPECT_EQ(0, mFw.enableChildChains(DOZABLE, true));
216     expectIptablesRestoreCommands(expected);
217 
218     expected = {
219         "*filter\n"
220         "-D fw_INPUT -j fw_powersave\n"
221         "-D fw_OUTPUT -j fw_powersave\n"
222         "COMMIT\n"
223     };
224     EXPECT_EQ(0, mFw.enableChildChains(POWERSAVE, false));
225     expectIptablesRestoreCommands(expected);
226 }
227 
TEST_F(FirewallControllerTest,TestFirewall)228 TEST_F(FirewallControllerTest, TestFirewall) {
229     std::vector<std::string> enableCommands = {
230             "*filter\n"
231             "-A fw_INPUT -j DROP\n"
232             "-A fw_OUTPUT -j REJECT\n"
233             "-A fw_FORWARD -j REJECT\n"
234             "COMMIT\n"};
235     std::vector<std::string> disableCommands = {
236             "*filter\n"
237             ":fw_INPUT -\n"
238             ":fw_OUTPUT -\n"
239             ":fw_FORWARD -\n"
240             "-6 -A fw_OUTPUT ! -o lo -s ::1 -j DROP\n"
241             "COMMIT\n"};
242     std::vector<std::string> noCommands = {};
243 
244     EXPECT_EQ(0, mFw.resetFirewall());
245     expectIptablesRestoreCommands(disableCommands);
246 
247     EXPECT_EQ(0, mFw.resetFirewall());
248     expectIptablesRestoreCommands(disableCommands);
249 
250     EXPECT_EQ(0, mFw.setFirewallType(DENYLIST));
251     expectIptablesRestoreCommands(disableCommands);
252 
253     EXPECT_EQ(0, mFw.setFirewallType(DENYLIST));
254     expectIptablesRestoreCommands(noCommands);
255 
256     std::vector<std::string> disableEnableCommands;
257     disableEnableCommands.insert(
258             disableEnableCommands.end(), disableCommands.begin(), disableCommands.end());
259     disableEnableCommands.insert(
260             disableEnableCommands.end(), enableCommands.begin(), enableCommands.end());
261 
262     EXPECT_EQ(0, mFw.setFirewallType(ALLOWLIST));
263     expectIptablesRestoreCommands(disableEnableCommands);
264 
265     std::vector<std::string> ifaceCommands = {
266         "*filter\n"
267         "-I fw_INPUT -i rmnet_data0 -j RETURN\n"
268         "-I fw_OUTPUT -o rmnet_data0 -j RETURN\n"
269         "COMMIT\n"
270     };
271     EXPECT_EQ(0, mFw.setInterfaceRule("rmnet_data0", ALLOW));
272     expectIptablesRestoreCommands(ifaceCommands);
273 
274     EXPECT_EQ(0, mFw.setInterfaceRule("rmnet_data0", ALLOW));
275     expectIptablesRestoreCommands(noCommands);
276 
277     ifaceCommands = {
278         "*filter\n"
279         "-D fw_INPUT -i rmnet_data0 -j RETURN\n"
280         "-D fw_OUTPUT -o rmnet_data0 -j RETURN\n"
281         "COMMIT\n"
282     };
283     EXPECT_EQ(0, mFw.setInterfaceRule("rmnet_data0", DENY));
284     expectIptablesRestoreCommands(ifaceCommands);
285 
286     EXPECT_EQ(0, mFw.setInterfaceRule("rmnet_data0", DENY));
287     expectIptablesRestoreCommands(noCommands);
288 
289     EXPECT_EQ(0, mFw.setFirewallType(ALLOWLIST));
290     expectIptablesRestoreCommands(noCommands);
291 
292     EXPECT_EQ(0, mFw.resetFirewall());
293     expectIptablesRestoreCommands(disableCommands);
294 
295     // TODO: calling resetFirewall and then setFirewallType(ALLOWLIST) does
296     // nothing. This seems like a clear bug.
297     EXPECT_EQ(0, mFw.setFirewallType(ALLOWLIST));
298     expectIptablesRestoreCommands(noCommands);
299 }
300 
TEST_F(FirewallControllerTest,TestDiscoverMaximumValidUid)301 TEST_F(FirewallControllerTest, TestDiscoverMaximumValidUid) {
302     struct {
303         const std::string description;
304         const std::string content;
305         const uint32_t expected;
306     } testCases[] = {
307             {
308                     .description = "root namespace case",
309                     .content = "         0          0 4294967295",
310                     .expected = 4294967294,
311             },
312             {
313                     .description = "container namespace case",
314                     .content = "         0     655360       5000\n"
315                                "      5000        600         50\n"
316                                "      5050     660410    1994950\n",
317                     .expected = 1999999,
318             },
319             {
320                     .description = "garbage content case",
321                     .content = "garbage",
322                     .expected = 4294967294,
323             },
324             {
325                     .description = "no content case",
326                     .content = "",
327                     .expected = 4294967294,
328             },
329     };
330 
331     const std::string tempFile = "/data/local/tmp/fake_uid_mapping";
332 
333     for (const auto& test : testCases) {
334         EXPECT_TRUE(WriteStringToFile(test.content, tempFile, false));
335         uint32_t got = FirewallController::discoverMaximumValidUid(tempFile);
336         EXPECT_EQ(0, remove(tempFile.c_str()));
337         if (got != test.expected) {
338             FAIL() << test.description << ":\n"
339                    << test.content << "\ngot " << got << ", but expected " << test.expected;
340         }
341     }
342 
343     // Also check when the file is not defined
344     EXPECT_NE(0, access(tempFile.c_str(), F_OK));
345     EXPECT_EQ(4294967294, FirewallController::discoverMaximumValidUid(tempFile));
346 }
347 
348 }  // namespace net
349 }  // namespace android
350