1 /*
2  * Copyright (C) 2016 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.wifi.util;
18 
19 import android.Manifest;
20 import android.app.AppOpsManager;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.content.pm.UserInfo;
24 import android.location.LocationManager;
25 import android.net.NetworkStack;
26 import android.os.Binder;
27 import android.os.Build;
28 import android.os.RemoteException;
29 import android.os.UserHandle;
30 import android.os.UserManager;
31 import android.util.Slog;
32 
33 import com.android.internal.annotations.GuardedBy;
34 import com.android.server.wifi.WifiInjector;
35 import com.android.server.wifi.WifiLog;
36 
37 import java.util.List;
38 
39 /**
40  * A wifi permissions utility assessing permissions
41  * for getting scan results by a package.
42  */
43 public class WifiPermissionsUtil {
44     private static final String TAG = "WifiPermissionsUtil";
45     private final WifiPermissionsWrapper mWifiPermissionsWrapper;
46     private final Context mContext;
47     private final AppOpsManager mAppOps;
48     private final UserManager mUserManager;
49     private final Object mLock = new Object();
50     @GuardedBy("mLock")
51     private LocationManager mLocationManager;
52     private WifiLog mLog;
53 
WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper, Context context, UserManager userManager, WifiInjector wifiInjector)54     public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper,
55             Context context, UserManager userManager, WifiInjector wifiInjector) {
56         mWifiPermissionsWrapper = wifiPermissionsWrapper;
57         mContext = context;
58         mUserManager = userManager;
59         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
60         mLog = wifiInjector.makeLog(TAG);
61     }
62 
63     /**
64      * Checks if the app has the permission to override Wi-Fi network configuration or not.
65      *
66      * @param uid uid of the app.
67      * @return true if the app does have the permission, false otherwise.
68      */
checkConfigOverridePermission(int uid)69     public boolean checkConfigOverridePermission(int uid) {
70         try {
71             int permission = mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid);
72             return (permission == PackageManager.PERMISSION_GRANTED);
73         } catch (RemoteException e) {
74             mLog.err("Error checking for permission: %").r(e.getMessage()).flush();
75             return false;
76         }
77     }
78 
79     /**
80      * Checks if the app has the permission to change Wi-Fi network configuration or not.
81      *
82      * @param uid uid of the app.
83      * @return true if the app does have the permission, false otherwise.
84      */
checkChangePermission(int uid)85     public boolean checkChangePermission(int uid) {
86         try {
87             int permission = mWifiPermissionsWrapper.getChangeWifiConfigPermission(uid);
88             return (permission == PackageManager.PERMISSION_GRANTED);
89         } catch (RemoteException e) {
90             mLog.err("Error checking for permission: %").r(e.getMessage()).flush();
91             return false;
92         }
93     }
94 
95     /**
96      * Checks if the app has the permission to access Wi-Fi state or not.
97      *
98      * @param uid uid of the app.
99      * @return true if the app does have the permission, false otherwise.
100      */
checkWifiAccessPermission(int uid)101     public boolean checkWifiAccessPermission(int uid) {
102         try {
103             int permission = mWifiPermissionsWrapper.getAccessWifiStatePermission(uid);
104             return (permission == PackageManager.PERMISSION_GRANTED);
105         } catch (RemoteException e) {
106             mLog.err("Error checking for permission: %").r(e.getMessage()).flush();
107             return false;
108         }
109     }
110 
111     /**
112      * Check and enforce Coarse or Fine Location permission (depending on target SDK).
113      *
114      * @param pkgName PackageName of the application requesting access
115      * @param uid The uid of the package
116      */
enforceLocationPermission(String pkgName, int uid)117     public void enforceLocationPermission(String pkgName, int uid) {
118         if (!checkCallersLocationPermission(pkgName, uid, /* coarseForTargetSdkLessThanQ */ true)) {
119             throw new SecurityException(
120                     "UID " + uid + " does not have Coarse/Fine Location permission");
121         }
122     }
123 
124     /**
125      * Checks whether than the target SDK of the package is less than the specified version code.
126      */
isTargetSdkLessThan(String packageName, int versionCode, int callingUid)127     public boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
128         long ident = Binder.clearCallingIdentity();
129         try {
130             if (mContext.getPackageManager().getApplicationInfoAsUser(
131                     packageName, 0, UserHandle.getUserId(callingUid)).targetSdkVersion
132                     < versionCode) {
133                 return true;
134             }
135         } catch (PackageManager.NameNotFoundException e) {
136             // In case of exception, assume unknown app (more strict checking)
137             // Note: This case will never happen since checkPackage is
138             // called to verify validity before checking App's version.
139         } finally {
140             Binder.restoreCallingIdentity(ident);
141         }
142         return false;
143     }
144 
145     /**
146      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
147      * android.Manifest.permission.ACCESS_FINE_LOCATION (depending on config/targetSDK leve)
148      * and a corresponding app op is allowed for this package and uid.
149      *
150      * @param pkgName PackageName of the application requesting access
151      * @param uid The uid of the package
152      * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
153      *                                    else (false or targetSDK >= Q) then will check for FINE
154      */
checkCallersLocationPermission(String pkgName, int uid, boolean coarseForTargetSdkLessThanQ)155     public boolean checkCallersLocationPermission(String pkgName, int uid,
156             boolean coarseForTargetSdkLessThanQ) {
157         boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
158 
159         String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
160         if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
161             // Having FINE permission implies having COARSE permission (but not the reverse)
162             permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
163         }
164         if (mWifiPermissionsWrapper.getUidPermission(permissionType, uid)
165                 == PackageManager.PERMISSION_DENIED) {
166             return false;
167         }
168 
169         // Always checking FINE - even if will not enforce. This will record the request for FINE
170         // so that a location request by the app is surfaced to the user.
171         boolean isAppOpAllowed = noteAppOpAllowed(AppOpsManager.OP_FINE_LOCATION, pkgName, uid);
172         if (!isAppOpAllowed && coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
173             isAppOpAllowed = noteAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid);
174         }
175         return isAppOpAllowed;
176     }
177 
178     /**
179      * Check and enforce Fine Location permission.
180      *
181      * @param pkgName PackageName of the application requesting access
182      * @param uid The uid of the package
183      */
enforceFineLocationPermission(String pkgName, int uid)184     public void enforceFineLocationPermission(String pkgName, int uid) {
185         if (!checkCallersFineLocationPermission(pkgName, uid, false)) {
186             throw new SecurityException("UID " + uid + " does not have Fine Location permission");
187         }
188     }
189 
190 
191     /**
192      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION
193      * and a corresponding app op is allowed for this package and uid.
194      *
195      * @param pkgName PackageName of the application requesting access
196      * @param uid The uid of the package
197      * @param hideFromAppOps True to invoke {@link AppOpsManager#checkOp(int, int, String)}, false
198      *                       to invoke {@link AppOpsManager#noteOp(int, int, String)}.
199      */
checkCallersFineLocationPermission(String pkgName, int uid, boolean hideFromAppOps)200     private boolean checkCallersFineLocationPermission(String pkgName, int uid,
201                                                        boolean hideFromAppOps) {
202         // Having FINE permission implies having COARSE permission (but not the reverse)
203         if (mWifiPermissionsWrapper.getUidPermission(
204                 Manifest.permission.ACCESS_FINE_LOCATION, uid)
205                 == PackageManager.PERMISSION_DENIED) {
206             return false;
207         }
208         if (hideFromAppOps) {
209             // Don't note the operation, just check if the app is allowed to perform the operation.
210             if (!checkAppOpAllowed(AppOpsManager.OP_FINE_LOCATION, pkgName, uid)) {
211                 return false;
212             }
213         } else {
214             if (!noteAppOpAllowed(AppOpsManager.OP_FINE_LOCATION, pkgName, uid)) {
215                 return false;
216             }
217         }
218         return true;
219     }
220 
221     /**
222      * Checks that calling process has android.Manifest.permission.LOCATION_HARDWARE.
223      *
224      * @param uid The uid of the package
225      */
checkCallersHardwareLocationPermission(int uid)226     private boolean checkCallersHardwareLocationPermission(int uid) {
227         return mWifiPermissionsWrapper.getUidPermission(Manifest.permission.LOCATION_HARDWARE, uid)
228                 == PackageManager.PERMISSION_GRANTED;
229     }
230 
231     /**
232      * API to determine if the caller has permissions to get scan results. Throws SecurityException
233      * if the caller has no permission.
234      * @param pkgName package name of the application requesting access
235      * @param uid The uid of the package
236      */
enforceCanAccessScanResults(String pkgName, int uid)237     public void enforceCanAccessScanResults(String pkgName, int uid) throws SecurityException {
238         checkPackage(uid, pkgName);
239 
240         // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_MANAGED_PROVISIONING,
241         // NETWORK_STACK & MAINLINE_NETWORK_STACK are granted a bypass.
242         if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
243                 || checkNetworkManagedProvisioningPermission(uid)
244                 || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) {
245             return;
246         }
247 
248         // Location mode must be enabled
249         if (!isLocationModeEnabled()) {
250             // Location mode is disabled, scan results cannot be returned
251             throw new SecurityException("Location mode is disabled for the device");
252         }
253 
254         // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS permission.
255         boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid);
256         // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
257         // location information.
258         boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName,
259                 uid, /* coarseForTargetSdkLessThanQ */ true);
260 
261         // If neither caller or app has location access, there is no need to check
262         // any other permissions. Deny access to scan results.
263         if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
264             throw new SecurityException("UID " + uid + " has no location permission");
265         }
266         // Check if Wifi Scan request is an operation allowed for this App.
267         if (!isScanAllowedbyApps(pkgName, uid)) {
268             throw new SecurityException("UID " + uid + " has no wifi scan permission");
269         }
270         // If the User or profile is current, permission is granted
271         // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
272         if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
273             throw new SecurityException("UID " + uid + " profile not permitted");
274         }
275     }
276 
277     /**
278      * API to determine if the caller has permissions to get scan results. Throws SecurityException
279      * if the caller has no permission.
280      * @param pkgName package name of the application requesting access
281      * @param uid The uid of the package
282      * @param ignoreLocationSettings Whether this request can bypass location settings.
283      * @param hideFromAppOps Whether to note the request in app-ops logging or not.
284      *
285      * Note: This is to be used for checking permissions in the internal WifiScanner API surface
286      * for requests coming from system apps.
287      */
enforceCanAccessScanResultsForWifiScanner( String pkgName, int uid, boolean ignoreLocationSettings, boolean hideFromAppOps)288     public void enforceCanAccessScanResultsForWifiScanner(
289             String pkgName, int uid, boolean ignoreLocationSettings, boolean hideFromAppOps)
290             throws SecurityException {
291         checkPackage(uid, pkgName);
292 
293         // Location mode must be enabled
294         if (!isLocationModeEnabled()) {
295             if (ignoreLocationSettings) {
296                 mLog.w("Request from " + pkgName + " violated location settings");
297             } else {
298                 // Location mode is disabled, scan results cannot be returned
299                 throw new SecurityException("Location mode is disabled for the device");
300             }
301         }
302         // LocationAccess by App: caller must have fine & hardware Location permission to have
303         // access to location information.
304         if (!checkCallersFineLocationPermission(pkgName, uid, hideFromAppOps)
305                 || !checkCallersHardwareLocationPermission(uid)) {
306             throw new SecurityException("UID " + uid + " has no location permission");
307         }
308         // Check if Wifi Scan request is an operation allowed for this App.
309         if (!isScanAllowedbyApps(pkgName, uid)) {
310             throw new SecurityException("UID " + uid + " has no wifi scan permission");
311         }
312     }
313 
314     /**
315      *
316      * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION
317      * and a corresponding app op is allowed for this package and uid
318      *
319      * @param pkgName package name of the application requesting access
320      * @param uid The uid of the package
321      * @param needLocationModeEnabled indicates location mode must be enabled.
322      *
323      * @return true if caller has permission, false otherwise
324      */
checkCanAccessWifiDirect(String pkgName, int uid, boolean needLocationModeEnabled)325     public boolean checkCanAccessWifiDirect(String pkgName, int uid,
326                                             boolean needLocationModeEnabled) {
327         try {
328             checkPackage(uid, pkgName);
329         } catch (SecurityException se) {
330             Slog.e(TAG, "Package check exception - " + se);
331             return false;
332         }
333 
334         // Apps with NETWORK_SETTINGS are granted a bypass.
335         if (checkNetworkSettingsPermission(uid)) {
336             return true;
337         }
338 
339         // Location mode must be enabled if needed.
340         if (needLocationModeEnabled && !isLocationModeEnabled()) {
341             Slog.e(TAG, "Location mode is disabled for the device");
342             return false;
343         }
344 
345         // LocationAccess by App: caller must have Fine Location permission to have access to
346         // location information.
347         if (!checkCallersLocationPermission(pkgName, uid,
348                 /* coarseForTargetSdkLessThanQ */ false)) {
349             Slog.e(TAG, "UID " + uid + " has no location permission");
350             return false;
351         }
352         return true;
353     }
354 
355     /**
356      * API to check to validate if a package name belongs to a UID. Throws SecurityException
357      * if pkgName does not belongs to a UID
358      *
359      * @param pkgName package name of the application requesting access
360      * @param uid The uid of the package
361      *
362      */
checkPackage(int uid, String pkgName)363     public void checkPackage(int uid, String pkgName) throws SecurityException {
364         if (pkgName == null) {
365             throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
366         }
367         mAppOps.checkPackage(uid, pkgName);
368     }
369 
370     /**
371      * Returns true if the caller holds PEERS_MAC_ADDRESS permission.
372      */
checkCallerHasPeersMacAddressPermission(int uid)373     private boolean checkCallerHasPeersMacAddressPermission(int uid) {
374         return mWifiPermissionsWrapper.getUidPermission(
375                 android.Manifest.permission.PEERS_MAC_ADDRESS, uid)
376                 == PackageManager.PERMISSION_GRANTED;
377     }
378 
379     /**
380      * Returns true if Wifi scan operation is allowed for this caller
381      * and package.
382      */
isScanAllowedbyApps(String pkgName, int uid)383     private boolean isScanAllowedbyApps(String pkgName, int uid) {
384         return noteAppOpAllowed(AppOpsManager.OP_WIFI_SCAN, pkgName, uid);
385     }
386 
387     /**
388      * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
389      */
checkInteractAcrossUsersFull(int uid)390     private boolean checkInteractAcrossUsersFull(int uid) {
391         return mWifiPermissionsWrapper.getUidPermission(
392                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
393                 == PackageManager.PERMISSION_GRANTED;
394     }
395 
396     /**
397      * Returns true if the calling user is the current one or a profile of the
398      * current user.
399      */
isCurrentProfile(int uid)400     private boolean isCurrentProfile(int uid) {
401         int currentUser = mWifiPermissionsWrapper.getCurrentUser();
402         int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid);
403         if (callingUserId == currentUser) {
404             return true;
405         } else {
406             List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser);
407             for (UserInfo user : userProfiles) {
408                 if (user.id == callingUserId) {
409                     return true;
410                 }
411             }
412         }
413         return false;
414     }
415 
noteAppOpAllowed(int op, String pkgName, int uid)416     private boolean noteAppOpAllowed(int op, String pkgName, int uid) {
417         return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED;
418     }
419 
checkAppOpAllowed(int op, String pkgName, int uid)420     private boolean checkAppOpAllowed(int op, String pkgName, int uid) {
421         return mAppOps.checkOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED;
422     }
423 
retrieveLocationManagerIfNecessary()424     private boolean retrieveLocationManagerIfNecessary() {
425         // This is going to be accessed by multiple threads.
426         synchronized (mLock) {
427             if (mLocationManager == null) {
428                 mLocationManager =
429                         (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
430             }
431         }
432         return mLocationManager != null;
433     }
434 
435     /**
436      * Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
437      */
isLocationModeEnabled()438     public boolean isLocationModeEnabled() {
439         if (!retrieveLocationManagerIfNecessary()) return false;
440         return mLocationManager.isLocationEnabledForUser(UserHandle.of(
441                 mWifiPermissionsWrapper.getCurrentUser()));
442     }
443 
444     /**
445      * Returns true if the |uid| holds NETWORK_SETTINGS permission.
446      */
checkNetworkSettingsPermission(int uid)447     public boolean checkNetworkSettingsPermission(int uid) {
448         return mWifiPermissionsWrapper.getUidPermission(
449                 android.Manifest.permission.NETWORK_SETTINGS, uid)
450                 == PackageManager.PERMISSION_GRANTED;
451     }
452 
453     /**
454      * Returns true if the |uid| holds LOCAL_MAC_ADDRESS permission.
455      */
checkLocalMacAddressPermission(int uid)456     public boolean checkLocalMacAddressPermission(int uid) {
457         return mWifiPermissionsWrapper.getUidPermission(
458                 android.Manifest.permission.LOCAL_MAC_ADDRESS, uid)
459                 == PackageManager.PERMISSION_GRANTED;
460     }
461 
462     /**
463      * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
464      */
checkNetworkSetupWizardPermission(int uid)465     public boolean checkNetworkSetupWizardPermission(int uid) {
466         return mWifiPermissionsWrapper.getUidPermission(
467                 android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
468                 == PackageManager.PERMISSION_GRANTED;
469     }
470 
471     /**
472      * Returns true if the |uid| holds NETWORK_STACK permission.
473      */
checkNetworkStackPermission(int uid)474     public boolean checkNetworkStackPermission(int uid) {
475         return mWifiPermissionsWrapper.getUidPermission(
476                 android.Manifest.permission.NETWORK_STACK, uid)
477                 == PackageManager.PERMISSION_GRANTED;
478     }
479 
480     /**
481      * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
482      */
checkMainlineNetworkStackPermission(int uid)483     public boolean checkMainlineNetworkStackPermission(int uid) {
484         return mWifiPermissionsWrapper.getUidPermission(
485                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
486                 == PackageManager.PERMISSION_GRANTED;
487     }
488 
489     /**
490      * Returns true if the |uid| holds NETWORK_MANAGED_PROVISIONING permission.
491      */
checkNetworkManagedProvisioningPermission(int uid)492     public boolean checkNetworkManagedProvisioningPermission(int uid) {
493         return mWifiPermissionsWrapper.getUidPermission(
494                 android.Manifest.permission.NETWORK_MANAGED_PROVISIONING, uid)
495                 == PackageManager.PERMISSION_GRANTED;
496     }
497 
498     /**
499      * Returns true if the |uid| holds NETWORK_CARRIER_PROVISIONING permission.
500      */
checkNetworkCarrierProvisioningPermission(int uid)501     public boolean checkNetworkCarrierProvisioningPermission(int uid) {
502         return mWifiPermissionsWrapper.getUidPermission(
503                 android.Manifest.permission.NETWORK_CARRIER_PROVISIONING, uid)
504                 == PackageManager.PERMISSION_GRANTED;
505     }
506 
507     /**
508      * Returns true if the |callingUid|/\callingPackage| holds SYSTEM_ALERT_WINDOW permission.
509      */
checkSystemAlertWindowPermission(int callingUid, String callingPackage)510     public boolean checkSystemAlertWindowPermission(int callingUid, String callingPackage) {
511         final int mode = mAppOps.noteOp(
512                 AppOpsManager.OP_SYSTEM_ALERT_WINDOW, callingUid, callingPackage);
513         if (mode == AppOpsManager.MODE_DEFAULT) {
514             return mWifiPermissionsWrapper.getUidPermission(
515                     Manifest.permission.SYSTEM_ALERT_WINDOW, callingUid)
516                     == PackageManager.PERMISSION_GRANTED;
517         }
518         return mode == AppOpsManager.MODE_ALLOWED;
519     }
520 }
521