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