diff options
author | Jeff Sharkey <jsharkey@android.com> | 2014-10-28 16:50:07 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2015-01-15 15:46:59 -0800 |
commit | fbe497fcd808e4317572ad48c42545105309a347 (patch) | |
tree | aceefeed8789d8a4145c4b467f98e10538d66f1a /server/StrictController.cpp | |
parent | 1a3c689be29bfbe0c7f3eb3134e9b2a2208f839c (diff) | |
download | netd-fbe497fcd808e4317572ad48c42545105309a347.tar.gz |
Offer to detect non-SSL/TLS network traffic.
Introduces new module that provides network-related features for
the StrictMode developer API. The first feature offers to detect
sockets sending data not wrapped inside a layer of SSL/TLS
encryption.
This carefully only adds overhead to UIDs that have requested
detection, and it uses CONNMARK to quickly accept/reject packets
from streams that have already been inspected. Detection is done
by looking for a well-known TLS handshake header; it's not future
proof, but it's a good start. Handles both IPv4 and IPv6.
When requested, we also log the triggering packet through NFLOG and
back up to the framework to aid investigation.
Bug: 18335678
Change-Id: Ie8fab785139dfb55a71b6dc7a0f3c75a8408224b
Diffstat (limited to 'server/StrictController.cpp')
-rw-r--r-- | server/StrictController.cpp | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/server/StrictController.cpp b/server/StrictController.cpp new file mode 100644 index 00000000..20232ea7 --- /dev/null +++ b/server/StrictController.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define LOG_TAG "StrictController" +#define LOG_NDEBUG 0 + +#include <cutils/log.h> + +#include "NetdConstants.h" +#include "StrictController.h" + +const char* StrictController::LOCAL_OUTPUT = "st_OUTPUT"; +const char* StrictController::LOCAL_CLEAR_DETECT = "st_clear_detect"; +const char* StrictController::LOCAL_CLEAR_CAUGHT = "st_clear_caught"; +const char* StrictController::LOCAL_PENALTY_LOG = "st_penalty_log"; +const char* StrictController::LOCAL_PENALTY_REJECT = "st_penalty_reject"; + +StrictController::StrictController(void) { +} + +int StrictController::enableStrict(void) { + int res = 0; + + disableStrict(); + + // Mark 0x01 means resolved and ACCEPT + // Mark 0x02 means resolved and REJECT + + // Chain triggered when cleartext socket detected and penalty is log + res |= execIptables(V4V6, "-N", LOCAL_PENALTY_LOG, NULL); + res |= execIptables(V4V6, "-A", LOCAL_PENALTY_LOG, + "-j", "CONNMARK", "--or-mark", "0x01000000", NULL); + res |= execIptables(V4V6, "-A", LOCAL_PENALTY_LOG, + "-j", "NFLOG", "--nflog-group", "0", NULL); + + // Chain triggered when cleartext socket detected and penalty is reject + res |= execIptables(V4V6, "-N", LOCAL_PENALTY_REJECT, NULL); + res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT, + "-j", "CONNMARK", "--or-mark", "0x02000000", NULL); + res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT, + "-j", "NFLOG", "--nflog-group", "0", NULL); + res |= execIptables(V4V6, "-A", LOCAL_PENALTY_REJECT, + "-j", "REJECT", NULL); + + // Create chain to detect non-TLS traffic. We use a high-order + // mark bit to keep track of connections that we've already resolved. + res |= execIptables(V4V6, "-N", LOCAL_CLEAR_DETECT, NULL); + res |= execIptables(V4V6, "-N", LOCAL_CLEAR_CAUGHT, NULL); + + // Quickly skip connections that we've already resolved + res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT, + "-m", "connmark", "--mark", "0x02000000/0x02000000", + "-j", "REJECT", NULL); + res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT, + "-m", "connmark", "--mark", "0x01000000/0x01000000", + "-j", "RETURN", NULL); + + // Look for IPv4 TCP/UDP connections with TLS/DTLS header + res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp", + "-m", "u32", "--u32", "0>>22&0x3C@ 12>>26&0x3C@ 0&0xFFFF0000=0x16030000 &&" + "0>>22&0x3C@ 12>>26&0x3C@ 4&0x00FF0000=0x00010000", + "-j", "CONNMARK", "--or-mark", "0x01000000", NULL); + res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "udp", + "-m", "u32", "--u32", "0>>22&0x3C@ 8&0xFFFF0000=0x16FE0000 &&" + "0>>22&0x3C@ 20&0x00FF0000=0x00010000", + "-j", "CONNMARK", "--or-mark", "0x01000000", NULL); + + // Look for IPv6 TCP/UDP connections with TLS/DTLS header. The IPv6 header + // doesn't have an IHL field to shift with, so we have to manually add in + // the 40-byte offset at every step. + res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp", + "-m", "u32", "--u32", "52>>26&0x3C@ 40&0xFFFF0000=0x16030000 &&" + "52>>26&0x3C@ 44&0x00FF0000=0x00010000", + "-j", "CONNMARK", "--or-mark", "0x01000000", NULL); + res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp", + "-m", "u32", "--u32", "48&0xFFFF0000=0x16FE0000 &&" + "60&0x00FF0000=0x00010000", + "-j", "CONNMARK", "--or-mark", "0x01000000", NULL); + + // Skip newly classified connections from above + res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT, + "-m", "connmark", "--mark", "0x01000000/0x01000000", + "-j", "RETURN", NULL); + + // Handle TCP/UDP payloads that didn't match TLS/DTLS filters above, + // which means we've probably found cleartext data. The TCP variant + // depends on u32 returning false when we try reading into the message + // body to ignore empty ACK packets. + res |= execIptables(V4, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp", + "-m", "state", "--state", "ESTABLISHED", + "-m", "u32", "--u32", "0>>22&0x3C@ 12>>26&0x3C@ 0&0x0=0x0", + "-j", LOCAL_CLEAR_CAUGHT, NULL); + res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "tcp", + "-m", "state", "--state", "ESTABLISHED", + "-m", "u32", "--u32", "52>>26&0x3C@ 40&0x0=0x0", + "-j", LOCAL_CLEAR_CAUGHT, NULL); + + res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp", + "-j", LOCAL_CLEAR_CAUGHT, NULL); + + return res; +} + +int StrictController::disableStrict(void) { + int res = 0; + + // Flush any existing rules + res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL); + + res |= execIptables(V4V6, "-F", LOCAL_PENALTY_LOG, NULL); + res |= execIptables(V4V6, "-F", LOCAL_PENALTY_REJECT, NULL); + res |= execIptables(V4V6, "-F", LOCAL_CLEAR_CAUGHT, NULL); + res |= execIptables(V4V6, "-F", LOCAL_CLEAR_DETECT, NULL); + + res |= execIptables(V4V6, "-X", LOCAL_PENALTY_LOG, NULL); + res |= execIptables(V4V6, "-X", LOCAL_PENALTY_REJECT, NULL); + res |= execIptables(V4V6, "-X", LOCAL_CLEAR_CAUGHT, NULL); + res |= execIptables(V4V6, "-X", LOCAL_CLEAR_DETECT, NULL); + + return res; +} + +int StrictController::setUidCleartextPenalty(uid_t uid, StrictPenalty penalty) { + char uidStr[16]; + sprintf(uidStr, "%d", uid); + + int res = 0; + if (penalty == ACCEPT) { + // Clean up any old rules + execIptables(V4V6, "-D", LOCAL_OUTPUT, + "-m", "owner", "--uid-owner", uidStr, + "-j", LOCAL_CLEAR_DETECT, NULL); + execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT, + "-m", "owner", "--uid-owner", uidStr, + "-j", LOCAL_PENALTY_LOG, NULL); + execIptables(V4V6, "-D", LOCAL_CLEAR_CAUGHT, + "-m", "owner", "--uid-owner", uidStr, + "-j", LOCAL_PENALTY_REJECT, NULL); + + } else { + // Always take a detour to investigate this UID + res |= execIptables(V4V6, "-I", LOCAL_OUTPUT, + "-m", "owner", "--uid-owner", uidStr, + "-j", LOCAL_CLEAR_DETECT, NULL); + + if (penalty == LOG) { + res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT, + "-m", "owner", "--uid-owner", uidStr, + "-j", LOCAL_PENALTY_LOG, NULL); + } else if (penalty == REJECT) { + res |= execIptables(V4V6, "-I", LOCAL_CLEAR_CAUGHT, + "-m", "owner", "--uid-owner", uidStr, + "-j", LOCAL_PENALTY_REJECT, NULL); + } + } + + return res; +} |