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::execIptables = ::execIptables;
     37 auto StrictController::execIptablesRestore = ::execIptablesRestore;
     38 
     39 const char* StrictController::LOCAL_OUTPUT = "st_OUTPUT";
     40 const char* StrictController::LOCAL_CLEAR_DETECT = "st_clear_detect";
     41 const char* StrictController::LOCAL_CLEAR_CAUGHT = "st_clear_caught";
     42 const char* StrictController::LOCAL_PENALTY_LOG = "st_penalty_log";
     43 const char* StrictController::LOCAL_PENALTY_REJECT = "st_penalty_reject";
     44 
     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\x04");
    131 
    132     res |= execIptablesRestore(V4, android::base::Join(v4, '\n'));
    133     res |= execIptablesRestore(V6, android::base::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\x04"
    153     };
    154     const std::string commands = android::base::Join(commandList, '\n');
    155     return execIptablesRestore(V4V6, commands);
    156 #undef CLEAR_CHAIN
    157 }
    158 
    159 int StrictController::setUidCleartextPenalty(uid_t uid, StrictPenalty penalty) {
    160     char uidStr[16];
    161     sprintf(uidStr, "%d", uid);
    162 
    163     int res = 0;
    164     if (penalty == ACCEPT) {
    165         // Clean up any old rules
    166         execIptables(V4V6, "-D", LOCAL_OUTPUT,
    167                 "-m", "owner", "--uid-owner", uidStr,
    168                 "-j", LOCAL_CLEAR_DETECT, NULL);
    169         execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT,
    170                 "-m", "owner", "--uid-owner", uidStr,
    171                 "-j", LOCAL_PENALTY_LOG, NULL);
    172         execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT,
    173                 "-m", "owner", "--uid-owner", uidStr,
    174                 "-j", LOCAL_PENALTY_REJECT, NULL);
    175 
    176     } else {
    177         // Always take a detour to investigate this UID
    178         res |= execIptables(V4V6, "-I", LOCAL_OUTPUT,
    179                 "-m", "owner", "--uid-owner", uidStr,
    180                 "-j", LOCAL_CLEAR_DETECT, NULL);
    181 
    182         if (penalty == LOG) {
    183             res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT,
    184                     "-m", "owner", "--uid-owner", uidStr,
    185                     "-j", LOCAL_PENALTY_LOG, NULL);
    186         } else if (penalty == REJECT) {
    187             res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT,
    188                     "-m", "owner", "--uid-owner", uidStr,
    189                     "-j", LOCAL_PENALTY_REJECT, NULL);
    190         }
    191     }
    192 
    193     return res;
    194 }
    195