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
StrictController(void)37 StrictController::StrictController(void) {
38 }
39
enableStrict(void)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
disableStrict(void)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
setUidCleartextPenalty(uid_t uid,StrictPenalty penalty)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