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