Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5cb29734 authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

Only sync adapters with access can see an account - framework

It was possible for a sync adapter without accounts access to
see the account which it is supposed to sync which can be used to
identify the user. This change ensures that only sync adapters
with account access can run (which results in seeing the account),
otherwise we involve the user to approve access only to this account.

A sync adapter can access an account if one of these is true:
 - it is signed as the authenticator for this account
 - has the GET_ACCOUNTS permission
 - has an auth token for the account
 - it is a preinstalled app (system or privileged)

The main thing we need to figure out is if the extra prompts
for giving access to a sync adapter to the account create too
much friction.

bug:28163381

Change-Id: Ie083bb681b5a2aed81ca5f6a062193a175fad77e
parent 50ff32c1
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.accounts;

import static android.Manifest.permission.GET_ACCOUNTS;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.Size;
@@ -28,6 +29,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.res.Resources;
import android.database.SQLException;
import android.os.Build;
@@ -265,6 +267,15 @@ public class AccountManager {
            "android.accounts.AccountAuthenticator";
    public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";

    /**
     * Token for the special case where a UID has access only to an account
     * but no authenticator specific auth tokens.
     *
     * @hide
     */
    public static final String ACCOUNT_ACCESS_TOKEN =
            "com.android.abbfd278-af8b-415d-af8b-7571d5dab133";

    private final Context mContext;
    private final IAccountManager mService;
    private final Handler mMainHandler;
@@ -2960,4 +2971,49 @@ public class AccountManager {
            }
        }.start();
    }

    /**
     * Gets whether a given package under a user has access to an account.
     * Can be called only from the system UID.
     *
     * @param account The account for which to check.
     * @param packageName The package for which to check.
     * @param userHandle The user for which to check.
     * @return True if the package can access the account.
     *
     * @hide
     */
    public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
            @NonNull UserHandle userHandle) {
        try {
            return mService.hasAccountAccess(account, packageName, userHandle);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Creates an intent to request access to a given account for a UID.
     * The returned intent should be stated for a result where {@link
     * Activity#RESULT_OK} result means access was granted whereas {@link
     * Activity#RESULT_CANCELED} result means access wasn't granted. Can
     * be called only from the system UID.
     *
     * @param account The account for which to request.
     * @param packageName The package name which to request.
     * @param userHandle The user for which to request.
     * @return The intent to request account access or null if the package
     *     doesn't exist.
     *
     * @hide
     */
    public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
            @NonNull String packageName, @NonNull UserHandle userHandle) {
        try {
            return mService.createRequestAccountAccessIntentSenderAsUser(account, packageName,
                    userHandle);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.accounts;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.os.RemoteCallback;

/**
 * Account manager local system service interface.
 *
 * @hide Only for use within the system server.
 */
public abstract class AccountManagerInternal {

    /**
     * Requests that a given package is given access to an account.
     * The provided callback will be invoked with a {@link android.os.Bundle}
     * containing the result which will be a boolean value mapped to the
     * {@link AccountManager#KEY_BOOLEAN_RESULT} key.
     *
     * @param account The account for which to request.
     * @param packageName The package name for which to request.
     * @param userId Concrete user id for which to request.
     * @param callback A callback for receiving the result.
     */
    public abstract void requestAccountAccess(@NonNull  Account account,
            @NonNull String packageName, @IntRange(from = 0) int userId,
            @NonNull RemoteCallback callback);
}
+6 −4
Original line number Diff line number Diff line
@@ -35,12 +35,10 @@ import java.io.IOException;
 */
public class GrantCredentialsPermissionActivity extends Activity implements View.OnClickListener {
    public static final String EXTRAS_ACCOUNT = "account";
    public static final String EXTRAS_AUTH_TOKEN_LABEL = "authTokenLabel";
    public static final String EXTRAS_AUTH_TOKEN_TYPE = "authTokenType";
    public static final String EXTRAS_RESPONSE = "response";
    public static final String EXTRAS_ACCOUNT_TYPE_LABEL = "accountTypeLabel";
    public static final String EXTRAS_PACKAGES = "application";
    public static final String EXTRAS_REQUESTING_UID = "uid";

    private Account mAccount;
    private String mAuthTokenType;
    private int mUid;
@@ -109,7 +107,11 @@ public class GrantCredentialsPermissionActivity extends Activity implements View
                }
            }
        };
        AccountManager.get(this).getAuthTokenLabel(mAccount.type, mAuthTokenType, callback, null);

        if (!AccountManager.ACCOUNT_ACCESS_TOKEN.equals(mAuthTokenType)) {
            AccountManager.get(this).getAuthTokenLabel(mAccount.type,
                    mAuthTokenType, callback, null);
        }

        findViewById(R.id.allow_button).setOnClickListener(this);
        findViewById(R.id.deny_button).setOnClickListener(this);
+9 −1
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@ package android.accounts;
import android.accounts.IAccountManagerResponse;
import android.accounts.Account;
import android.accounts.AuthenticatorDescription;
import android.content.IntentSender;
import android.os.Bundle;

import android.os.RemoteCallback;
import android.os.UserHandle;

/**
 * Central application service that provides account management.
@@ -102,4 +104,10 @@ interface IAccountManager {
    /* Check if credentials update is suggested */
    void isCredentialsUpdateSuggested(in IAccountManagerResponse response, in Account account,
        String statusToken);

    /* Check if the package in a user can access an account */
    boolean hasAccountAccess(in Account account, String packageName, in UserHandle userHandle);
    /* Crate an intent to request account access for package and a given user id */
    IntentSender createRequestAccountAccessIntentSenderAsUser(in Account account,
        String packageName, in UserHandle userHandle);
}
+20 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.content;

import android.annotation.Nullable;
import android.text.TextUtils;
import android.os.Parcelable;
import android.os.Parcel;
@@ -33,6 +34,7 @@ public class SyncAdapterType implements Parcelable {
    private final boolean isAlwaysSyncable;
    private final boolean allowParallelSyncs;
    private final String settingsActivity;
    private final String packageName;

    public SyncAdapterType(String authority, String accountType, boolean userVisible,
            boolean supportsUploading) {
@@ -50,6 +52,7 @@ public class SyncAdapterType implements Parcelable {
        this.allowParallelSyncs = false;
        this.settingsActivity = null;
        this.isKey = false;
        this.packageName = null;
    }

    /** @hide */
@@ -57,7 +60,8 @@ public class SyncAdapterType implements Parcelable {
            boolean supportsUploading,
            boolean isAlwaysSyncable,
            boolean allowParallelSyncs,
            String settingsActivity) {
            String settingsActivity,
            String packageName) {
        if (TextUtils.isEmpty(authority)) {
            throw new IllegalArgumentException("the authority must not be empty: " + authority);
        }
@@ -72,6 +76,7 @@ public class SyncAdapterType implements Parcelable {
        this.allowParallelSyncs = allowParallelSyncs;
        this.settingsActivity = settingsActivity;
        this.isKey = false;
        this.packageName = packageName;
    }

    private SyncAdapterType(String authority, String accountType) {
@@ -89,6 +94,7 @@ public class SyncAdapterType implements Parcelable {
        this.allowParallelSyncs = false;
        this.settingsActivity = null;
        this.isKey = true;
        this.packageName = null;
    }

    public boolean supportsUploading() {
@@ -148,6 +154,16 @@ public class SyncAdapterType implements Parcelable {
        return settingsActivity;
    }

    /**
     * The package hosting the sync adapter.
     * @return The package name.
     *
     * @hide
     */
    public @Nullable String getPackageName() {
        return packageName;
    }

    public static SyncAdapterType newKey(String authority, String accountType) {
        return new SyncAdapterType(authority, accountType);
    }
@@ -181,6 +197,7 @@ public class SyncAdapterType implements Parcelable {
                    + ", isAlwaysSyncable=" + isAlwaysSyncable
                    + ", allowParallelSyncs=" + allowParallelSyncs
                    + ", settingsActivity=" + settingsActivity
                    + ", packageName=" + packageName
                    + "}";
        }
    }
@@ -201,6 +218,7 @@ public class SyncAdapterType implements Parcelable {
        dest.writeInt(isAlwaysSyncable ? 1 : 0);
        dest.writeInt(allowParallelSyncs ? 1 : 0);
        dest.writeString(settingsActivity);
        dest.writeString(packageName);
    }

    public SyncAdapterType(Parcel source) {
@@ -211,6 +229,7 @@ public class SyncAdapterType implements Parcelable {
                source.readInt() != 0,
                source.readInt() != 0,
                source.readInt() != 0,
                source.readString(),
                source.readString());
    }

Loading