# Include EAP types. If you uncomment any here, also uncomment them from
# the EAP function body.

include eapfunctions.md5,
include eapfunctions.mschap2,
include eapfunctions.tls,
include eapfunctions.ttls,
include eapfunctions.peap,


# Main EAP body

function EAP (

  # Decode all concatenated EAP-Message attributes into the request list, using
  # the attribute space in which the dummy attribute 'EAP-Packet' is defined.

  all EAP-Message dictdecode EAP-Packet or (
    rep:Log-Line := rep:Log-Line . " - Could not decode EAP packet!",
    return (result = RAD-Code::Access-Reject)
  ),

  # Initialize reply list (on which an EAP Request to the peer is built ;-)
  # EAP type specific functions will increment rep:Id wherever necessary.
  # This sets things up so that you can just exit with a Success or Failure.

  rep:Id = Id,

  # If we haven't got any identity in the session yet, use User-Name. If
  # we receive a Response-Identity, this will override the stored identity,
  # and similarly for inner names or tunneled EAP types.
  #
  # When we finally get to the point of verifying credentials and/or accepting,
  # we use that name to obtain the user's profile from the database.

  state-user-name exists or state-user-name = User-Name,

  # Handle Response/Identity. This overrides the username stored in the
  # session. We always continue the EAP conversation by starting a preferred
  # EAP type here.

  Response-Identity exists and (
    rep:Log-Line := rep:Log-Line . " EAP: Identity " . Response-Identity,
    state-user-name := Response-Identity,

    # Preferred EAP type

    EAP-MSCHAP2
  )

  # Handle NAK and Expanded-NAK. 

  or (Response-Nak exists or Response-Exp-Nak exists) and (

    rep:Log-Line := rep:Log-Line . " EAP: Got NAK, client accepts types " . 
		    (hex (Response-Nak . Response-Exp-Nak)) asmac ", ",

    # If we have an earlier NAK from the state database, then the client
    # changed its mind and we immediately reject it, to prevent endless loops 
    # with buggy clients or clients that like playing poker.

    state-eap-nak exists and (
      rep:Log-Line := rep:Log-Line . " Client changed its mind, rejecting",
      rep:Failure = 1
    )
    or (
      
      # Forget all previous state, except the username.

      delall str,
      state-user-name exists and str = state-user-name,
      delall PERSIST:Any,
      str exists and state-user-name = str,

      # Test for EAP types supported by client in order of our preference.

      (NAK-TTLS exists or NAK-Exp-TTLS exists) and EAP-TTLS
      or (NAK-TLS exists or NAK-Exp-TLS exists) and EAP-TLS
      or (NAK-PEAP exists or NAK-Exp-PEAP exists) and PEAP
      or (NAK-MSCHAP2 exists or NAK-Exp-MSCHAP2 exists) and EAP-MSCHAP2
      or (NAK-MD5-Challenge exists or NAK-Exp-MD5-Challenge exists) and EAP-MD5

      # Client doesn't want to continue, or wanted types we don't support

      or NAK-None exists and (
	rep:Log-Line := rep:Log-Line . " - client doesn't want EAP, rejecting.",
	rep:Failure = 1
      )
      or (
	rep:Log-Line := rep:Log-Line . " - none supported here, rejecting.",
	rep:Failure = 1
      )
    )
  )

  # Handle type-specific responses by branching to applicable EAP type.  
  # It would probably clearer here to have the EAP types as constant names 
  # for a fixed field attribute occupying the EAP Type field and test that, 
  # but that would require each type number to be defined twice in the 
  # dictionary.

  or Response-TTLS-Flags exists and EAP-TTLS
  or Response-TLS-Flags exists and EAP-TLS
  or Response-PEAP-Flags exists and PEAP
  or (Response-MSCHAP2-Id exists or
      Response-MSCHAP2-Success exists or 
      Response-MSCHAP2-Failure exists) and EAP-MSCHAP2
  or Response-MD5-Challenge exists and EAP-MD5
  or (
      rep:Log-Line := rep:Log-Line . " - unsupported EAP type, rejecting.",
      rep:Failure = 1
  ),

  # Encode reply list into EAP-Message (will be auto-split when encoding the
  # RADIUS packet) using space of dummy attribute 'EAP-Packet' as ground space

  rep:EAP-Message = dictencode rep:EAP-Packet,

  # We're done here. Set the result according to the EAP status. The top level
  # behaviour file will act upon it.

  rep:Success exists and return (result = RAD-Code::Access-Accept),
  rep:Failure exists and return (result = RAD-Code::Access-Reject),
  return (result = RAD-Code::Access-Challenge)
)

# vim:softtabstop=2:sw=2

