• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.keychain;
18 
19 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_INSTALLED;
20 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_REMOVED;
21 
22 import android.annotation.Nullable;
23 import android.app.BroadcastOptions;
24 import android.app.IntentService;
25 import android.app.admin.SecurityLog;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.PackageManager;
29 import android.content.pm.StringParceledListSlice;
30 import android.os.Binder;
31 import android.os.Build;
32 import android.os.IBinder;
33 import android.os.Process;
34 import android.os.UserHandle;
35 import android.security.Credentials;
36 import android.security.IKeyChainService;
37 import android.security.KeyChain;
38 import android.security.KeyStore;
39 import android.security.keymaster.KeymasterArguments;
40 import android.security.keymaster.KeymasterCertificateChain;
41 import android.security.keystore.AttestationUtils;
42 import android.security.keystore.DeviceIdAttestationException;
43 import android.security.keystore.KeyGenParameterSpec;
44 import android.security.keystore.ParcelableKeyGenParameterSpec;
45 import android.security.keystore.StrongBoxUnavailableException;
46 import android.text.TextUtils;
47 import android.util.Base64;
48 import android.util.Log;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.widget.LockPatternUtils;
52 import com.android.keychain.internal.ExistingKeysProvider;
53 import com.android.keychain.internal.GrantsDatabase;
54 import com.android.org.conscrypt.TrustedCertificateStore;
55 
56 import java.io.ByteArrayInputStream;
57 import java.io.IOException;
58 import java.security.InvalidAlgorithmParameterException;
59 import java.security.KeyPair;
60 import java.security.KeyPairGenerator;
61 import java.security.NoSuchAlgorithmException;
62 import java.security.NoSuchProviderException;
63 import java.security.cert.Certificate;
64 import java.security.cert.CertificateEncodingException;
65 import java.security.cert.CertificateException;
66 import java.security.cert.CertificateFactory;
67 import java.security.cert.X509Certificate;
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.Collections;
71 import java.util.HashSet;
72 import java.util.List;
73 import java.util.Set;
74 
75 import javax.security.auth.x500.X500Principal;
76 
77 public class KeyChainService extends IntentService {
78 
79     private static final String TAG = "KeyChain";
80     private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
81     private final Set<Integer> ALLOWED_UIDS = Collections.unmodifiableSet(
82             new HashSet(Arrays.asList(KeyStore.UID_SELF, Process.WIFI_UID)));
83 
84     /** created in onCreate(), closed in onDestroy() */
85     private GrantsDatabase mGrantsDb;
86     private Injector mInjector;
87     private final KeyStore mKeyStore = KeyStore.getInstance();
88 
KeyChainService()89     public KeyChainService() {
90         super(KeyChainService.class.getSimpleName());
91         mInjector = new Injector();
92     }
93 
onCreate()94     @Override public void onCreate() {
95         super.onCreate();
96         mGrantsDb = new GrantsDatabase(this, new KeyStoreAliasesProvider(mKeyStore));
97     }
98 
99     @Override
onDestroy()100     public void onDestroy() {
101         super.onDestroy();
102         mGrantsDb.destroy();
103         mGrantsDb = null;
104     }
105 
106     private static class KeyStoreAliasesProvider implements ExistingKeysProvider {
107         private final KeyStore mKeyStore;
108 
KeyStoreAliasesProvider(KeyStore keyStore)109         KeyStoreAliasesProvider(KeyStore keyStore) {
110             mKeyStore = keyStore;
111         }
112 
113         @Override
getExistingKeyAliases()114         public List<String> getExistingKeyAliases() {
115             List<String> aliases = new ArrayList<String>();
116             String[] keyStoreAliases = mKeyStore.list(Credentials.USER_PRIVATE_KEY);
117             if (keyStoreAliases == null) {
118                 return aliases;
119             }
120 
121             for (String alias: keyStoreAliases) {
122                 Log.w(TAG, "Got Alias from KeyStore: " + alias);
123                 String unPrefixedAlias = alias.replaceFirst("^" + Credentials.USER_PRIVATE_KEY, "");
124                 if (!unPrefixedAlias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
125                     aliases.add(unPrefixedAlias);
126                 }
127             }
128             return aliases;
129         }
130     }
131 
132     private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
133         private final TrustedCertificateStore mTrustedCertificateStore
134                 = new TrustedCertificateStore();
135         private final Context mContext = KeyChainService.this;
136 
137         @Override
138         public String requestPrivateKey(String alias) {
139             if (!hasGrant(alias)) {
140                 return null;
141             }
142 
143             final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
144             final int uid = mInjector.getCallingUid();
145             Log.i(TAG, String.format("UID %d will be granted access to %s", uid, keystoreAlias));
146             return mKeyStore.grant(keystoreAlias, uid);
147         }
148 
149         @Override public byte[] getCertificate(String alias) {
150             if (!hasGrant(alias)) {
151                 return null;
152             }
153             return mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
154         }
155 
156         @Override public byte[] getCaCertificates(String alias) {
157             if (!hasGrant(alias)) {
158                 return null;
159             }
160             return mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
161         }
162 
163         @Override public boolean isUserSelectable(String alias) {
164             validateAlias(alias);
165             return mGrantsDb.isUserSelectable(alias);
166         }
167 
168         @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
169             validateAlias(alias);
170             checkSystemCaller();
171             Log.i(TAG, String.format("Marking certificate %s as user-selectable: %b", alias,
172                     isUserSelectable));
173             mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
174         }
175 
176         @Override public int generateKeyPair(
177                 String algorithm, ParcelableKeyGenParameterSpec parcelableSpec) {
178             checkSystemCaller();
179             final KeyGenParameterSpec spec = parcelableSpec.getSpec();
180             final String alias = spec.getKeystoreAlias();
181 
182             Log.i(TAG, String.format("About to generate key with alias %s, algorithm %s",
183                     alias, algorithm));
184 
185             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
186                 throw new IllegalArgumentException("The alias specified for the key denotes "
187                         + "a reserved value and cannot be used to name a key");
188             }
189             // Validate the alias here to avoid relying on KeyGenParameterSpec c'tor preventing
190             // the creation of a KeyGenParameterSpec instance with a non-empty alias.
191             if (TextUtils.isEmpty(alias) || spec.getUid() != KeyStore.UID_SELF) {
192                 Log.e(TAG, "Cannot generate key pair with empty alias or specified uid.");
193                 return KeyChain.KEY_GEN_MISSING_ALIAS;
194             }
195 
196             if (spec.getAttestationChallenge() != null) {
197                 Log.e(TAG, "Key generation request should not include an Attestation challenge.");
198                 return KeyChain.KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE;
199             }
200 
201             if (!removeKeyPair(alias)) {
202                 Log.e(TAG, "Failed to remove previously-installed alias " + alias);
203                 //TODO: Introduce a different error code in R to distinguish the failure to remove
204                 // old keys from other failures.
205                 return KeyChain.KEY_GEN_FAILURE;
206             }
207 
208             try {
209                 KeyPairGenerator generator = KeyPairGenerator.getInstance(
210                         algorithm, "AndroidKeyStore");
211                 // Do not prepend USER_PRIVATE_KEY to the alias because
212                 // AndroidKeyStoreKeyPairGeneratorSpi will helpfully prepend that in
213                 // generateKeyPair.
214                 generator.initialize(spec);
215                 KeyPair kp = generator.generateKeyPair();
216                 if (kp == null) {
217                     Log.e(TAG, "Key generation failed.");
218                     return KeyChain.KEY_GEN_FAILURE;
219                 }
220                 return KeyChain.KEY_GEN_SUCCESS;
221             } catch (NoSuchAlgorithmException e) {
222                 Log.e(TAG, "Invalid algorithm requested", e);
223                 return KeyChain.KEY_GEN_NO_SUCH_ALGORITHM;
224             } catch (InvalidAlgorithmParameterException e) {
225                 Log.e(TAG, "Invalid algorithm params", e);
226                 return KeyChain.KEY_GEN_INVALID_ALGORITHM_PARAMETERS;
227             } catch (NoSuchProviderException e) {
228                 Log.e(TAG, "Could not find Keystore.", e);
229                 return KeyChain.KEY_GEN_NO_KEYSTORE_PROVIDER;
230             } catch (StrongBoxUnavailableException e) {
231                 Log.e(TAG, "StrongBox unavailable.", e);
232                 return KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE;
233             }
234         }
235 
236         @Override public int attestKey(
237                 String alias, byte[] attestationChallenge,
238                 int[] idAttestationFlags,
239                 KeymasterCertificateChain attestationChain) {
240             checkSystemCaller();
241             validateAlias(alias);
242 
243             if (attestationChallenge == null) {
244                 Log.e(TAG, String.format("Missing attestation challenge for alias %s", alias));
245                 return KeyChain.KEY_ATTESTATION_MISSING_CHALLENGE;
246             }
247 
248             if (Log.isLoggable(TAG, Log.DEBUG)) {
249                 Log.d(TAG, String.format("About to attest key alias %s, challenge %s, flags %s",
250                         alias, Base64.encodeToString(attestationChallenge, Base64.NO_WRAP),
251                         Arrays.toString(idAttestationFlags)));
252             }
253 
254             KeymasterArguments attestArgs;
255             try {
256                 attestArgs = AttestationUtils.prepareAttestationArguments(
257                         mContext, idAttestationFlags, attestationChallenge);
258             } catch (DeviceIdAttestationException e) {
259                 Log.e(TAG, "Failed collecting attestation data", e);
260                 return KeyChain.KEY_ATTESTATION_CANNOT_COLLECT_DATA;
261             }
262             int errorCode = checkKeyChainStatus(alias, attestationChain, attestArgs);
263             if (errorCode == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
264                 // b/69471841: id attestation might fail due to incorrect provisioning of device
265                 try {
266                     attestArgs =
267                             AttestationUtils.prepareAttestationArgumentsIfMisprovisioned(
268                             mContext, idAttestationFlags, attestationChallenge);
269                     if (attestArgs == null) {
270                         return errorCode;
271                     }
272                 } catch (DeviceIdAttestationException e) {
273                     Log.e(TAG, "Failed collecting attestation data "
274                             + "during second attempt on misprovisioned device", e);
275                     return KeyChain.KEY_ATTESTATION_CANNOT_COLLECT_DATA;
276                 }
277             }
278 
279             return checkKeyChainStatus(alias, attestationChain, attestArgs);
280         }
281 
282         private int checkKeyChainStatus(
283                 String alias,
284                 KeymasterCertificateChain attestationChain,
285                 KeymasterArguments attestArgs) {
286 
287             final String keystoreAlias = Credentials.USER_PRIVATE_KEY + alias;
288             final int errorCode = mKeyStore.attestKey(keystoreAlias, attestArgs, attestationChain);
289             if (errorCode != KeyStore.NO_ERROR) {
290                 Log.e(TAG, String.format("Failure attesting for key %s: %d", alias, errorCode));
291                 if (errorCode == KeyStore.CANNOT_ATTEST_IDS) {
292                     return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS;
293                 } else {
294                     // General failure, cannot discern which.
295                     return KeyChain.KEY_ATTESTATION_FAILURE;
296                 }
297             }
298 
299             return KeyChain.KEY_ATTESTATION_SUCCESS;
300         }
301 
302         @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate,
303                 byte[] userCertificateChain) {
304             checkSystemCaller();
305             if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate,
306                         KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
307                 Log.e(TAG, "Failed to import user certificate " + userCertificate);
308                 return false;
309             }
310 
311             if (userCertificateChain != null && userCertificateChain.length > 0) {
312                 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain,
313                             KeyStore.UID_SELF, KeyStore.FLAG_NONE)) {
314                     Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
315                     if (!mKeyStore.delete(Credentials.USER_CERTIFICATE + alias)) {
316                         Log.e(TAG, "Failed to clean up key chain after certificate chain"
317                                 + " importing failed");
318                     }
319                     return false;
320                 }
321             } else {
322                 if (!mKeyStore.delete(Credentials.CA_CERTIFICATE + alias)) {
323                     Log.e(TAG, "Failed to remove CA certificate chain for alias " + alias);
324                 }
325             }
326 
327             if (Log.isLoggable(TAG, Log.DEBUG)) {
328                 Log.d(TAG, String.format("Set certificate for key alias %s : user %s CA chain: %s",
329                         alias, emptyOrBase64Encoded(userCertificate),
330                         emptyOrBase64Encoded(userCertificateChain)));
331             }
332             broadcastKeychainChange();
333             broadcastLegacyStorageChange();
334             return true;
335         }
336 
337         private void validateAlias(String alias) {
338             if (alias == null) {
339                 throw new NullPointerException("alias == null");
340             }
341         }
342 
343         private boolean hasGrant(String alias) {
344             validateAlias(alias);
345 
346             final int callingUid = mInjector.getCallingUid();
347             if (!mGrantsDb.hasGrant(callingUid, alias)) {
348                 Log.w(TAG, String.format(
349                         "uid %d doesn't have permission to access the requested alias %s",
350                         callingUid, alias));
351                 return false;
352             }
353 
354             return true;
355         }
356 
357         @Override public String installCaCertificate(byte[] caCertificate) {
358             checkCertInstallerOrSystemCaller();
359             final String alias;
360             String subject = null;
361             final boolean isSecurityLoggingEnabled = mInjector.isSecurityLoggingEnabled();
362             try {
363                 final X509Certificate cert = parseCertificate(caCertificate);
364 
365                 final boolean isDebugLoggable = Log.isLoggable(TAG, Log.DEBUG);
366                 subject = cert.getSubjectX500Principal().getName(X500Principal.CANONICAL);
367                 if (isDebugLoggable) {
368                     Log.d(TAG, String.format("Installing CA certificate: %s", subject));
369                 }
370 
371                 synchronized (mTrustedCertificateStore) {
372                     mTrustedCertificateStore.installCertificate(cert);
373                     alias = mTrustedCertificateStore.getCertificateAlias(cert);
374                 }
375             } catch (IOException | CertificateException e) {
376                 Log.w(TAG, "Failed installing CA certificate", e);
377                 if (isSecurityLoggingEnabled && subject != null) {
378                     mInjector.writeSecurityEvent(
379                             TAG_CERT_AUTHORITY_INSTALLED, 0 /*result*/, subject,
380                             UserHandle.myUserId());
381                 }
382                 throw new IllegalStateException(e);
383             }
384             if (isSecurityLoggingEnabled && subject != null) {
385                 mInjector.writeSecurityEvent(
386                         TAG_CERT_AUTHORITY_INSTALLED, 1 /*result*/, subject,
387                         UserHandle.myUserId());
388             }
389 
390             // If the caller is the cert installer, install the CA certificate into KeyStore.
391             // This is a temporary solution to enable CA certificates to be used as VPN trust
392             // anchors. Ultimately, the user should explicitly choose to install the VPN trust
393             // anchor separately and independently of CA certificates, at which point this code
394             // should be removed.
395             if (CERT_INSTALLER_PACKAGE.equals(callingPackage())) {
396                 final boolean result = mKeyStore.put(
397                         String.format("%s%s %s", Credentials.CA_CERTIFICATE, subject, alias),
398                         caCertificate, Process.SYSTEM_UID,
399                         KeyStore.FLAG_NONE);
400                 Log.d(TAG, String.format(
401                         "Attempted installing %s (subject: %s) to KeyStore. Result: %b", alias,
402                         subject, result));
403             }
404 
405             broadcastLegacyStorageChange();
406             broadcastTrustStoreChange();
407             return alias;
408         }
409 
410         /**
411          * Install a key pair to the keystore.
412          *
413          * @param privateKey The private key associated with the client certificate
414          * @param userCertificate The client certificate to be installed
415          * @param userCertificateChain The rest of the chain for the client certificate
416          * @param alias The alias under which the key pair is installed. It is invalid to pass
417          *              {@code KeyChain.KEY_ALIAS_SELECTION_DENIED}.
418          * @param uid Can be only one of two values: Either {@code KeyStore.UID_SELF} to indicate
419          *            installation into the current user's system Keystore instance, or
420          *            {@code Process.WIFI_UID} to indicate installation into the main user's
421          *            WiFi Keystore instance. It is only valid to pass {@code Process.WIFI_UID} to
422          *            the KeyChain service on user 0.
423          * @return Whether the operation succeeded or not.
424          */
425         @Override public boolean installKeyPair(@Nullable byte[] privateKey,
426                 @Nullable byte[] userCertificate, @Nullable byte[] userCertificateChain,
427                 String alias, int uid) {
428             checkCertInstallerOrSystemCaller();
429             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
430                 throw new IllegalArgumentException("The alias specified for the key denotes "
431                         + "a reserved value and cannot be used to name a key");
432             }
433             if (!ALLOWED_UIDS.contains(uid)) {
434                 Log.e(TAG,
435                         String.format("Installing alias %s as UID %d is now allowed.", alias, uid));
436                 return false;
437             }
438 
439             if (privateKey == null && userCertificate == null && userCertificateChain == null) {
440                 Log.e(TAG, String.format("Nothing to install for alias %s", alias));
441                 return false;
442             }
443 
444             if (uid == Process.WIFI_UID && UserHandle.myUserId() != UserHandle.USER_SYSTEM) {
445                 Log.e(TAG, String.format(
446                         "Installation into the WiFi Keystore should be called from the primary "
447                                 + "user, not user %d",
448                         UserHandle.myUserId()));
449                 return false;
450             }
451 
452             if (Log.isLoggable(TAG, Log.DEBUG)) {
453                 Log.d(TAG, String.format("Installing certificate and key to alias %s to uid %d: "
454                                 + "user cert %s CA chain: %s", alias, uid,
455                                 emptyOrBase64Encoded(userCertificate),
456                                 emptyOrBase64Encoded(userCertificateChain)));
457             }
458 
459             if (!removeKeyPair(alias)) {
460                 return false;
461             }
462             if (privateKey != null && !mKeyStore.importKey(
463                     Credentials.USER_PRIVATE_KEY + alias, privateKey, uid, KeyStore.FLAG_NONE)) {
464                 Log.e(TAG, "Failed to import private key " + alias);
465                 return false;
466             }
467             if (userCertificate != null &&
468                     !mKeyStore.put(Credentials.USER_CERTIFICATE + alias, userCertificate,
469                         uid, KeyStore.FLAG_NONE)) {
470                 Log.e(TAG, "Failed to import user certificate " + userCertificate);
471                 if (!mKeyStore.delete(Credentials.USER_PRIVATE_KEY + alias)) {
472                     Log.e(TAG, "Failed to delete private key after certificate importing failed");
473                 }
474                 return false;
475             }
476             if (userCertificateChain != null && userCertificateChain.length > 0) {
477                 if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, userCertificateChain, uid,
478                         KeyStore.FLAG_NONE)) {
479                     Log.e(TAG, "Failed to import certificate chain" + userCertificateChain);
480                     if (!removeKeyPair(alias)) {
481                         Log.e(TAG, "Failed to clean up key chain after certificate chain"
482                                 + " importing failed");
483                     }
484                     return false;
485                 }
486             }
487             broadcastKeychainChange();
488             broadcastLegacyStorageChange();
489             return true;
490         }
491 
492         @Override public boolean removeKeyPair(String alias) {
493             checkCertInstallerOrSystemCaller();
494             if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
495                 return false;
496             }
497             Log.w(TAG, String.format(
498                     "WARNING: Removing alias %s, existing grants will be revoked.", alias));
499             mGrantsDb.removeAliasInformation(alias);
500             broadcastKeychainChange();
501             broadcastLegacyStorageChange();
502             return true;
503         }
504 
505         private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
506             CertificateFactory cf = CertificateFactory.getInstance("X.509");
507             return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
508         }
509 
510         @Override public boolean reset() {
511             // only Settings should be able to reset
512             checkSystemCaller();
513             mGrantsDb.removeAllAliasesInformation();
514             boolean ok = true;
515             synchronized (mTrustedCertificateStore) {
516                 // delete user-installed CA certs
517                 for (String alias : mTrustedCertificateStore.aliases()) {
518                     if (TrustedCertificateStore.isUser(alias)) {
519                         if (!deleteCertificateEntry(alias)) {
520                             ok = false;
521                         }
522                     }
523                 }
524             }
525             broadcastTrustStoreChange();
526             broadcastKeychainChange();
527             broadcastLegacyStorageChange();
528             return ok;
529         }
530 
531         @Override public boolean deleteCaCertificate(String alias) {
532             // only Settings should be able to delete
533             checkSystemCaller();
534             boolean ok = true;
535             Log.i(TAG, String.format("Deleting CA certificate %s", alias));
536             synchronized (mTrustedCertificateStore) {
537                 ok = deleteCertificateEntry(alias);
538             }
539             broadcastTrustStoreChange();
540             broadcastLegacyStorageChange();
541             return ok;
542         }
543 
544         private boolean deleteCertificateEntry(String alias) {
545             String subjectForAudit = null;
546             if (mInjector.isSecurityLoggingEnabled()) {
547                 final Certificate cert = mTrustedCertificateStore.getCertificate(alias);
548                 if (cert instanceof X509Certificate) {
549                     subjectForAudit = ((X509Certificate) cert)
550                             .getSubjectX500Principal().getName(X500Principal.CANONICAL);
551                 }
552             }
553 
554             try {
555                 mTrustedCertificateStore.deleteCertificateEntry(alias);
556                 if (subjectForAudit != null) {
557                     mInjector.writeSecurityEvent(
558                             TAG_CERT_AUTHORITY_REMOVED, 1 /*result*/, subjectForAudit,
559                             UserHandle.myUserId());
560                 }
561                 return true;
562             } catch (IOException | CertificateException e) {
563                 Log.w(TAG, "Problem removing CA certificate " + alias, e);
564                 if (subjectForAudit != null) {
565                     mInjector.writeSecurityEvent(
566                             TAG_CERT_AUTHORITY_REMOVED, 0 /*result*/, subjectForAudit,
567                             UserHandle.myUserId());
568                 }
569                 return false;
570             }
571         }
572 
573         private void checkCertInstallerOrSystemCaller() {
574             final String caller = callingPackage();
575             if (!isCallerWithSystemUid() && !CERT_INSTALLER_PACKAGE.equals(caller)) {
576                 throw new SecurityException("Not system or cert installer package: " + caller);
577             }
578         }
579 
580         private void checkSystemCaller() {
581             if (!isCallerWithSystemUid()) {
582                 throw new SecurityException("Not system package: " + callingPackage());
583             }
584         }
585 
586         private boolean isCallerWithSystemUid() {
587             return UserHandle.isSameApp(mInjector.getCallingUid(), Process.SYSTEM_UID);
588         }
589 
590         private String callingPackage() {
591             return getPackageManager().getNameForUid(mInjector.getCallingUid());
592         }
593 
594         @Override public boolean hasGrant(int uid, String alias) {
595             checkSystemCaller();
596             return mGrantsDb.hasGrant(uid, alias);
597         }
598 
599         @Override public void setGrant(int uid, String alias, boolean value) {
600             checkSystemCaller();
601             mGrantsDb.setGrant(uid, alias, value);
602             broadcastPermissionChange(uid, alias, value);
603             broadcastLegacyStorageChange();
604         }
605 
606         @Override
607         public StringParceledListSlice getUserCaAliases() {
608             synchronized (mTrustedCertificateStore) {
609                 return new StringParceledListSlice(new ArrayList<String>(
610                         mTrustedCertificateStore.userAliases()));
611             }
612         }
613 
614         @Override
615         public StringParceledListSlice getSystemCaAliases() {
616             synchronized (mTrustedCertificateStore) {
617                 return new StringParceledListSlice(new ArrayList<String>(
618                         mTrustedCertificateStore.allSystemAliases()));
619             }
620         }
621 
622         @Override
623         public boolean containsCaAlias(String alias) {
624             return mTrustedCertificateStore.containsAlias(alias);
625         }
626 
627         @Override
628         public byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem) {
629             synchronized (mTrustedCertificateStore) {
630                 X509Certificate certificate = (X509Certificate) mTrustedCertificateStore
631                         .getCertificate(alias, includeDeletedSystem);
632                 if (certificate == null) {
633                     Log.w(TAG, "Could not find CA certificate " + alias);
634                     return null;
635                 }
636                 try {
637                     return certificate.getEncoded();
638                 } catch (CertificateEncodingException e) {
639                     Log.w(TAG, "Error while encoding CA certificate " + alias);
640                     return null;
641                 }
642             }
643         }
644 
645         @Override
646         public List<String> getCaCertificateChainAliases(String rootAlias,
647                 boolean includeDeletedSystem) {
648             synchronized (mTrustedCertificateStore) {
649                 X509Certificate root = (X509Certificate) mTrustedCertificateStore.getCertificate(
650                         rootAlias, includeDeletedSystem);
651                 try {
652                     List<X509Certificate> chain = mTrustedCertificateStore.getCertificateChain(
653                             root);
654                     List<String> aliases = new ArrayList<String>(chain.size());
655                     final int n = chain.size();
656                     for (int i = 0; i < n; ++i) {
657                         String alias = mTrustedCertificateStore.getCertificateAlias(chain.get(i),
658                                 true);
659                         if (alias != null) {
660                             aliases.add(alias);
661                         }
662                     }
663                     return aliases;
664                 } catch (CertificateException e) {
665                     Log.w(TAG, "Error retrieving cert chain for root " + rootAlias);
666                     return Collections.emptyList();
667                 }
668             }
669         }
670     };
671 
onBind(Intent intent)672     @Override public IBinder onBind(Intent intent) {
673         if (IKeyChainService.class.getName().equals(intent.getAction())) {
674             return mIKeyChainService;
675         }
676         return null;
677     }
678 
679     @Override
onHandleIntent(final Intent intent)680     protected void onHandleIntent(final Intent intent) {
681         if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
682             mGrantsDb.purgeOldGrants(getPackageManager());
683         }
684     }
685 
broadcastLegacyStorageChange()686     private void broadcastLegacyStorageChange() {
687         Intent intent = new Intent(KeyChain.ACTION_STORAGE_CHANGED);
688         BroadcastOptions opts = BroadcastOptions.makeBasic();
689         opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.N_MR1);
690         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()), null, opts.toBundle());
691     }
692 
broadcastKeychainChange()693     private void broadcastKeychainChange() {
694         Intent intent = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
695         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
696     }
697 
broadcastTrustStoreChange()698     private void broadcastTrustStoreChange() {
699         Intent intent = new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED);
700         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
701     }
702 
broadcastPermissionChange(int uid, String alias, boolean access)703     private void broadcastPermissionChange(int uid, String alias, boolean access) {
704         // Since the permission change only impacts one uid only send to that uid's packages.
705         final PackageManager packageManager = getPackageManager();
706         String[] packages = packageManager.getPackagesForUid(uid);
707         if (packages == null) {
708             return;
709         }
710         for (String pckg : packages) {
711             Intent intent = new Intent(KeyChain.ACTION_KEY_ACCESS_CHANGED);
712             intent.putExtra(KeyChain.EXTRA_KEY_ALIAS, alias);
713             intent.putExtra(KeyChain.EXTRA_KEY_ACCESSIBLE, access);
714             intent.setPackage(pckg);
715             sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
716         }
717     }
718 
emptyOrBase64Encoded(byte[] cert)719     private static String emptyOrBase64Encoded(byte[] cert) {
720         if (cert == null) {
721             return "";
722         }
723         return Base64.encodeToString(cert, Base64.NO_WRAP);
724     }
725 
726     @VisibleForTesting
setInjector(Injector injector)727     void setInjector(Injector injector) {
728         mInjector = injector;
729     }
730 
731     /**
732      * Injector for mocking out dependencies in tests.
733      */
734     @VisibleForTesting
735     static class Injector {
isSecurityLoggingEnabled()736         public boolean isSecurityLoggingEnabled() {
737             return SecurityLog.isLoggingEnabled();
738         }
739 
writeSecurityEvent(int tag, Object... payload)740         public void writeSecurityEvent(int tag, Object... payload) {
741             SecurityLog.writeEvent(tag, payload);
742         }
743 
getCallingUid()744         public int getCallingUid() {
745             return Binder.getCallingUid();
746         }
747     }
748 }
749