/* * Copyright (C) 2019 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 android.net.eap; import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA; import static com.android.internal.net.eap.message.EapData.EAP_TYPE_AKA_PRIME; import static com.android.internal.net.eap.message.EapData.EAP_TYPE_MSCHAP_V2; import static com.android.internal.net.eap.message.EapData.EAP_TYPE_SIM; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.telephony.Annotation.UiccAppType; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.net.eap.message.EapData.EapMethod; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * EapSessionConfig represents a container for EAP method configuration. * *

The EAP authentication server decides which EAP method is used, so clients are encouraged to * provide configs for several EAP methods. * * @hide */ @SystemApi public final class EapSessionConfig { /** @hide */ @VisibleForTesting static final byte[] DEFAULT_IDENTITY = new byte[0]; // IANA -> EapMethodConfig for that method /** @hide */ public final Map eapConfigs; /** @hide */ public final byte[] eapIdentity; /** @hide */ @VisibleForTesting public EapSessionConfig(Map eapConfigs, byte[] eapIdentity) { this.eapConfigs = Collections.unmodifiableMap(eapConfigs); this.eapIdentity = eapIdentity; } /** Retrieves client's EAP Identity */ @NonNull public byte[] getEapIdentity() { return eapIdentity; } /** * Retrieves configuration for EAP SIM * * @return the configuration for EAP SIM, or null if it was not set */ @Nullable public EapSimConfig getEapSimConfig() { return (EapSimConfig) eapConfigs.get(EAP_TYPE_SIM); } /** * Retrieves configuration for EAP AKA * * @return the configuration for EAP AKA, or null if it was not set */ @Nullable public EapAkaConfig getEapAkaConfig() { return (EapAkaConfig) eapConfigs.get(EAP_TYPE_AKA); } /** * Retrieves configuration for EAP AKA' * * @return the configuration for EAP AKA', or null if it was not set */ @Nullable public EapAkaPrimeConfig getEapAkaPrimeConfig() { return (EapAkaPrimeConfig) eapConfigs.get(EAP_TYPE_AKA_PRIME); } /** * Retrieves configuration for EAP MSCHAPV2 * * @return the configuration for EAP MSCHAPV2, or null if it was not set */ @Nullable public EapMsChapV2Config getEapMsChapV2onfig() { return (EapMsChapV2Config) eapConfigs.get(EAP_TYPE_MSCHAP_V2); } /** This class can be used to incrementally construct an {@link EapSessionConfig}. */ public static final class Builder { private final Map mEapConfigs; private byte[] mEapIdentity; /** Constructs and returns a new Builder for constructing an {@link EapSessionConfig}. */ public Builder() { mEapConfigs = new HashMap<>(); mEapIdentity = DEFAULT_IDENTITY; } /** * Sets the client's EAP Identity. * * @param eapIdentity byte[] representing the client's EAP Identity. * @return Builder this, to facilitate chaining. */ @NonNull public Builder setEapIdentity(@NonNull byte[] eapIdentity) { this.mEapIdentity = eapIdentity.clone(); return this; } /** * Sets the configuration for EAP SIM. * * @param subId int the client's subId to be authenticated. * @param apptype the {@link UiccAppType} apptype to be used for authentication. * @return Builder this, to facilitate chaining. */ @NonNull public Builder setEapSimConfig(int subId, @UiccAppType int apptype) { mEapConfigs.put(EAP_TYPE_SIM, new EapSimConfig(subId, apptype)); return this; } /** * Sets the configuration for EAP AKA. * * @param subId int the client's subId to be authenticated. * @param apptype the {@link UiccAppType} apptype to be used for authentication. * @return Builder this, to facilitate chaining. */ @NonNull public Builder setEapAkaConfig(int subId, @UiccAppType int apptype) { mEapConfigs.put(EAP_TYPE_AKA, new EapAkaConfig(subId, apptype)); return this; } /** * Sets the configuration for EAP AKA'. * * @param subId int the client's subId to be authenticated. * @param apptype the {@link UiccAppType} apptype to be used for authentication. * @param networkName String the network name to be used for authentication. * @param allowMismatchedNetworkNames indicates whether the EAP library can ignore potential * mismatches between the given network name and that received in an EAP-AKA' session. * If false, mismatched network names will be handled as an Authentication Reject * message. * @return Builder this, to facilitate chaining. */ @NonNull public Builder setEapAkaPrimeConfig( int subId, @UiccAppType int apptype, @NonNull String networkName, boolean allowMismatchedNetworkNames) { mEapConfigs.put( EAP_TYPE_AKA_PRIME, new EapAkaPrimeConfig( subId, apptype, networkName, allowMismatchedNetworkNames)); return this; } /** * Sets the configuration for EAP MSCHAPv2. * * @param username String the client account's username to be authenticated. * @param password String the client account's password to be authenticated. * @return Builder this, to faciliate chaining. */ @NonNull public Builder setEapMsChapV2Config(@NonNull String username, @NonNull String password) { mEapConfigs.put(EAP_TYPE_MSCHAP_V2, new EapMsChapV2Config(username, password)); return this; } /** * Constructs and returns an EapSessionConfig with the configurations applied to this * Builder. * * @return the EapSessionConfig constructed by this Builder. */ @NonNull public EapSessionConfig build() { if (mEapConfigs.isEmpty()) { throw new IllegalStateException("Must have at least one EAP method configured"); } return new EapSessionConfig(mEapConfigs, mEapIdentity); } } /** * EapMethodConfig represents a generic EAP method configuration. */ public abstract static class EapMethodConfig { /** @hide */ @EapMethod public final int methodType; /** @hide */ EapMethodConfig(@EapMethod int methodType) { this.methodType = methodType; } /** * Retrieves the EAP method type * * @return the IANA-defined EAP method constant */ public int getMethodType() { return methodType; } /** * Check if this is EAP-only safe method. * * @return whether the method is EAP-only safe * * @see RFC 5998#section 4, for safe eap * methods * * @hide */ public boolean isEapOnlySafeMethod() { return false; } } /** * EapUiccConfig represents the configs needed for EAP methods that rely on UICC cards for * authentication. */ public abstract static class EapUiccConfig extends EapMethodConfig { /** @hide */ public final int subId; /** @hide */ public final int apptype; private EapUiccConfig(@EapMethod int methodType, int subId, @UiccAppType int apptype) { super(methodType); this.subId = subId; this.apptype = apptype; } /** * Retrieves the subId * * @return the subId */ public int getSubId() { return subId; } /** * Retrieves the UICC app type * * @return the {@link UiccAppType} constant */ public int getAppType() { return apptype; } /** @hide */ @Override public boolean isEapOnlySafeMethod() { return true; } } /** * EapSimConfig represents the configs needed for an EAP SIM session. */ public static class EapSimConfig extends EapUiccConfig { /** @hide */ @VisibleForTesting public EapSimConfig(int subId, @UiccAppType int apptype) { super(EAP_TYPE_SIM, subId, apptype); } } /** * EapAkaConfig represents the configs needed for an EAP AKA session. */ public static class EapAkaConfig extends EapUiccConfig { /** @hide */ @VisibleForTesting public EapAkaConfig(int subId, @UiccAppType int apptype) { this(EAP_TYPE_AKA, subId, apptype); } /** @hide */ EapAkaConfig(int methodType, int subId, @UiccAppType int apptype) { super(methodType, subId, apptype); } } /** * EapAkaPrimeConfig represents the configs needed for an EAP-AKA' session. */ public static class EapAkaPrimeConfig extends EapAkaConfig { /** @hide */ @NonNull public final String networkName; /** @hide */ public final boolean allowMismatchedNetworkNames; /** @hide */ @VisibleForTesting public EapAkaPrimeConfig( int subId, @UiccAppType int apptype, @NonNull String networkName, boolean allowMismatchedNetworkNames) { super(EAP_TYPE_AKA_PRIME, subId, apptype); if (networkName == null) { throw new IllegalArgumentException("NetworkName was null"); } this.networkName = networkName; this.allowMismatchedNetworkNames = allowMismatchedNetworkNames; } /** * Retrieves the UICC app type * * @return the {@link UiccAppType} constant */ @NonNull public String getNetworkName() { return networkName; } /** * Checks if mismatched network names are allowed * * @return whether network name mismatches are allowed */ public boolean allowsMismatchedNetworkNames() { return allowMismatchedNetworkNames; } } /** * EapMsChapV2Config represents the configs needed for an EAP MSCHAPv2 session. */ public static class EapMsChapV2Config extends EapMethodConfig { /** @hide */ @NonNull public final String username; /** @hide */ @NonNull public final String password; /** @hide */ @VisibleForTesting public EapMsChapV2Config(String username, String password) { super(EAP_TYPE_MSCHAP_V2); if (username == null || password == null) { throw new IllegalArgumentException("Username or password was null"); } this.username = username; this.password = password; } /** * Retrieves the username * * @return the username to be used by MSCHAPV2 */ @NonNull public String getUsername() { return username; } /** * Retrieves the password * * @return the password to be used by MSCHAPV2 */ @NonNull public String getPassword() { return password; } } /** * Checks if all the methods in the session are EAP-only safe * * @return whether all the methods in the session are EAP-only safe * * @see RFC 5998#section 4, for safe eap * methods * * @hide */ public boolean areAllMethodsEapOnlySafe() { for(Map.Entry eapConfigsEntry : eapConfigs.entrySet()) { if (!eapConfigsEntry.getValue().isEapOnlySafeMethod()) { return false; } } return true; } }