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 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.internal.net.eap.EapResult;
28 import com.android.internal.net.eap.EapResult.EapError;
29 import com.android.internal.net.eap.EapResult.EapFailure;
30 import com.android.internal.net.eap.exceptions.EapInvalidRequestException;
31 import com.android.internal.net.eap.message.EapData.EapMethod;
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     /*
41      * Used for transitioning to a state where EAP-Failure messages are expected next. This
42      * allows all EAP methods to easily transition to a pre-failure state in the event of errors,
43      * failed authentication, etc.
44      */
45     protected boolean mIsExpectingEapFailure = false;
46 
47     /**
48      * Returns the EAP Method type for this EapMethodStateMachine implementation.
49      *
50      * @return the IANA value for the EAP Method represented by this EapMethodStateMachine
51      */
52     @EapMethod
getEapMethod()53     abstract int getEapMethod();
54 
55     @VisibleForTesting
getState()56     protected SimpleState getState() {
57         return mState;
58     }
59 
60     @VisibleForTesting
transitionTo(EapMethodState newState)61     protected void transitionTo(EapMethodState newState) {
62         LOG.d(
63                 this.getClass().getSimpleName(),
64                 "Transitioning from " + mState.getClass().getSimpleName()
65                         + " to " + newState.getClass().getSimpleName());
66         super.transitionTo(newState);
67     }
68 
handleEapNotification(String tag, EapMessage message)69     abstract EapResult handleEapNotification(String tag, EapMessage message);
70 
71     protected abstract class EapMethodState extends SimpleState {
72         /**
73          * Handles premature EAP-Success and EAP-Failure messages, as well as EAP-Notification
74          * messages.
75          *
76          * @param tag the String logging tag to be used while handing message
77          * @param message the EapMessage to be checked for early Success/Failure/Notification
78          *                messages
79          * @return the EapResult generated from handling the give EapMessage, or null if the message
80          * Type matches that of the current EAP method
81          */
82         @Nullable
handleEapSuccessFailureNotification(String tag, EapMessage message)83         EapResult handleEapSuccessFailureNotification(String tag, EapMessage message) {
84             if (message.eapCode == EAP_CODE_SUCCESS) {
85                 // EAP-SUCCESS is required to be the last EAP message sent during the EAP protocol,
86                 // so receiving a premature SUCCESS message is an unrecoverable error.
87                 return new EapError(
88                         new EapInvalidRequestException(
89                                 "Received an EAP-Success in the " + tag));
90             } else if (message.eapCode == EAP_CODE_FAILURE) {
91                 transitionTo(new FinalState());
92                 return new EapFailure();
93             } else if (message.eapData.eapType == EAP_NOTIFICATION) {
94                 return handleEapNotification(tag, message);
95             } else if (mIsExpectingEapFailure) {
96                 // Expecting EAP-Failure message. Didn't receive EAP-Failure or EAP-Notification,
97                 // so log and return EAP-Error.
98                 LOG.e(tag, "Expecting EAP-Failure. Received non-Failure/Notification message");
99                 return new EapError(
100                         new EapInvalidRequestException(
101                                 "Expecting EAP-Failure. Received received "
102                                         + message.eapData.eapType));
103             } else if (message.eapData.eapType != getEapMethod()) {
104                 return new EapError(new EapInvalidRequestException(
105                         "Expected EAP Type " + getEapMethod()
106                                 + ", received " + message.eapData.eapType));
107             }
108 
109             return null;
110         }
111     }
112 
113     protected class FinalState extends EapMethodState {
114         @Override
process(EapMessage msg)115         public EapResult process(EapMessage msg) {
116             return new EapError(
117                     new IllegalStateException("Attempting to process from a FinalState"));
118         }
119     }
120 }
121