Home | History | Annotate | Download | only in server
      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