/** * Copyright (c) 2016, 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. */ #define LOG_TAG "Netd" #include #include #include #include #include #include #include #include "android/net/BnNetd.h" #include "Controllers.h" #include "DumpWriter.h" #include "NetdConstants.h" #include "NetdNativeService.h" #include "RouteController.h" #include "SockDiag.h" #include "UidRanges.h" using android::base::StringPrintf; namespace android { namespace net { namespace { const char CONNECTIVITY_INTERNAL[] = "android.permission.CONNECTIVITY_INTERNAL"; const char DUMP[] = "android.permission.DUMP"; binder::Status checkPermission(const char *permission) { pid_t pid; uid_t uid; if (checkCallingPermission(String16(permission), (int32_t *) &pid, (int32_t *) &uid)) { return binder::Status::ok(); } else { auto err = StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission); return binder::Status::fromExceptionCode(binder::Status::EX_SECURITY, String8(err.c_str())); } } #define ENFORCE_PERMISSION(permission) { \ binder::Status status = checkPermission((permission)); \ if (!status.isOk()) { \ return status; \ } \ } #define NETD_LOCKING_RPC(permission, lock) \ ENFORCE_PERMISSION(permission); \ android::RWLock::AutoWLock _lock(lock); #define NETD_BIG_LOCK_RPC(permission) NETD_LOCKING_RPC((permission), gBigNetdLock) } // namespace status_t NetdNativeService::start() { IPCThreadState::self()->disableBackgroundScheduling(true); status_t ret = BinderService::publish(); if (ret != android::OK) { return ret; } sp ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); return android::OK; } status_t NetdNativeService::dump(int fd, const Vector & /* args */) { const binder::Status dump_permission = checkPermission(DUMP); if (!dump_permission.isOk()) { const String8 msg(dump_permission.toString8()); write(fd, msg.string(), msg.size()); return PERMISSION_DENIED; } // This method does not grab any locks. If individual classes need locking // their dump() methods MUST handle locking appropriately. DumpWriter dw(fd); dw.blankline(); gCtls->netCtrl.dump(dw); dw.blankline(); return NO_ERROR; } binder::Status NetdNativeService::isAlive(bool *alive) { NETD_BIG_LOCK_RPC(CONNECTIVITY_INTERNAL); *alive = true; return binder::Status::ok(); } binder::Status NetdNativeService::firewallReplaceUidChain(const android::String16& chainName, bool isWhitelist, const std::vector& uids, bool *ret) { NETD_LOCKING_RPC(CONNECTIVITY_INTERNAL, gCtls->firewallCtrl.lock); android::String8 name = android::String8(chainName); int err = gCtls->firewallCtrl.replaceUidChain(name.string(), isWhitelist, uids); *ret = (err == 0); return binder::Status::ok(); } binder::Status NetdNativeService::bandwidthEnableDataSaver(bool enable, bool *ret) { NETD_LOCKING_RPC(CONNECTIVITY_INTERNAL, gCtls->bandwidthCtrl.lock); int err = gCtls->bandwidthCtrl.enableDataSaver(enable); *ret = (err == 0); return binder::Status::ok(); } binder::Status NetdNativeService::networkRejectNonSecureVpn(bool add, const std::vector& uidRangeArray) { // TODO: elsewhere RouteController is only used from the tethering and network controllers, so // it should be possible to use the same lock as NetworkController. However, every call through // the CommandListener "network" command will need to hold this lock too, not just the ones that // read/modify network internal state (that is sufficient for ::dump() because it doesn't // look at routes, but it's not enough here). NETD_BIG_LOCK_RPC(CONNECTIVITY_INTERNAL); UidRanges uidRanges(uidRangeArray); int err; if (add) { err = RouteController::addUsersToRejectNonSecureNetworkRule(uidRanges); } else { err = RouteController::removeUsersFromRejectNonSecureNetworkRule(uidRanges); } if (err != 0) { return binder::Status::fromServiceSpecificError(-err, String8::format("RouteController error: %s", strerror(-err))); } return binder::Status::ok(); } binder::Status NetdNativeService::socketDestroy(const std::vector& uids, const std::vector& skipUids) { ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); SockDiag sd; if (!sd.open()) { return binder::Status::fromServiceSpecificError(EIO, String8("Could not open SOCK_DIAG socket")); } UidRanges uidRanges(uids); int err = sd.destroySockets(uidRanges, std::set(skipUids.begin(), skipUids.end())); if (err) { return binder::Status::fromServiceSpecificError(-err, String8::format("destroySockets: %s", strerror(-err))); } return binder::Status::ok(); } binder::Status NetdNativeService::setResolverConfiguration(int32_t netId, const std::vector& servers, const std::vector& domains, const std::vector& params) { // This function intentionally does not lock within Netd, as Bionic is thread-safe. ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); int err = gCtls->resolverCtrl.setResolverConfiguration(netId, servers, domains, params); if (err != 0) { return binder::Status::fromServiceSpecificError(-err, String8::format("ResolverController error: %s", strerror(-err))); } return binder::Status::ok(); } binder::Status NetdNativeService::getResolverInfo(int32_t netId, std::vector* servers, std::vector* domains, std::vector* params, std::vector* stats) { // This function intentionally does not lock within Netd, as Bionic is thread-safe. ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); int err = gCtls->resolverCtrl.getResolverInfo(netId, servers, domains, params, stats); if (err != 0) { return binder::Status::fromServiceSpecificError(-err, String8::format("ResolverController error: %s", strerror(-err))); } return binder::Status::ok(); } } // namespace net } // namespace android