OSDN Git Service

Implement permissions set/clear for both networks and users.
authorSreeram Ramachandran <sreeram@google.com>
Fri, 11 Apr 2014 02:58:06 +0000 (19:58 -0700)
committerSreeram Ramachandran <sreeram@google.com>
Tue, 13 May 2014 23:09:53 +0000 (23:09 +0000)
(cherry picked from commit 47a0fa0ad402704b639fbc2868c55adc432bb6ea)

Change-Id: Ibb71025e1e2c72bd300fb7379d5ab54da4a6982e

CommandListener.cpp
Fwmark.cpp
NetworkController.cpp
NetworkController.h
Permission.h
PermissionsController.cpp
PermissionsController.h
RouteController.cpp
RouteController.h

index 122589a..34a28fa 100644 (file)
 #include <string>
 #include <vector>
 
+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>(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 <netId> <interface> [CNS|CI]
+    //    0      1       2         3            4
+    // network create <netId> <interface> [<permission> ...]
     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 <netId>
     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  [<permission> ...]  <uid>  ...
+    // network permission   user  clear     <uid>     ...
+    // network permission network  set  [<permission> ...] <netId> ...
+    // network permission network clear    <netId>    ...
+    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<unsigned> 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 <add|remove> <netId> <num-resolvers> <resolver1> .. <resolverN> [searchDomain1] .. [searchDomainM]
     // network route <add|remove> <other-route-params>
     // network legacy <uid> route <add|remove> <other-route-params>
     // network default set <netId>
     // network default clear
-    // network permission user|network set [CI] [CNS] <id1> .. <idN>
-    // network permission user|network clear <id1> .. <idN>
     // network vpn create <netId> [owner_uid]
     // network vpn destroy <netId>
     // network <bind|unbind> <netId> <uid1> .. <uidN>
index 62aac5b..52c8606 100644 (file)
@@ -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;
index fe78935..185f7d7 100644 (file)
@@ -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<unsigned, std::string>::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<unsigned, std::string>(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<unsigned>& 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<unsigned>& 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<unsigned, std::string>::const_iterator Iterator;
+        std::pair<Iterator, Iterator> 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),
index d0d8569..1063f92 100644 (file)
@@ -22,6 +22,7 @@
 #include <list>
 #include <map>
 #include <string>
+#include <vector>
 
 #include <stddef.h>
 #include <stdint.h>
@@ -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<unsigned>& uid);
+    bool setPermissionForNetwork(Permission permission, const std::vector<unsigned>& netId);
+
 private:
     struct UidEntry {
         int uid_start;
index bc51b27..8cb6b8f 100644 (file)
 // 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);
index dc72229..3240ec8 100644 (file)
 
 #include "PermissionsController.h"
 
-Permission PermissionsController::getPermissionForNetwork(unsigned netId) const {
-    std::map<unsigned, Permission>::const_iterator iter = mNetworks.find(netId);
-    return iter != mNetworks.end() ? iter->second : PERMISSION_NONE;
+namespace {
+
+Permission get(const std::map<unsigned, Permission>& map, unsigned id) {
+    std::map<unsigned, Permission>::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<unsigned, Permission>* 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);
 }
index f7acd30..c21d7f7 100644 (file)
 
 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<unsigned, Permission> mUsers;
     std::map<unsigned, Permission> mNetworks;
 };
 
index 643c2e1..737dd9f 100644 (file)
@@ -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);
 }
index 2c80954..65f8f84 100644 (file)
@@ -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