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 17package com.android.server.wifi.util; 18 19import android.Manifest; 20import android.app.AppOpsManager; 21import android.content.Context; 22import android.content.pm.PackageManager; 23import android.content.pm.UserInfo; 24import android.net.ConnectivityManager; 25import android.net.NetworkScoreManager; 26import android.os.RemoteException; 27import android.os.UserManager; 28import android.provider.Settings; 29 30import com.android.server.wifi.WifiInjector; 31import com.android.server.wifi.WifiLog; 32import com.android.server.wifi.WifiSettingsStore; 33 34import java.util.List; 35 36/** 37 * A wifi permissions utility assessing permissions 38 * for getting scan results by a package. 39 */ 40public class WifiPermissionsUtil { 41 private static final String TAG = "WifiPermissionsUtil"; 42 private final WifiPermissionsWrapper mWifiPermissionsWrapper; 43 private final Context mContext; 44 private final AppOpsManager mAppOps; 45 private final UserManager mUserManager; 46 private final WifiSettingsStore mSettingsStore; 47 private final NetworkScoreManager mNetworkScoreManager; 48 private WifiLog mLog; 49 50 public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper, 51 Context context, WifiSettingsStore settingsStore, UserManager userManager, 52 NetworkScoreManager networkScoreManager, WifiInjector wifiInjector) { 53 mWifiPermissionsWrapper = wifiPermissionsWrapper; 54 mContext = context; 55 mUserManager = userManager; 56 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 57 mSettingsStore = settingsStore; 58 mLog = wifiInjector.makeLog(TAG); 59 mNetworkScoreManager = networkScoreManager; 60 } 61 62 /** 63 * Checks if the app has the permission to override Wi-Fi network configuration or not. 64 * 65 * @param uid uid of the app. 66 * @return true if the app does have the permission, false otherwise. 67 */ 68 public boolean checkConfigOverridePermission(int uid) { 69 try { 70 int permission = mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid); 71 return (permission == PackageManager.PERMISSION_GRANTED); 72 } catch (RemoteException e) { 73 mLog.err("Error checking for permission: %").r(e.getMessage()).flush(); 74 return false; 75 } 76 } 77 78 /** 79 * Check and enforce tether change permission. 80 * 81 * @param context Context object of the caller. 82 */ 83 public void enforceTetherChangePermission(Context context) { 84 ConnectivityManager.enforceTetherChangePermission(context); 85 } 86 87 /** 88 * Check and enforce Location permission. 89 * 90 * @param pkgName PackageName of the application requesting access 91 * @param uid The uid of the package 92 */ 93 public void enforceLocationPermission(String pkgName, int uid) { 94 if (!checkCallersLocationPermission(pkgName, uid)) { 95 throw new SecurityException("UID " + uid + " does not have Location permission"); 96 } 97 } 98 99 /** 100 * API to determine if the caller has permissions to get 101 * scan results. 102 * @param pkgName Packagename of the application requesting access 103 * @param uid The uid of the package 104 * @param minVersion Minimum app API Version number to enforce location permission 105 * @return boolean true or false if permissions is granted 106 */ 107 public boolean canAccessScanResults(String pkgName, int uid, 108 int minVersion) throws SecurityException { 109 mAppOps.checkPackage(uid, pkgName); 110 // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS 111 // permission or is an Active Nw scorer. 112 boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid) 113 || isCallerActiveNwScorer(uid); 114 // LocationAccess by App: For AppVersion older than minVersion, 115 // it is sufficient to check if the App is foreground. 116 // Otherwise, Location Mode must be enabled and caller must have 117 // Coarse Location permission to have access to location information. 118 boolean canAppPackageUseLocation = isLegacyForeground(pkgName, minVersion) 119 || (isLocationModeEnabled(pkgName) 120 && checkCallersLocationPermission(pkgName, uid)); 121 // If neither caller or app has location access, there is no need to check 122 // any other permissions. Deny access to scan results. 123 if (!canCallingUidAccessLocation && !canAppPackageUseLocation) { 124 mLog.tC("Denied: no location permission"); 125 return false; 126 } 127 // Check if Wifi Scan request is an operation allowed for this App. 128 if (!isScanAllowedbyApps(pkgName, uid)) { 129 mLog.tC("Denied: app wifi scan not allowed"); 130 return false; 131 } 132 // If the User or profile is current, permission is granted 133 // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. 134 if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) { 135 mLog.tC("Denied: Profile not permitted"); 136 return false; 137 } 138 return true; 139 } 140 141 /** 142 * Returns true if the caller holds PEERS_MAC_ADDRESS permission. 143 */ 144 private boolean checkCallerHasPeersMacAddressPermission(int uid) { 145 return mWifiPermissionsWrapper.getUidPermission( 146 android.Manifest.permission.PEERS_MAC_ADDRESS, uid) 147 == PackageManager.PERMISSION_GRANTED; 148 } 149 150 /** 151 * Returns true if the caller is an Active Network Scorer. 152 */ 153 private boolean isCallerActiveNwScorer(int uid) { 154 return mNetworkScoreManager.isCallerActiveScorer(uid); 155 } 156 157 /** 158 * Returns true if Wifi scan operation is allowed for this caller 159 * and package. 160 */ 161 private boolean isScanAllowedbyApps(String pkgName, int uid) { 162 return checkAppOpAllowed(AppOpsManager.OP_WIFI_SCAN, pkgName, uid); 163 } 164 165 /** 166 * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL. 167 */ 168 private boolean checkInteractAcrossUsersFull(int uid) { 169 return mWifiPermissionsWrapper.getUidPermission( 170 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid) 171 == PackageManager.PERMISSION_GRANTED; 172 } 173 174 /** 175 * Returns true if the calling user is the current one or a profile of the 176 * current user. 177 */ 178 private boolean isCurrentProfile(int uid) { 179 int currentUser = mWifiPermissionsWrapper.getCurrentUser(); 180 int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid); 181 if (callingUserId == currentUser) { 182 return true; 183 } else { 184 List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser); 185 for (UserInfo user: userProfiles) { 186 if (user.id == callingUserId) { 187 return true; 188 } 189 } 190 } 191 return false; 192 } 193 194 /** 195 * Returns true if the App version is older than minVersion. 196 */ 197 private boolean isLegacyVersion(String pkgName, int minVersion) { 198 try { 199 if (mContext.getPackageManager().getApplicationInfo(pkgName, 0) 200 .targetSdkVersion < minVersion) { 201 return true; 202 } 203 } catch (PackageManager.NameNotFoundException e) { 204 // In case of exception, assume known app (more strict checking) 205 // Note: This case will never happen since checkPackage is 206 // called to verify valididity before checking App's version. 207 } 208 return false; 209 } 210 211 private boolean checkAppOpAllowed(int op, String pkgName, int uid) { 212 return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; 213 } 214 215 private boolean isLegacyForeground(String pkgName, int version) { 216 return isLegacyVersion(pkgName, version) && isForegroundApp(pkgName); 217 } 218 219 private boolean isForegroundApp(String pkgName) { 220 return pkgName.equals(mWifiPermissionsWrapper.getTopPkgName()); 221 } 222 223 /** 224 * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION 225 * and a corresponding app op is allowed for this package and uid. 226 */ 227 private boolean checkCallersLocationPermission(String pkgName, int uid) { 228 // Coarse Permission implies Fine permission 229 if ((mWifiPermissionsWrapper.getUidPermission( 230 Manifest.permission.ACCESS_COARSE_LOCATION, uid) 231 == PackageManager.PERMISSION_GRANTED) 232 && checkAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid)) { 233 return true; 234 } 235 return false; 236 } 237 private boolean isLocationModeEnabled(String pkgName) { 238 // Location mode check on applications that are later than version. 239 return (mSettingsStore.getLocationModeSetting(mContext) 240 != Settings.Secure.LOCATION_MODE_OFF); 241 } 242} 243