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