Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 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.settings; |
| 18 | |
| 19 | import android.app.Activity; |
| 20 | import android.app.AlertDialog; |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 21 | import android.app.admin.DevicePolicyManager; |
Julia Reynolds | 565653c | 2014-06-12 11:49:12 -0400 | [diff] [blame] | 22 | import android.content.Context; |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 23 | import android.content.DialogInterface; |
| 24 | import android.content.Intent; |
Zoltan Szatmary-Ban | 307e3d0 | 2015-07-09 17:30:20 +0100 | [diff] [blame] | 25 | import android.content.pm.PackageManager; |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 26 | import android.content.pm.UserInfo; |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 27 | import android.content.res.Resources; |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 28 | import android.os.AsyncTask; |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 29 | import android.os.Bundle; |
Vinit Deshapnde | 6c0a193 | 2013-09-30 10:41:27 -0700 | [diff] [blame] | 30 | import android.os.Process; |
Jason Monk | 39b4674 | 2015-09-10 15:52:51 -0400 | [diff] [blame] | 31 | import android.os.RemoteException; |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 32 | import android.os.UserHandle; |
Julia Reynolds | 565653c | 2014-06-12 11:49:12 -0400 | [diff] [blame] | 33 | import android.os.UserManager; |
Kenny Root | b50b15c | 2012-03-21 14:57:29 -0700 | [diff] [blame] | 34 | import android.security.Credentials; |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 35 | import android.security.KeyChain; |
Jason Monk | 39b4674 | 2015-09-10 15:52:51 -0400 | [diff] [blame] | 36 | import android.security.KeyChain.KeyChainConnection; |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 37 | import android.security.KeyStore; |
| 38 | import android.text.Editable; |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 39 | import android.text.TextUtils; |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 40 | import android.text.TextWatcher; |
| 41 | import android.util.Log; |
| 42 | import android.view.View; |
| 43 | import android.widget.Button; |
| 44 | import android.widget.TextView; |
| 45 | import android.widget.Toast; |
| 46 | |
Julia Reynolds | 565653c | 2014-06-12 11:49:12 -0400 | [diff] [blame] | 47 | import com.android.internal.widget.LockPatternUtils; |
Vinit Deshapnde | 6c0a193 | 2013-09-30 10:41:27 -0700 | [diff] [blame] | 48 | import com.android.org.bouncycastle.asn1.ASN1InputStream; |
| 49 | import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo; |
Maurice Lam | 2eb170c | 2017-04-28 16:18:47 -0700 | [diff] [blame] | 50 | import com.android.settings.password.ChooseLockSettingsHelper; |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 51 | import com.android.settings.security.ConfigureKeyGuardDialog; |
insight.lee | 03159e4 | 2017-01-17 08:27:01 +0900 | [diff] [blame] | 52 | import com.android.settings.vpn2.VpnUtils; |
Vinit Deshapnde | 6c0a193 | 2013-09-30 10:41:27 -0700 | [diff] [blame] | 53 | |
Vinit Deshapnde | 6c0a193 | 2013-09-30 10:41:27 -0700 | [diff] [blame] | 54 | import java.io.ByteArrayInputStream; |
| 55 | import java.io.IOException; |
| 56 | |
Maurice Lam | 2eb170c | 2017-04-28 16:18:47 -0700 | [diff] [blame] | 57 | import sun.security.util.ObjectIdentifier; |
| 58 | import sun.security.x509.AlgorithmId; |
| 59 | |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 60 | /** |
| 61 | * CredentialStorage handles KeyStore reset, unlock, and install. |
| 62 | * |
| 63 | * CredentialStorage has a pretty convoluted state machine to migrate |
| 64 | * from the old style separate keystore password to a new key guard |
| 65 | * based password, as well as to deal with setting up the key guard if |
| 66 | * necessary. |
| 67 | * |
| 68 | * KeyStore: UNINITALIZED |
| 69 | * KeyGuard: OFF |
| 70 | * Action: set up key guard |
| 71 | * Notes: factory state |
| 72 | * |
| 73 | * KeyStore: UNINITALIZED |
| 74 | * KeyGuard: ON |
| 75 | * Action: confirm key guard |
| 76 | * Notes: user had key guard but no keystore and upgraded from pre-ICS |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 77 | * OR user had key guard and pre-ICS keystore password which was then reset |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 78 | * |
| 79 | * KeyStore: LOCKED |
| 80 | * KeyGuard: OFF/ON |
| 81 | * Action: old unlock dialog |
| 82 | * Notes: assume old password, need to use it to unlock. |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 83 | * if unlock, ensure key guard before install. |
| 84 | * if reset, treat as UNINITALIZED/OFF |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 85 | * |
| 86 | * KeyStore: UNLOCKED |
| 87 | * KeyGuard: OFF |
| 88 | * Action: set up key guard |
| 89 | * Notes: ensure key guard, then proceed |
| 90 | * |
| 91 | * KeyStore: UNLOCKED |
| 92 | * keyguard: ON |
| 93 | * Action: normal unlock/install |
| 94 | * Notes: this is the common case |
| 95 | */ |
| 96 | public final class CredentialStorage extends Activity { |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 97 | |
| 98 | private static final String TAG = "CredentialStorage"; |
| 99 | |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 100 | public static final String ACTION_UNLOCK = "com.android.credentials.UNLOCK"; |
| 101 | public static final String ACTION_INSTALL = "com.android.credentials.INSTALL"; |
| 102 | public static final String ACTION_RESET = "com.android.credentials.RESET"; |
| 103 | |
| 104 | // This is the minimum acceptable password quality. If the current password quality is |
| 105 | // lower than this, keystore should not be activated. |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 106 | public static final int MIN_PASSWORD_QUALITY = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 107 | |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 108 | private static final int CONFIRM_KEY_GUARD_REQUEST = 1; |
Ricky Wai | c0e5070 | 2016-05-24 11:13:56 +0100 | [diff] [blame] | 109 | private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 2; |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 110 | |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 111 | private final KeyStore mKeyStore = KeyStore.getInstance(); |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 112 | |
| 113 | /** |
| 114 | * When non-null, the bundle containing credentials to install. |
| 115 | */ |
| 116 | private Bundle mInstallBundle; |
| 117 | |
| 118 | /** |
| 119 | * After unsuccessful KeyStore.unlock, the number of unlock |
| 120 | * attempts remaining before the KeyStore will reset itself. |
| 121 | * |
| 122 | * Reset to -1 on successful unlock or reset. |
| 123 | */ |
| 124 | private int mRetriesRemaining = -1; |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 125 | |
Kenny Root | cf008c2 | 2012-09-16 14:01:43 -0700 | [diff] [blame] | 126 | @Override |
| 127 | protected void onResume() { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 128 | super.onResume(); |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 129 | |
| 130 | Intent intent = getIntent(); |
| 131 | String action = intent.getAction(); |
Julia Reynolds | 565653c | 2014-06-12 11:49:12 -0400 | [diff] [blame] | 132 | UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); |
| 133 | if (!userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CREDENTIALS)) { |
| 134 | if (ACTION_RESET.equals(action)) { |
| 135 | new ResetDialog(); |
| 136 | } else { |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 137 | if (ACTION_INSTALL.equals(action) && checkCallerIsCertInstallerOrSelfInProfile()) { |
Julia Reynolds | 565653c | 2014-06-12 11:49:12 -0400 | [diff] [blame] | 138 | mInstallBundle = intent.getExtras(); |
| 139 | } |
| 140 | // ACTION_UNLOCK also handled here in addition to ACTION_INSTALL |
| 141 | handleUnlockOrInstall(); |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 142 | } |
Julia Reynolds | 565653c | 2014-06-12 11:49:12 -0400 | [diff] [blame] | 143 | } else { |
Julia Reynolds | a12fc84 | 2015-03-03 12:32:30 -0500 | [diff] [blame] | 144 | // Users can set a screen lock if there is none even if they can't modify the |
| 145 | // credentials store. |
| 146 | if (ACTION_UNLOCK.equals(action) && mKeyStore.state() == KeyStore.State.UNINITIALIZED) { |
| 147 | ensureKeyGuard(); |
| 148 | } else { |
| 149 | finish(); |
| 150 | } |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 151 | } |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Based on the current state of the KeyStore and key guard, try to |
| 156 | * make progress on unlocking or installing to the keystore. |
| 157 | */ |
| 158 | private void handleUnlockOrInstall() { |
| 159 | // something already decided we are done, do not proceed |
| 160 | if (isFinishing()) { |
| 161 | return; |
| 162 | } |
| 163 | switch (mKeyStore.state()) { |
| 164 | case UNINITIALIZED: { |
| 165 | ensureKeyGuard(); |
| 166 | return; |
| 167 | } |
| 168 | case LOCKED: { |
| 169 | new UnlockDialog(); |
| 170 | return; |
| 171 | } |
| 172 | case UNLOCKED: { |
| 173 | if (!checkKeyGuardQuality()) { |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 174 | final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog(); |
| 175 | dialog.show(getFragmentManager(), ConfigureKeyGuardDialog.TAG); |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 176 | return; |
| 177 | } |
| 178 | installIfAvailable(); |
| 179 | finish(); |
| 180 | return; |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 181 | } |
| 182 | } |
| 183 | } |
| 184 | |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 185 | /** |
| 186 | * Make sure the user enters the key guard to set or change the |
| 187 | * keystore password. This can be used in UNINITIALIZED to set the |
| 188 | * keystore password or UNLOCKED to change the password (as is the |
| 189 | * case after unlocking with an old-style password). |
| 190 | */ |
| 191 | private void ensureKeyGuard() { |
| 192 | if (!checkKeyGuardQuality()) { |
| 193 | // key guard not setup, doing so will initialize keystore |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 194 | final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog(); |
| 195 | dialog.show(getFragmentManager(), ConfigureKeyGuardDialog.TAG); |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 196 | // will return to onResume after Activity |
| 197 | return; |
| 198 | } |
| 199 | // force key guard confirmation |
Ricky Wai | c0e5070 | 2016-05-24 11:13:56 +0100 | [diff] [blame] | 200 | if (confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST)) { |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 201 | // will return password value via onActivityResult |
| 202 | return; |
| 203 | } |
| 204 | finish(); |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * Returns true if the currently set key guard matches our minimum quality requirements. |
| 209 | */ |
| 210 | private boolean checkKeyGuardQuality() { |
Robin Lee | 330c205 | 2016-04-07 14:55:06 +0100 | [diff] [blame] | 211 | int credentialOwner = |
| 212 | UserManager.get(this).getCredentialOwnerProfile(UserHandle.myUserId()); |
| 213 | int quality = new LockPatternUtils(this).getActivePasswordQuality(credentialOwner); |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 214 | return (quality >= MIN_PASSWORD_QUALITY); |
| 215 | } |
| 216 | |
Vinit Deshapnde | 6c0a193 | 2013-09-30 10:41:27 -0700 | [diff] [blame] | 217 | private boolean isHardwareBackedKey(byte[] keyData) { |
| 218 | try { |
| 219 | ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData)); |
| 220 | PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject()); |
Kenny Root | 1b291c1 | 2016-01-20 15:50:55 -0800 | [diff] [blame] | 221 | String algOid = pki.getAlgorithmId().getAlgorithm().getId(); |
| 222 | String algName = new AlgorithmId(new ObjectIdentifier(algOid)).getName(); |
Vinit Deshapnde | 6c0a193 | 2013-09-30 10:41:27 -0700 | [diff] [blame] | 223 | |
| 224 | return KeyChain.isBoundKeyAlgorithm(algName); |
| 225 | } catch (IOException e) { |
| 226 | Log.e(TAG, "Failed to parse key data"); |
| 227 | return false; |
| 228 | } |
| 229 | } |
| 230 | |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 231 | /** |
| 232 | * Install credentials if available, otherwise do nothing. |
| 233 | */ |
| 234 | private void installIfAvailable() { |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 235 | if (mInstallBundle == null || mInstallBundle.isEmpty()) { |
| 236 | return; |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 237 | } |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 238 | |
| 239 | Bundle bundle = mInstallBundle; |
| 240 | mInstallBundle = null; |
| 241 | |
Robin Lee | 635426b | 2015-01-30 14:57:25 +0000 | [diff] [blame] | 242 | final int uid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, KeyStore.UID_SELF); |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 243 | |
Robin Lee | 635426b | 2015-01-30 14:57:25 +0000 | [diff] [blame] | 244 | if (uid != KeyStore.UID_SELF && !UserHandle.isSameUser(uid, Process.myUid())) { |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 245 | int dstUserId = UserHandle.getUserId(uid); |
| 246 | int myUserId = UserHandle.myUserId(); |
| 247 | |
| 248 | // Restrict install target to the wifi uid. |
| 249 | if (uid != Process.WIFI_UID) { |
| 250 | Log.e(TAG, "Failed to install credentials as uid " + uid + ": cross-user installs" |
| 251 | + " may only target wifi uids"); |
| 252 | return; |
| 253 | } |
| 254 | |
| 255 | Intent installIntent = new Intent(ACTION_INSTALL) |
| 256 | .setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) |
| 257 | .putExtras(bundle); |
| 258 | startActivityAsUser(installIntent, new UserHandle(dstUserId)); |
| 259 | return; |
| 260 | } |
| 261 | |
| 262 | if (bundle.containsKey(Credentials.EXTRA_USER_PRIVATE_KEY_NAME)) { |
| 263 | String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME); |
| 264 | byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA); |
| 265 | |
| 266 | int flags = KeyStore.FLAG_ENCRYPTED; |
| 267 | if (uid == Process.WIFI_UID && isHardwareBackedKey(value)) { |
| 268 | // Hardware backed keystore is secure enough to allow for WIFI stack |
| 269 | // to enable access to secure networks without user intervention |
| 270 | Log.d(TAG, "Saving private key with FLAG_NONE for WIFI_UID"); |
| 271 | flags = KeyStore.FLAG_NONE; |
| 272 | } |
| 273 | |
| 274 | if (!mKeyStore.importKey(key, value, uid, flags)) { |
| 275 | Log.e(TAG, "Failed to install " + key + " as uid " + uid); |
| 276 | return; |
| 277 | } |
| 278 | } |
| 279 | |
Chad Brubaker | 616b3fe | 2016-07-19 11:13:46 -0700 | [diff] [blame] | 280 | int flags = KeyStore.FLAG_NONE; |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 281 | |
| 282 | if (bundle.containsKey(Credentials.EXTRA_USER_CERTIFICATE_NAME)) { |
| 283 | String certName = bundle.getString(Credentials.EXTRA_USER_CERTIFICATE_NAME); |
| 284 | byte[] certData = bundle.getByteArray(Credentials.EXTRA_USER_CERTIFICATE_DATA); |
| 285 | |
| 286 | if (!mKeyStore.put(certName, certData, uid, flags)) { |
| 287 | Log.e(TAG, "Failed to install " + certName + " as uid " + uid); |
| 288 | return; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | if (bundle.containsKey(Credentials.EXTRA_CA_CERTIFICATES_NAME)) { |
| 293 | String caListName = bundle.getString(Credentials.EXTRA_CA_CERTIFICATES_NAME); |
| 294 | byte[] caListData = bundle.getByteArray(Credentials.EXTRA_CA_CERTIFICATES_DATA); |
| 295 | |
| 296 | if (!mKeyStore.put(caListName, caListData, uid, flags)) { |
| 297 | Log.e(TAG, "Failed to install " + caListName + " as uid " + uid); |
| 298 | return; |
| 299 | } |
| 300 | } |
| 301 | |
Chad Brubaker | 68baa35 | 2017-04-10 10:26:46 -0700 | [diff] [blame] | 302 | // Send the broadcast. |
| 303 | Intent broadcast = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED); |
| 304 | sendBroadcast(broadcast); |
| 305 | |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 306 | setResult(RESULT_OK); |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 307 | } |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 308 | |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 309 | /** |
| 310 | * Prompt for reset confirmation, resetting on confirmation, finishing otherwise. |
| 311 | */ |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 312 | private class ResetDialog |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 313 | implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 314 | private boolean mResetConfirmed; |
| 315 | |
| 316 | private ResetDialog() { |
| 317 | AlertDialog dialog = new AlertDialog.Builder(CredentialStorage.this) |
| 318 | .setTitle(android.R.string.dialog_alert_title) |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 319 | .setMessage(R.string.credentials_reset_hint) |
| 320 | .setPositiveButton(android.R.string.ok, this) |
| 321 | .setNegativeButton(android.R.string.cancel, this) |
| 322 | .create(); |
| 323 | dialog.setOnDismissListener(this); |
| 324 | dialog.show(); |
| 325 | } |
| 326 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 327 | @Override |
| 328 | public void onClick(DialogInterface dialog, int button) { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 329 | mResetConfirmed = (button == DialogInterface.BUTTON_POSITIVE); |
| 330 | } |
| 331 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 332 | @Override |
| 333 | public void onDismiss(DialogInterface dialog) { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 334 | if (mResetConfirmed) { |
| 335 | mResetConfirmed = false; |
Ricky Wai | c0e5070 | 2016-05-24 11:13:56 +0100 | [diff] [blame] | 336 | if (confirmKeyGuard(CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST)) { |
| 337 | // will return password value via onActivityResult |
| 338 | return; |
| 339 | } |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 340 | } |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 341 | finish(); |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 342 | } |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 343 | } |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 344 | |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 345 | /** |
| 346 | * Background task to handle reset of both keystore and user installed CAs. |
| 347 | */ |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 348 | private class ResetKeyStoreAndKeyChain extends AsyncTask<Void, Void, Boolean> { |
| 349 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 350 | @Override |
| 351 | protected Boolean doInBackground(Void... unused) { |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 352 | |
Chad Brubaker | ce10b5e | 2015-05-21 15:57:24 -0700 | [diff] [blame] | 353 | // Clear all the users credentials could have been installed in for this user. |
Ricky Wai | c0e5070 | 2016-05-24 11:13:56 +0100 | [diff] [blame] | 354 | new LockPatternUtils(CredentialStorage.this).resetKeyStore(UserHandle.myUserId()); |
Chad Brubaker | ce10b5e | 2015-05-21 15:57:24 -0700 | [diff] [blame] | 355 | |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 356 | try { |
Brian Carlstrom | 435e45e | 2011-05-17 16:22:06 -0700 | [diff] [blame] | 357 | KeyChainConnection keyChainConnection = KeyChain.bind(CredentialStorage.this); |
| 358 | try { |
| 359 | return keyChainConnection.getService().reset(); |
| 360 | } catch (RemoteException e) { |
| 361 | return false; |
| 362 | } finally { |
| 363 | keyChainConnection.close(); |
| 364 | } |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 365 | } catch (InterruptedException e) { |
| 366 | Thread.currentThread().interrupt(); |
| 367 | return false; |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 368 | } |
| 369 | } |
| 370 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 371 | @Override |
| 372 | protected void onPostExecute(Boolean success) { |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 373 | if (success) { |
| 374 | Toast.makeText(CredentialStorage.this, |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 375 | R.string.credentials_erased, Toast.LENGTH_SHORT).show(); |
insight.lee | 03159e4 | 2017-01-17 08:27:01 +0900 | [diff] [blame] | 376 | clearLegacyVpnIfEstablished(); |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 377 | } else { |
| 378 | Toast.makeText(CredentialStorage.this, |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 379 | R.string.credentials_not_erased, Toast.LENGTH_SHORT).show(); |
Brian Carlstrom | 9815429 | 2011-05-12 23:58:39 -0700 | [diff] [blame] | 380 | } |
| 381 | finish(); |
| 382 | } |
| 383 | } |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 384 | |
insight.lee | 03159e4 | 2017-01-17 08:27:01 +0900 | [diff] [blame] | 385 | private void clearLegacyVpnIfEstablished() { |
| 386 | boolean isDone = VpnUtils.disconnectLegacyVpn(getApplicationContext()); |
| 387 | if (isDone) { |
| 388 | Toast.makeText(CredentialStorage.this, R.string.vpn_disconnected, |
| 389 | Toast.LENGTH_SHORT).show(); |
| 390 | } |
| 391 | } |
| 392 | |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 393 | /** |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 394 | * Check that the caller is either certinstaller or Settings running in a profile of this user. |
| 395 | */ |
| 396 | private boolean checkCallerIsCertInstallerOrSelfInProfile() { |
| 397 | if (TextUtils.equals("com.android.certinstaller", getCallingPackage())) { |
Zoltan Szatmary-Ban | 307e3d0 | 2015-07-09 17:30:20 +0100 | [diff] [blame] | 398 | // CertInstaller is allowed to install credentials if it has the same signature as |
| 399 | // Settings package. |
| 400 | return getPackageManager().checkSignatures( |
| 401 | getCallingPackage(), getPackageName()) == PackageManager.SIGNATURE_MATCH; |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 402 | } |
| 403 | |
| 404 | final int launchedFromUserId; |
| 405 | try { |
Sudheer Shanka | acb1a61 | 2016-11-10 15:30:14 -0800 | [diff] [blame] | 406 | int launchedFromUid = android.app.ActivityManager.getService() |
Robin Lee | 8823c3e | 2014-11-06 20:57:11 +0000 | [diff] [blame] | 407 | .getLaunchedFromUid(getActivityToken()); |
| 408 | if (launchedFromUid == -1) { |
| 409 | Log.e(TAG, ACTION_INSTALL + " must be started with startActivityForResult"); |
| 410 | return false; |
| 411 | } |
| 412 | if (!UserHandle.isSameApp(launchedFromUid, Process.myUid())) { |
| 413 | // Not the same app |
| 414 | return false; |
| 415 | } |
| 416 | launchedFromUserId = UserHandle.getUserId(launchedFromUid); |
| 417 | } catch (RemoteException re) { |
| 418 | // Error talking to ActivityManager, just give up |
| 419 | return false; |
| 420 | } |
| 421 | |
| 422 | UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); |
| 423 | UserInfo parentInfo = userManager.getProfileParent(launchedFromUserId); |
| 424 | if (parentInfo == null || parentInfo.id != UserHandle.myUserId()) { |
| 425 | // Caller is not running in a profile of this user |
| 426 | return false; |
| 427 | } |
| 428 | return true; |
| 429 | } |
| 430 | |
| 431 | /** |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 432 | * Confirm existing key guard, returning password via onActivityResult. |
| 433 | */ |
Ricky Wai | c0e5070 | 2016-05-24 11:13:56 +0100 | [diff] [blame] | 434 | private boolean confirmKeyGuard(int requestCode) { |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 435 | Resources res = getResources(); |
| 436 | boolean launched = new ChooseLockSettingsHelper(this) |
Ricky Wai | c0e5070 | 2016-05-24 11:13:56 +0100 | [diff] [blame] | 437 | .launchConfirmationActivity(requestCode, |
Jorim Jaggi | 8a09b61 | 2015-04-06 17:47:18 -0700 | [diff] [blame] | 438 | res.getText(R.string.credentials_title), true); |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 439 | return launched; |
| 440 | } |
| 441 | |
| 442 | @Override |
| 443 | public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| 444 | super.onActivityResult(requestCode, resultCode, data); |
| 445 | |
| 446 | /** |
| 447 | * Receive key guard password initiated by confirmKeyGuard. |
| 448 | */ |
| 449 | if (requestCode == CONFIRM_KEY_GUARD_REQUEST) { |
| 450 | if (resultCode == Activity.RESULT_OK) { |
| 451 | String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); |
| 452 | if (!TextUtils.isEmpty(password)) { |
| 453 | // success |
Chad Brubaker | 7236f2a | 2015-05-07 10:29:18 -0700 | [diff] [blame] | 454 | mKeyStore.unlock(password); |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 455 | // return to onResume |
| 456 | return; |
| 457 | } |
| 458 | } |
| 459 | // failed confirmation, bail |
| 460 | finish(); |
Ricky Wai | c0e5070 | 2016-05-24 11:13:56 +0100 | [diff] [blame] | 461 | } else if (requestCode == CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST) { |
| 462 | if (resultCode == Activity.RESULT_OK) { |
| 463 | new ResetKeyStoreAndKeyChain().execute(); |
| 464 | return; |
| 465 | } |
| 466 | // failed confirmation, bail |
| 467 | finish(); |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 468 | } |
| 469 | } |
| 470 | |
| 471 | /** |
| 472 | * Prompt for unlock with old-style password. |
| 473 | * |
| 474 | * On successful unlock, ensure migration to key guard before continuing. |
| 475 | * On unsuccessful unlock, retry by calling handleUnlockOrInstall. |
| 476 | */ |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 477 | private class UnlockDialog implements TextWatcher, |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 478 | DialogInterface.OnClickListener, DialogInterface.OnDismissListener { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 479 | private boolean mUnlockConfirmed; |
| 480 | |
| 481 | private final Button mButton; |
| 482 | private final TextView mOldPassword; |
| 483 | private final TextView mError; |
| 484 | |
| 485 | private UnlockDialog() { |
| 486 | View view = View.inflate(CredentialStorage.this, R.layout.credentials_dialog, null); |
| 487 | |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 488 | CharSequence text; |
| 489 | if (mRetriesRemaining == -1) { |
| 490 | text = getResources().getText(R.string.credentials_unlock_hint); |
| 491 | } else if (mRetriesRemaining > 3) { |
| 492 | text = getResources().getText(R.string.credentials_wrong_password); |
| 493 | } else if (mRetriesRemaining == 1) { |
| 494 | text = getResources().getText(R.string.credentials_reset_warning); |
| 495 | } else { |
| 496 | text = getString(R.string.credentials_reset_warning_plural, mRetriesRemaining); |
| 497 | } |
| 498 | |
| 499 | ((TextView) view.findViewById(R.id.hint)).setText(text); |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 500 | mOldPassword = (TextView) view.findViewById(R.id.old_password); |
| 501 | mOldPassword.setVisibility(View.VISIBLE); |
| 502 | mOldPassword.addTextChangedListener(this); |
| 503 | mError = (TextView) view.findViewById(R.id.error); |
| 504 | |
| 505 | AlertDialog dialog = new AlertDialog.Builder(CredentialStorage.this) |
| 506 | .setView(view) |
| 507 | .setTitle(R.string.credentials_unlock) |
| 508 | .setPositiveButton(android.R.string.ok, this) |
| 509 | .setNegativeButton(android.R.string.cancel, this) |
| 510 | .create(); |
| 511 | dialog.setOnDismissListener(this); |
| 512 | dialog.show(); |
| 513 | mButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); |
| 514 | mButton.setEnabled(false); |
| 515 | } |
| 516 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 517 | @Override |
| 518 | public void afterTextChanged(Editable editable) { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 519 | mButton.setEnabled(mOldPassword == null || mOldPassword.getText().length() > 0); |
| 520 | } |
| 521 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 522 | @Override |
| 523 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 524 | } |
| 525 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 526 | @Override |
| 527 | public void onTextChanged(CharSequence s, int start, int before, int count) { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 528 | } |
| 529 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 530 | @Override |
| 531 | public void onClick(DialogInterface dialog, int button) { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 532 | mUnlockConfirmed = (button == DialogInterface.BUTTON_POSITIVE); |
| 533 | } |
| 534 | |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 535 | @Override |
| 536 | public void onDismiss(DialogInterface dialog) { |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 537 | if (mUnlockConfirmed) { |
| 538 | mUnlockConfirmed = false; |
| 539 | mError.setVisibility(View.VISIBLE); |
| 540 | mKeyStore.unlock(mOldPassword.getText().toString()); |
| 541 | int error = mKeyStore.getLastError(); |
| 542 | if (error == KeyStore.NO_ERROR) { |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 543 | mRetriesRemaining = -1; |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 544 | Toast.makeText(CredentialStorage.this, |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 545 | R.string.credentials_enabled, |
| 546 | Toast.LENGTH_SHORT).show(); |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 547 | // aha, now we are unlocked, switch to key guard. |
| 548 | // we'll end up back in onResume to install |
| 549 | ensureKeyGuard(); |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 550 | } else if (error == KeyStore.UNINITIALIZED) { |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 551 | mRetriesRemaining = -1; |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 552 | Toast.makeText(CredentialStorage.this, |
Fan Zhang | ab0a0c8 | 2017-06-05 17:15:21 -0700 | [diff] [blame] | 553 | R.string.credentials_erased, |
| 554 | Toast.LENGTH_SHORT).show(); |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 555 | // we are reset, we can now set new password with key guard |
| 556 | handleUnlockOrInstall(); |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 557 | } else if (error >= KeyStore.WRONG_PASSWORD) { |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 558 | // we need to try again |
| 559 | mRetriesRemaining = error - KeyStore.WRONG_PASSWORD + 1; |
| 560 | handleUnlockOrInstall(); |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 561 | } |
Brian Carlstrom | 0e88f4d | 2011-06-02 16:47:15 -0700 | [diff] [blame] | 562 | return; |
Brian Carlstrom | d4023b7 | 2011-05-25 13:24:20 -0700 | [diff] [blame] | 563 | } |
| 564 | finish(); |
| 565 | } |
| 566 | } |
Chia-chi Yeh | 91d65a2 | 2011-01-20 18:46:01 +0800 | [diff] [blame] | 567 | } |