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.os.RemoteException; 25 import android.os.UserManager; 26 import android.provider.Settings; 27 28 import com.android.server.wifi.WifiInjector; 29 import com.android.server.wifi.WifiLog; 30 import com.android.server.wifi.WifiSettingsStore; 31 32 import java.util.List; 33 34 /** 35 * A wifi permissions utility assessing permissions 36 * for getting scan results by a package. 37 */ 38 public class WifiPermissionsUtil { 39 private static final String TAG = "WifiPermissionsUtil"; 40 private final WifiPermissionsWrapper mWifiPermissionsWrapper; 41 private final Context mContext; 42 private final AppOpsManager mAppOps; 43 private final UserManager mUserManager; 44 private final WifiSettingsStore mSettingsStore; 45 private WifiLog mLog; 46 47 public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper, 48 Context context, WifiSettingsStore settingsStore, UserManager userManager, 49 WifiInjector wifiInjector) { 50 mWifiPermissionsWrapper = wifiPermissionsWrapper; 51 mContext = context; 52 mUserManager = userManager; 53 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 54 mSettingsStore = settingsStore; 55 mLog = wifiInjector.makeLog(TAG); 56 } 57 58 /** 59 * Checks if the app has the permission to override Wi-Fi network configuration or not. 60 * 61 * @param uid uid of the app. 62 * @return true if the app does have the permission, false otherwise. 63 */ 64 public boolean checkConfigOverridePermission(int uid) { 65 try { 66 int permission = mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid); 67 return (permission == PackageManager.PERMISSION_GRANTED); 68 } catch (RemoteException e) { 69 mLog.err("Error checking for permission: %").r(e.getMessage()).flush(); 70 return false; 71 } 72 } 73 74 /** 75 * Checks if the app has the permission to change Wi-Fi network configuration or not. 76 * 77 * @param uid uid of the app. 78 * @return true if the app does have the permission, false otherwise. 79 */ 80 public boolean checkChangePermission(int uid) { 81 try { 82 int permission = mWifiPermissionsWrapper.getChangeWifiConfigPermission(uid); 83 return (permission == PackageManager.PERMISSION_GRANTED); 84 } catch (RemoteException e) { 85 mLog.err("Error checking for permission: %").r(e.getMessage()).flush(); 86 return false; 87 } 88 } 89 90 /** 91 * Checks if the app has the permission to access Wi-Fi state or not. 92 * 93 * @param uid uid of the app. 94 * @return true if the app does have the permission, false otherwise. 95 */ 96 public boolean checkWifiAccessPermission(int uid) { 97 try { 98 int permission = mWifiPermissionsWrapper.getAccessWifiStatePermission(uid); 99 return (permission == PackageManager.PERMISSION_GRANTED); 100 } catch (RemoteException e) { 101 mLog.err("Error checking for permission: %").r(e.getMessage()).flush(); 102 return false; 103 } 104 } 105 106 /** 107 * Check and enforce Coarse Location permission. 108 * 109 * @param pkgName PackageName of the application requesting access 110 * @param uid The uid of the package 111 */ 112 public void enforceLocationPermission(String pkgName, int uid) { 113 if (!checkCallersLocationPermission(pkgName, uid)) { 114 throw new SecurityException("UID " + uid + " does not have Coarse Location permission"); 115 } 116 } 117 118 119 /** 120 * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION 121 * and a corresponding app op is allowed for this package and uid. 122 * 123 * @param pkgName PackageName of the application requesting access 124 * @param uid The uid of the package 125 */ 126 public boolean checkCallersLocationPermission(String pkgName, int uid) { 127 // Having FINE permission implies having COARSE permission (but not the reverse) 128 if ((mWifiPermissionsWrapper.getUidPermission( 129 Manifest.permission.ACCESS_COARSE_LOCATION, uid) 130 == PackageManager.PERMISSION_GRANTED) 131 && checkAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid)) { 132 return true; 133 } 134 return false; 135 } 136 137 /** 138 * Check and enforce Fine Location permission. 139 * 140 * @param pkgName PackageName of the application requesting access 141 * @param uid The uid of the package 142 */ 143 public void enforceFineLocationPermission(String pkgName, int uid) { 144 if (!checkCallersFineLocationPermission(pkgName, uid)) { 145 throw new SecurityException("UID " + uid + " does not have Fine Location permission"); 146 } 147 } 148 149 150 /** 151 * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION 152 * and a corresponding app op is allowed for this package and uid. 153 * 154 * @param pkgName PackageName of the application requesting access 155 * @param uid The uid of the package 156 */ 157 public boolean checkCallersFineLocationPermission(String pkgName, int uid) { 158 // Having FINE permission implies having COARSE permission (but not the reverse) 159 if ((mWifiPermissionsWrapper.getUidPermission( 160 Manifest.permission.ACCESS_FINE_LOCATION, uid) 161 == PackageManager.PERMISSION_GRANTED) 162 && checkAppOpAllowed(AppOpsManager.OP_FINE_LOCATION, pkgName, uid)) { 163 return true; 164 } 165 return false; 166 } 167 168 /** 169 * API to determine if the caller has permissions to get scan results. Throws SecurityException 170 * if the caller has no permission. 171 * @param pkgName package name of the application requesting access 172 * @param uid The uid of the package 173 */ 174 public void enforceCanAccessScanResults(String pkgName, int uid) throws SecurityException { 175 mAppOps.checkPackage(uid, pkgName); 176 177 // Apps with NETWORK_SETTINGS & NETWORK_SETUP_WIZARD are granted a bypass. 178 if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)) { 179 return; 180 } 181 182 // Location mode must be enabled 183 if (!isLocationModeEnabled()) { 184 // Location mode is disabled, scan results cannot be returned 185 throw new SecurityException("Location mode is disabled for the device"); 186 } 187 188 // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS permission. 189 boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid); 190 // LocationAccess by App: caller must have 191 // Coarse Location permission to have access to location information. 192 boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, uid); 193 194 // If neither caller or app has location access, there is no need to check 195 // any other permissions. Deny access to scan results. 196 if (!canCallingUidAccessLocation && !canAppPackageUseLocation) { 197 throw new SecurityException("UID " + uid + " has no location permission"); 198 } 199 // Check if Wifi Scan request is an operation allowed for this App. 200 if (!isScanAllowedbyApps(pkgName, uid)) { 201 throw new SecurityException("UID " + uid + " has no wifi scan permission"); 202 } 203 // If the User or profile is current, permission is granted 204 // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. 205 if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) { 206 throw new SecurityException("UID " + uid + " profile not permitted"); 207 } 208 } 209 210 /** 211 * Returns true if the caller holds PEERS_MAC_ADDRESS permission. 212 */ 213 private boolean checkCallerHasPeersMacAddressPermission(int uid) { 214 return mWifiPermissionsWrapper.getUidPermission( 215 android.Manifest.permission.PEERS_MAC_ADDRESS, uid) 216 == PackageManager.PERMISSION_GRANTED; 217 } 218 219 /** 220 * Returns true if Wifi scan operation is allowed for this caller 221 * and package. 222 */ 223 private boolean isScanAllowedbyApps(String pkgName, int uid) { 224 return checkAppOpAllowed(AppOpsManager.OP_WIFI_SCAN, pkgName, uid); 225 } 226 227 /** 228 * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL. 229 */ 230 private boolean checkInteractAcrossUsersFull(int uid) { 231 return mWifiPermissionsWrapper.getUidPermission( 232 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid) 233 == PackageManager.PERMISSION_GRANTED; 234 } 235 236 /** 237 * Returns true if the calling user is the current one or a profile of the 238 * current user. 239 */ 240 private boolean isCurrentProfile(int uid) { 241 int currentUser = mWifiPermissionsWrapper.getCurrentUser(); 242 int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid); 243 if (callingUserId == currentUser) { 244 return true; 245 } else { 246 List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser); 247 for (UserInfo user : userProfiles) { 248 if (user.id == callingUserId) { 249 return true; 250 } 251 } 252 } 253 return false; 254 } 255 256 /** 257 * Returns true if the App version is older than minVersion. 258 */ 259 public boolean isLegacyVersion(String pkgName, int minVersion) { 260 try { 261 if (mContext.getPackageManager().getApplicationInfo(pkgName, 0) 262 .targetSdkVersion < minVersion) { 263 return true; 264 } 265 } catch (PackageManager.NameNotFoundException e) { 266 // In case of exception, assume known app (more strict checking) 267 // Note: This case will never happen since checkPackage is 268 // called to verify valididity before checking App's version. 269 } 270 return false; 271 } 272 273 private boolean checkAppOpAllowed(int op, String pkgName, int uid) { 274 return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; 275 } 276 277 private boolean isLocationModeEnabled() { 278 return (mSettingsStore.getLocationModeSetting(mContext) 279 != Settings.Secure.LOCATION_MODE_OFF); 280 } 281 282 /** 283 * Returns true if the |uid| holds NETWORK_SETTINGS permission. 284 */ 285 public boolean checkNetworkSettingsPermission(int uid) { 286 return mWifiPermissionsWrapper.getUidPermission( 287 android.Manifest.permission.NETWORK_SETTINGS, uid) 288 == PackageManager.PERMISSION_GRANTED; 289 } 290 291 /** 292 * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission. 293 */ 294 public boolean checkNetworkSetupWizardPermission(int uid) { 295 return mWifiPermissionsWrapper.getUidPermission( 296 android.Manifest.permission.NETWORK_SETUP_WIZARD, uid) 297 == PackageManager.PERMISSION_GRANTED; 298 } 299 } 300