Exfiltrate cert code from DevicePolicyManager
The intent is for this not to cause any behaviour changes, just to make it easier to see what is going on with the code. Permissions are checked in DevicePolicyManagerService. All calls to CertificateMonitor are privileged. Test: runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java Test: cts-tradefed run cts-dev --module CtsDevicePolicyManagerTestCases Change-Id: I98224087315a62234732f08b53fe91884be86386
This commit is contained in:
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 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.res.Resources;
|
||||
import android.graphics.Color;
|
||||
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.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 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 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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 updateInstalledCertificates(final UserHandle userHandle) {
|
||||
if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<String> installedCerts;
|
||||
try {
|
||||
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) {
|
||||
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 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 null;
|
||||
}
|
||||
|
||||
final Resources resources = mInjector.getResources();
|
||||
final int smallIconId;
|
||||
final String contentText;
|
||||
|
||||
int parentUserId = userHandle.getIdentifier();
|
||||
|
||||
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;
|
||||
} else {
|
||||
contentText = resources.getString(R.string.ssl_ca_cert_noti_by_unknown);
|
||||
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);
|
||||
dialogIntent.putExtra(Settings.EXTRA_NUMBER_OF_CERTIFICATES, pendingCertificateCount);
|
||||
dialogIntent.putExtra(Intent.EXTRA_USER_ID, userHandle.getIdentifier());
|
||||
|
||||
// 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));
|
||||
|
||||
return new Notification.Builder(userContext, SystemNotificationChannels.SECURITY)
|
||||
.setSmallIcon(smallIconId)
|
||||
.setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning,
|
||||
pendingCertificateCount))
|
||||
.setContentText(contentText)
|
||||
.setContentIntent(notifyIntent)
|
||||
.setShowWhen(false)
|
||||
.setColor(R.color.system_notification_accent_color)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static X509Certificate parseCert(byte[] certBuffer) throws CertificateException {
|
||||
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
||||
return (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(
|
||||
certBuffer));
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -398,6 +393,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
*/
|
||||
boolean mIsWatch;
|
||||
|
||||
private final CertificateMonitor mCertificateMonitor;
|
||||
private final SecurityLogMonitor mSecurityLogMonitor;
|
||||
private NetworkLogger mNetworkLogger;
|
||||
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -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,
|
||||
@ -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()
|
||||
@ -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();
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4690,7 +4664,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
}
|
||||
saveSettingsLocked(userId);
|
||||
}
|
||||
new MonitoringCertNotificationTask(this, mInjector).execute(userId);
|
||||
mCertificateMonitor.onCertificateApprovalsChanged(userId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
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));
|
||||
synchronized (this) {
|
||||
getUserData(userHandle.getIdentifier()).mOwnerInstalledCaCerts.add(alias);
|
||||
saveSettingsLocked(userHandle.getIdentifier());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@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);
|
||||
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 com.android.server.devicepolicy;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
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.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
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.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> {
|
||||
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;
|
||||
|
||||
public MonitoringCertNotificationTask(final DevicePolicyManagerService service,
|
||||
final DevicePolicyManagerService.Injector injector) {
|
||||
super();
|
||||
mService = service;
|
||||
mInjector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Integer... params) {
|
||||
int userHandle = params[0];
|
||||
|
||||
if (userHandle == UserHandle.USER_ALL) {
|
||||
for (UserInfo userInfo : mInjector.getUserManager().getUsers(true)) {
|
||||
repostOrClearNotification(userInfo.getUserHandle());
|
||||
}
|
||||
} else {
|
||||
repostOrClearNotification(UserHandle.of(userHandle));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void repostOrClearNotification(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;
|
||||
try {
|
||||
pendingCertificateCount = mService.retainAcceptedCertificates(
|
||||
userHandle, getInstalledCaCertificates(userHandle));
|
||||
} catch (RemoteException | RuntimeException e) {
|
||||
Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingCertificateCount != 0) {
|
||||
showNotification(userHandle, pendingCertificateCount);
|
||||
} 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.
|
||||
final Context userContext;
|
||||
try {
|
||||
userContext = mInjector.createContextAsUser(userHandle);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build and show a warning notification
|
||||
int smallIconId;
|
||||
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()) {
|
||||
contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
|
||||
mService.getDeviceOwnerName());
|
||||
smallIconId = R.drawable.stat_sys_certificate_info;
|
||||
} else {
|
||||
contentText = resources.getString(R.string.ssl_ca_cert_noti_by_unknown);
|
||||
smallIconId = android.R.drawable.stat_sys_warning;
|
||||
}
|
||||
|
||||
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,
|
||||
dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
|
||||
UserHandle.of(parentUserId));
|
||||
|
||||
final Notification noti =
|
||||
new Notification.Builder(userContext, SystemNotificationChannels.SECURITY)
|
||||
.setSmallIcon(smallIconId)
|
||||
.setContentTitle(resources.getQuantityText(R.plurals.ssl_ca_cert_warning,
|
||||
pendingCertificateCount))
|
||||
.setContentText(contentText)
|
||||
.setContentIntent(notifyIntent)
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,13 +17,18 @@ package com.android.server.devicepolicy;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.backup.IBackupManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.database.ContentObserver;
|
||||
import android.media.IAudioService;
|
||||
import android.net.IIpConnectivityMetrics;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManagerInternal;
|
||||
import android.os.UserHandle;
|
||||
@ -301,6 +306,12 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
|
||||
return context.userManagerForMock.isSplitSystemUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
|
||||
Intent intent, int flags, Bundle options, UserHandle user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void registerContentObserver(Uri uri, boolean notifyForDescendents,
|
||||
ContentObserver observer, int userHandle) {
|
||||
|
Reference in New Issue
Block a user