Home | History | Annotate | Download | only in util
      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