OSDN Git Service

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