diff options
-rw-r--r-- | client/Android.mk | 2 | ||||
-rw-r--r-- | client/NetdClient.cpp | 76 | ||||
-rw-r--r-- | include/FwmarkCommand.h | 16 | ||||
-rw-r--r-- | include/NetdClient.h | 34 | ||||
-rw-r--r-- | server/FwmarkServer.cpp | 45 |
5 files changed, 135 insertions, 38 deletions
diff --git a/client/Android.mk b/client/Android.mk index 2a4878c4..0332deaf 100644 --- a/client/Android.mk +++ b/client/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_C_INCLUDES := system/netd/include +LOCAL_C_INCLUDES := bionic/libc/dns/include system/netd/include LOCAL_MODULE := libnetd_client LOCAL_SRC_FILES := FwmarkClient.cpp NetdClient.cpp diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp index 2e8390c8..31c2e4d2 100644 --- a/client/NetdClient.cpp +++ b/client/NetdClient.cpp @@ -14,8 +14,11 @@ * limitations under the License. */ +#include "NetdClient.h" + #include "FwmarkClient.h" #include "FwmarkCommand.h" +#include "resolv_netid.h" #include <sys/socket.h> #include <unistd.h> @@ -31,14 +34,17 @@ int closeFdAndRestoreErrno(int fd) { typedef int (*ConnectFunctionType)(int, const sockaddr*, socklen_t); typedef int (*AcceptFunctionType)(int, sockaddr*, socklen_t*); +typedef unsigned (*NetIdForResolvFunctionType)(unsigned); +// These variables are only modified at startup (when libc.so is loaded) and never afterwards, so +// it's okay that they are read later at runtime without a lock. ConnectFunctionType libcConnect = 0; AcceptFunctionType libcAccept = 0; int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) { if (FwmarkClient::shouldSetFwmark(sockfd, addr)) { - char data[] = {FWMARK_COMMAND_ON_CONNECT}; - if (!FwmarkClient().send(data, sizeof(data), sockfd)) { + FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0}; + if (!FwmarkClient().send(&command, sizeof(command), sockfd)) { return -1; } } @@ -59,14 +65,49 @@ int netdClientAccept(int sockfd, sockaddr* addr, socklen_t* addrlen) { addr = &socketAddress; } if (FwmarkClient::shouldSetFwmark(acceptedSocket, addr)) { - char data[] = {FWMARK_COMMAND_ON_ACCEPT}; - if (!FwmarkClient().send(data, sizeof(data), acceptedSocket)) { + FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0}; + if (!FwmarkClient().send(&command, sizeof(command), acceptedSocket)) { return closeFdAndRestoreErrno(acceptedSocket); } } return acceptedSocket; } +// TODO: Convert to C++11 std::atomic<unsigned>. +volatile sig_atomic_t netIdForProcess = NETID_UNSET; +volatile sig_atomic_t netIdForResolv = NETID_UNSET; + +unsigned getNetworkForResolv(unsigned netId) { + if (netId != NETID_UNSET) { + return netId; + } + netId = netIdForProcess; + if (netId != NETID_UNSET) { + return netId; + } + return netIdForResolv; +} + +bool setNetworkForTarget(unsigned netId, volatile sig_atomic_t* target) { + if (netId == NETID_UNSET) { + *target = netId; + return true; + } + // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked + // with the netId. Don't create an AF_INET socket, because then the creation itself might cause + // another check with the fwmark server (see netdClientSocket()), which would be wasteful. + int socketFd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (socketFd < 0) { + return false; + } + bool status = setNetworkForSocket(netId, socketFd); + closeFdAndRestoreErrno(socketFd); + if (status) { + *target = netId; + } + return status; +} + } // namespace extern "C" void netdClientInitConnect(ConnectFunctionType* function) { @@ -82,3 +123,30 @@ extern "C" void netdClientInitAccept(AcceptFunctionType* function) { *function = netdClientAccept; } } + +extern "C" void netdClientInitNetIdForResolv(NetIdForResolvFunctionType* function) { + if (function) { + *function = getNetworkForResolv; + } +} + +extern "C" unsigned getNetworkForProcess() { + return netIdForProcess; +} + +extern "C" bool setNetworkForSocket(unsigned netId, int socketFd) { + if (socketFd < 0) { + errno = EBADF; + return false; + } + FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId}; + return FwmarkClient().send(&command, sizeof(command), socketFd); +} + +extern "C" bool setNetworkForProcess(unsigned netId) { + return setNetworkForTarget(netId, &netIdForProcess); +} + +extern "C" bool setNetworkForResolv(unsigned netId) { + return setNetworkForTarget(netId, &netIdForResolv); +} diff --git a/include/FwmarkCommand.h b/include/FwmarkCommand.h index 5854a816..9ec93e20 100644 --- a/include/FwmarkCommand.h +++ b/include/FwmarkCommand.h @@ -17,13 +17,15 @@ #ifndef NETD_INCLUDE_FWMARK_COMMAND_H #define NETD_INCLUDE_FWMARK_COMMAND_H -#include <stdint.h> - // Commands sent from clients to the fwmark server to mark sockets (i.e., set their SO_MARK). -const uint8_t FWMARK_COMMAND_ON_CREATE = 0; -const uint8_t FWMARK_COMMAND_ON_CONNECT = 1; -const uint8_t FWMARK_COMMAND_ON_ACCEPT = 2; -const uint8_t FWMARK_COMMAND_SELECT_NETWORK = 3; -const uint8_t FWMARK_COMMAND_PROTECT_FROM_VPN = 4; +struct FwmarkCommand { + enum { + ON_ACCEPT, + ON_CONNECT, + SELECT_NETWORK, + PROTECT_FROM_VPN, + } cmdId; + unsigned netId; // used only in the SELECT_NETWORK command; ignored otherwise. +}; #endif // NETD_INCLUDE_FWMARK_COMMAND_H diff --git a/include/NetdClient.h b/include/NetdClient.h new file mode 100644 index 00000000..c3f24095 --- /dev/null +++ b/include/NetdClient.h @@ -0,0 +1,34 @@ +/* + * 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 NETD_INCLUDE_NETD_CLIENT_H +#define NETD_INCLUDE_NETD_CLIENT_H + +#include <stdbool.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +bool setNetworkForSocket(unsigned netId, int socketFd); + +unsigned getNetworkForProcess(void); +bool setNetworkForProcess(unsigned netId); + +bool setNetworkForResolv(unsigned netId); + +__END_DECLS + +#endif // NETD_INCLUDE_NETD_CLIENT_H diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp index 187d4bfb..56127ca0 100644 --- a/server/FwmarkServer.cpp +++ b/server/FwmarkServer.cpp @@ -21,16 +21,9 @@ #include "NetworkController.h" #include "PermissionsController.h" - #include <sys/socket.h> #include <unistd.h> -namespace { - -const int MAX_COMMAND_LENGTH = 16; - -} // namespace - FwmarkServer::FwmarkServer(NetworkController* networkController, PermissionsController* permissionsController) : SocketListener("fwmarkd", true), @@ -57,9 +50,10 @@ bool FwmarkServer::onDataAvailable(SocketClient* client) { } void FwmarkServer::processClient(SocketClient* client, int* fd) { - char command[MAX_COMMAND_LENGTH] = {}; + FwmarkCommand command; + iovec iov; - iov.iov_base = command; + iov.iov_base = &command; iov.iov_len = sizeof(command); msghdr message; @@ -81,6 +75,11 @@ void FwmarkServer::processClient(SocketClient* client, int* fd) { return; } + if (messageLength != sizeof(command)) { + errno = EINVAL; + return; + } + cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message); if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS && cmsgh->cmsg_len == CMSG_LEN(sizeof(*fd))) { @@ -98,14 +97,18 @@ void FwmarkServer::processClient(SocketClient* client, int* fd) { return; } - switch (command[0]) { - case FWMARK_COMMAND_ON_CREATE: { - // on socket creation - // TODO + switch (command.cmdId) { + case FwmarkCommand::ON_ACCEPT: { + // Called after a socket accept(). The kernel would've marked the netId into the socket + // already, so we just need to check permissions here. + if (!mPermissionsController->isUserPermittedOnNetwork(client->getUid(), fwmark.netId)) { + errno = EPERM; + return; + } break; } - case FWMARK_COMMAND_ON_CONNECT: { + case FwmarkCommand::ON_CONNECT: { // Set the netId (of the default network) into the fwmark, if it has not already been // set explicitly. Called before a socket connect() happens. if (!fwmark.explicitlySelected) { @@ -114,23 +117,13 @@ void FwmarkServer::processClient(SocketClient* client, int* fd) { break; } - case FWMARK_COMMAND_ON_ACCEPT: { - // Called after a socket accept(). The kernel would've marked the netId into the socket - // already, so we just need to check permissions here. - if (!mPermissionsController->isUserPermittedOnNetwork(client->getUid(), fwmark.netId)) { - errno = EPERM; - return; - } - break; - } - - case FWMARK_COMMAND_SELECT_NETWORK: { + case FwmarkCommand::SELECT_NETWORK: { // set socket mark // TODO break; } - case FWMARK_COMMAND_PROTECT_FROM_VPN: { + case FwmarkCommand::PROTECT_FROM_VPN: { // set vpn protect // TODO break; |