am 3eb6e4b4: Merge "Fix EAP-TLS reconnect after reboot issue" into klp-dev
* commit '3eb6e4b4e46d64f96cba2d92265f7d3da0b8106a': Fix EAP-TLS reconnect after reboot issue
This commit is contained in:
@@ -57,6 +57,7 @@ class WifiController extends StateMachine {
|
||||
private int mStayAwakeConditions;
|
||||
private long mIdleMillis;
|
||||
private int mSleepPolicy;
|
||||
private boolean mFirstUserSignOnSeen = false;
|
||||
|
||||
private AlarmManager mAlarmManager;
|
||||
private PendingIntent mIdleIntent;
|
||||
@@ -113,6 +114,7 @@ class WifiController extends StateMachine {
|
||||
static final int CMD_AIRPLANE_TOGGLED = BASE + 9;
|
||||
static final int CMD_SET_AP = BASE + 10;
|
||||
static final int CMD_DEFERRED_TOGGLE = BASE + 11;
|
||||
static final int CMD_USER_PRESENT = BASE + 12;
|
||||
|
||||
private DefaultState mDefaultState = new DefaultState();
|
||||
private StaEnabledState mStaEnabledState = new StaEnabledState();
|
||||
@@ -361,6 +363,9 @@ class WifiController extends StateMachine {
|
||||
case CMD_AIRPLANE_TOGGLED:
|
||||
case CMD_EMERGENCY_MODE_CHANGED:
|
||||
break;
|
||||
case CMD_USER_PRESENT:
|
||||
mFirstUserSignOnSeen = true;
|
||||
break;
|
||||
case CMD_DEFERRED_TOGGLE:
|
||||
log("DEFERRED_TOGGLE ignored due to state change");
|
||||
break;
|
||||
@@ -639,6 +644,15 @@ class WifiController extends StateMachine {
|
||||
if (msg.what == CMD_DEVICE_IDLE) {
|
||||
checkLocksAndTransitionWhenDeviceIdle();
|
||||
// We let default state handle the rest of work
|
||||
} else if (msg.what == CMD_USER_PRESENT) {
|
||||
// TLS networks can't connect until user unlocks keystore. KeyStore
|
||||
// unlocks when the user punches PIN after the reboot. So use this
|
||||
// trigger to get those networks connected.
|
||||
if (mFirstUserSignOnSeen == false) {
|
||||
mWifiStateMachine.reloadTlsNetworksAndReconnect();
|
||||
}
|
||||
mFirstUserSignOnSeen = true;
|
||||
return HANDLED;
|
||||
}
|
||||
return NOT_HANDLED;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGE
|
||||
import static com.android.server.wifi.WifiController.CMD_SCREEN_OFF;
|
||||
import static com.android.server.wifi.WifiController.CMD_SCREEN_ON;
|
||||
import static com.android.server.wifi.WifiController.CMD_SET_AP;
|
||||
import static com.android.server.wifi.WifiController.CMD_USER_PRESENT;
|
||||
import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
|
||||
/**
|
||||
* WifiService handles remote WiFi operation requests by implementing
|
||||
@@ -1084,6 +1085,8 @@ public final class WifiService extends IWifiManager.Stub {
|
||||
String action = intent.getAction();
|
||||
if (action.equals(Intent.ACTION_SCREEN_ON)) {
|
||||
mWifiController.sendMessage(CMD_SCREEN_ON);
|
||||
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
|
||||
mWifiController.sendMessage(CMD_USER_PRESENT);
|
||||
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
|
||||
mWifiController.sendMessage(CMD_SCREEN_OFF);
|
||||
} else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
|
||||
@@ -1120,6 +1123,7 @@ public final class WifiService extends IWifiManager.Stub {
|
||||
private void registerForBroadcasts() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
|
||||
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
|
||||
intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
|
||||
|
||||
@@ -57,6 +57,7 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
@@ -742,6 +743,26 @@ class WifiConfigStore {
|
||||
markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
|
||||
}
|
||||
|
||||
boolean needsUnlockedKeyStore() {
|
||||
|
||||
// Any network using certificates to authenticate access requires
|
||||
// unlocked key store; unless the certificates can be stored with
|
||||
// hardware encryption
|
||||
|
||||
for(WifiConfiguration config : mConfiguredNetworks.values()) {
|
||||
|
||||
if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
|
||||
&& config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
|
||||
|
||||
if (config.enterpriseConfig.needsSoftwareBackedKeyStore()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void writeIpAndProxyConfigurations() {
|
||||
|
||||
/* Make a copy */
|
||||
@@ -1223,7 +1244,6 @@ class WifiConfigStore {
|
||||
* Keyguard settings may eventually be controlled by device policy.
|
||||
* We check here if keystore is unlocked before installing
|
||||
* credentials.
|
||||
* TODO: Figure a way to store these credentials for wifi alone
|
||||
* TODO: Do we need a dialog here ?
|
||||
*/
|
||||
if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
|
||||
@@ -1583,6 +1603,7 @@ class WifiConfigStore {
|
||||
}
|
||||
|
||||
config.enterpriseConfig.migrateCerts(mKeyStore);
|
||||
config.enterpriseConfig.initializeSoftwareKeystoreFlag(mKeyStore);
|
||||
}
|
||||
|
||||
private String removeDoubleQuotes(String string) {
|
||||
|
||||
@@ -19,8 +19,10 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Process;
|
||||
import android.security.Credentials;
|
||||
import android.security.KeyChain;
|
||||
import android.security.KeyStore;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -43,6 +45,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class WifiEnterpriseConfig implements Parcelable {
|
||||
private static final String TAG = "WifiEnterpriseConfig";
|
||||
private static final boolean DBG = false;
|
||||
/**
|
||||
* In old configurations, the "private_key" field was used. However, newer
|
||||
* configurations use the key_id field with the engine_id set to "keystore".
|
||||
@@ -91,6 +94,7 @@ public class WifiEnterpriseConfig implements Parcelable {
|
||||
private X509Certificate mCaCert;
|
||||
private PrivateKey mClientPrivateKey;
|
||||
private X509Certificate mClientCertificate;
|
||||
private boolean mNeedsSoftwareKeystore = false;
|
||||
|
||||
/** This represents an empty value of an enterprise field.
|
||||
* NULL is used at wpa_supplicant to indicate an empty value
|
||||
@@ -509,6 +513,18 @@ public class WifiEnterpriseConfig implements Parcelable {
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean isHardwareBackedKey(PrivateKey key) {
|
||||
return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm());
|
||||
}
|
||||
|
||||
static boolean hasHardwareBackedKey(Certificate certificate) {
|
||||
return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm());
|
||||
}
|
||||
|
||||
boolean needsSoftwareBackedKeyStore() {
|
||||
return mNeedsSoftwareKeystore;
|
||||
}
|
||||
|
||||
boolean installKeys(android.security.KeyStore keyStore, String name) {
|
||||
boolean ret = true;
|
||||
String privKeyName = Credentials.USER_PRIVATE_KEY + name;
|
||||
@@ -516,8 +532,23 @@ public class WifiEnterpriseConfig implements Parcelable {
|
||||
String caCertName = Credentials.CA_CERTIFICATE + name;
|
||||
if (mClientCertificate != null) {
|
||||
byte[] privKeyData = mClientPrivateKey.getEncoded();
|
||||
ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
|
||||
KeyStore.FLAG_ENCRYPTED);
|
||||
if (isHardwareBackedKey(mClientPrivateKey)) {
|
||||
// Hardware backed key store is secure enough to store keys un-encrypted, this
|
||||
// removes the need for user to punch a PIN to get access to these keys
|
||||
if (DBG) Slog.d(TAG, "importing keys " + name + " in hardware backed " +
|
||||
"store");
|
||||
ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
|
||||
KeyStore.FLAG_NONE);
|
||||
} else {
|
||||
// Software backed key store is NOT secure enough to store keys un-encrypted.
|
||||
// Save keys encrypted so they are protected with user's PIN. User will
|
||||
// have to unlock phone before being able to use these keys and connect to
|
||||
// networks.
|
||||
if (DBG) Slog.d(TAG, "importing keys " + name + " in software backed store");
|
||||
ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID,
|
||||
KeyStore.FLAG_ENCRYPTED);
|
||||
mNeedsSoftwareKeystore = true;
|
||||
}
|
||||
if (ret == false) {
|
||||
return ret;
|
||||
}
|
||||
@@ -561,7 +592,9 @@ public class WifiEnterpriseConfig implements Parcelable {
|
||||
Certificate cert) {
|
||||
try {
|
||||
byte[] certData = Credentials.convertToPem(cert);
|
||||
return keyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_ENCRYPTED);
|
||||
if (DBG) Slog.d(TAG, "putting certificate " + name + " in keystore");
|
||||
return keyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
|
||||
|
||||
} catch (IOException e1) {
|
||||
return false;
|
||||
} catch (CertificateException e2) {
|
||||
@@ -573,6 +606,7 @@ public class WifiEnterpriseConfig implements Parcelable {
|
||||
String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX);
|
||||
// a valid client certificate is configured
|
||||
if (!TextUtils.isEmpty(client)) {
|
||||
if (DBG) Slog.d(TAG, "removing client private key and user cert");
|
||||
keyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
|
||||
keyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
|
||||
}
|
||||
@@ -580,6 +614,7 @@ public class WifiEnterpriseConfig implements Parcelable {
|
||||
String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX);
|
||||
// a valid ca certificate is configured
|
||||
if (!TextUtils.isEmpty(ca)) {
|
||||
if (DBG) Slog.d(TAG, "removing CA cert");
|
||||
keyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
|
||||
}
|
||||
}
|
||||
@@ -684,6 +719,61 @@ public class WifiEnterpriseConfig implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
void initializeSoftwareKeystoreFlag(android.security.KeyStore keyStore) {
|
||||
String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX);
|
||||
if (!TextUtils.isEmpty(client)) {
|
||||
// a valid client certificate is configured
|
||||
|
||||
// BUGBUG: keyStore.get() never returns certBytes; because it is not
|
||||
// taking WIFI_UID as a parameter. It always looks for certificate
|
||||
// with SYSTEM_UID, and never finds any Wifi certificates. Assuming that
|
||||
// all certificates need software keystore until we get the get() API
|
||||
// fixed.
|
||||
|
||||
mNeedsSoftwareKeystore = true;
|
||||
|
||||
/*
|
||||
try {
|
||||
|
||||
if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials
|
||||
.USER_CERTIFICATE + client);
|
||||
|
||||
CertificateFactory factory = CertificateFactory.getInstance("X.509");
|
||||
if (factory == null) {
|
||||
Slog.e(TAG, "Error getting certificate factory");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client);
|
||||
if (certBytes != null) {
|
||||
Certificate cert = (X509Certificate) factory.generateCertificate(
|
||||
new ByteArrayInputStream(certBytes));
|
||||
|
||||
if (cert != null) {
|
||||
mNeedsSoftwareKeystore = hasHardwareBackedKey(cert);
|
||||
|
||||
if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials
|
||||
.USER_CERTIFICATE + client);
|
||||
if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" :
|
||||
"does not need" ) + " software key store");
|
||||
} else {
|
||||
Slog.d(TAG, "could not generate certificate");
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "Could not load client certificate " + Credentials
|
||||
.USER_CERTIFICATE + client);
|
||||
mNeedsSoftwareKeystore = true;
|
||||
}
|
||||
|
||||
} catch(CertificateException e) {
|
||||
Slog.e(TAG, "Could not read certificates");
|
||||
mCaCert = null;
|
||||
mClientCertificate = null;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
private String removeDoubleQuotes(String string) {
|
||||
if (TextUtils.isEmpty(string)) return "";
|
||||
int length = string.length();
|
||||
|
||||
@@ -426,6 +426,8 @@ public class WifiStateMachine extends StateMachine {
|
||||
static final int CMD_IP_ADDRESS_UPDATED = BASE + 140;
|
||||
/* An IP address was removed from our interface */
|
||||
static final int CMD_IP_ADDRESS_REMOVED = BASE + 141;
|
||||
/* Reload all networks and reconnect */
|
||||
static final int CMD_RELOAD_TLS_AND_RECONNECT = BASE + 142;
|
||||
|
||||
/* Wifi state machine modes of operation */
|
||||
/* CONNECT_MODE - connect to any 'known' AP when it becomes available */
|
||||
@@ -1319,6 +1321,14 @@ public class WifiStateMachine extends StateMachine {
|
||||
sendMessage(CMD_REASSOCIATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload networks and then reconnect; helps load correct data for TLS networks
|
||||
*/
|
||||
|
||||
public void reloadTlsNetworksAndReconnect() {
|
||||
sendMessage(CMD_RELOAD_TLS_AND_RECONNECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a network synchronously
|
||||
*
|
||||
@@ -2445,6 +2455,7 @@ public class WifiStateMachine extends StateMachine {
|
||||
case CMD_DISCONNECT:
|
||||
case CMD_RECONNECT:
|
||||
case CMD_REASSOCIATE:
|
||||
case CMD_RELOAD_TLS_AND_RECONNECT:
|
||||
case WifiMonitor.SUP_CONNECTION_EVENT:
|
||||
case WifiMonitor.SUP_DISCONNECTION_EVENT:
|
||||
case WifiMonitor.NETWORK_CONNECTION_EVENT:
|
||||
@@ -3395,6 +3406,13 @@ public class WifiStateMachine extends StateMachine {
|
||||
case CMD_REASSOCIATE:
|
||||
mWifiNative.reassociate();
|
||||
break;
|
||||
case CMD_RELOAD_TLS_AND_RECONNECT:
|
||||
if (mWifiConfigStore.needsUnlockedKeyStore()) {
|
||||
logd("Reconnecting to give a chance to un-connected TLS networks");
|
||||
mWifiNative.disconnect();
|
||||
mWifiNative.reconnect();
|
||||
}
|
||||
break;
|
||||
case WifiManager.CONNECT_NETWORK:
|
||||
/* The connect message can contain a network id passed as arg1 on message or
|
||||
* or a config passed as obj on message.
|
||||
|
||||
Reference in New Issue
Block a user