Credential storage triggers unlock when keystore is locked.

If the phone is in the unlocked state and keystore is locked, storing
credentials asks for a password that does not exist to the user. Replace
this workflow with a key guard confirmation, asking the user to unlock
the screen in the same way they would normally unlock their phone.

Bug: 68298609
Test: adb push sample_credentials.p12 /sdcard/
Test: adb shell su 1000 service call android.security.keystore 9 i32 0
Test: adb shell am start -a android.credentials.INSTALL --user 10
Test: adb shell su 1000 service call android.security.keystore 9 i32 10

Change-Id: I8a3068a5d7de508fb417016acdf41b1712a2e7cc
This commit is contained in:
Irina Dumitrescu
2018-05-10 18:06:53 +01:00
parent 8e060b42dd
commit efdfbeaff4
3 changed files with 12 additions and 200 deletions

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="15dip">
<TextView android:id="@+id/hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10sp"/>
<TextView android:id="@+id/error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10sp"
android:textColor="@color/red"
android:textStyle="bold"
android:visibility="gone"/>
<TextView android:id="@+id/old_password_prompt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/credentials_old_password"
android:visibility="gone"/>
<EditText android:id="@+id/old_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:password="true"
android:singleLine="true"
android:visibility="gone"/>
</LinearLayout>
</ScrollView>

View File

@@ -5613,27 +5613,12 @@
<string name="credential_for_vpn_and_apps">Installed for VPN and apps</string>
<!-- Sub-heading for a user credential installed to be used as part of a Wi-Fi configuration. [CHAR LIMIT=NONE]. -->
<string name="credential_for_wifi">Installed for Wi-Fi</string>
<!-- Title of dialog to enable credential storage [CHAR LIMIT=30] -->
<string name="credentials_unlock"></string>
<!-- Description of dialog to enable credential storage [CHAR LIMIT=NONE] -->
<string name="credentials_unlock_hint">Type the password for credential storage.</string>
<!-- Description of the input box for the old password [CHAR LIMIT=30] -->
<string name="credentials_old_password">Current password:</string>
<!-- Description of dialog to reset credential storage [CHAR LIMIT=NONE] -->
<string name="credentials_reset_hint">Remove all the contents?</string>
<!-- Error message [CHAR LIMIT=NONE] -->
<string name="credentials_wrong_password">Incorrect password.</string>
<!-- Error message [CHAR LIMIT=NONE] -->
<string name="credentials_reset_warning">Incorrect password. You have one more chance before credential storage is erased.</string>
<!-- Error message [CHAR LIMIT=NONE] -->
<string name="credentials_reset_warning_plural">Incorrect password. You have <xliff:g id="number" example="5">%1$d</xliff:g> more chances before credential storage is erased.</string>
<!-- Toast message [CHAR LIMIT=30] -->
<string name="credentials_erased">Credential storage is erased.</string>
<!-- Toast message [CHAR LIMIT=30] when credential storage containing private keys and certificates could not be erased (opposite of string credentials_erased) -->
<string name="credentials_not_erased">Credential storage couldn\u2019t be erased.</string>
<!-- Toast message [CHAR LIMIT=30] -->
<string name="credentials_enabled">Credential storage is enabled.</string>
<!-- This string is in a dialog, and the dialog shows up on a device that's managed by a user's company. It lets the user know that they need to have a secure lock screen (PIN, password, or pattern) before they can use credential storage [CHAR LIMIT=NONE] -->
<string name="credentials_configure_lock_screen_hint">Before you can use credential storage, your device need to have a secure lock screen</string>
<!-- This string is for the content of the button that leads user to lock screen settings [CHAR LIMIT=20] -->

View File

@@ -35,25 +35,17 @@ import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
import android.security.KeyStore;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.widget.LockPatternUtils;
import com.android.org.bouncycastle.asn1.ASN1InputStream;
import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.security.ConfigureKeyGuardDialog;
import com.android.settings.vpn2.VpnUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;
@@ -78,8 +70,8 @@ import sun.security.x509.AlgorithmId;
*
* KeyStore: LOCKED
* KeyGuard: OFF/ON
* Action: old unlock dialog
* Notes: assume old password, need to use it to unlock.
* Action: confirm key guard
* Notes: request normal unlock to unlock the keystore.
* if unlock, ensure key guard before install.
* if reset, treat as UNINITALIZED/OFF
*
@@ -115,14 +107,6 @@ public final class CredentialStorage extends Activity {
*/
private Bundle mInstallBundle;
/**
* After unsuccessful KeyStore.unlock, the number of unlock
* attempts remaining before the KeyStore will reset itself.
*
* Reset to -1 on successful unlock or reset.
*/
private int mRetriesRemaining = -1;
@Override
protected void onResume() {
super.onResume();
@@ -166,11 +150,12 @@ public final class CredentialStorage extends Activity {
return;
}
case LOCKED: {
new UnlockDialog();
// Force key guard confirmation
confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST);
return;
}
case UNLOCKED: {
if (!checkKeyGuardQuality()) {
if (isActivePasswordQualityInsufficient()) {
final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
dialog.show(getFragmentManager(), ConfigureKeyGuardDialog.TAG);
return;
@@ -189,7 +174,7 @@ public final class CredentialStorage extends Activity {
* case after unlocking with an old-style password).
*/
private void ensureKeyGuard() {
if (!checkKeyGuardQuality()) {
if (isActivePasswordQualityInsufficient()) {
// key guard not setup, doing so will initialize keystore
final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
dialog.show(getFragmentManager(), ConfigureKeyGuardDialog.TAG);
@@ -205,9 +190,9 @@ public final class CredentialStorage extends Activity {
}
/**
* Returns true if the currently set key guard matches our minimum quality requirements.
* Returns true if the currently set key guard violates our minimum quality requirements.
*/
private boolean checkKeyGuardQuality() {
private boolean isActivePasswordQualityInsufficient() {
final int credentialOwner =
UserManager.get(this).getCredentialOwnerProfile(UserHandle.myUserId());
final int quality = new LockPatternUtils(this).getActivePasswordQuality(credentialOwner);
@@ -457,11 +442,8 @@ public final class CredentialStorage extends Activity {
final UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
final UserInfo parentInfo = userManager.getProfileParent(launchedFromUserId);
if (parentInfo == null || parentInfo.id != UserHandle.myUserId()) {
// Caller is not running in a profile of this user
return false;
}
return true;
// Caller is running in a profile of this user
return ((parentInfo != null) && (parentInfo.id == UserHandle.myUserId()));
}
/**
@@ -469,19 +451,15 @@ public final class CredentialStorage extends Activity {
*/
private boolean confirmKeyGuard(int requestCode) {
final Resources res = getResources();
boolean launched = new ChooseLockSettingsHelper(this)
return new ChooseLockSettingsHelper(this)
.launchConfirmationActivity(requestCode,
res.getText(R.string.credentials_title), true);
return launched;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
/**
* Receive key guard password initiated by confirmKeyGuard.
*/
// Receive key guard password initiated by confirmKeyGuard.
if (requestCode == CONFIRM_KEY_GUARD_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
final String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
@@ -503,102 +481,4 @@ public final class CredentialStorage extends Activity {
finish();
}
}
/**
* Prompt for unlock with old-style password.
*
* On successful unlock, ensure migration to key guard before continuing.
* On unsuccessful unlock, retry by calling handleUnlockOrInstall.
*/
private class UnlockDialog implements TextWatcher,
DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
private boolean mUnlockConfirmed;
private final Button mButton;
private final TextView mOldPassword;
private final TextView mError;
private UnlockDialog() {
final View view = View.inflate(
CredentialStorage.this, R.layout.credentials_dialog, null);
final CharSequence text;
if (mRetriesRemaining == -1) {
text = getResources().getText(R.string.credentials_unlock_hint);
} else if (mRetriesRemaining > 3) {
text = getResources().getText(R.string.credentials_wrong_password);
} else if (mRetriesRemaining == 1) {
text = getResources().getText(R.string.credentials_reset_warning);
} else {
text = getString(R.string.credentials_reset_warning_plural, mRetriesRemaining);
}
((TextView) view.findViewById(R.id.hint)).setText(text);
mOldPassword = (TextView) view.findViewById(R.id.old_password);
mOldPassword.setVisibility(View.VISIBLE);
mOldPassword.addTextChangedListener(this);
mError = (TextView) view.findViewById(R.id.error);
final AlertDialog dialog = new AlertDialog.Builder(CredentialStorage.this)
.setView(view)
.setTitle(R.string.credentials_unlock)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, this)
.create();
dialog.setOnDismissListener(this);
dialog.show();
mButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
mButton.setEnabled(false);
}
@Override
public void afterTextChanged(Editable editable) {
mButton.setEnabled(mOldPassword == null || mOldPassword.getText().length() > 0);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void onClick(DialogInterface dialog, int button) {
mUnlockConfirmed = (button == DialogInterface.BUTTON_POSITIVE);
}
@Override
public void onDismiss(DialogInterface dialog) {
if (mUnlockConfirmed) {
mUnlockConfirmed = false;
mError.setVisibility(View.VISIBLE);
mKeyStore.unlock(mOldPassword.getText().toString());
final int error = mKeyStore.getLastError();
if (error == KeyStore.NO_ERROR) {
mRetriesRemaining = -1;
Toast.makeText(CredentialStorage.this,
R.string.credentials_enabled,
Toast.LENGTH_SHORT).show();
// aha, now we are unlocked, switch to key guard.
// we'll end up back in onResume to install
ensureKeyGuard();
} else if (error == KeyStore.UNINITIALIZED) {
mRetriesRemaining = -1;
Toast.makeText(CredentialStorage.this,
R.string.credentials_erased,
Toast.LENGTH_SHORT).show();
// we are reset, we can now set new password with key guard
handleUnlockOrInstall();
} else if (error >= KeyStore.WRONG_PASSWORD) {
// we need to try again
mRetriesRemaining = error - KeyStore.WRONG_PASSWORD + 1;
handleUnlockOrInstall();
}
return;
}
finish();
}
}
}