• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.net.ConnectivityManager;
25 import android.net.NetworkScoreManager;
26 import android.os.RemoteException;
27 import android.os.UserManager;
28 import android.provider.Settings;
29 
30 import com.android.server.wifi.WifiInjector;
31 import com.android.server.wifi.WifiLog;
32 import com.android.server.wifi.WifiSettingsStore;
33 
34 import java.util.List;
35 
36 /**
37  * A wifi permissions utility assessing permissions
38  * for getting scan results by a package.
39  */
40 public 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 
WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper, Context context, WifiSettingsStore settingsStore, UserManager userManager, NetworkScoreManager networkScoreManager, WifiInjector wifiInjector)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      */
checkConfigOverridePermission(int uid)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      */
enforceTetherChangePermission(Context context)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      */
enforceLocationPermission(String pkgName, int uid)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      */
canAccessScanResults(String pkgName, int uid, int minVersion)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      */
checkCallerHasPeersMacAddressPermission(int uid)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      */
isCallerActiveNwScorer(int uid)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      */
isScanAllowedbyApps(String pkgName, int uid)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      */
checkInteractAcrossUsersFull(int uid)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      */
isCurrentProfile(int uid)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      */
isLegacyVersion(String pkgName, int minVersion)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 
checkAppOpAllowed(int op, String pkgName, int uid)211     private boolean checkAppOpAllowed(int op, String pkgName, int uid) {
212         return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED;
213     }
214 
isLegacyForeground(String pkgName, int version)215     private boolean isLegacyForeground(String pkgName, int version) {
216         return isLegacyVersion(pkgName, version) && isForegroundApp(pkgName);
217     }
218 
isForegroundApp(String pkgName)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      */
checkCallersLocationPermission(String pkgName, int uid)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     }
isLocationModeEnabled(String pkgName)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