FwmarkServer.cpp revision a675b00423e5a370e72a7f10f6b632b4d15a1ff4
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 "FwmarkServer.h"
18
19#include "Fwmark.h"
20#include "FwmarkCommand.h"
21#include "NetworkController.h"
22#include "resolv_netid.h"
23
24#include <sys/socket.h>
25#include <unistd.h>
26
27FwmarkServer::FwmarkServer(NetworkController* networkController) :
28        SocketListener("fwmarkd", true), mNetworkController(networkController) {
29}
30
31bool FwmarkServer::onDataAvailable(SocketClient* client) {
32    int fd = -1;
33    int error = processClient(client, &fd);
34    if (fd >= 0) {
35        close(fd);
36    }
37
38    // Always send a response even if there were connection errors or read errors, so that we don't
39    // inadvertently cause the client to hang (which always waits for a response).
40    client->sendData(&error, sizeof(error));
41
42    // Always close the client connection (by returning false). This prevents a DoS attack where
43    // the client issues multiple commands on the same connection, never reading the responses,
44    // causing its receive buffer to fill up, and thus causing our client->sendData() to block.
45    return false;
46}
47
48int FwmarkServer::processClient(SocketClient* client, int* fd) {
49    FwmarkCommand command;
50
51    iovec iov;
52    iov.iov_base = &command;
53    iov.iov_len = sizeof(command);
54
55    msghdr message;
56    memset(&message, 0, sizeof(message));
57    message.msg_iov = &iov;
58    message.msg_iovlen = 1;
59
60    union {
61        cmsghdr cmh;
62        char cmsg[CMSG_SPACE(sizeof(*fd))];
63    } cmsgu;
64
65    memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
66    message.msg_control = cmsgu.cmsg;
67    message.msg_controllen = sizeof(cmsgu.cmsg);
68
69    int messageLength = TEMP_FAILURE_RETRY(recvmsg(client->getSocket(), &message, 0));
70    if (messageLength <= 0) {
71        return -errno;
72    }
73
74    if (messageLength != sizeof(command)) {
75        return -EBADMSG;
76    }
77
78    cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
79    if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS &&
80        cmsgh->cmsg_len == CMSG_LEN(sizeof(*fd))) {
81        memcpy(fd, CMSG_DATA(cmsgh), sizeof(*fd));
82    }
83
84    if (*fd < 0) {
85        return -EBADF;
86    }
87
88    Fwmark fwmark;
89    socklen_t fwmarkLen = sizeof(fwmark.intValue);
90    if (getsockopt(*fd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen) == -1) {
91        return -errno;
92    }
93
94    Permission permission = mNetworkController->getPermissionForUser(client->getUid());
95
96    switch (command.cmdId) {
97        case FwmarkCommand::ON_ACCEPT: {
98            // Called after a socket accept(). The kernel would've marked the netId and necessary
99            // permissions bits, so we just add the rest of the user's permissions here.
100            permission = static_cast<Permission>(permission | fwmark.permission);
101            break;
102        }
103
104        case FwmarkCommand::ON_CONNECT: {
105            // Set the netId (of the default network) into the fwmark, if it has not already been
106            // set explicitly. Called before a socket connect() happens.
107            if (!fwmark.explicitlySelected) {
108                fwmark.netId = mNetworkController->getDefaultNetwork();
109            }
110            break;
111        }
112
113        case FwmarkCommand::SELECT_NETWORK: {
114            fwmark.netId = command.netId;
115            if (command.netId == NETID_UNSET) {
116                fwmark.explicitlySelected = false;
117            } else {
118                fwmark.explicitlySelected = true;
119                // If the socket already has the protectedFromVpn bit set, don't reset it, because
120                // non-CONNECTIVITY_INTERNAL apps (e.g.: VpnService) may also protect sockets.
121                if (permission & PERMISSION_CONNECTIVITY_INTERNAL) {
122                    fwmark.protectedFromVpn = true;
123                }
124                if (!mNetworkController->isValidNetwork(command.netId)) {
125                    return -ENONET;
126                }
127                if (!mNetworkController->isUserPermittedOnNetwork(client->getUid(),
128                                                                  command.netId)) {
129                    return -EPERM;
130                }
131            }
132            break;
133        }
134
135        case FwmarkCommand::PROTECT_FROM_VPN: {
136            // set vpn protect
137            // TODO
138            break;
139        }
140
141        default: {
142            // unknown command
143            return -EPROTO;
144        }
145    }
146
147    fwmark.permission = permission;
148
149    if (setsockopt(*fd, SOL_SOCKET, SO_MARK, &fwmark.intValue, sizeof(fwmark.intValue)) == -1) {
150        return -errno;
151    }
152
153    return 0;
154}
155