Loading services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java→services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java +232 −0 Original line number Diff line number Diff line Loading @@ -19,100 +19,175 @@ package com.android.server.devicepolicy; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Color; import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.security.Credentials; import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; import android.util.Log; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.R; import java.util.ArrayList; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.List; import java.util.Set; public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> { public class CertificateMonitor { protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; protected static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning; private final DevicePolicyManagerService mService; private final DevicePolicyManagerService.Injector mInjector; private final Handler mHandler; public MonitoringCertNotificationTask(final DevicePolicyManagerService service, final DevicePolicyManagerService.Injector injector) { super(); public CertificateMonitor(final DevicePolicyManagerService service, final DevicePolicyManagerService.Injector injector, final Handler handler) { mService = service; mInjector = injector; mHandler = handler; // Broadcast filter for changes to the trusted certificate store. Listens on the background // handler to avoid blocking time-critical tasks on the main handler thread. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_STARTED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mInjector.mContext.registerReceiverAsUser( mRootCaReceiver, UserHandle.ALL, filter, null, mHandler); } @Override protected Void doInBackground(Integer... params) { int userHandle = params[0]; public String installCaCert(final UserHandle userHandle, byte[] certBuffer) { // Convert certificate data from X509 format to PEM. byte[] pemCert; try { X509Certificate cert = parseCert(certBuffer); pemCert = Credentials.convertToPem(cert); } catch (CertificateException | IOException ce) { Log.e(LOG_TAG, "Problem converting cert", ce); return null; } if (userHandle == UserHandle.USER_ALL) { for (UserInfo userInfo : mInjector.getUserManager().getUsers(true)) { repostOrClearNotification(userInfo.getUserHandle()); try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) { return keyChainConnection.getService().installCaCertificate(pemCert); } catch (RemoteException e) { Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); } catch (InterruptedException e1) { Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); } } else { repostOrClearNotification(UserHandle.of(userHandle)); return null; } public void uninstallCaCerts(final UserHandle userHandle, final String[] aliases) { try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) { for (int i = 0 ; i < aliases.length; i++) { keyChainConnection.getService().deleteCaCertificate(aliases[i]); } } catch (RemoteException e) { Log.e(LOG_TAG, "from CaCertUninstaller: ", e); } catch (InterruptedException ie) { Log.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); } } public List<String> getInstalledCaCertificates(UserHandle userHandle) throws RemoteException, RuntimeException { try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) { return conn.getService().getUserCaAliases().getList(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } catch (AssertionError e) { throw new RuntimeException(e); } } public void onCertificateApprovalsChanged(int userId) { mHandler.post(() -> updateInstalledCertificates(UserHandle.of(userId))); } /** * Broadcast receiver for changes to the trusted certificate store. */ private final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (StorageManager.inCryptKeeperBounce()) { return; } final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); updateInstalledCertificates(UserHandle.of(userId)); } }; private void repostOrClearNotification(UserHandle userHandle) { private void updateInstalledCertificates(final UserHandle userHandle) { if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) { return; } // Call out to KeyChain to check for CAs which are waiting for approval. final int pendingCertificateCount; final List<String> installedCerts; try { pendingCertificateCount = mService.retainAcceptedCertificates( userHandle, getInstalledCaCertificates(userHandle)); installedCerts = getInstalledCaCertificates(userHandle); } catch (RemoteException | RuntimeException e) { Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e); return; } mService.onInstalledCertificatesChanged(userHandle, installedCerts); final int pendingCertificateCount = installedCerts.size() - mService.getAcceptedCaCertificates(userHandle).size(); if (pendingCertificateCount != 0) { showNotification(userHandle, pendingCertificateCount); final Notification noti = buildNotification(userHandle, pendingCertificateCount); mInjector.getNotificationManager().notifyAsUser( LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle); } else { mInjector.getNotificationManager().cancelAsUser( LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, userHandle); } } private void showNotification(UserHandle userHandle, int pendingCertificateCount) { // Create a context for the target user. private Notification buildNotification(UserHandle userHandle, int pendingCertificateCount) { final Context userContext; try { userContext = mInjector.createContextAsUser(userHandle); } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e); return; return null; } // Build and show a warning notification int smallIconId; String contentText; final Resources resources = mInjector.getResources(); final int smallIconId; final String contentText; int parentUserId = userHandle.getIdentifier(); Resources resources = mInjector.getResources(); if (mService.getProfileOwner(userHandle.getIdentifier()) != null) { contentText = resources.getString(R.string.ssl_ca_cert_noti_managed, mService.getProfileOwnerName(userHandle.getIdentifier())); smallIconId = R.drawable.stat_sys_certificate_info; parentUserId = mService.getProfileParentId(userHandle.getIdentifier()); } else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) { final String ownerName = mService.getDeviceOwnerName(); contentText = resources.getString(R.string.ssl_ca_cert_noti_managed, mService.getDeviceOwnerName()); smallIconId = R.drawable.stat_sys_certificate_info; Loading @@ -121,19 +196,24 @@ public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Voi smallIconId = android.R.drawable.stat_sys_warning; } // Create an intent to launch an activity showing information about the certificate. Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO); dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // TODO this next line is taken from original notification code in // {@link DevicePolicyManagerService} but not a very good way of doing it. Do it better. dialogIntent.setPackage("com.android.settings"); dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, pendingCertificateCount); dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier()); PendingIntent notifyIntent = PendingIntent.getActivityAsUser(userContext, 0, // The intent should only be allowed to resolve to a system app. ActivityInfo targetInfo = dialogIntent.resolveActivityInfo( mInjector.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY); if (targetInfo != null) { dialogIntent.setComponent(targetInfo.getComponentName()); } PendingIntent notifyIntent = mInjector.pendingIntentGetActivityAsUser(userContext, 0, dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null, UserHandle.of(parentUserId)); final Notification noti = new Notification.Builder(userContext, SystemNotificationChannels.SECURITY) return new Notification.Builder(userContext, SystemNotificationChannels.SECURITY) .setSmallIcon(smallIconId) .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning, pendingCertificateCount)) Loading @@ -142,20 +222,11 @@ public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Voi .setShowWhen(false) .setColor(R.color.system_notification_accent_color) .build(); mInjector.getNotificationManager().notifyAsUser( LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle); } private List<String> getInstalledCaCertificates(UserHandle userHandle) throws RemoteException, RuntimeException { try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) { return conn.getService().getUserCaAliases().getList(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } catch (AssertionError e) { throw new RuntimeException(e); } private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream( certBuffer)); } } services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +55 −114 Original line number Diff line number Diff line Loading @@ -114,7 +114,6 @@ import android.net.Uri; import android.net.metrics.IpConnectivityLog; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Binder; import android.os.Build; import android.os.Bundle; Loading Loading @@ -184,7 +183,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; Loading @@ -193,9 +191,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.DateFormat; import java.text.NumberFormat; import java.util.ArrayList; Loading Loading @@ -398,6 +393,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ boolean mIsWatch; private final CertificateMonitor mCertificateMonitor; private final SecurityLogMonitor mSecurityLogMonitor; private NetworkLogger mNetworkLogger; Loading Loading @@ -530,19 +526,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final Handler mHandler; final Handler mBackgroundHandler; /** Listens on any device, even when mHasFeature == false. */ final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (StorageManager.inCryptKeeperBounce()) { return; } final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); new MonitoringCertNotificationTask(DevicePolicyManagerService.this, mInjector) .execute(userHandle); } }; /** Listens only if mHasFeature == true. */ final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -630,25 +613,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle); } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) { clearWipeProfileNotification(); } else if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) { mBackgroundHandler.post(() -> { try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser( UserHandle.of(userHandle))) { final List<String> caCerts = keyChainConnection.getService().getUserCaAliases().getList(); synchronized (DevicePolicyManagerService.this) { if (getUserData(userHandle).mOwnerInstalledCaCerts .retainAll(caCerts)) { saveSettingsLocked(userHandle); } } } catch (InterruptedException e) { Slog.w(LOG_TAG, "error talking to IKeyChainService", e); Thread.currentThread().interrupt(); } catch (RemoteException e) { Slog.w(LOG_TAG, "error talking to IKeyChainService", e); } }); } } Loading Loading @@ -1527,7 +1491,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @VisibleForTesting static class Injector { private final Context mContext; public final Context mContext; Injector(Context context) { mContext = context; Loading Loading @@ -1720,6 +1684,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return "/data/system/"; } PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode, @NonNull Intent intent, int flags, Bundle options, UserHandle user) { return PendingIntent.getActivityAsUser( context, requestCode, intent, flags, options, user); } void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer, int userHandle) { mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents, Loading Loading @@ -1810,6 +1780,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mLocalService = new LocalService(); mLockPatternUtils = injector.newLockPatternUtils(); // TODO: why does SecurityLogMonitor need to be created even when mHasFeature == false? mSecurityLogMonitor = new SecurityLogMonitor(this); mHasFeature = mInjector.getPackageManager() Loading @@ -1818,27 +1789,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { .hasSystemFeature(PackageManager.FEATURE_WATCH); mBackgroundHandler = BackgroundThread.getHandler(); // Broadcast filter for changes to the trusted certificate store. These changes get a // separate intent filter so we can listen to them even when device_admin is off. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_STARTED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mRootCaReceiver, UserHandle.ALL, filter, null, mHandler); // Needed when mHasFeature == false, because it controls the certificate warning text. mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler); if (!mHasFeature) { // Skip the rest of the initialization return; } filter = new IntentFilter(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BOOT_COMPLETED); filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_STARTED); filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); filter = new IntentFilter(); Loading Loading @@ -3083,33 +3047,43 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** * Remove deleted CA certificates from the "approved" list for a particular user, counting * the number still remaining to approve. * Clean up internal state when the set of installed trusted CA certificates changes. * * @param userHandle user to check for. This must be a real user and not, for example, * {@link UserHandle#ALL}. * @param installedCertificates the full set of certificate authorities currently installed for * {@param userHandle}. After calling this function, {@code mAcceptedCaCertificates} will * correspond to some subset of this. * * @return number of certificates yet to be approved by {@param userHandle}. */ protected synchronized int retainAcceptedCertificates(final UserHandle userHandle, protected void onInstalledCertificatesChanged(final UserHandle userHandle, final @NonNull Collection<String> installedCertificates) { if (!mHasFeature) { return; } enforceManageUsers(); if (!mHasFeature) { return installedCertificates.size(); } else { synchronized (this) { final DevicePolicyData policy = getUserData(userHandle.getIdentifier()); // Remove deleted certificates. Flush xml if necessary. if (policy.mAcceptedCaCertificates.retainAll(installedCertificates)) { boolean changed = false; changed |= policy.mAcceptedCaCertificates.retainAll(installedCertificates); changed |= policy.mOwnerInstalledCaCerts.retainAll(installedCertificates); if (changed) { saveSettingsLocked(userHandle.getIdentifier()); } } } // Trim approved certificates from the count. return installedCertificates.size() - policy.mAcceptedCaCertificates.size(); /** * Internal method used by {@link CertificateMonitor}. */ protected Set<String> getAcceptedCaCertificates(final UserHandle userHandle) { if (!mHasFeature) { return Collections.<String> emptySet(); } synchronized (this) { final DevicePolicyData policy = getUserData(userHandle.getIdentifier()); return policy.mAcceptedCaCertificates; } } Loading Loading @@ -4690,7 +4664,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } saveSettingsLocked(userId); } new MonitoringCertNotificationTask(this, mInjector).execute(userId); mCertificateMonitor.onCertificateApprovalsChanged(userId); return true; } Loading @@ -4713,8 +4687,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getUserData(userInfo.id).mAcceptedCaCertificates.clear(); saveSettingsLocked(userInfo.id); } new MonitoringCertNotificationTask(this, mInjector).execute(userInfo.id); mCertificateMonitor.onCertificateApprovalsChanged(userId); } } } Loading @@ -4722,79 +4695,47 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer) throws RemoteException { enforceCanManageCaCerts(admin, callerPackage); byte[] pemCert; try { X509Certificate cert = parseCert(certBuffer); pemCert = Credentials.convertToPem(cert); } catch (CertificateException ce) { Log.e(LOG_TAG, "Problem converting cert", ce); return false; } catch (IOException ioe) { Log.e(LOG_TAG, "Problem reading cert", ioe); if (!mHasFeature) { return false; } enforceCanManageCaCerts(admin, callerPackage); final UserHandle userHandle = UserHandle.of(mInjector.userHandleGetCallingUserId()); final String alias; final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); final long id = mInjector.binderClearCallingIdentity(); String alias = null; try { try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser( userHandle)) { alias = keyChainConnection.getService().installCaCertificate(pemCert); } catch (RemoteException e) { Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); alias = mCertificateMonitor.installCaCert(userHandle, certBuffer); if (alias == null) { Log.w(LOG_TAG, "Problem installing cert"); return false; } } catch (InterruptedException e1) { Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); } if (alias == null) { Log.w(LOG_TAG, "Problem installing cert"); } else { synchronized (this) { final int userId = userHandle.getIdentifier(); getUserData(userId).mOwnerInstalledCaCerts.add(alias); saveSettingsLocked(userId); getUserData(userHandle.getIdentifier()).mOwnerInstalledCaCerts.add(alias); saveSettingsLocked(userHandle.getIdentifier()); } return true; } return false; } private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream( certBuffer)); } @Override public void uninstallCaCerts(ComponentName admin, String callerPackage, String[] aliases) { if (!mHasFeature) { return; } enforceCanManageCaCerts(admin, callerPackage); final int userId = mInjector.userHandleGetCallingUserId(); final UserHandle userHandle = UserHandle.of(userId); final long id = mInjector.binderClearCallingIdentity(); try { try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser( userHandle)) { for (int i = 0 ; i < aliases.length; i++) { keyChainConnection.getService().deleteCaCertificate(aliases[i]); } } catch (RemoteException e) { Log.e(LOG_TAG, "from CaCertUninstaller: ", e); return; } } catch (InterruptedException ie) { Log.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); return; mCertificateMonitor.uninstallCaCerts(UserHandle.of(userId), aliases); } finally { mInjector.binderRestoreCallingIdentity(id); } synchronized (this) { if (getUserData(userId).mOwnerInstalledCaCerts.removeAll(Arrays.asList(aliases))) { saveSettingsLocked(userId); Loading services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +11 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/devicepolicy/java/com/android/server/devicepolicy/MonitoringCertNotificationTask.java→services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java +232 −0 Original line number Diff line number Diff line Loading @@ -19,100 +19,175 @@ package com.android.server.devicepolicy; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Color; import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.security.Credentials; import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; import android.util.Log; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.R; import java.util.ArrayList; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.List; import java.util.Set; public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> { public class CertificateMonitor { protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; protected static final int MONITORING_CERT_NOTIFICATION_ID = R.plurals.ssl_ca_cert_warning; private final DevicePolicyManagerService mService; private final DevicePolicyManagerService.Injector mInjector; private final Handler mHandler; public MonitoringCertNotificationTask(final DevicePolicyManagerService service, final DevicePolicyManagerService.Injector injector) { super(); public CertificateMonitor(final DevicePolicyManagerService service, final DevicePolicyManagerService.Injector injector, final Handler handler) { mService = service; mInjector = injector; mHandler = handler; // Broadcast filter for changes to the trusted certificate store. Listens on the background // handler to avoid blocking time-critical tasks on the main handler thread. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_STARTED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mInjector.mContext.registerReceiverAsUser( mRootCaReceiver, UserHandle.ALL, filter, null, mHandler); } @Override protected Void doInBackground(Integer... params) { int userHandle = params[0]; public String installCaCert(final UserHandle userHandle, byte[] certBuffer) { // Convert certificate data from X509 format to PEM. byte[] pemCert; try { X509Certificate cert = parseCert(certBuffer); pemCert = Credentials.convertToPem(cert); } catch (CertificateException | IOException ce) { Log.e(LOG_TAG, "Problem converting cert", ce); return null; } if (userHandle == UserHandle.USER_ALL) { for (UserInfo userInfo : mInjector.getUserManager().getUsers(true)) { repostOrClearNotification(userInfo.getUserHandle()); try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) { return keyChainConnection.getService().installCaCertificate(pemCert); } catch (RemoteException e) { Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); } catch (InterruptedException e1) { Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); } } else { repostOrClearNotification(UserHandle.of(userHandle)); return null; } public void uninstallCaCerts(final UserHandle userHandle, final String[] aliases) { try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) { for (int i = 0 ; i < aliases.length; i++) { keyChainConnection.getService().deleteCaCertificate(aliases[i]); } } catch (RemoteException e) { Log.e(LOG_TAG, "from CaCertUninstaller: ", e); } catch (InterruptedException ie) { Log.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); } } public List<String> getInstalledCaCertificates(UserHandle userHandle) throws RemoteException, RuntimeException { try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) { return conn.getService().getUserCaAliases().getList(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } catch (AssertionError e) { throw new RuntimeException(e); } } public void onCertificateApprovalsChanged(int userId) { mHandler.post(() -> updateInstalledCertificates(UserHandle.of(userId))); } /** * Broadcast receiver for changes to the trusted certificate store. */ private final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (StorageManager.inCryptKeeperBounce()) { return; } final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); updateInstalledCertificates(UserHandle.of(userId)); } }; private void repostOrClearNotification(UserHandle userHandle) { private void updateInstalledCertificates(final UserHandle userHandle) { if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) { return; } // Call out to KeyChain to check for CAs which are waiting for approval. final int pendingCertificateCount; final List<String> installedCerts; try { pendingCertificateCount = mService.retainAcceptedCertificates( userHandle, getInstalledCaCertificates(userHandle)); installedCerts = getInstalledCaCertificates(userHandle); } catch (RemoteException | RuntimeException e) { Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e); return; } mService.onInstalledCertificatesChanged(userHandle, installedCerts); final int pendingCertificateCount = installedCerts.size() - mService.getAcceptedCaCertificates(userHandle).size(); if (pendingCertificateCount != 0) { showNotification(userHandle, pendingCertificateCount); final Notification noti = buildNotification(userHandle, pendingCertificateCount); mInjector.getNotificationManager().notifyAsUser( LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle); } else { mInjector.getNotificationManager().cancelAsUser( LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, userHandle); } } private void showNotification(UserHandle userHandle, int pendingCertificateCount) { // Create a context for the target user. private Notification buildNotification(UserHandle userHandle, int pendingCertificateCount) { final Context userContext; try { userContext = mInjector.createContextAsUser(userHandle); } catch (PackageManager.NameNotFoundException e) { Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e); return; return null; } // Build and show a warning notification int smallIconId; String contentText; final Resources resources = mInjector.getResources(); final int smallIconId; final String contentText; int parentUserId = userHandle.getIdentifier(); Resources resources = mInjector.getResources(); if (mService.getProfileOwner(userHandle.getIdentifier()) != null) { contentText = resources.getString(R.string.ssl_ca_cert_noti_managed, mService.getProfileOwnerName(userHandle.getIdentifier())); smallIconId = R.drawable.stat_sys_certificate_info; parentUserId = mService.getProfileParentId(userHandle.getIdentifier()); } else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) { final String ownerName = mService.getDeviceOwnerName(); contentText = resources.getString(R.string.ssl_ca_cert_noti_managed, mService.getDeviceOwnerName()); smallIconId = R.drawable.stat_sys_certificate_info; Loading @@ -121,19 +196,24 @@ public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Voi smallIconId = android.R.drawable.stat_sys_warning; } // Create an intent to launch an activity showing information about the certificate. Intent dialogIntent = new Intent(Settings.ACTION_MONITORING_CERT_INFO); dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // TODO this next line is taken from original notification code in // {@link DevicePolicyManagerService} but not a very good way of doing it. Do it better. dialogIntent.setPackage("com.android.settings"); dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, pendingCertificateCount); dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier()); PendingIntent notifyIntent = PendingIntent.getActivityAsUser(userContext, 0, // The intent should only be allowed to resolve to a system app. ActivityInfo targetInfo = dialogIntent.resolveActivityInfo( mInjector.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY); if (targetInfo != null) { dialogIntent.setComponent(targetInfo.getComponentName()); } PendingIntent notifyIntent = mInjector.pendingIntentGetActivityAsUser(userContext, 0, dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null, UserHandle.of(parentUserId)); final Notification noti = new Notification.Builder(userContext, SystemNotificationChannels.SECURITY) return new Notification.Builder(userContext, SystemNotificationChannels.SECURITY) .setSmallIcon(smallIconId) .setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning, pendingCertificateCount)) Loading @@ -142,20 +222,11 @@ public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Voi .setShowWhen(false) .setColor(R.color.system_notification_accent_color) .build(); mInjector.getNotificationManager().notifyAsUser( LOG_TAG, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle); } private List<String> getInstalledCaCertificates(UserHandle userHandle) throws RemoteException, RuntimeException { try (KeyChainConnection conn = mInjector.keyChainBindAsUser(userHandle)) { return conn.getService().getUserCaAliases().getList(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } catch (AssertionError e) { throw new RuntimeException(e); } private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream( certBuffer)); } }
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +55 −114 Original line number Diff line number Diff line Loading @@ -114,7 +114,6 @@ import android.net.Uri; import android.net.metrics.IpConnectivityLog; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Binder; import android.os.Build; import android.os.Bundle; Loading Loading @@ -184,7 +183,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; Loading @@ -193,9 +191,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.DateFormat; import java.text.NumberFormat; import java.util.ArrayList; Loading Loading @@ -398,6 +393,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ boolean mIsWatch; private final CertificateMonitor mCertificateMonitor; private final SecurityLogMonitor mSecurityLogMonitor; private NetworkLogger mNetworkLogger; Loading Loading @@ -530,19 +526,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final Handler mHandler; final Handler mBackgroundHandler; /** Listens on any device, even when mHasFeature == false. */ final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (StorageManager.inCryptKeeperBounce()) { return; } final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); new MonitoringCertNotificationTask(DevicePolicyManagerService.this, mInjector) .execute(userHandle); } }; /** Listens only if mHasFeature == true. */ final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -630,25 +613,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle); } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) { clearWipeProfileNotification(); } else if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) { mBackgroundHandler.post(() -> { try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser( UserHandle.of(userHandle))) { final List<String> caCerts = keyChainConnection.getService().getUserCaAliases().getList(); synchronized (DevicePolicyManagerService.this) { if (getUserData(userHandle).mOwnerInstalledCaCerts .retainAll(caCerts)) { saveSettingsLocked(userHandle); } } } catch (InterruptedException e) { Slog.w(LOG_TAG, "error talking to IKeyChainService", e); Thread.currentThread().interrupt(); } catch (RemoteException e) { Slog.w(LOG_TAG, "error talking to IKeyChainService", e); } }); } } Loading Loading @@ -1527,7 +1491,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @VisibleForTesting static class Injector { private final Context mContext; public final Context mContext; Injector(Context context) { mContext = context; Loading Loading @@ -1720,6 +1684,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return "/data/system/"; } PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode, @NonNull Intent intent, int flags, Bundle options, UserHandle user) { return PendingIntent.getActivityAsUser( context, requestCode, intent, flags, options, user); } void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer, int userHandle) { mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents, Loading Loading @@ -1810,6 +1780,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mLocalService = new LocalService(); mLockPatternUtils = injector.newLockPatternUtils(); // TODO: why does SecurityLogMonitor need to be created even when mHasFeature == false? mSecurityLogMonitor = new SecurityLogMonitor(this); mHasFeature = mInjector.getPackageManager() Loading @@ -1818,27 +1789,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { .hasSystemFeature(PackageManager.FEATURE_WATCH); mBackgroundHandler = BackgroundThread.getHandler(); // Broadcast filter for changes to the trusted certificate store. These changes get a // separate intent filter so we can listen to them even when device_admin is off. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_STARTED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mRootCaReceiver, UserHandle.ALL, filter, null, mHandler); // Needed when mHasFeature == false, because it controls the certificate warning text. mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler); if (!mHasFeature) { // Skip the rest of the initialization return; } filter = new IntentFilter(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BOOT_COMPLETED); filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_STARTED); filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); filter = new IntentFilter(); Loading Loading @@ -3083,33 +3047,43 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** * Remove deleted CA certificates from the "approved" list for a particular user, counting * the number still remaining to approve. * Clean up internal state when the set of installed trusted CA certificates changes. * * @param userHandle user to check for. This must be a real user and not, for example, * {@link UserHandle#ALL}. * @param installedCertificates the full set of certificate authorities currently installed for * {@param userHandle}. After calling this function, {@code mAcceptedCaCertificates} will * correspond to some subset of this. * * @return number of certificates yet to be approved by {@param userHandle}. */ protected synchronized int retainAcceptedCertificates(final UserHandle userHandle, protected void onInstalledCertificatesChanged(final UserHandle userHandle, final @NonNull Collection<String> installedCertificates) { if (!mHasFeature) { return; } enforceManageUsers(); if (!mHasFeature) { return installedCertificates.size(); } else { synchronized (this) { final DevicePolicyData policy = getUserData(userHandle.getIdentifier()); // Remove deleted certificates. Flush xml if necessary. if (policy.mAcceptedCaCertificates.retainAll(installedCertificates)) { boolean changed = false; changed |= policy.mAcceptedCaCertificates.retainAll(installedCertificates); changed |= policy.mOwnerInstalledCaCerts.retainAll(installedCertificates); if (changed) { saveSettingsLocked(userHandle.getIdentifier()); } } } // Trim approved certificates from the count. return installedCertificates.size() - policy.mAcceptedCaCertificates.size(); /** * Internal method used by {@link CertificateMonitor}. */ protected Set<String> getAcceptedCaCertificates(final UserHandle userHandle) { if (!mHasFeature) { return Collections.<String> emptySet(); } synchronized (this) { final DevicePolicyData policy = getUserData(userHandle.getIdentifier()); return policy.mAcceptedCaCertificates; } } Loading Loading @@ -4690,7 +4664,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } saveSettingsLocked(userId); } new MonitoringCertNotificationTask(this, mInjector).execute(userId); mCertificateMonitor.onCertificateApprovalsChanged(userId); return true; } Loading @@ -4713,8 +4687,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getUserData(userInfo.id).mAcceptedCaCertificates.clear(); saveSettingsLocked(userInfo.id); } new MonitoringCertNotificationTask(this, mInjector).execute(userInfo.id); mCertificateMonitor.onCertificateApprovalsChanged(userId); } } } Loading @@ -4722,79 +4695,47 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer) throws RemoteException { enforceCanManageCaCerts(admin, callerPackage); byte[] pemCert; try { X509Certificate cert = parseCert(certBuffer); pemCert = Credentials.convertToPem(cert); } catch (CertificateException ce) { Log.e(LOG_TAG, "Problem converting cert", ce); return false; } catch (IOException ioe) { Log.e(LOG_TAG, "Problem reading cert", ioe); if (!mHasFeature) { return false; } enforceCanManageCaCerts(admin, callerPackage); final UserHandle userHandle = UserHandle.of(mInjector.userHandleGetCallingUserId()); final String alias; final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); final long id = mInjector.binderClearCallingIdentity(); String alias = null; try { try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser( userHandle)) { alias = keyChainConnection.getService().installCaCertificate(pemCert); } catch (RemoteException e) { Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); alias = mCertificateMonitor.installCaCert(userHandle, certBuffer); if (alias == null) { Log.w(LOG_TAG, "Problem installing cert"); return false; } } catch (InterruptedException e1) { Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); } if (alias == null) { Log.w(LOG_TAG, "Problem installing cert"); } else { synchronized (this) { final int userId = userHandle.getIdentifier(); getUserData(userId).mOwnerInstalledCaCerts.add(alias); saveSettingsLocked(userId); getUserData(userHandle.getIdentifier()).mOwnerInstalledCaCerts.add(alias); saveSettingsLocked(userHandle.getIdentifier()); } return true; } return false; } private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream( certBuffer)); } @Override public void uninstallCaCerts(ComponentName admin, String callerPackage, String[] aliases) { if (!mHasFeature) { return; } enforceCanManageCaCerts(admin, callerPackage); final int userId = mInjector.userHandleGetCallingUserId(); final UserHandle userHandle = UserHandle.of(userId); final long id = mInjector.binderClearCallingIdentity(); try { try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser( userHandle)) { for (int i = 0 ; i < aliases.length; i++) { keyChainConnection.getService().deleteCaCertificate(aliases[i]); } } catch (RemoteException e) { Log.e(LOG_TAG, "from CaCertUninstaller: ", e); return; } } catch (InterruptedException ie) { Log.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); return; mCertificateMonitor.uninstallCaCerts(UserHandle.of(userId), aliases); } finally { mInjector.binderRestoreCallingIdentity(id); } synchronized (this) { if (getUserData(userId).mOwnerInstalledCaCerts.removeAll(Arrays.asList(aliases))) { saveSettingsLocked(userId); Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +11 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes