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 31const char* StrictController::LOCAL_OUTPUT = "st_OUTPUT"; 32const char* StrictController::LOCAL_CLEAR_DETECT = "st_clear_detect"; 33const char* StrictController::LOCAL_CLEAR_CAUGHT = "st_clear_caught"; 34const char* StrictController::LOCAL_PENALTY_LOG = "st_penalty_log"; 35const char* StrictController::LOCAL_PENALTY_REJECT = "st_penalty_reject"; 36 37StrictController::StrictController(void) { 38} 39 40int 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 133int 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 152int 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} 188