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