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 import static android.security.keystore.KeyProperties.UID_SELF;
22 
23 import android.Manifest;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.app.AppOpsManager;
27 import android.app.BroadcastOptions;
28 import android.app.IntentService;
29 import android.app.admin.SecurityLog;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.StringParceledListSlice;
35 import android.hardware.security.keymint.ErrorCode;
36 import android.net.Uri;
37 import android.os.Binder;
38 import android.os.Build;
39 import android.os.IBinder;
40 import android.os.Process;
41 import android.os.UserHandle;
42 import android.security.AppUriAuthenticationPolicy;
43 import android.security.CredentialManagementApp;
44 import android.security.IKeyChainService;
45 import android.security.KeyChain;
46 import android.security.KeyStore2;
47 import android.security.keystore.KeyGenParameterSpec;
48 import android.security.keystore.KeyProperties;
49 import android.security.keystore.ParcelableKeyGenParameterSpec;
50 import android.security.keystore.StrongBoxUnavailableException;
51 import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
52 import android.system.keystore2.Domain;
53 import android.system.keystore2.KeyDescriptor;
54 import android.system.keystore2.KeyPermission;
55 import android.text.TextUtils;
56 import android.util.Base64;
57 import android.util.Log;
58 
59 import com.android.internal.annotations.GuardedBy;
60 import com.android.internal.annotations.VisibleForTesting;
61 import com.android.internal.util.Preconditions;
62 import com.android.keychain.internal.ExistingKeysProvider;
63 import com.android.keychain.internal.GrantsDatabase;
64 import com.android.org.conscrypt.TrustedCertificateStore;
65 
66 import java.io.ByteArrayInputStream;
67 import java.io.ByteArrayOutputStream;
68 import java.io.IOException;
69 import java.security.InvalidAlgorithmParameterException;
70 import java.security.Key;
71 import java.security.KeyFactory;
72 import java.security.KeyPair;
73 import java.security.KeyPairGenerator;
74 import java.security.KeyStore;
75 import java.security.KeyStoreException;
76 import java.security.NoSuchAlgorithmException;
77 import java.security.NoSuchProviderException;
78 import java.security.PrivateKey;
79 import java.security.ProviderException;
80 import java.security.UnrecoverableKeyException;
81 import java.security.cert.Certificate;
82 import java.security.cert.CertificateEncodingException;
83 import java.security.cert.CertificateException;
84 import java.security.cert.CertificateFactory;
85 import java.security.cert.X509Certificate;
86 import java.security.spec.InvalidKeySpecException;
87 import java.security.spec.PKCS8EncodedKeySpec;
88 import java.util.ArrayList;
89 import java.util.Arrays;
90 import java.util.Collection;
91 import java.util.Collections;
92 import java.util.Enumeration;
93 import java.util.HashSet;
94 import java.util.List;
95 import java.util.Map;
96 import java.util.Set;
97 
98 import javax.security.auth.x500.X500Principal;
99 
100 public class KeyChainService extends IntentService {
101 
102     private static final String TAG = "KeyChain";
103     private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
104     private final Set<Integer> ALLOWED_UIDS = Collections.unmodifiableSet(
105             new HashSet(Arrays.asList(UID_SELF, Process.WIFI_UID)));
106 
107     private static final String MSG_NOT_SYSTEM = "Not system package";
108     private static final String MSG_NOT_SYSTEM_OR_CERT_INSTALLER =
109             "Not system or cert installer package";
110     private static final String MSG_NOT_SYSTEM_OR_CRED_MNG_APP =
111             "Not system or credential management app package";
112 
113     /** created in onCreate(), closed in onDestroy() */
114     private GrantsDatabase mGrantsDb;
115     private Injector mInjector;
116     private KeyStore mKeyStore;
117     private KeyChainStateStorage mStateStorage;
118 
119     private Object mCredentialManagementAppLock = new Object();
120     @Nullable
121     @GuardedBy("mCredentialManagementAppLock")
122     private CredentialManagementApp mCredentialManagementApp;
123 
KeyChainService()124     public KeyChainService() {
125         super(KeyChainService.class.getSimpleName());
126         mInjector = new Injector();
127     }
128 
getKeyStore()129     private KeyStore getKeyStore() {
130         try {
131             final KeyStore keystore = mInjector.getKeyStoreInstance();
132             keystore.load(null);
133             return keystore;
134         } catch (KeyStoreException | IOException | NoSuchAlgorithmException
135                 | CertificateException e) {
136             Log.e(TAG, "Error opening AndroidKeyStore.", e);
137             throw new RuntimeException("Error opening AndroidKeyStore.", e);
138         }
139     }
140 
getKeyStore(boolean useWifiNamespace)141     private KeyStore getKeyStore(boolean useWifiNamespace) {
142         if (!useWifiNamespace) {
143             return mKeyStore;
144         }
145         try {
146             final KeyStore keystore = mInjector.getKeyStoreInstance();
147             keystore.load(
148                     new AndroidKeyStoreLoadStoreParameter(
149                             KeyProperties.NAMESPACE_WIFI));
150             return keystore;
151         } catch (IOException | CertificateException | KeyStoreException
152                 | NoSuchAlgorithmException e) {
153             Log.e(TAG, "Failed to open AndroidKeyStore for WI-FI namespace.", e);
154             return null;
155         }
156     }
157 
onCreate()158     @Override public void onCreate() {
159         super.onCreate();
160         mKeyStore = getKeyStore();
161         mGrantsDb = new GrantsDatabase(this, new KeyStoreAliasesProvider(mKeyStore));
162         mStateStorage = new KeyChainStateStorage(getDataDir());
163 
164         synchronized (mCredentialManagementAppLock) {
165             mCredentialManagementApp = mStateStorage.loadCredentialManagementApp();
166         }
167     }
168 
169     @Override
onDestroy()170     public void onDestroy() {
171         super.onDestroy();
172         mGrantsDb.destroy();
173         mGrantsDb = null;
174     }
175 
176     private static class KeyStoreAliasesProvider implements ExistingKeysProvider {
177         private final KeyStore mKeyStore;
178 
KeyStoreAliasesProvider(KeyStore keyStore)179         KeyStoreAliasesProvider(KeyStore keyStore) {
180             mKeyStore = keyStore;
181         }
182 
183         @Override
getExistingKeyAliases()184         public List<String> getExistingKeyAliases() {
185             final List<String> keyStoreAliases = new ArrayList<>();
186             try {
187                 final Enumeration<String> aliases = mKeyStore.aliases();
188                 while (aliases.hasMoreElements()) {
189                     final String alias = aliases.nextElement();
190                     if (mKeyStore.isKeyEntry(alias)) {
191                         keyStoreAliases.add(alias);
192                     }
193                 }
194             } catch (KeyStoreException e) {
195                 Log.e(TAG, "Error while loading entries from keystore. "
196                         + "List may be empty or incomplete.");
197             }
198 
199             return keyStoreAliases;
200         }
201     }
202 
makeKeyDescriptor(String alias)203     private KeyDescriptor makeKeyDescriptor(String alias) {
204         final KeyDescriptor key = new KeyDescriptor();
205         key.domain = Domain.APP;
206         key.nspace = KeyProperties.NAMESPACE_APPLICATION;
207         key.alias = alias;
208         key.blob = null;
209         return key;
210     }
211 
212     private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
213         private final TrustedCertificateStore mTrustedCertificateStore
214                 = new TrustedCertificateStore();
215         private final Context mContext = KeyChainService.this;
216 
217         @Override
218         public String requestPrivateKey(String alias) {
219             final CallerIdentity caller = getCaller();
220             if (!hasGrant(alias, caller)) {
221                 return null;
222             }
223 
224             final int granteeUid = caller.mUid;
225 
226             try {
227                 final KeyStore2 keyStore2 = KeyStore2.getInstance();
228                 KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias), granteeUid,
229                         KeyPermission.USE | KeyPermission.GET_INFO);
230                 return KeyChain.getGrantString(grant);
231             } catch (android.security.KeyStoreException e) {
232                 Log.e(TAG, "Failed to grant " + alias + " to uid: " + granteeUid, e);
233                 return null;
234             }
235         }
236 
237         @Override
238         public String getWifiKeyGrantAsUser(String alias) {
239             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
240 
241             if (!hasGrant(alias, Process.WIFI_UID)) {
242                 return null;
243             }
244 
245             KeyStore2 keyStore2 = KeyStore2.getInstance();
246             try {
247                 KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias),
248                         Process.WIFI_UID, KeyPermission.USE | KeyPermission.GET_INFO);
249                 return KeyStore2.makeKeystoreEngineGrantString(grant.nspace);
250             } catch (android.security.KeyStoreException e) {
251                 Log.e(TAG, "Failed to grant " + alias + " to uid: " + Process.WIFI_UID, e);
252                 return null;
253             }
254         }
255 
256         @Override public byte[] getCertificate(String alias) {
257             final CallerIdentity caller = getCaller();
258             if (!hasGrant(alias, caller) && !isSystemUid(caller)) {
259                 return null;
260             }
261             try {
262                 if (!mKeyStore.isCertificateEntry(alias)) {
263                     final Certificate cert = mKeyStore.getCertificate(alias);
264                     if (cert == null) return null;
265                     return cert.getEncoded();
266                 } else {
267                     return null;
268                 }
269             } catch (KeyStoreException | CertificateEncodingException e) {
270                 Log.e(TAG, "Failed to retrieve certificate.", e);
271                 return null;
272             }
273         }
274 
275         @Override public byte[] getCaCertificates(String alias) {
276             final CallerIdentity caller = getCaller();
277             if (!hasGrant(alias, caller) && !isSystemUid(caller)) {
278                 return null;
279             }
280             try {
281                 if (mKeyStore.isCertificateEntry(alias)) {
282                     return mKeyStore.getCertificate(alias).getEncoded();
283                 } else {
284                     final Certificate[] certs = mKeyStore.getCertificateChain(alias);
285                     if (certs == null || certs.length <= 1) return null;
286                     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
287                     for (int i = 1; i < certs.length; ++i) {
288                         outputStream.write(certs[i].getEncoded());
289                     }
290                     return outputStream.toByteArray();
291                 }
292             } catch (KeyStoreException | CertificateEncodingException | IOException e) {
293                 Log.e(TAG, "Failed to retrieve certificate(s) from AndroidKeyStore.", e);
294                 return null;
295             }
296         }
297 
298         @Override public boolean isUserSelectable(String alias) {
299             validateAlias(alias);
300             return mGrantsDb.isUserSelectable(alias);
301         }
302 
303         @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
304             validateAlias(alias);
305             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
306             Log.i(TAG, String.format("Marking certificate %s as user-selectable: %b", alias,
307                     isUserSelectable));
308             mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
309         }
310 
311         @Override public int generateKeyPair(
312                 String algorithm, ParcelableKeyGenParameterSpec parcelableSpec) {
313             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
314             final KeyGenParameterSpec spec = parcelableSpec.getSpec();
315             final String alias = spec.getKeystoreAlias();
316 
317             Log.i(TAG, String.format("About to generate key with alias %s, algorithm %s",
318                     alias, algorithm));
319 
320             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
321                 throw new IllegalArgumentException("The alias specified for the key denotes "
322                         + "a reserved value and cannot be used to name a key");
323             }
324             // Validate the alias here to avoid relying on KeyGenParameterSpec c'tor preventing
325             // the creation of a KeyGenParameterSpec instance with a non-empty alias.
326             if (TextUtils.isEmpty(alias) || spec.getUid() != UID_SELF) {
327                 Log.e(TAG, "Cannot generate key pair with empty alias or specified uid.");
328                 return KeyChain.KEY_GEN_MISSING_ALIAS;
329             }
330 
331             try {
332                 KeyPairGenerator generator = KeyPairGenerator.getInstance(
333                         algorithm, "AndroidKeyStore");
334                 // Do not prepend USER_PRIVATE_KEY to the alias because
335                 // AndroidKeyStoreKeyPairGeneratorSpi will helpfully prepend that in
336                 // generateKeyPair.
337                 generator.initialize(spec);
338                 KeyPair kp = generator.generateKeyPair();
339                 if (kp == null) {
340                     Log.e(TAG, "Key generation failed.");
341                     return KeyChain.KEY_GEN_FAILURE;
342                 }
343                 return KeyChain.KEY_GEN_SUCCESS;
344             } catch (NoSuchAlgorithmException e) {
345                 Log.e(TAG, "Invalid algorithm requested", e);
346                 return KeyChain.KEY_GEN_NO_SUCH_ALGORITHM;
347             } catch (InvalidAlgorithmParameterException e) {
348                 Log.e(TAG, "Invalid algorithm params", e);
349                 return KeyChain.KEY_GEN_INVALID_ALGORITHM_PARAMETERS;
350             } catch (NoSuchProviderException e) {
351                 Log.e(TAG, "Could not find Keystore.", e);
352                 return KeyChain.KEY_GEN_NO_KEYSTORE_PROVIDER;
353             } catch (StrongBoxUnavailableException e) {
354                 Log.e(TAG, "StrongBox unavailable.", e);
355                 return KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE;
356             } catch (ProviderException e) {
357                 Throwable t = e.getCause();
358                 if (t instanceof android.security.KeyStoreException) {
359                     if (((android.security.KeyStoreException) t).getErrorCode()
360                             == ErrorCode.CANNOT_ATTEST_IDS) {
361                         return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS;
362                     }
363                 }
364                 Log.e(TAG, "KeyStore error.", e);
365                 return KeyChain.KEY_GEN_FAILURE;
366             }
367         }
368 
369         @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate,
370                 byte[] userCertificateChain) {
371             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
372 
373             final PrivateKey privateKey;
374             try {
375                 final Key key = mKeyStore.getKey(alias, null);
376                 if (! (key instanceof PrivateKey)) {
377                     return false;
378                 }
379                 privateKey = (PrivateKey) key;
380             } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
381                 Log.e(TAG, "Failed to get private key entry.", e);
382                 return false;
383             }
384 
385             final ArrayList<Certificate> certs = new ArrayList<>();
386             try {
387                 if (userCertificate != null) {
388                     certs.add(parseCertificate(userCertificate));
389                 }
390                 if (userCertificateChain != null) {
391                     certs.addAll(parseCertificates(userCertificateChain));
392                 }
393             } catch (CertificateException e) {
394                 Log.e(TAG, "Failed to parse user certificate.", e);
395                 return false;
396             }
397 
398             final Certificate[] certsArray = certs.toArray(new Certificate[0]);
399 
400             try {
401                 // setKeyEntry with a private key loaded from AndroidKeyStore replaces
402                 // the certificate components without touching the private key if
403                 // the alias is the same as that of the private key.
404                 mKeyStore.setKeyEntry(alias, privateKey, null, certsArray);
405             } catch (KeyStoreException e) {
406                 Log.e(TAG, "Failed update key certificates.", e);
407                 return false;
408             }
409 
410             if (Log.isLoggable(TAG, Log.DEBUG)) {
411                 Log.d(TAG, String.format("Set certificate for key alias %s : user %s CA chain: %s",
412                         alias, emptyOrBase64Encoded(userCertificate),
413                         emptyOrBase64Encoded(userCertificateChain)));
414             }
415             broadcastKeychainChange();
416             broadcastLegacyStorageChange();
417             return true;
418         }
419 
420         private void validateAlias(String alias) {
421             if (alias == null) {
422                 throw new NullPointerException("alias == null");
423             }
424         }
425 
426         private boolean hasGrant(String alias, CallerIdentity caller) {
427             return hasGrant(alias, caller.mUid);
428         }
429 
430         private boolean hasGrant(String alias, int targetUid) {
431             validateAlias(alias);
432 
433             if (!mGrantsDb.hasGrant(targetUid, alias)) {
434                 Log.w(TAG, String.format(
435                         "uid %d doesn't have permission to access the requested alias %s",
436                         targetUid, alias));
437                 return false;
438             }
439 
440             return true;
441         }
442 
443         @Override public String installCaCertificate(byte[] caCertificate) {
444             final CallerIdentity caller = getCaller();
445             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
446                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
447             final String alias;
448             String subject = null;
449             final boolean isSecurityLoggingEnabled = mInjector.isSecurityLoggingEnabled();
450             final X509Certificate cert;
451             try {
452                 cert = parseCertificate(caCertificate);
453 
454                 final boolean isDebugLoggable = Log.isLoggable(TAG, Log.DEBUG);
455                 subject = cert.getSubjectX500Principal().getName(X500Principal.CANONICAL);
456                 if (isDebugLoggable) {
457                     Log.d(TAG, String.format("Installing CA certificate: %s", subject));
458                 }
459 
460                 synchronized (mTrustedCertificateStore) {
461                     mTrustedCertificateStore.installCertificate(cert);
462                     alias = mTrustedCertificateStore.getCertificateAlias(cert);
463                 }
464             } catch (IOException | CertificateException e) {
465                 Log.w(TAG, "Failed installing CA certificate", e);
466                 if (isSecurityLoggingEnabled && subject != null) {
467                     mInjector.writeSecurityEvent(
468                             TAG_CERT_AUTHORITY_INSTALLED, 0 /*result*/, subject,
469                             UserHandle.myUserId());
470                 }
471                 throw new IllegalStateException(e);
472             }
473             if (isSecurityLoggingEnabled && subject != null) {
474                 mInjector.writeSecurityEvent(
475                         TAG_CERT_AUTHORITY_INSTALLED, 1 /*result*/, subject,
476                         UserHandle.myUserId());
477             }
478 
479             // If the caller is the cert installer, install the CA certificate into KeyStore.
480             // This is a temporary solution to enable CA certificates to be used as VPN trust
481             // anchors. Ultimately, the user should explicitly choose to install the VPN trust
482             // anchor separately and independently of CA certificates, at which point this code
483             // should be removed.
484             if (CERT_INSTALLER_PACKAGE.equals(caller.mPackageName)) {
485                 try {
486                     mKeyStore.setCertificateEntry(String.format("%s %s", subject, alias), cert);
487                 } catch(KeyStoreException e) {
488                     Log.e(TAG, String.format(
489                             "Attempted installing %s (subject: %s) to KeyStore. Failed", alias,
490                             subject), e);
491                 }
492             }
493 
494             broadcastLegacyStorageChange();
495             broadcastTrustStoreChange();
496             return alias;
497         }
498 
499         /**
500          * Install a key pair to the keystore.
501          *
502          * @param privateKey The private key associated with the client certificate
503          * @param userCertificate The client certificate to be installed
504          * @param userCertificateChain The rest of the chain for the client certificate
505          * @param alias The alias under which the key pair is installed. It is invalid to pass
506          *              {@code KeyChain.KEY_ALIAS_SELECTION_DENIED}.
507          * @param uid Can be only one of two values: Either
508          *            {@code android.security.keystore.KeyProperties.UID_SELF} to indicate
509          *            installation into the current user's system Keystore instance, or {@code
510          *            Process.WIFI_UID} to indicate installation into the main user's WiFi Keystore
511          *            instance. It is only valid to pass {@code Process.WIFI_UID} to the KeyChain
512          *            service on user 0.
513          * @return Whether the operation succeeded or not.
514          */
515         @Override public boolean installKeyPair(@Nullable byte[] privateKey,
516                 @Nullable byte[] userCertificate, @Nullable byte[] userCertificateChain,
517                 String alias, int uid) {
518             final CallerIdentity caller = getCaller();
519             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
520                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
521             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
522                 throw new IllegalArgumentException("The alias specified for the key denotes "
523                         + "a reserved value and cannot be used to name a key");
524             }
525             if (!ALLOWED_UIDS.contains(uid)) {
526                 Log.e(TAG,
527                         String.format("Installing alias %s as UID %d is now allowed.", alias, uid));
528                 return false;
529             }
530 
531             if (privateKey == null && userCertificate == null && userCertificateChain == null) {
532                 Log.e(TAG, String.format("Nothing to install for alias %s", alias));
533                 return false;
534             }
535 
536             if (uid == Process.WIFI_UID && UserHandle.myUserId() != UserHandle.USER_SYSTEM) {
537                 Log.e(TAG, String.format(
538                         "Installation into the WiFi Keystore should be called from the primary "
539                                 + "user, not user %d",
540                         UserHandle.myUserId()));
541                 return false;
542             }
543 
544             if (Log.isLoggable(TAG, Log.DEBUG)) {
545                 Log.d(TAG, String.format("Installing certificate and key to alias %s to uid %d: "
546                                 + "user cert %s CA chain: %s", alias, uid,
547                                 emptyOrBase64Encoded(userCertificate),
548                                 emptyOrBase64Encoded(userCertificateChain)));
549             }
550 
551             final ArrayList<Certificate> certs = new ArrayList<>();
552             try {
553                 if (userCertificate != null) {
554                     certs.add(parseCertificate(userCertificate));
555                 }
556                 if (userCertificateChain != null) {
557                     certs.addAll(parseCertificates(userCertificateChain));
558                 }
559             } catch (CertificateException e) {
560                 Log.e(TAG, "Failed to parse user certificate.", e);
561                 return false;
562             }
563 
564             if (certs.isEmpty()) {
565                 Log.e(TAG, "Cannot install private key without public certificate.");
566                 return false;
567             }
568 
569             final Certificate[] certificates = certs.toArray(new Certificate[0]);
570 
571             final PrivateKey privateKey1;
572             try {
573                 if (privateKey != null) {
574                     final KeyFactory keyFactory =
575                             KeyFactory.getInstance(certificates[0].getPublicKey().getAlgorithm());
576                     privateKey1 = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
577                 } else {
578                     privateKey1 = null;
579                 }
580             } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
581                 Log.e(TAG, "Failed to parse private key.", e);
582                 return false;
583             }
584 
585             KeyStore keystore = getKeyStore(uid == Process.WIFI_UID);
586             if (keystore == null) {
587                 return false;
588             }
589 
590             try {
591                 if (privateKey != null) {
592                     keystore.setKeyEntry(alias, privateKey1, null, certificates);
593                 } else {
594                     if (certificates.length > 1) {
595                         Log.e(TAG,
596                                 "Cannot install key certificate chain without private key.");
597                         return false;
598                     }
599                     keystore.setCertificateEntry(alias, certificates[0]);
600                 }
601             } catch (KeyStoreException e) {
602                 Log.e(TAG, "Failed to install key pair.", e);
603             }
604 
605             broadcastKeychainChange();
606             broadcastLegacyStorageChange();
607             return true;
608         }
609 
610         @Override public boolean removeKeyPair(String alias) {
611             final CallerIdentity caller = getCaller();
612             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
613                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
614             return removeKeyPairInternal(alias);
615         }
616 
617         private boolean removeKeyPairInternal(String alias) {
618             try {
619                 mKeyStore.deleteEntry(alias);
620             } catch (KeyStoreException e) {
621                 Log.e(TAG, String.format(
622                         "Failed not remove keystore entry with alias %s", alias));
623                 return false;
624             }
625             Log.w(TAG, String.format(
626                     "WARNING: Removing alias %s, existing grants will be revoked.", alias));
627             mGrantsDb.removeAliasInformation(alias);
628             broadcastKeychainChange();
629             broadcastLegacyStorageChange();
630             return true;
631         }
632 
633         @Override public boolean containsKeyPair(String alias) {
634             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
635             try {
636                 final Key key = mKeyStore.getKey(alias, null);
637                 return key instanceof PrivateKey;
638             } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) {
639                 Log.w("Error while trying to check for key presence.", e);
640                 return false;
641             }
642         }
643 
644         private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
645             CertificateFactory cf = CertificateFactory.getInstance("X.509");
646             return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
647         }
648         private Collection<X509Certificate> parseCertificates(byte[] bytes)
649                 throws CertificateException {
650             final CertificateFactory cf = CertificateFactory.getInstance("X.509");
651             return (Collection<X509Certificate>)
652                     cf.generateCertificates(new ByteArrayInputStream(bytes));
653         }
654 
655         @Override public boolean reset() {
656             // only Settings should be able to reset
657             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
658             mGrantsDb.removeAllAliasesInformation();
659             boolean ok = true;
660             synchronized (mTrustedCertificateStore) {
661                 // delete user-installed CA certs
662                 for (String alias : mTrustedCertificateStore.aliases()) {
663                     if (TrustedCertificateStore.isUser(alias)) {
664                         if (!deleteCertificateEntry(alias)) {
665                             ok = false;
666                         }
667                     }
668                 }
669             }
670             broadcastTrustStoreChange();
671             broadcastKeychainChange();
672             broadcastLegacyStorageChange();
673             return ok;
674         }
675 
676         @Override public boolean deleteCaCertificate(String alias) {
677             // only Settings should be able to delete
678             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
679             boolean ok = true;
680             Log.i(TAG, String.format("Deleting CA certificate %s", alias));
681             synchronized (mTrustedCertificateStore) {
682                 ok = deleteCertificateEntry(alias);
683             }
684             broadcastTrustStoreChange();
685             broadcastLegacyStorageChange();
686             return ok;
687         }
688 
689         private boolean deleteCertificateEntry(String alias) {
690             String subjectForAudit = null;
691             if (mInjector.isSecurityLoggingEnabled()) {
692                 final Certificate cert = mTrustedCertificateStore.getCertificate(alias);
693                 if (cert instanceof X509Certificate) {
694                     subjectForAudit = ((X509Certificate) cert)
695                             .getSubjectX500Principal().getName(X500Principal.CANONICAL);
696                 }
697             }
698 
699             try {
700                 mTrustedCertificateStore.deleteCertificateEntry(alias);
701                 if (subjectForAudit != null) {
702                     mInjector.writeSecurityEvent(
703                             TAG_CERT_AUTHORITY_REMOVED, 1 /*result*/, subjectForAudit,
704                             UserHandle.myUserId());
705                 }
706                 return true;
707             } catch (IOException | CertificateException e) {
708                 Log.w(TAG, "Problem removing CA certificate " + alias, e);
709                 if (subjectForAudit != null) {
710                     mInjector.writeSecurityEvent(
711                             TAG_CERT_AUTHORITY_REMOVED, 0 /*result*/, subjectForAudit,
712                             UserHandle.myUserId());
713                 }
714                 return false;
715             }
716         }
717 
718         private boolean hasManageCredentialManagementAppPermission(CallerIdentity caller) {
719             return mContext.checkPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP,
720                     caller.mPid, caller.mUid) == PackageManager.PERMISSION_GRANTED;
721         }
722 
723         private boolean isCertInstaller(CallerIdentity caller) {
724             return caller.mPackageName != null
725                     && CERT_INSTALLER_PACKAGE.equals(caller.mPackageName);
726         }
727 
728         private boolean isCredentialManagementApp(CallerIdentity caller) {
729             synchronized (mCredentialManagementAppLock) {
730                 return mCredentialManagementApp != null && caller.mPackageName != null
731                         && caller.mPackageName.equals(mCredentialManagementApp.getPackageName());
732             }
733         }
734 
735         private boolean isSystemUid(CallerIdentity caller) {
736             return UserHandle.isSameApp(caller.mUid, Process.SYSTEM_UID);
737         }
738 
739         @Override public boolean hasGrant(int uid, String alias) {
740             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
741             return mGrantsDb.hasGrant(uid, alias);
742         }
743 
744         @Override public boolean setGrant(int uid, String alias, boolean granted) {
745             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
746             Preconditions.checkArgument(containsKeyPair(alias),
747                     "Alias not associated with a key.");
748             mGrantsDb.setGrant(uid, alias, granted);
749             if (!granted) {
750                 try {
751                     KeyStore2.getInstance().ungrant(makeKeyDescriptor(alias), uid);
752                 } catch (android.security.KeyStoreException e) {
753                     Log.e(TAG, "Failed to ungrant " + alias + " to uid: " + uid, e);
754                     return false;
755                 }
756             }
757             broadcastPermissionChange(uid, alias, granted);
758             broadcastLegacyStorageChange();
759             return true;
760         }
761 
762         @Override public int[] getGrants(String alias) {
763             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
764             try {
765                 if (mKeyStore.isKeyEntry(alias)) {
766                     return mGrantsDb.getGrants(alias);
767                 }
768             } catch (KeyStoreException e) {
769                 Log.w(TAG, "Error while checking if key exists.", e);
770             }
771             throw new IllegalArgumentException("Alias not found: " + alias);
772         }
773 
774         @Override
775         public StringParceledListSlice getUserCaAliases() {
776             synchronized (mTrustedCertificateStore) {
777                 return new StringParceledListSlice(new ArrayList<String>(
778                         mTrustedCertificateStore.userAliases()));
779             }
780         }
781 
782         @Override
783         public StringParceledListSlice getSystemCaAliases() {
784             synchronized (mTrustedCertificateStore) {
785                 return new StringParceledListSlice(new ArrayList<String>(
786                         mTrustedCertificateStore.allSystemAliases()));
787             }
788         }
789 
790         @Override
791         public boolean containsCaAlias(String alias) {
792             return mTrustedCertificateStore.containsAlias(alias);
793         }
794 
795         @Override
796         public byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem) {
797             synchronized (mTrustedCertificateStore) {
798                 X509Certificate certificate = (X509Certificate) mTrustedCertificateStore
799                         .getCertificate(alias, includeDeletedSystem);
800                 if (certificate == null) {
801                     Log.w(TAG, "Could not find CA certificate " + alias);
802                     return null;
803                 }
804                 try {
805                     return certificate.getEncoded();
806                 } catch (CertificateEncodingException e) {
807                     Log.w(TAG, "Error while encoding CA certificate " + alias);
808                     return null;
809                 }
810             }
811         }
812 
813         @Override
814         public List<String> getCaCertificateChainAliases(String rootAlias,
815                 boolean includeDeletedSystem) {
816             synchronized (mTrustedCertificateStore) {
817                 X509Certificate root = (X509Certificate) mTrustedCertificateStore.getCertificate(
818                         rootAlias, includeDeletedSystem);
819                 try {
820                     List<X509Certificate> chain = mTrustedCertificateStore.getCertificateChain(
821                             root);
822                     List<String> aliases = new ArrayList<String>(chain.size());
823                     final int n = chain.size();
824                     for (int i = 0; i < n; ++i) {
825                         String alias = mTrustedCertificateStore.getCertificateAlias(chain.get(i),
826                                 true);
827                         if (alias != null) {
828                             aliases.add(alias);
829                         }
830                     }
831                     return aliases;
832                 } catch (CertificateException e) {
833                     Log.w(TAG, "Error retrieving cert chain for root " + rootAlias);
834                     return Collections.emptyList();
835                 }
836             }
837         }
838 
839         @Override
840         public void setCredentialManagementApp(@NonNull String packageName,
841                 @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
842             final CallerIdentity caller = getCaller();
843             Preconditions.checkCallAuthorization(isSystemUid(caller)
844                     || hasManageCredentialManagementAppPermission(caller), MSG_NOT_SYSTEM);
845             checkValidAuthenticationPolicy(authenticationPolicy);
846 
847             synchronized (mCredentialManagementAppLock) {
848                 if (mCredentialManagementApp != null) {
849                     final String existingPackage = mCredentialManagementApp.getPackageName();
850                     if (existingPackage.equals(packageName)) {
851                         // Update existing credential management app's policy
852                         removeOrphanedKeyPairs(authenticationPolicy);
853                     } else {
854                         // Replace existing credential management app
855                         removeOrphanedKeyPairs(null);
856                         setManageCredentialsAppOps(existingPackage, false);
857                     }
858                 }
859                 setManageCredentialsAppOps(packageName, true);
860                 mCredentialManagementApp = new CredentialManagementApp(packageName,
861                         authenticationPolicy);
862                 mStateStorage.saveCredentialManagementApp(mCredentialManagementApp);
863             }
864         }
865 
866         private void setManageCredentialsAppOps(String packageName, boolean allowed) {
867             try {
868                 int mode = allowed ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_DEFAULT;
869                 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(packageName, 0);
870                 getSystemService(AppOpsManager.class).setMode(AppOpsManager.OP_MANAGE_CREDENTIALS,
871                         appInfo.uid, packageName, mode);
872             } catch (PackageManager.NameNotFoundException e) {
873                 Log.e(TAG, "Unable to find info for package: " + packageName);
874             }
875         }
876 
877         private void removeOrphanedKeyPairs(
878                 @Nullable AppUriAuthenticationPolicy newPolicy) {
879             Set<String> existingAliases = mCredentialManagementApp.getAuthenticationPolicy()
880                     .getAliases();
881             Set<String> newAliases = newPolicy != null ? newPolicy.getAliases() : new HashSet<>();
882 
883             // Uninstall all certificates that are no longer included in the new
884             // authentication policy
885             for (String existingAlias : existingAliases) {
886                 if (!newAliases.contains(existingAlias)) {
887                     removeKeyPairInternal(existingAlias);
888                 }
889             }
890         }
891 
892         private void checkValidAuthenticationPolicy(
893                 @NonNull AppUriAuthenticationPolicy authenticationPolicy) {
894             if (authenticationPolicy == null
895                     || authenticationPolicy.getAppAndUriMappings().isEmpty()) {
896                 throw new IllegalArgumentException("The authentication policy is null or empty");
897             }
898             // Check whether any of the aliases in the policy already exist
899             for (String alias : authenticationPolicy.getAliases()) {
900                 if (requestPrivateKey(alias) != null) {
901                     throw new IllegalArgumentException(String.format("The authentication policy "
902                             + "contains an installed alias: %s", alias));
903                 }
904             }
905         }
906 
907         @Override
908         public boolean hasCredentialManagementApp() {
909             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
910             synchronized (mCredentialManagementAppLock) {
911                 return mCredentialManagementApp != null;
912             }
913         }
914 
915         @Nullable
916         @Override
917         public String getCredentialManagementAppPackageName() {
918             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
919             synchronized (mCredentialManagementAppLock) {
920                 return mCredentialManagementApp != null
921                         ? mCredentialManagementApp.getPackageName()
922                         : null;
923             }
924         }
925 
926         @Nullable
927         @Override
928         public AppUriAuthenticationPolicy getCredentialManagementAppPolicy() {
929             final CallerIdentity caller = getCaller();
930             Preconditions.checkCallAuthorization(isSystemUid(caller)
931                             || isCredentialManagementApp(caller), MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
932             synchronized (mCredentialManagementAppLock) {
933                 return mCredentialManagementApp != null
934                         ? mCredentialManagementApp.getAuthenticationPolicy()
935                         : null;
936             }
937         }
938 
939         @Nullable
940         @Override
941         public String getPredefinedAliasForPackageAndUri(@NonNull String packageName,
942                 @Nullable Uri uri) {
943             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
944             synchronized (mCredentialManagementAppLock) {
945                 if (mCredentialManagementApp == null || uri == null) {
946                     return null;
947                 }
948                 Map<Uri, String> urisToAliases = mCredentialManagementApp.getAuthenticationPolicy()
949                         .getAppAndUriMappings().get(packageName);
950                 return urisToAliases != null ? urisToAliases.get(uri) : null;
951             }
952         }
953 
954         @Override
955         public void removeCredentialManagementApp() {
956             final CallerIdentity caller = getCaller();
957             Preconditions.checkCallAuthorization(isSystemUid(caller)
958                             || isCredentialManagementApp(caller)
959                             || hasManageCredentialManagementAppPermission(caller),
960                     MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
961             synchronized (mCredentialManagementAppLock) {
962                 if (mCredentialManagementApp != null) {
963                     // Remove all certificates
964                     removeOrphanedKeyPairs(null);
965                     setManageCredentialsAppOps(mCredentialManagementApp.getPackageName(), false);
966                 }
967                 mCredentialManagementApp = null;
968                 mStateStorage.saveCredentialManagementApp(mCredentialManagementApp);
969             }
970         }
971 
972         @Override
973         public boolean isCredentialManagementApp(@NonNull String packageName) {
974             final CallerIdentity caller = getCaller();
975             Preconditions.checkCallAuthorization(isSystemUid(caller)
976                     || isCredentialManagementApp(caller), MSG_NOT_SYSTEM_OR_CRED_MNG_APP);
977             synchronized (mCredentialManagementAppLock) {
978                 return packageName.equals(mCredentialManagementApp.getPackageName());
979             }
980         }
981     };
982 
onBind(Intent intent)983     @Override public IBinder onBind(Intent intent) {
984         if (IKeyChainService.class.getName().equals(intent.getAction())) {
985             return mIKeyChainService;
986         }
987         return null;
988     }
989 
990     @Override
onHandleIntent(final Intent intent)991     protected void onHandleIntent(final Intent intent) {
992         if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
993             mGrantsDb.purgeOldGrants(getPackageManager());
994         }
995     }
996 
broadcastLegacyStorageChange()997     private void broadcastLegacyStorageChange() {
998         Intent intent = new Intent(KeyChain.ACTION_STORAGE_CHANGED);
999         BroadcastOptions opts = BroadcastOptions.makeBasic();
1000         opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.N_MR1);
1001         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()), null, opts.toBundle());
1002     }
1003 
broadcastKeychainChange()1004     private void broadcastKeychainChange() {
1005         Intent intent = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
1006         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
1007     }
1008 
broadcastTrustStoreChange()1009     private void broadcastTrustStoreChange() {
1010         Intent intent = new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED);
1011         sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
1012     }
1013 
broadcastPermissionChange(int uid, String alias, boolean access)1014     private void broadcastPermissionChange(int uid, String alias, boolean access) {
1015         // Since the permission change only impacts one uid only send to that uid's packages.
1016         final PackageManager packageManager = getPackageManager();
1017         String[] packages = packageManager.getPackagesForUid(uid);
1018         if (packages == null) {
1019             return;
1020         }
1021         for (String pckg : packages) {
1022             Intent intent = new Intent(KeyChain.ACTION_KEY_ACCESS_CHANGED);
1023             intent.putExtra(KeyChain.EXTRA_KEY_ALIAS, alias);
1024             intent.putExtra(KeyChain.EXTRA_KEY_ACCESSIBLE, access);
1025             intent.setPackage(pckg);
1026             sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
1027         }
1028     }
1029 
emptyOrBase64Encoded(byte[] cert)1030     private static String emptyOrBase64Encoded(byte[] cert) {
1031         if (cert == null) {
1032             return "";
1033         }
1034         return Base64.encodeToString(cert, Base64.NO_WRAP);
1035     }
1036 
1037     private final class CallerIdentity {
1038 
1039         final int mUid;
1040         final int mPid;
1041         final String mPackageName;
1042 
CallerIdentity()1043         CallerIdentity() {
1044             mUid = mInjector.getCallingUid();
1045             mPid = Binder.getCallingPid();
1046             mPackageName = getPackageManager().getNameForUid(mUid);
1047         }
1048     }
1049 
getCaller()1050     private CallerIdentity getCaller() {
1051         return new CallerIdentity();
1052     }
1053 
1054     @VisibleForTesting
setInjector(Injector injector)1055     void setInjector(Injector injector) {
1056         mInjector = injector;
1057     }
1058 
1059     /**
1060      * Injector for mocking out dependencies in tests.
1061      */
1062     @VisibleForTesting
1063     static class Injector {
isSecurityLoggingEnabled()1064         public boolean isSecurityLoggingEnabled() {
1065             return SecurityLog.isLoggingEnabled();
1066         }
1067 
writeSecurityEvent(int tag, Object... payload)1068         public void writeSecurityEvent(int tag, Object... payload) {
1069             SecurityLog.writeEvent(tag, payload);
1070         }
1071 
getCallingUid()1072         public int getCallingUid() {
1073             return Binder.getCallingUid();
1074         }
1075 
getKeyStoreInstance()1076         public KeyStore getKeyStoreInstance() throws KeyStoreException {
1077             return KeyStore.getInstance("AndroidKeyStore");
1078         }
1079     }
1080 }
1081