1 /*
2  * Copyright (C) 2023 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.sdksandbox;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.provider.DeviceConfig;
23 import android.text.TextUtils;
24 import android.util.ArrayMap;
25 import android.util.ArraySet;
26 import android.util.Base64;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.server.sdksandbox.proto.Activity.ActivityAllowlists;
32 import com.android.server.sdksandbox.proto.Activity.AllowedActivities;
33 import com.android.server.sdksandbox.proto.BroadcastReceiver.AllowedBroadcastReceivers;
34 import com.android.server.sdksandbox.proto.BroadcastReceiver.BroadcastReceiverAllowlists;
35 import com.android.server.sdksandbox.proto.ContentProvider.AllowedContentProviders;
36 import com.android.server.sdksandbox.proto.ContentProvider.ContentProviderAllowlists;
37 import com.android.server.sdksandbox.proto.Services.AllowedServices;
38 import com.android.server.sdksandbox.proto.Services.ServiceAllowlists;
39 
40 import com.google.protobuf.Parser;
41 
42 import java.util.Map;
43 import java.util.Objects;
44 
45 class SdkSandboxSettingsListener implements DeviceConfig.OnPropertiesChangedListener {
46 
47     private static final String TAG = "SdkSandboxManager";
48     private static final String PROPERTY_DISABLE_SDK_SANDBOX = "disable_sdk_sandbox";
49     private static final boolean DEFAULT_VALUE_DISABLE_SDK_SANDBOX = true;
50 
51     // Prefix all the keys with sdksandbox_ as the namespace is shared with PPAPI
52     /**
53      * Property to enforce restrictions for SDK sandbox processes. If the value of this property is
54      * {@code true}, the restrictions will be enforced.
55      */
56     private static final String PROPERTY_ENFORCE_RESTRICTIONS = "sdksandbox_enforce_restrictions";
57 
58     private static final boolean DEFAULT_VALUE_ENFORCE_RESTRICTIONS = true;
59 
60     /** We need to keep in sync with the property used in ProcessList */
61     private static final String PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS =
62             "apply_sdk_sandbox_next_restrictions";
63 
64     private static final boolean DEFAULT_VALUE_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = false;
65 
66     private static final String PROPERTY_BROADCASTRECEIVER_ALLOWLIST =
67             "sdksandbox_broadcastreceiver_allowlist_per_targetSdkVersion";
68 
69     // Property for the canary set allowlist indicating which broadcast receivers can be registered
70     // by the sandbox.
71     private static final String PROPERTY_NEXT_BROADCASTRECEIVER_ALLOWLIST =
72             "sdksandbox_next_broadcastreceiver_allowlist";
73 
74     private static final String PROPERTY_CONTENTPROVIDER_ALLOWLIST =
75             "contentprovider_allowlist_per_targetSdkVersion";
76 
77     // Property indicating the ContentProvider canary allowlist.
78     private static final String PROPERTY_NEXT_CONTENTPROVIDER_ALLOWLIST =
79             "sdksandbox_next_contentprovider_allowlist";
80 
81     private static final String PROPERTY_SERVICES_ALLOWLIST =
82             "services_allowlist_per_targetSdkVersion";
83 
84     // Property for canary set for service restrictions
85     private static final String PROPERTY_NEXT_SERVICE_ALLOWLIST =
86             "sdksandbox_next_service_allowlist";
87 
88     private static final String PROPERTY_ACTIVITY_ALLOWLIST =
89             "sdksandbox_activity_allowlist_per_targetSdkVersion";
90     private static final String PROPERTY_NEXT_ACTIVITY_ALLOWLIST =
91             "sdksandbox_next_activity_allowlist";
92     private final Context mContext;
93     private final Object mLock = new Object();
94     private final SdkSandboxManagerService mSdkSandboxManagerService;
95 
96     @GuardedBy("mLock")
97     private boolean mKillSwitchEnabled =
98             DeviceConfig.getBoolean(
99                     DeviceConfig.NAMESPACE_ADSERVICES,
100                     PROPERTY_DISABLE_SDK_SANDBOX,
101                     DEFAULT_VALUE_DISABLE_SDK_SANDBOX);
102 
103     @GuardedBy("mLock")
104     private boolean mEnforceRestrictions =
105             DeviceConfig.getBoolean(
106                     DeviceConfig.NAMESPACE_ADSERVICES,
107                     PROPERTY_ENFORCE_RESTRICTIONS,
108                     DEFAULT_VALUE_ENFORCE_RESTRICTIONS);
109 
110     @GuardedBy("mLock")
111     private boolean mSdkSandboxApplyRestrictionsNext =
112             DeviceConfig.getBoolean(
113                     DeviceConfig.NAMESPACE_ADSERVICES,
114                     PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS,
115                     DEFAULT_VALUE_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS);
116 
117     @GuardedBy("mLock")
118     private Map<Integer, AllowedServices> mServiceAllowlistPerTargetSdkVersion =
119             getServicesAllowlist(
120                     DeviceConfig.getProperty(
121                             DeviceConfig.NAMESPACE_ADSERVICES, PROPERTY_SERVICES_ALLOWLIST));
122 
123     @GuardedBy("mLock")
124     private AllowedServices mNextServiceAllowlist =
125             getNextServiceDeviceConfigAllowlist(
126                     DeviceConfig.getProperty(
127                             DeviceConfig.NAMESPACE_ADSERVICES, PROPERTY_NEXT_SERVICE_ALLOWLIST));
128 
129     @GuardedBy("mLock")
130     private ArrayMap<Integer, ArraySet<String>> mContentProviderAllowlistPerTargetSdkVersion =
131             getContentProviderDeviceConfigAllowlist(
132                     DeviceConfig.getProperty(
133                             DeviceConfig.NAMESPACE_ADSERVICES, PROPERTY_CONTENTPROVIDER_ALLOWLIST));
134 
135     @GuardedBy("mLock")
136     private ArraySet<String> mNextContentProviderAllowlist =
137             getNextContentProviderDeviceConfigAllowlist(
138                     DeviceConfig.getProperty(
139                             DeviceConfig.NAMESPACE_ADSERVICES,
140                             PROPERTY_NEXT_CONTENTPROVIDER_ALLOWLIST));
141 
142     @Nullable
143     @GuardedBy("mLock")
144     private ArrayMap<Integer, ArraySet<String>> mBroadcastReceiverAllowlistPerTargetSdkVersion =
145             getBroadcastReceiverDeviceConfigAllowlist(
146                     DeviceConfig.getProperty(
147                             DeviceConfig.NAMESPACE_ADSERVICES,
148                             PROPERTY_BROADCASTRECEIVER_ALLOWLIST));
149 
150     @GuardedBy("mLock")
151     private ArraySet<String> mNextBroadcastReceiverAllowlist =
152             getNextBroadcastReceiverDeviceConfigAllowlist(
153                     DeviceConfig.getProperty(
154                             DeviceConfig.NAMESPACE_ADSERVICES,
155                             PROPERTY_NEXT_BROADCASTRECEIVER_ALLOWLIST));
156 
157     @Nullable
158     @GuardedBy("mLock")
159     private ArrayMap<Integer, ArraySet<String>> mActivityAllowlistPerTargetSdkVersion =
160             getActivityDeviceConfigAllowlist(
161                     DeviceConfig.getProperty(
162                             DeviceConfig.NAMESPACE_ADSERVICES, PROPERTY_ACTIVITY_ALLOWLIST));
163 
164     @Nullable
165     @GuardedBy("mLock")
166     private ArraySet<String> mNextActivityAllowlist =
167             getNextActivityDeviceConfigAllowlist(
168                     DeviceConfig.getProperty(
169                             DeviceConfig.NAMESPACE_ADSERVICES, PROPERTY_NEXT_ACTIVITY_ALLOWLIST));
170 
SdkSandboxSettingsListener(Context context, SdkSandboxManagerService sdkSandboxManagerService)171     SdkSandboxSettingsListener(Context context, SdkSandboxManagerService sdkSandboxManagerService) {
172         mContext = context;
173         mSdkSandboxManagerService = sdkSandboxManagerService;
174         DeviceConfig.addOnPropertiesChangedListener(
175                 DeviceConfig.NAMESPACE_ADSERVICES, mContext.getMainExecutor(), this);
176     }
177 
178     @Override
onPropertiesChanged(@onNull DeviceConfig.Properties properties)179     public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
180         synchronized (mLock) {
181             if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_ADSERVICES)) {
182                 return;
183             }
184             for (String name : properties.getKeyset()) {
185                 if (name == null) {
186                     continue;
187                 }
188 
189                 switch (name) {
190                     case PROPERTY_DISABLE_SDK_SANDBOX:
191                         boolean killSwitchPreviouslyEnabled = mKillSwitchEnabled;
192                         mKillSwitchEnabled =
193                                 properties.getBoolean(
194                                         PROPERTY_DISABLE_SDK_SANDBOX,
195                                         DEFAULT_VALUE_DISABLE_SDK_SANDBOX);
196                         if (mKillSwitchEnabled && !killSwitchPreviouslyEnabled) {
197                             Log.i(TAG, "SDK sandbox killswitch has become enabled");
198                             this.mSdkSandboxManagerService.stopAllSandboxes();
199                         }
200                         break;
201                     case PROPERTY_ENFORCE_RESTRICTIONS:
202                         mEnforceRestrictions =
203                                 properties.getBoolean(
204                                         PROPERTY_ENFORCE_RESTRICTIONS,
205                                         DEFAULT_VALUE_ENFORCE_RESTRICTIONS);
206                         break;
207                     case PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS:
208                         mSdkSandboxApplyRestrictionsNext =
209                                 properties.getBoolean(
210                                         PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS,
211                                         DEFAULT_VALUE_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS);
212                         break;
213                     case PROPERTY_SERVICES_ALLOWLIST:
214                         mServiceAllowlistPerTargetSdkVersion =
215                                 getServicesAllowlist(
216                                         properties.getString(PROPERTY_SERVICES_ALLOWLIST, null));
217                         break;
218                     case PROPERTY_NEXT_SERVICE_ALLOWLIST:
219                         mNextServiceAllowlist =
220                                 getNextServiceDeviceConfigAllowlist(
221                                         properties.getString(
222                                                 PROPERTY_NEXT_SERVICE_ALLOWLIST, null));
223                         break;
224                     case PROPERTY_CONTENTPROVIDER_ALLOWLIST:
225                         mContentProviderAllowlistPerTargetSdkVersion =
226                                 getContentProviderDeviceConfigAllowlist(
227                                         properties.getString(
228                                                 PROPERTY_CONTENTPROVIDER_ALLOWLIST, null));
229                         break;
230                     case PROPERTY_NEXT_CONTENTPROVIDER_ALLOWLIST:
231                         mNextContentProviderAllowlist =
232                                 getNextContentProviderDeviceConfigAllowlist(
233                                         properties.getString(
234                                                 PROPERTY_NEXT_CONTENTPROVIDER_ALLOWLIST, null));
235                         break;
236                     case PROPERTY_BROADCASTRECEIVER_ALLOWLIST:
237                         mBroadcastReceiverAllowlistPerTargetSdkVersion =
238                                 getBroadcastReceiverDeviceConfigAllowlist(
239                                         properties.getString(
240                                                 PROPERTY_BROADCASTRECEIVER_ALLOWLIST, null));
241                         break;
242                     case PROPERTY_NEXT_BROADCASTRECEIVER_ALLOWLIST:
243                         mNextBroadcastReceiverAllowlist =
244                                 getNextBroadcastReceiverDeviceConfigAllowlist(
245                                         properties.getString(
246                                                 PROPERTY_NEXT_BROADCASTRECEIVER_ALLOWLIST, null));
247                         break;
248                     case PROPERTY_ACTIVITY_ALLOWLIST:
249                         mActivityAllowlistPerTargetSdkVersion =
250                                 getActivityDeviceConfigAllowlist(
251                                         properties.getString(PROPERTY_ACTIVITY_ALLOWLIST, null));
252                         break;
253                     case PROPERTY_NEXT_ACTIVITY_ALLOWLIST:
254                         mNextActivityAllowlist =
255                                 getNextActivityDeviceConfigAllowlist(
256                                         properties.getString(
257                                                 PROPERTY_NEXT_ACTIVITY_ALLOWLIST, null));
258                         break;
259                     default:
260                 }
261             }
262         }
263     }
264 
isKillSwitchEnabled()265     public boolean isKillSwitchEnabled() {
266         synchronized (mLock) {
267             return mKillSwitchEnabled;
268         }
269     }
270 
setKillSwitchState(boolean enabled)271     void setKillSwitchState(boolean enabled) {
272         synchronized (mLock) {
273             DeviceConfig.setProperty(
274                     DeviceConfig.NAMESPACE_ADSERVICES,
275                     PROPERTY_DISABLE_SDK_SANDBOX,
276                     Boolean.toString(enabled),
277                     false);
278             mKillSwitchEnabled = enabled;
279         }
280     }
281 
282     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
unregisterPropertiesListener()283     void unregisterPropertiesListener() {
284         DeviceConfig.removeOnPropertiesChangedListener(this);
285     }
286 
areRestrictionsEnforced()287     public boolean areRestrictionsEnforced() {
288         synchronized (mLock) {
289             return mEnforceRestrictions;
290         }
291     }
292 
applySdkSandboxRestrictionsNext()293     public boolean applySdkSandboxRestrictionsNext() {
294         synchronized (mLock) {
295             return mSdkSandboxApplyRestrictionsNext;
296         }
297     }
298 
getServiceAllowlistForTargetSdkVersion(int targetSdkVersion)299     public AllowedServices getServiceAllowlistForTargetSdkVersion(int targetSdkVersion) {
300         synchronized (mLock) {
301             return mServiceAllowlistPerTargetSdkVersion.get(targetSdkVersion);
302         }
303     }
304 
getNextServiceAllowlist()305     public AllowedServices getNextServiceAllowlist() {
306         synchronized (mLock) {
307             return mNextServiceAllowlist;
308         }
309     }
310 
getContentProviderAllowlistPerTargetSdkVersion()311     public ArrayMap<Integer, ArraySet<String>> getContentProviderAllowlistPerTargetSdkVersion() {
312         synchronized (mLock) {
313             return mContentProviderAllowlistPerTargetSdkVersion;
314         }
315     }
316 
getNextContentProviderAllowlist()317     public ArraySet<String> getNextContentProviderAllowlist() {
318         synchronized (mLock) {
319             return mNextContentProviderAllowlist;
320         }
321     }
322 
323     @Nullable
getBroadcastReceiverAllowlistPerTargetSdkVersion()324     public ArrayMap<Integer, ArraySet<String>> getBroadcastReceiverAllowlistPerTargetSdkVersion() {
325         synchronized (mLock) {
326             return mBroadcastReceiverAllowlistPerTargetSdkVersion;
327         }
328     }
329 
330     @Nullable
getNextBroadcastReceiverAllowlist()331     public ArraySet<String> getNextBroadcastReceiverAllowlist() {
332         synchronized (mLock) {
333             return mNextBroadcastReceiverAllowlist;
334         }
335     }
336 
337     @Nullable
getActivityAllowlistPerTargetSdkVersion()338     public ArrayMap<Integer, ArraySet<String>> getActivityAllowlistPerTargetSdkVersion() {
339         synchronized (mLock) {
340             return mActivityAllowlistPerTargetSdkVersion;
341         }
342     }
343 
344     @Nullable
getNextActivityAllowlist()345     public ArraySet<String> getNextActivityAllowlist() {
346         synchronized (mLock) {
347             return mNextActivityAllowlist;
348         }
349     }
350 
351     /**
352      * Helper function to decode a proto property
353      *
354      * @param property The property which needs to be decoded
355      * @param base64value The base64 value of the property
356      * @return The decoded value of the property passed as the parameter
357      */
getDecodedPropertyValue( @onNull String property, @NonNull String base64value)358     private static byte[] getDecodedPropertyValue(
359             @NonNull String property, @NonNull String base64value) {
360         try {
361             return Base64.decode(base64value, Base64.NO_PADDING | Base64.NO_WRAP);
362         } catch (IllegalArgumentException e) {
363             Log.e(TAG, "Error while decoding " + property + " Error: " + e);
364         }
365         return null;
366     }
367 
368     @Nullable
getDeviceConfigProtoProperty( Parser<T> parser, @NonNull String property, @Nullable String value)369     private static <T> T getDeviceConfigProtoProperty(
370             Parser<T> parser, @NonNull String property, @Nullable String value) {
371         if (TextUtils.isEmpty(value)) {
372             Log.d(TAG, "Property " + property + " is empty.");
373             return null;
374         }
375         final byte[] decode = getDecodedPropertyValue(property, value);
376         if (Objects.isNull(decode)) {
377             return null;
378         }
379 
380         T proto = null;
381         try {
382             proto = parser.parseFrom(decode);
383         } catch (Exception e) {
384             Log.e(TAG, "Error while parsing " + property + ". Error: ", e);
385         }
386 
387         return proto;
388     }
389 
390     @NonNull
getServicesAllowlist(@ullable String value)391     private static Map<Integer, AllowedServices> getServicesAllowlist(@Nullable String value) {
392         final ServiceAllowlists allowedServicesProto =
393                 getDeviceConfigProtoProperty(
394                         ServiceAllowlists.parser(), PROPERTY_SERVICES_ALLOWLIST, value);
395         return allowedServicesProto == null
396                 ? new ArrayMap<>()
397                 : allowedServicesProto.getAllowlistPerTargetSdkMap();
398     }
399 
400     @Nullable
getNextServiceDeviceConfigAllowlist(@ullable String value)401     private AllowedServices getNextServiceDeviceConfigAllowlist(@Nullable String value) {
402         return getDeviceConfigProtoProperty(
403                 AllowedServices.parser(), PROPERTY_NEXT_SERVICE_ALLOWLIST, value);
404     }
405 
406     @NonNull
getContentProviderDeviceConfigAllowlist( @ullable String value)407     private static ArrayMap<Integer, ArraySet<String>> getContentProviderDeviceConfigAllowlist(
408             @Nullable String value) {
409         ContentProviderAllowlists contentProviderAllowlistsProto =
410                 getDeviceConfigProtoProperty(
411                         ContentProviderAllowlists.parser(),
412                         PROPERTY_CONTENTPROVIDER_ALLOWLIST,
413                         value);
414         // Content providers are restricted by default. If the property is not set, or it is an
415         // empty string, there are no content providers to allowlist.
416         if (contentProviderAllowlistsProto == null) {
417             return new ArrayMap<>();
418         }
419 
420         ArrayMap<Integer, ArraySet<String>> allowedContentProviders = new ArrayMap<>();
421 
422         contentProviderAllowlistsProto
423                 .getAllowlistPerTargetSdkMap()
424                 .forEach(
425                         (sdkVersion, allowList) -> {
426                             allowedContentProviders.put(
427                                     sdkVersion, new ArraySet<>(allowList.getAuthoritiesList()));
428                         });
429         return allowedContentProviders;
430     }
431 
432     @Nullable
getNextContentProviderDeviceConfigAllowlist( @ullable String value)433     private static ArraySet<String> getNextContentProviderDeviceConfigAllowlist(
434             @Nullable String value) {
435         AllowedContentProviders allowedContentProviders =
436                 getDeviceConfigProtoProperty(
437                         AllowedContentProviders.parser(),
438                         PROPERTY_NEXT_CONTENTPROVIDER_ALLOWLIST,
439                         value);
440         if (allowedContentProviders == null) {
441             return null;
442         }
443         return new ArraySet<>(allowedContentProviders.getAuthoritiesList());
444     }
445 
446     @Nullable
getBroadcastReceiverDeviceConfigAllowlist( @ullable String value)447     private static ArrayMap<Integer, ArraySet<String>> getBroadcastReceiverDeviceConfigAllowlist(
448             @Nullable String value) {
449         BroadcastReceiverAllowlists broadcastReceiverAllowlistsProto =
450                 getDeviceConfigProtoProperty(
451                         BroadcastReceiverAllowlists.parser(),
452                         PROPERTY_BROADCASTRECEIVER_ALLOWLIST,
453                         value);
454 
455         if (broadcastReceiverAllowlistsProto == null) {
456             return null;
457         }
458 
459         ArrayMap<Integer, ArraySet<String>> allowedBroadcastReceivers = new ArrayMap<>();
460 
461         broadcastReceiverAllowlistsProto
462                 .getAllowlistPerTargetSdkMap()
463                 .forEach(
464                         (sdkVersion, allowList) -> {
465                             allowedBroadcastReceivers.put(
466                                     sdkVersion, new ArraySet<>(allowList.getIntentActionsList()));
467                         });
468         return allowedBroadcastReceivers;
469     }
470 
471     @Nullable
getNextBroadcastReceiverDeviceConfigAllowlist( @ullable String value)472     private static ArraySet<String> getNextBroadcastReceiverDeviceConfigAllowlist(
473             @Nullable String value) {
474         AllowedBroadcastReceivers allowedBroadcastReceivers =
475                 getDeviceConfigProtoProperty(
476                         AllowedBroadcastReceivers.parser(),
477                         PROPERTY_NEXT_BROADCASTRECEIVER_ALLOWLIST,
478                         value);
479         if (allowedBroadcastReceivers == null) {
480             return null;
481         }
482         return new ArraySet<>(allowedBroadcastReceivers.getIntentActionsList());
483     }
484 
485     @Nullable
getActivityDeviceConfigAllowlist( @ullable String value)486     private static ArrayMap<Integer, ArraySet<String>> getActivityDeviceConfigAllowlist(
487             @Nullable String value) {
488         ActivityAllowlists activityAllowlistsProto =
489                 getDeviceConfigProtoProperty(
490                         ActivityAllowlists.parser(), PROPERTY_ACTIVITY_ALLOWLIST, value);
491 
492         if (activityAllowlistsProto == null) {
493             return null;
494         }
495 
496         ArrayMap<Integer, ArraySet<String>> allowedActivities = new ArrayMap<>();
497 
498         activityAllowlistsProto
499                 .getAllowlistPerTargetSdkMap()
500                 .forEach(
501                         (sdkVersion, allowList) -> {
502                             allowedActivities.put(
503                                     sdkVersion, new ArraySet<>(allowList.getActionsList()));
504                         });
505         return allowedActivities;
506     }
507 
508     @Nullable
getNextActivityDeviceConfigAllowlist(@ullable String value)509     private static ArraySet<String> getNextActivityDeviceConfigAllowlist(@Nullable String value) {
510         AllowedActivities allowedActivities =
511                 getDeviceConfigProtoProperty(
512                         AllowedActivities.parser(), PROPERTY_NEXT_ACTIVITY_ALLOWLIST, value);
513         if (allowedActivities == null) {
514             return null;
515         }
516         return new ArraySet<>(allowedActivities.getActionsList());
517     }
518 }
519