/** * 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 <vector> #include <android-base/stringprintf.h> #include <cutils/log.h> #include <cutils/properties.h> #include <utils/Errors.h> #include <utils/String16.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include "android/net/BnNetd.h" #include "Controllers.h" #include "DumpWriter.h" #include "EventReporter.h" #include "InterfaceController.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())); } } binder::Status getXfrmStatus(int xfrmCode) { switch(xfrmCode) { case 0: return binder::Status::ok(); case -ENOENT: return binder::Status::fromServiceSpecificError(xfrmCode); } return binder::Status::fromExceptionCode(xfrmCode); } #define ENFORCE_DEBUGGABLE() { \ char value[PROPERTY_VALUE_MAX + 1]; \ if (property_get("ro.debuggable", value, NULL) != 1 \ || value[0] != '1') { \ return binder::Status::fromExceptionCode( \ binder::Status::EX_SECURITY, \ String8("Not available in production builds.") \ ); \ } \ } #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<NetdNativeService>::publish(); if (ret != android::OK) { return ret; } sp<ProcessState> ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); return android::OK; } status_t NetdNativeService::dump(int fd, const Vector<String16> & /* 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<int32_t>& 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<UidRange>& 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<UidRange>& uids, const std::vector<int32_t>& 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<uid_t>(skipUids.begin(), skipUids.end()), true /* excludeLoopback */); 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<std::string>& servers, const std::vector<std::string>& domains, const std::vector<int32_t>& 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<std::string>* servers, std::vector<std::string>* domains, std::vector<int32_t>* params, std::vector<int32_t>* 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(); } binder::Status NetdNativeService::tetherApplyDnsInterfaces(bool *ret) { NETD_BIG_LOCK_RPC(CONNECTIVITY_INTERNAL); *ret = gCtls->tetherCtrl.applyDnsInterfaces(); return binder::Status::ok(); } binder::Status NetdNativeService::interfaceAddAddress(const std::string &ifName, const std::string &addrString, int prefixLength) { ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); const int err = InterfaceController::addAddress( ifName.c_str(), addrString.c_str(), prefixLength); if (err != 0) { return binder::Status::fromServiceSpecificError(-err, String8::format("InterfaceController error: %s", strerror(-err))); } return binder::Status::ok(); } binder::Status NetdNativeService::interfaceDelAddress(const std::string &ifName, const std::string &addrString, int prefixLength) { ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); const int err = InterfaceController::delAddress( ifName.c_str(), addrString.c_str(), prefixLength); if (err != 0) { return binder::Status::fromServiceSpecificError(-err, String8::format("InterfaceController error: %s", strerror(-err))); } return binder::Status::ok(); } binder::Status NetdNativeService::setProcSysNet( int32_t family, int32_t which, const std::string &ifname, const std::string ¶meter, const std::string &value) { ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); const char *familyStr; switch (family) { case INetd::IPV4: familyStr = "ipv4"; break; case INetd::IPV6: familyStr = "ipv6"; break; default: return binder::Status::fromServiceSpecificError(EAFNOSUPPORT, String8("Bad family")); } const char *whichStr; switch (which) { case INetd::CONF: whichStr = "conf"; break; case INetd::NEIGH: whichStr = "neigh"; break; default: return binder::Status::fromServiceSpecificError(EINVAL, String8("Bad category")); } const int err = InterfaceController::setParameter( familyStr, whichStr, ifname.c_str(), parameter.c_str(), value.c_str()); if (err != 0) { return binder::Status::fromServiceSpecificError(-err, String8::format("ResolverController error: %s", strerror(-err))); } return binder::Status::ok(); } binder::Status NetdNativeService::getMetricsReportingLevel(int *reportingLevel) { // This function intentionally does not lock, since the only thing it does is one read from an // atomic_int. ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); ENFORCE_DEBUGGABLE(); *reportingLevel = gCtls->eventReporter.getMetricsReportingLevel(); return binder::Status::ok(); } binder::Status NetdNativeService::setMetricsReportingLevel(const int reportingLevel) { // This function intentionally does not lock, since the only thing it does is one write to an // atomic_int. ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); ENFORCE_DEBUGGABLE(); return (gCtls->eventReporter.setMetricsReportingLevel(reportingLevel) == 0) ? binder::Status::ok() : binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT); } binder::Status NetdNativeService::ipSecAllocateSpi( int32_t transformId, int32_t direction, const std::string& localAddress, const std::string& remoteAddress, int32_t inSpi, int32_t* outSpi) { // Necessary locking done in IpSecService and kernel ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); ALOGD("ipSecAllocateSpi()"); return getXfrmStatus(gCtls->xfrmCtrl.ipSecAllocateSpi( transformId, direction, localAddress, remoteAddress, inSpi, outSpi)); } binder::Status NetdNativeService::ipSecAddSecurityAssociation( int32_t transformId, int32_t mode, int32_t direction, const std::string& localAddress, const std::string& remoteAddress, int64_t underlyingNetworkHandle, int32_t spi, const std::string& authAlgo, const std::vector<uint8_t>& authKey, int32_t authTruncBits, const std::string& cryptAlgo, const std::vector<uint8_t>& cryptKey, int32_t cryptTruncBits, int32_t encapType, int32_t encapLocalPort, int32_t encapRemotePort, int32_t* allocatedSpi) { // Necessary locking done in IpSecService and kernel ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); ALOGD("ipSecAddSecurityAssociation()"); return getXfrmStatus(gCtls->xfrmCtrl.ipSecAddSecurityAssociation( transformId, mode, direction, localAddress, remoteAddress, underlyingNetworkHandle, spi, authAlgo, authKey, authTruncBits, cryptAlgo, cryptKey, cryptTruncBits, encapType, encapLocalPort, encapRemotePort, allocatedSpi)); } binder::Status NetdNativeService::ipSecDeleteSecurityAssociation( int32_t transformId, int32_t direction, const std::string& localAddress, const std::string& remoteAddress, int32_t spi) { // Necessary locking done in IpSecService and kernel ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); ALOGD("ipSecDeleteSecurityAssociation()"); return getXfrmStatus(gCtls->xfrmCtrl.ipSecDeleteSecurityAssociation( transformId, direction, localAddress, remoteAddress, spi)); } binder::Status NetdNativeService::ipSecApplyTransportModeTransform( const android::base::unique_fd& socket, int32_t transformId, int32_t direction, const std::string& localAddress, const std::string& remoteAddress, int32_t spi) { // Necessary locking done in IpSecService and kernel ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); ALOGD("ipSecApplyTransportModeTransform()"); return getXfrmStatus(gCtls->xfrmCtrl.ipSecApplyTransportModeTransform( socket, transformId, direction, localAddress, remoteAddress, spi)); } binder::Status NetdNativeService::ipSecRemoveTransportModeTransform( const android::base::unique_fd& socket) { // Necessary locking done in IpSecService and kernel ENFORCE_PERMISSION(CONNECTIVITY_INTERNAL); ALOGD("ipSecRemoveTransportModeTransform()"); return getXfrmStatus(gCtls->xfrmCtrl.ipSecRemoveTransportModeTransform( socket)); } } // namespace net } // namespace android