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.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.internal.widget.LockPatternUtils;
63 import com.android.keychain.internal.ExistingKeysProvider;
64 import com.android.keychain.internal.GrantsDatabase;
65 import com.android.org.conscrypt.TrustedCertificateStore;
66 
67 import java.io.ByteArrayInputStream;
68 import java.io.ByteArrayOutputStream;
69 import java.io.IOException;
70 import java.security.InvalidAlgorithmParameterException;
71 import java.security.Key;
72 import java.security.KeyFactory;
73 import java.security.KeyPair;
74 import java.security.KeyPairGenerator;
75 import java.security.KeyStore;
76 import java.security.KeyStoreException;
77 import java.security.NoSuchAlgorithmException;
78 import java.security.NoSuchProviderException;
79 import java.security.PrivateKey;
80 import java.security.ProviderException;
81 import java.security.UnrecoverableKeyException;
82 import java.security.cert.Certificate;
83 import java.security.cert.CertificateEncodingException;
84 import java.security.cert.CertificateException;
85 import java.security.cert.CertificateFactory;
86 import java.security.cert.X509Certificate;
87 import java.security.spec.InvalidKeySpecException;
88 import java.security.spec.PKCS8EncodedKeySpec;
89 import java.util.ArrayList;
90 import java.util.Arrays;
91 import java.util.Collection;
92 import java.util.Collections;
93 import java.util.Enumeration;
94 import java.util.HashSet;
95 import java.util.List;
96 import java.util.Map;
97 import java.util.Set;
98 
99 import javax.security.auth.x500.X500Principal;
100 
101 public class KeyChainService extends IntentService {
102 
103     private static final String TAG = "KeyChain";
104     private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
105     private final Set<Integer> ALLOWED_UIDS = Collections.unmodifiableSet(
106             new HashSet(Arrays.asList(UID_SELF, Process.WIFI_UID)));
107 
108     private static final String MSG_NOT_SYSTEM = "Not system package";
109     private static final String MSG_NOT_SYSTEM_OR_CERT_INSTALLER =
110             "Not system or cert installer package";
111     private static final String MSG_NOT_SYSTEM_OR_CRED_MNG_APP =
112             "Not system or credential management app package";
113 
114     /** created in onCreate(), closed in onDestroy() */
115     private GrantsDatabase mGrantsDb;
116     private Injector mInjector;
117     private KeyStore mKeyStore;
118     private KeyChainStateStorage mStateStorage;
119 
120     private Object mCredentialManagementAppLock = new Object();
121     @Nullable
122     @GuardedBy("mCredentialManagementAppLock")
123     private CredentialManagementApp mCredentialManagementApp;
124 
KeyChainService()125     public KeyChainService() {
126         super(KeyChainService.class.getSimpleName());
127         mInjector = new Injector();
128     }
129 
getKeyStore()130     private KeyStore getKeyStore() {
131         try {
132             final KeyStore keystore = mInjector.getKeyStoreInstance();
133             keystore.load(null);
134             return keystore;
135         } catch (KeyStoreException | IOException | NoSuchAlgorithmException
136                 | CertificateException e) {
137             Log.e(TAG, "Error opening AndroidKeyStore.", e);
138             throw new RuntimeException("Error opening AndroidKeyStore.", e);
139         }
140     }
141 
getKeyStore(boolean useWifiNamespace)142     private KeyStore getKeyStore(boolean useWifiNamespace) {
143         if (!useWifiNamespace) {
144             return mKeyStore;
145         }
146         try {
147             final KeyStore keystore = mInjector.getKeyStoreInstance();
148             keystore.load(
149                     new AndroidKeyStoreLoadStoreParameter(
150                             KeyProperties.NAMESPACE_WIFI));
151             return keystore;
152         } catch (IOException | CertificateException | KeyStoreException
153                 | NoSuchAlgorithmException e) {
154             Log.e(TAG, "Failed to open AndroidKeyStore for WI-FI namespace.", e);
155             return null;
156         }
157     }
158 
onCreate()159     @Override public void onCreate() {
160         super.onCreate();
161         mKeyStore = getKeyStore();
162         mGrantsDb = new GrantsDatabase(this, new KeyStoreAliasesProvider(mKeyStore));
163         mStateStorage = new KeyChainStateStorage(getDataDir());
164 
165         synchronized (mCredentialManagementAppLock) {
166             mCredentialManagementApp = mStateStorage.loadCredentialManagementApp();
167         }
168     }
169 
170     @Override
onDestroy()171     public void onDestroy() {
172         super.onDestroy();
173         mGrantsDb.destroy();
174         mGrantsDb = null;
175     }
176 
177     private static class KeyStoreAliasesProvider implements ExistingKeysProvider {
178         private final KeyStore mKeyStore;
179 
KeyStoreAliasesProvider(KeyStore keyStore)180         KeyStoreAliasesProvider(KeyStore keyStore) {
181             mKeyStore = keyStore;
182         }
183 
184         @Override
getExistingKeyAliases()185         public List<String> getExistingKeyAliases() {
186             final List<String> keyStoreAliases = new ArrayList<>();
187             try {
188                 final Enumeration<String> aliases = mKeyStore.aliases();
189                 while (aliases.hasMoreElements()) {
190                     final String alias = aliases.nextElement();
191                     if (!alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
192                         if (mKeyStore.isKeyEntry(alias)) {
193                             keyStoreAliases.add(alias);
194                         }
195                     }
196                 }
197             } catch (KeyStoreException e) {
198                 Log.e(TAG, "Error while loading entries from keystore. "
199                         + "List may be empty or incomplete.");
200             }
201 
202             return keyStoreAliases;
203         }
204     }
205 
makeKeyDescriptor(String alias)206     private KeyDescriptor makeKeyDescriptor(String alias) {
207         final KeyDescriptor key = new KeyDescriptor();
208         key.domain = Domain.APP;
209         key.nspace = KeyProperties.NAMESPACE_APPLICATION;
210         key.alias = alias;
211         key.blob = null;
212         return key;
213     }
214 
215     private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
216         private final TrustedCertificateStore mTrustedCertificateStore
217                 = new TrustedCertificateStore();
218         private final Context mContext = KeyChainService.this;
219 
220         @Override
221         public String requestPrivateKey(String alias) {
222             final CallerIdentity caller = getCaller();
223             if (!hasGrant(alias, caller)) {
224                 return null;
225             }
226 
227             final int granteeUid = caller.mUid;
228 
229             try {
230                 final KeyStore2 keyStore2 = KeyStore2.getInstance();
231                 KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias), granteeUid,
232                         KeyPermission.USE | KeyPermission.GET_INFO);
233                 return KeyChain.getGrantString(grant);
234             } catch (android.security.KeyStoreException e) {
235                 Log.e(TAG, "Failed to grant " + alias + " to uid: " + granteeUid, e);
236                 return null;
237             }
238         }
239 
240         @Override
241         public String getWifiKeyGrantAsUser(String alias) {
242             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
243 
244             if (!hasGrant(alias, Process.WIFI_UID)) {
245                 return null;
246             }
247 
248             KeyStore2 keyStore2 = KeyStore2.getInstance();
249             try {
250                 KeyDescriptor grant = keyStore2.grant(makeKeyDescriptor(alias),
251                         Process.WIFI_UID, KeyPermission.USE | KeyPermission.GET_INFO);
252                 return KeyStore2.makeKeystoreEngineGrantString(grant.nspace);
253             } catch (android.security.KeyStoreException e) {
254                 Log.e(TAG, "Failed to grant " + alias + " to uid: " + Process.WIFI_UID, e);
255                 return null;
256             }
257         }
258 
259         @Override public byte[] getCertificate(String alias) {
260             final CallerIdentity caller = getCaller();
261             if (!hasGrant(alias, caller) && !isSystemUid(caller)) {
262                 return null;
263             }
264             try {
265                 if (!mKeyStore.isCertificateEntry(alias)) {
266                     final Certificate cert = mKeyStore.getCertificate(alias);
267                     if (cert == null) return null;
268                     return cert.getEncoded();
269                 } else {
270                     return null;
271                 }
272             } catch (KeyStoreException | CertificateEncodingException e) {
273                 Log.e(TAG, "Failed to retrieve certificate.", e);
274                 return null;
275             }
276         }
277 
278         @Override public byte[] getCaCertificates(String alias) {
279             final CallerIdentity caller = getCaller();
280             if (!hasGrant(alias, caller) && !isSystemUid(caller)) {
281                 return null;
282             }
283             try {
284                 if (mKeyStore.isCertificateEntry(alias)) {
285                     return mKeyStore.getCertificate(alias).getEncoded();
286                 } else {
287                     final Certificate[] certs = mKeyStore.getCertificateChain(alias);
288                     if (certs == null || certs.length <= 1) return null;
289                     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
290                     for (int i = 1; i < certs.length; ++i) {
291                         outputStream.write(certs[i].getEncoded());
292                     }
293                     return outputStream.toByteArray();
294                 }
295             } catch (KeyStoreException | CertificateEncodingException | IOException e) {
296                 Log.e(TAG, "Failed to retrieve certificate(s) from AndroidKeyStore.", e);
297                 return null;
298             }
299         }
300 
301         @Override public boolean isUserSelectable(String alias) {
302             validateAlias(alias);
303             return mGrantsDb.isUserSelectable(alias);
304         }
305 
306         @Override public void setUserSelectable(String alias, boolean isUserSelectable) {
307             validateAlias(alias);
308             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
309             Log.i(TAG, String.format("Marking certificate %s as user-selectable: %b", alias,
310                     isUserSelectable));
311             mGrantsDb.setIsUserSelectable(alias, isUserSelectable);
312         }
313 
314         @Override public int generateKeyPair(
315                 String algorithm, ParcelableKeyGenParameterSpec parcelableSpec) {
316             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
317             final KeyGenParameterSpec spec = parcelableSpec.getSpec();
318             final String alias = spec.getKeystoreAlias();
319 
320             Log.i(TAG, String.format("About to generate key with alias %s, algorithm %s",
321                     alias, algorithm));
322 
323             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
324                 throw new IllegalArgumentException("The alias specified for the key denotes "
325                         + "a reserved value and cannot be used to name a key");
326             }
327             // Validate the alias here to avoid relying on KeyGenParameterSpec c'tor preventing
328             // the creation of a KeyGenParameterSpec instance with a non-empty alias.
329             if (TextUtils.isEmpty(alias) || spec.getUid() != UID_SELF) {
330                 Log.e(TAG, "Cannot generate key pair with empty alias or specified uid.");
331                 return KeyChain.KEY_GEN_MISSING_ALIAS;
332             }
333 
334             try {
335                 KeyPairGenerator generator = KeyPairGenerator.getInstance(
336                         algorithm, "AndroidKeyStore");
337                 // Do not prepend USER_PRIVATE_KEY to the alias because
338                 // AndroidKeyStoreKeyPairGeneratorSpi will helpfully prepend that in
339                 // generateKeyPair.
340                 generator.initialize(spec);
341                 KeyPair kp = generator.generateKeyPair();
342                 if (kp == null) {
343                     Log.e(TAG, "Key generation failed.");
344                     return KeyChain.KEY_GEN_FAILURE;
345                 }
346                 return KeyChain.KEY_GEN_SUCCESS;
347             } catch (NoSuchAlgorithmException e) {
348                 Log.e(TAG, "Invalid algorithm requested", e);
349                 return KeyChain.KEY_GEN_NO_SUCH_ALGORITHM;
350             } catch (InvalidAlgorithmParameterException e) {
351                 Log.e(TAG, "Invalid algorithm params", e);
352                 return KeyChain.KEY_GEN_INVALID_ALGORITHM_PARAMETERS;
353             } catch (NoSuchProviderException e) {
354                 Log.e(TAG, "Could not find Keystore.", e);
355                 return KeyChain.KEY_GEN_NO_KEYSTORE_PROVIDER;
356             } catch (StrongBoxUnavailableException e) {
357                 Log.e(TAG, "StrongBox unavailable.", e);
358                 return KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE;
359             } catch (ProviderException e) {
360                 Throwable t = e.getCause();
361                 if (t instanceof android.security.KeyStoreException) {
362                     if (((android.security.KeyStoreException) t).getErrorCode()
363                             == ErrorCode.CANNOT_ATTEST_IDS) {
364                         return KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS;
365                     }
366                 }
367                 Log.e(TAG, "KeyStore error.", e);
368                 return KeyChain.KEY_GEN_FAILURE;
369             }
370         }
371 
372         @Override public boolean setKeyPairCertificate(String alias, byte[] userCertificate,
373                 byte[] userCertificateChain) {
374             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
375 
376             final PrivateKey privateKey;
377             try {
378                 final Key key = mKeyStore.getKey(alias, null);
379                 if (! (key instanceof PrivateKey)) {
380                     return false;
381                 }
382                 privateKey = (PrivateKey) key;
383             } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
384                 Log.e(TAG, "Failed to get private key entry.", e);
385                 return false;
386             }
387 
388             final ArrayList<Certificate> certs = new ArrayList<>();
389             try {
390                 if (userCertificate != null) {
391                     certs.add(parseCertificate(userCertificate));
392                 }
393                 if (userCertificateChain != null) {
394                     certs.addAll(parseCertificates(userCertificateChain));
395                 }
396             } catch (CertificateException e) {
397                 Log.e(TAG, "Failed to parse user certificate.", e);
398                 return false;
399             }
400 
401             final Certificate[] certsArray = certs.toArray(new Certificate[0]);
402 
403             try {
404                 // setKeyEntry with a private key loaded from AndroidKeyStore replaces
405                 // the certificate components without touching the private key if
406                 // the alias is the same as that of the private key.
407                 mKeyStore.setKeyEntry(alias, privateKey, null, certsArray);
408             } catch (KeyStoreException e) {
409                 Log.e(TAG, "Failed update key certificates.", e);
410                 return false;
411             }
412 
413             if (Log.isLoggable(TAG, Log.DEBUG)) {
414                 Log.d(TAG, String.format("Set certificate for key alias %s : user %s CA chain: %s",
415                         alias, emptyOrBase64Encoded(userCertificate),
416                         emptyOrBase64Encoded(userCertificateChain)));
417             }
418             broadcastKeychainChange();
419             broadcastLegacyStorageChange();
420             return true;
421         }
422 
423         private void validateAlias(String alias) {
424             if (alias == null) {
425                 throw new NullPointerException("alias == null");
426             }
427         }
428 
429         private boolean hasGrant(String alias, CallerIdentity caller) {
430             return hasGrant(alias, caller.mUid);
431         }
432 
433         private boolean hasGrant(String alias, int targetUid) {
434             validateAlias(alias);
435 
436             if (!mGrantsDb.hasGrant(targetUid, alias)) {
437                 Log.w(TAG, String.format(
438                         "uid %d doesn't have permission to access the requested alias %s",
439                         targetUid, alias));
440                 return false;
441             }
442 
443             return true;
444         }
445 
446         @Override public String installCaCertificate(byte[] caCertificate) {
447             final CallerIdentity caller = getCaller();
448             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
449                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
450             final String alias;
451             String subject = null;
452             final boolean isSecurityLoggingEnabled = mInjector.isSecurityLoggingEnabled();
453             final X509Certificate cert;
454             try {
455                 cert = parseCertificate(caCertificate);
456 
457                 final boolean isDebugLoggable = Log.isLoggable(TAG, Log.DEBUG);
458                 subject = cert.getSubjectX500Principal().getName(X500Principal.CANONICAL);
459                 if (isDebugLoggable) {
460                     Log.d(TAG, String.format("Installing CA certificate: %s", subject));
461                 }
462 
463                 synchronized (mTrustedCertificateStore) {
464                     mTrustedCertificateStore.installCertificate(cert);
465                     alias = mTrustedCertificateStore.getCertificateAlias(cert);
466                 }
467             } catch (IOException | CertificateException e) {
468                 Log.w(TAG, "Failed installing CA certificate", e);
469                 if (isSecurityLoggingEnabled && subject != null) {
470                     mInjector.writeSecurityEvent(
471                             TAG_CERT_AUTHORITY_INSTALLED, 0 /*result*/, subject,
472                             UserHandle.myUserId());
473                 }
474                 throw new IllegalStateException(e);
475             }
476             if (isSecurityLoggingEnabled && subject != null) {
477                 mInjector.writeSecurityEvent(
478                         TAG_CERT_AUTHORITY_INSTALLED, 1 /*result*/, subject,
479                         UserHandle.myUserId());
480             }
481 
482             // If the caller is the cert installer, install the CA certificate into KeyStore.
483             // This is a temporary solution to enable CA certificates to be used as VPN trust
484             // anchors. Ultimately, the user should explicitly choose to install the VPN trust
485             // anchor separately and independently of CA certificates, at which point this code
486             // should be removed.
487             if (CERT_INSTALLER_PACKAGE.equals(caller.mPackageName)) {
488                 try {
489                     mKeyStore.setCertificateEntry(String.format("%s %s", subject, alias), cert);
490                 } catch(KeyStoreException e) {
491                     Log.e(TAG, String.format(
492                             "Attempted installing %s (subject: %s) to KeyStore. Failed", alias,
493                             subject), e);
494                 }
495             }
496 
497             broadcastLegacyStorageChange();
498             broadcastTrustStoreChange();
499             return alias;
500         }
501 
502         /**
503          * Install a key pair to the keystore.
504          *
505          * @param privateKey The private key associated with the client certificate
506          * @param userCertificate The client certificate to be installed
507          * @param userCertificateChain The rest of the chain for the client certificate
508          * @param alias The alias under which the key pair is installed. It is invalid to pass
509          *              {@code KeyChain.KEY_ALIAS_SELECTION_DENIED}.
510          * @param uid Can be only one of two values: Either
511          *            {@code android.security.KeyStore.UID_SELF} to indicate installation into the
512          *            current user's system Keystore instance, or {@code Process.WIFI_UID} to
513          *            indicate installation into the main user's WiFi Keystore instance. It is only
514          *            valid to pass {@code Process.WIFI_UID} to the KeyChain service on user 0.
515          * @return Whether the operation succeeded or not.
516          */
517         @Override public boolean installKeyPair(@Nullable byte[] privateKey,
518                 @Nullable byte[] userCertificate, @Nullable byte[] userCertificateChain,
519                 String alias, int uid) {
520             final CallerIdentity caller = getCaller();
521             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
522                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
523             if (KeyChain.KEY_ALIAS_SELECTION_DENIED.equals(alias)) {
524                 throw new IllegalArgumentException("The alias specified for the key denotes "
525                         + "a reserved value and cannot be used to name a key");
526             }
527             if (!ALLOWED_UIDS.contains(uid)) {
528                 Log.e(TAG,
529                         String.format("Installing alias %s as UID %d is now allowed.", alias, uid));
530                 return false;
531             }
532 
533             if (privateKey == null && userCertificate == null && userCertificateChain == null) {
534                 Log.e(TAG, String.format("Nothing to install for alias %s", alias));
535                 return false;
536             }
537 
538             if (uid == Process.WIFI_UID && UserHandle.myUserId() != UserHandle.USER_SYSTEM) {
539                 Log.e(TAG, String.format(
540                         "Installation into the WiFi Keystore should be called from the primary "
541                                 + "user, not user %d",
542                         UserHandle.myUserId()));
543                 return false;
544             }
545 
546             if (Log.isLoggable(TAG, Log.DEBUG)) {
547                 Log.d(TAG, String.format("Installing certificate and key to alias %s to uid %d: "
548                                 + "user cert %s CA chain: %s", alias, uid,
549                                 emptyOrBase64Encoded(userCertificate),
550                                 emptyOrBase64Encoded(userCertificateChain)));
551             }
552 
553             final ArrayList<Certificate> certs = new ArrayList<>();
554             try {
555                 if (userCertificate != null) {
556                     certs.add(parseCertificate(userCertificate));
557                 }
558                 if (userCertificateChain != null) {
559                     certs.addAll(parseCertificates(userCertificateChain));
560                 }
561             } catch (CertificateException e) {
562                 Log.e(TAG, "Failed to parse user certificate.", e);
563                 return false;
564             }
565 
566             if (certs.isEmpty()) {
567                 Log.e(TAG, "Cannot install private key without public certificate.");
568                 return false;
569             }
570 
571             final Certificate[] certificates = certs.toArray(new Certificate[0]);
572 
573             final PrivateKey privateKey1;
574             try {
575                 if (privateKey != null) {
576                     final KeyFactory keyFactory =
577                             KeyFactory.getInstance(certificates[0].getPublicKey().getAlgorithm());
578                     privateKey1 = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
579                 } else {
580                     privateKey1 = null;
581                 }
582             } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
583                 Log.e(TAG, "Failed to parse private key.", e);
584                 return false;
585             }
586 
587             KeyStore keystore = getKeyStore(uid == Process.WIFI_UID);
588             if (keystore == null) {
589                 return false;
590             }
591 
592             try {
593                 if (privateKey != null) {
594                     keystore.setKeyEntry(alias, privateKey1, null, certificates);
595                 } else {
596                     if (certificates.length > 1) {
597                         Log.e(TAG,
598                                 "Cannot install key certificate chain without private key.");
599                         return false;
600                     }
601                     keystore.setCertificateEntry(alias, certificates[0]);
602                 }
603             } catch (KeyStoreException e) {
604                 Log.e(TAG, "Failed to install key pair.", e);
605             }
606 
607             broadcastKeychainChange();
608             broadcastLegacyStorageChange();
609             return true;
610         }
611 
612         @Override public boolean removeKeyPair(String alias) {
613             final CallerIdentity caller = getCaller();
614             Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
615                     MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
616             return removeKeyPairInternal(alias);
617         }
618 
619         private boolean removeKeyPairInternal(String alias) {
620             try {
621                 mKeyStore.deleteEntry(alias);
622             } catch (KeyStoreException e) {
623                 Log.e(TAG, String.format(
624                         "Failed not remove keystore entry with alias %s", alias));
625                 return false;
626             }
627             Log.w(TAG, String.format(
628                     "WARNING: Removing alias %s, existing grants will be revoked.", alias));
629             mGrantsDb.removeAliasInformation(alias);
630             broadcastKeychainChange();
631             broadcastLegacyStorageChange();
632             return true;
633         }
634 
635         @Override public boolean containsKeyPair(String alias) {
636             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
637             try {
638                 final Key key = mKeyStore.getKey(alias, null);
639                 return key instanceof PrivateKey;
640             } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) {
641                 Log.w("Error while trying to check for key presence.", e);
642                 return false;
643             }
644         }
645 
646         private X509Certificate parseCertificate(byte[] bytes) throws CertificateException {
647             CertificateFactory cf = CertificateFactory.getInstance("X.509");
648             return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
649         }
650         private Collection<X509Certificate> parseCertificates(byte[] bytes)
651                 throws CertificateException {
652             final CertificateFactory cf = CertificateFactory.getInstance("X.509");
653             return (Collection<X509Certificate>)
654                     cf.generateCertificates(new ByteArrayInputStream(bytes));
655         }
656 
657         @Override public boolean reset() {
658             // only Settings should be able to reset
659             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
660             mGrantsDb.removeAllAliasesInformation();
661             boolean ok = true;
662             synchronized (mTrustedCertificateStore) {
663                 // delete user-installed CA certs
664                 for (String alias : mTrustedCertificateStore.aliases()) {
665                     if (TrustedCertificateStore.isUser(alias)) {
666                         if (!deleteCertificateEntry(alias)) {
667                             ok = false;
668                         }
669                     }
670                 }
671             }
672             broadcastTrustStoreChange();
673             broadcastKeychainChange();
674             broadcastLegacyStorageChange();
675             return ok;
676         }
677 
678         @Override public boolean deleteCaCertificate(String alias) {
679             // only Settings should be able to delete
680             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
681             boolean ok = true;
682             Log.i(TAG, String.format("Deleting CA certificate %s", alias));
683             synchronized (mTrustedCertificateStore) {
684                 ok = deleteCertificateEntry(alias);
685             }
686             broadcastTrustStoreChange();
687             broadcastLegacyStorageChange();
688             return ok;
689         }
690 
691         private boolean deleteCertificateEntry(String alias) {
692             String subjectForAudit = null;
693             if (mInjector.isSecurityLoggingEnabled()) {
694                 final Certificate cert = mTrustedCertificateStore.getCertificate(alias);
695                 if (cert instanceof X509Certificate) {
696                     subjectForAudit = ((X509Certificate) cert)
697                             .getSubjectX500Principal().getName(X500Principal.CANONICAL);
698                 }
699             }
700 
701             try {
702                 mTrustedCertificateStore.deleteCertificateEntry(alias);
703                 if (subjectForAudit != null) {
704                     mInjector.writeSecurityEvent(
705                             TAG_CERT_AUTHORITY_REMOVED, 1 /*result*/, subjectForAudit,
706                             UserHandle.myUserId());
707                 }
708                 return true;
709             } catch (IOException | CertificateException e) {
710                 Log.w(TAG, "Problem removing CA certificate " + alias, e);
711                 if (subjectForAudit != null) {
712                     mInjector.writeSecurityEvent(
713                             TAG_CERT_AUTHORITY_REMOVED, 0 /*result*/, subjectForAudit,
714                             UserHandle.myUserId());
715                 }
716                 return false;
717             }
718         }
719 
720         private boolean hasManageCredentialManagementAppPermission(CallerIdentity caller) {
721             return mContext.checkPermission(Manifest.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP,
722                     caller.mPid, caller.mUid) == PackageManager.PERMISSION_GRANTED;
723         }
724 
725         private boolean isCertInstaller(CallerIdentity caller) {
726             return caller.mPackageName != null
727                     && CERT_INSTALLER_PACKAGE.equals(caller.mPackageName);
728         }
729 
730         private boolean isCredentialManagementApp(CallerIdentity caller) {
731             synchronized (mCredentialManagementAppLock) {
732                 return mCredentialManagementApp != null && caller.mPackageName != null
733                         && caller.mPackageName.equals(mCredentialManagementApp.getPackageName());
734             }
735         }
736 
737         private boolean isSystemUid(CallerIdentity caller) {
738             return UserHandle.isSameApp(caller.mUid, Process.SYSTEM_UID);
739         }
740 
741         @Override public boolean hasGrant(int uid, String alias) {
742             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
743             return mGrantsDb.hasGrant(uid, alias);
744         }
745 
746         @Override public boolean setGrant(int uid, String alias, boolean granted) {
747             Preconditions.checkCallAuthorization(isSystemUid(getCaller()), MSG_NOT_SYSTEM);
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