summaryrefslogtreecommitdiff
path: root/server/StrictController.cpp
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2014-10-28 16:50:07 -0700
committerJeff Sharkey <jsharkey@android.com>2015-01-15 15:46:59 -0800
commitfbe497fcd808e4317572ad48c42545105309a347 (patch)
treeaceefeed8789d8a4145c4b467f98e10538d66f1a /server/StrictController.cpp
parent1a3c689be29bfbe0c7f3eb3134e9b2a2208f839c (diff)
downloadnetd-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.cpp176
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;
+}