diff options
Diffstat (limited to 'service/java/com/android/server/wifi/hotspot2/PasspointProvider.java')
-rw-r--r-- | service/java/com/android/server/wifi/hotspot2/PasspointProvider.java | 1009 |
1 files changed, 0 insertions, 1009 deletions
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java deleted file mode 100644 index 8e7364d4b..000000000 --- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java +++ /dev/null @@ -1,1009 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -package com.android.server.wifi.hotspot2; - -import static android.net.wifi.WifiConfiguration.MeteredOverride; - -import android.annotation.Nullable; -import android.net.wifi.EAPConstants; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiEnterpriseConfig; -import android.net.wifi.hotspot2.PasspointConfiguration; -import android.net.wifi.hotspot2.pps.Credential; -import android.net.wifi.hotspot2.pps.Credential.SimCredential; -import android.net.wifi.hotspot2.pps.Credential.UserCredential; -import android.net.wifi.hotspot2.pps.HomeSp; -import android.telephony.TelephonyManager; -import android.text.TextUtils; -import android.util.Base64; -import android.util.Log; -import android.util.Pair; - -import com.android.server.wifi.IMSIParameter; -import com.android.server.wifi.WifiCarrierInfoManager; -import com.android.server.wifi.WifiKeyStore; -import com.android.server.wifi.hotspot2.anqp.ANQPElement; -import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType; -import com.android.server.wifi.hotspot2.anqp.DomainNameElement; -import com.android.server.wifi.hotspot2.anqp.NAIRealmElement; -import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement; -import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement; -import com.android.server.wifi.hotspot2.anqp.eap.AuthParam; -import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth; -import com.android.server.wifi.util.ArrayUtils; -import com.android.server.wifi.util.InformationElementUtil.RoamingConsortium; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.cert.Certificate; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Abstraction for Passpoint service provider. This class contains the both static - * Passpoint configuration data and the runtime data (e.g. blacklisted SSIDs, statistics). - */ -public class PasspointProvider { - private static final String TAG = "PasspointProvider"; - - /** - * Used as part of alias string for certificates and keys. The alias string is in the format - * of: [KEY_TYPE]_HS2_[ProviderID] - * For example: "CACERT_HS2_0", "USRCERT_HS2_0", "USRPKEY_HS2_0", "CACERT_HS2_REMEDIATION_0" - */ - private static final String ALIAS_HS_TYPE = "HS2_"; - private static final String ALIAS_ALIAS_REMEDIATION_TYPE = "REMEDIATION_"; - - private static final String SYSTEM_CA_STORE_PATH = "/system/etc/security/cacerts"; - - private final PasspointConfiguration mConfig; - private final WifiKeyStore mKeyStore; - - /** - * Aliases for the private keys and certificates installed in the keystore. Each alias - * is a suffix of the actual certificate or key name installed in the keystore. The - * certificate or key name in the keystore is consist of |Type|_|alias|. - * This will be consistent with the usage of the term "alias" in {@link WifiEnterpriseConfig}. - */ - private List<String> mCaCertificateAliases; - private String mClientPrivateKeyAndCertificateAlias; - private String mRemediationCaCertificateAlias; - - private final long mProviderId; - private final int mCreatorUid; - private final String mPackageName; - - private final IMSIParameter mImsiParameter; - - private final int mEAPMethodID; - private final AuthParam mAuthParam; - private final WifiCarrierInfoManager mWifiCarrierInfoManager; - - private int mBestGuessCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; - private boolean mHasEverConnected; - private boolean mIsShared; - private boolean mIsFromSuggestion; - private boolean mIsTrusted; - private boolean mVerboseLoggingEnabled; - - public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, - WifiCarrierInfoManager wifiCarrierInfoManager, long providerId, int creatorUid, - String packageName, boolean isFromSuggestion) { - this(config, keyStore, wifiCarrierInfoManager, providerId, creatorUid, packageName, - isFromSuggestion, null, null, null, false, false); - } - - public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, - WifiCarrierInfoManager wifiCarrierInfoManager, long providerId, int creatorUid, - String packageName, boolean isFromSuggestion, List<String> caCertificateAliases, - String clientPrivateKeyAndCertificateAlias, String remediationCaCertificateAlias, - boolean hasEverConnected, boolean isShared) { - // Maintain a copy of the configuration to avoid it being updated by others. - mConfig = new PasspointConfiguration(config); - mKeyStore = keyStore; - mProviderId = providerId; - mCreatorUid = creatorUid; - mPackageName = packageName; - mCaCertificateAliases = caCertificateAliases; - mClientPrivateKeyAndCertificateAlias = clientPrivateKeyAndCertificateAlias; - mRemediationCaCertificateAlias = remediationCaCertificateAlias; - mHasEverConnected = hasEverConnected; - mIsShared = isShared; - mIsFromSuggestion = isFromSuggestion; - mWifiCarrierInfoManager = wifiCarrierInfoManager; - mIsTrusted = true; - - // Setup EAP method and authentication parameter based on the credential. - if (mConfig.getCredential().getUserCredential() != null) { - mEAPMethodID = EAPConstants.EAP_TTLS; - mAuthParam = new NonEAPInnerAuth(NonEAPInnerAuth.getAuthTypeID( - mConfig.getCredential().getUserCredential().getNonEapInnerMethod())); - mImsiParameter = null; - } else if (mConfig.getCredential().getCertCredential() != null) { - mEAPMethodID = EAPConstants.EAP_TLS; - mAuthParam = null; - mImsiParameter = null; - } else { - mEAPMethodID = mConfig.getCredential().getSimCredential().getEapType(); - mAuthParam = null; - mImsiParameter = IMSIParameter.build( - mConfig.getCredential().getSimCredential().getImsi()); - } - } - - /** - * Set passpoint network trusted or not. - * Default is true. Only allows to change when it is from suggestion. - */ - public void setTrusted(boolean trusted) { - if (!mIsFromSuggestion) { - Log.e(TAG, "setTrusted can only be called for suggestion passpoint network"); - return; - } - mIsTrusted = trusted; - } - - public boolean isTrusted() { - return mIsTrusted; - } - - public PasspointConfiguration getConfig() { - // Return a copy of the configuration to avoid it being updated by others. - return new PasspointConfiguration(mConfig); - } - - public List<String> getCaCertificateAliases() { - return mCaCertificateAliases; - } - - public String getClientPrivateKeyAndCertificateAlias() { - return mClientPrivateKeyAndCertificateAlias; - } - - public String getRemediationCaCertificateAlias() { - return mRemediationCaCertificateAlias; - } - - public long getProviderId() { - return mProviderId; - } - - public int getCreatorUid() { - return mCreatorUid; - } - - @Nullable - public String getPackageName() { - return mPackageName; - } - - public boolean getHasEverConnected() { - return mHasEverConnected; - } - - public void setHasEverConnected(boolean hasEverConnected) { - mHasEverConnected = hasEverConnected; - } - - public boolean isFromSuggestion() { - return mIsFromSuggestion; - } - - /** - * Enable/disable the auto-join configuration of the corresponding passpoint configuration. - * - * @return true if the setting has changed - */ - public boolean setAutojoinEnabled(boolean autoJoinEnabled) { - boolean changed = mConfig.isAutojoinEnabled() != autoJoinEnabled; - mConfig.setAutojoinEnabled(autoJoinEnabled); - return changed; - } - - public boolean isAutojoinEnabled() { - return mConfig.isAutojoinEnabled(); - } - - /** - * Enable/disable mac randomization for this passpoint profile. - * - * @return true if the setting has changed - */ - public boolean setMacRandomizationEnabled(boolean enabled) { - boolean changed = mConfig.isMacRandomizationEnabled() != enabled; - mConfig.setMacRandomizationEnabled(enabled); - return changed; - } - - /** - * Get whether mac randomization is enabled for this passpoint profile. - */ - public boolean isMacRandomizationEnabled() { - return mConfig.isMacRandomizationEnabled(); - } - - /** - * Get the metered override for this passpoint profile. - * - * @return true if the setting has changed - */ - public boolean setMeteredOverride(@MeteredOverride int meteredOverride) { - boolean changed = mConfig.getMeteredOverride() != meteredOverride; - mConfig.setMeteredOverride(meteredOverride); - return changed; - } - - /** - * Install certificates and key based on current configuration. - * Note: the certificates and keys in the configuration will get cleared once - * they're installed in the keystore. - * - * @return true on success - */ - public boolean installCertsAndKeys() { - // Install CA certificate. - X509Certificate[] x509Certificates = mConfig.getCredential().getCaCertificates(); - if (x509Certificates != null) { - mCaCertificateAliases = new ArrayList<>(); - for (int i = 0; i < x509Certificates.length; i++) { - String alias = String.format("%s%s_%d", ALIAS_HS_TYPE, mProviderId, i); - if (!mKeyStore.putCaCertInKeyStore(alias, x509Certificates[i])) { - Log.e(TAG, "Failed to install CA Certificate " + alias); - uninstallCertsAndKeys(); - return false; - } else { - mCaCertificateAliases.add(alias); - } - } - } - - // Install the client private key & certificate. - if (mConfig.getCredential().getClientPrivateKey() != null - && mConfig.getCredential().getClientCertificateChain() != null) { - String keyName = ALIAS_HS_TYPE + mProviderId; - PrivateKey clientKey = mConfig.getCredential().getClientPrivateKey(); - X509Certificate clientCert = getClientCertificate( - mConfig.getCredential().getClientCertificateChain(), - mConfig.getCredential().getCertCredential().getCertSha256Fingerprint()); - if (clientCert == null) { - Log.e(TAG, "Failed to locate client certificate"); - uninstallCertsAndKeys(); - return false; - } - if (!mKeyStore.putUserPrivKeyAndCertsInKeyStore( - keyName, clientKey, new Certificate[] {clientCert})) { - Log.e(TAG, "Failed to install client private key or certificate"); - uninstallCertsAndKeys(); - return false; - } - mClientPrivateKeyAndCertificateAlias = keyName; - } - - if (mConfig.getSubscriptionUpdate() != null) { - X509Certificate certificate = mConfig.getSubscriptionUpdate().getCaCertificate(); - if (certificate == null) { - Log.e(TAG, "Failed to locate CA certificate for remediation"); - uninstallCertsAndKeys(); - return false; - } - String certName = ALIAS_HS_TYPE + ALIAS_ALIAS_REMEDIATION_TYPE + mProviderId; - if (!mKeyStore.putCaCertInKeyStore(certName, certificate)) { - Log.e(TAG, "Failed to install CA certificate for remediation"); - uninstallCertsAndKeys(); - return false; - } - mRemediationCaCertificateAlias = certName; - } - - // Clear the keys and certificates in the configuration. - mConfig.getCredential().setCaCertificates(null); - mConfig.getCredential().setClientPrivateKey(null); - mConfig.getCredential().setClientCertificateChain(null); - if (mConfig.getSubscriptionUpdate() != null) { - mConfig.getSubscriptionUpdate().setCaCertificate(null); - } - return true; - } - - /** - * Remove any installed certificates and key. - */ - public void uninstallCertsAndKeys() { - if (mCaCertificateAliases != null) { - for (String certificateAlias : mCaCertificateAliases) { - if (!mKeyStore.removeEntryFromKeyStore(certificateAlias)) { - Log.e(TAG, "Failed to remove entry: " + certificateAlias); - } - } - mCaCertificateAliases = null; - } - if (mClientPrivateKeyAndCertificateAlias != null) { - if (!mKeyStore.removeEntryFromKeyStore(mClientPrivateKeyAndCertificateAlias)) { - Log.e(TAG, "Failed to remove entry: " + mClientPrivateKeyAndCertificateAlias); - } - mClientPrivateKeyAndCertificateAlias = null; - } - if (mRemediationCaCertificateAlias != null) { - if (!mKeyStore.removeEntryFromKeyStore(mRemediationCaCertificateAlias)) { - Log.e(TAG, "Failed to remove entry: " + mRemediationCaCertificateAlias); - } - mRemediationCaCertificateAlias = null; - } - } - - /** - * Try to update the carrier ID according to the IMSI parameter of passpoint configuration. - * - * @return true if the carrier ID is updated, otherwise false. - */ - public boolean tryUpdateCarrierId() { - return mWifiCarrierInfoManager.tryUpdateCarrierIdForPasspoint(mConfig); - } - - private @Nullable String getMatchingSimImsi() { - String matchingSIMImsi = null; - if (mConfig.getCarrierId() != TelephonyManager.UNKNOWN_CARRIER_ID) { - matchingSIMImsi = mWifiCarrierInfoManager - .getMatchingImsi(mConfig.getCarrierId()); - } else { - // Get the IMSI and carrier ID of SIM card which match with the IMSI prefix from - // passpoint profile - Pair<String, Integer> imsiCarrierIdPair = mWifiCarrierInfoManager - .getMatchingImsiCarrierId(mConfig.getCredential().getSimCredential().getImsi()); - if (imsiCarrierIdPair != null) { - matchingSIMImsi = imsiCarrierIdPair.first; - mBestGuessCarrierId = imsiCarrierIdPair.second; - } - } - - return matchingSIMImsi; - } - - /** - * Return the matching status with the given AP, based on the ANQP elements from the AP. - * - * @param anqpElements ANQP elements from the AP - * @param roamingConsortiumFromAp Roaming Consortium information element from the AP - * @return {@link PasspointMatch} - */ - public PasspointMatch match(Map<ANQPElementType, ANQPElement> anqpElements, - RoamingConsortium roamingConsortiumFromAp) { - // If the profile requires a SIM credential, make sure that the installed SIM matches - String matchingSimImsi = null; - if (mConfig.getCredential().getSimCredential() != null) { - matchingSimImsi = getMatchingSimImsi(); - if (TextUtils.isEmpty(matchingSimImsi)) { - if (mVerboseLoggingEnabled) { - Log.d(TAG, "No SIM card with IMSI " - + mConfig.getCredential().getSimCredential().getImsi() - + " is installed, final match: " + PasspointMatch.None); - } - return PasspointMatch.None; - } - } - - // Match FQDN for Home provider or RCOI(s) for Roaming provider - // For SIM credential, the FQDN is in the format of wlan.mnc*.mcc*.3gppnetwork.org - PasspointMatch providerMatch = matchFqdnAndRcoi(anqpElements, roamingConsortiumFromAp, - matchingSimImsi); - - // 3GPP Network matching - if (providerMatch == PasspointMatch.None && ANQPMatcher.matchThreeGPPNetwork( - (ThreeGPPNetworkElement) anqpElements.get(ANQPElementType.ANQP3GPPNetwork), - mImsiParameter, matchingSimImsi)) { - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Final RoamingProvider match with " - + anqpElements.get(ANQPElementType.ANQP3GPPNetwork)); - } - return PasspointMatch.RoamingProvider; - } - - // Perform NAI Realm matching - boolean realmMatch = ANQPMatcher.matchNAIRealm( - (NAIRealmElement) anqpElements.get(ANQPElementType.ANQPNAIRealm), - mConfig.getCredential().getRealm()); - - // In case of no realm match, return provider match as is. - if (!realmMatch) { - if (mVerboseLoggingEnabled) { - Log.d(TAG, "No NAI realm match, final match: " + providerMatch); - } - return providerMatch; - } - - if (mVerboseLoggingEnabled) { - Log.d(TAG, "NAI realm match with " + mConfig.getCredential().getRealm()); - } - - // Promote the provider match to RoamingProvider if provider match is not found, but NAI - // realm is matched. - if (providerMatch == PasspointMatch.None) { - providerMatch = PasspointMatch.RoamingProvider; - } - - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Final match: " + providerMatch); - } - return providerMatch; - } - - /** - * Generate a WifiConfiguration based on the provider's configuration. The generated - * WifiConfiguration will include all the necessary credentials for network connection except - * the SSID, which should be added by the caller when the config is being used for network - * connection. - * - * @return {@link WifiConfiguration} - */ - public WifiConfiguration getWifiConfig() { - WifiConfiguration wifiConfig = new WifiConfiguration(); - wifiConfig.FQDN = mConfig.getHomeSp().getFqdn(); - wifiConfig.setPasspointUniqueId(mConfig.getUniqueId()); - if (mConfig.getHomeSp().getRoamingConsortiumOis() != null) { - wifiConfig.roamingConsortiumIds = Arrays.copyOf( - mConfig.getHomeSp().getRoamingConsortiumOis(), - mConfig.getHomeSp().getRoamingConsortiumOis().length); - } - if (mConfig.getUpdateIdentifier() != Integer.MIN_VALUE) { - // R2 profile, it needs to set updateIdentifier HS2.0 Indication element as PPS MO - // ID in Association Request. - wifiConfig.updateIdentifier = Integer.toString(mConfig.getUpdateIdentifier()); - if (isMeteredNetwork(mConfig)) { - wifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED; - } - } - wifiConfig.providerFriendlyName = mConfig.getHomeSp().getFriendlyName(); - wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); - wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); - int carrierId = mConfig.getCarrierId(); - if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { - carrierId = mBestGuessCarrierId; - } - wifiConfig.carrierId = carrierId; - - // Set RSN only to tell wpa_supplicant that this network is for Passpoint. - wifiConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN); - - WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); - enterpriseConfig.setRealm(mConfig.getCredential().getRealm()); - enterpriseConfig.setDomainSuffixMatch(mConfig.getHomeSp().getFqdn()); - if (mConfig.getCredential().getUserCredential() != null) { - buildEnterpriseConfigForUserCredential(enterpriseConfig, - mConfig.getCredential().getUserCredential()); - setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm()); - } else if (mConfig.getCredential().getCertCredential() != null) { - buildEnterpriseConfigForCertCredential(enterpriseConfig); - setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm()); - } else { - buildEnterpriseConfigForSimCredential(enterpriseConfig, - mConfig.getCredential().getSimCredential()); - } - // If AAA server trusted names are specified, use it to replace HOME SP FQDN - // and use system CA regardless of provisioned CA certificate. - if (!ArrayUtils.isEmpty(mConfig.getAaaServerTrustedNames())) { - enterpriseConfig.setDomainSuffixMatch( - String.join(";", mConfig.getAaaServerTrustedNames())); - enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH); - } - wifiConfig.enterpriseConfig = enterpriseConfig; - // PPS MO Credential/CheckAAAServerCertStatus node contains a flag which indicates - // if the mobile device needs to check the AAA server certificate's revocation status - // during EAP authentication. - if (mConfig.getCredential().getCheckAaaServerCertStatus()) { - // Check server certificate using OCSP (Online Certificate Status Protocol). - wifiConfig.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS); - } - wifiConfig.allowAutojoin = isAutojoinEnabled(); - wifiConfig.shared = mIsShared; - wifiConfig.fromWifiNetworkSuggestion = mIsFromSuggestion; - wifiConfig.ephemeral = mIsFromSuggestion; - wifiConfig.creatorName = mPackageName; - wifiConfig.creatorUid = mCreatorUid; - wifiConfig.trusted = mIsTrusted; - if (mConfig.isMacRandomizationEnabled()) { - wifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT; - } else { - wifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE; - } - wifiConfig.meteredOverride = mConfig.getMeteredOverride(); - return wifiConfig; - } - - /** - * @return true if provider is backed by a SIM credential. - */ - public boolean isSimCredential() { - return mConfig.getCredential().getSimCredential() != null; - } - - /** - * Convert a legacy {@link WifiConfiguration} representation of a Passpoint configuration to - * a {@link PasspointConfiguration}. This is used for migrating legacy Passpoint - * configuration (release N and older). - * - * @param wifiConfig The {@link WifiConfiguration} to convert - * @return {@link PasspointConfiguration} - */ - public static PasspointConfiguration convertFromWifiConfig(WifiConfiguration wifiConfig) { - PasspointConfiguration passpointConfig = new PasspointConfiguration(); - - // Setup HomeSP. - HomeSp homeSp = new HomeSp(); - if (TextUtils.isEmpty(wifiConfig.FQDN)) { - Log.e(TAG, "Missing FQDN"); - return null; - } - homeSp.setFqdn(wifiConfig.FQDN); - homeSp.setFriendlyName(wifiConfig.providerFriendlyName); - if (wifiConfig.roamingConsortiumIds != null) { - homeSp.setRoamingConsortiumOis(Arrays.copyOf( - wifiConfig.roamingConsortiumIds, wifiConfig.roamingConsortiumIds.length)); - } - passpointConfig.setHomeSp(homeSp); - passpointConfig.setCarrierId(wifiConfig.carrierId); - - // Setup Credential. - Credential credential = new Credential(); - credential.setRealm(wifiConfig.enterpriseConfig.getRealm()); - switch (wifiConfig.enterpriseConfig.getEapMethod()) { - case WifiEnterpriseConfig.Eap.TTLS: - credential.setUserCredential(buildUserCredentialFromEnterpriseConfig( - wifiConfig.enterpriseConfig)); - break; - case WifiEnterpriseConfig.Eap.TLS: - Credential.CertificateCredential certCred = new Credential.CertificateCredential(); - certCred.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3); - credential.setCertCredential(certCred); - break; - case WifiEnterpriseConfig.Eap.SIM: - credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( - EAPConstants.EAP_SIM, wifiConfig.enterpriseConfig)); - break; - case WifiEnterpriseConfig.Eap.AKA: - credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( - EAPConstants.EAP_AKA, wifiConfig.enterpriseConfig)); - break; - case WifiEnterpriseConfig.Eap.AKA_PRIME: - credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( - EAPConstants.EAP_AKA_PRIME, wifiConfig.enterpriseConfig)); - break; - default: - Log.e(TAG, "Unsupported EAP method: " - + wifiConfig.enterpriseConfig.getEapMethod()); - return null; - } - if (credential.getUserCredential() == null && credential.getCertCredential() == null - && credential.getSimCredential() == null) { - Log.e(TAG, "Missing credential"); - return null; - } - passpointConfig.setCredential(credential); - - return passpointConfig; - } - - @Override - public boolean equals(Object thatObject) { - if (this == thatObject) { - return true; - } - if (!(thatObject instanceof PasspointProvider)) { - return false; - } - PasspointProvider that = (PasspointProvider) thatObject; - return mProviderId == that.mProviderId - && (mCaCertificateAliases == null ? that.mCaCertificateAliases == null - : mCaCertificateAliases.equals(that.mCaCertificateAliases)) - && TextUtils.equals(mClientPrivateKeyAndCertificateAlias, - that.mClientPrivateKeyAndCertificateAlias) - && (mConfig == null ? that.mConfig == null : mConfig.equals(that.mConfig)) - && TextUtils.equals(mRemediationCaCertificateAlias, - that.mRemediationCaCertificateAlias); - } - - @Override - public int hashCode() { - return Objects.hash(mProviderId, mCaCertificateAliases, - mClientPrivateKeyAndCertificateAlias, mConfig, mRemediationCaCertificateAlias); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("ProviderId: ").append(mProviderId).append("\n"); - builder.append("CreatorUID: ").append(mCreatorUid).append("\n"); - builder.append("Best guess Carrier ID: ").append(mBestGuessCarrierId).append("\n"); - builder.append("Ever connected: ").append(mHasEverConnected).append("\n"); - builder.append("Shared: ").append(mIsShared).append("\n"); - builder.append("Suggestion: ").append(mIsFromSuggestion).append("\n"); - builder.append("Trusted: ").append(mIsTrusted).append("\n"); - - if (mPackageName != null) { - builder.append("PackageName: ").append(mPackageName).append("\n"); - } - builder.append("Configuration Begin ---\n"); - builder.append(mConfig); - builder.append("Configuration End ---\n"); - builder.append("WifiConfiguration Begin ---\n"); - builder.append(getWifiConfig()); - builder.append("WifiConfiguration End ---\n"); - return builder.toString(); - } - - /** - * Retrieve the client certificate from the certificates chain. The certificate - * with the matching SHA256 digest is the client certificate. - * - * @param certChain The client certificates chain - * @param expectedSha256Fingerprint The expected SHA256 digest of the client certificate - * @return {@link java.security.cert.X509Certificate} - */ - private static X509Certificate getClientCertificate(X509Certificate[] certChain, - byte[] expectedSha256Fingerprint) { - if (certChain == null) { - return null; - } - try { - MessageDigest digester = MessageDigest.getInstance("SHA-256"); - for (X509Certificate certificate : certChain) { - digester.reset(); - byte[] fingerprint = digester.digest(certificate.getEncoded()); - if (Arrays.equals(expectedSha256Fingerprint, fingerprint)) { - return certificate; - } - } - } catch (CertificateEncodingException | NoSuchAlgorithmException e) { - return null; - } - - return null; - } - - /** - * Determines the Passpoint network is a metered network. - * - * Expiration date -> non-metered - * Data limit -> metered - * Time usage limit -> metered - * @param passpointConfig instance of {@link PasspointConfiguration} - * @return {@code true} if the network is a metered network, {@code false} otherwise. - */ - private boolean isMeteredNetwork(PasspointConfiguration passpointConfig) { - if (passpointConfig == null) return false; - - // If DataLimit is zero, there is unlimited data usage for the account. - // If TimeLimit is zero, there is unlimited time usage for the account. - return passpointConfig.getUsageLimitDataLimit() > 0 - || passpointConfig.getUsageLimitTimeLimitInMinutes() > 0; - } - - /** - * Match given OIs to the Roaming Consortium OIs - * - * @param providerOis Provider OIs to match against - * @param roamingConsortiumElement RCOIs in the ANQP element - * @param roamingConsortiumFromAp RCOIs in the AP scan results - * @param matchAll Indicates if all providerOis must match the RCOIs elements - * @return {@code true} if there is a match, {@code false} otherwise. - */ - private boolean matchOis(long[] providerOis, - RoamingConsortiumElement roamingConsortiumElement, - RoamingConsortium roamingConsortiumFromAp, - boolean matchAll) { - - - // ANQP Roaming Consortium OI matching. - if (ANQPMatcher.matchRoamingConsortium(roamingConsortiumElement, providerOis, matchAll)) { - if (mVerboseLoggingEnabled) { - Log.e(TAG, "ANQP RCOI match " + roamingConsortiumElement); - } - return true; - } - - // AP Roaming Consortium OI matching. - long[] apRoamingConsortiums = roamingConsortiumFromAp.getRoamingConsortiums(); - if (apRoamingConsortiums == null || providerOis == null) { - return false; - } - // Roaming Consortium OI information element matching. - for (long apOi: apRoamingConsortiums) { - boolean matched = false; - for (long providerOi: providerOis) { - if (apOi == providerOi) { - if (mVerboseLoggingEnabled) { - Log.e(TAG, "AP RCOI match: " + apOi); - } - if (!matchAll) { - return true; - } else { - matched = true; - break; - } - } - } - if (matchAll && !matched) { - return false; - } - } - return matchAll; - } - - /** - * Perform a provider match based on the given ANQP elements for FQDN and RCOI - * - * @param anqpElements List of ANQP elements - * @param roamingConsortiumFromAp Roaming Consortium information element from the AP - * @param matchingSIMImsi Installed SIM IMSI that matches the SIM credential ANQP element - * @return {@link PasspointMatch} - */ - private PasspointMatch matchFqdnAndRcoi(Map<ANQPElementType, ANQPElement> anqpElements, - RoamingConsortium roamingConsortiumFromAp, String matchingSIMImsi) { - // Domain name matching. - if (ANQPMatcher.matchDomainName( - (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName), - mConfig.getHomeSp().getFqdn(), mImsiParameter, matchingSIMImsi)) { - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Domain name " + mConfig.getHomeSp().getFqdn() - + " match: HomeProvider"); - } - return PasspointMatch.HomeProvider; - } - - // Other Home Partners matching. - if (mConfig.getHomeSp().getOtherHomePartners() != null) { - for (String otherHomePartner : mConfig.getHomeSp().getOtherHomePartners()) { - if (ANQPMatcher.matchDomainName( - (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName), - otherHomePartner, null, null)) { - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Other Home Partner " + otherHomePartner - + " match: HomeProvider"); - } - return PasspointMatch.HomeProvider; - } - } - } - - // HomeOI matching - if (mConfig.getHomeSp().getMatchAllOis() != null) { - // Ensure that every HomeOI whose corresponding HomeOIRequired value is true shall match - // an OI in the Roaming Consortium advertised by the hotspot operator. - if (matchOis(mConfig.getHomeSp().getMatchAllOis(), (RoamingConsortiumElement) - anqpElements.get(ANQPElementType.ANQPRoamingConsortium), - roamingConsortiumFromAp, true)) { - if (mVerboseLoggingEnabled) { - Log.e(TAG, "All HomeOI RCOI match: HomeProvider"); - } - return PasspointMatch.HomeProvider; - } - } else if (mConfig.getHomeSp().getMatchAnyOis() != null) { - // Ensure that any HomeOI whose corresponding HomeOIRequired value is false shall match - // an OI in the Roaming Consortium advertised by the hotspot operator. - if (matchOis(mConfig.getHomeSp().getMatchAnyOis(), (RoamingConsortiumElement) - anqpElements.get(ANQPElementType.ANQPRoamingConsortium), - roamingConsortiumFromAp, false)) { - if (mVerboseLoggingEnabled) { - Log.e(TAG, "Any HomeOI RCOI match: HomeProvider"); - } - return PasspointMatch.HomeProvider; - } - } - - // Roaming Consortium OI matching. - if (matchOis(mConfig.getHomeSp().getRoamingConsortiumOis(), (RoamingConsortiumElement) - anqpElements.get(ANQPElementType.ANQPRoamingConsortium), - roamingConsortiumFromAp, false)) { - if (mVerboseLoggingEnabled) { - Log.e(TAG, "ANQP RCOI match: RoamingProvider"); - } - return PasspointMatch.RoamingProvider; - } - if (mVerboseLoggingEnabled) { - Log.e(TAG, "No domain name or RCOI match"); - } - return PasspointMatch.None; - } - - /** - * Fill in WifiEnterpriseConfig with information from an user credential. - * - * @param config Instance of {@link WifiEnterpriseConfig} - * @param credential Instance of {@link UserCredential} - */ - private void buildEnterpriseConfigForUserCredential(WifiEnterpriseConfig config, - Credential.UserCredential credential) { - String password; - try { - byte[] pwOctets = Base64.decode(credential.getPassword(), Base64.DEFAULT); - password = new String(pwOctets, StandardCharsets.UTF_8); - } catch (IllegalArgumentException e) { - Log.w(TAG, "Failed to decode password"); - password = credential.getPassword(); - } - config.setEapMethod(WifiEnterpriseConfig.Eap.TTLS); - config.setIdentity(credential.getUsername()); - config.setPassword(password); - if (!ArrayUtils.isEmpty(mCaCertificateAliases)) { - config.setCaCertificateAliases(mCaCertificateAliases.toArray(new String[0])); - } else { - config.setCaPath(SYSTEM_CA_STORE_PATH); - } - int phase2Method = WifiEnterpriseConfig.Phase2.NONE; - switch (credential.getNonEapInnerMethod()) { - case Credential.UserCredential.AUTH_METHOD_PAP: - phase2Method = WifiEnterpriseConfig.Phase2.PAP; - break; - case Credential.UserCredential.AUTH_METHOD_MSCHAP: - phase2Method = WifiEnterpriseConfig.Phase2.MSCHAP; - break; - case Credential.UserCredential.AUTH_METHOD_MSCHAPV2: - phase2Method = WifiEnterpriseConfig.Phase2.MSCHAPV2; - break; - default: - // Should never happen since this is already validated when the provider is - // added. - Log.wtf(TAG, "Unsupported Auth: " + credential.getNonEapInnerMethod()); - break; - } - config.setPhase2Method(phase2Method); - } - - /** - * Fill in WifiEnterpriseConfig with information from a certificate credential. - * - * @param config Instance of {@link WifiEnterpriseConfig} - */ - private void buildEnterpriseConfigForCertCredential(WifiEnterpriseConfig config) { - config.setEapMethod(WifiEnterpriseConfig.Eap.TLS); - config.setClientCertificateAlias(mClientPrivateKeyAndCertificateAlias); - if (!ArrayUtils.isEmpty(mCaCertificateAliases)) { - config.setCaCertificateAliases(mCaCertificateAliases.toArray(new String[0])); - } else { - config.setCaPath(SYSTEM_CA_STORE_PATH); - } - } - - /** - * Fill in WifiEnterpriseConfig with information from a SIM credential. - * - * @param config Instance of {@link WifiEnterpriseConfig} - * @param credential Instance of {@link SimCredential} - */ - private void buildEnterpriseConfigForSimCredential(WifiEnterpriseConfig config, - Credential.SimCredential credential) { - int eapMethod = WifiEnterpriseConfig.Eap.NONE; - switch(credential.getEapType()) { - case EAPConstants.EAP_SIM: - eapMethod = WifiEnterpriseConfig.Eap.SIM; - break; - case EAPConstants.EAP_AKA: - eapMethod = WifiEnterpriseConfig.Eap.AKA; - break; - case EAPConstants.EAP_AKA_PRIME: - eapMethod = WifiEnterpriseConfig.Eap.AKA_PRIME; - break; - default: - // Should never happen since this is already validated when the provider is - // added. - Log.wtf(TAG, "Unsupported EAP Method: " + credential.getEapType()); - break; - } - config.setEapMethod(eapMethod); - config.setPlmn(credential.getImsi()); - } - - private static void setAnonymousIdentityToNaiRealm(WifiEnterpriseConfig config, String realm) { - /** - * Set WPA supplicant's anonymous identity field to a string containing the NAI realm, so - * that this value will be sent to the EAP server as part of the EAP-Response/ Identity - * packet. WPA supplicant will reset this field after using it for the EAP-Response/Identity - * packet, and revert to using the (real) identity field for subsequent transactions that - * request an identity (e.g. in EAP-TTLS). - * - * This NAI realm value (the portion of the identity after the '@') is used to tell the - * AAA server which AAA/H to forward packets to. The hardcoded username, "anonymous", is a - * placeholder that is not used--it is set to this value by convention. See Section 5.1 of - * RFC3748 for more details. - * - * NOTE: we do not set this value for EAP-SIM/AKA/AKA', since the EAP server expects the - * EAP-Response/Identity packet to contain an actual, IMSI-based identity, in order to - * identify the device. - */ - config.setAnonymousIdentity("anonymous@" + realm); - } - - /** - * Helper function for creating a - * {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} from the given - * {@link WifiEnterpriseConfig} - * - * @param config The enterprise configuration containing the credential - * @return {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} - */ - private static Credential.UserCredential buildUserCredentialFromEnterpriseConfig( - WifiEnterpriseConfig config) { - Credential.UserCredential userCredential = new Credential.UserCredential(); - userCredential.setEapType(EAPConstants.EAP_TTLS); - - if (TextUtils.isEmpty(config.getIdentity())) { - Log.e(TAG, "Missing username for user credential"); - return null; - } - userCredential.setUsername(config.getIdentity()); - - if (TextUtils.isEmpty(config.getPassword())) { - Log.e(TAG, "Missing password for user credential"); - return null; - } - String encodedPassword = - new String(Base64.encode(config.getPassword().getBytes(StandardCharsets.UTF_8), - Base64.DEFAULT), StandardCharsets.UTF_8); - userCredential.setPassword(encodedPassword); - - switch(config.getPhase2Method()) { - case WifiEnterpriseConfig.Phase2.PAP: - userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_PAP); - break; - case WifiEnterpriseConfig.Phase2.MSCHAP: - userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAP); - break; - case WifiEnterpriseConfig.Phase2.MSCHAPV2: - userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2); - break; - default: - Log.e(TAG, "Unsupported phase2 method for TTLS: " + config.getPhase2Method()); - return null; - } - return userCredential; - } - - /** - * Helper function for creating a - * {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} from the given - * {@link WifiEnterpriseConfig} - * - * @param eapType The EAP type of the SIM credential - * @param config The enterprise configuration containing the credential - * @return {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} - */ - private static Credential.SimCredential buildSimCredentialFromEnterpriseConfig( - int eapType, WifiEnterpriseConfig config) { - Credential.SimCredential simCredential = new Credential.SimCredential(); - if (TextUtils.isEmpty(config.getPlmn())) { - Log.e(TAG, "Missing IMSI for SIM credential"); - return null; - } - simCredential.setImsi(config.getPlmn()); - simCredential.setEapType(eapType); - return simCredential; - } - - /** - * Enable verbose logging - * @param verbose more than 0 enables verbose logging - */ - public void enableVerboseLogging(int verbose) { - mVerboseLoggingEnabled = (verbose > 0) ? true : false; - } -} |