am 42bc734b: Merge "Block access to accounts for limited users." into jb-mr2-dev

* commit '42bc734b302f1fe68c031e013513535bed4c24f1':
  Block access to accounts for limited users.
This commit is contained in:
Amith Yamasani
2013-03-30 18:34:57 -07:00
committed by Android Git Automerger
11 changed files with 197 additions and 41 deletions

View File

@ -16984,6 +16984,7 @@ package android.os {
}
public class UserManager {
method public static synchronized android.os.UserManager get(android.content.Context);
method public long getSerialNumberForUser(android.os.UserHandle);
method public int getUserCount();
method public android.os.UserHandle getUserForSerialNumber(long);

View File

@ -387,6 +387,23 @@ public class AccountManager {
}
}
/**
* @hide
* For use by internal activities. Returns the list of accounts that the calling package
* is authorized to use, particularly for shared accounts.
* @param packageName package name of the calling app.
* @param uid the uid of the calling app.
* @return the accounts that are available to this package and user.
*/
public Account[] getAccountsForPackage(String packageName, int uid) {
try {
return mService.getAccountsForPackage(packageName, uid);
} catch (RemoteException re) {
// possible security exception
throw new RuntimeException(re);
}
}
/**
* Lists all accounts of a particular type. The account type is a
* string token corresponding to the authenticator and useful domain
@ -575,7 +592,7 @@ public class AccountManager {
public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
return mService.addAccount(account, password, userdata);
return mService.addAccountExplicitly(account, password, userdata);
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
@ -1123,7 +1140,7 @@ public class AccountManager {
return new AmsTask(activity, handler, callback) {
public void doWork() throws RemoteException {
mService.addAcount(mResponse, accountType, authTokenType,
mService.addAccount(mResponse, accountType, authTokenType,
requiredFeatures, activity != null, optionsIn);
}
}.start();

View File

@ -18,9 +18,14 @@ package android.accounts;
import com.google.android.collect.Sets;
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@ -29,6 +34,7 @@ import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.R;
@ -119,6 +125,9 @@ public class ChooseTypeAndAccountActivity extends Activity
private Parcelable[] mExistingAccounts = null;
private int mSelectedItemIndex;
private Button mOkButton;
private int mCallingUid;
private String mCallingPackage;
private boolean mDisallowAddAccounts;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -128,6 +137,24 @@ public class ChooseTypeAndAccountActivity extends Activity
+ savedInstanceState + ")");
}
String message = null;
try {
IBinder activityToken = getActivityToken();
mCallingUid = ActivityManagerNative.getDefault().getLaunchedFromUid(activityToken);
mCallingPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
activityToken);
if (mCallingUid != 0 && mCallingPackage != null) {
Bundle restrictions = UserManager.get(this)
.getUserRestrictions(new UserHandle(UserHandle.getUserId(mCallingUid)));
mDisallowAddAccounts =
restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false);
}
} catch (RemoteException re) {
// Couldn't figure out caller details
Log.w(getClass().getSimpleName(), "Unable to get caller identity \n" + re);
}
// save some items we use frequently
final Intent intent = getIntent();
@ -179,6 +206,11 @@ public class ChooseTypeAndAccountActivity extends Activity
// If there are no relevant accounts and only one relevant account type go directly to
// add account. Otherwise let the user choose.
if (mAccounts.isEmpty()) {
if (mDisallowAddAccounts) {
setContentView(R.layout.app_not_authorized);
setTitle(R.string.error_message_title);
return;
}
if (mSetOfRelevantAccountTypes.size() == 1) {
runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next());
} else {
@ -296,7 +328,8 @@ public class ChooseTypeAndAccountActivity extends Activity
}
if (accountName == null || accountType == null) {
Account[] currentAccounts = AccountManager.get(this).getAccounts();
Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage(
mCallingPackage, mCallingUid);
Set<Account> preExistingAccounts = new HashSet<Account>();
for (Parcelable accountParcel : mExistingAccounts) {
preExistingAccounts.add((Account) accountParcel);
@ -347,7 +380,8 @@ public class ChooseTypeAndAccountActivity extends Activity
AccountManager.KEY_INTENT);
if (intent != null) {
mPendingRequest = REQUEST_ADD_ACCOUNT;
mExistingAccounts = AccountManager.get(this).getAccounts();
mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage,
mCallingUid);
intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(intent, REQUEST_ADD_ACCOUNT);
return;
@ -424,12 +458,14 @@ public class ChooseTypeAndAccountActivity extends Activity
private String[] getListOfDisplayableOptions(ArrayList<Account> accounts) {
// List of options includes all accounts found together with "Add new account" as the
// last item in the list.
String[] listItems = new String[accounts.size() + 1];
String[] listItems = new String[accounts.size() + (mDisallowAddAccounts ? 0 : 1)];
for (int i = 0; i < accounts.size(); i++) {
listItems[i] = accounts.get(i).name;
}
listItems[accounts.size()] = getResources().getString(
R.string.add_account_button_label);
if (!mDisallowAddAccounts) {
listItems[accounts.size()] = getResources().getString(
R.string.add_account_button_label);
}
return listItems;
}
@ -439,7 +475,8 @@ public class ChooseTypeAndAccountActivity extends Activity
* allowable accounts, if provided.
*/
private ArrayList<Account> getAcceptableAccountChoices(AccountManager accountManager) {
final Account[] accounts = accountManager.getAccounts();
final Account[] accounts = accountManager.getAccountsForPackage(mCallingPackage,
mCallingUid);
ArrayList<Account> accountsToPopulate = new ArrayList<Account>(accounts.length);
for (Account account : accounts) {
if (mSetOfAllowableAccounts != null

View File

@ -31,10 +31,11 @@ interface IAccountManager {
String getUserData(in Account account, String key);
AuthenticatorDescription[] getAuthenticatorTypes();
Account[] getAccounts(String accountType);
Account[] getAccountsForPackage(String packageName, int uid);
Account[] getAccountsAsUser(String accountType, int userId);
void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features);
void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
boolean addAccount(in Account account, String password, in Bundle extras);
boolean addAccountExplicitly(in Account account, String password, in Bundle extras);
void removeAccount(in IAccountManagerResponse response, in Account account);
void invalidateAuthToken(String accountType, String authToken);
String peekAuthToken(in Account account, String authTokenType);
@ -47,7 +48,7 @@ interface IAccountManager {
void getAuthToken(in IAccountManagerResponse response, in Account account,
String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch,
in Bundle options);
void addAcount(in IAccountManagerResponse response, String accountType,
void addAccount(in IAccountManagerResponse response, String accountType,
String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
in Bundle options);
void updateCredentials(in IAccountManagerResponse response, in Account account,

View File

@ -121,6 +121,14 @@ public class UserManager {
*/
public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
private static UserManager sInstance = null;
public synchronized static UserManager get(Context context) {
if (sInstance == null) {
sInstance = (UserManager) context.getSystemService(Context.USER_SERVICE);
}
return sInstance;
}
/** @hide */
public UserManager(Context context, IUserManager service) {

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2013, 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Customizable description text -->
<TextView android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_gravity="start|center_vertical"
android:paddingTop="16dip"
android:paddingBottom="16dip"
android:paddingStart="16dip"
android:paddingEnd="16dip"
android:text="@string/app_no_restricted_accounts"
/>
<!-- Horizontal divider line -->
<View android:layout_height="1dip"
android:layout_width="match_parent"
android:background="?android:attr/dividerHorizontal" />
<!-- Alert dialog style buttons along the bottom. -->
<LinearLayout android:id="@+id/button_bar"
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:measureWithLargestChild="true">
<Button android:id="@android:id/button1"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@android:string/yes"
android:onClick="onCancelButtonClicked" />
</LinearLayout>
</LinearLayout>

View File

@ -4063,5 +4063,10 @@
<string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
<!-- Default name of the owner user [CHAR LIMIT=20] -->
<string name="owner_name" msgid="3879126011135546571">Owner</string>
<!-- Error message title [CHAR LIMIT=35] -->
<string name="error_message_title">Error</string>
<!-- Message informing user that app is not permitted to access accounts. [CHAR LIMIT=none] -->
<string name="app_no_restricted_accounts">This application does not support accounts for limited users</string>
<!-- Message informing user that the requested activity could not be found [CHAR LIMIT=none] -->
<string name="app_not_found">No application found to handle this action</string>
</resources>

View File

@ -869,7 +869,7 @@
<java-symbol type="string" name="config_chooseAccountActivity" />
<java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
<java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
<java-symbol type="string" name="error_message_title" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
<java-symbol type="plurals" name="abbrev_in_num_hours" />
@ -1121,6 +1121,7 @@
<java-symbol type="layout" name="sms_short_code_confirmation_dialog" />
<java-symbol type="layout" name="keyguard_add_widget" />
<java-symbol type="layout" name="action_bar_up_container" />
<java-symbol type="layout" name="app_not_authorized" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />

View File

@ -322,7 +322,7 @@ public class SettingsProvider extends ContentProvider {
@Override
public boolean onCreate() {
mBackupManager = new BackupManager(getContext());
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
mUserManager = UserManager.get(getContext());
setAppOps(AppOpsManager.OP_NONE, AppOpsManager.OP_WRITE_SETTINGS);
establishDbTracking(UserHandle.USER_OWNER);

View File

@ -206,8 +206,7 @@ class QuickSettings {
mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
@Override
protected Pair<String, Drawable> doInBackground(Void... params) {
final UserManager um =
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
final UserManager um = UserManager.get(mContext);
// Fall back to the UserManager nickname if we can't read the name from the local
// profile below.
@ -292,8 +291,7 @@ class QuickSettings {
@Override
public void onClick(View v) {
mBar.collapseAllPanels(true);
final UserManager um =
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
final UserManager um = UserManager.get(mContext);
if (um.getUsers(true).size() > 1) {
try {
WindowManagerGlobal.getWindowManagerService().lockNow(null);

View File

@ -58,6 +58,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@ -270,7 +271,7 @@ public class AccountManagerService
private UserManager getUserManager() {
if (mUserManager == null) {
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mUserManager = UserManager.get(mContext);
}
return mUserManager;
}
@ -542,9 +543,9 @@ public class AccountManagerService
}
@Override
public boolean addAccount(Account account, String password, Bundle extras) {
public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "addAccount: " + account
Log.v(TAG, "addAccountExplicitly: " + account
+ ", caller's uid " + Binder.getCallingUid()
+ ", pid " + Binder.getCallingPid());
}
@ -1167,7 +1168,7 @@ public class AccountManagerService
final int callingUid = getCallingUid();
clearCallingIdentity();
if (callingUid != android.os.Process.SYSTEM_UID) {
if (callingUid != Process.SYSTEM_UID) {
throw new SecurityException("can only call from system");
}
UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
@ -1395,7 +1396,7 @@ public class AccountManagerService
return id;
}
public void addAcount(final IAccountManagerResponse response, final String accountType,
public void addAccount(final IAccountManagerResponse response, final String accountType,
final String authTokenType, final String[] requiredFeatures,
final boolean expectActivityLaunch, final Bundle optionsIn) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@ -1412,7 +1413,7 @@ public class AccountManagerService
checkManageAccountsPermission();
// Is user disallowed from modifying accounts?
if (getUserManager().hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
if (!canUserModifyAccounts(Binder.getCallingUid())) {
try {
response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
"User is not allowed to add an account!");
@ -1457,7 +1458,7 @@ public class AccountManagerService
int userId) {
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
&& Binder.getCallingUid() != android.os.Process.myUid()) {
&& Binder.getCallingUid() != Process.myUid()) {
throw new SecurityException("User " + UserHandle.getCallingUserId()
+ " trying to confirm account credentials for " + userId);
}
@ -1573,7 +1574,8 @@ public class AccountManagerService
public void run() throws RemoteException {
synchronized (mAccounts.cacheLock) {
mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid);
mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
null);
}
// check whether each account matches the requested features
mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
@ -1662,7 +1664,7 @@ public class AccountManagerService
long identityToken = clearCallingIdentity();
try {
synchronized (accounts.cacheLock) {
return getAccountsFromCacheLocked(accounts, null, callingUid);
return getAccountsFromCacheLocked(accounts, null, callingUid, null);
}
} finally {
restoreCallingIdentity(identityToken);
@ -1703,7 +1705,7 @@ public class AccountManagerService
if (userAccounts == null) continue;
synchronized (userAccounts.cacheLock) {
Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
Binder.getCallingUid());
Binder.getCallingUid(), null);
for (int a = 0; a < accounts.length; a++) {
runningAccounts.add(new AccountAndUser(accounts[a], userId));
}
@ -1717,10 +1719,15 @@ public class AccountManagerService
@Override
public Account[] getAccountsAsUser(String type, int userId) {
final int callingUid = Binder.getCallingUid();
return getAccountsAsUser(type, userId, null, -1);
}
private Account[] getAccountsAsUser(String type, int userId, String callingPackage,
int packageUid) {
int callingUid = Binder.getCallingUid();
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
&& callingUid != android.os.Process.myUid()) {
&& callingUid != Process.myUid()) {
throw new SecurityException("User " + UserHandle.getCallingUserId()
+ " trying to get account for " + userId);
}
@ -1730,12 +1737,17 @@ public class AccountManagerService
+ ", caller's uid " + Binder.getCallingUid()
+ ", pid " + Binder.getCallingPid());
}
// If the original calling app was using the framework account chooser activity, we'll
// be passed in the original caller's uid here, which is what should be used for filtering.
if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
callingUid = packageUid;
}
checkReadAccountsPermission();
UserAccounts accounts = getUserAccounts(userId);
long identityToken = clearCallingIdentity();
try {
synchronized (accounts.cacheLock) {
return getAccountsFromCacheLocked(accounts, type, callingUid);
return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage);
}
} finally {
restoreCallingIdentity(identityToken);
@ -1806,6 +1818,16 @@ public class AccountManagerService
return getAccountsAsUser(type, UserHandle.getCallingUserId());
}
@Override
public Account[] getAccountsForPackage(String packageName, int uid) {
int callingUid = Binder.getCallingUid();
if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
+ callingUid + " with uid=" + uid);
}
return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid);
}
public void getAccountsByFeatures(IAccountManagerResponse response,
String type, String[] features) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@ -1825,7 +1847,7 @@ public class AccountManagerService
if (features == null || features.length == 0) {
Account[] accounts;
synchronized (userAccounts.cacheLock) {
accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid);
accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
}
Bundle result = new Bundle();
result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
@ -2348,7 +2370,7 @@ public class AccountManagerService
}
} else {
Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
android.os.Process.myUid());
Process.myUid(), null);
fout.println("Accounts: " + accounts.length);
for (Account account : accounts) {
fout.println(" " + account);
@ -2501,7 +2523,7 @@ public class AccountManagerService
private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
int callerUid) {
if (callerUid == android.os.Process.SYSTEM_UID) {
if (callerUid == Process.SYSTEM_UID) {
return true;
}
UserAccounts accounts = getUserAccountsForCaller();
@ -2554,8 +2576,10 @@ public class AccountManagerService
}
private boolean canUserModifyAccounts(int callingUid) {
if (callingUid != android.os.Process.myUid()) {
if (getUserManager().hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
if (callingUid != Process.myUid()) {
if (getUserManager().getUserRestrictions(
new UserHandle(UserHandle.getUserId(callingUid)))
.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
return false;
}
}
@ -2566,7 +2590,7 @@ public class AccountManagerService
throws RemoteException {
final int callingUid = getCallingUid();
if (callingUid != android.os.Process.SYSTEM_UID) {
if (callingUid != Process.SYSTEM_UID) {
throw new SecurityException();
}
@ -2686,9 +2710,9 @@ public class AccountManagerService
}
private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
int callingUid) {
int callingUid, String callingPackage) {
if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
|| callingUid == android.os.Process.myUid()) {
|| callingUid == Process.myUid()) {
return unfiltered;
}
if (mUserManager.getUserInfo(userAccounts.userId).isRestricted()) {
@ -2712,6 +2736,10 @@ public class AccountManagerService
PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
if (pi != null && pi.restrictedAccountType != null) {
requiredAccountType = pi.restrictedAccountType;
// If it matches the package name of the original caller, use this choice.
if (callingPackage != null && packageName.equals(callingPackage)) {
break;
}
}
}
} catch (NameNotFoundException nnfe) {
@ -2740,15 +2768,19 @@ public class AccountManagerService
}
}
/*
* packageName can be null. If not null, it should be used to filter out restricted accounts
* that the package is not allowed to access.
*/
protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
int callingUid) {
int callingUid, String callingPackage) {
if (accountType != null) {
final Account[] accounts = userAccounts.accountCache.get(accountType);
if (accounts == null) {
return EMPTY_ACCOUNT_ARRAY;
} else {
return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
callingUid);
callingUid, callingPackage);
}
} else {
int totalLength = 0;
@ -2765,7 +2797,7 @@ public class AccountManagerService
accountsOfType.length);
totalLength += accountsOfType.length;
}
return filterSharedAccounts(userAccounts, accounts, callingUid);
return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
}
}