OSDN Git Service

Add a test for StrictController.
[android-x86/system-netd.git] / server / StrictController.cpp
1 /*
2  * Copyright (C) 2014 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
17 #include <errno.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #define LOG_TAG "StrictController"
23 #define LOG_NDEBUG 0
24
25 #include <cutils/log.h>
26
27 #include "ConnmarkFlags.h"
28 #include "NetdConstants.h"
29 #include "StrictController.h"
30
31 auto StrictController::execIptables = ::execIptables;
32
33 const char* StrictController::LOCAL_OUTPUT = "st_OUTPUT";
34 const char* StrictController::LOCAL_CLEAR_DETECT = "st_clear_detect";
35 const char* StrictController::LOCAL_CLEAR_CAUGHT = "st_clear_caught";
36 const char* StrictController::LOCAL_PENALTY_LOG = "st_penalty_log";
37 const char* StrictController::LOCAL_PENALTY_REJECT = "st_penalty_reject";
38
39 StrictController::StrictController(void) {
40 }
41
42 int StrictController::enableStrict(void) {
43     char connmarkFlagAccept[16];
44     char connmarkFlagReject[16];
45     char connmarkFlagTestAccept[32];
46     char connmarkFlagTestReject[32];
47     sprintf(connmarkFlagAccept, "0x%x", ConnmarkFlags::STRICT_RESOLVED_ACCEPT);
48     sprintf(connmarkFlagReject, "0x%x", ConnmarkFlags::STRICT_RESOLVED_REJECT);
49     sprintf(connmarkFlagTestAccept, "0x%x/0x%x",
50             ConnmarkFlags::STRICT_RESOLVED_ACCEPT,
51             ConnmarkFlags::STRICT_RESOLVED_ACCEPT);
52     sprintf(connmarkFlagTestReject, "0x%x/0x%x",
53             ConnmarkFlags::STRICT_RESOLVED_REJECT,
54             ConnmarkFlags::STRICT_RESOLVED_REJECT);
55
56     int res = 0;
57
58     disableStrict();
59
60     // Chain triggered when cleartext socket detected and penalty is log
61     res |= execIptables(V4V6, "-N", LOCAL_PENALTY_LOG, NULL);
62     res |= execIptables(V4V6, "-A", LOCAL_PENALTY_LOG,
63             "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
64     res |= execIptables(V4V6, "-A", LOCAL_PENALTY_LOG,
65             "-j", "NFLOG", "--nflog-group", "0", NULL);
66
67     // Chain triggered when cleartext socket detected and penalty is reject
68     res |= execIptables(V4V6, "-N", LOCAL_PENALTY_REJECT, NULL);
69     res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
70             "-j", "CONNMARK", "--or-mark", connmarkFlagReject, NULL);
71     res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
72             "-j", "NFLOG", "--nflog-group", "0", NULL);
73     res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT,
74             "-j", "REJECT", NULL);
75
76     // Create chain to detect non-TLS traffic. We use a high-order
77     // mark bit to keep track of connections that we've already resolved.
78     res |= execIptables(V4V6, "-N", LOCAL_CLEAR_DETECT, NULL);
79     res |= execIptables(V4V6, "-N", LOCAL_CLEAR_CAUGHT, NULL);
80
81     // Quickly skip connections that we've already resolved
82     res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
83             "-m", "connmark", "--mark", connmarkFlagTestReject,
84             "-j", "REJECT", NULL);
85     res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
86             "-m", "connmark", "--mark", connmarkFlagTestAccept,
87             "-j", "RETURN", NULL);
88
89     // Look for IPv4 TCP/UDP connections with TLS/DTLS header
90     res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
91             "-m", "u32", "--u32", "0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000 &&"
92                                   "0>>22&0x3C@ 12>>26&0x3C@ 4&0x00FF0000=0x00010000",
93             "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
94     res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
95             "-m", "u32", "--u32", "0>>22&0x3C@ 8&0xFFFF0000=0x16FE0000 &&"
96                                   "0>>22&0x3C@ 20&0x00FF0000=0x00010000",
97             "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
98
99     // Look for IPv6 TCP/UDP connections with TLS/DTLS header.  The IPv6 header
100     // doesn't have an IHL field to shift with, so we have to manually add in
101     // the 40-byte offset at every step.
102     res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
103             "-m", "u32", "--u32", "52>>26&0x3C@ 40&0xFFFF0000=0x16030000 &&"
104                                   "52>>26&0x3C@ 44&0x00FF0000=0x00010000",
105             "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
106     res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
107             "-m", "u32", "--u32", "48&0xFFFF0000=0x16FE0000 &&"
108                                   "60&0x00FF0000=0x00010000",
109             "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
110
111     // Skip newly classified connections from above
112     res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
113             "-m", "connmark", "--mark", connmarkFlagTestAccept,
114             "-j", "RETURN", NULL);
115
116     // Handle TCP/UDP payloads that didn't match TLS/DTLS filters above,
117     // which means we've probably found cleartext data.  The TCP variant
118     // depends on u32 returning false when we try reading into the message
119     // body to ignore empty ACK packets.
120     res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
121             "-m", "state", "--state", "ESTABLISHED",
122             "-m", "u32", "--u32", "0>>22&0x3C@ 12>>26&0x3C@ 0&0x0=0x0",
123             "-j", LOCAL_CLEAR_CAUGHT, NULL);
124     res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp",
125             "-m", "state", "--state", "ESTABLISHED",
126             "-m", "u32", "--u32", "52>>26&0x3C@ 40&0x0=0x0",
127             "-j", LOCAL_CLEAR_CAUGHT, NULL);
128
129     res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
130             "-j", LOCAL_CLEAR_CAUGHT, NULL);
131
132     return res;
133 }
134
135 int StrictController::disableStrict(void) {
136     int res = 0;
137
138     // Flush any existing rules
139     res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
140
141     res |= execIptables(V4V6, "-F", LOCAL_PENALTY_LOG, NULL);
142     res |= execIptables(V4V6, "-F", LOCAL_PENALTY_REJECT, NULL);
143     res |= execIptables(V4V6, "-F", LOCAL_CLEAR_CAUGHT, NULL);
144     res |= execIptables(V4V6, "-F", LOCAL_CLEAR_DETECT, NULL);
145
146     res |= execIptables(V4V6, "-X", LOCAL_PENALTY_LOG, NULL);
147     res |= execIptables(V4V6, "-X", LOCAL_PENALTY_REJECT, NULL);
148     res |= execIptables(V4V6, "-X", LOCAL_CLEAR_CAUGHT, NULL);
149     res |= execIptables(V4V6, "-X", LOCAL_CLEAR_DETECT, NULL);
150
151     return res;
152 }
153
154 int StrictController::setUidCleartextPenalty(uid_t uid, StrictPenalty penalty) {
155     char uidStr[16];
156     sprintf(uidStr, "%d", uid);
157
158     int res = 0;
159     if (penalty == ACCEPT) {
160         // Clean up any old rules
161         execIptables(V4V6, "-D", LOCAL_OUTPUT,
162                 "-m", "owner", "--uid-owner", uidStr,
163                 "-j", LOCAL_CLEAR_DETECT, NULL);
164         execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT,
165                 "-m", "owner", "--uid-owner", uidStr,
166                 "-j", LOCAL_PENALTY_LOG, NULL);
167         execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT,
168                 "-m", "owner", "--uid-owner", uidStr,
169                 "-j", LOCAL_PENALTY_REJECT, NULL);
170
171     } else {
172         // Always take a detour to investigate this UID
173         res |= execIptables(V4V6, "-I", LOCAL_OUTPUT,
174                 "-m", "owner", "--uid-owner", uidStr,
175                 "-j", LOCAL_CLEAR_DETECT, NULL);
176
177         if (penalty == LOG) {
178             res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT,
179                     "-m", "owner", "--uid-owner", uidStr,
180                     "-j", LOCAL_PENALTY_LOG, NULL);
181         } else if (penalty == REJECT) {
182             res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT,
183                     "-m", "owner", "--uid-owner", uidStr,
184                     "-j", LOCAL_PENALTY_REJECT, NULL);
185         }
186     }
187
188     return res;
189 }