Log audit events for root CA install/delete.

Bug: 70886042
Test: m -j RunKeyChainRoboTests
Change-Id: I3a22360a29fcb927a1fe506f2130a367576195aa
diff --git a/src/com/android/keychain/KeyChainService.java b/src/com/android/keychain/KeyChainService.java
index 4ce8378..7b778cf 100644
--- a/src/com/android/keychain/KeyChainService.java
+++ b/src/com/android/keychain/KeyChainService.java
@@ -16,36 +16,37 @@
 
 package com.android.keychain;
 
+import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_INSTALLED;
+import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_REMOVED;
+
 import android.app.BroadcastOptions;
 import android.app.IntentService;
-import android.content.ContentValues;
+import android.app.admin.SecurityLog;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.StringParceledListSlice;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
-import android.os.Process;
 import android.os.UserHandle;
 import android.security.Credentials;
 import android.security.IKeyChainService;
 import android.security.KeyChain;
+import android.security.KeyStore;
 import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keymaster.KeymasterDefs;
-import android.security.KeyStore;
 import android.security.keystore.AttestationUtils;
 import android.security.keystore.DeviceIdAttestationException;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.text.TextUtils;
 import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.keychain.internal.GrantsDatabase;
+import com.android.org.conscrypt.TrustedCertificateStore;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.security.InvalidAlgorithmParameterException;
@@ -53,26 +54,28 @@
 import java.security.KeyPairGenerator;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
-import java.security.cert.CertificateException;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import java.util.Set;
-import java.util.List;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
-import com.android.org.conscrypt.TrustedCertificateStore;
+import javax.security.auth.x500.X500Principal;
 
 public class KeyChainService extends IntentService {
 
     private static final String TAG = "KeyChain";
 
     /** created in onCreate(), closed in onDestroy() */
-    public GrantsDatabase mGrantsDb;
+    private GrantsDatabase mGrantsDb;
+    private Injector mInjector;
 
     public KeyChainService() {
         super(KeyChainService.class.getSimpleName());
+        mInjector = new Injector();
     }
 
     @Override public void onCreate() {
@@ -98,7 +101,7 @@
             checkArgs(alias);
 
             final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
-            final int uid = Binder.getCallingUid();
+            final int uid = mInjector.getCallingUid();
             return mKeyStore.grant(keystoreAlias, uid);
         }
 
@@ -241,7 +244,7 @@
             validateAlias(alias);
             validateKeyStoreState();
 
-            final int callingUid = getCallingUid();
+            final int callingUid = mInjector.getCallingUid();
             if (!mGrantsDb.hasGrant(callingUid, alias)) {
                 throw new IllegalStateException("uid " + callingUid
                         + " doesn't have permission to access the requested alias");
@@ -251,16 +254,27 @@
         @Override public String installCaCertificate(byte[] caCertificate) {
             checkCertInstallerOrSystemCaller();
             final String alias;
+            String subjectForAudit = null;
             try {
                 final X509Certificate cert = parseCertificate(caCertificate);
+                if (mInjector.isSecurityLoggingEnabled()) {
+                    subjectForAudit =
+                            cert.getSubjectX500Principal().getName(X500Principal.CANONICAL);
+                }
                 synchronized (mTrustedCertificateStore) {
                     mTrustedCertificateStore.installCertificate(cert);
                     alias = mTrustedCertificateStore.getCertificateAlias(cert);
                 }
-            } catch (IOException e) {
+            } catch (IOException | CertificateException e) {
+                if (subjectForAudit != null) {
+                    mInjector.writeSecurityEvent(
+                            TAG_CERT_AUTHORITY_INSTALLED, 0 /*result*/, subjectForAudit);
+                }
                 throw new IllegalStateException(e);
-            } catch (CertificateException e) {
-                throw new IllegalStateException(e);
+            }
+            if (subjectForAudit != null) {
+                mInjector.writeSecurityEvent(
+                        TAG_CERT_AUTHORITY_INSTALLED, 1 /*result*/, subjectForAudit);
             }
             broadcastLegacyStorageChange();
             broadcastTrustStoreChange();
@@ -366,14 +380,28 @@
         }
 
         private boolean deleteCertificateEntry(String alias) {
+            String subjectForAudit = null;
+            if (mInjector.isSecurityLoggingEnabled()) {
+                final Certificate cert = mTrustedCertificateStore.getCertificate(alias);
+                if (cert instanceof X509Certificate) {
+                    subjectForAudit = ((X509Certificate) cert)
+                            .getSubjectX500Principal().getName(X500Principal.CANONICAL);
+                }
+            }
+
             try {
                 mTrustedCertificateStore.deleteCertificateEntry(alias);
+                if (subjectForAudit != null) {
+                    mInjector.writeSecurityEvent(
+                            TAG_CERT_AUTHORITY_REMOVED, 1 /*result*/, subjectForAudit);
+                }
                 return true;
-            } catch (IOException e) {
+            } catch (IOException | CertificateException e) {
                 Log.w(TAG, "Problem removing CA certificate " + alias, e);
-                return false;
-            } catch (CertificateException e) {
-                Log.w(TAG, "Problem removing CA certificate " + alias, e);
+                if (subjectForAudit != null) {
+                    mInjector.writeSecurityEvent(
+                            TAG_CERT_AUTHORITY_REMOVED, 0 /*result*/, subjectForAudit);
+                }
                 return false;
             }
         }
@@ -395,7 +423,7 @@
          * Returns null if actually caller is expected, otherwise return bad package to report
          */
         private String checkCaller(String expectedPackage) {
-            String actualPackage = getPackageManager().getNameForUid(getCallingUid());
+            String actualPackage = getPackageManager().getNameForUid(mInjector.getCallingUid());
             return (!expectedPackage.equals(actualPackage)) ? actualPackage : null;
         }
 
@@ -523,4 +551,27 @@
             sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
         }
     }
+
+    @VisibleForTesting
+    void setInjector(Injector injector) {
+        mInjector = injector;
+    }
+
+    /**
+     * Injector for mocking out dependencies in tests.
+     */
+    @VisibleForTesting
+    static class Injector {
+        public boolean isSecurityLoggingEnabled() {
+            return SecurityLog.isLoggingEnabled();
+        }
+
+        public void writeSecurityEvent(int tag, Object... payload) {
+            SecurityLog.writeEvent(tag, payload);
+        }
+
+        public int getCallingUid() {
+            return Binder.getCallingUid();
+        }
+    }
 }