Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 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 | |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 17 | #include <string> |
| 18 | #include <vector> |
| 19 | |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 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 |
Logan Chien | 3f46148 | 2018-04-23 14:31:32 +0800 | [diff] [blame] | 27 | #include <log/log.h> |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 28 | |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 29 | #include <android-base/stringprintf.h> |
| 30 | #include <android-base/strings.h> |
| 31 | |
Alex Klyubin | fe90998 | 2015-02-02 11:30:27 -0800 | [diff] [blame] | 32 | #include "ConnmarkFlags.h" |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 33 | #include "NetdConstants.h" |
| 34 | #include "StrictController.h" |
| 35 | |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 36 | auto StrictController::execIptablesRestore = ::execIptablesRestore; |
Lorenzo Colitti | 9028d91 | 2016-03-28 02:34:54 +0900 | [diff] [blame] | 37 | |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 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 | |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 44 | using android::base::Join; |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 45 | using android::base::StringPrintf; |
| 46 | |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 47 | StrictController::StrictController(void) { |
| 48 | } |
| 49 | |
Luke Huang | a67dd56 | 2018-07-17 19:58:25 +0800 | [diff] [blame] | 50 | int StrictController::setupIptablesHooks(void) { |
Alex Klyubin | fe90998 | 2015-02-02 11:30:27 -0800 | [diff] [blame] | 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 | |
Luke Huang | a67dd56 | 2018-07-17 19:58:25 +0800 | [diff] [blame] | 64 | resetChains(); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 65 | |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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 | |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 75 | // Chain triggered when cleartext socket detected and penalty is log |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 78 | |
| 79 | // Chain triggered when cleartext socket detected and penalty is reject |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 83 | |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 84 | // We use a high-order mark bit to keep track of connections that we've already resolved. |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 85 | // Quickly skip connections that we've already resolved |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 88 | |
| 89 | // Look for IPv4 TCP/UDP connections with TLS/DTLS header |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 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. |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 113 | |
| 114 | // Skip newly classified connections from above |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 115 | CMD_V4V6("-A %s -m connmark --mark %s -j RETURN", LOCAL_CLEAR_DETECT, connmarkFlagTestAccept); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 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. |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 124 | |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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); |
Lorenzo Colitti | 20b128b | 2017-02-10 11:01:08 +0900 | [diff] [blame] | 130 | CMD_V4V6("COMMIT\n"); |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 131 | |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 132 | res |= execIptablesRestore(V4, Join(v4, '\n')); |
| 133 | res |= execIptablesRestore(V6, Join(v6, '\n')); |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 134 | |
| 135 | #undef CMD_V4 |
| 136 | #undef CMD_V6 |
| 137 | #undef CMD_V4V6 |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 138 | |
Luke Huang | a67dd56 | 2018-07-17 19:58:25 +0800 | [diff] [blame] | 139 | return res ? -EREMOTEIO : 0; |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 140 | } |
| 141 | |
Luke Huang | a67dd56 | 2018-07-17 19:58:25 +0800 | [diff] [blame] | 142 | int StrictController::resetChains(void) { |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 143 | // Flush any existing rules |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 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), |
Lorenzo Colitti | 20b128b | 2017-02-10 11:01:08 +0900 | [diff] [blame] | 152 | "COMMIT\n" |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 153 | }; |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 154 | const std::string commands = Join(commandList, '\n'); |
Luke Huang | a67dd56 | 2018-07-17 19:58:25 +0800 | [diff] [blame] | 155 | return (execIptablesRestore(V4V6, commands) == 0) ? 0 : -EREMOTEIO; |
Lorenzo Colitti | e60c0a5 | 2016-03-29 00:53:45 +0900 | [diff] [blame] | 156 | #undef CLEAR_CHAIN |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 157 | } |
| 158 | |
| 159 | int StrictController::setUidCleartextPenalty(uid_t uid, StrictPenalty penalty) { |
Lorenzo Colitti | cc40ed9 | 2017-09-05 18:57:55 +0900 | [diff] [blame] | 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 | |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 167 | std::vector<std::string> commands; |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 168 | if (penalty == ACCEPT) { |
| 169 | // Clean up any old rules |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 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", |
Lorenzo Colitti | cc40ed9 | 2017-09-05 18:57:55 +0900 | [diff] [blame] | 175 | LOCAL_CLEAR_CAUGHT, uid, perUidChain.c_str()), |
| 176 | StringPrintf("-F %s", perUidChain.c_str()), |
| 177 | StringPrintf("-X %s", perUidChain.c_str()), |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 178 | }; |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 179 | } else { |
| 180 | // Always take a detour to investigate this UID |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 181 | commands.push_back("*filter"); |
Lorenzo Colitti | cc40ed9 | 2017-09-05 18:57:55 +0900 | [diff] [blame] | 182 | commands.push_back(StringPrintf(":%s -", perUidChain.c_str())); |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 183 | commands.push_back(StringPrintf("-I %s -m owner --uid-owner %d -j %s", |
| 184 | LOCAL_OUTPUT, uid, LOCAL_CLEAR_DETECT)); |
Lorenzo Colitti | cc40ed9 | 2017-09-05 18:57:55 +0900 | [diff] [blame] | 185 | commands.push_back(StringPrintf("-I %s -m owner --uid-owner %d -j %s", |
| 186 | LOCAL_CLEAR_CAUGHT, uid, perUidChain.c_str())); |
| 187 | |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 188 | if (penalty == LOG) { |
Lorenzo Colitti | cc40ed9 | 2017-09-05 18:57:55 +0900 | [diff] [blame] | 189 | commands.push_back(StringPrintf("-A %s -j %s", perUidChain.c_str(), LOCAL_PENALTY_LOG)); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 190 | } else if (penalty == REJECT) { |
Lorenzo Colitti | cc40ed9 | 2017-09-05 18:57:55 +0900 | [diff] [blame] | 191 | commands.push_back(StringPrintf("-A %s -j %s", perUidChain.c_str(), |
| 192 | LOCAL_PENALTY_REJECT)); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 193 | } |
| 194 | } |
Lorenzo Colitti | 6ee2598 | 2017-07-18 21:44:04 +0900 | [diff] [blame] | 195 | commands.push_back("COMMIT\n"); |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 196 | |
Luke Huang | a67dd56 | 2018-07-17 19:58:25 +0800 | [diff] [blame] | 197 | return (execIptablesRestore(V4V6, Join(commands, "\n")) == 0) ? 0 : -EREMOTEIO; |
Jeff Sharkey | fbe497f | 2014-10-28 16:50:07 -0700 | [diff] [blame] | 198 | } |