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 package com.android.server.connectivity;
18 
19 import static android.Manifest.permission.CHANGE_NETWORK_STATE;
20 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
21 import static android.Manifest.permission.INTERNET;
22 import static android.Manifest.permission.NETWORK_STACK;
23 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
24 import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
25 import static android.content.pm.PackageManager.GET_PERMISSIONS;
26 import static android.content.pm.PackageManager.MATCH_ANY_USER;
27 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
28 import static android.os.Process.INVALID_UID;
29 import static android.os.Process.SYSTEM_UID;
30 
31 import android.annotation.NonNull;
32 import android.content.Context;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.PackageInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.PackageManager.NameNotFoundException;
37 import android.content.pm.PackageManagerInternal;
38 import android.content.pm.UserInfo;
39 import android.net.INetd;
40 import android.net.UidRange;
41 import android.os.Build;
42 import android.os.RemoteException;
43 import android.os.ServiceSpecificException;
44 import android.os.UserHandle;
45 import android.os.UserManager;
46 import android.system.OsConstants;
47 import android.util.ArraySet;
48 import android.util.Log;
49 import android.util.SparseArray;
50 import android.util.SparseIntArray;
51 
52 import com.android.internal.annotations.GuardedBy;
53 import com.android.internal.annotations.VisibleForTesting;
54 import com.android.internal.util.ArrayUtils;
55 import com.android.internal.util.IndentingPrintWriter;
56 import com.android.server.LocalServices;
57 import com.android.server.SystemConfig;
58 
59 import java.util.ArrayList;
60 import java.util.Collection;
61 import java.util.HashMap;
62 import java.util.HashSet;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Map.Entry;
66 import java.util.Set;
67 
68 
69 /**
70  * A utility class to inform Netd of UID permisisons.
71  * Does a mass update at boot and then monitors for app install/remove.
72  *
73  * @hide
74  */
75 public class PermissionMonitor {
76     private static final String TAG = "PermissionMonitor";
77     private static final boolean DBG = true;
78     protected static final Boolean SYSTEM = Boolean.TRUE;
79     protected static final Boolean NETWORK = Boolean.FALSE;
80     private static final int VERSION_Q = Build.VERSION_CODES.Q;
81 
82     private final PackageManager mPackageManager;
83     private final UserManager mUserManager;
84     private final INetd mNetd;
85 
86     // Values are User IDs.
87     @GuardedBy("this")
88     private final Set<Integer> mUsers = new HashSet<>();
89 
90     // Keys are app uids. Values are true for SYSTEM permission and false for NETWORK permission.
91     @GuardedBy("this")
92     private final Map<Integer, Boolean> mApps = new HashMap<>();
93 
94     // Keys are active non-bypassable and fully-routed VPN's interface name, Values are uid ranges
95     // for apps under the VPN
96     @GuardedBy("this")
97     private final Map<String, Set<UidRange>> mVpnUidRanges = new HashMap<>();
98 
99     // A set of appIds for apps across all users on the device. We track appIds instead of uids
100     // directly to reduce its size and also eliminate the need to update this set when user is
101     // added/removed.
102     @GuardedBy("this")
103     private final Set<Integer> mAllApps = new HashSet<>();
104 
105     private class PackageListObserver implements PackageManagerInternal.PackageListObserver {
106 
getPermissionForUid(int uid)107         private int getPermissionForUid(int uid) {
108             int permission = 0;
109             // Check all the packages for this UID. The UID has the permission if any of the
110             // packages in it has the permission.
111             String[] packages = mPackageManager.getPackagesForUid(uid);
112             if (packages != null && packages.length > 0) {
113                 for (String name : packages) {
114                     final PackageInfo app = getPackageInfo(name);
115                     if (app != null && app.requestedPermissions != null) {
116                         permission |= getNetdPermissionMask(app.requestedPermissions,
117                               app.requestedPermissionsFlags);
118                     }
119                 }
120             } else {
121                 // The last package of this uid is removed from device. Clean the package up.
122                 permission = INetd.PERMISSION_UNINSTALLED;
123             }
124             return permission;
125         }
126 
127         @Override
onPackageAdded(String packageName, int uid)128         public void onPackageAdded(String packageName, int uid) {
129             sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
130         }
131 
132         @Override
onPackageChanged(@onNull String packageName, int uid)133         public void onPackageChanged(@NonNull String packageName, int uid) {
134             sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
135         }
136 
137         @Override
onPackageRemoved(String packageName, int uid)138         public void onPackageRemoved(String packageName, int uid) {
139             sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
140         }
141     }
142 
PermissionMonitor(Context context, INetd netd)143     public PermissionMonitor(Context context, INetd netd) {
144         mPackageManager = context.getPackageManager();
145         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
146         mNetd = netd;
147     }
148 
149     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
150     // receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
startMonitoring()151     public synchronized void startMonitoring() {
152         log("Monitoring");
153 
154         PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
155         if (pmi != null) {
156             pmi.getPackageList(new PackageListObserver());
157         } else {
158             loge("failed to get the PackageManagerInternal service");
159         }
160         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
161                 | MATCH_ANY_USER);
162         if (apps == null) {
163             loge("No apps");
164             return;
165         }
166 
167         SparseIntArray netdPermsUids = new SparseIntArray();
168 
169         for (PackageInfo app : apps) {
170             int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
171             if (uid < 0) {
172                 continue;
173             }
174             mAllApps.add(UserHandle.getAppId(uid));
175 
176             boolean isNetwork = hasNetworkPermission(app);
177             boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
178 
179             if (isNetwork || hasRestrictedPermission) {
180                 Boolean permission = mApps.get(uid);
181                 // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
182                 // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
183                 if (permission == null || permission == NETWORK) {
184                     mApps.put(uid, hasRestrictedPermission);
185                 }
186             }
187 
188             //TODO: unify the management of the permissions into one codepath.
189             int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
190                     app.requestedPermissionsFlags);
191             netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
192         }
193 
194         List<UserInfo> users = mUserManager.getUsers(true);  // exclude dying users
195         if (users != null) {
196             for (UserInfo user : users) {
197                 mUsers.add(user.id);
198             }
199         }
200 
201         final SparseArray<ArraySet<String>> systemPermission =
202                 SystemConfig.getInstance().getSystemPermissions();
203         for (int i = 0; i < systemPermission.size(); i++) {
204             ArraySet<String> perms = systemPermission.valueAt(i);
205             int uid = systemPermission.keyAt(i);
206             int netdPermission = 0;
207             // Get the uids of native services that have UPDATE_DEVICE_STATS or INTERNET permission.
208             if (perms != null) {
209                 netdPermission |= perms.contains(UPDATE_DEVICE_STATS)
210                         ? INetd.PERMISSION_UPDATE_DEVICE_STATS : 0;
211                 netdPermission |= perms.contains(INTERNET)
212                         ? INetd.PERMISSION_INTERNET : 0;
213             }
214             netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission);
215         }
216         log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
217         update(mUsers, mApps, true);
218         sendPackagePermissionsToNetd(netdPermsUids);
219     }
220 
221     @VisibleForTesting
isVendorApp(@onNull ApplicationInfo appInfo)222     static boolean isVendorApp(@NonNull ApplicationInfo appInfo) {
223         return appInfo.isVendor() || appInfo.isOem() || appInfo.isProduct();
224     }
225 
226     @VisibleForTesting
getDeviceFirstSdkInt()227     protected int getDeviceFirstSdkInt() {
228         return Build.VERSION.FIRST_SDK_INT;
229     }
230 
231     @VisibleForTesting
hasPermission(@onNull final PackageInfo app, @NonNull final String permission)232     boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) {
233         if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
234             return false;
235         }
236         final int index = ArrayUtils.indexOf(app.requestedPermissions, permission);
237         if (index < 0 || index >= app.requestedPermissionsFlags.length) return false;
238         return (app.requestedPermissionsFlags[index] & REQUESTED_PERMISSION_GRANTED) != 0;
239     }
240 
241     @VisibleForTesting
hasNetworkPermission(@onNull final PackageInfo app)242     boolean hasNetworkPermission(@NonNull final PackageInfo app) {
243         return hasPermission(app, CHANGE_NETWORK_STATE);
244     }
245 
246     @VisibleForTesting
hasRestrictedNetworkPermission(@onNull final PackageInfo app)247     boolean hasRestrictedNetworkPermission(@NonNull final PackageInfo app) {
248         // TODO : remove this check in the future(b/31479477). All apps should just
249         // request the appropriate permission for their use case since android Q.
250         if (app.applicationInfo != null) {
251             // Backward compatibility for b/114245686, on devices that launched before Q daemons
252             // and apps running as the system UID are exempted from this check.
253             if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
254                 return true;
255             }
256 
257             if (app.applicationInfo.targetSdkVersion < VERSION_Q
258                     && isVendorApp(app.applicationInfo)) {
259                 return true;
260             }
261         }
262 
263         return hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK)
264                 || hasPermission(app, NETWORK_STACK)
265                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
266     }
267 
268     /** Returns whether the given uid has using background network permission. */
hasUseBackgroundNetworksPermission(final int uid)269     public synchronized boolean hasUseBackgroundNetworksPermission(final int uid) {
270         // Apps with any of the CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_INTERNAL or
271         // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission has the permission to use background
272         // networks. mApps contains the result of checks for both hasNetworkPermission and
273         // hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
274         // permissions at least.
275         return mApps.containsKey(uid);
276     }
277 
toIntArray(Collection<Integer> list)278     private int[] toIntArray(Collection<Integer> list) {
279         int[] array = new int[list.size()];
280         int i = 0;
281         for (Integer item : list) {
282             array[i++] = item;
283         }
284         return array;
285     }
286 
update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add)287     private void update(Set<Integer> users, Map<Integer, Boolean> apps, boolean add) {
288         List<Integer> network = new ArrayList<>();
289         List<Integer> system = new ArrayList<>();
290         for (Entry<Integer, Boolean> app : apps.entrySet()) {
291             List<Integer> list = app.getValue() ? system : network;
292             for (int user : users) {
293                 list.add(UserHandle.getUid(user, app.getKey()));
294             }
295         }
296         try {
297             if (add) {
298                 mNetd.networkSetPermissionForUser(INetd.PERMISSION_NETWORK, toIntArray(network));
299                 mNetd.networkSetPermissionForUser(INetd.PERMISSION_SYSTEM, toIntArray(system));
300             } else {
301                 mNetd.networkClearPermissionForUser(toIntArray(network));
302                 mNetd.networkClearPermissionForUser(toIntArray(system));
303             }
304         } catch (RemoteException e) {
305             loge("Exception when updating permissions: " + e);
306         }
307     }
308 
309     /**
310      * Called when a user is added. See {link #ACTION_USER_ADDED}.
311      *
312      * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}.
313      *
314      * @hide
315      */
onUserAdded(int user)316     public synchronized void onUserAdded(int user) {
317         if (user < 0) {
318             loge("Invalid user in onUserAdded: " + user);
319             return;
320         }
321         mUsers.add(user);
322 
323         Set<Integer> users = new HashSet<>();
324         users.add(user);
325         update(users, mApps, true);
326     }
327 
328     /**
329      * Called when an user is removed. See {link #ACTION_USER_REMOVED}.
330      *
331      * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}.
332      *
333      * @hide
334      */
onUserRemoved(int user)335     public synchronized void onUserRemoved(int user) {
336         if (user < 0) {
337             loge("Invalid user in onUserRemoved: " + user);
338             return;
339         }
340         mUsers.remove(user);
341 
342         Set<Integer> users = new HashSet<>();
343         users.add(user);
344         update(users, mApps, false);
345     }
346 
347     @VisibleForTesting
highestPermissionForUid(Boolean currentPermission, String name)348     protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
349         if (currentPermission == SYSTEM) {
350             return currentPermission;
351         }
352         try {
353             final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
354             final boolean isNetwork = hasNetworkPermission(app);
355             final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
356             if (isNetwork || hasRestrictedPermission) {
357                 currentPermission = hasRestrictedPermission;
358             }
359         } catch (NameNotFoundException e) {
360             // App not found.
361             loge("NameNotFoundException " + name);
362         }
363         return currentPermission;
364     }
365 
366     /**
367      * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
368      *
369      * @param packageName The name of the new package.
370      * @param uid The uid of the new package.
371      *
372      * @hide
373      */
onPackageAdded(String packageName, int uid)374     public synchronized void onPackageAdded(String packageName, int uid) {
375         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
376         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
377         final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
378         if (permission != mApps.get(uid)) {
379             mApps.put(uid, permission);
380 
381             Map<Integer, Boolean> apps = new HashMap<>();
382             apps.put(uid, permission);
383             update(mUsers, apps, true);
384         }
385 
386         // If the newly-installed package falls within some VPN's uid range, update Netd with it.
387         // This needs to happen after the mApps update above, since removeBypassingUids() depends
388         // on mApps to check if the package can bypass VPN.
389         for (Map.Entry<String, Set<UidRange>> vpn : mVpnUidRanges.entrySet()) {
390             if (UidRange.containsUid(vpn.getValue(), uid)) {
391                 final Set<Integer> changedUids = new HashSet<>();
392                 changedUids.add(uid);
393                 removeBypassingUids(changedUids, /* vpnAppUid */ -1);
394                 updateVpnUids(vpn.getKey(), changedUids, true);
395             }
396         }
397         mAllApps.add(UserHandle.getAppId(uid));
398     }
399 
400     /**
401      * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
402      *
403      * @param uid containing the integer uid previously assigned to the package.
404      *
405      * @hide
406      */
onPackageRemoved(int uid)407     public synchronized void onPackageRemoved(int uid) {
408         // If the newly-removed package falls within some VPN's uid range, update Netd with it.
409         // This needs to happen before the mApps update below, since removeBypassingUids() depends
410         // on mApps to check if the package can bypass VPN.
411         for (Map.Entry<String, Set<UidRange>> vpn : mVpnUidRanges.entrySet()) {
412             if (UidRange.containsUid(vpn.getValue(), uid)) {
413                 final Set<Integer> changedUids = new HashSet<>();
414                 changedUids.add(uid);
415                 removeBypassingUids(changedUids, /* vpnAppUid */ -1);
416                 updateVpnUids(vpn.getKey(), changedUids, false);
417             }
418         }
419         // If the package has been removed from all users on the device, clear it form mAllApps.
420         if (mPackageManager.getNameForUid(uid) == null) {
421             mAllApps.remove(UserHandle.getAppId(uid));
422         }
423 
424         Map<Integer, Boolean> apps = new HashMap<>();
425         Boolean permission = null;
426         String[] packages = mPackageManager.getPackagesForUid(uid);
427         if (packages != null && packages.length > 0) {
428             for (String name : packages) {
429                 permission = highestPermissionForUid(permission, name);
430                 if (permission == SYSTEM) {
431                     // An app with this UID still has the SYSTEM permission.
432                     // Therefore, this UID must already have the SYSTEM permission.
433                     // Nothing to do.
434                     return;
435                 }
436             }
437         }
438         if (permission == mApps.get(uid)) {
439             // The permissions of this UID have not changed. Nothing to do.
440             return;
441         } else if (permission != null) {
442             mApps.put(uid, permission);
443             apps.put(uid, permission);
444             update(mUsers, apps, true);
445         } else {
446             mApps.remove(uid);
447             apps.put(uid, NETWORK);  // doesn't matter which permission we pick here
448             update(mUsers, apps, false);
449         }
450     }
451 
getNetdPermissionMask(String[] requestedPermissions, int[] requestedPermissionsFlags)452     private static int getNetdPermissionMask(String[] requestedPermissions,
453                                              int[] requestedPermissionsFlags) {
454         int permissions = 0;
455         if (requestedPermissions == null || requestedPermissionsFlags == null) return permissions;
456         for (int i = 0; i < requestedPermissions.length; i++) {
457             if (requestedPermissions[i].equals(INTERNET)
458                     && ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
459                 permissions |= INetd.PERMISSION_INTERNET;
460             }
461             if (requestedPermissions[i].equals(UPDATE_DEVICE_STATS)
462                     && ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
463                 permissions |= INetd.PERMISSION_UPDATE_DEVICE_STATS;
464             }
465         }
466         return permissions;
467     }
468 
getPackageInfo(String packageName)469     private PackageInfo getPackageInfo(String packageName) {
470         try {
471             PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS
472                     | MATCH_ANY_USER);
473             return app;
474         } catch (NameNotFoundException e) {
475             return null;
476         }
477     }
478 
479     /**
480      * Called when a new set of UID ranges are added to an active VPN network
481      *
482      * @param iface The active VPN network's interface name
483      * @param rangesToAdd The new UID ranges to be added to the network
484      * @param vpnAppUid The uid of the VPN app
485      */
onVpnUidRangesAdded(@onNull String iface, Set<UidRange> rangesToAdd, int vpnAppUid)486     public synchronized void onVpnUidRangesAdded(@NonNull String iface, Set<UidRange> rangesToAdd,
487             int vpnAppUid) {
488         // Calculate the list of new app uids under the VPN due to the new UID ranges and update
489         // Netd about them. Because mAllApps only contains appIds instead of uids, the result might
490         // be an overestimation if an app is not installed on the user on which the VPN is running,
491         // but that's safe.
492         final Set<Integer> changedUids = intersectUids(rangesToAdd, mAllApps);
493         removeBypassingUids(changedUids, vpnAppUid);
494         updateVpnUids(iface, changedUids, true);
495         if (mVpnUidRanges.containsKey(iface)) {
496             mVpnUidRanges.get(iface).addAll(rangesToAdd);
497         } else {
498             mVpnUidRanges.put(iface, new HashSet<UidRange>(rangesToAdd));
499         }
500     }
501 
502     /**
503      * Called when a set of UID ranges are removed from an active VPN network
504      *
505      * @param iface The VPN network's interface name
506      * @param rangesToRemove Existing UID ranges to be removed from the VPN network
507      * @param vpnAppUid The uid of the VPN app
508      */
onVpnUidRangesRemoved(@onNull String iface, Set<UidRange> rangesToRemove, int vpnAppUid)509     public synchronized void onVpnUidRangesRemoved(@NonNull String iface,
510             Set<UidRange> rangesToRemove, int vpnAppUid) {
511         // Calculate the list of app uids that are no longer under the VPN due to the removed UID
512         // ranges and update Netd about them.
513         final Set<Integer> changedUids = intersectUids(rangesToRemove, mAllApps);
514         removeBypassingUids(changedUids, vpnAppUid);
515         updateVpnUids(iface, changedUids, false);
516         Set<UidRange> existingRanges = mVpnUidRanges.getOrDefault(iface, null);
517         if (existingRanges == null) {
518             loge("Attempt to remove unknown vpn uid Range iface = " + iface);
519             return;
520         }
521         existingRanges.removeAll(rangesToRemove);
522         if (existingRanges.size() == 0) {
523             mVpnUidRanges.remove(iface);
524         }
525     }
526 
527     /**
528      * Compute the intersection of a set of UidRanges and appIds. Returns a set of uids
529      * that satisfies:
530      *   1. falls into one of the UidRange
531      *   2. matches one of the appIds
532      */
intersectUids(Set<UidRange> ranges, Set<Integer> appIds)533     private Set<Integer> intersectUids(Set<UidRange> ranges, Set<Integer> appIds) {
534         Set<Integer> result = new HashSet<>();
535         for (UidRange range : ranges) {
536             for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) {
537                 for (int appId : appIds) {
538                     final int uid = UserHandle.getUid(userId, appId);
539                     if (range.contains(uid)) {
540                         result.add(uid);
541                     }
542                 }
543             }
544         }
545         return result;
546     }
547 
548     /**
549      * Remove all apps which can elect to bypass the VPN from the list of uids
550      *
551      * An app can elect to bypass the VPN if it hold SYSTEM permission, or if its the active VPN
552      * app itself.
553      *
554      * @param uids The list of uids to operate on
555      * @param vpnAppUid The uid of the VPN app
556      */
removeBypassingUids(Set<Integer> uids, int vpnAppUid)557     private void removeBypassingUids(Set<Integer> uids, int vpnAppUid) {
558         uids.remove(vpnAppUid);
559         uids.removeIf(uid -> mApps.getOrDefault(uid, NETWORK) == SYSTEM);
560     }
561 
562     /**
563      * Update netd about the list of uids that are under an active VPN connection which they cannot
564      * bypass.
565      *
566      * This is to instruct netd to set up appropriate filtering rules for these uids, such that they
567      * can only receive ingress packets from the VPN's tunnel interface (and loopback).
568      *
569      * @param iface the interface name of the active VPN connection
570      * @param add {@code true} if the uids are to be added to the interface, {@code false} if they
571      *        are to be removed from the interface.
572      */
updateVpnUids(String iface, Set<Integer> uids, boolean add)573     private void updateVpnUids(String iface, Set<Integer> uids, boolean add) {
574         if (uids.size() == 0) {
575             return;
576         }
577         try {
578             if (add) {
579                 mNetd.firewallAddUidInterfaceRules(iface, toIntArray(uids));
580             } else {
581                 mNetd.firewallRemoveUidInterfaceRules(toIntArray(uids));
582             }
583         } catch (ServiceSpecificException e) {
584             // Silently ignore exception when device does not support eBPF, otherwise just log
585             // the exception and do not crash
586             if (e.errorCode != OsConstants.EOPNOTSUPP) {
587                 loge("Exception when updating permissions: ", e);
588             }
589         } catch (RemoteException e) {
590             loge("Exception when updating permissions: ", e);
591         }
592     }
593 
594     /**
595      * Called by PackageListObserver when a package is installed/uninstalled. Send the updated
596      * permission information to netd.
597      *
598      * @param uid the app uid of the package installed
599      * @param permissions the permissions the app requested and netd cares about.
600      *
601      * @hide
602      */
603     @VisibleForTesting
sendPackagePermissionsForUid(int uid, int permissions)604     void sendPackagePermissionsForUid(int uid, int permissions) {
605         SparseIntArray netdPermissionsAppIds = new SparseIntArray();
606         netdPermissionsAppIds.put(uid, permissions);
607         sendPackagePermissionsToNetd(netdPermissionsAppIds);
608     }
609 
610     /**
611      * Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET
612      * and/or UPDATE_DEVICE_STATS permission of the uids in array.
613      *
614      * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the
615      * permission is 0, revoke all permissions of that uid.
616      *
617      * @hide
618      */
619     @VisibleForTesting
sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds)620     void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
621         if (mNetd == null) {
622             Log.e(TAG, "Failed to get the netd service");
623             return;
624         }
625         ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
626         ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
627         ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
628         ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
629         ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
630         for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
631             int permissions = netdPermissionsAppIds.valueAt(i);
632             switch(permissions) {
633                 case (INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS):
634                     allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
635                     break;
636                 case INetd.PERMISSION_INTERNET:
637                     internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
638                     break;
639                 case INetd.PERMISSION_UPDATE_DEVICE_STATS:
640                     updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
641                     break;
642                 case INetd.PERMISSION_NONE:
643                     noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
644                     break;
645                 case INetd.PERMISSION_UNINSTALLED:
646                     uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
647                 default:
648                     Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
649                             + netdPermissionsAppIds.keyAt(i));
650             }
651         }
652         try {
653             // TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
654             if (allPermissionAppIds.size() != 0) {
655                 mNetd.trafficSetNetPermForUids(
656                         INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
657                         ArrayUtils.convertToIntArray(allPermissionAppIds));
658             }
659             if (internetPermissionAppIds.size() != 0) {
660                 mNetd.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
661                         ArrayUtils.convertToIntArray(internetPermissionAppIds));
662             }
663             if (updateStatsPermissionAppIds.size() != 0) {
664                 mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
665                         ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
666             }
667             if (noPermissionAppIds.size() != 0) {
668                 mNetd.trafficSetNetPermForUids(INetd.PERMISSION_NONE,
669                         ArrayUtils.convertToIntArray(noPermissionAppIds));
670             }
671             if (uninstalledAppIds.size() != 0) {
672                 mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UNINSTALLED,
673                         ArrayUtils.convertToIntArray(uninstalledAppIds));
674             }
675         } catch (RemoteException e) {
676             Log.e(TAG, "Pass appId list of special permission failed." + e);
677         }
678     }
679 
680     /** Should only be used by unit tests */
681     @VisibleForTesting
getVpnUidRanges(String iface)682     public Set<UidRange> getVpnUidRanges(String iface) {
683         return mVpnUidRanges.get(iface);
684     }
685 
686     /** Dump info to dumpsys */
dump(IndentingPrintWriter pw)687     public void dump(IndentingPrintWriter pw) {
688         pw.println("Interface filtering rules:");
689         pw.increaseIndent();
690         for (Map.Entry<String, Set<UidRange>> vpn : mVpnUidRanges.entrySet()) {
691             pw.println("Interface: " + vpn.getKey());
692             pw.println("UIDs: " + vpn.getValue().toString());
693             pw.println();
694         }
695         pw.decreaseIndent();
696     }
697 
log(String s)698     private static void log(String s) {
699         if (DBG) {
700             Log.d(TAG, s);
701         }
702     }
703 
loge(String s)704     private static void loge(String s) {
705         Log.e(TAG, s);
706     }
707 
loge(String s, Throwable e)708     private static void loge(String s, Throwable e) {
709         Log.e(TAG, s, e);
710     }
711 }
712