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