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