1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.net.eap.statemachine; 18 19 import static com.android.internal.net.eap.EapAuthenticator.LOG; 20 import static com.android.internal.net.eap.message.EapData.EAP_NOTIFICATION; 21 import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_FAILURE; 22 import static com.android.internal.net.eap.message.EapMessage.EAP_CODE_SUCCESS; 23 24 import android.annotation.Nullable; 25 import android.net.eap.EapSessionConfig.EapMethodConfig.EapMethod; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.internal.net.eap.EapResult; 29 import com.android.internal.net.eap.EapResult.EapError; 30 import com.android.internal.net.eap.EapResult.EapFailure; 31 import com.android.internal.net.eap.exceptions.EapInvalidRequestException; 32 import com.android.internal.net.eap.message.EapMessage; 33 import com.android.internal.net.utils.SimpleStateMachine; 34 35 /** 36 * EapMethodStateMachine is an abstract class representing a state machine for EAP Method 37 * implementations. 38 */ 39 public abstract class EapMethodStateMachine extends SimpleStateMachine<EapMessage, EapResult> { 40 // Minimum key lengths specified in RFC 3748#1.2 41 public static final int MIN_MSK_LEN_BYTES = 64; 42 public static final int MIN_EMSK_LEN_BYTES = 64; 43 44 /* 45 * Used for transitioning to a state where EAP-Failure messages are expected next. This 46 * allows all EAP methods to easily transition to a pre-failure state in the event of errors, 47 * failed authentication, etc. 48 */ 49 protected boolean mIsExpectingEapFailure = false; 50 51 /** 52 * Returns the EAP Method type for this EapMethodStateMachine implementation. 53 * 54 * @return the IANA value for the EAP Method represented by this EapMethodStateMachine 55 */ 56 @EapMethod getEapMethod()57 abstract int getEapMethod(); 58 59 @VisibleForTesting getState()60 protected SimpleState getState() { 61 return mState; 62 } 63 64 @VisibleForTesting transitionTo(EapMethodState newState)65 protected void transitionTo(EapMethodState newState) { 66 LOG.d( 67 this.getClass().getSimpleName(), 68 "Transitioning from " + mState.getClass().getSimpleName() 69 + " to " + newState.getClass().getSimpleName()); 70 super.transitionTo(newState); 71 } 72 handleEapNotification(String tag, EapMessage message)73 abstract EapResult handleEapNotification(String tag, EapMessage message); 74 75 protected abstract class EapMethodState extends SimpleState { 76 /** 77 * Handles premature EAP-Success and EAP-Failure messages, as well as EAP-Notification 78 * messages. 79 * 80 * @param tag the String logging tag to be used while handing message 81 * @param message the EapMessage to be checked for early Success/Failure/Notification 82 * messages 83 * @return the EapResult generated from handling the give EapMessage, or null if the message 84 * Type matches that of the current EAP method 85 */ 86 @Nullable handleEapSuccessFailureNotification(String tag, EapMessage message)87 EapResult handleEapSuccessFailureNotification(String tag, EapMessage message) { 88 if (message.eapCode == EAP_CODE_SUCCESS) { 89 // EAP-SUCCESS is required to be the last EAP message sent during the EAP protocol, 90 // so receiving a premature SUCCESS message is an unrecoverable error. 91 return new EapError( 92 new EapInvalidRequestException( 93 "Received an EAP-Success in the " + tag)); 94 } else if (message.eapCode == EAP_CODE_FAILURE) { 95 transitionTo(new FinalState()); 96 return new EapFailure(); 97 } else if (message.eapData.eapType == EAP_NOTIFICATION) { 98 return handleEapNotification(tag, message); 99 } else if (mIsExpectingEapFailure) { 100 // Expecting EAP-Failure message. Didn't receive EAP-Failure or EAP-Notification, 101 // so log and return EAP-Error. 102 LOG.e(tag, "Expecting EAP-Failure. Received non-Failure/Notification message"); 103 return new EapError( 104 new EapInvalidRequestException( 105 "Expecting EAP-Failure. Received received " 106 + message.eapData.eapType)); 107 } else if (message.eapData.eapType != getEapMethod()) { 108 return new EapError(new EapInvalidRequestException( 109 "Expected EAP Type " + getEapMethod() 110 + ", received " + message.eapData.eapType)); 111 } 112 113 return null; 114 } 115 } 116 117 protected class FinalState extends EapMethodState { 118 @Override process(EapMessage msg)119 public EapResult process(EapMessage msg) { 120 return new EapError( 121 new IllegalStateException("Attempting to process from a FinalState")); 122 } 123 } 124 } 125