Skip to content
This repository has been archived by the owner on Oct 29, 2024. It is now read-only.

Commit

Permalink
gmscompat: support bypassing permission requirements of GmsCore services
Browse files Browse the repository at this point in the history
  • Loading branch information
muhomorr authored and thestinger committed May 1, 2023
1 parent f5111d9 commit b8f5ed5
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 1 deletion.
6 changes: 6 additions & 0 deletions core/java/android/app/ContextImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -2259,6 +2259,12 @@ public int checkSelfPermission(String permission) {
return PERMISSION_DENIED;
}

if (GmsCompat.isEnabled()) {
if (GmsHooks.shouldSpoofSelfPermissionCheck(permission)) {
return PERMISSION_GRANTED;
}
}

return checkPermission(permission, Process.myPid(), Process.myUid());
}

Expand Down
12 changes: 12 additions & 0 deletions core/java/android/os/Binder.java
Original file line number Diff line number Diff line change
Expand Up @@ -684,9 +684,14 @@ public void attachInterface(@Nullable IInterface owner, @Nullable String descrip
if (BinderRedirector.enabled()) {
mPerformRedirectionCheck = "com.google.android.gms.common.internal.IGmsCallbacks".equals(descriptor);
}

if (GmsCompat.isGmsCore()) {
mIsGmsServiceBroker = GmsHooks.GMS_SERVICE_BROKER_INTERFACE_DESCRIPTOR.equals(descriptor);
}
}

private boolean mPerformRedirectionCheck;
private boolean mIsGmsServiceBroker;

/**
* Default implementation returns an empty interface name.
Expand Down Expand Up @@ -1286,7 +1291,11 @@ private boolean execTransactInternal(int code, long dataObj, long replyObj, int
final boolean tracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_AIDL) &&
(Binder.isStackTrackingEnabled() || Binder.isTracingEnabled(callingUid));
data.mPerformBinderRedirectionCheck = mPerformRedirectionCheck;
boolean onBeginGmsServiceBrokerCallRet = false;
try {
if (mIsGmsServiceBroker) {
onBeginGmsServiceBrokerCallRet = GmsHooks.onBeginGmsServiceBrokerCall(code, data);
}
final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
if (heavyHitterWatcher != null) {
// Notify the heavy hitter watcher, if it's enabled.
Expand Down Expand Up @@ -1328,6 +1337,9 @@ private boolean execTransactInternal(int code, long dataObj, long replyObj, int
res = true;
} finally {
data.mPerformBinderRedirectionCheck = false;
if (onBeginGmsServiceBrokerCallRet) {
GmsHooks.onEndGmsServiceBrokerCall();
}
if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_AIDL);
}
Expand Down
19 changes: 19 additions & 0 deletions core/java/com/android/internal/gmscompat/GmsCompatConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;

import com.android.internal.gmscompat.flags.GmsFlag;

Expand All @@ -26,6 +27,8 @@ public class GmsCompatConfig implements Parcelable {
public final ArrayMap<String, ArrayList<String>> forceDefaultFlagsMap = new ArrayMap<>();
// keys are package names, values are list of permissions self-checks of which should be spoofed
public final ArrayMap<String, ArrayList<String>> spoofSelfPermissionChecksMap = new ArrayMap<>();
// keys are serviceIds, values are service permission requirements that need to be bypassed
public final SparseArray<ArraySet<String>> gmsServiceBrokerPermissionBypasses = new SparseArray<>();

// set only in processes for which GmsCompat is enabled, to speed up lookups
public ArraySet<String> spoofSelfPermissionChecks;
Expand Down Expand Up @@ -93,6 +96,15 @@ public void writeToParcel(Parcel p, int wtpFlags) {

writeArrayMapStringStringList(forceDefaultFlagsMap, p);
writeArrayMapStringStringList(spoofSelfPermissionChecksMap, p);
{
var map = gmsServiceBrokerPermissionBypasses;
int cnt = map.size();
p.writeInt(cnt);
for (int i = 0; i < cnt; ++i) {
p.writeInt(map.keyAt(i));
p.writeArraySet(map.valueAt(i));
}
}
}

static void writeArrayMapStringStringList(ArrayMap<String, ArrayList<String>> map, Parcel p) {
Expand Down Expand Up @@ -141,6 +153,13 @@ public GmsCompatConfig createFromParcel(Parcel p) {

readArrayMapStringStringList(p, r.forceDefaultFlagsMap);
readArrayMapStringStringList(p, r.spoofSelfPermissionChecksMap);
{
int cnt = p.readInt();
ClassLoader cl = String.class.getClassLoader();
for (int i = 0; i < cnt; ++i) {
r.gmsServiceBrokerPermissionBypasses.put(p.readInt(), (ArraySet<String>) p.readArraySet(cl));
}
}

if (GmsCompat.isEnabled()) {
ArrayList<String> perms = r.spoofSelfPermissionChecksMap.get(ActivityThread.currentPackageName());
Expand Down
64 changes: 64 additions & 0 deletions core/java/com/android/internal/gmscompat/GmsHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@
import android.provider.Downloads;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.webkit.WebView;

import com.android.internal.gmscompat.client.ClientPriorityManager;
import com.android.internal.gmscompat.client.GmsCompatClientService;
import com.android.internal.gmscompat.flags.GmsFlag;
import com.android.internal.gmscompat.sysservice.GmcPackageManager;
import com.android.internal.gmscompat.util.GmcActivityUtils;
import com.android.internal.gmscompat.util.GmsCoreActivityLauncher;

Expand Down Expand Up @@ -104,6 +106,7 @@ public static void init(Context ctx, String packageName) {
}

configUpdateLock = new Object();
tlPermissionsToSpoof = new ThreadLocal<>();

// Locking is needed to prevent a race that would occur if config is updated via
// BinderGca2Gms#updateConfig in the time window between BinderGms2Gca#connect and setConfig()
Expand Down Expand Up @@ -649,5 +652,66 @@ public static Service maybeInstantiateService(String className) {
private static volatile SQLiteOpenHelper phenotypeDb;
public static SQLiteOpenHelper getPhenotypeDb() { return phenotypeDb; }

private static ThreadLocal<ArraySet<String>> tlPermissionsToSpoof;

public static boolean shouldSpoofSelfPermissionCheck(String perm) {
ArraySet<String> set = tlPermissionsToSpoof.get();
if (set == null) {
return false;
}

return set.contains(perm);
}

public static final String GMS_SERVICE_BROKER_INTERFACE_DESCRIPTOR =
"com.google.android.gms.common.internal.IGmsServiceBroker";

public static boolean onBeginGmsServiceBrokerCall(int transactionCode, Parcel data) {
if (transactionCode != 46) { // getService() method
return false;
}

try {
data.enforceInterface(GMS_SERVICE_BROKER_INTERFACE_DESCRIPTOR);
// IGmsCallbacks binder
data.readStrongBinder();

if (data.readInt() == 1) { // GetServiceRequest is present
// GetServiceRequest object header
data.readInt();
data.readInt();

// version
data.readInt();
data.readInt();

// id of serviceId property
data.readInt();

int serviceId = data.readInt();

ArraySet<String> permsToSpoof = config().gmsServiceBrokerPermissionBypasses.get(serviceId);
if (permsToSpoof != null) {
Log.d(TAG, "start spoofing self permission checks for getService() call for API "
+ serviceId + ", perms: " + Arrays.toString(permsToSpoof.toArray()));
tlPermissionsToSpoof.set(permsToSpoof);
// there's a second layer of caching inside GmsCore, need to notify permission
// change listener used by that cache
GmcPackageManager.notifyPermissionsChangeListeners();
return true;
}
}
} finally {
data.setDataPosition(0);
}

return false;
}

public static void onEndGmsServiceBrokerCall() {
Log.d(TAG, "end self permission check spoofing");
tlPermissionsToSpoof.set(null);
}

private GmsHooks() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import android.content.pm.PackageInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VersionedPackage;
import android.os.Process;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.gmscompat.GmsInfo;
import com.android.internal.gmscompat.PlayStoreHooks;
Expand Down Expand Up @@ -135,7 +137,31 @@ public boolean hasSystemFeature(String name) {

// requires privileged OBSERVE_GRANT_REVOKE_PERMISSIONS permission
@Override
public void addOnPermissionsChangeListener(OnPermissionsChangedListener listener) {}
public void addOnPermissionsChangeListener(OnPermissionsChangedListener listener) {
synchronized (onPermissionsChangedListeners) {
onPermissionsChangedListeners.add(listener);
}
}

@Override
public void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener) {
synchronized (onPermissionsChangedListeners) {
onPermissionsChangedListeners.remove(listener);
}
}

public static void notifyPermissionsChangeListeners() {
Log.d("GmcPackageManager", "notifyPermissionsChangeListeners");
int myUid = Process.myUid();
synchronized (onPermissionsChangedListeners) {
for (OnPermissionsChangedListener l : onPermissionsChangedListeners) {
l.onPermissionsChanged(myUid);
}
}
}

private static final ArrayList<OnPermissionsChangedListener> onPermissionsChangedListeners =
new ArrayList<>();

// MATCH_ANY_USER flag requires privileged INTERACT_ACROSS_USERS permission

Expand Down

0 comments on commit b8f5ed5

Please sign in to comment.