Merge remote-tracking branch 'goog/mirror-m-wireless-internal-release'
Change-Id: I51337014e2851f47dd5e183c4bfdf39bafa59942
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/client/Android.mk b/client/Android.mk
index 0c5e7ea..d3393d0 100644
--- a/client/Android.mk
+++ b/client/Android.mk
@@ -16,10 +16,11 @@
include $(CLEAR_VARS)
-LOCAL_C_INCLUDES := bionic/libc/dns/include external/libcxx/include system/netd/include
+LOCAL_C_INCLUDES := bionic/libc/dns/include system/netd/include
LOCAL_CLANG := true
LOCAL_CPPFLAGS := -std=c++11 -Wall -Werror
LOCAL_MODULE := libnetd_client
LOCAL_SRC_FILES := FwmarkClient.cpp NetdClient.cpp
+include external/libcxx/libcxx.mk
include $(BUILD_SHARED_LIBRARY)
diff --git a/client/FwmarkClient.cpp b/client/FwmarkClient.cpp
index db2009f..0ac1fbb 100644
--- a/client/FwmarkClient.cpp
+++ b/client/FwmarkClient.cpp
@@ -16,7 +16,9 @@
#include "FwmarkClient.h"
+#include <errno.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
@@ -41,7 +43,7 @@
}
int FwmarkClient::send(void* data, size_t len, int fd) {
- mChannel = socket(AF_UNIX, SOCK_STREAM, 0);
+ mChannel = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (mChannel == -1) {
return -errno;
}
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index fce362e..3157d3a 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -16,15 +16,17 @@
#include "NetdClient.h"
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <atomic>
+
#include "Fwmark.h"
#include "FwmarkClient.h"
#include "FwmarkCommand.h"
#include "resolv_netid.h"
-#include <atomic>
-#include <sys/socket.h>
-#include <unistd.h>
-
namespace {
std::atomic_uint netIdForProcess(NETID_UNSET);
@@ -116,9 +118,9 @@
// might itself cause another check with the fwmark server, which would be wasteful.
int socketFd;
if (libcSocket) {
- socketFd = libcSocket(AF_INET6, SOCK_DGRAM, 0);
+ socketFd = libcSocket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
} else {
- socketFd = socket(AF_INET6, SOCK_DGRAM, 0);
+ socketFd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
}
if (socketFd < 0) {
return -errno;
diff --git a/server/Android.mk b/server/Android.mk
index 9111d84..ecc8829 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -19,9 +19,7 @@
LOCAL_C_INCLUDES := \
$(call include-path-for, libhardware_legacy)/hardware_legacy \
bionic/libc/dns/include \
- external/libcxx/include \
external/mdnsresponder/mDNSShared \
- external/openssl/include \
system/netd/include \
LOCAL_CLANG := true
@@ -37,7 +35,13 @@
liblogwrap \
libmdnssd \
libnetutils \
+ libnl \
libsysutils \
+ libbase \
+ libutils \
+
+LOCAL_STATIC_LIBRARIES := \
+ libpcap \
LOCAL_SRC_FILES := \
BandwidthController.cpp \
@@ -63,12 +67,14 @@
ResolverController.cpp \
RouteController.cpp \
SoftapController.cpp \
+ StrictController.cpp \
TetherController.cpp \
UidRanges.cpp \
VirtualNetwork.cpp \
main.cpp \
oem_iptables_hook.cpp \
+include external/libcxx/libcxx.mk
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp
index e1db680..e5cf36c 100644
--- a/server/BandwidthController.cpp
+++ b/server/BandwidthController.cpp
@@ -786,7 +786,7 @@
return -1;
asprintf(&fname, "/proc/net/xt_quota/%s", costName);
- fp = fopen(fname, "r");
+ fp = fopen(fname, "re");
free(fname);
if (!fp) {
ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
@@ -843,7 +843,7 @@
}
asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
- fp = fopen(fname, "w");
+ fp = fopen(fname, "we");
free(fname);
if (!fp) {
ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
@@ -1100,12 +1100,12 @@
return -1;
}
- asprintf(&alertName, "%sAlert", costName);
if (!*alertBytes) {
ALOGE("No prior alert set for %s alert", costName);
return -1;
}
+ asprintf(&alertName, "%sAlert", costName);
asprintf(&chainName, "bw_costly_%s", costName);
asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName);
res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index 69e285f..d795396 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -92,6 +92,7 @@
ResolverController *CommandListener::sResolverCtrl = NULL;
FirewallController *CommandListener::sFirewallCtrl = NULL;
ClatdController *CommandListener::sClatdCtrl = NULL;
+StrictController *CommandListener::sStrictCtrl = NULL;
/**
* List of module chains to be created, along with explicit ordering. ORDERING
@@ -116,6 +117,7 @@
static const char* FILTER_OUTPUT[] = {
OEM_IPTABLES_FILTER_OUTPUT,
FirewallController::LOCAL_OUTPUT,
+ StrictController::LOCAL_OUTPUT,
BandwidthController::LOCAL_OUTPUT,
NULL,
};
@@ -182,6 +184,7 @@
registerCmd(new FirewallCmd());
registerCmd(new ClatdCmd());
registerCmd(new NetworkCommand());
+ registerCmd(new StrictCmd());
if (!sNetCtrl)
sNetCtrl = new NetworkController();
@@ -205,6 +208,8 @@
sInterfaceCtrl = new InterfaceController();
if (!sClatdCtrl)
sClatdCtrl = new ClatdController(sNetCtrl);
+ if (!sStrictCtrl)
+ sStrictCtrl = new StrictController();
/*
* This is the only time we touch top-level chains in iptables; controllers
@@ -1397,6 +1402,76 @@
return 0;
}
+CommandListener::StrictCmd::StrictCmd() :
+ NetdCommand("strict") {
+}
+
+int CommandListener::StrictCmd::sendGenericOkFail(SocketClient *cli, int cond) {
+ if (!cond) {
+ cli->sendMsg(ResponseCode::CommandOkay, "Strict command succeeded", false);
+ } else {
+ cli->sendMsg(ResponseCode::OperationFailed, "Strict command failed", false);
+ }
+ return 0;
+}
+
+StrictPenalty CommandListener::StrictCmd::parsePenalty(const char* arg) {
+ if (!strcmp(arg, "reject")) {
+ return REJECT;
+ } else if (!strcmp(arg, "log")) {
+ return LOG;
+ } else if (!strcmp(arg, "accept")) {
+ return ACCEPT;
+ } else {
+ return INVALID;
+ }
+}
+
+int CommandListener::StrictCmd::runCommand(SocketClient *cli, int argc,
+ char **argv) {
+ if (argc < 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing command", false);
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "enable")) {
+ int res = sStrictCtrl->enableStrict();
+ return sendGenericOkFail(cli, res);
+ }
+ if (!strcmp(argv[1], "disable")) {
+ int res = sStrictCtrl->disableStrict();
+ return sendGenericOkFail(cli, res);
+ }
+
+ if (!strcmp(argv[1], "set_uid_cleartext_policy")) {
+ if (argc != 4) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: strict set_uid_cleartext_policy <uid> <accept|log|reject>",
+ false);
+ return 0;
+ }
+
+ errno = 0;
+ unsigned long int uid = strtoul(argv[2], NULL, 0);
+ if (errno || uid > UID_MAX) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid UID", false);
+ return 0;
+ }
+
+ StrictPenalty penalty = parsePenalty(argv[3]);
+ if (penalty == INVALID) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Invalid penalty argument", false);
+ return 0;
+ }
+
+ int res = sStrictCtrl->setUidCleartextPenalty((uid_t) uid, penalty);
+ return sendGenericOkFail(cli, res);
+ }
+
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown command", false);
+ return 0;
+}
+
CommandListener::NetworkCommand::NetworkCommand() : NetdCommand("network") {
}
diff --git a/server/CommandListener.h b/server/CommandListener.h
index f605647..b71bc45 100644
--- a/server/CommandListener.h
+++ b/server/CommandListener.h
@@ -31,6 +31,7 @@
#include "ResolverController.h"
#include "FirewallController.h"
#include "ClatdController.h"
+#include "StrictController.h"
class CommandListener : public FrameworkListener {
static TetherController *sTetherCtrl;
@@ -43,6 +44,7 @@
static ResolverController *sResolverCtrl;
static FirewallController *sFirewallCtrl;
static ClatdController *sClatdCtrl;
+ static StrictController *sStrictCtrl;
public:
static NetworkController *sNetCtrl;
@@ -143,6 +145,16 @@
int runCommand(SocketClient *c, int argc, char ** argv);
};
+ class StrictCmd : public NetdCommand {
+ public:
+ StrictCmd();
+ virtual ~StrictCmd() {}
+ int runCommand(SocketClient *c, int argc, char ** argv);
+ protected:
+ int sendGenericOkFail(SocketClient *cli, int cond);
+ static StrictPenalty parsePenalty(const char* arg);
+ };
+
class NetworkCommand : public NetdCommand {
public:
NetworkCommand();
diff --git a/server/ConnmarkFlags.h b/server/ConnmarkFlags.h
new file mode 100644
index 0000000..2bbefc0
--- /dev/null
+++ b/server/ConnmarkFlags.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef _CONNMARK_FLAGS_H
+#define _CONNMARK_FLAGS_H
+
+/*
+ * iptables CONNMARK flag values used by various controllers. These values
+ * need to be stored in one place to avoid clashes.
+ */
+class ConnmarkFlags {
+public:
+ static const unsigned int STRICT_RESOLVED_ACCEPT = 0x01000000;
+ static const unsigned int STRICT_RESOLVED_REJECT = 0x02000000;
+};
+
+#endif
diff --git a/server/DnsProxyListener.cpp b/server/DnsProxyListener.cpp
index 6e27057..a0f0a30 100644
--- a/server/DnsProxyListener.cpp
+++ b/server/DnsProxyListener.cpp
@@ -83,12 +83,15 @@
return NULL;
}
+static bool sendBE32(SocketClient* c, uint32_t data) {
+ uint32_t be_data = htonl(data);
+ return c->sendData(&be_data, sizeof(be_data)) == 0;
+}
+
// Sends 4 bytes of big-endian length, followed by the data.
// Returns true on success.
-static bool sendLenAndData(SocketClient *c, const int len, const void* data) {
- uint32_t len_be = htonl(len);
- return c->sendData(&len_be, 4) == 0 &&
- (len == 0 || c->sendData(data, len) == 0);
+static bool sendLenAndData(SocketClient* c, const int len, const void* data) {
+ return sendBE32(c, len) && (len == 0 || c->sendData(data, len) == 0);
}
// Returns true on success
@@ -119,6 +122,42 @@
return success;
}
+static bool sendaddrinfo(SocketClient* c, struct addrinfo* ai) {
+ // struct addrinfo {
+ // int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
+ // int ai_family; /* PF_xxx */
+ // int ai_socktype; /* SOCK_xxx */
+ // int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ // socklen_t ai_addrlen; /* length of ai_addr */
+ // char *ai_canonname; /* canonical name for hostname */
+ // struct sockaddr *ai_addr; /* binary address */
+ // struct addrinfo *ai_next; /* next structure in linked list */
+ // };
+
+ // Write the struct piece by piece because we might be a 64-bit netd
+ // talking to a 32-bit process.
+ bool success =
+ sendBE32(c, ai->ai_flags) &&
+ sendBE32(c, ai->ai_family) &&
+ sendBE32(c, ai->ai_socktype) &&
+ sendBE32(c, ai->ai_protocol);
+ if (!success) {
+ return false;
+ }
+
+ // ai_addrlen and ai_addr.
+ if (!sendLenAndData(c, ai->ai_addrlen, ai->ai_addr)) {
+ return false;
+ }
+
+ // strlen(ai_canonname) and ai_canonname.
+ if (!sendLenAndData(c, ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0, ai->ai_canonname)) {
+ return false;
+ }
+
+ return true;
+}
+
void DnsProxyListener::GetAddrInfoHandler::run() {
if (DBG) {
ALOGD("GetAddrInfoHandler, now for %s / %s / %u / %u", mHost, mService, mNetId, mMark);
@@ -133,14 +172,10 @@
bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
struct addrinfo* ai = result;
while (ai && success) {
- success = sendLenAndData(mClient, sizeof(struct addrinfo), ai)
- && sendLenAndData(mClient, ai->ai_addrlen, ai->ai_addr)
- && sendLenAndData(mClient,
- ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0,
- ai->ai_canonname);
+ success = sendBE32(mClient, 1) && sendaddrinfo(mClient, ai);
ai = ai->ai_next;
}
- success = success && sendLenAndData(mClient, 0, "");
+ success = success && sendBE32(mClient, 0);
if (!success) {
ALOGW("Error writing DNS result to client");
}
diff --git a/server/InterfaceController.cpp b/server/InterfaceController.cpp
index b7a4d0b..76f5e05 100644
--- a/server/InterfaceController.cpp
+++ b/server/InterfaceController.cpp
@@ -15,14 +15,22 @@
*/
#include <dirent.h>
+#include <errno.h>
+#include <malloc.h>
#define LOG_TAG "InterfaceController"
+#include <base/file.h>
+#include <base/stringprintf.h>
#include <cutils/log.h>
#include <logwrap/logwrap.h>
#include "InterfaceController.h"
#include "RouteController.h"
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+
const char ipv6_proc_path[] = "/proc/sys/net/ipv6/conf";
const char sys_net_path[] = "/sys/class/net";
@@ -47,15 +55,12 @@
}
int InterfaceController::writeIPv6ProcPath(const char *interface, const char *setting, const char *value) {
- char *path;
if (!isIfaceName(interface)) {
errno = ENOENT;
return -1;
}
- asprintf(&path, "%s/%s/%s", ipv6_proc_path, interface, setting);
- int success = writeFile(path, value, strlen(value));
- free(path);
- return success;
+ std::string path(StringPrintf("%s/%s/%s", ipv6_proc_path, interface, setting));
+ return WriteStringToFile(value, path);
}
int InterfaceController::setEnableIPv6(const char *interface, const int on) {
@@ -132,23 +137,18 @@
// ID to get the table. If it's set to -1000, routes from interface ID 5 will go into
// table 1005, etc.
void InterfaceController::setAcceptRARouteTable(int tableOrOffset) {
- char* value;
- asprintf(&value, "%d", tableOrOffset);
- setOnAllInterfaces("accept_ra_rt_table", value);
- free(value);
+ std::string value(StringPrintf("%d", tableOrOffset));
+ setOnAllInterfaces("accept_ra_rt_table", value.c_str());
}
int InterfaceController::setMtu(const char *interface, const char *mtu)
{
- char *path;
if (!isIfaceName(interface)) {
errno = ENOENT;
return -1;
}
- asprintf(&path, "%s/%s/mtu", sys_net_path, interface);
- int success = writeFile(path, mtu, strlen(mtu));
- free(path);
- return success;
+ std::string path(StringPrintf("%s/%s/mtu", sys_net_path, interface));
+ return WriteStringToFile(mtu, path);
}
void InterfaceController::setIPv6OptimisticMode(const char *value) {
diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp
index 4823c91..d2e0bbe 100644
--- a/server/NetdConstants.cpp
+++ b/server/NetdConstants.cpp
@@ -14,14 +14,15 @@
* limitations under the License.
*/
+#include <ctype.h>
+#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
+#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
-#include <ctype.h>
-#include <net/if.h>
#define LOG_TAG "Netd"
@@ -112,43 +113,6 @@
return res;
}
-int writeFile(const char *path, const char *value, int size) {
- int fd = open(path, O_WRONLY);
- if (fd < 0) {
- ALOGE("Failed to open %s: %s", path, strerror(errno));
- return -1;
- }
-
- if (write(fd, value, size) != size) {
- ALOGE("Failed to write %s: %s", path, strerror(errno));
- close(fd);
- return -1;
- }
- close(fd);
- return 0;
-}
-
-int readFile(const char *path, char *buf, int *sizep)
-{
- int fd = open(path, O_RDONLY);
- int size;
-
- if (fd < 0) {
- ALOGE("Failed to open %s: %s", path, strerror(errno));
- return -1;
- }
-
- size = read(fd, buf, *sizep);
- if (size < 0) {
- ALOGE("Failed to write %s: %s", path, strerror(errno));
- close(fd);
- return -1;
- }
- *sizep = size;
- close(fd);
- return 0;
-}
-
/*
* Check an interface name for plausibility. This should e.g. help against
* directory traversal.
diff --git a/server/NetdConstants.h b/server/NetdConstants.h
index 7b894a8..296661d 100644
--- a/server/NetdConstants.h
+++ b/server/NetdConstants.h
@@ -35,8 +35,6 @@
int execIptables(IptablesTarget target, ...);
int execIptablesSilently(IptablesTarget target, ...);
-int writeFile(const char *path, const char *value, int size);
-int readFile(const char *path, char *buf, int *sizep);
bool isIfaceName(const char *name);
int parsePrefix(const char *prefix, uint8_t *family, void *address, int size, uint8_t *prefixlen);
diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp
index 6c81c18..5535119 100644
--- a/server/NetlinkHandler.cpp
+++ b/server/NetlinkHandler.cpp
@@ -58,26 +58,26 @@
}
if (!strcmp(subsys, "net")) {
- int action = evt->getAction();
+ NetlinkEvent::Action action = evt->getAction();
const char *iface = evt->findParam("INTERFACE");
- if (action == evt->NlActionAdd) {
+ if (action == NetlinkEvent::Action::kAdd) {
notifyInterfaceAdded(iface);
- } else if (action == evt->NlActionRemove) {
+ } else if (action == NetlinkEvent::Action::kRemove) {
notifyInterfaceRemoved(iface);
- } else if (action == evt->NlActionChange) {
+ } else if (action == NetlinkEvent::Action::kChange) {
evt->dump();
notifyInterfaceChanged("nana", true);
- } else if (action == evt->NlActionLinkUp) {
+ } else if (action == NetlinkEvent::Action::kLinkUp) {
notifyInterfaceLinkChanged(iface, true);
- } else if (action == evt->NlActionLinkDown) {
+ } else if (action == NetlinkEvent::Action::kLinkDown) {
notifyInterfaceLinkChanged(iface, false);
- } else if (action == evt->NlActionAddressUpdated ||
- action == evt->NlActionAddressRemoved) {
+ } else if (action == NetlinkEvent::Action::kAddressUpdated ||
+ action == NetlinkEvent::Action::kAddressRemoved) {
const char *address = evt->findParam("ADDRESS");
const char *flags = evt->findParam("FLAGS");
const char *scope = evt->findParam("SCOPE");
- if (action == evt->NlActionAddressRemoved && iface && address) {
+ if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {
int resetMask = strchr(address, ':') ? RESET_IPV6_ADDRESSES : RESET_IPV4_ADDRESSES;
resetMask |= RESET_IGNORE_INTERFACE_ADDRESS;
if (int ret = ifc_reset_connections(iface, resetMask)) {
@@ -88,14 +88,14 @@
if (iface && flags && scope) {
notifyAddressChanged(action, address, iface, flags, scope);
}
- } else if (action == evt->NlActionRdnss) {
+ } else if (action == NetlinkEvent::Action::kRdnss) {
const char *lifetime = evt->findParam("LIFETIME");
const char *servers = evt->findParam("SERVERS");
if (lifetime && servers) {
notifyInterfaceDnsServers(iface, lifetime, servers);
}
- } else if (action == evt->NlActionRouteUpdated ||
- action == evt->NlActionRouteRemoved) {
+ } else if (action == NetlinkEvent::Action::kRouteUpdated ||
+ action == NetlinkEvent::Action::kRouteRemoved) {
const char *route = evt->findParam("ROUTE");
const char *gateway = evt->findParam("GATEWAY");
const char *iface = evt->findParam("INTERFACE");
@@ -109,6 +109,11 @@
const char *iface = evt->findParam("INTERFACE");
notifyQuotaLimitReached(alertName, iface);
+ } else if (!strcmp(subsys, "strict")) {
+ const char *uid = evt->findParam("UID");
+ const char *hex = evt->findParam("HEX");
+ notifyStrictCleartext(uid, hex);
+
} else if (!strcmp(subsys, "xt_idletimer")) {
const char *label = evt->findParam("INTERFACE");
const char *state = evt->findParam("STATE");
@@ -169,12 +174,12 @@
"IfaceClass %s %s %s", isActive ? "active" : "idle", name, timestamp);
}
-void NetlinkHandler::notifyAddressChanged(int action, const char *addr,
+void NetlinkHandler::notifyAddressChanged(NetlinkEvent::Action action, const char *addr,
const char *iface, const char *flags,
const char *scope) {
notify(ResponseCode::InterfaceAddressChange,
"Address %s %s %s %s %s",
- (action == NetlinkEvent::NlActionAddressUpdated) ? kUpdated : kRemoved,
+ (action == NetlinkEvent::Action::kAddressUpdated) ? kUpdated : kRemoved,
addr, iface, flags, scope);
}
@@ -185,14 +190,18 @@
iface, lifetime, servers);
}
-void NetlinkHandler::notifyRouteChange(int action, const char *route,
+void NetlinkHandler::notifyRouteChange(NetlinkEvent::Action action, const char *route,
const char *gateway, const char *iface) {
notify(ResponseCode::RouteChange,
"Route %s %s%s%s%s%s",
- (action == NetlinkEvent::NlActionRouteUpdated) ? kUpdated : kRemoved,
+ (action == NetlinkEvent::Action::kRouteUpdated) ? kUpdated : kRemoved,
route,
*gateway ? " via " : "",
gateway,
*iface ? " dev " : "",
iface);
}
+
+void NetlinkHandler::notifyStrictCleartext(const char* uid, const char* hex) {
+ notify(ResponseCode::StrictCleartext, "%s %s", uid, hex);
+}
diff --git a/server/NetlinkHandler.h b/server/NetlinkHandler.h
index 83baa2b..c70867e 100644
--- a/server/NetlinkHandler.h
+++ b/server/NetlinkHandler.h
@@ -17,6 +17,7 @@
#ifndef _NETLINKHANDLER_H
#define _NETLINKHANDLER_H
+#include <sysutils/NetlinkEvent.h>
#include <sysutils/NetlinkListener.h>
#include "NetlinkManager.h"
@@ -41,10 +42,11 @@
void notifyQuotaLimitReached(const char *name, const char *iface);
void notifyInterfaceClassActivity(const char *name, bool isActive,
const char *timestamp);
- void notifyAddressChanged(int action, const char *addr, const char *iface,
+ void notifyAddressChanged(NetlinkEvent::Action action, const char *addr, const char *iface,
const char *flags, const char *scope);
void notifyInterfaceDnsServers(const char *iface, const char *lifetime,
const char *servers);
- void notifyRouteChange(int action, const char *route, const char *gateway, const char *iface);
+ void notifyRouteChange(NetlinkEvent::Action action, const char *route, const char *gateway, const char *iface);
+ void notifyStrictCleartext(const char* uid, const char* hex);
};
#endif
diff --git a/server/NetlinkManager.cpp b/server/NetlinkManager.cpp
index 1d731ac..76af46f 100644
--- a/server/NetlinkManager.cpp
+++ b/server/NetlinkManager.cpp
@@ -30,10 +30,24 @@
#include <cutils/log.h>
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_log.h>
+#include <linux/netfilter/nfnetlink_compat.h>
+
+#include <arpa/inet.h>
+
#include "NetlinkManager.h"
#include "NetlinkHandler.h"
+#include "pcap-netfilter-linux-android.h"
+
const int NetlinkManager::NFLOG_QUOTA_GROUP = 1;
+const int NetlinkManager::NETFILTER_STRICT_GROUP = 2;
NetlinkManager *NetlinkManager::sInstance = NULL;
@@ -51,7 +65,7 @@
}
NetlinkHandler *NetlinkManager::setupSocket(int *sock, int netlinkFamily,
- int groups, int format) {
+ int groups, int format, bool configNflog) {
struct sockaddr_nl nladdr;
int sz = 64 * 1024;
@@ -62,7 +76,7 @@
nladdr.nl_pid = getpid();
nladdr.nl_groups = groups;
- if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {
+ if ((*sock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, netlinkFamily)) < 0) {
ALOGE("Unable to create netlink socket: %s", strerror(errno));
return NULL;
}
@@ -85,6 +99,21 @@
return NULL;
}
+ if (configNflog) {
+ if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) {
+ ALOGE("Failed NFULNL_CFG_CMD_PF_UNBIND: %s", strerror(errno));
+ return NULL;
+ }
+ if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_PF_BIND, AF_INET) < 0) {
+ ALOGE("Failed NFULNL_CFG_CMD_PF_BIND: %s", strerror(errno));
+ return NULL;
+ }
+ if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) {
+ ALOGE("Failed NFULNL_CFG_CMD_BIND: %s", strerror(errno));
+ return NULL;
+ }
+ }
+
NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
if (handler->start()) {
ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));
@@ -97,7 +126,7 @@
int NetlinkManager::start() {
if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
- 0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {
+ 0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII, false)) == NULL) {
return -1;
}
@@ -107,13 +136,19 @@
RTMGRP_IPV6_IFADDR |
RTMGRP_IPV6_ROUTE |
(1 << (RTNLGRP_ND_USEROPT - 1)),
- NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
+ NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
return -1;
}
if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
- NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
- ALOGE("Unable to open quota2 logging socket");
+ NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {
+ ALOGE("Unable to open quota socket");
+ // TODO: return -1 once the emulator gets a new kernel.
+ }
+
+ if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,
+ 0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST, true)) == NULL) {
+ ALOGE("Unable to open strict socket");
// TODO: return -1 once the emulator gets a new kernel.
}
@@ -158,5 +193,18 @@
mQuotaSock = -1;
}
+ if (mStrictHandler) {
+ if (mStrictHandler->stop()) {
+ ALOGE("Unable to stop strict NetlinkHandler: %s", strerror(errno));
+ status = -1;
+ }
+
+ delete mStrictHandler;
+ mStrictHandler = NULL;
+
+ close(mStrictSock);
+ mStrictSock = -1;
+ }
+
return status;
}
diff --git a/server/NetlinkManager.h b/server/NetlinkManager.h
index 5187a59..40a5722 100644
--- a/server/NetlinkManager.h
+++ b/server/NetlinkManager.h
@@ -32,9 +32,11 @@
NetlinkHandler *mUeventHandler;
NetlinkHandler *mRouteHandler;
NetlinkHandler *mQuotaHandler;
+ NetlinkHandler *mStrictHandler;
int mUeventSock;
int mRouteSock;
int mQuotaSock;
+ int mStrictSock;
public:
virtual ~NetlinkManager();
@@ -47,15 +49,14 @@
static NetlinkManager *Instance();
- /* This is the nflog group arg that the xt_quota2 neftiler will use. */
+ /* Group used by xt_quota2 */
static const int NFLOG_QUOTA_GROUP;
-
- /* This is the group that the xt_IDLETIMER netfilter will use. */
- static const int IDLETIMER_GROUP;
+ /* Group used by StrictController rules */
+ static const int NETFILTER_STRICT_GROUP;
private:
NetlinkManager();
NetlinkHandler* setupSocket(int *sock, int netlinkFamily, int groups,
- int format);
+ int format, bool configNflog);
};
#endif
diff --git a/server/PppController.cpp b/server/PppController.cpp
index 6b54c31..581b9c6 100644
--- a/server/PppController.cpp
+++ b/server/PppController.cpp
@@ -85,6 +85,8 @@
char *lr;
asprintf(&lr, "%s:%s", l, r);
+ free(l);
+ free(r);
snprintf(dev, sizeof(dev), "/dev/%s", tty);
@@ -94,6 +96,9 @@
lr, "ms-dns", d1, "ms-dns", d2, "lcp-max-configure", "99999", (char *) NULL)) {
ALOGE("execl failed (%s)", strerror(errno));
}
+ free(lr);
+ free(d1);
+ free(d2);
ALOGE("Should never get here!");
return 0;
} else {
diff --git a/server/ResponseCode.h b/server/ResponseCode.h
index 6a0c22c..19d76c3 100644
--- a/server/ResponseCode.h
+++ b/server/ResponseCode.h
@@ -77,5 +77,6 @@
static const int InterfaceAddressChange = 614;
static const int InterfaceDnsInfo = 615;
static const int RouteChange = 616;
+ static const int StrictCleartext = 617;
};
#endif
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index cdca2de..889779d 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -16,22 +16,29 @@
#include "RouteController.h"
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fib_rules.h>
+#include <net/if.h>
+#include <sys/stat.h>
+
+#include <private/android_filesystem_config.h>
+
+#include <map>
+
#include "Fwmark.h"
#include "UidRanges.h"
#include "DummyNetwork.h"
+#include "base/file.h"
#define LOG_TAG "Netd"
#include "log/log.h"
#include "logwrap/logwrap.h"
#include "netutils/ifc.h"
#include "resolv_netid.h"
-#include <arpa/inet.h>
-#include <fcntl.h>
-#include <linux/fib_rules.h>
-#include <map>
-#include <net/if.h>
-#include <sys/stat.h>
+using android::base::WriteStringToFile;
namespace {
@@ -93,7 +100,6 @@
const bool MODIFY_NON_UID_BASED_RULES = true;
const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
-const int RT_TABLES_FLAGS = O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC;
const mode_t RT_TABLES_MODE = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // mode 0644, rw-r--r--
const unsigned ROUTE_FLUSH_ATTEMPTS = 2;
@@ -164,21 +170,10 @@
addTableName(entry.second, entry.first, &contents);
}
- int fd = open(RT_TABLES_PATH, RT_TABLES_FLAGS, RT_TABLES_MODE);
- if (fd == -1) {
- ALOGE("failed to create %s (%s)", RT_TABLES_PATH, strerror(errno));
+ if (!WriteStringToFile(contents, RT_TABLES_PATH, RT_TABLES_MODE, AID_SYSTEM, AID_WIFI)) {
+ ALOGE("failed to write to %s (%s)", RT_TABLES_PATH, strerror(errno));
return;
}
- // File creation is affected by umask, so make sure the right mode bits are set.
- if (fchmod(fd, RT_TABLES_MODE) == -1) {
- ALOGE("failed to set mode 0%o on %s (%s)", RT_TABLES_MODE, RT_TABLES_PATH, strerror(errno));
- }
- ssize_t bytesWritten = write(fd, contents.data(), contents.size());
- if (bytesWritten != static_cast<ssize_t>(contents.size())) {
- ALOGE("failed to write to %s (%zd vs %zu bytes) (%s)", RT_TABLES_PATH, bytesWritten,
- contents.size(), strerror(errno));
- }
- close(fd);
}
// Sends a netlink request and expects an ack.
@@ -202,7 +197,7 @@
nlmsgerr err;
} response;
- int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ int sock = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sock != -1 &&
connect(sock, reinterpret_cast<const sockaddr*>(&NETLINK_ADDRESS),
sizeof(NETLINK_ADDRESS)) != -1 &&
diff --git a/server/SoftapController.cpp b/server/SoftapController.cpp
index 853ca8f..d9e40a4 100644
--- a/server/SoftapController.cpp
+++ b/server/SoftapController.cpp
@@ -34,6 +34,8 @@
#include <openssl/sha.h>
#define LOG_TAG "SoftapController"
+#include <base/file.h>
+#include <base/stringprintf.h>
#include <cutils/log.h>
#include <netutils/ifc.h>
#include <private/android_filesystem_config.h>
@@ -42,6 +44,9 @@
#include "SoftapController.h"
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+
static const char HOSTAPD_CONF_FILE[] = "/data/misc/wifi/hostapd.conf";
static const char HOSTAPD_BIN_FILE[] = "/system/bin/hostapd";
@@ -116,13 +121,8 @@
* argv[7] - Key
*/
int SoftapController::setSoftap(int argc, char *argv[]) {
- char psk_str[2*SHA256_DIGEST_LENGTH+1];
- int ret = ResponseCode::SoftapStatusResult;
- int fd;
int hidden = 0;
int channel = AP_CHANNEL_DEFAULT;
- char *wbuf = NULL;
- char *fbuf = NULL;
if (argc < 5) {
ALOGE("Softap set is missing arguments. Please use:");
@@ -139,62 +139,42 @@
channel = AP_CHANNEL_DEFAULT;
}
- asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface="
- "/data/misc/wifi/hostapd\nssid=%s\nchannel=%d\nieee80211n=1\n"
- "hw_mode=%c\nignore_broadcast_ssid=%d\nwowlan_triggers=any\n",
- argv[2], argv[3], channel, (channel <= 14) ? 'g' : 'a', hidden);
+ std::string wbuf(StringPrintf("interface=%s\n"
+ "driver=nl80211\n"
+ "ctrl_interface=/data/misc/wifi/hostapd\n"
+ "ssid=%s\n"
+ "channel=%d\n"
+ "ieee80211n=1\n"
+ "hw_mode=%c\n"
+ "ignore_broadcast_ssid=%d\n"
+ "wowlan_triggers=any\n",
+ argv[2], argv[3], channel, (channel <= 14) ? 'g' : 'a', hidden));
+ std::string fbuf;
if (argc > 7) {
+ char psk_str[2*SHA256_DIGEST_LENGTH+1];
if (!strcmp(argv[6], "wpa-psk")) {
generatePsk(argv[3], argv[7], psk_str);
- asprintf(&fbuf, "%swpa=3\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
+ fbuf = StringPrintf("%swpa=3\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf.c_str(), psk_str);
} else if (!strcmp(argv[6], "wpa2-psk")) {
generatePsk(argv[3], argv[7], psk_str);
- asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
+ fbuf = StringPrintf("%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf.c_str(), psk_str);
} else if (!strcmp(argv[6], "open")) {
- asprintf(&fbuf, "%s", wbuf);
+ fbuf = wbuf;
}
} else if (argc > 6) {
if (!strcmp(argv[6], "open")) {
- asprintf(&fbuf, "%s", wbuf);
+ fbuf = wbuf;
}
} else {
- asprintf(&fbuf, "%s", wbuf);
+ fbuf = wbuf;
}
- fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);
- if (fd < 0) {
- ALOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
- free(wbuf);
- free(fbuf);
- return ResponseCode::OperationFailed;
- }
- if (write(fd, fbuf, strlen(fbuf)) < 0) {
+ if (!WriteStringToFile(fbuf, HOSTAPD_CONF_FILE, 0660, AID_SYSTEM, AID_WIFI)) {
ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
- ret = ResponseCode::OperationFailed;
- }
- free(wbuf);
- free(fbuf);
-
- /* Note: apparently open can fail to set permissions correctly at times */
- if (fchmod(fd, 0660) < 0) {
- ALOGE("Error changing permissions of %s to 0660: %s",
- HOSTAPD_CONF_FILE, strerror(errno));
- close(fd);
- unlink(HOSTAPD_CONF_FILE);
return ResponseCode::OperationFailed;
}
-
- if (fchown(fd, AID_SYSTEM, AID_WIFI) < 0) {
- ALOGE("Error changing group ownership of %s to %d: %s",
- HOSTAPD_CONF_FILE, AID_WIFI, strerror(errno));
- close(fd);
- unlink(HOSTAPD_CONF_FILE);
- return ResponseCode::OperationFailed;
- }
-
- close(fd);
- return ret;
+ return ResponseCode::SoftapStatusResult;
}
/*
diff --git a/server/StrictController.cpp b/server/StrictController.cpp
new file mode 100644
index 0000000..a04124d
--- /dev/null
+++ b/server/StrictController.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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 "ConnmarkFlags.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) {
+ char connmarkFlagAccept[16];
+ char connmarkFlagReject[16];
+ char connmarkFlagTestAccept[32];
+ char connmarkFlagTestReject[32];
+ sprintf(connmarkFlagAccept, "0x%x", ConnmarkFlags::STRICT_RESOLVED_ACCEPT);
+ sprintf(connmarkFlagReject, "0x%x", ConnmarkFlags::STRICT_RESOLVED_REJECT);
+ sprintf(connmarkFlagTestAccept, "0x%x/0x%x",
+ ConnmarkFlags::STRICT_RESOLVED_ACCEPT,
+ ConnmarkFlags::STRICT_RESOLVED_ACCEPT);
+ sprintf(connmarkFlagTestReject, "0x%x/0x%x",
+ ConnmarkFlags::STRICT_RESOLVED_REJECT,
+ ConnmarkFlags::STRICT_RESOLVED_REJECT);
+
+ int res = 0;
+
+ disableStrict();
+
+ // 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", connmarkFlagAccept, 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", connmarkFlagReject, 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", connmarkFlagTestReject,
+ "-j", "REJECT", NULL);
+ res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
+ "-m", "connmark", "--mark", connmarkFlagTestAccept,
+ "-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", connmarkFlagAccept, 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", connmarkFlagAccept, 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", connmarkFlagAccept, NULL);
+ res |= execIptables(V6, "-A", LOCAL_CLEAR_DETECT, "-p", "udp",
+ "-m", "u32", "--u32", "48&0xFFFF0000=0x16FE0000 &&"
+ "60&0x00FF0000=0x00010000",
+ "-j", "CONNMARK", "--or-mark", connmarkFlagAccept, NULL);
+
+ // Skip newly classified connections from above
+ res |= execIptables(V4V6, "-A", LOCAL_CLEAR_DETECT,
+ "-m", "connmark", "--mark", connmarkFlagTestAccept,
+ "-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;
+}
diff --git a/server/StrictController.h b/server/StrictController.h
new file mode 100644
index 0000000..52a6779
--- /dev/null
+++ b/server/StrictController.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef _STRICT_CONTROLLER_H
+#define _STRICT_CONTROLLER_H
+
+#include <string>
+
+enum StrictPenalty { INVALID, ACCEPT, LOG, REJECT };
+
+/*
+ * Help apps catch unwanted low-level networking behavior, like
+ * connections not wrapped in TLS.
+ */
+class StrictController {
+public:
+ StrictController();
+
+ int enableStrict(void);
+ int disableStrict(void);
+
+ int setUidCleartextPenalty(uid_t, StrictPenalty);
+
+ static const char* LOCAL_OUTPUT;
+ static const char* LOCAL_CLEAR_DETECT;
+ static const char* LOCAL_CLEAR_CAUGHT;
+ static const char* LOCAL_PENALTY_LOG;
+ static const char* LOCAL_PENALTY_REJECT;
+};
+
+#endif
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index dad03b4..092d456 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -27,6 +27,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <base/file.h>
#define LOG_TAG "TetherController"
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -36,6 +37,9 @@
#include "Permission.h"
#include "TetherController.h"
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+
namespace {
static const char BP_TOOLS_MODE[] = "bp-tools";
@@ -43,20 +47,7 @@
static const char IPV6_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv6/conf/all/forwarding";
bool writeToFile(const char* filename, const char* value) {
- int fd = open(filename, O_WRONLY);
- if (fd < 0) {
- ALOGE("Failed to open %s: %s", filename, strerror(errno));
- return false;
- }
-
- const ssize_t len = strlen(value);
- if (write(fd, value, len) != len) {
- ALOGE("Failed to write %s to %s: %s", value, filename, strerror(errno));
- close(fd);
- return false;
- }
- close(fd);
- return true;
+ return WriteStringToFile(value, file);
}
bool inBpToolsMode() {
@@ -68,7 +59,6 @@
} // namespace
-
TetherController::TetherController() {
mInterfaces = new InterfaceCollection();
mDnsNetId = 0;
@@ -178,6 +168,8 @@
char *start = strdup(inet_ntoa(addrs[addrIndex++]));
char *end = strdup(inet_ntoa(addrs[addrIndex++]));
asprintf(&(args[nextArg++]),"--dhcp-range=%s,%s,1h", start, end);
+ free(start);
+ free(end);
}
if (execv(args[0], args)) {