From: Sreeram Ramachandran Date: Fri, 11 Apr 2014 02:58:06 +0000 (-0700) Subject: Implement permissions set/clear for both networks and users. X-Git-Tag: android-x86-7.1-r1~290 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=379bd33f7640e2c4bef902be0ed6cb96378c8c2e;p=android-x86%2Fsystem-netd.git Implement permissions set/clear for both networks and users. (cherry picked from commit 47a0fa0ad402704b639fbc2868c55adc432bb6ea) Change-Id: Ibb71025e1e2c72bd300fb7379d5ab54da4a6982e --- diff --git a/CommandListener.cpp b/CommandListener.cpp index 122589a..34a28fa 100644 --- a/CommandListener.cpp +++ b/CommandListener.cpp @@ -50,6 +50,24 @@ #include #include +namespace { + +// Parses string permissions in argv[*nextArg], argv[*nextArg + 1], etc. and converts them into a +// Permission enum. On return, nextArg will point to one past the last valid permissions string. +Permission parseMultiplePermissions(int argc, char** argv, int* nextArg) { + Permission permission = PERMISSION_NONE; + for (; *nextArg < argc; ++*nextArg) { + Permission p = permissionFromString(argv[*nextArg]); + if (p == PERMISSION_NONE) { + break; + } + permission = static_cast(permission | p); + } + return permission; +} + +} // namespace + PermissionsController* CommandListener::sPermissionsController = NULL; RouteController* CommandListener::sRouteController = NULL; NetworkController *CommandListener::sNetCtrl = NULL; @@ -1628,8 +1646,8 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc, return syntaxError(client, "Missing argument"); } - // 0 1 2 3 4 - // network create [CNS|CI] + // 0 1 2 3 4 + // network create [ ...] if (!strcmp(argv[1], "create")) { if (argc < 4) { return syntaxError(client, "Missing argument"); @@ -1640,12 +1658,10 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc, return paramError(client, "Invalid netId"); } const char* interface = argv[3]; - Permission permission = PERMISSION_NONE; - if (argc > 4) { - permission = permissionFromString(argv[4]); - if (permission == PERMISSION_NONE) { - return paramError(client, "Invalid permission"); - } + int nextArg = 4; + Permission permission = parseMultiplePermissions(argc, argv, &nextArg); + if (nextArg != argc) { + return syntaxError(client, "Unknown trailing argument(s)"); } if (!sNetCtrl->createNetwork(netId, interface, permission)) { return operationError(client, "createNetwork() failed"); @@ -1656,8 +1672,8 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc, // 0 1 2 // network destroy if (!strcmp(argv[1], "destroy")) { - if (argc < 3) { - return syntaxError(client, "Missing netId"); + if (argc != 3) { + return syntaxError(client, "Incorrect number of arguments"); } // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId. unsigned netId = strtoul(argv[2], NULL, 0); @@ -1670,13 +1686,53 @@ int CommandListener::NetworkCommand::runCommand(SocketClient* client, int argc, return success(client); } + // 0 1 2 3 4 + // network permission user set [ ...] ... + // network permission user clear ... + // network permission network set [ ...] ... + // network permission network clear ... + if (!strcmp(argv[1], "permission")) { + if (argc < 5) { + return syntaxError(client, "Missing argument"); + } + int nextArg = 4; + Permission permission = PERMISSION_NONE; + if (!strcmp(argv[3], "set")) { + permission = parseMultiplePermissions(argc, argv, &nextArg); + } else if (strcmp(argv[3], "clear")) { + return syntaxError(client, "Unknown argument"); + } + std::vector ids; + for (; nextArg < argc; ++nextArg) { + char* endPtr; + unsigned id = strtoul(argv[nextArg], &endPtr, 0); + if (!*argv[nextArg] || *endPtr) { + return syntaxError(client, "Invalid id"); + } + ids.push_back(id); + } + if (ids.empty()) { + return syntaxError(client, "Missing id"); + } + if (!strcmp(argv[2], "user")) { + if (!sNetCtrl->setPermissionForUser(permission, ids)) { + return operationError(client, "setPermissionForUser() failed"); + } + } else if (!strcmp(argv[2], "network")) { + if (!sNetCtrl->setPermissionForNetwork(permission, ids)) { + return operationError(client, "setPermissionForNetwork() failed"); + } + } else { + return syntaxError(client, "Unknown argument"); + } + return success(client); + } + // network dns .. [searchDomain1] .. [searchDomainM] // network route // network legacy route // network default set // network default clear - // network permission user|network set [CI] [CNS] .. - // network permission user|network clear .. // network vpn create [owner_uid] // network vpn destroy // network .. diff --git a/Fwmark.cpp b/Fwmark.cpp index 62aac5b..52c8606 100644 --- a/Fwmark.cpp +++ b/Fwmark.cpp @@ -34,11 +34,10 @@ uint32_t getFwmark(unsigned netId, bool exp, bool protect, Permission permission if (protect) { fwmark |= FWMARK_MASK_PROTECT; } - if (permission == PERMISSION_CHANGE_NETWORK_STATE) { - fwmark |= FWMARK_MASK_CHANGE_NETWORK_STATE; - } else if (permission == PERMISSION_CONNECTIVITY_INTERNAL) { - // CONNECTIVITY_INTERNAL implies CHANGE_NETWORK_STATE. + if (permission & PERMISSION_CHANGE_NETWORK_STATE) { fwmark |= FWMARK_MASK_CHANGE_NETWORK_STATE; + } + if (permission & PERMISSION_CONNECTIVITY_INTERNAL) { fwmark |= FWMARK_MASK_CONNECTIVITY_INTERNAL; } return fwmark; diff --git a/NetworkController.cpp b/NetworkController.cpp index fe78935..185f7d7 100644 --- a/NetworkController.cpp +++ b/NetworkController.cpp @@ -132,12 +132,14 @@ unsigned NetworkController::getNetworkId(const char* interface) { bool NetworkController::createNetwork(unsigned netId, const char* interface, Permission permission) { if (!isNetIdValid(netId) || !interface) { + ALOGE("invalid netId %u or interface null", netId); return false; } typedef std::multimap::const_iterator Iterator; for (Iterator iter = mNetIdToInterfaces.begin(); iter != mNetIdToInterfaces.end(); ++iter) { if (iter->second == interface) { + ALOGE("interface %s already assigned to netId %u", interface, iter->first); return false; } } @@ -146,17 +148,18 @@ bool NetworkController::createNetwork(unsigned netId, const char* interface, return false; } - mPermissionsController->setPermissionForNetwork(netId, permission); + mPermissionsController->setPermissionForNetwork(permission, netId); mNetIdToInterfaces.insert(std::pair(netId, interface)); return true; } bool NetworkController::destroyNetwork(unsigned netId) { if (!isNetIdValid(netId)) { + ALOGE("invalid netId %u", netId); return false; } - // TODO: ioctl(SIOCKILLADDR, ...); + // TODO: ioctl(SIOCKILLADDR, ...) to kill all sockets on the old network. bool status = true; @@ -170,12 +173,60 @@ bool NetworkController::destroyNetwork(unsigned netId) { } } - mPermissionsController->clearPermissionForNetwork(netId); + mPermissionsController->setPermissionForNetwork(PERMISSION_NONE, netId); mNetIdToInterfaces.erase(netId); return status; } +bool NetworkController::setPermissionForUser(Permission permission, + const std::vector& uid) { + for (size_t i = 0; i < uid.size(); ++i) { + mPermissionsController->setPermissionForUser(permission, uid[i]); + } + return true; +} + +bool NetworkController::setPermissionForNetwork(Permission newPermission, + const std::vector& netId) { + bool status = true; + + for (size_t i = 0; i < netId.size(); ++i) { + if (!isNetIdValid(netId[i])) { + ALOGE("invalid netId %u", netId[i]); + status = false; + continue; + } + + typedef std::multimap::const_iterator Iterator; + std::pair range = mNetIdToInterfaces.equal_range(netId[i]); + if (range.first == range.second) { + ALOGE("unknown netId %u", netId[i]); + status = false; + continue; + } + + Permission oldPermission = mPermissionsController->getPermissionForNetwork(netId[i]); + if (oldPermission == newPermission) { + continue; + } + + // TODO: ioctl(SIOCKILLADDR, ...) to kill sockets on the network that don't have + // newPermission. + + for (Iterator iter = range.first; iter != range.second; ++iter) { + if (!mRouteController->modifyNetworkPermission(netId[i], iter->second.c_str(), + oldPermission, newPermission)) { + status = false; + } + } + + mPermissionsController->setPermissionForNetwork(newPermission, netId[i]); + } + + return status; +} + NetworkController::UidEntry::UidEntry( int start, int end, unsigned netId, bool forward_dns) : uid_start(start), diff --git a/NetworkController.h b/NetworkController.h index d0d8569..1063f92 100644 --- a/NetworkController.h +++ b/NetworkController.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,9 @@ public: bool createNetwork(unsigned netId, const char* interface, Permission permission); bool destroyNetwork(unsigned netId); + bool setPermissionForUser(Permission permission, const std::vector& uid); + bool setPermissionForNetwork(Permission permission, const std::vector& netId); + private: struct UidEntry { int uid_start; diff --git a/Permission.h b/Permission.h index bc51b27..8cb6b8f 100644 --- a/Permission.h +++ b/Permission.h @@ -22,13 +22,17 @@ // app must hold to be allowed to use the network. PERMISSION_NONE means "no special permission is // held by the app" or "no special permission is required to use the network". // -// Currently, each permission includes all the permissions above it (i.e., CONNECTIVITY_INTERNAL -// implies CHANGE_NETWORK_STATE), which is why these are not bit values that need to be OR'ed -// together. This may change in the future. +// Permissions are flags that can be OR'ed together to represent combinations of permissions. +// +// NOTE: netd doesn't enforce any semantics for the permission values. E.g., while all apps with +// CONNECTIVITY_INTERNAL may also implicitly or explicitly have CHANGE_NETWORK_STATE, netd doesn't +// enforce it. It's the responsibility of the caller to set all permissions bits explicitly. I.e., +// if you set the PERMISSION_CONNECTIVITY_INTERNAL bit, you should probably also set the +// PERMISSION_CHANGE_NETWORK_STATE bit. enum Permission { - PERMISSION_NONE, - PERMISSION_CHANGE_NETWORK_STATE, - PERMISSION_CONNECTIVITY_INTERNAL + PERMISSION_NONE = 0x0, + PERMISSION_CHANGE_NETWORK_STATE = 0x1, + PERMISSION_CONNECTIVITY_INTERNAL = 0x2 }; Permission permissionFromString(const char* permission); diff --git a/PermissionsController.cpp b/PermissionsController.cpp index dc72229..3240ec8 100644 --- a/PermissionsController.cpp +++ b/PermissionsController.cpp @@ -16,19 +16,35 @@ #include "PermissionsController.h" -Permission PermissionsController::getPermissionForNetwork(unsigned netId) const { - std::map::const_iterator iter = mNetworks.find(netId); - return iter != mNetworks.end() ? iter->second : PERMISSION_NONE; +namespace { + +Permission get(const std::map& map, unsigned id) { + std::map::const_iterator iter = map.find(id); + return iter != map.end() ? iter->second : PERMISSION_NONE; } -void PermissionsController::setPermissionForNetwork(unsigned netId, Permission permission) { +void set(std::map* map, Permission permission, unsigned id) { if (permission == PERMISSION_NONE) { - clearPermissionForNetwork(netId); - return; + map->erase(id); + } else { + (*map)[id] = permission; } - mNetworks[netId] = permission; } -void PermissionsController::clearPermissionForNetwork(unsigned netId) { - mNetworks.erase(netId); +} // namespace + +Permission PermissionsController::getPermissionForUser(unsigned uid) const { + return get(mUsers, uid); +} + +void PermissionsController::setPermissionForUser(Permission permission, unsigned uid) { + set(&mUsers, permission, uid); +} + +Permission PermissionsController::getPermissionForNetwork(unsigned netId) const { + return get(mNetworks, netId); +} + +void PermissionsController::setPermissionForNetwork(Permission permission, unsigned netId) { + set(&mNetworks, permission, netId); } diff --git a/PermissionsController.h b/PermissionsController.h index f7acd30..c21d7f7 100644 --- a/PermissionsController.h +++ b/PermissionsController.h @@ -23,11 +23,14 @@ class PermissionsController { public: + Permission getPermissionForUser(unsigned uid) const; + void setPermissionForUser(Permission permission, unsigned uid); + Permission getPermissionForNetwork(unsigned netId) const; - void setPermissionForNetwork(unsigned netId, Permission permission); - void clearPermissionForNetwork(unsigned netId); + void setPermissionForNetwork(Permission permission, unsigned netId); private: + std::map mUsers; std::map mNetworks; }; diff --git a/RouteController.cpp b/RouteController.cpp index 643c2e1..737dd9f 100644 --- a/RouteController.cpp +++ b/RouteController.cpp @@ -77,7 +77,8 @@ bool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table, return true; } -bool modifyNetwork(unsigned netId, const char* interface, Permission permission, bool add) { +bool modifyRules(unsigned netId, const char* interface, Permission permission, bool add, + bool modifyIptables) { uint32_t table = getRouteTableForInterface(interface); if (!table) { return false; @@ -126,12 +127,14 @@ bool modifyNetwork(unsigned netId, const char* interface, Permission permission, // ping replies). // + Mark sockets that accept connections from this interface so that the connection stays on // the same interface. - action = add ? "-A" : "-D"; - char markString[UINT32_HEX_STRLEN]; - snprintf(markString, sizeof(markString), "0x%x", netId); - if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK", - "--set-mark", markString, NULL)) { - return false; + if (modifyIptables) { + action = add ? "-A" : "-D"; + char markString[UINT32_HEX_STRLEN]; + snprintf(markString, sizeof(markString), "0x%x", netId); + if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK", + "--set-mark", markString, NULL)) { + return false; + } } return true; @@ -140,9 +143,17 @@ bool modifyNetwork(unsigned netId, const char* interface, Permission permission, } // namespace bool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) { - return modifyNetwork(netId, interface, permission, true); + return modifyRules(netId, interface, permission, true, true); } bool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) { - return modifyNetwork(netId, interface, permission, false); + return modifyRules(netId, interface, permission, false, true); + // TODO: Flush the routing table. +} + +bool RouteController::modifyNetworkPermission(unsigned netId, const char* interface, + Permission oldPermission, Permission newPermission) { + // Add the new rules before deleting the old ones, to avoid race conditions. + return modifyRules(netId, interface, newPermission, true, false) && + modifyRules(netId, interface, oldPermission, false, false); } diff --git a/RouteController.h b/RouteController.h index 2c80954..65f8f84 100644 --- a/RouteController.h +++ b/RouteController.h @@ -25,6 +25,8 @@ public: static bool createNetwork(unsigned netId, const char* interface, Permission permission); static bool destroyNetwork(unsigned netId, const char* interface, Permission permission); + static bool modifyNetworkPermission(unsigned netId, const char* interface, + Permission oldPermission, Permission newPermission); }; #endif // SYSTEM_NETD_ROUTE_CONTROLLER_H