1 /* 2 * Copyright (C) 2019 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.location; 18 19 import static android.Manifest.permission.ACCESS_COARSE_LOCATION; 20 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 21 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 22 23 import android.annotation.IntDef; 24 import android.annotation.Nullable; 25 import android.app.AppOpsManager; 26 import android.content.Context; 27 import android.os.Binder; 28 import android.os.UserHandle; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.util.ArrayUtils; 32 import com.android.internal.util.Preconditions; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.util.Objects; 37 38 /** 39 * Represents the calling process's uid, pid, and package name. 40 */ 41 public final class CallerIdentity { 42 43 public static final int PERMISSION_NONE = 0; 44 public static final int PERMISSION_COARSE = 1; 45 public static final int PERMISSION_FINE = 2; 46 47 @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE}) 48 @Retention(RetentionPolicy.SOURCE) 49 public @interface PermissionLevel {} 50 51 /** 52 * Converts the given permission level to the corresponding permission. 53 */ asPermission(@ermissionLevel int permissionLevel)54 public static String asPermission(@PermissionLevel int permissionLevel) { 55 switch (permissionLevel) { 56 case PERMISSION_COARSE: 57 return ACCESS_COARSE_LOCATION; 58 case PERMISSION_FINE: 59 return ACCESS_FINE_LOCATION; 60 default: 61 throw new IllegalArgumentException(); 62 } 63 } 64 65 /** 66 * Converts the given permission level to the corresponding appop. 67 */ asAppOp(@ermissionLevel int permissionLevel)68 public static int asAppOp(@PermissionLevel int permissionLevel) { 69 switch (permissionLevel) { 70 case PERMISSION_COARSE: 71 return AppOpsManager.OP_COARSE_LOCATION; 72 case PERMISSION_FINE: 73 return AppOpsManager.OP_FINE_LOCATION; 74 default: 75 throw new IllegalArgumentException(); 76 } 77 } 78 79 /** 80 * Creates a CallerIdentity from the current binder identity, using the given package and 81 * feature id. The package will be checked to enforce it belongs to the calling uid, and a 82 * security exception will be thrown if it is invalid. 83 */ fromBinder(Context context, String packageName, @Nullable String featureId)84 public static CallerIdentity fromBinder(Context context, String packageName, 85 @Nullable String featureId) { 86 return fromBinder(context, packageName, featureId, null); 87 } 88 89 /** 90 * Creates a CallerIdentity from the current binder identity, using the given package, feature 91 * id, and listener id. The package will be checked to enforce it belongs to the calling uid, 92 * and a security exception will be thrown if it is invalid. 93 */ fromBinder(Context context, String packageName, @Nullable String featureId, @Nullable String listenerId)94 public static CallerIdentity fromBinder(Context context, String packageName, 95 @Nullable String featureId, @Nullable String listenerId) { 96 int uid = Binder.getCallingUid(); 97 if (!ArrayUtils.contains(context.getPackageManager().getPackagesForUid(uid), packageName)) { 98 throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid); 99 } 100 101 return fromBinderUnsafe(context, packageName, featureId, listenerId); 102 } 103 104 /** 105 * Creates a CallerIdentity from the current binder identity, using the given package and 106 * feature id. The package will not be checked to enforce that it belongs to the calling uid - 107 * this method should only be used if the package will be validated by some other means, such as 108 * an appops call. 109 */ fromBinderUnsafe(Context context, String packageName, @Nullable String featureId)110 public static CallerIdentity fromBinderUnsafe(Context context, String packageName, 111 @Nullable String featureId) { 112 return fromBinderUnsafe(context, packageName, featureId, null); 113 } 114 115 /** 116 * Creates a CallerIdentity from the current binder identity, using the given package, feature 117 * id, and listener id. The package will not be checked to enforce that it belongs to the 118 * calling uid - this method should only be used if the package will be validated by some other 119 * means, such as an appops call. 120 */ fromBinderUnsafe(Context context, String packageName, @Nullable String featureId, @Nullable String listenerId)121 public static CallerIdentity fromBinderUnsafe(Context context, String packageName, 122 @Nullable String featureId, @Nullable String listenerId) { 123 return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), 124 UserHandle.getCallingUserId(), packageName, featureId, listenerId, 125 getBinderPermissionLevel(context)); 126 } 127 128 /** 129 * Throws a security exception if the caller does not hold a location permission. 130 */ enforceCallingOrSelfLocationPermission(Context context)131 public static void enforceCallingOrSelfLocationPermission(Context context) { 132 enforceLocationPermission(Binder.getCallingUid(), getBinderPermissionLevel(context)); 133 } 134 135 /** 136 * Returns false if the caller does not hold a location permission, true otherwise. 137 */ checkCallingOrSelfLocationPermission(Context context)138 public static boolean checkCallingOrSelfLocationPermission(Context context) { 139 return checkLocationPermission(getBinderPermissionLevel(context)); 140 } 141 enforceLocationPermission(int uid, @PermissionLevel int permissionLevel)142 private static void enforceLocationPermission(int uid, @PermissionLevel int permissionLevel) { 143 if (checkLocationPermission(permissionLevel)) { 144 return; 145 } 146 147 throw new SecurityException("uid " + uid + " does not have " + ACCESS_COARSE_LOCATION 148 + " or " + ACCESS_FINE_LOCATION + "."); 149 } 150 checkLocationPermission(@ermissionLevel int permissionLevel)151 private static boolean checkLocationPermission(@PermissionLevel int permissionLevel) { 152 return permissionLevel >= PERMISSION_COARSE; 153 } 154 getBinderPermissionLevel(Context context)155 private static @PermissionLevel int getBinderPermissionLevel(Context context) { 156 if (context.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) == PERMISSION_GRANTED) { 157 return PERMISSION_FINE; 158 } 159 if (context.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED) { 160 return PERMISSION_COARSE; 161 } 162 163 return PERMISSION_NONE; 164 } 165 166 /** The calling UID. */ 167 public final int uid; 168 169 /** The calling PID. */ 170 public final int pid; 171 172 /** The calling user. */ 173 public final int userId; 174 175 /** The calling package name. */ 176 public final String packageName; 177 178 /** The calling feature id. */ 179 public final @Nullable String featureId; 180 181 /** The calling listener id. */ 182 public final @Nullable String listenerId; 183 184 /** 185 * The calling location permission level. This field should only be used for validating 186 * permissions for API access. It should not be used for validating permissions for location 187 * access - that must be done through appops. 188 */ 189 public final @PermissionLevel int permissionLevel; 190 191 @VisibleForTesting CallerIdentity(int uid, int pid, int userId, String packageName, @Nullable String featureId, @PermissionLevel int permissionLevel)192 public CallerIdentity(int uid, int pid, int userId, String packageName, 193 @Nullable String featureId, @PermissionLevel int permissionLevel) { 194 this(uid, pid, userId, packageName, featureId, null, permissionLevel); 195 } 196 CallerIdentity(int uid, int pid, int userId, String packageName, @Nullable String featureId, @Nullable String listenerId, @PermissionLevel int permissionLevel)197 private CallerIdentity(int uid, int pid, int userId, String packageName, 198 @Nullable String featureId, @Nullable String listenerId, 199 @PermissionLevel int permissionLevel) { 200 this.uid = uid; 201 this.pid = pid; 202 this.userId = userId; 203 this.packageName = Objects.requireNonNull(packageName); 204 this.featureId = featureId; 205 this.listenerId = listenerId; 206 this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE, 207 PERMISSION_FINE, "permissionLevel"); 208 } 209 210 /** 211 * Throws a security exception if the CallerIdentity does not hold a location permission. 212 */ enforceLocationPermission()213 public void enforceLocationPermission() { 214 enforceLocationPermission(uid, permissionLevel); 215 } 216 217 @Override toString()218 public String toString() { 219 int length = 10 + packageName.length(); 220 if (featureId != null) { 221 length += featureId.length(); 222 } 223 224 StringBuilder builder = new StringBuilder(length); 225 builder.append(pid).append("/").append(packageName); 226 if (featureId != null) { 227 builder.append("["); 228 if (featureId.startsWith(packageName)) { 229 builder.append(featureId.substring(packageName.length())); 230 } else { 231 builder.append(featureId); 232 } 233 builder.append("]"); 234 } 235 return builder.toString(); 236 } 237 238 @Override equals(Object o)239 public boolean equals(Object o) { 240 if (this == o) { 241 return true; 242 } 243 if (!(o instanceof CallerIdentity)) { 244 return false; 245 } 246 CallerIdentity that = (CallerIdentity) o; 247 return uid == that.uid 248 && pid == that.pid 249 && packageName.equals(that.packageName) 250 && Objects.equals(featureId, that.featureId) 251 && Objects.equals(listenerId, that.listenerId); 252 } 253 254 @Override hashCode()255 public int hashCode() { 256 return Objects.hash(uid, pid, packageName, featureId); 257 } 258 } 259