1 /* 2 * Copyright (C) 2022 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.connectivity; 18 19 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 20 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 21 22 import static com.android.server.connectivity.ConnectivityFlags.CARRIER_SERVICE_CHANGED_USE_CALLBACK; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.pm.ApplicationInfo; 31 import android.content.pm.PackageManager; 32 import android.net.NetworkCapabilities; 33 import android.net.NetworkSpecifier; 34 import android.net.TelephonyNetworkSpecifier; 35 import android.net.TransportInfo; 36 import android.net.wifi.WifiInfo; 37 import android.os.Handler; 38 import android.os.HandlerThread; 39 import android.os.Process; 40 import android.telephony.SubscriptionManager; 41 import android.telephony.TelephonyManager; 42 import android.util.Log; 43 import android.util.SparseArray; 44 45 import com.android.internal.annotations.GuardedBy; 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.internal.util.IndentingPrintWriter; 48 import com.android.modules.utils.HandlerExecutor; 49 import com.android.modules.utils.build.SdkLevel; 50 import com.android.net.module.util.DeviceConfigUtils; 51 import com.android.networkstack.apishim.TelephonyManagerShimImpl; 52 import com.android.networkstack.apishim.common.TelephonyManagerShim; 53 import com.android.networkstack.apishim.common.TelephonyManagerShim.CarrierPrivilegesListenerShim; 54 import com.android.networkstack.apishim.common.UnsupportedApiLevelException; 55 56 import java.util.ArrayList; 57 import java.util.List; 58 import java.util.concurrent.Executor; 59 import java.util.function.BiConsumer; 60 61 /** 62 * Tracks the uid of the carrier privileged app that provides the carrier config. 63 * Authenticates if the caller has same uid as 64 * carrier privileged app that provides the carrier config 65 * @hide 66 */ 67 public class CarrierPrivilegeAuthenticator { 68 private static final String TAG = CarrierPrivilegeAuthenticator.class.getSimpleName(); 69 private static final boolean DBG = true; 70 71 // The context is for the current user (system server) 72 private final Context mContext; 73 private final TelephonyManagerShim mTelephonyManagerShim; 74 private final TelephonyManager mTelephonyManager; 75 @GuardedBy("mLock") 76 private final SparseArray<CarrierServiceUidWithSubId> mCarrierServiceUidWithSubId = 77 new SparseArray<>(2 /* initialCapacity */); 78 @GuardedBy("mLock") 79 private int mModemCount = 0; 80 private final Object mLock = new Object(); 81 private final Handler mHandler; 82 @NonNull 83 private final List<PrivilegeListener> mCarrierPrivilegesChangedListeners = new ArrayList<>(); 84 private final boolean mUseCallbacksForServiceChanged; 85 private final boolean mRequestRestrictedWifiEnabled; 86 @NonNull 87 private final BiConsumer<Integer, Integer> mListener; 88 CarrierPrivilegeAuthenticator(@onNull final Context c, @NonNull final Dependencies deps, @NonNull final TelephonyManager t, @NonNull final TelephonyManagerShim telephonyManagerShim, final boolean requestRestrictedWifiEnabled, @NonNull BiConsumer<Integer, Integer> listener, @NonNull final Handler connectivityServiceHandler)89 public CarrierPrivilegeAuthenticator(@NonNull final Context c, 90 @NonNull final Dependencies deps, 91 @NonNull final TelephonyManager t, 92 @NonNull final TelephonyManagerShim telephonyManagerShim, 93 final boolean requestRestrictedWifiEnabled, 94 @NonNull BiConsumer<Integer, Integer> listener, 95 @NonNull final Handler connectivityServiceHandler) { 96 mContext = c; 97 mTelephonyManager = t; 98 mTelephonyManagerShim = telephonyManagerShim; 99 mUseCallbacksForServiceChanged = deps.isFeatureEnabled( 100 c, CARRIER_SERVICE_CHANGED_USE_CALLBACK); 101 mRequestRestrictedWifiEnabled = requestRestrictedWifiEnabled; 102 mListener = listener; 103 if (mRequestRestrictedWifiEnabled) { 104 mHandler = connectivityServiceHandler; 105 } else { 106 final HandlerThread thread = deps.makeHandlerThread(); 107 thread.start(); 108 mHandler = new Handler(thread.getLooper()); 109 synchronized (mLock) { 110 registerSimConfigChangedReceiver(); 111 simConfigChanged(); 112 } 113 } 114 } 115 registerSimConfigChangedReceiver()116 private void registerSimConfigChangedReceiver() { 117 final IntentFilter filter = new IntentFilter(); 118 filter.addAction(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED); 119 // Never unregistered because the system server never stops 120 mContext.registerReceiver(new BroadcastReceiver() { 121 @Override 122 public void onReceive(final Context context, final Intent intent) { 123 switch (intent.getAction()) { 124 case TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED: 125 simConfigChanged(); 126 break; 127 default: 128 Log.d(TAG, "Unknown intent received, action: " + intent.getAction()); 129 } 130 } 131 }, filter, null, mHandler); 132 } 133 134 /** 135 * Start CarrierPrivilegeAuthenticator 136 */ start()137 public void start() { 138 if (mRequestRestrictedWifiEnabled) { 139 registerSimConfigChangedReceiver(); 140 mHandler.post(this::simConfigChanged); 141 } 142 } 143 CarrierPrivilegeAuthenticator(@onNull final Context c, @NonNull final TelephonyManager t, final boolean requestRestrictedWifiEnabled, @NonNull BiConsumer<Integer, Integer> listener, @NonNull final Handler connectivityServiceHandler)144 public CarrierPrivilegeAuthenticator(@NonNull final Context c, 145 @NonNull final TelephonyManager t, final boolean requestRestrictedWifiEnabled, 146 @NonNull BiConsumer<Integer, Integer> listener, 147 @NonNull final Handler connectivityServiceHandler) { 148 this(c, new Dependencies(), t, TelephonyManagerShimImpl.newInstance(t), 149 requestRestrictedWifiEnabled, listener, connectivityServiceHandler); 150 } 151 152 public static class Dependencies { 153 /** 154 * Create a HandlerThread to use in CarrierPrivilegeAuthenticator. 155 */ makeHandlerThread()156 public HandlerThread makeHandlerThread() { 157 return new HandlerThread(TAG); 158 } 159 160 /** 161 * @see DeviceConfigUtils#isTetheringFeatureEnabled 162 */ isFeatureEnabled(Context context, String name)163 public boolean isFeatureEnabled(Context context, String name) { 164 return DeviceConfigUtils.isTetheringFeatureEnabled(context, name); 165 } 166 } 167 simConfigChanged()168 private void simConfigChanged() { 169 // If mRequestRestrictedWifiEnabled is false, constructor calls simConfigChanged 170 if (mRequestRestrictedWifiEnabled) { 171 ensureRunningOnHandlerThread(); 172 } 173 synchronized (mLock) { 174 unregisterCarrierPrivilegesListeners(); 175 mModemCount = mTelephonyManager.getActiveModemCount(); 176 registerCarrierPrivilegesListeners(mModemCount); 177 if (!mUseCallbacksForServiceChanged) updateCarrierServiceUid(); 178 } 179 } 180 181 private static class CarrierServiceUidWithSubId { 182 final int mUid; 183 final int mSubId; 184 CarrierServiceUidWithSubId(int uid, int subId)185 CarrierServiceUidWithSubId(int uid, int subId) { 186 mUid = uid; 187 mSubId = subId; 188 } 189 190 @Override equals(Object obj)191 public boolean equals(Object obj) { 192 if (!(obj instanceof CarrierServiceUidWithSubId)) { 193 return false; 194 } 195 CarrierServiceUidWithSubId compare = (CarrierServiceUidWithSubId) obj; 196 return (mUid == compare.mUid && mSubId == compare.mSubId); 197 } 198 199 @Override hashCode()200 public int hashCode() { 201 return mUid * 31 + mSubId; 202 } 203 } 204 private class PrivilegeListener implements CarrierPrivilegesListenerShim { 205 public final int mLogicalSlot; 206 PrivilegeListener(final int logicalSlot)207 PrivilegeListener(final int logicalSlot) { 208 mLogicalSlot = logicalSlot; 209 } 210 211 @Override onCarrierPrivilegesChanged( @onNull List<String> privilegedPackageNames, @NonNull int[] privilegedUids)212 public void onCarrierPrivilegesChanged( 213 @NonNull List<String> privilegedPackageNames, 214 @NonNull int[] privilegedUids) { 215 ensureRunningOnHandlerThread(); 216 if (mUseCallbacksForServiceChanged) return; 217 // Re-trigger the synchronous check (which is also very cheap due 218 // to caching in CarrierPrivilegesTracker). This allows consistency 219 // with the onSubscriptionsChangedListener and broadcasts. 220 updateCarrierServiceUid(); 221 } 222 223 @Override onCarrierServiceChanged(@ullable final String carrierServicePackageName, final int carrierServiceUid)224 public void onCarrierServiceChanged(@Nullable final String carrierServicePackageName, 225 final int carrierServiceUid) { 226 ensureRunningOnHandlerThread(); 227 if (!mUseCallbacksForServiceChanged) { 228 // Re-trigger the synchronous check (which is also very cheap due 229 // to caching in CarrierPrivilegesTracker). This allows consistency 230 // with the onSubscriptionsChangedListener and broadcasts. 231 updateCarrierServiceUid(); 232 return; 233 } 234 synchronized (mLock) { 235 CarrierServiceUidWithSubId oldPair = 236 mCarrierServiceUidWithSubId.get(mLogicalSlot); 237 int subId = getSubId(mLogicalSlot); 238 mCarrierServiceUidWithSubId.put( 239 mLogicalSlot, 240 new CarrierServiceUidWithSubId(carrierServiceUid, subId)); 241 if (oldPair != null 242 && oldPair.mUid != Process.INVALID_UID 243 && oldPair.mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID 244 && !oldPair.equals(mCarrierServiceUidWithSubId.get(mLogicalSlot))) { 245 mListener.accept(oldPair.mUid, oldPair.mSubId); 246 } 247 } 248 } 249 } 250 registerCarrierPrivilegesListeners(final int modemCount)251 private void registerCarrierPrivilegesListeners(final int modemCount) { 252 final HandlerExecutor executor = new HandlerExecutor(mHandler); 253 try { 254 for (int i = 0; i < modemCount; i++) { 255 PrivilegeListener carrierPrivilegesListener = new PrivilegeListener(i); 256 addCarrierPrivilegesListener(executor, carrierPrivilegesListener); 257 mCarrierPrivilegesChangedListeners.add(carrierPrivilegesListener); 258 } 259 } catch (IllegalArgumentException e) { 260 Log.e(TAG, "Encountered exception registering carrier privileges listeners", e); 261 } 262 } 263 264 @GuardedBy("mLock") unregisterCarrierPrivilegesListeners()265 private void unregisterCarrierPrivilegesListeners() { 266 for (PrivilegeListener carrierPrivilegesListener : mCarrierPrivilegesChangedListeners) { 267 removeCarrierPrivilegesListener(carrierPrivilegesListener); 268 CarrierServiceUidWithSubId oldPair = 269 mCarrierServiceUidWithSubId.get(carrierPrivilegesListener.mLogicalSlot); 270 mCarrierServiceUidWithSubId.remove(carrierPrivilegesListener.mLogicalSlot); 271 if (oldPair != null 272 && oldPair.mUid != Process.INVALID_UID 273 && oldPair.mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 274 mListener.accept(oldPair.mUid, oldPair.mSubId); 275 } 276 } 277 mCarrierPrivilegesChangedListeners.clear(); 278 } 279 getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex)280 private String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) { 281 try { 282 return mTelephonyManagerShim.getCarrierServicePackageNameForLogicalSlot( 283 logicalSlotIndex); 284 } catch (UnsupportedApiLevelException unsupportedApiLevelException) { 285 // Should not happen since CarrierPrivilegeAuthenticator is only used on T+ 286 Log.e(TAG, "getCarrierServicePackageNameForLogicalSlot API is not available"); 287 } 288 return null; 289 } 290 291 /** 292 * Check if a UID is the carrier service app of the subscription ID in the provided capabilities 293 * 294 * This returns whether the passed UID is the carrier service package for the subscription ID 295 * stored in the telephony network specifier in the passed network capabilities. 296 * If the capabilities don't code for a cellular or Wi-Fi network, or if they don't have the 297 * subscription ID in their specifier, this returns false. 298 * 299 * This method can be used to check that a network request that requires the UID to be 300 * the carrier service UID is indeed called by such a UID. An example of such a network could 301 * be a network with the {@link android.net.NetworkCapabilities#NET_CAPABILITY_CBS} 302 * capability. 303 * It can also be used to check that a factory is entitled to grant access to a given network 304 * to a given UID on grounds that it is the carrier service package. 305 * 306 * @param callingUid uid of the app claimed to be the carrier service package. 307 * @param networkCapabilities the network capabilities for which carrier privilege is checked. 308 * @return true if uid provides the relevant carrier config else false. 309 */ isCarrierServiceUidForNetworkCapabilities(int callingUid, @NonNull NetworkCapabilities networkCapabilities)310 public boolean isCarrierServiceUidForNetworkCapabilities(int callingUid, 311 @NonNull NetworkCapabilities networkCapabilities) { 312 if (callingUid == Process.INVALID_UID) { 313 return false; 314 } 315 int subId = getSubIdFromNetworkCapabilities(networkCapabilities); 316 if (SubscriptionManager.INVALID_SUBSCRIPTION_ID == subId) { 317 return false; 318 } 319 return callingUid == getCarrierServiceUidForSubId(subId); 320 } 321 322 /** 323 * Extract the SubscriptionId from the NetworkCapabilities. 324 * 325 * @param networkCapabilities the network capabilities which may contains the SubscriptionId. 326 * @return the SubscriptionId. 327 */ getSubIdFromNetworkCapabilities(@onNull NetworkCapabilities networkCapabilities)328 public int getSubIdFromNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) { 329 int subId; 330 if (networkCapabilities.hasSingleTransportBesidesTest(TRANSPORT_CELLULAR)) { 331 subId = getSubIdFromTelephonySpecifier(networkCapabilities.getNetworkSpecifier()); 332 } else if (networkCapabilities.hasSingleTransportBesidesTest(TRANSPORT_WIFI)) { 333 subId = getSubIdFromWifiTransportInfo(networkCapabilities.getTransportInfo()); 334 } else { 335 subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 336 } 337 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID 338 && mRequestRestrictedWifiEnabled 339 && networkCapabilities.getSubscriptionIds().size() == 1) { 340 subId = networkCapabilities.getSubscriptionIds().toArray(new Integer[0])[0]; 341 } 342 343 if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID 344 && !networkCapabilities.getSubscriptionIds().contains(subId)) { 345 // Ideally, the code above should just use networkCapabilities.getSubscriptionIds() 346 // for simplicity and future-proofing. However, this is not the historical behavior, 347 // and there is no enforcement that they do not differ, so log a terrible failure if 348 // they do not match to gain confidence this never happens. 349 // TODO : when there is confidence that this never happens, rewrite the code above 350 // with NetworkCapabilities#getSubscriptionIds. 351 Log.wtf(TAG, "NetworkCapabilities subIds are inconsistent between " 352 + "specifier/transportInfo and mSubIds : " + networkCapabilities); 353 } 354 return subId; 355 } 356 357 @VisibleForTesting getSubId(int slotIndex)358 protected int getSubId(int slotIndex) { 359 if (SdkLevel.isAtLeastU()) { 360 return SubscriptionManager.getSubscriptionId(slotIndex); 361 } else { 362 SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class); 363 int[] subIds = sm.getSubscriptionIds(slotIndex); 364 if (subIds != null && subIds.length > 0) { 365 return subIds[0]; 366 } 367 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 368 } 369 } 370 371 @VisibleForTesting updateCarrierServiceUid()372 void updateCarrierServiceUid() { 373 synchronized (mLock) { 374 SparseArray<CarrierServiceUidWithSubId> copy = mCarrierServiceUidWithSubId.clone(); 375 mCarrierServiceUidWithSubId.clear(); 376 for (int i = 0; i < mModemCount; i++) { 377 int subId = getSubId(i); 378 mCarrierServiceUidWithSubId.put( 379 i, 380 new CarrierServiceUidWithSubId( 381 getCarrierServicePackageUidForSlot(i), subId)); 382 } 383 for (int i = 0; i < copy.size(); ++i) { 384 CarrierServiceUidWithSubId oldPair = copy.valueAt(i); 385 CarrierServiceUidWithSubId newPair = mCarrierServiceUidWithSubId.get(copy.keyAt(i)); 386 if (oldPair.mUid != Process.INVALID_UID 387 && oldPair.mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID 388 && !oldPair.equals(newPair)) { 389 mListener.accept(oldPair.mUid, oldPair.mSubId); 390 } 391 } 392 } 393 } 394 395 @VisibleForTesting getCarrierServiceUidForSubId(int subId)396 int getCarrierServiceUidForSubId(int subId) { 397 synchronized (mLock) { 398 for (int i = 0; i < mCarrierServiceUidWithSubId.size(); ++i) { 399 if (mCarrierServiceUidWithSubId.valueAt(i).mSubId == subId) { 400 return mCarrierServiceUidWithSubId.valueAt(i).mUid; 401 } 402 } 403 return Process.INVALID_UID; 404 } 405 } 406 407 @VisibleForTesting getUidForPackage(String pkgName)408 int getUidForPackage(String pkgName) { 409 if (pkgName == null) { 410 return Process.INVALID_UID; 411 } 412 try { 413 PackageManager pm = mContext.getPackageManager(); 414 if (pm != null) { 415 ApplicationInfo applicationInfo = pm.getApplicationInfo(pkgName, 0); 416 if (applicationInfo != null) { 417 return applicationInfo.uid; 418 } 419 } 420 } catch (PackageManager.NameNotFoundException exception) { 421 // Didn't find package. Try other users 422 Log.i(TAG, "Unable to find uid for package " + pkgName); 423 } 424 return Process.INVALID_UID; 425 } 426 427 @VisibleForTesting getCarrierServicePackageUidForSlot(int slotId)428 int getCarrierServicePackageUidForSlot(int slotId) { 429 return getUidForPackage(getCarrierServicePackageNameForLogicalSlot(slotId)); 430 } 431 432 @VisibleForTesting getSubIdFromTelephonySpecifier(@ullable final NetworkSpecifier specifier)433 int getSubIdFromTelephonySpecifier(@Nullable final NetworkSpecifier specifier) { 434 if (specifier instanceof TelephonyNetworkSpecifier) { 435 return ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); 436 } 437 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 438 } 439 getSubIdFromWifiTransportInfo(@ullable final TransportInfo info)440 int getSubIdFromWifiTransportInfo(@Nullable final TransportInfo info) { 441 if (info instanceof WifiInfo) { 442 return ((WifiInfo) info).getSubscriptionId(); 443 } 444 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 445 } 446 447 // Helper methods to avoid having to deal with UnsupportedApiLevelException. addCarrierPrivilegesListener(@onNull final Executor executor, @NonNull final PrivilegeListener listener)448 private void addCarrierPrivilegesListener(@NonNull final Executor executor, 449 @NonNull final PrivilegeListener listener) { 450 try { 451 mTelephonyManagerShim.addCarrierPrivilegesListener(listener.mLogicalSlot, executor, 452 listener); 453 } catch (UnsupportedApiLevelException unsupportedApiLevelException) { 454 // Should not happen since CarrierPrivilegeAuthenticator is only used on T+ 455 Log.e(TAG, "addCarrierPrivilegesListener API is not available"); 456 } 457 } 458 removeCarrierPrivilegesListener(PrivilegeListener listener)459 private void removeCarrierPrivilegesListener(PrivilegeListener listener) { 460 try { 461 mTelephonyManagerShim.removeCarrierPrivilegesListener(listener); 462 } catch (UnsupportedApiLevelException unsupportedApiLevelException) { 463 // Should not happen since CarrierPrivilegeAuthenticator is only used on T+ 464 Log.e(TAG, "removeCarrierPrivilegesListener API is not available"); 465 } 466 } 467 ensureRunningOnHandlerThread()468 private void ensureRunningOnHandlerThread() { 469 if (mHandler.getLooper().getThread() != Thread.currentThread()) { 470 throw new IllegalStateException( 471 "Not running on handler thread: " + Thread.currentThread().getName()); 472 } 473 } 474 dump(IndentingPrintWriter pw)475 public void dump(IndentingPrintWriter pw) { 476 pw.println("CarrierPrivilegeAuthenticator:"); 477 pw.println("mRequestRestrictedWifiEnabled = " + mRequestRestrictedWifiEnabled); 478 synchronized (mLock) { 479 for (int i = 0; i < mCarrierServiceUidWithSubId.size(); ++i) { 480 final int logicalSlot = mCarrierServiceUidWithSubId.keyAt(i); 481 final int serviceUid = mCarrierServiceUidWithSubId.valueAt(i).mUid; 482 final int subId = mCarrierServiceUidWithSubId.valueAt(i).mSubId; 483 pw.println("Logical slot = " + logicalSlot + " : uid = " + serviceUid 484 + " : subId = " + subId); 485 } 486 } 487 } 488 } 489