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 <string> 18 #include <vector> 19 20 #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 27 #include <cutils/log.h> 28 29 #include <android-base/stringprintf.h> 30 #include <android-base/strings.h> 31 32 #include "ConnmarkFlags.h" 33 #include "NetdConstants.h" 34 #include "StrictController.h" 35 36 auto StrictController::execIptablesRestore = ::execIptablesRestore; 37 38 const char* StrictController::LOCAL_OUTPUT = "st_OUTPUT"; 39 const char* StrictController::LOCAL_CLEAR_DETECT = "st_clear_detect"; 40 const char* StrictController::LOCAL_CLEAR_CAUGHT = "st_clear_caught"; 41 const char* StrictController::LOCAL_PENALTY_LOG = "st_penalty_log"; 42 const char* StrictController::LOCAL_PENALTY_REJECT = "st_penalty_reject"; 43 44 using android::base::Join; 45 using android::base::StringPrintf; 46 47 StrictController::StrictController(void) { 48 } 49 50 int StrictController::enableStrict(void) { 51 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 64 disableStrict(); 65 66 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 75 // Chain triggered when cleartext socket detected and penalty is log 76 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); 78 79 // Chain triggered when cleartext socket detected and penalty is reject 80 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); 83 84 // We use a high-order mark bit to keep track of connections that we've already resolved. 85 // Quickly skip connections that we've already resolved 86 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); 88 89 // Look for IPv4 TCP/UDP connections with TLS/DTLS header 90 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); 100 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. 104 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); 113 114 // Skip newly classified connections from above 115 CMD_V4V6("-A %s -m connmark --mark %s -j RETURN", LOCAL_CLEAR_DETECT, connmarkFlagTestAccept); 116 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. 121 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); 124 125 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); 130 CMD_V4V6("COMMIT\n"); 131 132 res |= execIptablesRestore(V4, Join(v4, '\n')); 133 res |= execIptablesRestore(V6, Join(v6, '\n')); 134 135 #undef CMD_V4 136 #undef CMD_V6 137 #undef CMD_V4V6 138 139 return res; 140 } 141 142 int StrictController::disableStrict(void) { 143 // Flush any existing rules 144 #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), 152 "COMMIT\n" 153 }; 154 const std::string commands = Join(commandList, '\n'); 155 return execIptablesRestore(V4V6, commands); 156 #undef CLEAR_CHAIN 157 } 158 159 int StrictController::setUidCleartextPenalty(uid_t uid, StrictPenalty penalty) { 160 // 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 167 std::vector<std::string> commands; 168 if (penalty == ACCEPT) { 169 // Clean up any old rules 170 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", 175 LOCAL_CLEAR_CAUGHT, uid, perUidChain.c_str()), 176 StringPrintf("-F %s", perUidChain.c_str()), 177 StringPrintf("-X %s", perUidChain.c_str()), 178 }; 179 } else { 180 // Always take a detour to investigate this UID 181 commands.push_back("*filter"); 182 commands.push_back(StringPrintf(":%s -", perUidChain.c_str())); 183 commands.push_back(StringPrintf("-I %s -m owner --uid-owner %d -j %s", 184 LOCAL_OUTPUT, uid, LOCAL_CLEAR_DETECT)); 185 commands.push_back(StringPrintf("-I %s -m owner --uid-owner %d -j %s", 186 LOCAL_CLEAR_CAUGHT, uid, perUidChain.c_str())); 187 188 if (penalty == LOG) { 189 commands.push_back(StringPrintf("-A %s -j %s", perUidChain.c_str(), LOCAL_PENALTY_LOG)); 190 } else if (penalty == REJECT) { 191 commands.push_back(StringPrintf("-A %s -j %s", perUidChain.c_str(), 192 LOCAL_PENALTY_REJECT)); 193 } 194 } 195 commands.push_back("COMMIT\n"); 196 197 return execIptablesRestore(V4V6, Join(commands, "\n")); 198 } 199