summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlex Johnston <acjohnston@google.com>2019-08-29 11:20:50 +0100
committerRubin Xu <rubinxu@google.com>2020-07-15 15:04:33 +0100
commit7830ab325155c712bb7bbe3e1bef9d2a714730f2 (patch)
tree5a1a436f9024c5d97b63a761e33f0309c476d336 /src
parenta4e1b7a9a931ccf991abc323dd9d8069896797fa (diff)
downloadplatform_packages_apps_CertInstaller-7830ab325155c712bb7bbe3e1bef9d2a714730f2.tar.gz
platform_packages_apps_CertInstaller-7830ab325155c712bb7bbe3e1bef9d2a714730f2.tar.bz2
platform_packages_apps_CertInstaller-7830ab325155c712bb7bbe3e1bef9d2a714730f2.zip
[DO NOT MERGE] Rollup changes from R
This change includes the following commits from internal R branch: 7a7578a Remove config_auto_cert_approval from CertInstaller 313c05a Unify manual and programmatic key installation flows 91a7c21 Added functionality to select type of certificate to be installed from the Settings app 2e6c20e Confirm credential after warning dialog when installing CA certificate ffac486 Separated flow when installing different certificate types 2138394 Created dialogs when installing certificates outside Settings d5aa3c3 Added invalid certificate dialog adecd13 Updated WiFi certificate flow 5d14580 Add an exported flag in manifest 10d9c4f Fix strings for certificate installation in CertInstaller 8df978d Modify name certificate dialog 9cdfff2 Install WiFi certificate from Files app c43d233 [WifiInstaller] Remove Passpoint profile before installing 2411689 Prevent guest user from installing credentials a9ec06a Update CertInstaller message to include app name. Bug: 161347472 Test: builds & manual testing Change-Id: I01a48a7d91c40a83db1f21b29c0d8df2724604c7
Diffstat (limited to 'src')
-rw-r--r--src/com/android/certinstaller/CertInstaller.java284
-rw-r--r--src/com/android/certinstaller/CertInstallerMain.java148
-rw-r--r--src/com/android/certinstaller/CredentialHelper.java69
-rw-r--r--src/com/android/certinstaller/WiFiInstaller.java7
4 files changed, 357 insertions, 151 deletions
diff --git a/src/com/android/certinstaller/CertInstaller.java b/src/com/android/certinstaller/CertInstaller.java
index dd849a7..3a6b7b3 100644
--- a/src/com/android/certinstaller/CertInstaller.java
+++ b/src/com/android/certinstaller/CertInstaller.java
@@ -21,25 +21,24 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.KeyguardManager;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Process;
+import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
-import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Slog;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.EditText;
-import android.widget.Spinner;
+import android.widget.RadioGroup;
import android.widget.Toast;
import java.io.Serializable;
@@ -57,17 +56,15 @@ public class CertInstaller extends Activity {
private static final int NAME_CREDENTIAL_DIALOG = 1;
private static final int PKCS12_PASSWORD_DIALOG = 2;
private static final int PROGRESS_BAR_DIALOG = 3;
+ private static final int REDIRECT_CA_CERTIFICATE_DIALOG = 4;
+ private static final int SELECT_CERTIFICATE_USAGE_DIALOG = 5;
+ private static final int INVALID_CERTIFICATE_DIALOG = 6;
private static final int REQUEST_SYSTEM_INSTALL_CODE = 1;
- private static final int REQUEST_CONFIRM_CREDENTIALS = 2;
// key to states Bundle
private static final String NEXT_ACTION_KEY = "na";
- // Values for usage type spinner
- private static final int USAGE_TYPE_SYSTEM = 0;
- private static final int USAGE_TYPE_WIFI = 1;
-
private final ViewHelper mView = new ViewHelper();
private int mState;
@@ -98,20 +95,8 @@ public class CertInstaller extends Activity {
toastErrorAndFinish(R.string.no_cert_to_saved);
finish();
} else {
- // Confirm credentials if there's _only_ a CA certificate
- // NOTE: This will affect WiFi CA certificates - those should not require
- // confirming the lock screen credentials but the code currently cannot skip the
- // confirmation for WiFi CA certificates because the user designates the certificate
- // to a UID only after this stage.
- if (mCredentials.hasCaCerts() && !mCredentials.hasPrivateKey() &&
- !mCredentials.hasUserCertificate()) {
- KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
- Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null, null);
- if (intent == null) { // No screenlock
- extractPkcs12OrInstall();
- } else {
- startActivityForResult(intent, REQUEST_CONFIRM_CREDENTIALS);
- }
+ if (installingCaCertificate()) {
+ extractPkcs12OrInstall();
} else {
if (mCredentials.hasUserCertificate() && !mCredentials.hasPrivateKey()) {
toastErrorAndFinish(R.string.action_missing_private_key);
@@ -129,6 +114,11 @@ public class CertInstaller extends Activity {
}
}
+ private boolean installingCaCertificate() {
+ return mCredentials.hasCaCerts() && !mCredentials.hasPrivateKey() &&
+ !mCredentials.hasUserCertificate();
+ }
+
@Override
protected void onResume() {
super.onResume();
@@ -164,7 +154,7 @@ public class CertInstaller extends Activity {
return createPkcs12PasswordDialog();
case NAME_CREDENTIAL_DIALOG:
- return createNameCredentialDialog();
+ return createNameCertificateDialog();
case PROGRESS_BAR_DIALOG:
ProgressDialog dialog = new ProgressDialog(this);
@@ -173,6 +163,15 @@ public class CertInstaller extends Activity {
dialog.setCancelable(false);
return dialog;
+ case REDIRECT_CA_CERTIFICATE_DIALOG:
+ return createRedirectCaCertificateDialog();
+
+ case SELECT_CERTIFICATE_USAGE_DIALOG:
+ return createSelectCertificateUsageDialog();
+
+ case INVALID_CERTIFICATE_DIALOG:
+ return createInvalidCertificateDialog();
+
default:
return null;
}
@@ -189,25 +188,14 @@ public class CertInstaller extends Activity {
}
Log.d(TAG, "credential is added: " + mCredentials.getName());
- Toast.makeText(this, getString(R.string.cert_is_added, mCredentials.getName()),
- Toast.LENGTH_LONG).show();
-
- if (mCredentials.includesVpnAndAppsTrustAnchors()) {
- // more work to do, don't finish just yet
- new InstallVpnAndAppsTrustAnchorsTask().execute();
- return;
+ if (mCredentials.getCertUsageSelected().equals(Credentials.CERTIFICATE_USAGE_WIFI)) {
+ Toast.makeText(this, R.string.wifi_cert_is_added, Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(this, R.string.user_cert_is_added, Toast.LENGTH_LONG).show();
}
setResult(RESULT_OK);
finish();
break;
- case REQUEST_CONFIRM_CREDENTIALS:
- if (resultCode == RESULT_OK) {
- extractPkcs12OrInstall();
- return;
- }
- // Failed to confirm credentials, do nothing.
- finish();
- break;
default:
Log.w(TAG, "unknown request code: " + requestCode);
finish();
@@ -223,14 +211,19 @@ public class CertInstaller extends Activity {
new Pkcs12ExtractAction("").run(this);
}
} else {
- MyAction action = new InstallOthersAction();
- action.run(this);
+ if (mCredentials.calledBySettings()) {
+ MyAction action = new InstallOthersAction();
+ action.run(this);
+ } else {
+ createRedirectOrSelectUsageDialog();
+ }
}
}
private class InstallVpnAndAppsTrustAnchorsTask extends AsyncTask<Void, Void, Boolean> {
- @Override protected Boolean doInBackground(Void... unused) {
+ @Override
+ protected Boolean doInBackground(Void... unused) {
try {
try (KeyChainConnection keyChainConnection = KeyChain.bind(CertInstaller.this)) {
return mCredentials.installVpnAndAppsTrustAnchors(CertInstaller.this,
@@ -242,8 +235,11 @@ public class CertInstaller extends Activity {
}
}
- @Override protected void onPostExecute(Boolean success) {
+ @Override
+ protected void onPostExecute(Boolean success) {
if (success) {
+ Toast.makeText(getApplicationContext(), R.string.ca_cert_is_added,
+ Toast.LENGTH_LONG).show();
setResult(RESULT_OK);
}
finish();
@@ -266,13 +262,38 @@ public class CertInstaller extends Activity {
return;
}
- nameCredential();
+ if (validCertificateSelected()) {
+ installCertificateOrShowNameDialog();
+ } else {
+ showDialog(INVALID_CERTIFICATE_DIALOG);
+ }
}
- private void nameCredential() {
+ private boolean validCertificateSelected() {
+ switch (mCredentials.getCertUsageSelected()) {
+ case Credentials.CERTIFICATE_USAGE_CA:
+ return mCredentials.hasOnlyVpnAndAppsTrustAnchors();
+ case Credentials.CERTIFICATE_USAGE_USER:
+ return mCredentials.hasUserCertificate()
+ && !mCredentials.hasOnlyVpnAndAppsTrustAnchors();
+ case Credentials.CERTIFICATE_USAGE_WIFI:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void installCertificateOrShowNameDialog() {
if (!mCredentials.hasAnyForSystemInstall()) {
toastErrorAndFinish(R.string.no_cert_to_saved);
+ } else if (mCredentials.hasOnlyVpnAndAppsTrustAnchors()) {
+ // If there's only a CA certificate to install, then it's going to be used
+ // as a trust anchor. Install it and skip importing to Keystore.
+
+ // more work to do, don't finish just yet
+ new InstallVpnAndAppsTrustAnchorsTask().execute();
} else {
+ // Name is required if installing User certificate
showDialog(NAME_CREDENTIAL_DIALOG);
}
}
@@ -302,7 +323,15 @@ public class CertInstaller extends Activity {
removeDialog(PROGRESS_BAR_DIALOG);
if (success) {
removeDialog(PKCS12_PASSWORD_DIALOG);
- nameCredential();
+ if (mCredentials.calledBySettings()) {
+ if (validCertificateSelected()) {
+ installCertificateOrShowNameDialog();
+ } else {
+ showDialog(INVALID_CERTIFICATE_DIALOG);
+ }
+ } else {
+ createRedirectOrSelectUsageDialog();
+ }
} else {
showDialog(PKCS12_PASSWORD_DIALOG);
mView.setText(R.id.credential_password, "");
@@ -310,6 +339,103 @@ public class CertInstaller extends Activity {
}
}
+ private void createRedirectOrSelectUsageDialog() {
+ if (mCredentials.hasOnlyVpnAndAppsTrustAnchors()) {
+ showDialog(REDIRECT_CA_CERTIFICATE_DIALOG);
+ } else {
+ showDialog(SELECT_CERTIFICATE_USAGE_DIALOG);
+ }
+ }
+
+ public CharSequence getCallingAppLabel() {
+ final String callingPkg = mCredentials.getReferrer();
+ if (callingPkg == null) {
+ Log.e(TAG, "Cannot get calling calling AppPackage");
+ return null;
+ }
+
+ final PackageManager pm = getPackageManager();
+ final ApplicationInfo appInfo;
+ try {
+ appInfo = pm.getApplicationInfo(callingPkg, PackageManager.MATCH_DISABLED_COMPONENTS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to find info for package: " + callingPkg);
+ return null;
+ }
+
+ return appInfo.loadLabel(pm);
+ }
+
+ private Dialog createRedirectCaCertificateDialog() {
+ final String message = getString(
+ R.string.redirect_ca_certificate_with_app_info_message, getCallingAppLabel());
+ Dialog d = new AlertDialog.Builder(this)
+ .setTitle(R.string.redirect_ca_certificate_title)
+ .setMessage(message)
+ .setPositiveButton(R.string.redirect_ca_certificate_close_button,
+ (dialog, id) -> toastErrorAndFinish(R.string.cert_not_saved))
+ .create();
+ d.setOnCancelListener(dialog -> toastErrorAndFinish(R.string.cert_not_saved));
+ return d;
+ }
+
+ private Dialog createSelectCertificateUsageDialog() {
+ ViewGroup view = (ViewGroup) View.inflate(this, R.layout.select_certificate_usage_dialog,
+ null);
+ mView.setView(view);
+
+ RadioGroup radioGroup = view.findViewById(R.id.certificate_usage);
+ radioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+ switch (checkedId) {
+ case R.id.user_certificate:
+ mCredentials.setCertUsageSelectedAndUid(Credentials.CERTIFICATE_USAGE_USER);
+ break;
+ case R.id.wifi_certificate:
+ mCredentials.setCertUsageSelectedAndUid(Credentials.CERTIFICATE_USAGE_WIFI);
+ default:
+ Slog.i(TAG, "Unknown selection for scope");
+ }
+ });
+
+
+ final Context appContext = getApplicationContext();
+ Dialog d = new AlertDialog.Builder(this)
+ .setView(view)
+ .setPositiveButton(android.R.string.ok, (dialog, id) -> {
+ showDialog(NAME_CREDENTIAL_DIALOG);
+ })
+ .setNegativeButton(android.R.string.cancel,
+ (dialog, id) -> toastErrorAndFinish(R.string.cert_not_saved))
+ .create();
+ d.setOnCancelListener(dialog -> toastErrorAndFinish(R.string.cert_not_saved));
+ return d;
+ }
+
+ private Dialog createInvalidCertificateDialog() {
+ Dialog d = new AlertDialog.Builder(this)
+ .setTitle(R.string.invalid_certificate_title)
+ .setMessage(getString(R.string.invalid_certificate_message,
+ getCertificateUsageName()))
+ .setPositiveButton(R.string.invalid_certificate_close_button,
+ (dialog, id) -> toastErrorAndFinish(R.string.cert_not_saved))
+ .create();
+ d.setOnCancelListener(dialog -> finish());
+ return d;
+ }
+
+ String getCertificateUsageName() {
+ switch (mCredentials.getCertUsageSelected()) {
+ case Credentials.CERTIFICATE_USAGE_CA:
+ return getString(R.string.ca_certificate);
+ case Credentials.CERTIFICATE_USAGE_USER:
+ return getString(R.string.user_certificate);
+ case Credentials.CERTIFICATE_USAGE_WIFI:
+ return getString(R.string.wifi_certificate);
+ default:
+ return getString(R.string.certificate);
+ }
+ }
+
private Dialog createPkcs12PasswordDialog() {
View view = View.inflate(this, R.layout.password_dialog, null);
mView.setView(view);
@@ -337,53 +463,23 @@ public class CertInstaller extends Activity {
return d;
}
- private Dialog createNameCredentialDialog() {
- ViewGroup view = (ViewGroup) View.inflate(this, R.layout.name_credential_dialog, null);
+ private Dialog createNameCertificateDialog() {
+ ViewGroup view = (ViewGroup) View.inflate(this, R.layout.name_certificate_dialog, null);
mView.setView(view);
if (mView.getHasEmptyError()) {
mView.showError(R.string.name_empty_error);
mView.setHasEmptyError(false);
}
- mView.setText(R.id.credential_info, mCredentials.getDescription(this).toString());
- final EditText nameInput = view.findViewById(R.id.credential_name);
- if (mCredentials.isInstallAsUidSet()) {
- view.findViewById(R.id.credential_usage_group).setVisibility(View.GONE);
- } else {
- final Spinner usageSpinner = view.findViewById(R.id.credential_usage);
- final View ca_capabilities_warning = view.findViewById(R.id.credential_capabilities_warning);
-
- usageSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- switch ((int) id) {
- case USAGE_TYPE_SYSTEM:
- ca_capabilities_warning.setVisibility(
- mCredentials.includesVpnAndAppsTrustAnchors() ?
- View.VISIBLE : View.GONE);
- mCredentials.setInstallAsUid(KeyStore.UID_SELF);
- break;
- case USAGE_TYPE_WIFI:
- ca_capabilities_warning.setVisibility(View.GONE);
- mCredentials.setInstallAsUid(Process.WIFI_UID);
- break;
- default:
- Log.w(TAG, "Unknown selection for scope: " + id);
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
- }
+ final EditText nameInput = view.findViewById(R.id.certificate_name);
nameInput.setText(getDefaultName());
nameInput.selectAll();
final Context appContext = getApplicationContext();
+
Dialog d = new AlertDialog.Builder(this)
.setView(view)
.setTitle(R.string.name_credential_dialog_title)
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
- String name = mView.getText(R.id.credential_name);
+ String name = mView.getText(R.id.certificate_name);
if (TextUtils.isEmpty(name)) {
mView.setHasEmptyError(true);
removeDialog(NAME_CREDENTIAL_DIALOG);
@@ -391,16 +487,7 @@ public class CertInstaller extends Activity {
} else {
removeDialog(NAME_CREDENTIAL_DIALOG);
mCredentials.setName(name);
-
- // install everything to system keystore
- try {
- startActivityForResult(
- mCredentials.createSystemInstallIntent(appContext),
- REQUEST_SYSTEM_INSTALL_CODE);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "systemInstall(): " + e);
- toastErrorAndFinish(R.string.cert_not_saved);
- }
+ installCertificateToKeystore(appContext);
}
})
.setNegativeButton(android.R.string.cancel,
@@ -410,6 +497,17 @@ public class CertInstaller extends Activity {
return d;
}
+ private void installCertificateToKeystore(Context context) {
+ try {
+ startActivityForResult(
+ mCredentials.createSystemInstallIntent(context),
+ REQUEST_SYSTEM_INSTALL_CODE);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "installCertificateToKeystore(): ", e);
+ toastErrorAndFinish(R.string.cert_not_saved);
+ }
+ }
+
private String getDefaultName() {
String name = mCredentials.getName();
if (TextUtils.isEmpty(name)) {
diff --git a/src/com/android/certinstaller/CertInstallerMain.java b/src/com/android/certinstaller/CertInstallerMain.java
index 7e93884..4cec5fc 100644
--- a/src/com/android/certinstaller/CertInstallerMain.java
+++ b/src/com/android/certinstaller/CertInstallerMain.java
@@ -16,10 +16,14 @@
package com.android.certinstaller;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.UserManager;
import android.preference.PreferenceActivity;
import android.provider.DocumentsContract;
@@ -28,6 +32,8 @@ import android.security.KeyChain;
import android.util.Log;
import android.widget.Toast;
+import libcore.io.IoUtils;
+
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -35,8 +41,6 @@ import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
-import libcore.io.IoUtils;
-
/**
* The main class for installing certificates to the system keystore. It reacts
* to the public {@link Credentials#INSTALL_ACTION} intent.
@@ -46,6 +50,7 @@ public class CertInstallerMain extends PreferenceActivity {
private static final int REQUEST_INSTALL = 1;
private static final int REQUEST_OPEN_DOCUMENT = 2;
+ private static final int REQUEST_CONFIRM_CREDENTIALS = 3;
private static final String INSTALL_CERT_AS_USER_CLASS = ".InstallCertAsUser";
@@ -72,7 +77,8 @@ public class CertInstallerMain extends PreferenceActivity {
setResult(RESULT_CANCELED);
UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
- if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CREDENTIALS)) {
+ if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_CREDENTIALS)
+ || userManager.isGuestUser()) {
finish();
return;
}
@@ -99,27 +105,56 @@ public class CertInstallerMain extends PreferenceActivity {
// If bundle is empty of any actual credentials, ask user to open.
// Otherwise, pass extras to CertInstaller to install those credentials.
// Either way, we use KeyChain.EXTRA_NAME as the default name if available.
- if (bundle == null
- || bundle.isEmpty()
- || (bundle.size() == 1
- && (bundle.containsKey(KeyChain.EXTRA_NAME)
- || bundle.containsKey(Credentials.EXTRA_INSTALL_AS_UID)))) {
- final String[] mimeTypes = MIME_MAPPINGS.keySet().toArray(new String[0]);
- final Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- openIntent.setType("*/*");
- openIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
- openIntent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
- startActivityForResult(openIntent, REQUEST_OPEN_DOCUMENT);
+ if (nullOrEmptyBundle(bundle) || bundleContainsNameOnly(bundle)
+ || bundleContainsInstallAsUidOnly(bundle)
+ || bundleContainsExtraCertificateUsageOnly(bundle)) {
+
+ // Confirm credentials if there's only a CA certificate
+ if (installingCaCertificate(bundle)) {
+ confirmDeviceCredential();
+ } else {
+ startOpenDocumentActivity();
+ }
} else {
- final Intent installIntent = new Intent(this, CertInstaller.class);
- installIntent.putExtras(intent);
- startActivityForResult(installIntent, REQUEST_INSTALL);
+ startInstallActivity(intent);
}
} else if (Intent.ACTION_VIEW.equals(action)) {
startInstallActivity(intent.getType(), intent.getData());
}
}
+ private boolean nullOrEmptyBundle(Bundle bundle) {
+ return bundle == null || bundle.isEmpty();
+ }
+
+ private boolean bundleContainsNameOnly(Bundle bundle) {
+ return bundle.size() == 1 && bundle.containsKey(KeyChain.EXTRA_NAME);
+ }
+
+ private boolean bundleContainsInstallAsUidOnly(Bundle bundle) {
+ return bundle.size() == 1 && bundle.containsKey(Credentials.EXTRA_INSTALL_AS_UID);
+ }
+
+ private boolean bundleContainsExtraCertificateUsageOnly(Bundle bundle) {
+ return bundle.size() == 1 && bundle.containsKey(Credentials.EXTRA_CERTIFICATE_USAGE);
+ }
+
+ private boolean installingCaCertificate(Bundle bundle) {
+ return bundle != null && bundle.size() == 1 && Credentials.CERTIFICATE_USAGE_CA.equals(
+ bundle.getString(Credentials.EXTRA_CERTIFICATE_USAGE));
+ }
+
+ private void confirmDeviceCredential() {
+ KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
+ Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null,
+ null);
+ if (intent == null) { // No screenlock
+ startOpenDocumentActivity();
+ } else {
+ startActivityForResult(intent, REQUEST_CONFIRM_CREDENTIALS);
+ }
+ }
+
// The maximum amount of data to read into memory before aborting.
// Without a limit, a sufficiently-large file will run us out of memory. A
// typical certificate or WiFi config is under 10k, so 10MiB should be more
@@ -146,6 +181,33 @@ public class CertInstallerMain extends PreferenceActivity {
return bytes.toByteArray();
}
+ private void startInstallActivity(Intent intent) {
+ final Intent installIntent = new Intent(this, CertInstaller.class);
+ if (intent.getExtras() != null && intent.getExtras().getString(Intent.EXTRA_REFERRER)
+ != null) {
+ Log.v(TAG, String.format(
+ "Removing referrer extra with value %s which was not meant to be included",
+ intent.getBundleExtra(Intent.EXTRA_REFERRER)));
+ intent.removeExtra(Intent.EXTRA_REFERRER);
+ }
+ installIntent.putExtras(intent);
+
+ try {
+ // The referrer is passed as an extra because the launched-from package needs to be
+ // obtained here and not in the CertInstaller.
+ // It is also safe to add the referrer as an extra because the CertInstaller activity
+ // is not exported, which means it cannot be called from other apps.
+ IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
+ installIntent.putExtra(Intent.EXTRA_REFERRER,
+ activityTaskManager.getLaunchedFromPackage(getActivityToken()));
+ startActivityForResult(installIntent, REQUEST_INSTALL);
+ } catch (RemoteException e) {
+ Log.v(TAG, "Could not talk to activity manager.", e);
+ Toast.makeText(this, R.string.cert_temp_error, Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+
private void startInstallActivity(String mimeType, Uri uri) {
if (mimeType == null) {
mimeType = getContentResolver().getType(uri);
@@ -165,8 +227,10 @@ public class CertInstallerMain extends PreferenceActivity {
in = getContentResolver().openInputStream(uri);
final byte[] raw = readWithLimit(in);
- startInstallActivity(target, raw);
+ Intent intent = getIntent();
+ intent.putExtra(target, raw);
+ startInstallActivity(intent);
} catch (IOException e) {
Log.e(TAG, "Failed to read certificate: " + e);
Toast.makeText(this, R.string.cert_read_error, Toast.LENGTH_LONG).show();
@@ -176,13 +240,6 @@ public class CertInstallerMain extends PreferenceActivity {
}
}
- private void startInstallActivity(String target, byte[] value) {
- Intent intent = new Intent(this, CertInstaller.class);
- intent.putExtra(target, value);
-
- startActivityForResult(intent, REQUEST_INSTALL);
- }
-
private void startWifiInstallActivity(String mimeType, Uri uri) {
Intent intent = new Intent(this, WiFiInstaller.class);
try (BufferedInputStream in =
@@ -198,19 +255,40 @@ public class CertInstallerMain extends PreferenceActivity {
}
}
+ private void startOpenDocumentActivity() {
+ final String[] mimeTypes = MIME_MAPPINGS.keySet().toArray(new String[0]);
+ final Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ openIntent.setType("*/*");
+ openIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
+ openIntent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true);
+ startActivityForResult(openIntent, REQUEST_OPEN_DOCUMENT);
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_OPEN_DOCUMENT) {
- if (resultCode == RESULT_OK) {
- startInstallActivity(null, data.getData());
- } else {
+ switch (requestCode) {
+ case REQUEST_INSTALL:
+ setResult(resultCode);
finish();
- }
- } else if (requestCode == REQUEST_INSTALL) {
- setResult(resultCode);
- finish();
- } else {
- Log.w(TAG, "unknown request code: " + requestCode);
+ break;
+ case REQUEST_OPEN_DOCUMENT:
+ if (resultCode == RESULT_OK) {
+ startInstallActivity(null, data.getData());
+ } else {
+ finish();
+ }
+ break;
+ case REQUEST_CONFIRM_CREDENTIALS:
+ if (resultCode == RESULT_OK) {
+ startOpenDocumentActivity();
+ return;
+ }
+ // Failed to confirm credentials, do nothing.
+ finish();
+ break;
+ default:
+ Log.w(TAG, "unknown request code: " + requestCode);
+ break;
}
}
}
diff --git a/src/com/android/certinstaller/CredentialHelper.java b/src/com/android/certinstaller/CredentialHelper.java
index 4ec2e7e..a1e9314 100644
--- a/src/com/android/certinstaller/CredentialHelper.java
+++ b/src/com/android/certinstaller/CredentialHelper.java
@@ -16,20 +16,24 @@
package com.android.certinstaller;
+import static android.security.KeyStore.UID_SELF;
+
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.Credentials;
-import android.security.KeyChain;
import android.security.IKeyChainService;
+import android.security.KeyChain;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
+
import com.android.org.bouncycastle.asn1.ASN1InputStream;
import com.android.org.bouncycastle.asn1.ASN1Sequence;
import com.android.org.bouncycastle.asn1.DEROctetString;
@@ -39,9 +43,9 @@ import com.android.org.conscrypt.TrustedCertificateStore;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.KeyFactory;
+import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry;
-import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
@@ -64,6 +68,7 @@ class CredentialHelper {
private static final String DATA_KEY = "data";
private static final String CERTS_KEY = "crts";
private static final String USER_KEY_ALGORITHM = "user_key_algorithm";
+ private static final String SETTINGS_PACKAGE = "com.android.settings";
private static final String TAG = "CredentialHelper";
@@ -71,6 +76,8 @@ class CredentialHelper {
private HashMap<String, byte[]> mBundle = new HashMap<String, byte[]>();
private String mName = "";
+ private String mCertUsageSelected = "";
+ private String mReferrer = "";
private int mUid = -1;
private PrivateKey mUserKey;
private X509Certificate mUserCert;
@@ -91,9 +98,21 @@ class CredentialHelper {
mName = name;
}
- mUid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, -1);
+ String certUsageSelected = bundle.getString(Credentials.EXTRA_CERTIFICATE_USAGE);
+ bundle.remove(Credentials.EXTRA_CERTIFICATE_USAGE);
+ if (certUsageSelected != null) {
+ setCertUsageSelectedAndUid(certUsageSelected);
+ } else {
+ mUid = bundle.getInt(Credentials.EXTRA_INSTALL_AS_UID, -1);
+ }
bundle.remove(Credentials.EXTRA_INSTALL_AS_UID);
+ String referrer = bundle.getString(Intent.EXTRA_REFERRER);
+ bundle.remove(Intent.EXTRA_REFERRER);
+ if (referrer != null) {
+ mReferrer = referrer;
+ }
+
Log.d(TAG, "# extras: " + bundle.size());
for (String key : bundle.keySet()) {
byte[] bytes = bundle.getByteArray(key);
@@ -196,6 +215,14 @@ class CredentialHelper {
return mBundle.containsKey(Credentials.EXTRA_PRIVATE_KEY);
}
+ int getUidFromCertificateUsage(String certUsage) {
+ if (Credentials.CERTIFICATE_USAGE_WIFI.equals(certUsage)) {
+ return Process.WIFI_UID;
+ } else {
+ return UID_SELF;
+ }
+ }
+
boolean hasUserCertificate() {
return (mUserCert != null);
}
@@ -262,16 +289,17 @@ class CredentialHelper {
return mName;
}
- void setInstallAsUid(int uid) {
- mUid = uid;
+ void setCertUsageSelectedAndUid(String certUsageSelected) {
+ mCertUsageSelected = certUsageSelected;
+ mUid = getUidFromCertificateUsage(certUsageSelected);
}
- boolean isInstallAsUidSet() {
- return mUid != -1;
+ String getCertUsageSelected() {
+ return mCertUsageSelected;
}
- int getInstallAsUid() {
- return mUid;
+ boolean calledBySettings() {
+ return mReferrer != null && mReferrer.equals(SETTINGS_PACKAGE);
}
Intent createSystemInstallIntent(final Context context) {
@@ -281,22 +309,17 @@ class CredentialHelper {
intent.setComponent(ComponentName.unflattenFromString(
context.getString(R.string.config_system_install_component)));
intent.putExtra(Credentials.EXTRA_INSTALL_AS_UID, mUid);
+ intent.putExtra(Credentials.EXTRA_USER_KEY_ALIAS, mName);
try {
if (mUserKey != null) {
- intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_NAME,
- Credentials.USER_PRIVATE_KEY + mName);
intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_DATA,
mUserKey.getEncoded());
}
if (mUserCert != null) {
- intent.putExtra(Credentials.EXTRA_USER_CERTIFICATE_NAME,
- Credentials.USER_CERTIFICATE + mName);
intent.putExtra(Credentials.EXTRA_USER_CERTIFICATE_DATA,
Credentials.convertToPem(mUserCert));
}
if (!mCaCerts.isEmpty()) {
- intent.putExtra(Credentials.EXTRA_CA_CERTIFICATES_NAME,
- Credentials.CA_CERTIFICATE + mName);
X509Certificate[] caCerts
= mCaCerts.toArray(new X509Certificate[mCaCerts.size()]);
intent.putExtra(Credentials.EXTRA_CA_CERTIFICATES_DATA,
@@ -340,12 +363,8 @@ class CredentialHelper {
}
private void maybeApproveCaCert(Context context, String alias) {
- // Some CTS verifier test asks testers to reset auto approved CA cert by removing
- // lock sreen, but it's not possible if we don't have Android lock screen. (e.g.
- // Android is running in the container). In this case, disable auto cert approval.
final KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
- if (keyguardManager.isDeviceSecure(UserHandle.myUserId())
- && context.getResources().getBoolean(R.bool.config_auto_cert_approval)) {
+ if (keyguardManager.isDeviceSecure(UserHandle.myUserId())) {
// Since the cert is installed by real user, the cert is approved by the user
final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
dpm.approveCaCert(alias, UserHandle.myUserId(), true);
@@ -432,14 +451,14 @@ class CredentialHelper {
}
/**
- * Returns whether this credential contains CA certificates to be used as trust anchors
+ * Returns true if this credential contains _only_ CA certificates to be used as trust anchors
* for VPN and apps.
*/
- public boolean includesVpnAndAppsTrustAnchors() {
+ public boolean hasOnlyVpnAndAppsTrustAnchors() {
if (!hasCaCerts()) {
return false;
}
- if (getInstallAsUid() != android.security.KeyStore.UID_SELF) {
+ if (mUid != UID_SELF) {
// VPN and Apps trust anchors can only be installed under UID_SELF
return false;
}
@@ -452,4 +471,8 @@ class CredentialHelper {
return true;
}
}
+
+ public String getReferrer() {
+ return mReferrer;
+ }
}
diff --git a/src/com/android/certinstaller/WiFiInstaller.java b/src/com/android/certinstaller/WiFiInstaller.java
index 90b8eb7..41827f6 100644
--- a/src/com/android/certinstaller/WiFiInstaller.java
+++ b/src/com/android/certinstaller/WiFiInstaller.java
@@ -93,6 +93,13 @@ public class WiFiInstaller extends Activity {
public void run() {
boolean success = true;
try {
+ mWifiManager.removePasspointConfiguration(
+ mPasspointConfig.getHomeSp().getFqdn());
+ } catch (IllegalArgumentException e) {
+ // Do nothing. This is expected if a profile with this FQDN does not
+ // exist.
+ }
+ try {
mWifiManager.addOrUpdatePasspointConfiguration(mPasspointConfig);
} catch (RuntimeException rte) {
Log.w(TAG, "Caught exception while installing wifi config: " +