summaryrefslogtreecommitdiff
path: root/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'service/java/com/android/server/wifi/hotspot2/PasspointProvider.java')
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointProvider.java1009
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;
- }
-}