diff options
author | Jim Miller <jaggies@google.com> | 2014-07-18 19:00:02 -0700 |
---|---|---|
committer | Jim Miller <jaggies@google.com> | 2014-07-28 21:32:01 -0700 |
commit | 604e7558ef32098644b2f9456d7743a07ae789dc (patch) | |
tree | c8e5ec30a280a3a26768a0df8c5f2e75f29e0d32 /services | |
parent | 3af1afb0696fac2b3e0dd537048c0e6d11d2df03 (diff) | |
download | frameworks_base-604e7558ef32098644b2f9456d7743a07ae789dc.zip frameworks_base-604e7558ef32098644b2f9456d7743a07ae789dc.tar.gz frameworks_base-604e7558ef32098644b2f9456d7743a07ae789dc.tar.bz2 |
Add new DevicePolicyManager API to allow fine-grained TrustAgent management
This adds a new feature that allows a device admin to specify a
whitelist of features that are allowed for the given admin.
Change-Id: I83f853318efbcf72308532d0a997374f73fa9c10
Diffstat (limited to 'services')
3 files changed, 248 insertions, 21 deletions
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index 51009af..0acb82f 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -16,10 +16,14 @@ package com.android.server.trust; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.ServiceConnection; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -30,6 +34,10 @@ import android.util.Log; import android.util.Slog; import android.service.trust.ITrustAgentService; import android.service.trust.ITrustAgentServiceCallback; +import android.service.trust.TrustAgentService; + +import java.util.ArrayList; +import java.util.List; /** * A wrapper around a TrustAgentService interface. Coordinates communication between @@ -43,6 +51,7 @@ public class TrustAgentWrapper { private static final int MSG_REVOKE_TRUST = 2; private static final int MSG_TRUST_TIMEOUT = 3; private static final int MSG_RESTART_TIMEOUT = 4; + private static final int MSG_DPM_CHANGED = 5; /** * Time in uptime millis that we wait for the service connection, both when starting @@ -67,6 +76,7 @@ public class TrustAgentWrapper { // Trust state private boolean mTrusted; private CharSequence mMessage; + private boolean mTrustDisabledByDpm; private final Handler mHandler = new Handler() { @Override @@ -109,6 +119,9 @@ public class TrustAgentWrapper { unbind(); mTrustManagerService.resetAgent(mName, mUserId); break; + case MSG_DPM_CHANGED: + updateDevicePolicyFeatures(mName); + break; } } }; @@ -141,6 +154,8 @@ public class TrustAgentWrapper { mTrustAgentService = ITrustAgentService.Stub.asInterface(service); mTrustManagerService.mArchive.logAgentConnected(mUserId, name); setCallback(mCallback); + updateDevicePolicyFeatures(name); + watchForDpmChanges(true); } @Override @@ -152,9 +167,21 @@ public class TrustAgentWrapper { if (mBound) { scheduleRestart(); } + // mTrustDisabledByDpm maintains state + watchForDpmChanges(false); } }; + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED + .equals(intent.getAction())) { + mHandler.sendEmptyMessage(MSG_DPM_CHANGED); + } + } + }; public TrustAgentWrapper(Context context, TrustManagerService trustManagerService, Intent intent, UserHandle user) { @@ -196,8 +223,62 @@ public class TrustAgentWrapper { } } + private void watchForDpmChanges(boolean start) { + if (start) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + filter.addAction(Intent.ACTION_USER_REMOVED); + mContext.registerReceiver(mBroadcastReceiver, filter); + } else { + mContext.unregisterReceiver(mBroadcastReceiver); + } + } + + private boolean updateDevicePolicyFeatures(ComponentName name) { + boolean trustDisabled = false; + if (DEBUG) Slog.v(TAG, "updateDevicePolicyFeatures(" + name + ")"); + try { + if (mTrustAgentService != null) { + DevicePolicyManager dpm = + (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); + if (dpm != null) { + // If trust disabled, only enable it if the options bundle is set and + // accepted by the TrustAgent. + if ((dpm.getKeyguardDisabledFeatures(null) + & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) { + List<String> features = dpm.getTrustAgentFeaturesEnabled(null, name); + if (DEBUG) Slog.v(TAG, "Detected trust agents disabled. Features = " + features); + if (features != null && features.size() > 0) { + Bundle bundle = new Bundle(); + bundle.putStringArrayList(TrustAgentService.KEY_FEATURES, + (ArrayList<String>)features); + if (DEBUG) { + Slog.v(TAG, "TrustAgent " + name.flattenToShortString() + + " disabled except "+ features); + } + trustDisabled = mTrustAgentService.setTrustAgentFeaturesEnabled(bundle); + } else { + if (DEBUG) Slog.v(TAG, "TrustAgent " + name + " disabled by flag"); + trustDisabled = true; // trust agent should be disabled + } + } + } else { + Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?", + new IllegalStateException("Stack trace:")); + } + } + } catch (RemoteException e) { + onError(e); + } + if (mTrustDisabledByDpm != trustDisabled) { + mTrustDisabledByDpm = trustDisabled; + mTrustManagerService.updateTrust(mUserId); + } + return trustDisabled; + } + public boolean isTrusted() { - return mTrusted; + return mTrusted && !mTrustDisabledByDpm; } public CharSequence getMessage() { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 14436aa..9c2df55 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -169,7 +169,7 @@ public class TrustManagerService extends SystemService { for (UserInfo userInfo : userInfos) { int disabledFeatures = lockPatternUtils.getDevicePolicyManager() .getKeyguardDisabledFeatures(null, userInfo.id); - boolean disableTrustAgents = + final boolean disableTrustAgents = (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0; List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 25f9e9b..f49451e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -80,6 +80,7 @@ import android.security.Credentials; import android.security.IKeyChainService; import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; +import android.service.trust.TrustAgentService; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; @@ -89,6 +90,11 @@ import android.util.Xml; import android.view.IWindowManager; import org.xmlpull.v1.XmlPullParser; + +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.END_TAG; +import static org.xmlpull.v1.XmlPullParser.TEXT; + import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -111,6 +117,8 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; /** @@ -260,6 +268,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management"; private static final String TAG_ACCOUNT_TYPE = "account-type"; private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested"; + private static final String TAG_MANAGE_TRUST_AGENT_FEATURES = "manage-trust-agent-features"; + private static final String TAG_TRUST_AGENT_FEATURE = "feature"; + private static final String TAG_TRUST_AGENT_COMPONENT = "component"; private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date"; private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout"; private static final String TAG_GLOBAL_PROXY_EXCLUSION_LIST = "global-proxy-exclusion-list"; @@ -333,6 +344,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean specifiesGlobalProxy = false; String globalProxySpec = null; String globalProxyExclusionList = null; + HashMap<String, List<String>> trustAgentFeatures = new HashMap<String, List<String>>(); ActiveAdmin(DeviceAdminInfo _info) { info = _info; @@ -463,15 +475,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } out.endTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT); } + if (!trustAgentFeatures.isEmpty()) { + Set<Entry<String, List<String>>> set = trustAgentFeatures.entrySet(); + out.startTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES); + for (Entry<String, List<String>> component : set) { + out.startTag(null, TAG_TRUST_AGENT_COMPONENT); + out.attribute(null, ATTR_VALUE, component.getKey()); + for (String feature : component.getValue()) { + out.startTag(null, TAG_TRUST_AGENT_FEATURE); + out.attribute(null, ATTR_VALUE, feature); + out.endTag(null, TAG_TRUST_AGENT_FEATURE); + } + out.endTag(null, TAG_TRUST_AGENT_COMPONENT); + } + out.endTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES); + } } void readFromXml(XmlPullParser parser) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + while ((type=parser.next()) != END_DOCUMENT + && (type != END_TAG || parser.getDepth() > outerDepth)) { + if (type == END_TAG || type == TEXT) { continue; } String tag = parser.getName(); @@ -541,22 +568,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { disabledKeyguardFeatures = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) { - int outerDepthDAM = parser.getDepth(); - int typeDAM; - while ((typeDAM=parser.next()) != XmlPullParser.END_DOCUMENT - && (typeDAM != XmlPullParser.END_TAG - || parser.getDepth() > outerDepthDAM)) { - if (typeDAM == XmlPullParser.END_TAG || typeDAM == XmlPullParser.TEXT) { - continue; - } - String tagDAM = parser.getName(); - if (TAG_ACCOUNT_TYPE.equals(tagDAM)) { - accountTypesWithManagementDisabled.add( - parser.getAttributeValue(null, ATTR_VALUE)); - } else { - Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); - } - } + accountTypesWithManagementDisabled = readDisableAccountInfo(parser, tag); + } else if (TAG_MANAGE_TRUST_AGENT_FEATURES.equals(tag)) { + trustAgentFeatures = getAllTrustAgentFeatures(parser, tag); } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); } @@ -564,6 +578,68 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private Set<String> readDisableAccountInfo(XmlPullParser parser, String tag) + throws XmlPullParserException, IOException { + int outerDepthDAM = parser.getDepth(); + int typeDAM; + Set<String> result = new HashSet<String>(); + while ((typeDAM=parser.next()) != END_DOCUMENT + && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) { + if (typeDAM == END_TAG || typeDAM == TEXT) { + continue; + } + String tagDAM = parser.getName(); + if (TAG_ACCOUNT_TYPE.equals(tagDAM)) { + result.add(parser.getAttributeValue(null, ATTR_VALUE)); + } else { + Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); + } + } + return result; + } + + private HashMap<String, List<String>> getAllTrustAgentFeatures(XmlPullParser parser, + String tag) throws XmlPullParserException, IOException { + int outerDepthDAM = parser.getDepth(); + int typeDAM; + HashMap<String, List<String>> result = new HashMap<String, List<String>>(); + while ((typeDAM=parser.next()) != END_DOCUMENT + && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) { + if (typeDAM == END_TAG || typeDAM == TEXT) { + continue; + } + String tagDAM = parser.getName(); + if (TAG_TRUST_AGENT_COMPONENT.equals(tagDAM)) { + final String component = parser.getAttributeValue(null, ATTR_VALUE); + result.put(component, getTrustAgentFeatures(parser, tag)); + } else { + Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); + } + } + return result; + } + + private List<String> getTrustAgentFeatures(XmlPullParser parser, String tag) + throws XmlPullParserException, IOException { + int outerDepthDAM = parser.getDepth(); + int typeDAM; + ArrayList<String> result = new ArrayList<String>(); + while ((typeDAM=parser.next()) != END_DOCUMENT + && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) { + if (typeDAM == END_TAG || typeDAM == TEXT) { + continue; + } + String tagDAM = parser.getName(); + if (TAG_TRUST_AGENT_FEATURE.equals(tagDAM)) { + final String feature = parser.getAttributeValue(null, ATTR_VALUE); + result.add(feature); + } else { + Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); + } + } + return result; + } + void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("uid="); pw.println(getUid()); pw.print(prefix); pw.println("policies:"); @@ -3533,6 +3609,76 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent, + List<String>features, int userHandle) { + if (!mHasFeature) { + return; + } + enforceCrossUserPermission(userHandle); + enforceNotManagedProfile(userHandle, "manage trust agent features"); + synchronized (this) { + if (admin == null) { + throw new NullPointerException("admin is null"); + } + if (agent == null) { + throw new NullPointerException("agent is null"); + } + ActiveAdmin ap = getActiveAdminForCallerLocked(admin, + DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES); + ap.trustAgentFeatures.put(agent.flattenToString(), features); + saveSettingsLocked(userHandle); + syncDeviceCapabilitiesLocked(getUserData(userHandle)); + } + } + + public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent, + int userHandle) { + if (!mHasFeature) { + return null; + } + enforceCrossUserPermission(userHandle); + synchronized (this) { + if (agent == null) { + throw new NullPointerException("agent is null"); + } + final String componentName = agent.flattenToString(); + if (admin != null) { + final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle); + return (ap != null) ? ap.trustAgentFeatures.get(componentName) : null; + } + + // Return strictest policy for this user and profiles that are visible from this user. + List<UserInfo> profiles = mUserManager.getProfiles(userHandle); + List<String> result = null; + for (UserInfo userInfo : profiles) { + DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier()); + final int N = policy.mAdminList.size(); + for (int i=0; i<N; i++) { + ActiveAdmin ap = policy.mAdminList.get(i); + // Compute the intersection of all features for active admins that disable + // trust agents: + if ((ap.disabledKeyguardFeatures + & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) { + final List<String> features = ap.trustAgentFeatures.get(componentName); + if (result == null) { + if (features == null || features.size() == 0) { + result = new ArrayList<String>(); + Slog.w(LOG_TAG, "admin " + ap.info.getPackageName() + + " has null trust agent feature set; all will be disabled"); + } else { + result = new ArrayList<String>(features.size()); + result.addAll(features); + } + } else { + result.retainAll(features); + } + } + } + } + return result; + } + } + @Override public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) { synchronized (this) { |