blob: 855bedbd8048af5c87d5392afdda8158aa3e4e74 [file] [log] [blame]
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -07001/*
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
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090017#include <string>
18#include <vector>
19
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070020#include <errno.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#define LOG_TAG "StrictController"
26#define LOG_NDEBUG 0
Logan Chien3f461482018-04-23 14:31:32 +080027#include <log/log.h>
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070028
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090029#include <android-base/stringprintf.h>
30#include <android-base/strings.h>
31
Alex Klyubinfe909982015-02-02 11:30:27 -080032#include "ConnmarkFlags.h"
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070033#include "NetdConstants.h"
34#include "StrictController.h"
35
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090036auto StrictController::execIptablesRestore = ::execIptablesRestore;
Lorenzo Colitti9028d912016-03-28 02:34:54 +090037
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070038const char* StrictController::LOCAL_OUTPUT = "st_OUTPUT";
39const char* StrictController::LOCAL_CLEAR_DETECT = "st_clear_detect";
40const char* StrictController::LOCAL_CLEAR_CAUGHT = "st_clear_caught";
41const char* StrictController::LOCAL_PENALTY_LOG = "st_penalty_log";
42const char* StrictController::LOCAL_PENALTY_REJECT = "st_penalty_reject";
43
Lorenzo Colitti6ee25982017-07-18 21:44:04 +090044using android::base::Join;
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090045using android::base::StringPrintf;
46
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070047StrictController::StrictController(void) {
48}
49
Luke Huanga67dd562018-07-17 19:58:25 +080050int StrictController::setupIptablesHooks(void) {
Alex Klyubinfe909982015-02-02 11:30:27 -080051 char connmarkFlagAccept[16];
52 char connmarkFlagReject[16];
53 char connmarkFlagTestAccept[32];
54 char connmarkFlagTestReject[32];
55 sprintf(connmarkFlagAccept, "0x%x", ConnmarkFlags::STRICT_RESOLVED_ACCEPT);
56 sprintf(connmarkFlagReject, "0x%x", ConnmarkFlags::STRICT_RESOLVED_REJECT);
57 sprintf(connmarkFlagTestAccept, "0x%x/0x%x",
58 ConnmarkFlags::STRICT_RESOLVED_ACCEPT,
59 ConnmarkFlags::STRICT_RESOLVED_ACCEPT);
60 sprintf(connmarkFlagTestReject, "0x%x/0x%x",
61 ConnmarkFlags::STRICT_RESOLVED_REJECT,
62 ConnmarkFlags::STRICT_RESOLVED_REJECT);
63
Luke Huanga67dd562018-07-17 19:58:25 +080064 resetChains();
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070065
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090066 int res = 0;
67 std::vector<std::string> v4, v6;
68
69#define CMD_V4(...) { auto cmd = StringPrintf(__VA_ARGS__); v4.push_back(cmd); }
70#define CMD_V6(...) { auto cmd = StringPrintf(__VA_ARGS__); v6.push_back(cmd); }
71#define CMD_V4V6(...) { CMD_V4(__VA_ARGS__); CMD_V6(__VA_ARGS__); };
72
73 CMD_V4V6("*filter");
74
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070075 // Chain triggered when cleartext socket detected and penalty is log
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090076 CMD_V4V6("-A %s -j CONNMARK --or-mark %s", LOCAL_PENALTY_LOG, connmarkFlagAccept);
77 CMD_V4V6("-A %s -j NFLOG --nflog-group 0", LOCAL_PENALTY_LOG);
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070078
79 // Chain triggered when cleartext socket detected and penalty is reject
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090080 CMD_V4V6("-A %s -j CONNMARK --or-mark %s", LOCAL_PENALTY_REJECT, connmarkFlagReject);
81 CMD_V4V6("-A %s -j NFLOG --nflog-group 0", LOCAL_PENALTY_REJECT);
82 CMD_V4V6("-A %s -j REJECT", LOCAL_PENALTY_REJECT);
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070083
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090084 // We use a high-order mark bit to keep track of connections that we've already resolved.
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070085 // Quickly skip connections that we've already resolved
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090086 CMD_V4V6("-A %s -m connmark --mark %s -j REJECT", LOCAL_CLEAR_DETECT, connmarkFlagTestReject);
87 CMD_V4V6("-A %s -m connmark --mark %s -j RETURN", LOCAL_CLEAR_DETECT, connmarkFlagTestAccept);
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -070088
89 // Look for IPv4 TCP/UDP connections with TLS/DTLS header
Lorenzo Colittie60c0a52016-03-29 00:53:45 +090090 const char *u32;
91 u32 = "0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000 &&"
92 "0>>22&0x3C@ 12>>26&0x3C@ 4&0x00FF0000=0x00010000";
93 CMD_V4("-A %s -p tcp -m u32 --u32 \"%s\" -j CONNMARK --or-mark %s",
94 LOCAL_CLEAR_DETECT, u32, connmarkFlagAccept);
95
96 u32 = "0>>22&0x3C@ 8&0xFFFF0000=0x16FE0000 &&"
97 "0>>22&0x3C@ 20&0x00FF0000=0x00010000";
98 CMD_V4("-A %s -p udp -m u32 --u32 \"%s\" -j CONNMARK --or-mark %s",
99 LOCAL_CLEAR_DETECT, u32, connmarkFlagAccept);
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700100
101 // Look for IPv6 TCP/UDP connections with TLS/DTLS header. The IPv6 header
102 // doesn't have an IHL field to shift with, so we have to manually add in
103 // the 40-byte offset at every step.
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900104 u32 = "52>>26&0x3C@ 40&0xFFFF0000=0x16030000 &&"
105 "52>>26&0x3C@ 44&0x00FF0000=0x00010000";
106 CMD_V6("-A %s -p tcp -m u32 --u32 \"%s\" -j CONNMARK --or-mark %s",
107 LOCAL_CLEAR_DETECT, u32, connmarkFlagAccept);
108
109 u32 = "48&0xFFFF0000=0x16FE0000 &&"
110 "60&0x00FF0000=0x00010000";
111 CMD_V6("-A %s -p udp -m u32 --u32 \"%s\" -j CONNMARK --or-mark %s",
112 LOCAL_CLEAR_DETECT, u32, connmarkFlagAccept);
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700113
114 // Skip newly classified connections from above
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900115 CMD_V4V6("-A %s -m connmark --mark %s -j RETURN", LOCAL_CLEAR_DETECT, connmarkFlagTestAccept);
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700116
117 // Handle TCP/UDP payloads that didn't match TLS/DTLS filters above,
118 // which means we've probably found cleartext data. The TCP variant
119 // depends on u32 returning false when we try reading into the message
120 // body to ignore empty ACK packets.
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900121 u32 = "0>>22&0x3C@ 12>>26&0x3C@ 0&0x0=0x0";
122 CMD_V4("-A %s -p tcp -m state --state ESTABLISHED -m u32 --u32 \"%s\" -j %s",
123 LOCAL_CLEAR_DETECT, u32, LOCAL_CLEAR_CAUGHT);
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700124
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900125 u32 = "52>>26&0x3C@ 40&0x0=0x0";
126 CMD_V6("-A %s -p tcp -m state --state ESTABLISHED -m u32 --u32 \"%s\" -j %s",
127 LOCAL_CLEAR_DETECT, u32, LOCAL_CLEAR_CAUGHT);
128
129 CMD_V4V6("-A %s -p udp -j %s", LOCAL_CLEAR_DETECT, LOCAL_CLEAR_CAUGHT);
Lorenzo Colitti20b128b2017-02-10 11:01:08 +0900130 CMD_V4V6("COMMIT\n");
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900131
Lorenzo Colitti6ee25982017-07-18 21:44:04 +0900132 res |= execIptablesRestore(V4, Join(v4, '\n'));
133 res |= execIptablesRestore(V6, Join(v6, '\n'));
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900134
135#undef CMD_V4
136#undef CMD_V6
137#undef CMD_V4V6
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700138
Luke Huanga67dd562018-07-17 19:58:25 +0800139 return res ? -EREMOTEIO : 0;
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700140}
141
Luke Huanga67dd562018-07-17 19:58:25 +0800142int StrictController::resetChains(void) {
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700143 // Flush any existing rules
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900144#define CLEAR_CHAIN(x) StringPrintf(":%s -", (x))
145 std::vector<std::string> commandList = {
146 "*filter",
147 CLEAR_CHAIN(LOCAL_OUTPUT),
148 CLEAR_CHAIN(LOCAL_PENALTY_LOG),
149 CLEAR_CHAIN(LOCAL_PENALTY_REJECT),
150 CLEAR_CHAIN(LOCAL_CLEAR_CAUGHT),
151 CLEAR_CHAIN(LOCAL_CLEAR_DETECT),
Lorenzo Colitti20b128b2017-02-10 11:01:08 +0900152 "COMMIT\n"
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900153 };
Lorenzo Colitti6ee25982017-07-18 21:44:04 +0900154 const std::string commands = Join(commandList, '\n');
Luke Huanga67dd562018-07-17 19:58:25 +0800155 return (execIptablesRestore(V4V6, commands) == 0) ? 0 : -EREMOTEIO;
Lorenzo Colittie60c0a52016-03-29 00:53:45 +0900156#undef CLEAR_CHAIN
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700157}
158
159int StrictController::setUidCleartextPenalty(uid_t uid, StrictPenalty penalty) {
Lorenzo Colitticc40ed92017-09-05 18:57:55 +0900160 // When a penalty is set, we don't know what penalty the UID previously had. In order to be able
161 // to clear the previous penalty without causing an iptables error by deleting rules that don't
162 // exist, put each UID's rules in a chain specific to that UID. That way, the commands we need
163 // to run to clear the previous penalty don't depend on what the penalty actually was - all we
164 // need to do is clear the chain.
165 std::string perUidChain = StringPrintf("st_clear_caught_%u", uid);
166
Lorenzo Colitti6ee25982017-07-18 21:44:04 +0900167 std::vector<std::string> commands;
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700168 if (penalty == ACCEPT) {
169 // Clean up any old rules
Lorenzo Colitti6ee25982017-07-18 21:44:04 +0900170 commands = {
171 "*filter",
172 StringPrintf("-D %s -m owner --uid-owner %d -j %s",
173 LOCAL_OUTPUT, uid, LOCAL_CLEAR_DETECT),
174 StringPrintf("-D %s -m owner --uid-owner %d -j %s",
Lorenzo Colitticc40ed92017-09-05 18:57:55 +0900175 LOCAL_CLEAR_CAUGHT, uid, perUidChain.c_str()),
176 StringPrintf("-F %s", perUidChain.c_str()),
177 StringPrintf("-X %s", perUidChain.c_str()),
Lorenzo Colitti6ee25982017-07-18 21:44:04 +0900178 };
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700179 } else {
180 // Always take a detour to investigate this UID
Lorenzo Colitti6ee25982017-07-18 21:44:04 +0900181 commands.push_back("*filter");
Lorenzo Colitticc40ed92017-09-05 18:57:55 +0900182 commands.push_back(StringPrintf(":%s -", perUidChain.c_str()));
Lorenzo Colitti6ee25982017-07-18 21:44:04 +0900183 commands.push_back(StringPrintf("-I %s -m owner --uid-owner %d -j %s",
184 LOCAL_OUTPUT, uid, LOCAL_CLEAR_DETECT));
Lorenzo Colitticc40ed92017-09-05 18:57:55 +0900185 commands.push_back(StringPrintf("-I %s -m owner --uid-owner %d -j %s",
186 LOCAL_CLEAR_CAUGHT, uid, perUidChain.c_str()));
187
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700188 if (penalty == LOG) {
Lorenzo Colitticc40ed92017-09-05 18:57:55 +0900189 commands.push_back(StringPrintf("-A %s -j %s", perUidChain.c_str(), LOCAL_PENALTY_LOG));
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700190 } else if (penalty == REJECT) {
Lorenzo Colitticc40ed92017-09-05 18:57:55 +0900191 commands.push_back(StringPrintf("-A %s -j %s", perUidChain.c_str(),
192 LOCAL_PENALTY_REJECT));
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700193 }
194 }
Lorenzo Colitti6ee25982017-07-18 21:44:04 +0900195 commands.push_back("COMMIT\n");
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700196
Luke Huanga67dd562018-07-17 19:58:25 +0800197 return (execIptablesRestore(V4V6, Join(commands, "\n")) == 0) ? 0 : -EREMOTEIO;
Jeff Sharkeyfbe497f2014-10-28 16:50:07 -0700198}